--- 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: */