Small fix for crashing, made audio to use Java byte array instead of copying bytes,
and made it to use array size returned from Java - seems to add latency to the audio
This commit is contained in:
@@ -22,6 +22,9 @@ using namespace std;
|
||||
#include "surfaceDB.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#ifdef ANDROID
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
SurfaceDB surfaceDB;
|
||||
|
||||
@@ -56,6 +59,14 @@ SDL_Surface *SurfaceDB::loadSurface( string fn, bool alpha ) {
|
||||
}
|
||||
|
||||
SDL_Surface *newSurface = SDL_LoadBMP( fn.c_str() );
|
||||
if( newSurface == NULL )
|
||||
{
|
||||
cout << "ERROR: Cannot load image " << fn << endl;
|
||||
#ifdef ANDROID
|
||||
__android_log_print(ANDROID_LOG_ERROR, "Alien Blaster", (string( "Cannot load image " ) + fn).c_str() );
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
SDL_SetColorKey( newSurface, SDL_SRCCOLORKEY,
|
||||
SDL_MapRGB(newSurface->format, transR, transG, transB) );
|
||||
if ( alpha ) {
|
||||
|
||||
@@ -98,16 +98,14 @@ AudioBootStrap ANDROIDAUD_bootstrap = {
|
||||
|
||||
static SDL_mutex * audioMutex = NULL;
|
||||
static SDL_cond * audioCond = NULL;
|
||||
static SDL_cond * audioCond2 = NULL;
|
||||
static unsigned char * audioBuffer = NULL;
|
||||
static size_t audioBufferSize = 0;
|
||||
static SDL_AudioSpec *audioFormat = NULL;
|
||||
static int audioInitialized = 0;
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void ANDROIDAUD_WaitAudio(_THIS)
|
||||
{
|
||||
/* We will block in PlayAudio(), do nothing here */
|
||||
}
|
||||
static int audioPlayed = 0;
|
||||
static jbyteArray audioBufferJNI = NULL;
|
||||
static JNIEnv * jniEnv = NULL;
|
||||
|
||||
static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS)
|
||||
{
|
||||
@@ -117,25 +115,38 @@ static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS)
|
||||
static void ANDROIDAUD_CloseAudio(_THIS)
|
||||
{
|
||||
SDL_mutex * audioMutex1;
|
||||
|
||||
|
||||
/*
|
||||
if ( this->hidden->mixbuf != NULL ) {
|
||||
SDL_FreeAudioMem(this->hidden->mixbuf);
|
||||
this->hidden->mixbuf = NULL;
|
||||
}
|
||||
*/
|
||||
if( audioMutex != NULL )
|
||||
{
|
||||
audioMutex1 = audioMutex;
|
||||
SDL_mutexP(audioMutex1);
|
||||
audioInitialized = 0;
|
||||
SDL_CondSignal(audioCond);
|
||||
SDL_CondSignal(audioCond2);
|
||||
audioMutex = NULL;
|
||||
SDL_DestroyCond(audioCond);
|
||||
SDL_DestroyCond(audioCond2);
|
||||
audioCond = NULL;
|
||||
audioCond2 = NULL;
|
||||
audioFormat = NULL;
|
||||
// TODO: this crashes JNI, so we're just memleaking it
|
||||
/*
|
||||
(*jniEnv)->ReleaseByteArrayElements(jniEnv, audioBufferJNI, (jbyte *)audioBuffer, 0);
|
||||
(*jniEnv)->DeleteGlobalRef(jniEnv, audioBufferJNI);
|
||||
*/
|
||||
jniEnv = NULL;
|
||||
audioBufferJNI = NULL;
|
||||
audioBuffer = NULL;
|
||||
audioBufferSize = 0;
|
||||
SDL_mutexV(audioMutex1);
|
||||
SDL_DestroyMutex(audioMutex1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,23 +154,15 @@ static int ANDROIDAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
||||
{
|
||||
if( ! (spec->format == AUDIO_S8 || spec->format == AUDIO_S16) )
|
||||
return (-1); // TODO: enable format conversion? Don't know how to do that in SDL
|
||||
|
||||
/* Allocate mixing buffer */
|
||||
this->hidden->mixlen = spec->size;
|
||||
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
|
||||
if ( this->hidden->mixbuf == NULL ) {
|
||||
return(-1);
|
||||
}
|
||||
SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
|
||||
|
||||
|
||||
if( audioMutex == NULL )
|
||||
{
|
||||
audioInitialized = 0;
|
||||
audioFormat = spec;
|
||||
audioBuffer = this->hidden->mixbuf;
|
||||
audioBufferSize = this->hidden->mixlen;
|
||||
audioMutex = SDL_CreateMutex();
|
||||
audioCond = SDL_CreateCond();
|
||||
audioCond2 = SDL_CreateCond();
|
||||
audioPlayed == 0;
|
||||
}
|
||||
|
||||
SDL_mutexP(audioMutex);
|
||||
@@ -175,6 +178,9 @@ static int ANDROIDAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
||||
}
|
||||
}
|
||||
|
||||
this->hidden->mixbuf = audioBuffer;
|
||||
this->hidden->mixlen = audioBufferSize;
|
||||
|
||||
audioFormat = NULL;
|
||||
|
||||
SDL_mutexV(audioMutex);
|
||||
@@ -182,16 +188,26 @@ static int ANDROIDAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* This function waits until it is possible to write a full sound buffer */
|
||||
static void ANDROIDAUD_WaitAudio(_THIS)
|
||||
{
|
||||
/* We will block in PlayAudio(), do nothing here */
|
||||
}
|
||||
|
||||
static void ANDROIDAUD_PlayAudio(_THIS)
|
||||
{
|
||||
SDL_mutexP(audioMutex);
|
||||
|
||||
audioBuffer = this->hidden->mixbuf;
|
||||
audioBufferSize = this->hidden->mixlen;
|
||||
|
||||
while( audioBuffer != NULL )
|
||||
SDL_CondWait( audioCond, audioMutex );
|
||||
//audioBuffer = this->hidden->mixbuf;
|
||||
//audioBufferSize = this->hidden->mixlen;
|
||||
|
||||
audioPlayed = 1;
|
||||
|
||||
SDL_CondSignal(audioCond2);
|
||||
SDL_CondWaitTimeout( audioCond, audioMutex, 1000 );
|
||||
|
||||
this->hidden->mixbuf = audioBuffer;
|
||||
|
||||
SDL_mutexV(audioMutex);
|
||||
}
|
||||
|
||||
@@ -216,12 +232,12 @@ extern jintArray JAVA_EXPORT_NAME(AudioThread_nativeAudioInit) (JNIEnv * env, jo
|
||||
{
|
||||
initData[0] = audioFormat->freq;
|
||||
initData[1] = audioFormat->channels;
|
||||
initData[2] = ( audioFormat->format == AUDIO_S16 ) ? 1 : 0;
|
||||
int bytesPerSample = (audioFormat->format & 0xFF) / 8;
|
||||
initData[2] = ( bytesPerSample == 2 ) ? 1 : 0;
|
||||
audioFormat->format = ( bytesPerSample == 2 ) ? AUDIO_S16 : AUDIO_S8;
|
||||
initData[3] = audioFormat->size;
|
||||
ret=(*env)->NewIntArray(env, 4);
|
||||
(*env)->SetIntArrayRegion(env, ret, 0, 4, (jint *)initData);
|
||||
audioInitialized = 1;
|
||||
SDL_CondSignal(audioCond);
|
||||
}
|
||||
|
||||
SDL_mutexV(audioMutex);
|
||||
@@ -229,33 +245,100 @@ extern jintArray JAVA_EXPORT_NAME(AudioThread_nativeAudioInit) (JNIEnv * env, jo
|
||||
return (ret);
|
||||
};
|
||||
|
||||
extern jint JAVA_EXPORT_NAME(AudioThread_nativeAudioBuffer) ( JNIEnv * env, jobject jobj, jbyteArray data )
|
||||
extern jobject JAVA_EXPORT_NAME(AudioThread_nativeAudioInit2) (JNIEnv * env, jobject jobj, jbyteArray buf)
|
||||
{
|
||||
jobject ret = NULL;
|
||||
|
||||
if( audioMutex == NULL )
|
||||
return;
|
||||
|
||||
SDL_mutexP(audioMutex);
|
||||
|
||||
if( audioInitialized == 0 )
|
||||
{
|
||||
/* Allocate mixing buffer */
|
||||
audioBufferJNI = (jbyteArray*)(*env)->NewGlobalRef(env, buf);
|
||||
audioBufferSize = (*env)->GetArrayLength(env, audioBufferJNI);
|
||||
jboolean isCopy = JNI_TRUE;
|
||||
audioBuffer = (unsigned char *) (*env)->GetByteArrayElements(env, audioBufferJNI, &isCopy);
|
||||
if( isCopy == JNI_TRUE )
|
||||
__android_log_print(ANDROID_LOG_ERROR, "libSDL", "AudioThread_nativeAudioInit2() JNI returns a copy of byte array - no audio will be played");
|
||||
|
||||
jniEnv = env;
|
||||
|
||||
int bytesPerSample = (audioFormat->format & 0xFF) / 8;
|
||||
audioFormat->samples = audioBufferSize / bytesPerSample / audioFormat->channels;
|
||||
audioFormat->size = audioBufferSize;
|
||||
SDL_memset(audioBuffer, audioFormat->silence, audioFormat->size);
|
||||
char t[512];
|
||||
//sprintf(t, "AudioThread_nativeAudioInit2() got byte array from JNI: size %i samples %i direct memory %i", audioBufferSize, audioFormat->samples, (isCopy == JNI_FALSE) );
|
||||
__android_log_print(ANDROID_LOG_INFO, "libSDL", t);
|
||||
|
||||
/*
|
||||
audioBuffer = (Uint8 *) SDL_AllocAudioMem(audioBufferSize);
|
||||
if ( audioBuffer == NULL ) {
|
||||
SDL_mutexV(audioMutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = (*env)->NewDirectByteBuffer(env, audioBuffer, audioBufferSize);
|
||||
*/
|
||||
|
||||
audioInitialized = 1;
|
||||
SDL_CondSignal(audioCond);
|
||||
}
|
||||
|
||||
SDL_mutexV(audioMutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern jint JAVA_EXPORT_NAME(AudioThread_nativeAudioBufferLock) ( JNIEnv * env, jobject jobj )
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if( audioMutex == NULL )
|
||||
return;
|
||||
return(-1);
|
||||
|
||||
SDL_mutexP(audioMutex);
|
||||
|
||||
if( !audioInitialized )
|
||||
ret = -1;
|
||||
|
||||
if( audioBuffer == NULL )
|
||||
{
|
||||
ret = 0;
|
||||
SDL_mutexV(audioMutex);
|
||||
SDL_CondSignal(audioCond);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if( audioPlayed == 0 )
|
||||
SDL_CondWaitTimeout(audioCond2, audioMutex, 1000);
|
||||
|
||||
if( audioBuffer == NULL ) // Should not happen
|
||||
ret = 0;
|
||||
else
|
||||
{
|
||||
(*env)->SetByteArrayRegion(env, data, 0, audioBufferSize, (jbyte *)audioBuffer);
|
||||
(*jniEnv)->ReleaseByteArrayElements(jniEnv, audioBufferJNI, (jbyte *)audioBuffer, 0);
|
||||
audioBuffer == NULL;
|
||||
ret = audioBufferSize;
|
||||
audioBuffer = NULL;
|
||||
audioBufferSize = 0;
|
||||
SDL_CondSignal(audioCond);
|
||||
}
|
||||
|
||||
SDL_mutexV(audioMutex);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
extern jint JAVA_EXPORT_NAME(AudioThread_nativeAudioBufferUnlock) ( JNIEnv * env, jobject jobj )
|
||||
{
|
||||
if( audioMutex == NULL )
|
||||
return(-1);
|
||||
|
||||
jboolean isCopy = JNI_TRUE;
|
||||
audioBuffer = (unsigned char *) (*env)->GetByteArrayElements(env, audioBufferJNI, &isCopy);
|
||||
if( isCopy == JNI_TRUE )
|
||||
__android_log_print(ANDROID_LOG_INFO, "libSDL", "AudioThread_nativeAudioBufferUnlock() JNI returns a copy of byte array - that's slow");
|
||||
|
||||
audioPlayed = 0;
|
||||
|
||||
SDL_mutexV(audioMutex);
|
||||
SDL_CondSignal(audioCond);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ import org.apache.http.impl.*;
|
||||
import org.apache.http.impl.client.*;
|
||||
import java.util.zip.*;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
||||
// TODO: export vibrator to SDL - interface is available in SDL 1.3
|
||||
@@ -164,13 +165,14 @@ class AudioThread extends Thread {
|
||||
private Activity mParent;
|
||||
private AudioTrack mAudio;
|
||||
private byte[] mAudioBuffer;
|
||||
private ByteBuffer mAudioBufferNative;
|
||||
|
||||
public AudioThread(Activity parent)
|
||||
{
|
||||
android.util.Log.i("SDL Java", "AudioThread created");
|
||||
mParent = parent;
|
||||
mAudio = null;
|
||||
mAudioBuffer = null;
|
||||
this.setPriority(Thread.MAX_PRIORITY);
|
||||
this.start();
|
||||
}
|
||||
|
||||
@@ -197,10 +199,11 @@ class AudioThread extends Thread {
|
||||
int encoding = initParams[2];
|
||||
encoding = ( encoding == 1 ) ? AudioFormat.ENCODING_PCM_16BIT :
|
||||
AudioFormat.ENCODING_PCM_8BIT;
|
||||
int bufSize = AudioTrack.getMinBufferSize( rate, channels, encoding);
|
||||
int bufSize = AudioTrack.getMinBufferSize( rate, channels, encoding );
|
||||
if( initParams[3] > bufSize )
|
||||
bufSize = initParams[3];
|
||||
mAudioBuffer = new byte[bufSize];
|
||||
nativeAudioInit2(mAudioBuffer);
|
||||
mAudio = new AudioTrack(AudioManager.STREAM_MUSIC,
|
||||
rate,
|
||||
channels,
|
||||
@@ -212,19 +215,26 @@ class AudioThread extends Thread {
|
||||
}
|
||||
else
|
||||
{
|
||||
int len = nativeAudioBuffer( mAudioBuffer );
|
||||
int len = nativeAudioBufferLock();
|
||||
if( len > 0 )
|
||||
mAudio.write( mAudioBuffer, 0, len );
|
||||
if( len < 0 )
|
||||
break;
|
||||
nativeAudioBufferUnlock();
|
||||
}
|
||||
}
|
||||
if( mAudio != null )
|
||||
{
|
||||
mAudio.stop();
|
||||
mAudio.release();
|
||||
mAudio = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static native int[] nativeAudioInit();
|
||||
private static native int nativeAudioBuffer( byte[] data );
|
||||
private static native int nativeAudioInit2(byte[] buf);
|
||||
private static native int nativeAudioBufferLock();
|
||||
private static native int nativeAudioBufferUnlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -313,7 +323,7 @@ public class DemoActivity extends Activity {
|
||||
|
||||
|
||||
|
||||
class DataDownloader extends Thread
|
||||
class DataDownloader extends Thread
|
||||
{
|
||||
class StatusWriter
|
||||
{
|
||||
@@ -365,9 +375,9 @@ public class DemoActivity extends Activity {
|
||||
} catch( SecurityException e ) { };
|
||||
if( checkFile != null )
|
||||
{
|
||||
Status.setText( "Already downloaded" );
|
||||
Status.setText( "No need to download" );
|
||||
DownloadComplete = true;
|
||||
Parent.initSDL();
|
||||
initParent();
|
||||
return;
|
||||
}
|
||||
checkFile = null;
|
||||
@@ -487,6 +497,11 @@ public class DemoActivity extends Activity {
|
||||
Status.setText( "Finished" );
|
||||
DownloadComplete = true;
|
||||
|
||||
initParent();
|
||||
};
|
||||
|
||||
private void initParent()
|
||||
{
|
||||
class Callback implements Runnable
|
||||
{
|
||||
public DemoActivity Parent;
|
||||
@@ -498,7 +513,7 @@ public class DemoActivity extends Activity {
|
||||
Callback cb = new Callback();
|
||||
cb.Parent = Parent;
|
||||
Parent.runOnUiThread(cb);
|
||||
};
|
||||
}
|
||||
|
||||
private String getOutFilePath(final String filename)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user