From 91333e8bd497d739296f7f7ca58131e6ecee8226 Mon Sep 17 00:00:00 2001 From: pelya Date: Wed, 9 Jun 2010 15:52:14 +0300 Subject: [PATCH] Fixed single-threaded audio implementation. Curent code works with Android 2.1 but fails with 1.6. --- .../project/jni/application/src/mixer.cpp | 3 - .../sdl/src/audio/android/SDL_androidaudio.c | 107 ++++++++++++------ .../sdl/src/video/android/SDL_androidinput.c | 6 +- .../sdl/src/video/android/SDL_androidvideo.c | 23 ++-- alienblaster/project/src/Audio.java | 12 +- 5 files changed, 87 insertions(+), 64 deletions(-) diff --git a/alienblaster/project/jni/application/src/mixer.cpp b/alienblaster/project/jni/application/src/mixer.cpp index 283c9db8a..8f205dcdd 100644 --- a/alienblaster/project/jni/application/src/mixer.cpp +++ b/alienblaster/project/jni/application/src/mixer.cpp @@ -35,9 +35,6 @@ Mixer & Mixer::mixer() } Mixer::Mixer() { - enabled = false; - return; - if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { printf("Couldn't initialize SDL audio subsystem: %s\n", SDL_GetError()); exit(1); diff --git a/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c b/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c index c303403e7..2918de64a 100644 --- a/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c +++ b/alienblaster/project/jni/sdl/src/audio/android/SDL_androidaudio.c @@ -46,6 +46,8 @@ static void ANDROIDAUD_WaitAudio(_THIS); static void ANDROIDAUD_PlayAudio(_THIS); static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS); static void ANDROIDAUD_CloseAudio(_THIS); +static void ANDROIDAUD_ThreadInit(_THIS); +static void ANDROIDAUD_ThreadDeinit(_THIS); /* Audio driver bootstrap functions */ static int ANDROIDAUD_Available(void) @@ -65,6 +67,8 @@ static int ANDROIDAUD_CreateDevice(SDL_AudioDriverImpl * impl) impl->PlayDevice = ANDROIDAUD_PlayAudio; impl->GetDeviceBuf = ANDROIDAUD_GetAudioBuf; impl->CloseDevice = ANDROIDAUD_CloseAudio; + impl->ThreadInit = ANDROIDAUD_ThreadInit; + impl->WaitDone = ANDROIDAUD_ThreadDeinit; impl->Deinitialize = ANDROIDAUD_DeleteDevice; impl->OnlyHasDefaultOutputDevice = 1; @@ -82,7 +86,7 @@ static size_t audioBufferSize = 0; // Extremely wicked JNI environment to call Java functions from C code static jbyteArray audioBufferJNI = NULL; -static JNIEnv * jniEnv = NULL; +static JavaVM *jniVM = NULL; static jclass JavaAudioThreadClass = NULL; static jobject JavaAudioThread = NULL; static jmethodID JavaFillBuffer = NULL; @@ -97,12 +101,18 @@ static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS) static void ANDROIDAUD_CloseAudio(_THIS) { + JNIEnv * jniEnv = NULL; + (*jniVM)->AttachCurrentThread(jniVM, &jniEnv, NULL); + (*jniEnv)->DeleteGlobalRef(jniEnv, audioBufferJNI); audioBufferJNI = NULL; audioBuffer = NULL; audioBufferSize = 0; (*jniEnv)->CallIntMethod( jniEnv, JavaAudioThread, JavaDeinitAudio ); + + /* We cannot call DetachCurrentThread() from main thread or we'll crash */ + /* (*jniVM)->DetachCurrentThread(jniVM); */ if ( this->hidden != NULL ) { SDL_free(this->hidden); @@ -113,13 +123,9 @@ static void ANDROIDAUD_CloseAudio(_THIS) static int ANDROIDAUD_OpenAudio(_THIS, const char *devname, int iscapture) { SDL_AudioSpec *audioFormat = &this->spec; - jintArray initArray = NULL; - int initData[4] = { 0, 0, 0, 0 }; // { rate, channels, encoding, bufsize }; - jobject * bufferObj; jboolean isCopy = JNI_TRUE; - unsigned char *audioBuffer; - int audioBufferSize; int bytesPerSample; + JNIEnv * jniEnv = NULL; this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc((sizeof *this->hidden)); if ( this->hidden == NULL ) { @@ -134,32 +140,40 @@ static int ANDROIDAUD_OpenAudio(_THIS, const char *devname, int iscapture) return (-1); // TODO: enable format conversion? Don't know how to do that in SDL } - - initData[0] = audioFormat->freq; - initData[1] = audioFormat->channels; bytesPerSample = (audioFormat->format & 0xFF) / 8; - initData[2] = ( bytesPerSample == 2 ) ? 1 : 0; audioFormat->format = ( bytesPerSample == 2 ) ? AUDIO_S16 : AUDIO_S8; - initData[3] = audioFormat->size; - initArray = (*jniEnv)->NewIntArray(jniEnv, 4); - (*jniEnv)->SetIntArrayRegion(jniEnv, initArray, 0, 4, (jint *)initData); - bufferObj = (*jniEnv)->CallObjectMethod( jniEnv, JavaAudioThread, JavaInitAudio, initArray ); + (*jniVM)->AttachCurrentThread(jniVM, &jniEnv, NULL); - if( ! bufferObj ) + if( !jniEnv ) + { + __android_log_print(ANDROID_LOG_ERROR, "libSDL", "ANDROIDAUD_OpenAudio: Java VM AttachCurrentThread() failed"); + return (-1); // TODO: enable format conversion? Don't know how to do that in SDL + } + + __android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_OpenAudio(): jniEnv %p JavaInitAudio %p JavaAudioThread %p", + jniEnv, JavaInitAudio, JavaAudioThread); + + audioBufferJNI = (*jniEnv)->CallObjectMethod( jniEnv, JavaAudioThread, JavaInitAudio, + (jint)audioFormat->freq, (jint)audioFormat->channels, + (jint)(( bytesPerSample == 2 ) ? 1 : 0), (jint)audioFormat->size); + + if( ! audioBufferJNI ) { __android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_OpenAudio(): failed to get audio buffer from JNI"); ANDROIDAUD_CloseAudio(this); return(-1); } - audioBufferJNI = (jbyteArray*)(*jniEnv)->NewGlobalRef(jniEnv, bufferObj); + audioBufferJNI = (*jniEnv)->NewGlobalRef(jniEnv, audioBufferJNI); audioBufferSize = (*jniEnv)->GetArrayLength(jniEnv, audioBufferJNI); audioBuffer = (unsigned char *) (*jniEnv)->GetByteArrayElements(jniEnv, audioBufferJNI, &isCopy); if( isCopy == JNI_TRUE ) __android_log_print(ANDROID_LOG_ERROR, "libSDL", "ANDROIDAUD_OpenAudio(): JNI returns a copy of byte array - no audio will be played"); - bytesPerSample = (audioFormat->format & 0xFF) / 8; + /* We cannot call DetachCurrentThread() from main thread or we'll crash */ + /* (*jniVM)->DetachCurrentThread(jniVM); */ + audioFormat->samples = audioBufferSize / bytesPerSample / audioFormat->channels; audioFormat->size = audioBufferSize; SDL_memset(audioBuffer, audioFormat->silence, audioFormat->size); @@ -175,15 +189,29 @@ static void ANDROIDAUD_WaitAudio(_THIS) /* We will block in PlayAudio(), do nothing here */ } +static JNIEnv * jniEnvPlaying = NULL; + +static void ANDROIDAUD_ThreadInit(_THIS) +{ + (*jniVM)->AttachCurrentThread(jniVM, &jniEnvPlaying, NULL); +}; + +static void ANDROIDAUD_ThreadDeinit(_THIS) +{ + (*jniVM)->DetachCurrentThread(jniVM); +}; + static void ANDROIDAUD_PlayAudio(_THIS) { - (*jniEnv)->ReleaseByteArrayElements(jniEnv, audioBufferJNI, (jbyte *)audioBuffer, 0); - audioBuffer == NULL; - - (*jniEnv)->CallIntMethod( jniEnv, JavaAudioThread, JavaDeinitAudio ); - jboolean isCopy = JNI_TRUE; - audioBuffer = (unsigned char *) (*jniEnv)->GetByteArrayElements(jniEnv, audioBufferJNI, &isCopy); + + (*jniEnvPlaying)->ReleaseByteArrayElements(jniEnvPlaying, audioBufferJNI, (jbyte *)audioBuffer, 0); + audioBuffer = NULL; + + (*jniEnvPlaying)->CallIntMethod( jniEnvPlaying, JavaAudioThread, JavaDeinitAudio ); + + audioBuffer = (unsigned char *) (*jniEnvPlaying)->GetByteArrayElements(jniEnvPlaying, audioBufferJNI, &isCopy); + if( isCopy == JNI_TRUE ) __android_log_print(ANDROID_LOG_INFO, "libSDL", "ANDROIDAUD_PlayAudio() JNI returns a copy of byte array - that's slow"); @@ -196,22 +224,27 @@ static void ANDROIDAUD_PlayAudio(_THIS) #define JAVA_EXPORT_NAME1(name,package) JAVA_EXPORT_NAME2(name,package) #define JAVA_EXPORT_NAME(name) JAVA_EXPORT_NAME1(name,SDL_JAVA_PACKAGE_PATH) -extern int JAVA_EXPORT_NAME(AudioThread_nativeAudioInitJavaCallbacks) (JNIEnv * env, jobject thiz) +JNIEXPORT jint JNICALL JAVA_EXPORT_NAME(AudioThread_nativeAudioInitJavaCallbacks) (JNIEnv * jniEnv, jobject thiz) { - __android_log_print(ANDROID_LOG_INFO, "libSDL", "nativeAudioInitJavaCallbacks(): enter"); - jniEnv = env; - JavaAudioThread = thiz; - + JavaAudioThread = (*jniEnv)->NewGlobalRef(jniEnv, thiz); + JavaAudioThreadClass = (*jniEnv)->GetObjectClass(jniEnv, thiz); JavaFillBuffer = (*jniEnv)->GetMethodID(jniEnv, JavaAudioThreadClass, "fillBuffer", "()I"); - JavaInitAudio = (*jniEnv)->GetMethodID(jniEnv, JavaAudioThreadClass, "initAudio", "([I)[B"); + JavaInitAudio = (*jniEnv)->GetMethodID(jniEnv, JavaAudioThreadClass, "initAudio", "(IIII)[B"); JavaDeinitAudio = (*jniEnv)->GetMethodID(jniEnv, JavaAudioThreadClass, "deinitAudio", "()I"); - if( ! JavaFillBuffer ) - __android_log_print(ANDROID_LOG_ERROR, "libSDL", "nativeAudioInitJavaCallbacks(): JavaFillBuffer is NULL"); - if( ! JavaInitAudio ) - __android_log_print(ANDROID_LOG_ERROR, "libSDL", "nativeAudioInitJavaCallbacks(): JavaInitAudio is NULL"); - if( ! JavaInitAudio ) - __android_log_print(ANDROID_LOG_ERROR, "libSDL", "nativeAudioInitJavaCallbacks(): JavaDeinitAudio is NULL"); - - + __android_log_print(ANDROID_LOG_INFO, "libSDL", "nativeAudioInitJavaCallbacks(): JavaAudioThread %p JavaFillBuffer %p JavaInitAudio %p JavaDeinitAudio %p", + JavaAudioThread, JavaFillBuffer, JavaInitAudio, JavaDeinitAudio); } + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) +{ + jniVM = vm; + return JNI_VERSION_1_2; +}; + +JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) +{ + /* TODO: free JavaAudioThread */ + jniVM = vm; +}; + diff --git a/alienblaster/project/jni/sdl/src/video/android/SDL_androidinput.c b/alienblaster/project/jni/sdl/src/video/android/SDL_androidinput.c index b4c053e62..2499c090e 100644 --- a/alienblaster/project/jni/sdl/src/video/android/SDL_androidinput.c +++ b/alienblaster/project/jni/sdl/src/video/android/SDL_androidinput.c @@ -59,7 +59,7 @@ static SDLKey keymap[KEYCODE_LAST+1]; enum MOUSE_ACTION { MOUSE_DOWN = 0, MOUSE_UP=1, MOUSE_MOVE=2 }; -extern void +JNIEXPORT void JNICALL JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeMouse) ( JNIEnv* env, jobject thiz, jint x, jint y, jint action ) { if( action == MOUSE_DOWN || action == MOUSE_UP ) @@ -79,7 +79,7 @@ static SDL_scancode TranslateKey(int scancode) } -void +JNIEXPORT void JNICALL JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeKey) ( JNIEnv* env, jobject thiz, jint key, jint action ) { //if( ! processAndroidTrackballKeyDelays(key, action) ) @@ -88,7 +88,7 @@ JAVA_EXPORT_NAME(DemoGLSurfaceView_nativeKey) ( JNIEnv* env, jobject thiz, jint } -extern void +JNIEXPORT void JNICALL JAVA_EXPORT_NAME(AccelerometerReader_nativeAccelerometer) ( JNIEnv* env, jobject thiz, jfloat accX, jfloat accY, jfloat accZ ) { // TODO: use accelerometer as joystick, make this configurable diff --git a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c index 33f260ceb..d5c127a50 100644 --- a/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c +++ b/alienblaster/project/jni/sdl/src/video/android/SDL_androidvideo.c @@ -67,11 +67,8 @@ static jclass JavaRendererClass = NULL; static jobject JavaRenderer = NULL; static jmethodID JavaSwapBuffers = NULL; - -static int CallJavaSwapBuffers(); static void SdlGlRenderInit(); - /* ANDROID driver bootstrap functions */ static int ANDROID_Available(void) @@ -193,6 +190,11 @@ void ANDROID_PumpEvents(_THIS) { } +static inline int CallJavaSwapBuffers() +{ + return (*JavaEnv)->CallIntMethod( JavaEnv, JavaRenderer, JavaSwapBuffers ); +} + void ANDROID_GL_SwapBuffers(_THIS, SDL_Window * window) { CallJavaSwapBuffers(); @@ -219,7 +221,7 @@ void ANDROID_GL_DeleteContext (_THIS, SDL_GLContext context) #define JAVA_EXPORT_NAME1(name,package) JAVA_EXPORT_NAME2(name,package) #define JAVA_EXPORT_NAME(name) JAVA_EXPORT_NAME1(name,SDL_JAVA_PACKAGE_PATH) -extern void +JNIEXPORT void JNICALL JAVA_EXPORT_NAME(DemoRenderer_nativeResize) ( JNIEnv* env, jobject thiz, jint w, jint h ) { sWindowWidth = w; @@ -227,15 +229,15 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeResize) ( JNIEnv* env, jobject thiz, jint __android_log_print(ANDROID_LOG_INFO, "libSDL", "Physical screen resolution is %dx%d", w, h); } -/* Call to finalize the graphics state */ -extern void +JNIEXPORT void JNICALL JAVA_EXPORT_NAME(DemoRenderer_nativeDone) ( JNIEnv* env, jobject thiz ) { __android_log_print(ANDROID_LOG_INFO, "libSDL", "quitting..."); SDL_SendQuit(); __android_log_print(ANDROID_LOG_INFO, "libSDL", "quit OK"); } -void + +JNIEXPORT void JNICALL JAVA_EXPORT_NAME(DemoRenderer_nativeInitJavaCallbacks) ( JNIEnv* env, jobject thiz ) { JavaEnv = env; @@ -247,10 +249,3 @@ JAVA_EXPORT_NAME(DemoRenderer_nativeInitJavaCallbacks) ( JNIEnv* env, jobject t ANDROID_InitOSKeymap(); } - -int CallJavaSwapBuffers() -{ - return (*JavaEnv)->CallIntMethod( JavaEnv, JavaRenderer, JavaSwapBuffers ); -} - - diff --git a/alienblaster/project/src/Audio.java b/alienblaster/project/src/Audio.java index 397842867..080d57e5f 100644 --- a/alienblaster/project/src/Audio.java +++ b/alienblaster/project/src/Audio.java @@ -37,20 +37,18 @@ class AudioThread { return 1; } - public byte[] initAudio(int[] initParams) + public byte[] initAudio(int rate, int channels, int encoding, int bufSize) { if( mAudio == null ) { - int rate = initParams[0]; - int channels = initParams[1]; channels = ( channels == 1 ) ? AudioFormat.CHANNEL_CONFIGURATION_MONO : AudioFormat.CHANNEL_CONFIGURATION_STEREO; - int encoding = initParams[2]; encoding = ( encoding == 1 ) ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; - int bufSize = AudioTrack.getMinBufferSize( rate, channels, encoding ); - if( initParams[3] > bufSize ) - bufSize = initParams[3]; + + if( AudioTrack.getMinBufferSize( rate, channels, encoding ) > bufSize ) + bufSize = AudioTrack.getMinBufferSize( rate, channels, encoding ); + mAudioBuffer = new byte[bufSize]; mAudio = new AudioTrack(AudioManager.STREAM_MUSIC, rate,