Implemented OpenSL-ES audio recording on Android

This commit is contained in:
Sam Lantinga 2020-02-11 16:14:02 -08:00
parent fe8ce66b53
commit 4bb95e8403
5 changed files with 377 additions and 219 deletions

View File

@ -783,6 +783,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
public static native void nativeSetenv(String name, String value); public static native void nativeSetenv(String name, String value);
public static native void onNativeOrientationChanged(int orientation); public static native void onNativeOrientationChanged(int orientation);
public static native void nativeAddTouch(int touchId, String name); public static native void nativeAddTouch(int touchId, String name);
public static native void nativePermissionResult(int requestCode, boolean result);
/** /**
* This method is called by SDL using JNI. * This method is called by SDL using JNI.
@ -1600,6 +1601,42 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
} }
return true; return true;
} }
/**
* This method is called by SDL using JNI.
*/
public static void requestPermission(String permission, int requestCode) {
if (mSingleton != null) {
mSingleton.checkPermission(permission, requestCode);
} else {
nativePermissionResult(requestCode, false);
}
}
/**
* This can be overridden
*/
public void checkPermission(String permission, int requestCode) {
if (Build.VERSION.SDK_INT < 23) {
nativePermissionResult(requestCode, true);
return;
}
if (this.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
this.requestPermissions(new String[]{permission}, requestCode);
} else {
nativePermissionResult(requestCode, true);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
nativePermissionResult(requestCode, true);
} else {
nativePermissionResult(requestCode, false);
}
}
} }
/** /**

View File

@ -1076,7 +1076,7 @@ SDL_GetAudioDeviceName(int index, int iscapture)
return NULL; return NULL;
} }
if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) { if (iscapture && !current_audio.impl.HasCaptureSupport) {
SDL_SetError("No capture support"); SDL_SetError("No capture support");
return NULL; return NULL;
} }
@ -1230,7 +1230,7 @@ open_audio_device(const char *devname, int iscapture,
return 0; return 0;
} }
if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) { if (iscapture && !current_audio.impl.HasCaptureSupport) {
SDL_SetError("No capture support"); SDL_SetError("No capture support");
return 0; return 0;
} }

View File

@ -26,8 +26,10 @@
https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
*/ */
#include "SDL_assert.h"
#include "SDL_audio.h" #include "SDL_audio.h"
#include "../SDL_audio_c.h" #include "../SDL_audio_c.h"
#include "../../core/android/SDL_android.h"
#include "SDL_openslES.h" #include "SDL_openslES.h"
/* for native audio */ /* for native audio */
@ -48,42 +50,50 @@
#define LOGV(...) #define LOGV(...)
#endif #endif
/*
#define SL_SPEAKER_FRONT_LEFT ((SLuint32) 0x00000001)
#define SL_SPEAKER_FRONT_RIGHT ((SLuint32) 0x00000002)
#define SL_SPEAKER_FRONT_CENTER ((SLuint32) 0x00000004)
#define SL_SPEAKER_LOW_FREQUENCY ((SLuint32) 0x00000008)
#define SL_SPEAKER_BACK_LEFT ((SLuint32) 0x00000010)
#define SL_SPEAKER_BACK_RIGHT ((SLuint32) 0x00000020)
#define SL_SPEAKER_FRONT_LEFT_OF_CENTER ((SLuint32) 0x00000040)
#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080)
#define SL_SPEAKER_BACK_CENTER ((SLuint32) 0x00000100)
#define SL_SPEAKER_SIDE_LEFT ((SLuint32) 0x00000200)
#define SL_SPEAKER_SIDE_RIGHT ((SLuint32) 0x00000400)
#define SL_SPEAKER_TOP_CENTER ((SLuint32) 0x00000800)
#define SL_SPEAKER_TOP_FRONT_LEFT ((SLuint32) 0x00001000)
#define SL_SPEAKER_TOP_FRONT_CENTER ((SLuint32) 0x00002000)
#define SL_SPEAKER_TOP_FRONT_RIGHT ((SLuint32) 0x00004000)
#define SL_SPEAKER_TOP_BACK_LEFT ((SLuint32) 0x00008000)
#define SL_SPEAKER_TOP_BACK_CENTER ((SLuint32) 0x00010000)
#define SL_SPEAKER_TOP_BACK_RIGHT ((SLuint32) 0x00020000)
*/
#define SL_ANDROID_SPEAKER_STEREO (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT)
#define SL_ANDROID_SPEAKER_QUAD (SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY)
#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
/* engine interfaces */ /* engine interfaces */
static SLObjectItf engineObject = NULL; static SLObjectItf engineObject;
static SLEngineItf engineEngine = NULL; static SLEngineItf engineEngine;
/* output mix interfaces */ /* output mix interfaces */
static SLObjectItf outputMixObject = NULL; static SLObjectItf outputMixObject;
// static SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
/* aux effect on the output mix, used by the buffer queue player */
/* static const SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR; */
/* buffer queue player interfaces */ /* buffer queue player interfaces */
static SLObjectItf bqPlayerObject = NULL; static SLObjectItf bqPlayerObject;
static SLPlayItf bqPlayerPlay = NULL; static SLPlayItf bqPlayerPlay;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL; static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
#if 0 #if 0
static SLEffectSendItf bqPlayerEffectSend = NULL; static SLVolumeItf bqPlayerVolume;
static SLMuteSoloItf bqPlayerMuteSolo = NULL;
static SLVolumeItf bqPlayerVolume = NULL;
#endif #endif
#if 0 /* recorder interfaces */
/* recorder interfaces TODO */ static SLObjectItf recorderObject;
static SLObjectItf recorderObject = NULL; static SLRecordItf recorderRecord;
static SLRecordItf recorderRecord;
static SLAndroidSimpleBufferQueueItf recorderBufferQueue; static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
#endif
/* pointer and size of the next player buffer to enqueue, and number of remaining buffers */
#if 0
static short *nextBuffer;
static unsigned nextSize;
static int nextCount;
#endif
// static SDL_AudioDevice* audioDevice = NULL;
#if 0 #if 0
static const char *sldevaudiorecorderstr = "SLES Audio Recorder"; static const char *sldevaudiorecorderstr = "SLES Audio Recorder";
@ -93,19 +103,34 @@ static const char *sldevaudioplayerstr = "SLES Audio Player";
#define SLES_DEV_AUDIO_PLAYER sldevaudioplayerstr #define SLES_DEV_AUDIO_PLAYER sldevaudioplayerstr
static void openslES_DetectDevices( int iscapture ) static void openslES_DetectDevices( int iscapture )
{ {
LOGI( "openSLES_DetectDevices()" ); LOGI( "openSLES_DetectDevices()" );
if ( iscapture ) if ( iscapture )
addfn( SLES_DEV_AUDIO_RECORDER ); addfn( SLES_DEV_AUDIO_RECORDER );
else else
addfn( SLES_DEV_AUDIO_PLAYER ); addfn( SLES_DEV_AUDIO_PLAYER );
return;
} }
#endif #endif
static void openslES_DestroyEngine(void); static void openslES_DestroyEngine(void)
{
LOGI("openslES_DestroyEngine()");
/* destroy output mix object, and invalidate all associated interfaces */
if (outputMixObject != NULL) {
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = NULL;
}
/* destroy engine object, and invalidate all associated interfaces */
if (engineObject != NULL) {
(*engineObject)->Destroy(engineObject);
engineObject = NULL;
engineEngine = NULL;
}
}
static int static int
openslES_CreateEngine() openslES_CreateEngine(void)
{ {
SLresult result; SLresult result;
@ -114,40 +139,33 @@ openslES_CreateEngine()
/* create engine */ /* create engine */
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("slCreateEngine failed"); LOGE("slCreateEngine failed: %d", result);
goto error; goto error;
} }
LOGI("slCreateEngine OK"); LOGI("slCreateEngine OK");
/* realize the engine */ /* realize the engine */
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeEngine failed"); LOGE("RealizeEngine failed: %d", result);
goto error; goto error;
} }
LOGI("RealizeEngine OK"); LOGI("RealizeEngine OK");
/* get the engine interface, which is needed in order to create other objects */ /* get the engine interface, which is needed in order to create other objects */
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("EngineGetInterface failed"); LOGE("EngineGetInterface failed: %d", result);
goto error; goto error;
} }
LOGI("EngineGetInterface OK"); LOGI("EngineGetInterface OK");
/* create output mix, with environmental reverb specified as a non-required interface */ /* create output mix */
/* const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB }; */
/* const SLboolean req[1] = { SL_BOOLEAN_FALSE }; */
const SLInterfaceID ids[1] = { SL_IID_VOLUME }; const SLInterfaceID ids[1] = { SL_IID_VOLUME };
const SLboolean req[1] = { SL_BOOLEAN_FALSE }; const SLboolean req[1] = { SL_BOOLEAN_FALSE };
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req); result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("CreateOutputMix failed"); LOGE("CreateOutputMix failed: %d", result);
goto error; goto error;
} }
LOGI("CreateOutputMix OK"); LOGI("CreateOutputMix OK");
@ -155,7 +173,7 @@ openslES_CreateEngine()
/* realize the output mix */ /* realize the output mix */
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeOutputMix failed"); LOGE("RealizeOutputMix failed: %d", result);
goto error; goto error;
} }
return 1; return 1;
@ -165,31 +183,181 @@ error:
return 0; return 0;
} }
static void openslES_DestroyPCMPlayer(_THIS); /* this callback handler is called every time a buffer finishes recording */
static void openslES_DestroyPCMRecorder(_THIS); static void
bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
static void openslES_DestroyEngine()
{ {
LOGI("openslES_DestroyEngine()"); struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) context;
// openslES_DestroyPCMPlayer(this); LOGV("SLES: Recording Callback");
// openslES_DestroyPCMRecorder(this); SDL_SemPost(audiodata->playsem);
}
/* destroy output mix object, and invalidate all associated interfaces */ static void
if (outputMixObject != NULL) { openslES_DestroyPCMRecorder(_THIS)
(*outputMixObject)->Destroy(outputMixObject); {
outputMixObject = NULL; struct SDL_PrivateAudioData *audiodata = this->hidden;
/* outputMixEnvironmentalReverb = NULL; */ SLresult result;
/* stop recording */
if (recorderRecord != NULL) {
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) {
LOGE("SetRecordState stopped: %d", result);
}
} }
/* destroy engine object, and invalidate all associated interfaces */ /* destroy audio recorder object, and invalidate all associated interfaces */
if (engineObject != NULL) { if (recorderObject != NULL) {
(*engineObject)->Destroy(engineObject); (*recorderObject)->Destroy(recorderObject);
engineObject = NULL; recorderObject = NULL;
engineEngine = NULL; recorderRecord = NULL;
recorderBufferQueue = NULL;
} }
return; if (audiodata->playsem) {
SDL_DestroySemaphore(audiodata->playsem);
audiodata->playsem = NULL;
}
if (audiodata->mixbuff) {
SDL_free(audiodata->mixbuff);
}
}
static int
openslES_CreatePCMRecorder(_THIS)
{
struct SDL_PrivateAudioData *audiodata = this->hidden;
SLDataFormat_PCM format_pcm;
SLresult result;
int i;
if (!Android_JNI_RequestPermission("android.permission.RECORD_AUDIO")) {
return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
}
/* Just go with signed 16-bit audio as it's the most compatible */
this->spec.format = AUDIO_S16SYS;
this->spec.channels = 1;
/*this->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
/* Update the fragment size as size in bytes */
SDL_CalculateAudioSpec(&this->spec);
LOGI("Try to open %u hz %u bit chan %u %s samples %u",
this->spec.freq, SDL_AUDIO_BITSIZE(this->spec.format),
this->spec.channels, (this->spec.format & 0x1000) ? "BE" : "LE", this->spec.samples);
/* configure audio source */
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSrc = {&loc_dev, NULL};
/* configure audio sink */
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS };
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = this->spec.channels;
format_pcm.samplesPerSec = this->spec.freq * 1000; /* / kilo Hz to milli Hz */
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
format_pcm.containerSize = SDL_AUDIO_BITSIZE(this->spec.format);
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
SLDataSink audioSnk = { &loc_bufq, &format_pcm };
/* create audio recorder */
/* (requires the RECORD_AUDIO permission) */
const SLInterfaceID ids[1] = {
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
};
const SLboolean req[1] = {
SL_BOOLEAN_TRUE,
};
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req);
if (SL_RESULT_SUCCESS != result) {
LOGE("CreateAudioRecorder failed: %d", result);
goto failed;
}
/* realize the recorder */
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeAudioPlayer failed: %d", result);
goto failed;
}
/* get the record interface */
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_RECORD interface get failed: %d", result);
goto failed;
}
/* get the buffer queue interface */
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
goto failed;
}
/* register callback on the buffer queue */
/* context is '(SDL_PrivateAudioData *)this->hidden' */
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, this->hidden);
if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback failed: %d", result);
goto failed;
}
/* Create the audio buffer semaphore */
audiodata->playsem = SDL_CreateSemaphore(0);
if (!audiodata->playsem) {
LOGE("cannot create Semaphore!");
goto failed;
}
/* Create the sound buffers */
audiodata->mixbuff = (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
if (audiodata->mixbuff == NULL) {
LOGE("mixbuffer allocate - out of memory");
goto failed;
}
for (i = 0; i < NUM_BUFFERS; i++) {
audiodata->pmixbuff[i] = audiodata->mixbuff + i * this->spec.size;
}
/* in case already recording, stop recording and clear buffer queue */
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) {
LOGE("Record set state failed: %d", result);
goto failed;
}
/* enqueue empty buffers to be filled by the recorder */
for (i = 0; i < NUM_BUFFERS; i++) {
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], this->spec.size);
if (SL_RESULT_SUCCESS != result) {
LOGE("Record enqueue buffers failed: %d", result);
goto failed;
}
}
/* start recording */
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
if (SL_RESULT_SUCCESS != result) {
LOGE("Record set state failed: %d", result);
goto failed;
}
return 0;
failed:
openslES_DestroyPCMRecorder(this);
return SDL_SetError("Open device failed!");
} }
/* this callback handler is called every time a buffer finishes playing */ /* this callback handler is called every time a buffer finishes playing */
@ -197,32 +365,49 @@ static void
bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{ {
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) context; struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) context;
LOGV("SLES: Playback Callmeback");
LOGV("SLES: Playback Callback");
SDL_SemPost(audiodata->playsem); SDL_SemPost(audiodata->playsem);
return;
}
static int
openslES_CreatePCMRecorder(_THIS)
{
/* struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */
LOGE("openslES_CreatePCMRecorder not implimented yet!");
return SDL_SetError("openslES_CreatePCMRecorder not implimented yet!");
} }
static void static void
openslES_DestroyPCMRecorder(_THIS) openslES_DestroyPCMPlayer(_THIS)
{ {
/* struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */ struct SDL_PrivateAudioData *audiodata = this->hidden;
SLresult result;
return; /* set the player's state to 'stopped' */
if (bqPlayerPlay != NULL) {
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) {
LOGE("SetPlayState stopped failed: %d", result);
}
}
/* destroy buffer queue audio player object, and invalidate all associated interfaces */
if (bqPlayerObject != NULL) {
(*bqPlayerObject)->Destroy(bqPlayerObject);
bqPlayerObject = NULL;
bqPlayerPlay = NULL;
bqPlayerBufferQueue = NULL;
}
if (audiodata->playsem) {
SDL_DestroySemaphore(audiodata->playsem);
audiodata->playsem = NULL;
}
if (audiodata->mixbuff) {
SDL_free(audiodata->mixbuff);
}
} }
static int static int
openslES_CreatePCMPlayer(_THIS) openslES_CreatePCMPlayer(_THIS)
{ {
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; struct SDL_PrivateAudioData *audiodata = this->hidden;
SLDataFormat_PCM format_pcm; SLDataFormat_PCM format_pcm;
SLresult result; SLresult result;
int i; int i;
@ -273,31 +458,6 @@ openslES_CreatePCMPlayer(_THIS)
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
} }
/*
#define SL_SPEAKER_FRONT_LEFT ((SLuint32) 0x00000001)
#define SL_SPEAKER_FRONT_RIGHT ((SLuint32) 0x00000002)
#define SL_SPEAKER_FRONT_CENTER ((SLuint32) 0x00000004)
#define SL_SPEAKER_LOW_FREQUENCY ((SLuint32) 0x00000008)
#define SL_SPEAKER_BACK_LEFT ((SLuint32) 0x00000010)
#define SL_SPEAKER_BACK_RIGHT ((SLuint32) 0x00000020)
#define SL_SPEAKER_FRONT_LEFT_OF_CENTER ((SLuint32) 0x00000040)
#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080)
#define SL_SPEAKER_BACK_CENTER ((SLuint32) 0x00000100)
#define SL_SPEAKER_SIDE_LEFT ((SLuint32) 0x00000200)
#define SL_SPEAKER_SIDE_RIGHT ((SLuint32) 0x00000400)
#define SL_SPEAKER_TOP_CENTER ((SLuint32) 0x00000800)
#define SL_SPEAKER_TOP_FRONT_LEFT ((SLuint32) 0x00001000)
#define SL_SPEAKER_TOP_FRONT_CENTER ((SLuint32) 0x00002000)
#define SL_SPEAKER_TOP_FRONT_RIGHT ((SLuint32) 0x00004000)
#define SL_SPEAKER_TOP_BACK_LEFT ((SLuint32) 0x00008000)
#define SL_SPEAKER_TOP_BACK_CENTER ((SLuint32) 0x00010000)
#define SL_SPEAKER_TOP_BACK_RIGHT ((SLuint32) 0x00020000)
*/
#define SL_ANDROID_SPEAKER_STEREO (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT)
#define SL_ANDROID_SPEAKER_QUAD (SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY)
#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
switch (this->spec.channels) switch (this->spec.channels)
{ {
case 1: case 1:
@ -350,28 +510,28 @@ openslES_CreatePCMPlayer(_THIS)
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req); result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("CreateAudioPlayer failed"); LOGE("CreateAudioPlayer failed: %d", result);
goto failed; goto failed;
} }
/* realize the player */ /* realize the player */
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeAudioPlayer failed"); LOGE("RealizeAudioPlayer failed: %d", result);
goto failed; goto failed;
} }
/* get the play interface */ /* get the play interface */
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_PLAY interface get failed"); LOGE("SL_IID_PLAY interface get failed: %d", result);
goto failed; goto failed;
} }
/* get the buffer queue interface */ /* get the buffer queue interface */
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_BUFFERQUEUE interface get failed"); LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
goto failed; goto failed;
} }
@ -379,33 +539,15 @@ openslES_CreatePCMPlayer(_THIS)
/* context is '(SDL_PrivateAudioData *)this->hidden' */ /* context is '(SDL_PrivateAudioData *)this->hidden' */
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this->hidden); result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this->hidden);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback failed"); LOGE("RegisterCallback failed: %d", result);
goto failed; goto failed;
} }
#if 0
/* get the effect send interface */
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND, &bqPlayerEffectSend);
if (SL_RESULT_SUCCESS != result)
{
LOGE("SL_IID_EFFECTSEND interface get failed");
goto failed;
}
#endif
#if 0 /* mute/solo is not supported for sources that are known to be mono, as this is */
/* get the mute/solo interface */
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);
assert(SL_RESULT_SUCCESS == result);
(void) result;
#endif
#if 0 #if 0
/* get the volume interface */ /* get the volume interface */
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_VOLUME interface get failed"); LOGE("SL_IID_VOLUME interface get failed: %d", result);
/* goto failed; */ /* goto failed; */
} }
#endif #endif
@ -431,7 +573,7 @@ openslES_CreatePCMPlayer(_THIS)
/* set the player's state to playing */ /* set the player's state to playing */
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
LOGE("Play set state failed"); LOGE("Play set state failed: %d", result);
goto failed; goto failed;
} }
@ -444,47 +586,6 @@ failed:
return SDL_SetError("Open device failed!"); return SDL_SetError("Open device failed!");
} }
static void
openslES_DestroyPCMPlayer(_THIS)
{
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
SLresult result;
/* set the player's state to 'stopped' */
if (bqPlayerPlay != NULL) {
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) {
SDL_SetError("Stopped set state failed");
}
}
/* destroy buffer queue audio player object, and invalidate all associated interfaces */
if (bqPlayerObject != NULL) {
(*bqPlayerObject)->Destroy(bqPlayerObject);
bqPlayerObject = NULL;
bqPlayerPlay = NULL;
bqPlayerBufferQueue = NULL;
#if 0
bqPlayerEffectSend = NULL;
bqPlayerMuteSolo = NULL;
bqPlayerVolume = NULL;
#endif
}
if (audiodata->playsem) {
SDL_DestroySemaphore(audiodata->playsem);
audiodata->playsem = NULL;
}
if (audiodata->mixbuff) {
SDL_free(audiodata->mixbuff);
}
return;
}
static int static int
openslES_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) openslES_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{ {
@ -494,44 +595,46 @@ openslES_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
} }
if (iscapture) { if (iscapture) {
LOGI("openslES_OpenDevice( ) %s for capture", devname); LOGI("openslES_OpenDevice() %s for capture", devname);
return openslES_CreatePCMRecorder(this); return openslES_CreatePCMRecorder(this);
} else { } else {
LOGI("openslES_OpenDevice( ) %s for playing", devname); LOGI("openslES_OpenDevice() %s for playing", devname);
return openslES_CreatePCMPlayer(this); return openslES_CreatePCMPlayer(this);
} }
} }
static void static void
openslES_CloseDevice(_THIS) openslES_WaitDevice(_THIS)
{ {
/* struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */ struct SDL_PrivateAudioData *audiodata = this->hidden;
if (this->iscapture) { LOGV("openslES_WaitDevice()");
LOGI("openslES_CloseDevice( ) for capture");
openslES_DestroyPCMRecorder(this);
} else {
LOGI("openslES_CloseDevice( ) for playing");
openslES_DestroyPCMPlayer(this);
}
SDL_free(this->hidden); /* Wait for an audio chunk to finish */
SDL_SemWait(audiodata->playsem);
return;
} }
static void static void
openslES_WaitDevice(_THIS) openslES_PlayDevice(_THIS)
{ {
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; struct SDL_PrivateAudioData *audiodata = this->hidden;
SLresult result;
LOGV("openslES_WaitDevice( )"); LOGV("======openslES_PlayDevice()======");
/* Wait for an audio chunk to finish */ /* Queue it up */
/* WaitForSingleObject(this->hidden->audio_sem, INFINITE); */ result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size);
SDL_SemWait(audiodata->playsem);
return; audiodata->next_buffer++;
if (audiodata->next_buffer >= NUM_BUFFERS) {
audiodata->next_buffer = 0;
}
/* If Enqueue fails, callback won't be called.
* Post the semphore, not to run out of buffer */
if (SL_RESULT_SUCCESS != result) {
SDL_SemPost(audiodata->playsem);
}
} }
/*/ n playn sem */ /*/ n playn sem */
@ -549,35 +652,54 @@ openslES_WaitDevice(_THIS)
static Uint8 * static Uint8 *
openslES_GetDeviceBuf(_THIS) openslES_GetDeviceBuf(_THIS)
{ {
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; struct SDL_PrivateAudioData *audiodata = this->hidden;
LOGV("openslES_GetDeviceBuf( )"); LOGV("openslES_GetDeviceBuf()");
return audiodata->pmixbuff[audiodata->next_buffer]; return audiodata->pmixbuff[audiodata->next_buffer];
} }
static void static int
openslES_PlayDevice(_THIS) openslES_CaptureFromDevice(_THIS, void *buffer, int buflen)
{ {
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; struct SDL_PrivateAudioData *audiodata = this->hidden;
SLresult result; SLresult result;
LOGV("======openslES_PlayDevice( )======"); /* Wait for new recorded data */
SDL_SemWait(audiodata->playsem);
/* Queue it up */ /* Copy it to the output buffer */
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size); SDL_assert(buflen == this->spec.size);
SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size);
/* Re-enqueue the buffer */
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size);
if (SL_RESULT_SUCCESS != result) {
LOGE("Record enqueue buffers failed: %d", result);
return -1;
}
audiodata->next_buffer++; audiodata->next_buffer++;
if (audiodata->next_buffer >= NUM_BUFFERS) { if (audiodata->next_buffer >= NUM_BUFFERS) {
audiodata->next_buffer = 0; audiodata->next_buffer = 0;
} }
/* If Enqueue fails, callback won't be called. return this->spec.size;
* Post the semphore, not to run out of buffer */ }
if (SL_RESULT_SUCCESS != result) {
SDL_SemPost(audiodata->playsem); static void
openslES_CloseDevice(_THIS)
{
/* struct SDL_PrivateAudioData *audiodata = this->hidden; */
if (this->iscapture) {
LOGI("openslES_CloseDevice() for capture");
openslES_DestroyPCMRecorder(this);
} else {
LOGI("openslES_CloseDevice() for playing");
openslES_DestroyPCMPlayer(this);
} }
return; SDL_free(this->hidden);
} }
static int static int
@ -594,18 +716,19 @@ openslES_Init(SDL_AudioDriverImpl * impl)
/* Set the function pointers */ /* Set the function pointers */
/* impl->DetectDevices = openslES_DetectDevices; */ /* impl->DetectDevices = openslES_DetectDevices; */
impl->OpenDevice = openslES_OpenDevice; impl->OpenDevice = openslES_OpenDevice;
impl->CloseDevice = openslES_CloseDevice; impl->WaitDevice = openslES_WaitDevice;
impl->PlayDevice = openslES_PlayDevice; impl->PlayDevice = openslES_PlayDevice;
impl->GetDeviceBuf = openslES_GetDeviceBuf; impl->GetDeviceBuf = openslES_GetDeviceBuf;
impl->CaptureFromDevice = openslES_CaptureFromDevice;
impl->CloseDevice = openslES_CloseDevice;
impl->Deinitialize = openslES_DestroyEngine; impl->Deinitialize = openslES_DestroyEngine;
impl->WaitDevice = openslES_WaitDevice;
/* and the capabilities */ /* and the capabilities */
impl->HasCaptureSupport = 0; /* TODO */ impl->HasCaptureSupport = 1;
impl->OnlyHasDefaultOutputDevice = 1; impl->OnlyHasDefaultOutputDevice = 1;
/* impl->OnlyHasDefaultInputDevice = 1; */ impl->OnlyHasDefaultCaptureDevice = 1;
LOGI("openslES_Init() - succes"); LOGI("openslES_Init() - success");
/* this audio target is available. */ /* this audio target is available. */
return 1; return 1;
@ -615,24 +738,24 @@ AudioBootStrap openslES_bootstrap = {
"openslES", "opensl ES audio driver", openslES_Init, 0 "openslES", "opensl ES audio driver", openslES_Init, 0
}; };
void openslES_ResumeDevices() void openslES_ResumeDevices(void)
{ {
if (bqPlayerPlay != NULL) { if (bqPlayerPlay != NULL) {
/* set the player's state to 'playing' */ /* set the player's state to 'playing' */
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
SDL_SetError("openslES_ResumeDevices failed"); LOGE("openslES_ResumeDevices failed: %d", result);
} }
} }
} }
void openslES_PauseDevices() void openslES_PauseDevices(void)
{ {
if (bqPlayerPlay != NULL) { if (bqPlayerPlay != NULL) {
/* set the player's state to 'paused' */ /* set the player's state to 'paused' */
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED); SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
if (SL_RESULT_SUCCESS != result) { if (SL_RESULT_SUCCESS != result) {
SDL_SetError("openslES_PauseDevices failed"); LOGE("openslES_PauseDevices failed: %d", result);
} }
} }
} }

View File

@ -32,14 +32,10 @@
struct SDL_PrivateAudioData struct SDL_PrivateAudioData
{ {
/* The file descriptor for the audio device */
Uint8 *mixbuff; Uint8 *mixbuff;
int next_buffer; int next_buffer;
Uint8 *pmixbuff[NUM_BUFFERS]; Uint8 *pmixbuff[NUM_BUFFERS];
SDL_sem *playsem; SDL_sem *playsem;
#if 0
SDL_sem *recsem;
#endif
}; };
void openslES_ResumeDevices(void); void openslES_ResumeDevices(void);

View File

@ -123,6 +123,8 @@ SDL_bool Android_JNI_SetSystemCursor(int cursorID);
SDL_bool Android_JNI_SupportsRelativeMouse(void); SDL_bool Android_JNI_SupportsRelativeMouse(void);
SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled); SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled);
/* Request permission */
SDL_bool Android_JNI_RequestPermission(const char *permission);
int SDL_GetAndroidSDKVersion(void); int SDL_GetAndroidSDKVersion(void);