From 4bb95e84036132ea698a0ac9acc83b6c148109b2 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 11 Feb 2020 16:14:02 -0800 Subject: [PATCH] Implemented OpenSL-ES audio recording on Android --- .../main/java/org/libsdl/app/SDLActivity.java | 37 ++ src/audio/SDL_audio.c | 4 +- src/audio/openslES/SDL_openslES.c | 545 +++++++++++------- src/audio/openslES/SDL_openslES.h | 4 - src/core/android/SDL_android.h | 2 + 5 files changed, 375 insertions(+), 217 deletions(-) diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index 9b872cd0728dd..6ee101ee58a0c 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -783,6 +783,7 @@ public static native void onNativeTouch(int touchDevId, int pointerFingerId, public static native void nativeSetenv(String name, String value); public static native void onNativeOrientationChanged(int orientation); 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. @@ -1600,6 +1601,42 @@ public static boolean setSystemCursor(int cursorID) { } 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); + } + } } /** diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 532998dc6eea4..3137493233c9c 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -1076,7 +1076,7 @@ SDL_GetAudioDeviceName(int index, int iscapture) return NULL; } - if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) { + if (iscapture && !current_audio.impl.HasCaptureSupport) { SDL_SetError("No capture support"); return NULL; } @@ -1230,7 +1230,7 @@ open_audio_device(const char *devname, int iscapture, return 0; } - if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) { + if (iscapture && !current_audio.impl.HasCaptureSupport) { SDL_SetError("No capture support"); return 0; } diff --git a/src/audio/openslES/SDL_openslES.c b/src/audio/openslES/SDL_openslES.c index 0936524baf876..08189ddde3e09 100644 --- a/src/audio/openslES/SDL_openslES.c +++ b/src/audio/openslES/SDL_openslES.c @@ -26,8 +26,10 @@ https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html */ +#include "SDL_assert.h" #include "SDL_audio.h" #include "../SDL_audio_c.h" +#include "../../core/android/SDL_android.h" #include "SDL_openslES.h" /* for native audio */ @@ -48,42 +50,50 @@ #define LOGV(...) #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 */ -static SLObjectItf engineObject = NULL; -static SLEngineItf engineEngine = NULL; +static SLObjectItf engineObject; +static SLEngineItf engineEngine; /* output mix interfaces */ -static SLObjectItf outputMixObject = NULL; -// 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; */ +static SLObjectItf outputMixObject; /* buffer queue player interfaces */ -static SLObjectItf bqPlayerObject = NULL; -static SLPlayItf bqPlayerPlay = NULL; -static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL; +static SLObjectItf bqPlayerObject; +static SLPlayItf bqPlayerPlay; +static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; #if 0 -static SLEffectSendItf bqPlayerEffectSend = NULL; -static SLMuteSoloItf bqPlayerMuteSolo = NULL; -static SLVolumeItf bqPlayerVolume = NULL; +static SLVolumeItf bqPlayerVolume; #endif -#if 0 -/* recorder interfaces TODO */ -static SLObjectItf recorderObject = NULL; -static SLRecordItf recorderRecord; +/* recorder interfaces */ +static SLObjectItf recorderObject; +static SLRecordItf recorderRecord; 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 static const char *sldevaudiorecorderstr = "SLES Audio Recorder"; @@ -93,19 +103,34 @@ static const char *sldevaudioplayerstr = "SLES Audio Player"; #define SLES_DEV_AUDIO_PLAYER sldevaudioplayerstr static void openslES_DetectDevices( int iscapture ) { - LOGI( "openSLES_DetectDevices()" ); + LOGI( "openSLES_DetectDevices()" ); if ( iscapture ) addfn( SLES_DEV_AUDIO_RECORDER ); - else + else addfn( SLES_DEV_AUDIO_PLAYER ); - return; } #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 -openslES_CreateEngine() +openslES_CreateEngine(void) { SLresult result; @@ -114,40 +139,33 @@ openslES_CreateEngine() /* create engine */ result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); if (SL_RESULT_SUCCESS != result) { - LOGE("slCreateEngine failed"); + LOGE("slCreateEngine failed: %d", result); goto error; } - LOGI("slCreateEngine OK"); /* realize the engine */ result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); if (SL_RESULT_SUCCESS != result) { - LOGE("RealizeEngine failed"); + LOGE("RealizeEngine failed: %d", result); goto error; } - LOGI("RealizeEngine OK"); /* get the engine interface, which is needed in order to create other objects */ result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); if (SL_RESULT_SUCCESS != result) { - LOGE("EngineGetInterface failed"); + LOGE("EngineGetInterface failed: %d", result); goto error; } - LOGI("EngineGetInterface OK"); - /* create output mix, with environmental reverb specified as a non-required interface */ - /* const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB }; */ - /* const SLboolean req[1] = { SL_BOOLEAN_FALSE }; */ - + /* create output mix */ const SLInterfaceID ids[1] = { SL_IID_VOLUME }; const SLboolean req[1] = { SL_BOOLEAN_FALSE }; result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req); - if (SL_RESULT_SUCCESS != result) { - LOGE("CreateOutputMix failed"); + LOGE("CreateOutputMix failed: %d", result); goto error; } LOGI("CreateOutputMix OK"); @@ -155,7 +173,7 @@ openslES_CreateEngine() /* realize the output mix */ result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); if (SL_RESULT_SUCCESS != result) { - LOGE("RealizeOutputMix failed"); + LOGE("RealizeOutputMix failed: %d", result); goto error; } return 1; @@ -165,31 +183,181 @@ openslES_CreateEngine() return 0; } -static void openslES_DestroyPCMPlayer(_THIS); -static void openslES_DestroyPCMRecorder(_THIS); +/* this callback handler is called every time a buffer finishes recording */ +static void +bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) +{ + struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) context; + + LOGV("SLES: Recording Callback"); + SDL_SemPost(audiodata->playsem); +} -static void openslES_DestroyEngine() +static void +openslES_DestroyPCMRecorder(_THIS) { - LOGI("openslES_DestroyEngine()"); + struct SDL_PrivateAudioData *audiodata = this->hidden; + SLresult result; -// openslES_DestroyPCMPlayer(this); -// openslES_DestroyPCMRecorder(this); + /* stop recording */ + if (recorderRecord != NULL) { + result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED); + if (SL_RESULT_SUCCESS != result) { + LOGE("SetRecordState stopped: %d", result); + } + } - /* destroy output mix object, and invalidate all associated interfaces */ - if (outputMixObject != NULL) { - (*outputMixObject)->Destroy(outputMixObject); - outputMixObject = NULL; - /* outputMixEnvironmentalReverb = NULL; */ + /* destroy audio recorder object, and invalidate all associated interfaces */ + if (recorderObject != NULL) { + (*recorderObject)->Destroy(recorderObject); + recorderObject = NULL; + recorderRecord = NULL; + recorderBufferQueue = NULL; } - /* destroy engine object, and invalidate all associated interfaces */ - if (engineObject != NULL) { - (*engineObject)->Destroy(engineObject); - engineObject = NULL; - engineEngine = NULL; + 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; + } } - return; + /* 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 */ @@ -197,32 +365,49 @@ static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) context; - LOGV("SLES: Playback Callmeback"); + + LOGV("SLES: Playback Callback"); SDL_SemPost(audiodata->playsem); - return; } -static int -openslES_CreatePCMRecorder(_THIS) +static void +openslES_DestroyPCMPlayer(_THIS) { -/* struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */ + struct SDL_PrivateAudioData *audiodata = this->hidden; + SLresult result; - LOGE("openslES_CreatePCMRecorder not implimented yet!"); - return SDL_SetError("openslES_CreatePCMRecorder not implimented yet!"); -} + /* 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); + } + } -static void -openslES_DestroyPCMRecorder(_THIS) -{ -/* struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */ + /* destroy buffer queue audio player object, and invalidate all associated interfaces */ + if (bqPlayerObject != NULL) { + + (*bqPlayerObject)->Destroy(bqPlayerObject); - return; + 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 openslES_CreatePCMPlayer(_THIS) { - struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; + struct SDL_PrivateAudioData *audiodata = this->hidden; SLDataFormat_PCM format_pcm; SLresult result; int i; @@ -273,31 +458,6 @@ openslES_CreatePCMPlayer(_THIS) 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) { case 1: @@ -350,28 +510,28 @@ openslES_CreatePCMPlayer(_THIS) result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req); if (SL_RESULT_SUCCESS != result) { - LOGE("CreateAudioPlayer failed"); + LOGE("CreateAudioPlayer failed: %d", result); goto failed; } /* realize the player */ result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); if (SL_RESULT_SUCCESS != result) { - LOGE("RealizeAudioPlayer failed"); + LOGE("RealizeAudioPlayer failed: %d", result); goto failed; } /* get the play interface */ result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); if (SL_RESULT_SUCCESS != result) { - LOGE("SL_IID_PLAY interface get failed"); + LOGE("SL_IID_PLAY interface get failed: %d", result); goto failed; } /* get the buffer queue interface */ result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue); if (SL_RESULT_SUCCESS != result) { - LOGE("SL_IID_BUFFERQUEUE interface get failed"); + LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result); goto failed; } @@ -379,33 +539,15 @@ openslES_CreatePCMPlayer(_THIS) /* context is '(SDL_PrivateAudioData *)this->hidden' */ result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this->hidden); if (SL_RESULT_SUCCESS != result) { - LOGE("RegisterCallback failed"); + LOGE("RegisterCallback failed: %d", result); 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 /* get the volume interface */ result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); if (SL_RESULT_SUCCESS != result) { - LOGE("SL_IID_VOLUME interface get failed"); + LOGE("SL_IID_VOLUME interface get failed: %d", result); /* goto failed; */ } #endif @@ -431,7 +573,7 @@ openslES_CreatePCMPlayer(_THIS) /* set the player's state to playing */ result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); if (SL_RESULT_SUCCESS != result) { - LOGE("Play set state failed"); + LOGE("Play set state failed: %d", result); goto failed; } @@ -444,47 +586,6 @@ openslES_CreatePCMPlayer(_THIS) 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 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) { - LOGI("openslES_OpenDevice( ) %s for capture", devname); + LOGI("openslES_OpenDevice() %s for capture", devname); return openslES_CreatePCMRecorder(this); } else { - LOGI("openslES_OpenDevice( ) %s for playing", devname); + LOGI("openslES_OpenDevice() %s for playing", devname); return openslES_CreatePCMPlayer(this); } } 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) { - LOGI("openslES_CloseDevice( ) for capture"); - openslES_DestroyPCMRecorder(this); - } else { - LOGI("openslES_CloseDevice( ) for playing"); - openslES_DestroyPCMPlayer(this); - } - - SDL_free(this->hidden); + LOGV("openslES_WaitDevice()"); - return; + /* Wait for an audio chunk to finish */ + SDL_SemWait(audiodata->playsem); } 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 */ - /* WaitForSingleObject(this->hidden->audio_sem, INFINITE); */ - SDL_SemWait(audiodata->playsem); + /* Queue it up */ + result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size); - 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 */ @@ -549,35 +652,54 @@ openslES_WaitDevice(_THIS) static Uint8 * 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]; } -static void -openslES_PlayDevice(_THIS) +static int +openslES_CaptureFromDevice(_THIS, void *buffer, int buflen) { - struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; + struct SDL_PrivateAudioData *audiodata = this->hidden; SLresult result; - LOGV("======openslES_PlayDevice( )======"); + /* Wait for new recorded data */ + SDL_SemWait(audiodata->playsem); - /* Queue it up */ - result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size); + /* Copy it to the output buffer */ + 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++; 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); + return this->spec.size; +} + +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 @@ -594,18 +716,19 @@ openslES_Init(SDL_AudioDriverImpl * impl) /* Set the function pointers */ /* impl->DetectDevices = openslES_DetectDevices; */ impl->OpenDevice = openslES_OpenDevice; - impl->CloseDevice = openslES_CloseDevice; + impl->WaitDevice = openslES_WaitDevice; impl->PlayDevice = openslES_PlayDevice; impl->GetDeviceBuf = openslES_GetDeviceBuf; + impl->CaptureFromDevice = openslES_CaptureFromDevice; + impl->CloseDevice = openslES_CloseDevice; impl->Deinitialize = openslES_DestroyEngine; - impl->WaitDevice = openslES_WaitDevice; /* and the capabilities */ - impl->HasCaptureSupport = 0; /* TODO */ - impl->OnlyHasDefaultOutputDevice = 1; - /* impl->OnlyHasDefaultInputDevice = 1; */ + impl->HasCaptureSupport = 1; + impl->OnlyHasDefaultOutputDevice = 1; + impl->OnlyHasDefaultCaptureDevice = 1; - LOGI("openslES_Init() - succes"); + LOGI("openslES_Init() - success"); /* this audio target is available. */ return 1; @@ -615,24 +738,24 @@ AudioBootStrap openslES_bootstrap = { "openslES", "opensl ES audio driver", openslES_Init, 0 }; -void openslES_ResumeDevices() +void openslES_ResumeDevices(void) { if (bqPlayerPlay != NULL) { /* set the player's state to 'playing' */ SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); 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) { /* set the player's state to 'paused' */ SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED); if (SL_RESULT_SUCCESS != result) { - SDL_SetError("openslES_PauseDevices failed"); + LOGE("openslES_PauseDevices failed: %d", result); } } } diff --git a/src/audio/openslES/SDL_openslES.h b/src/audio/openslES/SDL_openslES.h index 691f9f036953f..542870aeb4f66 100644 --- a/src/audio/openslES/SDL_openslES.h +++ b/src/audio/openslES/SDL_openslES.h @@ -32,14 +32,10 @@ struct SDL_PrivateAudioData { - /* The file descriptor for the audio device */ Uint8 *mixbuff; int next_buffer; Uint8 *pmixbuff[NUM_BUFFERS]; SDL_sem *playsem; -#if 0 - SDL_sem *recsem; -#endif }; void openslES_ResumeDevices(void); diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h index d51dc28088771..9f3f3cc7fcc3b 100644 --- a/src/core/android/SDL_android.h +++ b/src/core/android/SDL_android.h @@ -123,6 +123,8 @@ SDL_bool Android_JNI_SetSystemCursor(int cursorID); SDL_bool Android_JNI_SupportsRelativeMouse(void); SDL_bool Android_JNI_SetRelativeMouseEnabled(SDL_bool enabled); +/* Request permission */ +SDL_bool Android_JNI_RequestPermission(const char *permission); int SDL_GetAndroidSDKVersion(void);