OpenAL for Android

Started by paulscode, August 10, 2011, 02:06:31 AM

Previous topic - Next topic

paulscode

I am still working out the kinks with the Android version of my 3D Sound System.  In the mean time, I thought some folks might find this part of the library useful by itself.  This is my port of native OpenAL to Android with a simple Java interface.  It will basically allow you to add OpenAL to your projects without having to play around with c/c++ and JNI.  If optimization is a big concern for your project but you still need 3D sound, removing the overhead of SoundSystem and accessing OpenAL directly could be a good option.  This library should work on all Android versions back to 1.5 (although I've only actually tested it on my own phone).

The Java interface for this port is probably closer to JOAL rather than LWJGL's interface.  There are a few differences from JOAL, however (mainly in the methods that take array/ buffer parameters, the way the context is created and destroyed, and the lack of a direct interface to ALC with device and context objects Java-side).  That being said, code written for JOAL or LWJGL is pretty simple to make work with my interface (it's all OpenAL when you get right down to the nuts and bolts, after all).

Everything is accessed statically through the class paulscode.android.sound.ALAN.  Before creating any sources and what-not, first connect with the Android audio device and create an AL context by calling:
ALAN.create();

From there, you can access whatever AL functions you need through ALAN.  For example:
        int[] ALBufferIDs = new int[BUFFER_COUNT];
        int[] source = new int[1];
        int[] state = new int[1];
        ALAN.alGenSources( 1, source );
        ALAN.alGenBuffers( BUFFER_COUNT, ALBufferIDs );
        ALAN.alBufferData( ALBufferIDs[i], format, buffer, buffer.length, rate );
        ALAN.alSourceQueueBuffers( source[0], c, ALBufferIDs );
        ALAN.alSourceQueueBuffers( source[0], c, ALBufferIDs );
        ALAN.alSourcePlay( source[0] );
        ALAN.alGetSourcei( source[0], ALAN.AL_SOURCE_STATE, state );
        if( state[0] != ALAN.AL_PLAYING )
            whatever();


And when you're finished, destroy the AL context and disconnect from the Android audio device by calling:
ALAN.destroy();

In case you haven't caught on, ALAN isn't a personal name like some AI alias - it's just an abbreviated form of  "OpenAL for Android" (fewer keystrokes than OpenALAndroid).  On a humorous note, I was actually forced to rename a hundred or so references to my project's original title "Android OpenAL", which I was horrified to realize became "ANAL" when abbreviated.  THAT was a real pain in the butt ;D

Anyway, here is a very basic app to demonstrate streaming an .ogg file:
ALAN Demo    (source code)

I normally drop in the source code to my projects, but you should be able to just unzip the above APK file, grab the contents of the "lib" folder, and paste them into your own project's "lib" folder.  If you prefer to compile the source code yourself, you can unzip the source code archive above and either run the following commands from the terminal/command prompt:
ndk-build
ant debug
ant install

Or you can "create a new project from source" in Eclipse (requires the Android SDK and NDK to be installed, as well as the ADT plug-in and Sequoyah Android Native Code plugin from the update site plug-ins).  Once you've created the Eclipse project, just right-click on it in the left panel and navigate to "Android Tools->Add Native Support".

The above demo utilizes the Tremolo library for decoding the .ogg file.  It is a popular library for use on Android because it is extra light-weight and optimized for the ARM architecture, but there are plenty of other decoder libraries out there you could use if you have problems with that one.  In theory, you could even use any of a number of pure-Java audio decoders as well, including the ones included with my SoundSystem library.  I do not recommend copying the code from this demo - it very inefficient and meant only as a proof of concept.

One final note: OpenAL Android is licensed by the LGPL (since that is what OpenAL and Tremolo are licensed under).  While I normally shy away from this license, it actually meshes really well with Android apps, because the end user can use any of a number of free backup programs (such as ASTRO) to create a backup APK of your app.  From there, he can plug in a different version of the library into the APK, and simply reinstall it.  All you have to do is mention somewhere in your documentation where they can acquire the sourcecode for OpenAL Android (feel free to mention my website if you don't want to host the files yourself). Please let me know if you encounter any bugs (logcat output is always helpful).


paulscode

Yes, sadly it's been down since yesterday night.  I'm hoping 1&1 just needs to reboot a server when they come in to work this morning.

paulscode

A couple folks have asked me why they can't change the volume on the ALAN Demo app.  That's because I threw everything into the activity's onCreate method, and the thread blocks there in the stream loop until playback finishes.  This was just for demonstration purposes - you wouldn't normally write an app that way.  You'll also notice that the make-shift ogg decoder I threw together is really inefficient (takes encoded buffers from Java, decode them in native, sends then back to Java, then Java sends them back to native to be played).  I wouldn't recommend copying any part of that demo in a real-world app - it is entirely a proof of concept.

Thomas.

I tried ALAN Demo app on my galaxy S but it is not working... if you need some testing, I can help :)

08-29 21:47:34.171: INFO/ActivityManager(189): Start proc paulscode.android.sound.demo for activity paulscode.android.sound.demo/.ALAndroidDemo: pid=3425 uid=10102 gids={1015}
08-29 21:47:34.242: DEBUG/dalvikvm(3425): Debugger has detached; object registry had 1 entries
08-29 21:47:34.300: DEBUG/szipinf(3425): Initializing inflate state
08-29 21:47:34.312: DEBUG/dalvikvm(3425): Trying to load lib /data/data/paulscode.android.sound.demo/lib/libal-android.so 0x40514398
08-29 21:47:34.335: DEBUG/dalvikvm(3425): Added shared lib /data/data/paulscode.android.sound.demo/lib/libal-android.so 0x40514398
08-29 21:47:34.339: DEBUG/dalvikvm(3425): Trying to load lib /data/data/paulscode.android.sound.demo/lib/libogg-decoder.so 0x40514398
08-29 21:47:34.347: DEBUG/dalvikvm(3425): Added shared lib /data/data/paulscode.android.sound.demo/lib/libogg-decoder.so 0x40514398
08-29 21:47:34.347: DEBUG/dalvikvm(3425): No JNI_OnLoad found in /data/data/paulscode.android.sound.demo/lib/libogg-decoder.so 0x40514398, skipping init
08-29 21:47:34.351: VERBOSE/ALANDemo(3425): Starting up AL-Android
08-29 21:47:34.351: VERBOSE/alAndroidJNI.c(3425): Starting up ALAN...
08-29 21:47:34.351: VERBOSE/alAndroidJNI.c(3425):     Opening Android audio device
08-29 21:47:34.351: VERBOSE/alAndroidJNI.c(3425):     Creating AL context
08-29 21:47:34.351: VERBOSE/alAndroidJNI.c(3425): ALAN lives! (MWAHAHAhahahaha...)
08-29 21:47:34.351: VERBOSE/alAndroidJNI.c(3425):     Author: Paul Lamb, www.paulscode.com
08-29 21:47:34.351: VERBOSE/ALANDemo(3425): Opening audio file
08-29 21:47:34.351: VERBOSE/ALANDemo(3425): Reading raw audio data
08-29 21:47:34.375: DEBUG/dalvikvm(3425): GC_FOR_MALLOC freed 40K, 50% free 2692K/5379K, external 0K/0K, paused 24ms
08-29 21:47:34.382: INFO/dalvikvm-heap(3425): Grow heap (frag case) to 5.470MB for 453886-byte allocation
08-29 21:47:34.417: DEBUG/dalvikvm(3425): GC_FOR_MALLOC freed 0K, 47% free 3135K/5831K, external 0K/0K, paused 28ms
08-29 21:47:34.437: VERBOSE/ALANDemo(3425): Closing audio file
08-29 21:47:34.437: VERBOSE/ALANDemo(3425): Initializing Ogg decoder
08-29 21:47:34.453: DEBUG/AudioHardware(115): AudioHardware pcm playback is exiting standby.
08-29 21:47:34.453: VERBOSE/AudioHardware(115): open pcm_out driver
08-29 21:47:34.453: DEBUG/AudioHardware(115): openPcmOut_l() mPcmOpenCnt: 0
08-29 21:47:34.468: VERBOSE/ALANDemo(3425): Creating a source
08-29 21:47:34.468: VERBOSE/ALANDemo(3425): Generating buffer IDs
08-29 21:47:34.468: VERBOSE/ALANDemo(3425): Generating initial stream buffers
08-29 21:47:34.488: VERBOSE/ALANDemo(3425): Queueing up initial stream buffers
08-29 21:47:34.488: VERBOSE/ALANDemo(3425): Setting the source's volume
08-29 21:47:34.488: VERBOSE/ALANDemo(3425): Initiating playback
08-29 21:47:34.488: VERBOSE/ALANDemo(3425): Entering stream loop
08-29 21:47:34.519: VERBOSE/AudioHardware(115): openMixer_l() mMixerOpenCnt: 0
08-29 21:47:34.519: VERBOSE/AudioHardware(115): open playback normal
08-29 21:47:34.519: VERBOSE/AudioHardware(115): write() wakeup setting route SPK
08-29 21:47:34.562: WARN/AudioFlinger(115): write blocked for 109 msecs, 2 delayed writes, thread 0xcb58
08-29 21:47:34.578: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 49K, 47% free 3144K/5831K, external 0K/0K, paused 4ms+2ms
08-29 21:47:34.582: DEBUG/PhotoWall(306): VISIBILITY CHANGED
08-29 21:47:37.050: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 404K, 49% free 3159K/6087K, external 0K/0K, paused 2ms+1ms
08-29 21:47:39.375: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 2ms+2ms
08-29 21:47:41.687: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 3ms+3ms
08-29 21:47:44.011: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 6ms+2ms
08-29 21:47:44.140: WARN/ActivityManager(189): Launch timeout has expired, giving up wake lock!
08-29 21:47:44.246: WARN/ActivityManager(189): Activity idle timeout for HistoryRecord{407cf6c0 paulscode.android.sound.demo/.ALAndroidDemo}
08-29 21:47:46.351: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 6ms+7ms
08-29 21:47:48.652: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 4ms+4ms
08-29 21:47:50.996: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 7ms+2ms
08-29 21:47:53.304: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 2ms+1ms
08-29 21:47:55.605: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 417K, 49% free 3143K/6087K, external 0K/0K, paused 2ms+1ms
08-29 21:47:58.042: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 4ms+4ms
08-29 21:48:00.003: WARN/MakeYourClock(841): Unknown location
08-29 21:48:00.015: WARN/MakeYourClock(841): Unknown location
08-29 21:48:00.054: WARN/MakeYourClock(841): Unknown location
08-29 21:48:00.085: DEBUG/dalvikvm(841): GC_EXTERNAL_ALLOC freed 58K, 49% free 2912K/5639K, external 559K/756K, paused 30ms
08-29 21:48:00.375: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 6ms+2ms
08-29 21:48:02.695: DEBUG/dalvikvm(3425): GC_CONCURRENT freed 401K, 49% free 3159K/6087K, external 0K/0K, paused 2ms+2ms
08-29 21:48:03.218: INFO/InputDispatcher(189): Application is not responding: AppWindowToken{408acb20 token=HistoryRecord{407cf6c0 paulscode.android.sound.demo/.ALAndroidDemo}}.  5002.5ms since event, 5001.1ms since wait started
08-29 21:48:03.218: INFO/WindowManager(189): Input event dispatching timed out sending to application AppWindowToken{408acb20 token=HistoryRecord{407cf6c0 paulscode.android.sound.demo/.ALAndroidDemo}}
08-29 21:48:03.265: INFO/Process(189): Sending signal. PID: 3425 SIG: 3
08-29 21:48:03.265: INFO/dalvikvm(3425): threadid=4: reacting to signal 3
08-29 21:48:03.269: INFO/dalvikvm(3425): Wrote stack traces to '/data/anr/traces.txt'
08-29 21:48:03.273: INFO/Process(189): Sending signal. PID: 189 SIG: 3
08-29 21:48:03.273: INFO/dalvikvm(189): threadid=4: reacting to signal 3
08-29 21:48:03.292: INFO/dalvikvm(189): Wrote stack traces to '/data/anr/traces.txt'
08-29 21:48:03.292: INFO/Process(189): Sending signal. PID: 325 SIG: 3
08-29 21:48:03.292: INFO/dalvikvm(325): threadid=4: reacting to signal 3
08-29 21:48:03.296: INFO/dalvikvm(325): Wrote stack traces to '/data/anr/traces.txt'
08-29 21:48:03.296: INFO/Process(189): Sending signal. PID: 274 SIG: 3
08-29 21:48:03.296: INFO/dalvikvm(274): threadid=4: reacting to signal 3
08-29 21:48:03.300: INFO/dalvikvm(274): Wrote stack traces to '/data/anr/traces.txt'
08-29 21:48:03.500: DEBUG/dalvikvm(189): GC_EXPLICIT freed 887K, 37% free 7307K/11527K, external 3639K/4106K, paused 67ms
08-29 21:48:04.078: ERROR/ActivityManager(189): ANR in paulscode.android.sound.demo (paulscode.android.sound.demo/.ALAndroidDemo)
08-29 21:48:04.078: ERROR/ActivityManager(189): Reason: keyDispatchingTimedOut
08-29 21:48:04.078: ERROR/ActivityManager(189): Load: 1.07 / 1.19 / 0.84
08-29 21:48:04.078: ERROR/ActivityManager(189): CPU usage from 14070ms to 0ms ago:
08-29 21:48:04.078: ERROR/ActivityManager(189):   33% 3425/paulscode.android.sound.demo: 32% user + 1.4% kernel / faults: 334 minor
08-29 21:48:04.078: ERROR/ActivityManager(189):   3.7% 189/system_server: 1.6% user + 2% kernel / faults: 173 minor
08-29 21:48:04.078: ERROR/ActivityManager(189):   2.9% 115/mediaserver: 1.4% user + 1.4% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   1.2% 5/events/0: 0% user + 1.2% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0.8% 274/com.android.systemui: 0.6% user + 0.2% kernel / faults: 3 minor
08-29 21:48:04.078: ERROR/ActivityManager(189):   0.5% 14/kondemand/0: 0% user + 0.5% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0.2% 104/yaffs-bg-1: 0% user + 0.2% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0% 841/net.hubalek.android.makeyourclock: 0% user + 0% kernel / faults: 230 minor
08-29 21:48:04.078: ERROR/ActivityManager(189):   0.2% 31/vsync_workqueue: 0% user + 0.2% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0.2% 54/gp2a_wq: 0% user + 0.2% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0.2% 119/orientationd: 0% user + 0.2% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0% 30/pvr_timer/0: 0% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0% 51/file-storage: 0% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0% 131/pvr_workqueue: 0% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0% 132/adbd: 0% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   0% 325/com.android.phone: 0% user + 0% kernel / faults: 2 minor
08-29 21:48:04.078: ERROR/ActivityManager(189):   0% 3438/logcat: 0% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189): 41% TOTAL: 36% user + 5.6% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189): CPU usage from 289ms to 805ms later:
08-29 21:48:04.078: ERROR/ActivityManager(189):   25% 3425/paulscode.android.sound.demo: 25% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):     16% 3433/roid.sound.demo: 16% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):     7.4% 3425/roid.sound.demo: 7.4% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):     1.8% 3434/AudioTrackThrea: 0% user + 1.8% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   7.5% 189/system_server: 1.8% user + 5.6% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):     3.7% 224/InputDispatcher: 0% user + 3.7% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):     1.8% 197/SurfaceFlinger: 0% user + 1.8% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   1.3% 5/events/0: 0% user + 1.3% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):   1.4% 115/mediaserver: 1.4% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189):     1.4% 187/Playback Thread: 1.4% user + 0% kernel
08-29 21:48:04.078: ERROR/ActivityManager(189): 38% TOTAL: 32% user + 5.7% kernel

paulscode

Thanks, I haven't tested it on any other devices besides my DroidX yet, so that's not a huge surprise to me.

From the log, the problem seems to begin here:

08-29 21:47:34.519: VERBOSE/AudioHardware(115): openMixer_l() mMixerOpenCnt: 0
08-29 21:47:34.519: VERBOSE/AudioHardware(115): open playback normal
08-29 21:47:34.519: VERBOSE/AudioHardware(115): write() wakeup setting route SPK
08-29 21:47:34.562: WARN/AudioFlinger(115): write blocked for 109 msecs, 2 delayed writes, thread 0xcb58


And it then times out, with the following:
08-29 21:48:03.218: INFO/InputDispatcher(189): Application is not responding: AppWindowToken{408acb20 token=HistoryRecord{407cf6c0 paulscode.android.sound.demo/.ALAndroidDemo}}.  5002.5ms since event, 5001.1ms since wait started
08-29 21:48:03.218: INFO/WindowManager(189): Input event dispatching timed out sending to application AppWindowToken{408acb20 token=HistoryRecord{407cf6c0 paulscode.android.sound.demo/.ALAndroidDemo}}
08-29 21:48:03.265: INFO/Process(189): Sending signal. PID: 3425 SIG: 3
08-29 21:48:03.265: INFO/dalvikvm(3425): threadid=4: reacting to signal 3
08-29 21:48:03.269: INFO/dalvikvm(3425): Wrote stack traces to '/data/anr/traces.txt'


The first thing I'd like to rule out is the OS being too "smart" for its own good.  It might just be detecting that the "onCreate" thread is blocking, and shutting down the app.  Let me rewrite the demo a little less "hackish".

If that's not the problem, I'm guessing from where the problem starts that it's something related to the hardware mixer.  Is anything else playing audio at the same time you run the demo?  Try rebooting your phone and then running the demo again to see if it is still silent.

Also, which version of Android are you running on your phone?

The log also indicates that stack traces are being saved to /data/anr/traces.txt.  Could you could upload that output somewhere I could take a look at it?

Thomas.

#6
after reboot something strange is played... my phone is samsung galaxy s with cm7.1 (2.3.5) and here you have traces.txt ... I hope this helps :)

paulscode

Does the strange thing that was played sound like one or more drum beats plus sleigh bells, or clicking, or screeching (or something else)?  I know that seems like an irrelevant question, but it might help me decide where to start focusing my attention.  Each of those would indicate a separate problem:
1) If you heard one or more drum beats, it probably means the sound began playing correctly before the app crashed
2) If you hear clicking, it probably means there was a problem connecting to the audio device
3) If you hear screeching, it probably means a problem processing the audio data (endian issue, etc)

paulscode

Try this version of the demo.  It doesn't block the onCreate thread, so we can at least rule that out as the source of the problem.

Thomas.


paulscode

Oh, good!  I had a feeling it was related to the dumb way I coded that demo app.