Cross-calling FFTW from Borland Delphi ====================================== Last updated: 15/MAR/04 -- updated acknowledgements. Author: Mark G. Beckett (g.beckett@epcc.ed.ac.uk). Acknowledgements: Thanks to Gavin J. Pringle and Ratnadeep Abrol (both of The University of Edinburgh, Scotland) for their comments on this document and the accompanying document "Cross-calling FFTW from Borland Delphi". The work was carried out under EC funding (Contract No. IST-2000-29598/3D-PATHOLOGY). This document contains some notes on how to call the FFTW library from Delphi. It explains how to cross-call the single-precision version of FFTW functions, though double precision and long double precision functions could be implemented in an analogous manner. It is based on experiences with: Borland Delphi Professional, Version 7.0 for MS Windows, the Borland C++ Compiler suite, Version 5.5, and FFTW Version 3.0.1. Before reading this document, you should read the accompanying file "fftw-3.0.1BC_installation.txt", where you will find instructions on how to build FFTW for MS Windows (in a Delphi-friendly fashion). The easiest way to use FFTW with Delphi (on MS Windows, at least), is build the software as a dynamic library (called a DLL in MS Windows speak). This is because Borland Delphi cannot be link against static .LIB files, making it nearly impossible to link in the C runtime library with the various FFTW object files. Other methods for linking in FFTW may be possible, though have not been explored in this document (see [1]). The only significant downside of creating a DLL for FFTW that we have encountered, is that one cannot use the FFTW-supplied memory management functions "fftw_malloc", "fftw_free", etc. This is because the DLL is allocated a separate heap in free store from the Delphi, parent application. The Delphi memory manager is unaware of heap allocations that occur in this alternative heap and thus complains if you attempt to access such memory from within the Delphi environment. An advantage of calling the FFTW memory allocation routines, is that they correctly align arrays at 8 (16) byte boundaries: something that is important for the performance of single (double) precision using the SIMD extensions on Intel Pentium III/AMD XP processor families, at least. If instead, memory is allocated within Delphi, then it may not be aligned for optimal performance. This should not cause any errors in your application, since FFTW checks the alignment of arrays, before employing any specific "tricks", reverting to standard instructions if the alignment check fails. It might be possible to mimic the alignment code of FFTW in a Delphi memory allocation routine (see lines 45--68 in "kernel/alloc.c") -- this point is to be resolved. As noted in [2], there are a number of steps required to call a C library from Delphi. These are as follows: - Compile the C library in a suitable form. - If compiled as a DLL, export functions that will be available to Delphi code through the public interface. - Create a public interface on the Delphi side of the development. The first two steps in this process are discussed in the accompanying document "fftw-3.0.1BC_installation.txt". The third step is described below. Creating a Delphi interface to your library is not difficult, though there are a couple of subtleties to note. A sample interface is provided in "Delphi\fftw_interface.pas". Notes: 1) The routine names presented in the interface are those exported by the DLL. These can be listed using the TDUMP utility (provided with Borland C++ command-line tools). 2) Memory allocated/controlled by the routines _fftwf_malloc() and _fftwf_free() may not be accessed directly from Delphi (as the memory manager will complain about an Access Violation). 3) Use a function declaration for routines that return a value and procedure declarations for those that don't. 4) The C structure "plan" is best handled as a Pointer in Delphi. It should never be manipulated directly from Delphi -- use only FFTW manipulation routines. 5) Although it is the norm to define an array in Delphi as, for example, "Array of Single", this technique cannot be used for dynamically allocated memory. Instead one needs to pass a pointer, for example ^Single. This is not as safe as passing an array, so you might wish to include informative comments in your application source code. Based on the above interface, the Delphi implementation of the example code on page 3 of [3] is: program fftw_example; uses SysUtils, fftw_interface in 'fftw_interface.pas'; var in, out : Array of Single; plan : Pointer; {$APPTYPE CONSOLE} begin ... SetLength(in, N); SetLength(out, N); plan := _fftwf_plan_dft_1d(dataLength, @in[0], @out[0], FFTW_FORWARD, FFTW_ESTIMATE); ... _fftwf_execute(plan); ... _fftwf_destroy_plan(plan); end. References ========== [1] Charlie Calvert, Application Development with C++Builder and Delphi. Web page "http://community.borland.com/article/0,1410,10103,00.html". [2] Web page "http://rvelthuis.bei.t-online.de/articles/articles-cobjs.htm". [3] Matteo Frigo, Steven G. Johnson, "FFTW: for version 3.0.1". Available from "http://www.fftw.org/" (June 2003).