Fixed single-threaded audio implementation. Curent code works with Android 2.1 but fails with 1.6.

This commit is contained in:
pelya
2010-06-09 15:52:14 +03:00
parent 3aa634d175
commit 91333e8bd4
5 changed files with 87 additions and 64 deletions

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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 );
}

View File

@@ -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,