diff --git a/configure.ac b/configure.ac index e399dc5..aaa4028 100644 --- a/configure.ac +++ b/configure.ac @@ -85,8 +85,8 @@ AC_ARG_ENABLE(integer-samples, AC_ARG_ENABLE(openmp, [AC_HELP_STRING([--enable-openmp], - [use parallel multicore calculation through OpenMP [default=yes]])],, - [enable_openmp=yes]) + [use parallel multicore calculation through OpenMP [default=no]])],, + [enable_openmp=no]) # Let the user enable/disable the x86 optimizations. # Useful when compiling on non-x86 architectures. diff --git a/source/Android-lib/.classpath b/source/Android-lib/.classpath index 7bc01d9..5176974 100644 --- a/source/Android-lib/.classpath +++ b/source/Android-lib/.classpath @@ -1,9 +1,9 @@ - - + + diff --git a/source/Android-lib/jni/Android.mk b/source/Android-lib/jni/Android.mk index ef5cfef..98fbbf7 100644 --- a/source/Android-lib/jni/Android.mk +++ b/source/Android-lib/jni/Android.mk @@ -36,10 +36,16 @@ LOCAL_SHARED_LIBRARIES += -lgcc LOCAL_LDLIBS += -llog # for native asset manager #LOCAL_LDLIBS += -landroid -# don't export all symbols -# -# in ARM-only environment could add "-marm" switch to force arm instruction set instead -# of thumb for improved calculation performance. -LOCAL_CFLAGS += -fvisibility=hidden -I ../../../include -fdata-sections -ffunction-sections + +# Custom Flags: +# -fvisibility=hidden : don't export all symbols +# -fopenmp : enable these flags to allow using OpenMP for parallel computation +LOCAL_CFLAGS += -fvisibility=hidden -I ../../../include -fdata-sections -ffunction-sections #-fopenmp + +#LOCAL_LDFLAGS += -fopenmp + + +# Use ARM instruction set instead of Thumb for improved calculation performance in ARM CPUs +LOCAL_ARM_MODE := arm include $(BUILD_SHARED_LIBRARY) diff --git a/source/Android-lib/jni/Application.mk b/source/Android-lib/jni/Application.mk index 68ef2d0..9875799 100644 --- a/source/Android-lib/jni/Application.mk +++ b/source/Android-lib/jni/Application.mk @@ -6,4 +6,5 @@ APP_ABI := all #armeabi-v7a armeabi APP_OPTIM := release APP_STL := stlport_static -APP_CPPFLAGS := -fexceptions \ No newline at end of file +APP_CPPFLAGS := -fexceptions # -D SOUNDTOUCH_DISABLE_X86_OPTIMIZATIONS + diff --git a/source/Android-lib/jni/soundtouch-jni.cpp b/source/Android-lib/jni/soundtouch-jni.cpp index 15fb446..7922bb3 100644 --- a/source/Android-lib/jni/soundtouch-jni.cpp +++ b/source/Android-lib/jni/soundtouch-jni.cpp @@ -46,6 +46,54 @@ static void _setErrmsg(const char *msg) } +#ifdef _OPENMP + +#include +extern pthread_key_t gomp_tls_key; +static void * _p_gomp_tls = NULL; + +/// Function to initialize threading for OpenMP. +/// +/// This is a workaround for bug in Android NDK v10 regarding OpenMP: OpenMP works only if +/// called from the Android App main thread because in the main thread the gomp_tls storage is +/// properly set, however, Android does not properly initialize gomp_tls storage for other threads. +/// Thus if OpenMP routines are invoked from some other thread than the main thread, +/// the OpenMP routine will crash the application due to NULL pointer access on uninitialized storage. +/// +/// This workaround stores the gomp_tls storage from main thread, and copies to other threads. +/// In order this to work, the Application main thread needws to call at least "getVersionString" +/// routine. +static int _init_threading(bool warn) +{ + void *ptr = pthread_getspecific(gomp_tls_key); + LOGV("JNI thread-specific TLS storage %ld", (long)ptr); + if (ptr == NULL) + { + LOGV("JNI set missing TLS storage to %ld", (long)_p_gomp_tls); + pthread_setspecific(gomp_tls_key, _p_gomp_tls); + } + else + { + LOGV("JNI store this TLS storage"); + _p_gomp_tls = ptr; + } + // Where critical, show warning if storage still not properly initialized + if ((warn) && (_p_gomp_tls == NULL)) + { + _setErrmsg("Error - OpenMP threading not properly initialized: Call SoundTouch.getVersionString() from the App main thread!"); + return -1; + } + return 0; +} + +#else +static int _init_threading(bool warn) +{ + // do nothing if not OpenMP build + return 0; +} +#endif + // Processes the sound file static void _processFile(SoundTouch *pSoundTouch, const char *inFileName, const char *outFileName) @@ -118,6 +166,17 @@ extern "C" DLL_PUBLIC jstring Java_net_surina_soundtouch_SoundTouch_getVersionSt // Call example SoundTouch routine verStr = SoundTouch::getVersionString(); + /// gomp_tls storage bug workaround - see comments in _init_threading() function! + _init_threading(false); + + int threads = 0; + #pragma omp parallel + { + #pragma omp atomic + threads ++; + } + LOGV("JNI thread count %d", threads); + // return version as string return env->NewStringUTF(verStr); } @@ -176,6 +235,9 @@ extern "C" DLL_PUBLIC int Java_net_surina_soundtouch_SoundTouch_processFile(JNIE LOGV("JNI process file %s", inputFile); + /// gomp_tls storage bug workaround - see comments in _init_threading() function! + if (_init_threading(true)) return -1; + try { _processFile(ptr, inputFile, outputFile); diff --git a/source/Android-lib/res/layout/activity_example.xml b/source/Android-lib/res/layout/activity_example.xml index b333511..c71c77d 100644 --- a/source/Android-lib/res/layout/activity_example.xml +++ b/source/Android-lib/res/layout/activity_example.xml @@ -25,7 +25,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="5" - android:inputType="numberDecimal" + android:inputType="text" android:text="100" > @@ -46,8 +46,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:ems="5" - android:inputType="numberSigned" - android:text="0" > + android:inputType="text" + android:text="-0.318" > diff --git a/source/Android-lib/src/net/surina/ExampleActivity.java b/source/Android-lib/src/net/surina/ExampleActivity.java index 465cb9d..1aae8d8 100644 --- a/source/Android-lib/src/net/surina/ExampleActivity.java +++ b/source/Android-lib/src/net/surina/ExampleActivity.java @@ -146,11 +146,10 @@ public class ExampleActivity extends Activity implements OnClickListener } - /// Processing routine - @Override - protected Long doInBackground(Parameters... aparams) + + /// Function that does the SoundTouch processing + public final long doSoundTouchProcessing(Parameters params) { - Parameters params = aparams[0]; SoundTouch st = new SoundTouch(); st.setTempo(params.tempo); @@ -177,6 +176,15 @@ public class ExampleActivity extends Activity implements OnClickListener } return 0L; } + + + + /// Overloaded function that get called by the system to perform the background processing + @Override + protected Long doInBackground(Parameters... aparams) + { + return doSoundTouchProcessing(aparams[0]); + } } @@ -202,8 +210,9 @@ public class ExampleActivity extends Activity implements OnClickListener Toast.makeText(this, "Starting to process file " + params.inFileName + "...", Toast.LENGTH_SHORT).show(); - // start processing task in background + // start SoundTouch processing in a background thread task.execute(params); +// task.doSoundTouchProcessing(params); // this would run processing in main thread } catch (Exception exp)