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 "surfaceDB.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#ifdef ANDROID
|
||||||
|
#include <android/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
SurfaceDB surfaceDB;
|
SurfaceDB surfaceDB;
|
||||||
|
|
||||||
@@ -56,6 +59,14 @@ SDL_Surface *SurfaceDB::loadSurface( string fn, bool alpha ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SDL_Surface *newSurface = SDL_LoadBMP( fn.c_str() );
|
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_SetColorKey( newSurface, SDL_SRCCOLORKEY,
|
||||||
SDL_MapRGB(newSurface->format, transR, transG, transB) );
|
SDL_MapRGB(newSurface->format, transR, transG, transB) );
|
||||||
if ( alpha ) {
|
if ( alpha ) {
|
||||||
|
|||||||
@@ -98,16 +98,14 @@ AudioBootStrap ANDROIDAUD_bootstrap = {
|
|||||||
|
|
||||||
static SDL_mutex * audioMutex = NULL;
|
static SDL_mutex * audioMutex = NULL;
|
||||||
static SDL_cond * audioCond = NULL;
|
static SDL_cond * audioCond = NULL;
|
||||||
|
static SDL_cond * audioCond2 = NULL;
|
||||||
static unsigned char * audioBuffer = NULL;
|
static unsigned char * audioBuffer = NULL;
|
||||||
static size_t audioBufferSize = 0;
|
static size_t audioBufferSize = 0;
|
||||||
static SDL_AudioSpec *audioFormat = NULL;
|
static SDL_AudioSpec *audioFormat = NULL;
|
||||||
static int audioInitialized = 0;
|
static int audioInitialized = 0;
|
||||||
|
static int audioPlayed = 0;
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
static jbyteArray audioBufferJNI = NULL;
|
||||||
static void ANDROIDAUD_WaitAudio(_THIS)
|
static JNIEnv * jniEnv = NULL;
|
||||||
{
|
|
||||||
/* We will block in PlayAudio(), do nothing here */
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS)
|
static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS)
|
||||||
{
|
{
|
||||||
@@ -117,25 +115,38 @@ static Uint8 *ANDROIDAUD_GetAudioBuf(_THIS)
|
|||||||
static void ANDROIDAUD_CloseAudio(_THIS)
|
static void ANDROIDAUD_CloseAudio(_THIS)
|
||||||
{
|
{
|
||||||
SDL_mutex * audioMutex1;
|
SDL_mutex * audioMutex1;
|
||||||
|
|
||||||
|
/*
|
||||||
if ( this->hidden->mixbuf != NULL ) {
|
if ( this->hidden->mixbuf != NULL ) {
|
||||||
SDL_FreeAudioMem(this->hidden->mixbuf);
|
SDL_FreeAudioMem(this->hidden->mixbuf);
|
||||||
this->hidden->mixbuf = NULL;
|
this->hidden->mixbuf = NULL;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
if( audioMutex != NULL )
|
if( audioMutex != NULL )
|
||||||
{
|
{
|
||||||
audioMutex1 = audioMutex;
|
audioMutex1 = audioMutex;
|
||||||
SDL_mutexP(audioMutex1);
|
SDL_mutexP(audioMutex1);
|
||||||
audioInitialized = 0;
|
audioInitialized = 0;
|
||||||
SDL_CondSignal(audioCond);
|
SDL_CondSignal(audioCond);
|
||||||
|
SDL_CondSignal(audioCond2);
|
||||||
audioMutex = NULL;
|
audioMutex = NULL;
|
||||||
SDL_DestroyCond(audioCond);
|
SDL_DestroyCond(audioCond);
|
||||||
|
SDL_DestroyCond(audioCond2);
|
||||||
audioCond = NULL;
|
audioCond = NULL;
|
||||||
|
audioCond2 = NULL;
|
||||||
audioFormat = 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;
|
audioBuffer = NULL;
|
||||||
audioBufferSize = 0;
|
audioBufferSize = 0;
|
||||||
SDL_mutexV(audioMutex1);
|
SDL_mutexV(audioMutex1);
|
||||||
SDL_DestroyMutex(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) )
|
if( ! (spec->format == AUDIO_S8 || spec->format == AUDIO_S16) )
|
||||||
return (-1); // TODO: enable format conversion? Don't know how to do that in SDL
|
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 )
|
if( audioMutex == NULL )
|
||||||
{
|
{
|
||||||
audioInitialized = 0;
|
audioInitialized = 0;
|
||||||
audioFormat = spec;
|
audioFormat = spec;
|
||||||
audioBuffer = this->hidden->mixbuf;
|
|
||||||
audioBufferSize = this->hidden->mixlen;
|
|
||||||
audioMutex = SDL_CreateMutex();
|
audioMutex = SDL_CreateMutex();
|
||||||
audioCond = SDL_CreateCond();
|
audioCond = SDL_CreateCond();
|
||||||
|
audioCond2 = SDL_CreateCond();
|
||||||
|
audioPlayed == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_mutexP(audioMutex);
|
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;
|
audioFormat = NULL;
|
||||||
|
|
||||||
SDL_mutexV(audioMutex);
|
SDL_mutexV(audioMutex);
|
||||||
@@ -182,16 +188,26 @@ static int ANDROIDAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|||||||
return(0);
|
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)
|
static void ANDROIDAUD_PlayAudio(_THIS)
|
||||||
{
|
{
|
||||||
SDL_mutexP(audioMutex);
|
SDL_mutexP(audioMutex);
|
||||||
|
|
||||||
audioBuffer = this->hidden->mixbuf;
|
//audioBuffer = this->hidden->mixbuf;
|
||||||
audioBufferSize = this->hidden->mixlen;
|
//audioBufferSize = this->hidden->mixlen;
|
||||||
|
|
||||||
while( audioBuffer != NULL )
|
|
||||||
SDL_CondWait( audioCond, audioMutex );
|
|
||||||
|
|
||||||
|
audioPlayed = 1;
|
||||||
|
|
||||||
|
SDL_CondSignal(audioCond2);
|
||||||
|
SDL_CondWaitTimeout( audioCond, audioMutex, 1000 );
|
||||||
|
|
||||||
|
this->hidden->mixbuf = audioBuffer;
|
||||||
|
|
||||||
SDL_mutexV(audioMutex);
|
SDL_mutexV(audioMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,12 +232,12 @@ extern jintArray JAVA_EXPORT_NAME(AudioThread_nativeAudioInit) (JNIEnv * env, jo
|
|||||||
{
|
{
|
||||||
initData[0] = audioFormat->freq;
|
initData[0] = audioFormat->freq;
|
||||||
initData[1] = audioFormat->channels;
|
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;
|
initData[3] = audioFormat->size;
|
||||||
ret=(*env)->NewIntArray(env, 4);
|
ret=(*env)->NewIntArray(env, 4);
|
||||||
(*env)->SetIntArrayRegion(env, ret, 0, 4, (jint *)initData);
|
(*env)->SetIntArrayRegion(env, ret, 0, 4, (jint *)initData);
|
||||||
audioInitialized = 1;
|
|
||||||
SDL_CondSignal(audioCond);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_mutexV(audioMutex);
|
SDL_mutexV(audioMutex);
|
||||||
@@ -229,33 +245,100 @@ extern jintArray JAVA_EXPORT_NAME(AudioThread_nativeAudioInit) (JNIEnv * env, jo
|
|||||||
return (ret);
|
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;
|
int ret = 0;
|
||||||
|
|
||||||
if( audioMutex == NULL )
|
if( audioMutex == NULL )
|
||||||
return;
|
return(-1);
|
||||||
|
|
||||||
SDL_mutexP(audioMutex);
|
SDL_mutexP(audioMutex);
|
||||||
|
|
||||||
if( !audioInitialized )
|
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
|
else
|
||||||
{
|
{
|
||||||
(*env)->SetByteArrayRegion(env, data, 0, audioBufferSize, (jbyte *)audioBuffer);
|
(*jniEnv)->ReleaseByteArrayElements(jniEnv, audioBufferJNI, (jbyte *)audioBuffer, 0);
|
||||||
|
audioBuffer == NULL;
|
||||||
ret = audioBufferSize;
|
ret = audioBufferSize;
|
||||||
audioBuffer = NULL;
|
|
||||||
audioBufferSize = 0;
|
|
||||||
SDL_CondSignal(audioCond);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_mutexV(audioMutex);
|
|
||||||
|
|
||||||
return ret;
|
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 org.apache.http.impl.client.*;
|
||||||
import java.util.zip.*;
|
import java.util.zip.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
|
||||||
// TODO: export vibrator to SDL - interface is available in SDL 1.3
|
// TODO: export vibrator to SDL - interface is available in SDL 1.3
|
||||||
@@ -164,13 +165,14 @@ class AudioThread extends Thread {
|
|||||||
private Activity mParent;
|
private Activity mParent;
|
||||||
private AudioTrack mAudio;
|
private AudioTrack mAudio;
|
||||||
private byte[] mAudioBuffer;
|
private byte[] mAudioBuffer;
|
||||||
|
private ByteBuffer mAudioBufferNative;
|
||||||
|
|
||||||
public AudioThread(Activity parent)
|
public AudioThread(Activity parent)
|
||||||
{
|
{
|
||||||
android.util.Log.i("SDL Java", "AudioThread created");
|
|
||||||
mParent = parent;
|
mParent = parent;
|
||||||
mAudio = null;
|
mAudio = null;
|
||||||
mAudioBuffer = null;
|
mAudioBuffer = null;
|
||||||
|
this.setPriority(Thread.MAX_PRIORITY);
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,10 +199,11 @@ class AudioThread extends Thread {
|
|||||||
int encoding = initParams[2];
|
int encoding = initParams[2];
|
||||||
encoding = ( encoding == 1 ) ? AudioFormat.ENCODING_PCM_16BIT :
|
encoding = ( encoding == 1 ) ? AudioFormat.ENCODING_PCM_16BIT :
|
||||||
AudioFormat.ENCODING_PCM_8BIT;
|
AudioFormat.ENCODING_PCM_8BIT;
|
||||||
int bufSize = AudioTrack.getMinBufferSize( rate, channels, encoding);
|
int bufSize = AudioTrack.getMinBufferSize( rate, channels, encoding );
|
||||||
if( initParams[3] > bufSize )
|
if( initParams[3] > bufSize )
|
||||||
bufSize = initParams[3];
|
bufSize = initParams[3];
|
||||||
mAudioBuffer = new byte[bufSize];
|
mAudioBuffer = new byte[bufSize];
|
||||||
|
nativeAudioInit2(mAudioBuffer);
|
||||||
mAudio = new AudioTrack(AudioManager.STREAM_MUSIC,
|
mAudio = new AudioTrack(AudioManager.STREAM_MUSIC,
|
||||||
rate,
|
rate,
|
||||||
channels,
|
channels,
|
||||||
@@ -212,19 +215,26 @@ class AudioThread extends Thread {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int len = nativeAudioBuffer( mAudioBuffer );
|
int len = nativeAudioBufferLock();
|
||||||
if( len > 0 )
|
if( len > 0 )
|
||||||
mAudio.write( mAudioBuffer, 0, len );
|
mAudio.write( mAudioBuffer, 0, len );
|
||||||
if( len < 0 )
|
if( len < 0 )
|
||||||
break;
|
break;
|
||||||
|
nativeAudioBufferUnlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if( mAudio != null )
|
if( mAudio != null )
|
||||||
|
{
|
||||||
mAudio.stop();
|
mAudio.stop();
|
||||||
|
mAudio.release();
|
||||||
|
mAudio = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native int[] nativeAudioInit();
|
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
|
class StatusWriter
|
||||||
{
|
{
|
||||||
@@ -365,9 +375,9 @@ public class DemoActivity extends Activity {
|
|||||||
} catch( SecurityException e ) { };
|
} catch( SecurityException e ) { };
|
||||||
if( checkFile != null )
|
if( checkFile != null )
|
||||||
{
|
{
|
||||||
Status.setText( "Already downloaded" );
|
Status.setText( "No need to download" );
|
||||||
DownloadComplete = true;
|
DownloadComplete = true;
|
||||||
Parent.initSDL();
|
initParent();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
checkFile = null;
|
checkFile = null;
|
||||||
@@ -487,6 +497,11 @@ public class DemoActivity extends Activity {
|
|||||||
Status.setText( "Finished" );
|
Status.setText( "Finished" );
|
||||||
DownloadComplete = true;
|
DownloadComplete = true;
|
||||||
|
|
||||||
|
initParent();
|
||||||
|
};
|
||||||
|
|
||||||
|
private void initParent()
|
||||||
|
{
|
||||||
class Callback implements Runnable
|
class Callback implements Runnable
|
||||||
{
|
{
|
||||||
public DemoActivity Parent;
|
public DemoActivity Parent;
|
||||||
@@ -498,7 +513,7 @@ public class DemoActivity extends Activity {
|
|||||||
Callback cb = new Callback();
|
Callback cb = new Callback();
|
||||||
cb.Parent = Parent;
|
cb.Parent = Parent;
|
||||||
Parent.runOnUiThread(cb);
|
Parent.runOnUiThread(cb);
|
||||||
};
|
}
|
||||||
|
|
||||||
private String getOutFilePath(final String filename)
|
private String getOutFilePath(final String filename)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user