The open source Sound eXchange (SoX) project is a Swiss Army knife of sound processing programs according to it’s homepage. SoX can convert various sound file formats into other formats (commonly referred to as transcoding). SoX is also cross platform and can be compiled as a dynamic and static library. I wanted to access the SoX library from Delphi XE7 Firemonkey and Appmethod on Android, IOS, Windows, and OSX as a proof of concept to see how difficult it would be and how much it cost to get it working. I hired two developers via oDesk to accomplish this task. The first developer had the task of compiling the static and dynamic libraries for each platform and for providing a working demo in a language from that platform. The second developer had the task of converting the header file into Object Pascal and translating the demos from each platform language into Object Pascal for Firemonkey. It cost around $2,200 to get a working copy of the libraries on all four platforms and mostly working demos in Object Pascal for Delphi XE7 Firemonkey and Appmethod.
For creating the libSOX static library for IOS we followed instructions that we found here and here. I assume that for Mac OSX that we just modified the IOS version or the developer used his own initiative to make it happen. For creating the libSOX shared objects for Android we followed instructions that we found here and here. For creating the libSOX DLL for Windows we followed instructions that we found here.
For IOS we went with a static 32bit library (.a file). We have not created a 64bit version of the static library for IOS but if you REALLY need it contact me and we’ll get it done. There is a universal libsox.a file (32bit+64bit) linked at the bottom. For Mac OSX we went with a dynamic 32bit library (.dylib). For Android we went with 32bit Shared Objects (.so). For Windows we went with a dynamic 32bit library (.dll). The Firemonkey versions work on Windows, IOS, and Mac OSX. The Firemonkey version for Android loads the shared objects but does not successfully use the library yet. If you figure out how to get the Android version working in Firemonkey feel free to contribute back the working project and I will include it here.
One thing that made this project more difficult is that libSOX is mainly a command line program and is not really setup to be a library though it does have that capability as well. So additional work had to be done to make sure it would compile out to a library for each platform. One issue that we encountered when building the demo for Android was loading the Shared Objects in order so that Shared Objects that were depended on other shared objects be loaded first and in order of dependency. Once all of the Shared Objects were correctly loaded the libSOX Shared Object could then be loaded and all of the other Shared Objects it depends on would exist correctly. Here is some sample code of that in action:
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'liblpc10.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libgsm.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libogg.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libvorbis.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libvorbisenc.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libvorbisfile.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libFLAC.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libmp3lame.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libmad.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libpng.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libsmr.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libsmrx.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libsndfile.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libwavpack.so'))), RTLD_LAZY);
dlopen(PAnsiChar(AnsiString(TPath.Combine(TPath.GetDocumentsPath, 'libfmemopen.so'))), RTLD_LAZY);
Libsox_Path := TPath.Combine(TPath.GetDocumentsPath, 'libsox.so');
Here is the code from the Object Pascal SoX.pas header which shows how it loads the Windows DLL, the Mac OSX dylib, and the IOS static .a file with conditional compilation (we used the Object Pascal OpenGL headers as a roadmap on how to set it up):
{$IFDEF MSWINDOWS}
const
{$IFDEF DEBUG}
libsox = '../../lib/Win/LibSoX.dll';
{$ELSE}
libsox = '/lib/Win/LibSoX.dll';
{$ENDIF}
{$DEFINE STDCALL}
{$ENDIF MSWINDOWS}
{$IFDEF MACOS}
const
libsox = 'libsox.1.dylib';
{$DEFINE CDECL}
{$ELSE}
{$IFDEF IOS}
libsox = 'libsox.a';
{$DEFINE CDECL}
{$ENDIF}
{$ENDIF}
There are a lot of files here so if you’re attempting to get this working and something is missing leave a comment and I’ll see if I have it.
Download the full source code demo of libSOX for IOS and Mac OSX with project demos in XCode. Download the libsox.a 32bit+64bit static library and compile script. And download just the dynamic library libSOX file for Mac OSX.
Download the full source code demo of libSOX for Windows with project demo for Visual Studio.
Wow! Thank you so much for your contribution to the Delphi community. Going to try this!
-Brent
Thank you very much, great work as always. 😉
khalid
Hi, can you provide 64-bit library please? As far as I know we need it so apple can take our apps to iTunes.
Here is my mail: lotierm @(at) gmail.com.
Here is a universal static library for libsox.a and a build script for building it from source:
http://www.fmxexpress.com/wp-content/uploads/2015/02/libsox_uni.zip