Working audio implementation contributed by Joseph Lunderville
authorSam Lantinga <slouken@libsdl.org>
Thu, 13 Jan 2011 11:14:20 -0800
changeset 4995 9f9bea41e88f
parent 4994 e4ed74189d63
child 4996 8d7315668e35
Working audio implementation contributed by Joseph Lunderville
android-project/src/org/libsdl/app/SDLActivity.java
src/SDL_android.cpp
src/SDL_android.h
src/audio/android/SDL_androidaudio.c
src/audio/android/SDL_androidaudio.h
--- a/android-project/src/org/libsdl/app/SDLActivity.java	Thu Jan 13 09:15:51 2011 -0800
+++ b/android-project/src/org/libsdl/app/SDLActivity.java	Thu Jan 13 11:14:20 2011 -0800
@@ -29,11 +29,15 @@
     private static SDLSurface mSurface;
 
     // Audio
+    private static Thread mAudioThread;
     private static AudioTrack mAudioTrack;
 
     // Load the .so
     static {
         System.loadLibrary("SDL");
+        //System.loadLibrary("SDL_image");
+        //System.loadLibrary("SDL_mixer");
+        //System.loadLibrary("SDL_ttf");
         System.loadLibrary("main");
     }
 
@@ -67,12 +71,13 @@
     // C functions we call
     public static native void nativeInit();
     public static native void nativeQuit();
+    public static native void onNativeResize(int x, int y, int format);
     public static native void onNativeKeyDown(int keycode);
     public static native void onNativeKeyUp(int keycode);
     public static native void onNativeTouch(int action, float x, 
                                             float y, float p);
-    public static native void onNativeResize(int x, int y, int format);
     public static native void onNativeAccel(float x, float y, float z);
+    public static native void nativeRunAudioThread();
 
 
     // Java functions called from C
@@ -84,23 +89,83 @@
         mSurface.flipEGL();
     }
 
-    public static void updateAudio(byte [] buf) {
+    // Audio
+    private static Object buf;
+    
+    public static Object audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
+        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
+        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
+        
+        Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+        
+        // Let the user pick a larger buffer if they really want -- but ye
+        // gods they probably shouldn't, the minimums are horrifyingly high
+        // latency already
+        desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
+        
+        mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
+                channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
+        
+        audioStartThread();
+        
+        Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+        
+        if (is16Bit) {
+            buf = new short[desiredFrames * (isStereo ? 2 : 1)];
+        } else {
+            buf = new byte[desiredFrames * (isStereo ? 2 : 1)]; 
+        }
+        return buf;
+    }
     
-        if(mAudioTrack == null) {
-            // Hardcoded things are bad. FIXME when we have more sound stuff
-            // working properly. 
-            mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
-                        11025,
-                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
-                        AudioFormat.ENCODING_PCM_8BIT,
-                        2048,
-                        AudioTrack.MODE_STREAM);   
+    public static void audioStartThread() {
+        mAudioThread = new Thread(new Runnable() {
+            public void run() {
+                mAudioTrack.play();
+                nativeRunAudioThread();
+            }
+        });
+        
+        // I'd take REALTIME if I could get it!
+        mAudioThread.setPriority(Thread.MAX_PRIORITY);
+        mAudioThread.start();
+    }
+    
+    public static void audioWriteShortBuffer(short[] buffer) {
+        for (int i = 0; i < buffer.length; ) {
+            int result = mAudioTrack.write(buffer, i, buffer.length - i);
+            if (result > 0) {
+                i += result;
+            } else if (result == 0) {
+                try {
+                    Thread.sleep(10);
+                } catch(InterruptedException e) {
+                    // Nom nom
+                }
+            } else {
+                Log.w("SDL", "SDL audio: error return from write(short)");
+                return;
+            }
         }
-
-        mAudioTrack.write(buf, 0, buf.length);
-        mAudioTrack.play();
-        
-        Log.v("SDL", "Played some audio");
+    }
+    
+    public static void audioWriteByteBuffer(byte[] buffer) {
+        for (int i = 0; i < buffer.length; ) {
+            int result = mAudioTrack.write(buffer, i, buffer.length - i);
+            if (result > 0) {
+                i += result;
+            } else if (result == 0) {
+                try {
+                    Thread.sleep(10);
+                } catch(InterruptedException e) {
+                    // Nom nom
+                }
+            } else {
+                Log.w("SDL", "SDL audio: error return from write(short)");
+                return;
+            }
+        }
     }
 
 }
@@ -279,7 +344,7 @@
 
         } catch(Exception e) {
             Log.v("SDL", e + "");
-            for(StackTraceElement s : e.getStackTrace()) {
+            for (StackTraceElement s : e.getStackTrace()) {
                 Log.v("SDL", s.toString());
             }
         }
@@ -303,7 +368,7 @@
             
         } catch(Exception e) {
             Log.v("SDL", "flipEGL(): " + e);
-            for(StackTraceElement s : e.getStackTrace()) {
+            for (StackTraceElement s : e.getStackTrace()) {
                 Log.v("SDL", s.toString());
             }
         }
--- a/src/SDL_android.cpp	Thu Jan 13 09:15:51 2011 -0800
+++ b/src/SDL_android.cpp	Thu Jan 13 11:14:20 2011 -0800
@@ -27,7 +27,10 @@
 #include "events/SDL_events_c.h"
 #include "video/android/SDL_androidkeyboard.h"
 #include "video/android/SDL_androidvideo.h"
-}
+
+/* Impelemented in audio/android/SDL_androidaudio.c */
+extern void Android_RunAudioThread();
+} // C
 
 /*******************************************************************************
  This file links the Java side of Android with libsdl
@@ -39,23 +42,21 @@
 /*******************************************************************************
                                Globals
 *******************************************************************************/
-JavaVM* mVM = NULL;
-JNIEnv* mEnv = NULL;
-JNIEnv* mAudioThreadEnv = NULL; //See the note below for why this is necessary
+static JavaVM* mVM = NULL;
+static JNIEnv* mEnv = NULL;
+static JNIEnv* mAudioEnv = NULL;
 
-//Main activity
-jclass mActivityInstance;
+// Main activity
+static jclass mActivityInstance;
 
-//method signatures
-jmethodID midCreateGLContext;
-jmethodID midFlipBuffers;
-jmethodID midUpdateAudio;
+// method signatures
+static jmethodID midCreateGLContext;
+static jmethodID midFlipBuffers;
+static jmethodID midAudioInit;
+static jmethodID midAudioWriteShortBuffer;
+static jmethodID midAudioWriteByteBuffer;
 
-//Feature IDs
-static const int FEATURE_AUDIO = 1;
-static const int FEATURE_ACCEL = 2;
-
-//Accelerometer data storage
+// Accelerometer data storage
 float fLastAccelerometer[3];
 
 
@@ -82,42 +83,42 @@
     mActivityInstance = cls;
     midCreateGLContext = mEnv->GetStaticMethodID(cls,"createGLContext","()V");
     midFlipBuffers = mEnv->GetStaticMethodID(cls,"flipBuffers","()V");
-    midUpdateAudio = mEnv->GetStaticMethodID(cls,"updateAudio","([B)V");
+	midAudioInit = mEnv->GetStaticMethodID(cls, "audioInit", "(IZZI)Ljava/lang/Object;");
+	midAudioWriteShortBuffer = mEnv->GetStaticMethodID(cls, "audioWriteShortBuffer", "([S)V");
+	midAudioWriteByteBuffer = mEnv->GetStaticMethodID(cls, "audioWriteByteBuffer", "([B)V");
 
-    if(!midCreateGLContext || !midFlipBuffers || !midUpdateAudio) {
-        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Bad mids\n");
-    } else {
-#ifdef DEBUG
-        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Good mids\n");
-#endif
+    if(!midCreateGLContext || !midFlipBuffers || !midAudioInit ||
+       !midAudioWriteShortBuffer || !midAudioWriteByteBuffer) {
+		__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL: Couldn't locate Java callbacks, check that they're named and typed correctly");
     }
 }
 
-// Keydown
-extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(JNIEnv* env, 
-               jobject obj, jint keycode)
+// Resize
+extern "C" void Java_org_libsdl_app_SDLActivity_onNativeResize(
+                                    JNIEnv* env, jobject obj,
+                                    jint width, jint height, jint format)
 {
-#ifdef DEBUG
-    __android_log_print(ANDROID_LOG_INFO, "SDL", 
-                        "SDL: native key down %d\n", keycode);
-#endif
+    Android_SetScreenResolution(width, height, format);
+}
+
+// Keydown
+extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyDown(
+                                    JNIEnv* env, jobject obj, jint keycode)
+{
     Android_OnKeyDown(keycode);
 }
 
 // Keyup
-extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyUp(JNIEnv* env, 
-               jobject obj, jint keycode)
+extern "C" void Java_org_libsdl_app_SDLActivity_onNativeKeyUp(
+                                    JNIEnv* env, jobject obj, jint keycode)
 {
-#ifdef DEBUG
-    __android_log_print(ANDROID_LOG_INFO, "SDL", 
-                        "SDL: native key up %d\n", keycode);
-#endif
     Android_OnKeyUp(keycode);
 }
 
 // Touch
-extern "C" void Java_org_libsdl_app_SDLActivity_onNativeTouch(JNIEnv* env, 
-               jobject obj, jint action, jfloat x, jfloat y, jfloat p)
+extern "C" void Java_org_libsdl_app_SDLActivity_onNativeTouch(
+                                    JNIEnv* env, jobject obj,
+                                    jint action, jfloat x, jfloat y, jfloat p)
 {
 #ifdef DEBUG
     __android_log_print(ANDROID_LOG_INFO, "SDL", 
@@ -128,31 +129,30 @@
     //TODO: Pass this off to the SDL multitouch stuff
 }
 
-// Quit
-extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit( JNIEnv*  env, 
-                                                                jobject obj )
-{    
-    // Inject a SDL_QUIT event
-    SDL_SendQuit();
-}
-
-// Resize
-extern "C" void Java_org_libsdl_app_SDLActivity_onNativeResize(
-                                        JNIEnv*  env, jobject obj, jint width, 
-                                        jint height, jint format)
-{
-    Android_SetScreenResolution(width, height, format);
-}
-
+// Accelerometer
 extern "C" void Java_org_libsdl_app_SDLActivity_onNativeAccel(
-                                        JNIEnv*  env, jobject obj,
-                                        jfloat x, jfloat y, jfloat z)
+                                    JNIEnv* env, jobject obj,
+                                    jfloat x, jfloat y, jfloat z)
 {
     fLastAccelerometer[0] = x;
     fLastAccelerometer[1] = y;
     fLastAccelerometer[2] = z;   
 }
 
+// Quit
+extern "C" void Java_org_libsdl_app_SDLActivity_nativeQuit(
+                                    JNIEnv* env, jobject obj)
+{    
+    // Inject a SDL_QUIT event
+    SDL_SendQuit();
+}
+
+extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread(
+                                    JNIEnv* env)
+{
+	mVM->AttachCurrentThread(&mAudioEnv, NULL);
+	Android_RunAudioThread();
+}
 
 
 /*******************************************************************************
@@ -168,33 +168,81 @@
     mEnv->CallStaticVoidMethod(mActivityInstance, midFlipBuffers); 
 }
 
-extern "C" void Android_JNI_UpdateAudioBuffer(unsigned char *buf, int len)
+//
+// Audio support
+//
+static jint audioBufferFrames = 0;
+static bool audioBuffer16Bit = false;
+static bool audioBufferStereo = false;
+
+static jobject audioBuffer;
+static void * audioPinnedBuffer;
+
+extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames)
+{
+	__android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
+	audioBuffer16Bit = is16Bit;
+	audioBufferStereo = channelCount > 1;
+
+	audioBuffer = mEnv->CallStaticObjectMethod(mActivityInstance, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames);
+	audioBuffer = mEnv->NewGlobalRef(audioBuffer);
+
+	if (audioBuffer == NULL) {
+		__android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: didn't get back a good audio buffer!");
+		return 0;
+	}
+
+	if (audioBufferStereo) {
+		audioBufferFrames = mEnv->GetArrayLength((jshortArray)audioBuffer) / 2;
+	} else {
+		audioBufferFrames = mEnv->GetArrayLength((jbyteArray)audioBuffer);
+	}
+
+	return audioBufferFrames;
+}
+
+extern "C" void * Android_JNI_PinAudioBuffer()
 {
-    //Annoyingly we can't just call into Java from any thread. Because the audio
-    //callback is dispatched from the SDL audio thread (that wasn't made from
-    //java, we have to do some magic here to let the JVM know about the thread.
-    //Because everything it touches on the Java side is static anyway, it's 
-    //not a big deal, just annoying.
-    if(!mAudioThreadEnv) {
-        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: Need to set up audio thread env\n");
+	jboolean isCopy = JNI_FALSE;
+
+	if (audioPinnedBuffer != NULL) {
+		return audioPinnedBuffer;
+	}
+
+	if (audioBuffer16Bit) {
+		audioPinnedBuffer = mAudioEnv->GetShortArrayElements((jshortArray)audioBuffer, &isCopy);
+	} else {
+		audioPinnedBuffer = mAudioEnv->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy);
+	}
+
+	return audioPinnedBuffer;
+}
 
-        mVM->AttachCurrentThread(&mAudioThreadEnv, NULL);
+extern "C" void Android_JNI_WriteAudioBufferAndUnpin()
+{
+	if (audioPinnedBuffer == NULL) {
+		return;
+	}
 
-        __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: ok\n");
+	if (audioBuffer16Bit) {
+		mAudioEnv->ReleaseShortArrayElements((jshortArray)audioBuffer, (jshort *)audioPinnedBuffer, JNI_COMMIT);
+		mAudioEnv->CallStaticVoidMethod(mActivityInstance, midAudioWriteShortBuffer, (jshortArray)audioBuffer);
+	} else {
+		mAudioEnv->ReleaseByteArrayElements((jbyteArray)audioBuffer, (jbyte *)audioPinnedBuffer, JNI_COMMIT);
+		mAudioEnv->CallStaticVoidMethod(mActivityInstance, midAudioWriteByteBuffer, (jbyteArray)audioBuffer);
+	}
+
+	audioPinnedBuffer = NULL;
+}
+
+extern "C" void Android_JNI_CloseAudioDevice()
+{
+    if (audioBuffer) {
+        mEnv->DeleteGlobalRef(audioBuffer);
+        audioBuffer = NULL;
     }
-    
-    jbyteArray arr = mAudioThreadEnv->NewByteArray(len);
 
-    //blah. We probably should rework this so we avoid the copy. 
-    mAudioThreadEnv->SetByteArrayRegion(arr, 0, len, (jbyte *)buf);
-    
-    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: copied\n");
-
-    mAudioThreadEnv->CallStaticVoidMethod(  mActivityInstance, 
-                                            midUpdateAudio, arr );
-
-    __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL: invoked\n");
-    
+	// TODO: Implement
 }
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/SDL_android.h	Thu Jan 13 09:15:51 2011 -0800
+++ b/src/SDL_android.h	Thu Jan 13 11:14:20 2011 -0800
@@ -31,7 +31,12 @@
 /* Interface from the SDL library into the Android Java activity */
 void Android_JNI_CreateContext();
 void Android_JNI_SwapWindow();
-void Android_JNI_UpdateAudioBuffer(unsigned char *buf, int len);
+
+// Audio support
+int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames);
+void* Android_JNI_PinAudioBuffer();
+void Android_JNI_WriteAudioBufferAndUnpin();
+void Android_JNI_CloseAudioDevice();
 
 /* Ends C function definitions when using C++ */
 #ifdef __cplusplus
--- a/src/audio/android/SDL_androidaudio.c	Thu Jan 13 09:15:51 2011 -0800
+++ b/src/audio/android/SDL_androidaudio.c	Thu Jan 13 11:14:20 2011 -0800
@@ -18,8 +18,6 @@
 
     Sam Lantinga
     slouken@libsdl.org
-
-    This file written by Ryan C. Gordon (icculus@icculus.org)
 */
 #include "SDL_config.h"
 
@@ -28,18 +26,31 @@
 #include "SDL_audio.h"
 #include "../SDL_audio_c.h"
 #include "SDL_androidaudio.h"
+
 #include "../../SDL_android.h"
 
 #include <android/log.h>
 
+static void * audioDevice;
+
 static int
 AndroidAUD_OpenDevice(_THIS, const char *devname, int iscapture)
 {
-    SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
+    SDL_AudioFormat test_format;
     int valid_datatype = 0;
     
-    //TODO: Sample rates etc
-    __android_log_print(ANDROID_LOG_INFO, "SDL", "AndroidAudio Open\n");
+    if (iscapture) {
+    	//TODO: implement capture
+    	SDL_SetError("Capture not supported on Android");
+    	return 0;
+    }
+
+    if (audioDevice != NULL) {
+    	SDL_SetError("Only one audio device at a time please!");
+    	return 0;
+    }
+
+    audioDevice = this;
 
     this->hidden = SDL_malloc(sizeof(*(this->hidden)));
     if (!this->hidden) {
@@ -48,68 +59,78 @@
     }
     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
 
-    while ((!valid_datatype) && (test_format)) {
-        this->spec.format = test_format;
-        switch (test_format) {
-        case AUDIO_S8:
-            /*case AUDIO_S16LSB: */
-            valid_datatype = 1;
-            break;
-        default:
-            test_format = SDL_NextAudioFormat();
+    test_format = SDL_FirstAudioFormat(this->spec.format);
+    while (test_format != 0) { // no "UNKNOWN" constant
+        if ((test_format == AUDIO_U8) || (test_format == AUDIO_S16LSB)) {
+            this->spec.format = test_format;
             break;
         }
+        test_format = SDL_NextAudioFormat();
     }
     
+    if (test_format == 0) {
+    	// Didn't find a compatible format :(
+    	SDL_SetError("No compatible audio format!");
+    	return 0;
+    }
+
+    if (this->spec.channels > 1) {
+    	this->spec.channels = 2;
+    } else {
+    	this->spec.channels = 1;
+    }
+
+    if (this->spec.freq < 8000) {
+    	this->spec.freq = 8000;
+    }
+    if (this->spec.freq > 48000) {
+    	this->spec.freq = 48000;
+    }
+
+    // TODO: pass in/return a (Java) device ID, also whether we're opening for input or output
+    this->spec.samples = Android_JNI_OpenAudioDevice(this->spec.freq, this->spec.format == AUDIO_U8 ? 0 : 1, this->spec.channels, this->spec.samples);
+    SDL_CalculateAudioSpec(&this->spec);
+
+    if (this->spec.samples == 0) {
+    	// Init failed?
+    	SDL_SetError("Java-side initialization failed!");
+    	return 0;
+    }
+
     return 1;
 }
 
 static void
 AndroidAUD_PlayDevice(_THIS)
 {
-    __android_log_print(ANDROID_LOG_INFO, "SDL", "AndroidAudio Play\n");
-    
-
-    //playGenericSound(this->hidden->mixbuf, this->hidden->mixlen);
-    
-#if 0
-
-//    sound->rate = 22050; /* sample rate = 22050Hz */
-//    sound->vol = 127;    /* volume [0..127] for [min..max] */
-//    sound->pan = 64;     /* balance [0..127] for [left..right] */
-//    sound->format = 0;   /* 0 for 16-bit, 1 for 8-bit */
-//    playSound(sound);
-#endif
+    Android_JNI_WriteAudioBufferAndUnpin();
+    this->hidden->mixbuf = NULL;
 }
 
-
 static Uint8 *
 AndroidAUD_GetDeviceBuf(_THIS)
 {
-     //__android_log_print(ANDROID_LOG_INFO, "SDL", "****** get device buf\n");
-
-     
-    //    sound->data = this->hidden->mixbuf;/* pointer to raw audio data */
-//    sound->len = this->hidden->mixlen; /* size of raw data pointed to above */
-
-
-    Android_JNI_UpdateAudioBuffer(this->hidden->mixbuf, this->hidden->mixlen);
-    
-    return this->hidden->mixbuf;        /* is this right? */
-}
-
-static void
-AndroidAUD_WaitDevice(_THIS)
-{
-    /* stub */
-     __android_log_print(ANDROID_LOG_INFO, "SDL", "****** wait device buf\n");
+	if (this->hidden->mixbuf == NULL) {
+		this->hidden->mixbuf = Android_JNI_PinAudioBuffer();
+	}
+    return this->hidden->mixbuf;
 }
 
 static void
 AndroidAUD_CloseDevice(_THIS)
 {
-    /* stub */
-     __android_log_print(ANDROID_LOG_INFO, "SDL", "****** close device buf\n");
+    if (this->hidden != NULL) {
+    	if (this->hidden->mixbuf != NULL) {
+    		Android_JNI_WriteAudioBufferAndUnpin();
+    	}
+    	SDL_free(this->hidden);
+    	this->hidden = NULL;
+    }
+	Android_JNI_CloseAudioDevice();
+
+    if (audioDevice == this) {
+    	audioDevice = NULL;
+    }
 }
 
 static int
@@ -118,17 +139,15 @@
     /* Set the function pointers */
     impl->OpenDevice = AndroidAUD_OpenDevice;
     impl->PlayDevice = AndroidAUD_PlayDevice;
-    impl->WaitDevice = AndroidAUD_WaitDevice;
     impl->GetDeviceBuf = AndroidAUD_GetDeviceBuf;
     impl->CloseDevice = AndroidAUD_CloseDevice;
 
     /* and the capabilities */
+    impl->ProvidesOwnCallbackThread = 1;
     impl->HasCaptureSupport = 0; //TODO
     impl->OnlyHasDefaultOutputDevice = 1;
     impl->OnlyHasDefaultInputDevice = 1;
 
-    __android_log_print(ANDROID_LOG_INFO, "SDL","Audio init\n");
-
     return 1;   /* this audio target is available. */
 }
 
@@ -136,4 +155,11 @@
     "android", "SDL Android audio driver", AndroidAUD_Init, 0       /*1? */
 };
 
+/* Called by the Java code to start the audio processing on a thread */
+void
+Android_RunAudioThread()
+{
+	SDL_RunAudio(audioDevice);
+}
+
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/audio/android/SDL_androidaudio.h	Thu Jan 13 09:15:51 2011 -0800
+++ b/src/audio/android/SDL_androidaudio.h	Thu Jan 13 11:14:20 2011 -0800
@@ -34,9 +34,8 @@
     /* The file descriptor for the audio device */
     Uint8 *mixbuf;
     Uint32 mixlen;
-    Uint32 write_delay;
-    Uint32 initial_calls;
 };
 
 #endif /* _SDL_androidaudio_h */
+
 /* vi: set ts=4 sw=4 expandtab: */