From 64c55f41849259548e78e49457e7624b3bf9de1c Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 13 Dec 2017 18:36:53 -0800 Subject: [PATCH 1/7] oboe: OpenSL ES refactor engine Put Engine and OutputMixer into Singleton classes. Create separate input and output classes. --- src/common/AudioStreamBuilder.cpp | 8 +- src/opensles/AudioInputStreamOpenSLES.cpp | 28 ++++ src/opensles/AudioInputStreamOpenSLES.h | 39 +++++ src/opensles/AudioOutputStreamOpenSLES.cpp | 29 ++++ src/opensles/AudioOutputStreamOpenSLES.h | 40 +++++ src/opensles/AudioStreamOpenSLES.cpp | 167 ++++++++++++++------- src/opensles/AudioStreamOpenSLES.h | 43 ++++++ 7 files changed, 300 insertions(+), 54 deletions(-) create mode 100644 src/opensles/AudioInputStreamOpenSLES.cpp create mode 100644 src/opensles/AudioInputStreamOpenSLES.h create mode 100644 src/opensles/AudioOutputStreamOpenSLES.cpp create mode 100644 src/opensles/AudioOutputStreamOpenSLES.h diff --git a/src/common/AudioStreamBuilder.cpp b/src/common/AudioStreamBuilder.cpp index 6d92391b1..a3582e889 100644 --- a/src/common/AudioStreamBuilder.cpp +++ b/src/common/AudioStreamBuilder.cpp @@ -20,6 +20,8 @@ #include "OboeDebug.h" #include "oboe/Oboe.h" #include "oboe/AudioStreamBuilder.h" +#include "opensles/AudioInputStreamOpenSLES.h" +#include "opensles/AudioOutputStreamOpenSLES.h" #include "opensles/AudioStreamOpenSLES.h" namespace oboe { @@ -41,7 +43,11 @@ AudioStream *AudioStreamBuilder::build() { } // fall into using older existing API case AudioApi::OpenSLES: - stream = new AudioStreamOpenSLES(*this); + if (getDirection() == oboe::Direction::Output) { + stream = new AudioOutputStreamOpenSLES(*this); + } else if (getDirection() == oboe::Direction::Input) { + stream = new AudioInputStreamOpenSLES(*this); + } break; } return stream; diff --git a/src/opensles/AudioInputStreamOpenSLES.cpp b/src/opensles/AudioInputStreamOpenSLES.cpp new file mode 100644 index 000000000..688844530 --- /dev/null +++ b/src/opensles/AudioInputStreamOpenSLES.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "oboe/AudioStreamBuilder.h" +#include "AudioInputStreamOpenSLES.h" +#include "AudioStreamOpenSLES.h" + +using namespace oboe; + +AudioInputStreamOpenSLES::AudioInputStreamOpenSLES(const AudioStreamBuilder &builder) + : AudioStreamOpenSLES(builder) { +} + +AudioInputStreamOpenSLES::~AudioInputStreamOpenSLES() { +} diff --git a/src/opensles/AudioInputStreamOpenSLES.h b/src/opensles/AudioInputStreamOpenSLES.h new file mode 100644 index 000000000..3e8b0ff70 --- /dev/null +++ b/src/opensles/AudioInputStreamOpenSLES.h @@ -0,0 +1,39 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUDIO_INPUT_STREAM_OPENSL_ES_H_ +#define AUDIO_INPUT_STREAM_OPENSL_ES_H_ + + +#include +#include + +#include "oboe/Oboe.h" +#include "AudioStreamOpenSLES.h" + +namespace oboe { + +class AudioInputStreamOpenSLES : public AudioStreamOpenSLES { +public: + AudioInputStreamOpenSLES(); + explicit AudioInputStreamOpenSLES(const AudioStreamBuilder &builder); + + virtual ~AudioInputStreamOpenSLES(); +}; + +} // namespace oboe + +#endif //AUDIO_INPUT_STREAM_OPENSL_ES_H_ diff --git a/src/opensles/AudioOutputStreamOpenSLES.cpp b/src/opensles/AudioOutputStreamOpenSLES.cpp new file mode 100644 index 000000000..5be7b5448 --- /dev/null +++ b/src/opensles/AudioOutputStreamOpenSLES.cpp @@ -0,0 +1,29 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "oboe/AudioStreamBuilder.h" +#include "AudioOutputStreamOpenSLES.h" +#include "AudioStreamOpenSLES.h" + +using namespace oboe; + + +AudioOutputStreamOpenSLES::AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder) + : AudioStreamOpenSLES(builder) { +} + +AudioOutputStreamOpenSLES::~AudioOutputStreamOpenSLES() { +} diff --git a/src/opensles/AudioOutputStreamOpenSLES.h b/src/opensles/AudioOutputStreamOpenSLES.h new file mode 100644 index 000000000..75e87886c --- /dev/null +++ b/src/opensles/AudioOutputStreamOpenSLES.h @@ -0,0 +1,40 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AUDIO_OUTPUT_STREAM_OPENSL_ES_H_ +#define AUDIO_OUTPUT_STREAM_OPENSL_ES_H_ + + +#include +#include + +#include "oboe/Oboe.h" +#include "AudioStreamOpenSLES.h" + +namespace oboe { + +class AudioOutputStreamOpenSLES : public AudioStreamOpenSLES { +public: + AudioOutputStreamOpenSLES(); + explicit AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder); + + virtual ~AudioOutputStreamOpenSLES(); +private: +}; + +} // namespace oboe + +#endif //AUDIO_OUTPUT_STREAM_OPENSL_ES_H_ diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp index dfb9f8493..c1935a0ea 100644 --- a/src/opensles/AudioStreamOpenSLES.cpp +++ b/src/opensles/AudioStreamOpenSLES.cpp @@ -35,7 +35,7 @@ #define OBOE_BITS_PER_BYTE 8 // common value -namespace oboe { +using namespace oboe; /* * OSLES Helpers @@ -104,34 +104,15 @@ int chanCountToChanMask(int chanCount) { static const char *TAG = "AAudioStreamOpenSLES"; -// engine interfaces -static int32_t sOpenCount = 0; -static SLObjectItf sEngineObject = 0; -static SLEngineItf sEngineEngine; - -// output mix interfaces -static SLObjectItf sOutputMixObject = 0; - -static void CloseSLEngine(); - -SLresult AudioStreamOpenSLES::enqueueBuffer() { - // Ask the callback to fill the output buffer with data. - Result result = fireCallback(mCallbackBuffer, mFramesPerCallback); - if (result != Result::OK) { - LOGE("Oboe callback returned %d", result); - return SL_RESULT_INTERNAL_ERROR; - } else { - // Pass the data to OpenSLES. - return (*bq_)->Enqueue(bq_, mCallbackBuffer, mBytesPerCallback); +OpenSLEngine *OpenSLEngine::getInstance() { + // TODO mutex + if (sInstance == nullptr) { + sInstance = new OpenSLEngine(); } + return sInstance; } -// this callback handler is called every time a buffer finishes playing -static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { - ((AudioStreamOpenSLES *) context)->enqueueBuffer(); -} - -static SLresult OpenSLEngine() { +SLresult OpenSLEngine::open() { SLresult result = SL_RESULT_SUCCESS; if (sOpenCount > 0) { ++sOpenCount; @@ -159,29 +140,85 @@ static SLresult OpenSLEngine() { goto error; } + ++sOpenCount; + return result; + + error: + close(); + return result; +} + +void OpenSLEngine::close() { +// __android_log_print(ANDROID_LOG_INFO, TAG, "CloseSLEngine()"); + --sOpenCount; + if (sOpenCount > 0) { + return; + } + + if (sEngineObject != NULL) { + (*sEngineObject)->Destroy(sEngineObject); + sEngineObject = NULL; + sEngineEngine = NULL; + } +} + +SLresult OpenSLEngine::createOutputMix(SLObjectItf *objectItf) { + return (*sEngineEngine)->CreateOutputMix(sEngineEngine, objectItf, 0, 0, 0); +} + +SLresult OpenSLEngine::createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink) { + + const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE}; + const SLboolean req[] = {SL_BOOLEAN_TRUE}; + + // The Player + return (*sEngineEngine)->CreateAudioPlayer(sEngineEngine, objectItf, audioSource, + audioSink, + sizeof(ids) / sizeof(ids[0]), ids, req); +} + +OpenSLEngine *OpenSLEngine::sInstance = nullptr; + +OpenSLOutputMixer *OpenSLOutputMixer::getInstance() { + // TODO mutex + if (sInstance == nullptr) { + sInstance = new OpenSLOutputMixer(); + } + return sInstance; +} + +SLresult OpenSLOutputMixer::open() { + SLresult result = SL_RESULT_SUCCESS; + if (sOpenCount > 0) { + ++sOpenCount; + return SL_RESULT_SUCCESS; + } + // get the output mixer - result = (*sEngineEngine)->CreateOutputMix(sEngineEngine, &sOutputMixObject, 0, 0, 0); + result = OpenSLEngine::getInstance()->createOutputMix(&sOutputMixObject); if (SL_RESULT_SUCCESS != result) { - LOGE("OpenSLEngine() - CreateOutputMix() result:%s", getSLErrStr(result)); + LOGE("OpenSLOutputMixer() - createOutputMix() result:%s", getSLErrStr(result)); goto error; } // realize the output mix result = (*sOutputMixObject)->Realize(sOutputMixObject, SL_BOOLEAN_FALSE); if (SL_RESULT_SUCCESS != result) { - LOGE("OpenSLEngine() - Realize() sOutputMixObject result:%s", getSLErrStr(result)); + LOGE("OpenSLOutputMixer() - Realize() sOutputMixObject result:%s", getSLErrStr(result)); goto error; } ++sOpenCount; return result; -error: - CloseSLEngine(); + error: + close(); return result; } -static void CloseSLEngine() { +void OpenSLOutputMixer::close() { // __android_log_print(ANDROID_LOG_INFO, TAG, "CloseSLEngine()"); --sOpenCount; if (sOpenCount > 0) { @@ -192,12 +229,50 @@ static void CloseSLEngine() { (*sOutputMixObject)->Destroy(sOutputMixObject); sOutputMixObject = NULL; } +} - if (sEngineObject != NULL) { - (*sEngineObject)->Destroy(sEngineObject); - sEngineObject = NULL; - sEngineEngine = NULL; +SLresult OpenSLOutputMixer::createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource) { + SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, sOutputMixObject}; + SLDataSink audioSink = {&loc_outmix, NULL}; + return OpenSLEngine::getInstance()->createAudioPlayer(objectItf, audioSource, &audioSink); +} + +OpenSLOutputMixer *OpenSLOutputMixer::sInstance = nullptr; + +// this callback handler is called every time a buffer finishes playing +static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { + ((AudioStreamOpenSLES *) context)->enqueueBuffer(); +} + +SLresult AudioStreamOpenSLES::enqueueBuffer() { + // Ask the callback to fill the output buffer with data. + Result result = fireCallback(mCallbackBuffer, mFramesPerCallback); + if (result != Result::OK) { + LOGE("Oboe callback returned %d", result); + return SL_RESULT_INTERNAL_ERROR; + } else { + // Pass the data to OpenSLES. + return (*bq_)->Enqueue(bq_, mCallbackBuffer, mBytesPerCallback); + } +} + + +static void CloseSLEngine() { + OpenSLOutputMixer::getInstance()->close(); + OpenSLEngine::getInstance()->close(); +} + +static SLresult OpenSLContext() { + SLresult result = OpenSLEngine::getInstance()->open(); + if (SL_RESULT_SUCCESS == result) { + // get the output mixer + result = OpenSLOutputMixer::getInstance()->open(); + if (SL_RESULT_SUCCESS != result) { + CloseSLEngine(); + } } + return result; } AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder) @@ -206,8 +281,8 @@ AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder) bq_ = NULL; bqPlayerPlay_ = NULL; mFramesPerBurst = builder.getDefaultFramesPerBurst(); - OpenSLEngine(); - LOGD("AudioStreamOpenSLES(): after OpenSLEngine()"); + OpenSLContext(); + LOGD("AudioStreamOpenSLES(): after OpenSLContext()"); } AudioStreamOpenSLES::~AudioStreamOpenSLES() { @@ -290,8 +365,6 @@ Result AudioStreamOpenSLES::open() { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType static_cast(mBurstsPerBuffer)}; // numBuffers - // SLuint32 chanMask = SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; - // Define the audio data format. SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM, // formatType @@ -318,17 +391,7 @@ Result AudioStreamOpenSLES::open() { audioSrc.pFormat = &format_pcm_ex; } - // configure audio sink - SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, sOutputMixObject}; - SLDataSink audioSnk = {&loc_outmix, NULL}; - - const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE}; - const SLboolean req[] = {SL_BOOLEAN_TRUE}; - - // The Player - result = (*sEngineEngine)->CreateAudioPlayer(sEngineEngine, &bqPlayerObject_, &audioSrc, - &audioSnk, - sizeof(ids) / sizeof(ids[0]), ids, req); + result = OpenSLOutputMixer::getInstance()->createAudioPlayer(&bqPlayerObject_, &audioSrc); LOGD("CreateAudioPlayer() result:%s", getSLErrStr(result)); assert(SL_RESULT_SUCCESS == result); @@ -447,5 +510,3 @@ Result AudioStreamOpenSLES::waitForStateChange(StreamState currentState, int32_t AudioStreamOpenSLES::getFramesPerBurst() { return mFramesPerBurst; } - -} // namespace oboe diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h index 41ec329bd..a41d240a1 100644 --- a/src/opensles/AudioStreamOpenSLES.h +++ b/src/opensles/AudioStreamOpenSLES.h @@ -25,6 +25,49 @@ namespace oboe { +class OpenSLEngine { +public: + static OpenSLEngine *getInstance(); + + SLresult open(); + + void close(); + + SLresult createOutputMix(SLObjectItf *objectItf); + + SLresult createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink); + +private: + static OpenSLEngine *sInstance; + +// engine interfaces + int32_t sOpenCount = 0; + SLObjectItf sEngineObject = 0; + SLEngineItf sEngineEngine; +}; + +class OpenSLOutputMixer { +public: + static OpenSLOutputMixer *getInstance(); + + SLresult open(); + + void close(); + + SLresult createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource); + +private: + static OpenSLOutputMixer *sInstance; + +// engine interfaces + int32_t sOpenCount = 0; +// output mix interfaces + SLObjectItf sOutputMixObject = 0; +}; + /** * A stream that wraps OpenSL ES. * From 9a147a1376a84c1cc2536748e2e73119606eb59e Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Wed, 13 Dec 2017 20:23:25 -0800 Subject: [PATCH 2/7] oboe: move OpenSL ES output code to its own class --- src/opensles/AudioInputStreamOpenSLES.cpp | 42 ++++ src/opensles/AudioInputStreamOpenSLES.h | 15 ++ src/opensles/AudioOutputStreamOpenSLES.cpp | 209 +++++++++++++++++ src/opensles/AudioOutputStreamOpenSLES.h | 26 +++ src/opensles/AudioStreamOpenSLES.cpp | 255 +-------------------- src/opensles/AudioStreamOpenSLES.h | 31 +-- src/opensles/OpenSLESUtilities.cpp | 38 +++ src/opensles/OpenSLESUtilities.h | 6 + 8 files changed, 348 insertions(+), 274 deletions(-) diff --git a/src/opensles/AudioInputStreamOpenSLES.cpp b/src/opensles/AudioInputStreamOpenSLES.cpp index 688844530..694e97006 100644 --- a/src/opensles/AudioInputStreamOpenSLES.cpp +++ b/src/opensles/AudioInputStreamOpenSLES.cpp @@ -26,3 +26,45 @@ AudioInputStreamOpenSLES::AudioInputStreamOpenSLES(const AudioStreamBuilder &bui AudioInputStreamOpenSLES::~AudioInputStreamOpenSLES() { } + + +int AudioInputStreamOpenSLES::chanCountToChanMask(int chanCount) { + return 0; // TODO +} + + +Result AudioInputStreamOpenSLES::open() { + + return Result::ErrorUnimplemented; // TODO +} + +Result AudioInputStreamOpenSLES::close() { + + return AudioStreamOpenSLES::close(); +} + +Result AudioInputStreamOpenSLES::requestStart() +{ + return Result::ErrorUnimplemented; // TODO +} + + +Result AudioInputStreamOpenSLES::requestPause() { + return Result::ErrorUnimplemented; // TODO +} + +Result AudioInputStreamOpenSLES::requestFlush() { + return Result::ErrorUnimplemented; // TODO +} + +Result AudioInputStreamOpenSLES::requestStop() +{ + return Result::ErrorUnimplemented; // TODO +} + +Result AudioInputStreamOpenSLES::waitForStateChange(StreamState currentState, + StreamState *nextState, + int64_t timeoutNanoseconds) +{ + return Result::ErrorUnimplemented; // TODO +} diff --git a/src/opensles/AudioInputStreamOpenSLES.h b/src/opensles/AudioInputStreamOpenSLES.h index 3e8b0ff70..5bcc1febd 100644 --- a/src/opensles/AudioInputStreamOpenSLES.h +++ b/src/opensles/AudioInputStreamOpenSLES.h @@ -32,6 +32,21 @@ class AudioInputStreamOpenSLES : public AudioStreamOpenSLES { explicit AudioInputStreamOpenSLES(const AudioStreamBuilder &builder); virtual ~AudioInputStreamOpenSLES(); + + Result open() override; + Result close() override; + + Result requestStart() override; + Result requestPause() override; + Result requestFlush() override; + Result requestStop() override; + + Result waitForStateChange(StreamState currentState, + StreamState *nextState, + int64_t timeoutNanoseconds) override; + + int chanCountToChanMask(int chanCount) override; + }; } // namespace oboe diff --git a/src/opensles/AudioOutputStreamOpenSLES.cpp b/src/opensles/AudioOutputStreamOpenSLES.cpp index 5be7b5448..7dad9672d 100644 --- a/src/opensles/AudioOutputStreamOpenSLES.cpp +++ b/src/opensles/AudioOutputStreamOpenSLES.cpp @@ -14,16 +14,225 @@ * limitations under the License. */ +#include + +#include +#include + #include "oboe/AudioStreamBuilder.h" #include "AudioOutputStreamOpenSLES.h" #include "AudioStreamOpenSLES.h" +#include "OpenSLESUtilities.h" using namespace oboe; +#define OBOE_BITS_PER_BYTE 8 // common value AudioOutputStreamOpenSLES::AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder) : AudioStreamOpenSLES(builder) { + OpenSLOutputMixer::getInstance()->open(); } AudioOutputStreamOpenSLES::~AudioOutputStreamOpenSLES() { + OpenSLOutputMixer::getInstance()->close(); +} + +// These will wind up in +#define SL_ANDROID_SPEAKER_QUAD (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \ + | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT) + +#define SL_ANDROID_SPEAKER_5DOT1 (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \ + | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY| SL_SPEAKER_BACK_LEFT \ + | SL_SPEAKER_BACK_RIGHT) + +#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT \ + |SL_SPEAKER_SIDE_RIGHT) + +int AudioOutputStreamOpenSLES::chanCountToChanMask(int chanCount) { + int channelMask = 0; + + switch (chanCount) { + case 1: + channelMask = SL_SPEAKER_FRONT_CENTER; + break; + + case 2: + channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + break; + + case 4: // Quad + channelMask = SL_ANDROID_SPEAKER_QUAD; + break; + + case 6: // 5.1 + channelMask = SL_ANDROID_SPEAKER_5DOT1; + break; + + case 8: // 7.1 + channelMask = SL_ANDROID_SPEAKER_7DOT1; + break; + } + return channelMask; +} + +// this callback handler is called every time a buffer finishes playing +static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { + ((AudioStreamOpenSLES *) context)->enqueueBuffer(); +} + +Result AudioOutputStreamOpenSLES::open() { + + Result oboeResult = AudioStreamOpenSLES::open(); + if (Result::OK != oboeResult) return oboeResult; + + SLuint32 bitsPerSample = getBytesPerSample() * OBOE_BITS_PER_BYTE; + + // configure audio source + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { + SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType + static_cast(mBurstsPerBuffer)}; // numBuffers + + // Define the audio data format. + SLDataFormat_PCM format_pcm = { + SL_DATAFORMAT_PCM, // formatType + (SLuint32) mChannelCount, // numChannels + (SLuint32) (mSampleRate * kMillisPerSecond), // milliSamplesPerSec + bitsPerSample, // bitsPerSample + bitsPerSample, // containerSize; + (SLuint32) chanCountToChanMask(mChannelCount), // channelMask + getDefaultByteOrder(), + }; + + SLDataSource audioSrc = {&loc_bufq, &format_pcm}; + + /** + * API 21 (Lollipop) introduced support for floating-point data representation and an extended + * data format type: SLAndroidDataFormat_PCM_EX. If running on API 21+ use this newer format + * type, creating it from our original format. + */ + if (__ANDROID_API__ >= __ANDROID_API_L__) { + SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat()); + SLAndroidDataFormat_PCM_EX format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, + representation); + // Overwrite the previous format. + audioSrc.pFormat = &format_pcm_ex; + } + + SLresult result = OpenSLOutputMixer::getInstance()->createAudioPlayer(&bqPlayerObject_, + &audioSrc); + if (SL_RESULT_SUCCESS != result) { + LOGE("createAudioPlayer() result:%s", getSLErrStr(result)); + goto error; + } + + result = (*bqPlayerObject_)->Realize(bqPlayerObject_, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("Realize player object result:%s", getSLErrStr(result)); + goto error; + } + + result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_PLAY, &bqPlayerPlay_); + if (SL_RESULT_SUCCESS != result) { + LOGE("get player interface result:%s", getSLErrStr(result)); + goto error; + } + + // The BufferQueue + result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &bq_); + if (SL_RESULT_SUCCESS != result) { + LOGE("get bufferqueue interface:%p result:%s", bq_, getSLErrStr(result)); + goto error; + } + + // The register BufferQueue callback + result = (*bq_)->RegisterCallback(bq_, bqPlayerCallback, this); + if (SL_RESULT_SUCCESS != result) { + LOGE("register callback result:%s", getSLErrStr(result)); + goto error; + } + + return Result::OK; +error: + return Result::ErrorInternal; // TODO convert error from SLES to OBOE +} + +Result AudioOutputStreamOpenSLES::close() { + requestPause(); + if (bqPlayerObject_ != NULL) { + (*bqPlayerObject_)->Destroy(bqPlayerObject_); + bqPlayerObject_ = NULL; + + // invalidate any interfaces + bqPlayerPlay_ = NULL; + } + return AudioStreamOpenSLES::close(); +} + +Result AudioOutputStreamOpenSLES::setPlayState(SLuint32 newState) { + Result result = Result::OK; + LOGD("AudioOutputStreamOpenSLES(): setPlayState()"); + if (bqPlayerPlay_ == NULL) { + return Result::ErrorInvalidState; + } + SLresult slResult = (*bqPlayerPlay_)->SetPlayState(bqPlayerPlay_, newState); + if(SL_RESULT_SUCCESS != slResult) { + LOGD("AudioOutputStreamOpenSLES(): setPlayState() returned %s", getSLErrStr(slResult)); + result = Result::ErrorInvalidState; // TODO review + } else { + setState(StreamState::Pausing); + } + return result; +} + +Result AudioOutputStreamOpenSLES::requestStart() { + LOGD("AudioOutputStreamOpenSLES(): requestStart()"); + Result result = setPlayState(SL_PLAYSTATE_PLAYING); + if(result != Result::OK) { + result = Result::ErrorInvalidState; // TODO review + } else { + enqueueBuffer(); + setState(StreamState::Starting); + } + return result; +} + +Result AudioOutputStreamOpenSLES::requestPause() { + LOGD("AudioOutputStreamOpenSLES(): requestPause()"); + Result result = setPlayState(SL_PLAYSTATE_PAUSED); + if(result != Result::OK) { + result = Result::ErrorInvalidState; // TODO review + } else { + setState(StreamState::Pausing); + } + return result; +} + +Result AudioOutputStreamOpenSLES::requestFlush() { + LOGD("AudioOutputStreamOpenSLES(): requestFlush()"); + if (bqPlayerPlay_ == NULL) { + return Result::ErrorInvalidState; + } + return Result::ErrorUnimplemented; // TODO +} + +Result AudioOutputStreamOpenSLES::requestStop() { + LOGD("AudioOutputStreamOpenSLES(): requestStop()"); + Result result = setPlayState(SL_PLAYSTATE_STOPPED); + if(result != Result::OK) { + result = Result::ErrorInvalidState; // TODO review + } else { + setState(StreamState::Stopping); + } + return result; +} + +Result AudioOutputStreamOpenSLES::waitForStateChange(StreamState currentState, + StreamState *nextState, + int64_t timeoutNanoseconds) { + LOGD("AudioOutputStreamOpenSLES::waitForStateChange()"); + if (bqPlayerPlay_ == NULL) { + return Result::ErrorInvalidState; + } + return Result::ErrorUnimplemented; // TODO } diff --git a/src/opensles/AudioOutputStreamOpenSLES.h b/src/opensles/AudioOutputStreamOpenSLES.h index 75e87886c..964b09252 100644 --- a/src/opensles/AudioOutputStreamOpenSLES.h +++ b/src/opensles/AudioOutputStreamOpenSLES.h @@ -32,7 +32,33 @@ class AudioOutputStreamOpenSLES : public AudioStreamOpenSLES { explicit AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder); virtual ~AudioOutputStreamOpenSLES(); + + Result open() override; + Result close() override; + + Result requestStart() override; + Result requestPause() override; + Result requestFlush() override; + Result requestStop() override; + + Result waitForStateChange(StreamState currentState, + StreamState *nextState, + int64_t timeoutNanoseconds) override; + + int chanCountToChanMask(int chanCount) override; + private: + + /** + * Set OpenSL ES PLAYSTATE. + * + * @param newState SL_PLAYSTATE_PAUSED, SL_PLAYSTATE_PLAYING, SL_PLAYSTATE_STOPPED + * @return + */ + Result setPlayState(SLuint32 newState); + + SLObjectItf bqPlayerObject_ = nullptr; + SLPlayItf bqPlayerPlay_ = nullptr; }; } // namespace oboe diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp index c1935a0ea..fd08605cd 100644 --- a/src/opensles/AudioStreamOpenSLES.cpp +++ b/src/opensles/AudioStreamOpenSLES.cpp @@ -33,76 +33,8 @@ #define DEFAULT_SAMPLE_RATE 48000 // very common rate for mobile audio and video #define DEFAULT_CHANNEL_COUNT 2 // stereo -#define OBOE_BITS_PER_BYTE 8 // common value - using namespace oboe; -/* - * OSLES Helpers - */ -static const char *errStrings[] = { - "SL_RESULT_SUCCESS", // 0 - "SL_RESULT_PRECONDITIONS_VIOLATE", // 1 - "SL_RESULT_PARAMETER_INVALID", // 2 - "SL_RESULT_MEMORY_FAILURE", // 3 - "SL_RESULT_RESOURCE_ERROR", // 4 - "SL_RESULT_RESOURCE_LOST", // 5 - "SL_RESULT_IO_ERROR", // 6 - "SL_RESULT_BUFFER_INSUFFICIENT", // 7 - "SL_RESULT_CONTENT_CORRUPTED", // 8 - "SL_RESULT_CONTENT_UNSUPPORTED", // 9 - "SL_RESULT_CONTENT_NOT_FOUND", // 10 - "SL_RESULT_PERMISSION_DENIED", // 11 - "SL_RESULT_FEATURE_UNSUPPORTED", // 12 - "SL_RESULT_INTERNAL_ERROR", // 13 - "SL_RESULT_UNKNOWN_ERROR", // 14 - "SL_RESULT_OPERATION_ABORTED", // 15 - "SL_RESULT_CONTROL_LOST" // 16 -}; - -const char *getSLErrStr(SLresult code) { - return errStrings[code]; -} - -// These will wind up in -#define SL_ANDROID_SPEAKER_QUAD (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \ - | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT) - -#define SL_ANDROID_SPEAKER_5DOT1 (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \ - | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY| SL_SPEAKER_BACK_LEFT \ - | SL_SPEAKER_BACK_RIGHT) - -#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT \ - |SL_SPEAKER_SIDE_RIGHT) - -int chanCountToChanMask(int chanCount) { - int channelMask = 0; - - switch (chanCount) { - case 1: - channelMask = SL_SPEAKER_FRONT_CENTER; - break; - - case 2: - channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - break; - - case 4: // Quad - channelMask = SL_ANDROID_SPEAKER_QUAD; - break; - - case 6: // 5.1 - channelMask = SL_ANDROID_SPEAKER_5DOT1; - break; - - case 8: // 7.1 - channelMask = SL_ANDROID_SPEAKER_7DOT1; - break; - } - return channelMask; -} - -static const char *TAG = "AAudioStreamOpenSLES"; OpenSLEngine *OpenSLEngine::getInstance() { // TODO mutex @@ -240,11 +172,6 @@ SLresult OpenSLOutputMixer::createAudioPlayer(SLObjectItf *objectItf, OpenSLOutputMixer *OpenSLOutputMixer::sInstance = nullptr; -// this callback handler is called every time a buffer finishes playing -static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { - ((AudioStreamOpenSLES *) context)->enqueueBuffer(); -} - SLresult AudioStreamOpenSLES::enqueueBuffer() { // Ask the callback to fill the output buffer with data. Result result = fireCallback(mCallbackBuffer, mFramesPerCallback); @@ -257,65 +184,31 @@ SLresult AudioStreamOpenSLES::enqueueBuffer() { } } - -static void CloseSLEngine() { - OpenSLOutputMixer::getInstance()->close(); - OpenSLEngine::getInstance()->close(); -} - -static SLresult OpenSLContext() { - SLresult result = OpenSLEngine::getInstance()->open(); - if (SL_RESULT_SUCCESS == result) { - // get the output mixer - result = OpenSLOutputMixer::getInstance()->open(); - if (SL_RESULT_SUCCESS != result) { - CloseSLEngine(); - } - } - return result; -} - AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder) : AudioStreamBuffered(builder) { - bqPlayerObject_ = NULL; bq_ = NULL; - bqPlayerPlay_ = NULL; mFramesPerBurst = builder.getDefaultFramesPerBurst(); - OpenSLContext(); + OpenSLEngine::getInstance()->open(); LOGD("AudioStreamOpenSLES(): after OpenSLContext()"); } AudioStreamOpenSLES::~AudioStreamOpenSLES() { - CloseSLEngine(); + OpenSLEngine::getInstance()->close(); delete[] mCallbackBuffer; } -static SLuint32 ConvertFormatToRepresentation(AudioFormat format) { - switch(format) { - case AudioFormat::Invalid: - case AudioFormat::Unspecified: - return 0; - case AudioFormat::I16: - return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; - case AudioFormat::Float: - return SL_ANDROID_PCM_REPRESENTATION_FLOAT; - } -} - static bool s_isLittleEndian() { static uint32_t value = 1; return *((uint8_t *) &value) == 1; // Does address point to LSB? } -static SLuint32 s_getDefaultByteOrder() { +SLuint32 AudioStreamOpenSLES::getDefaultByteOrder() { return s_isLittleEndian() ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; } Result AudioStreamOpenSLES::open() { - SLresult result; - - __android_log_print(ANDROID_LOG_INFO, TAG, "AudioPlayerOpenSLES::Open(chans:%d, rate:%d)", + LOGI("AudioStreamOpenSLES::open(chans:%d, rate:%d)", mChannelCount, mSampleRate); if (__ANDROID_API__ < __ANDROID_API_L__ && mFormat == AudioFormat::Float){ @@ -358,62 +251,6 @@ Result AudioStreamOpenSLES::open() { LOGD("AudioStreamOpenSLES(): mFramesPerCallback = %d", mFramesPerCallback); LOGD("AudioStreamOpenSLES(): mBytesPerCallback = %d", mBytesPerCallback); - SLuint32 bitsPerSample = getBytesPerSample() * OBOE_BITS_PER_BYTE; - - // configure audio source - SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { - SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType - static_cast(mBurstsPerBuffer)}; // numBuffers - - // Define the audio data format. - SLDataFormat_PCM format_pcm = { - SL_DATAFORMAT_PCM, // formatType - (SLuint32) mChannelCount, // numChannels - (SLuint32) (mSampleRate * kMillisPerSecond), // milliSamplesPerSec - bitsPerSample, // bitsPerSample - bitsPerSample, // containerSize; - (SLuint32) chanCountToChanMask(mChannelCount), // channelMask - s_getDefaultByteOrder(), - }; - - SLDataSource audioSrc = {&loc_bufq, &format_pcm}; - - /** - * API 21 (Lollipop) introduced support for floating-point data representation and an extended - * data format type: SLAndroidDataFormat_PCM_EX. If running on API 21+ use this newer format - * type, creating it from our original format. - */ - if (__ANDROID_API__ >= __ANDROID_API_L__) { - SLuint32 representation = ConvertFormatToRepresentation(getFormat()); - SLAndroidDataFormat_PCM_EX format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, - representation); - // Overwrite the previous format. - audioSrc.pFormat = &format_pcm_ex; - } - - result = OpenSLOutputMixer::getInstance()->createAudioPlayer(&bqPlayerObject_, &audioSrc); - LOGD("CreateAudioPlayer() result:%s", getSLErrStr(result)); - assert(SL_RESULT_SUCCESS == result); - - result = (*bqPlayerObject_)->Realize(bqPlayerObject_, SL_BOOLEAN_FALSE); - LOGD("Realize player object result:%s", getSLErrStr(result)); - assert(SL_RESULT_SUCCESS == result); - - result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_PLAY, &bqPlayerPlay_); - LOGD("get player interface result:%s", getSLErrStr(result)); - assert(SL_RESULT_SUCCESS == result); - - // The BufferQueue - result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - &bq_); - LOGD("get bufferqueue interface:%p result:%s", bq_, getSLErrStr(result)); - assert(SL_RESULT_SUCCESS == result); - - // The register BufferQueue callback - result = (*bq_)->RegisterCallback(bq_, bqPlayerCallback, this); - LOGD("register callback result:%s", getSLErrStr(result)); - assert(SL_RESULT_SUCCESS == result); - mSharingMode = SharingMode::Shared; mBufferCapacityInFrames = mFramesPerBurst * mBurstsPerBuffer; @@ -421,92 +258,10 @@ Result AudioStreamOpenSLES::open() { } Result AudioStreamOpenSLES::close() { -// __android_log_write(ANDROID_LOG_INFO, TAG, "AudioStreamOpenSLES()"); - // TODO make sure callback is no longer being called - if (bqPlayerObject_ != NULL) { - (*bqPlayerObject_)->Destroy(bqPlayerObject_); - bqPlayerObject_ = NULL; - - // invalidate any interfaces - bqPlayerPlay_ = NULL; - bq_ = NULL; - } + bq_ = NULL; return Result::OK; } -Result AudioStreamOpenSLES::setPlayState(SLuint32 newState) -{ - Result result = Result::OK; - LOGD("AudioStreamOpenSLES(): setPlayState()"); - if (bqPlayerPlay_ == NULL) { - return Result::ErrorInvalidState; - } - SLresult slResult = (*bqPlayerPlay_)->SetPlayState(bqPlayerPlay_, newState); - if(SL_RESULT_SUCCESS != slResult) { - LOGD("AudioStreamOpenSLES(): setPlayState() returned %s", getSLErrStr(slResult)); - result = Result::ErrorInvalidState; // TODO review - } else { - setState(StreamState::Pausing); - } - return result; -} - -Result AudioStreamOpenSLES::requestStart() -{ - LOGD("AudioStreamOpenSLES(): requestStart()"); - Result result = setPlayState(SL_PLAYSTATE_PLAYING); - if(result != Result::OK) { - result = Result::ErrorInvalidState; // TODO review - } else { - enqueueBuffer(); - setState(StreamState::Starting); - } - return result; -} - - -Result AudioStreamOpenSLES::requestPause() { - LOGD("AudioStreamOpenSLES(): requestPause()"); - Result result = setPlayState(SL_PLAYSTATE_PAUSED); - if(result != Result::OK) { - result = Result::ErrorInvalidState; // TODO review - } else { - setState(StreamState::Pausing); - } - return result; -} - -Result AudioStreamOpenSLES::requestFlush() { - LOGD("AudioStreamOpenSLES(): requestFlush()"); - if (bqPlayerPlay_ == NULL) { - return Result::ErrorInvalidState; - } - return Result::ErrorUnimplemented; // TODO -} - -Result AudioStreamOpenSLES::requestStop() -{ - LOGD("AudioStreamOpenSLES(): requestStop()"); - Result result = setPlayState(SL_PLAYSTATE_STOPPED); - if(result != Result::OK) { - result = Result::ErrorInvalidState; // TODO review - } else { - setState(StreamState::Stopping); - } - return result; -} - -Result AudioStreamOpenSLES::waitForStateChange(StreamState currentState, - StreamState *nextState, - int64_t timeoutNanoseconds) -{ - LOGD("AudioStreamOpenSLES(): waitForStateChange()"); - if (bqPlayerPlay_ == NULL) { - return Result::ErrorInvalidState; - } - return Result::ErrorUnimplemented; // TODO -} - int32_t AudioStreamOpenSLES::getFramesPerBurst() { return mFramesPerBurst; } diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h index a41d240a1..653048c5b 100644 --- a/src/opensles/AudioStreamOpenSLES.h +++ b/src/opensles/AudioStreamOpenSLES.h @@ -83,21 +83,12 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { virtual ~AudioStreamOpenSLES(); - Result open() override; - Result close() override; - - Result requestStart() override; - Result requestPause() override; - Result requestFlush() override; - Result requestStop() override; + virtual Result open() override; + virtual Result close() override; // public, but don't call directly (called by the OSLES callback) SLresult enqueueBuffer(); - Result waitForStateChange(StreamState currentState, - StreamState *nextState, - int64_t timeoutNanoseconds) override; - /** * Query the current state, eg. OBOE_STREAM_STATE_PAUSING * @@ -107,9 +98,11 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { int32_t getFramesPerBurst() override; -protected: -private: + static SLuint32 getDefaultByteOrder(); + + virtual int chanCountToChanMask(int chanCount) = 0; +protected: /** * Internal use only. * Use this instead of directly setting the internal state variable. @@ -118,14 +111,6 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { mState = state; } - /** - * Set OpenSL ES state. - * - * @param newState SL_PLAYSTATE_PAUSED, SL_PLAYSTATE_PLAYING, SL_PLAYSTATE_STOPPED - * @return - */ - Result setPlayState(SLuint32 newState); - uint8_t *mCallbackBuffer; int32_t mBytesPerCallback; int32_t mFramesPerBurst = 0; @@ -133,9 +118,7 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { StreamState mState = StreamState::Uninitialized; // OpenSLES stuff - SLObjectItf bqPlayerObject_; - SLPlayItf bqPlayerPlay_; - SLAndroidSimpleBufferQueueItf bq_; + SLAndroidSimpleBufferQueueItf bq_ = nullptr; }; } // namespace oboe diff --git a/src/opensles/OpenSLESUtilities.cpp b/src/opensles/OpenSLESUtilities.cpp index 0636bda32..c764af382 100644 --- a/src/opensles/OpenSLESUtilities.cpp +++ b/src/opensles/OpenSLESUtilities.cpp @@ -18,6 +18,33 @@ namespace oboe { +/* + * OSLES Helpers + */ +static const char *errStrings[] = { + "SL_RESULT_SUCCESS", // 0 + "SL_RESULT_PRECONDITIONS_VIOLATE", // 1 + "SL_RESULT_PARAMETER_INVALID", // 2 + "SL_RESULT_MEMORY_FAILURE", // 3 + "SL_RESULT_RESOURCE_ERROR", // 4 + "SL_RESULT_RESOURCE_LOST", // 5 + "SL_RESULT_IO_ERROR", // 6 + "SL_RESULT_BUFFER_INSUFFICIENT", // 7 + "SL_RESULT_CONTENT_CORRUPTED", // 8 + "SL_RESULT_CONTENT_UNSUPPORTED", // 9 + "SL_RESULT_CONTENT_NOT_FOUND", // 10 + "SL_RESULT_PERMISSION_DENIED", // 11 + "SL_RESULT_FEATURE_UNSUPPORTED", // 12 + "SL_RESULT_INTERNAL_ERROR", // 13 + "SL_RESULT_UNKNOWN_ERROR", // 14 + "SL_RESULT_OPERATION_ABORTED", // 15 + "SL_RESULT_CONTROL_LOST" // 16 +}; + +const char *getSLErrStr(SLresult code) { + return errStrings[code]; +} + SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat( SLDataFormat_PCM format, SLuint32 representation) { SLAndroidDataFormat_PCM_EX format_pcm_ex; @@ -32,4 +59,15 @@ SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat( return format_pcm_ex; } +SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format) { + switch(format) { + case AudioFormat::Invalid: + case AudioFormat::Unspecified: + return 0; + case AudioFormat::I16: + return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; + case AudioFormat::Float: + return SL_ANDROID_PCM_REPRESENTATION_FLOAT; + } +} } // namespace oboe \ No newline at end of file diff --git a/src/opensles/OpenSLESUtilities.h b/src/opensles/OpenSLESUtilities.h index a0b73af4b..20f5498e5 100644 --- a/src/opensles/OpenSLESUtilities.h +++ b/src/opensles/OpenSLESUtilities.h @@ -18,9 +18,12 @@ #define OBOE_OPENSLES_OPENSLESUTILITIES_H #include +#include "oboe/Oboe.h" namespace oboe { +const char *getSLErrStr(SLresult code); + /** * Creates an extended PCM format from the supplied format and data representation. This method * should only be called on Android devices with API level 21+. API 21 introduced the @@ -34,6 +37,9 @@ namespace oboe { SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat(SLDataFormat_PCM format, SLuint32 representation); + +SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format); + } // namespace oboe #endif //OBOE_OPENSLES_OPENSLESUTILITIES_H From 6d952910338b9a24b3ee3b6328a70bf9875bec7c Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 14 Dec 2017 10:10:00 -0800 Subject: [PATCH 3/7] oboe: implement input stream Also fix some bugs in OpenSL ES implementation and replace asserts with error returns. --- src/opensles/AudioInputStreamOpenSLES.cpp | 175 +++++++++++++++++++-- src/opensles/AudioInputStreamOpenSLES.h | 4 + src/opensles/AudioOutputStreamOpenSLES.cpp | 53 ++----- src/opensles/AudioOutputStreamOpenSLES.h | 4 +- src/opensles/AudioStreamOpenSLES.cpp | 76 +++++++-- src/opensles/AudioStreamOpenSLES.h | 27 +++- 6 files changed, 267 insertions(+), 72 deletions(-) diff --git a/src/opensles/AudioInputStreamOpenSLES.cpp b/src/opensles/AudioInputStreamOpenSLES.cpp index 694e97006..5c9ecd78f 100644 --- a/src/opensles/AudioInputStreamOpenSLES.cpp +++ b/src/opensles/AudioInputStreamOpenSLES.cpp @@ -14,9 +14,15 @@ * limitations under the License. */ +#include + +#include +#include + #include "oboe/AudioStreamBuilder.h" #include "AudioInputStreamOpenSLES.h" #include "AudioStreamOpenSLES.h" +#include "OpenSLESUtilities.h" using namespace oboe; @@ -27,44 +33,187 @@ AudioInputStreamOpenSLES::AudioInputStreamOpenSLES(const AudioStreamBuilder &bui AudioInputStreamOpenSLES::~AudioInputStreamOpenSLES() { } - -int AudioInputStreamOpenSLES::chanCountToChanMask(int chanCount) { - return 0; // TODO +#define AUDIO_CHANNEL_COUNT_MAX 30u +#define SL_ANDROID_UNKNOWN_CHANNELMASK 0 + +int AudioInputStreamOpenSLES::chanCountToChanMask(int channelCount) { + // stolen from sles_channel_in_mask_from_count(chanCount); + switch (channelCount) { + case 1: + return SL_SPEAKER_FRONT_LEFT; + case 2: + return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + default: { + if (channelCount > AUDIO_CHANNEL_COUNT_MAX) { + return SL_ANDROID_UNKNOWN_CHANNELMASK; + } else { + SLuint32 bitfield = (1 << channelCount) - 1; + return SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(bitfield); + } + } + } } - Result AudioInputStreamOpenSLES::open() { - return Result::ErrorUnimplemented; // TODO + Result oboeResult = AudioStreamOpenSLES::open(); + if (Result::OK != oboeResult) return oboeResult; + + SLuint32 bitsPerSample = getBytesPerSample() * OBOE_BITS_PER_BYTE; + + // configure audio sink + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { + SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType + static_cast(mBurstsPerBuffer)}; // numBuffers + + // Define the audio data format. + SLDataFormat_PCM format_pcm = { + SL_DATAFORMAT_PCM, // formatType + (SLuint32) mChannelCount, // numChannels + (SLuint32) (mSampleRate * kMillisPerSecond), // milliSamplesPerSec + bitsPerSample, // bitsPerSample + bitsPerSample, // containerSize; + (SLuint32) chanCountToChanMask(mChannelCount), // channelMask + getDefaultByteOrder(), + }; + + SLDataSink audioSink = {&loc_bufq, &format_pcm}; + + /** + * API 21 (Lollipop) introduced support for floating-point data representation and an extended + * data format type: SLAndroidDataFormat_PCM_EX. If running on API 21+ use this newer format + * type, creating it from our original format. + */ + SLAndroidDataFormat_PCM_EX format_pcm_ex; + if (__ANDROID_API__ >= __ANDROID_API_L__) { + SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat()); + // Fill in the format structure. + format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, representation); + // Use in place of the previous format. + audioSink.pFormat = &format_pcm_ex; + } + + // configure audio source + SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, + SL_IODEVICE_AUDIOINPUT, + SL_DEFAULTDEVICEID_AUDIOINPUT, + NULL }; + SLDataSource audioSrc = {&loc_dev, NULL }; + + SLresult result = OpenSLEngine::getInstance()->createAudioRecorder(&mObjectInterface, + &audioSrc, + &audioSink); + if (SL_RESULT_SUCCESS != result) { + LOGE("createAudioRecorder() result:%s", getSLErrStr(result)); + goto error; + } + + // Configure the voice recognition preset, which has no + // signal processing, for lower latency. + SLAndroidConfigurationItf inputConfig; + result = (*mObjectInterface)->GetInterface(mObjectInterface, + SL_IID_ANDROIDCONFIGURATION, + &inputConfig); + if (SL_RESULT_SUCCESS == result) { + SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; + (*inputConfig)->SetConfiguration(inputConfig, + SL_ANDROID_KEY_RECORDING_PRESET, + &presetValue, + sizeof(SLuint32)); + } + + result = (*mObjectInterface)->Realize(mObjectInterface, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("Realize recorder object result:%s", getSLErrStr(result)); + goto error; + } + + result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_RECORD, &mRecordInterface); + if (SL_RESULT_SUCCESS != result) { + LOGE("get recorder interface result:%s", getSLErrStr(result)); + goto error; + } + + result = AudioStreamOpenSLES::registerBufferQueueCallback(); + if (SL_RESULT_SUCCESS != result) { + LOGE("get bufferqueue interface:%p result:%s", mSimpleBufferQueueInterface, getSLErrStr(result)); + goto error; + } + + return Result::OK; +error: + return Result::ErrorInternal; // TODO convert error from SLES to OBOE } Result AudioInputStreamOpenSLES::close() { - + requestStop(); + mRecordInterface = NULL; return AudioStreamOpenSLES::close(); } +Result AudioInputStreamOpenSLES::setRecordState(SLuint32 newState) { + Result result = Result::OK; + LOGD("AudioInputStreamOpenSLES::setRecordState(%d)", newState); + if (mRecordInterface == NULL) { + return Result::ErrorInvalidState; + } + SLresult slResult = (*mRecordInterface)->SetRecordState(mRecordInterface, newState); + if(SL_RESULT_SUCCESS != slResult) { + LOGD("AudioInputStreamOpenSLES::setPlayState() returned %s", getSLErrStr(slResult)); + result = Result::ErrorInvalidState; // TODO review + } else { + setState(StreamState::Pausing); + } + return result; +} + Result AudioInputStreamOpenSLES::requestStart() { - return Result::ErrorUnimplemented; // TODO + LOGD("AudioInputStreamOpenSLES::requestStart()"); + Result result = setRecordState(SL_RECORDSTATE_RECORDING); + if(result != Result::OK) { + result = Result::ErrorInvalidState; // TODO review + } else { + // Enqueue the first buffer so that we have data ready in the callback. + enqueueCallbackBuffer(mSimpleBufferQueueInterface); + setState(StreamState::Starting); + } + return result; } Result AudioInputStreamOpenSLES::requestPause() { - return Result::ErrorUnimplemented; // TODO + LOGD("AudioInputStreamOpenSLES::requestStop()"); + Result result = setRecordState(SL_RECORDSTATE_PAUSED); + if(result != Result::OK) { + result = Result::ErrorInvalidState; // TODO review + } else { + setState(StreamState::Pausing); + } + return result; } Result AudioInputStreamOpenSLES::requestFlush() { return Result::ErrorUnimplemented; // TODO } -Result AudioInputStreamOpenSLES::requestStop() -{ - return Result::ErrorUnimplemented; // TODO +Result AudioInputStreamOpenSLES::requestStop() { + LOGD("AudioInputStreamOpenSLES::requestStop()"); + Result result = setRecordState(SL_RECORDSTATE_STOPPED); + if(result != Result::OK) { + result = Result::ErrorInvalidState; // TODO review + } else { + setState(StreamState::Stopping); + } + return result; } Result AudioInputStreamOpenSLES::waitForStateChange(StreamState currentState, StreamState *nextState, - int64_t timeoutNanoseconds) -{ + int64_t timeoutNanoseconds) { + LOGD("AudioInputStreamOpenSLES::waitForStateChange()"); + if (mRecordInterface == NULL) { + return Result::ErrorInvalidState; + } return Result::ErrorUnimplemented; // TODO } diff --git a/src/opensles/AudioInputStreamOpenSLES.h b/src/opensles/AudioInputStreamOpenSLES.h index 5bcc1febd..83f608cff 100644 --- a/src/opensles/AudioInputStreamOpenSLES.h +++ b/src/opensles/AudioInputStreamOpenSLES.h @@ -46,7 +46,11 @@ class AudioInputStreamOpenSLES : public AudioStreamOpenSLES { int64_t timeoutNanoseconds) override; int chanCountToChanMask(int chanCount) override; +private: + Result setRecordState(SLuint32 newState); + + SLRecordItf mRecordInterface; }; } // namespace oboe diff --git a/src/opensles/AudioOutputStreamOpenSLES.cpp b/src/opensles/AudioOutputStreamOpenSLES.cpp index 7dad9672d..d963ed421 100644 --- a/src/opensles/AudioOutputStreamOpenSLES.cpp +++ b/src/opensles/AudioOutputStreamOpenSLES.cpp @@ -26,8 +26,6 @@ using namespace oboe; -#define OBOE_BITS_PER_BYTE 8 // common value - AudioOutputStreamOpenSLES::AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder) : AudioStreamOpenSLES(builder) { OpenSLOutputMixer::getInstance()->open(); @@ -75,13 +73,7 @@ int AudioOutputStreamOpenSLES::chanCountToChanMask(int chanCount) { return channelMask; } -// this callback handler is called every time a buffer finishes playing -static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { - ((AudioStreamOpenSLES *) context)->enqueueBuffer(); -} - Result AudioOutputStreamOpenSLES::open() { - Result oboeResult = AudioStreamOpenSLES::open(); if (Result::OK != oboeResult) return oboeResult; @@ -110,45 +102,37 @@ Result AudioOutputStreamOpenSLES::open() { * data format type: SLAndroidDataFormat_PCM_EX. If running on API 21+ use this newer format * type, creating it from our original format. */ + SLAndroidDataFormat_PCM_EX format_pcm_ex; if (__ANDROID_API__ >= __ANDROID_API_L__) { SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat()); - SLAndroidDataFormat_PCM_EX format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, - representation); - // Overwrite the previous format. + // Fill in the format structure. + format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, representation); + // Use in place of the previous format. audioSrc.pFormat = &format_pcm_ex; } - SLresult result = OpenSLOutputMixer::getInstance()->createAudioPlayer(&bqPlayerObject_, + SLresult result = OpenSLOutputMixer::getInstance()->createAudioPlayer(&mObjectInterface, &audioSrc); if (SL_RESULT_SUCCESS != result) { LOGE("createAudioPlayer() result:%s", getSLErrStr(result)); goto error; } - result = (*bqPlayerObject_)->Realize(bqPlayerObject_, SL_BOOLEAN_FALSE); + result = (*mObjectInterface)->Realize(mObjectInterface, SL_BOOLEAN_FALSE); if (SL_RESULT_SUCCESS != result) { LOGE("Realize player object result:%s", getSLErrStr(result)); goto error; } - result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_PLAY, &bqPlayerPlay_); + result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_PLAY, &mPlayInterface); if (SL_RESULT_SUCCESS != result) { LOGE("get player interface result:%s", getSLErrStr(result)); goto error; } - // The BufferQueue - result = (*bqPlayerObject_)->GetInterface(bqPlayerObject_, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - &bq_); - if (SL_RESULT_SUCCESS != result) { - LOGE("get bufferqueue interface:%p result:%s", bq_, getSLErrStr(result)); - goto error; - } - - // The register BufferQueue callback - result = (*bq_)->RegisterCallback(bq_, bqPlayerCallback, this); + result = AudioStreamOpenSLES::registerBufferQueueCallback(); if (SL_RESULT_SUCCESS != result) { - LOGE("register callback result:%s", getSLErrStr(result)); + LOGE("get bufferqueue interface:%p result:%s", mSimpleBufferQueueInterface, getSLErrStr(result)); goto error; } @@ -159,23 +143,18 @@ Result AudioOutputStreamOpenSLES::open() { Result AudioOutputStreamOpenSLES::close() { requestPause(); - if (bqPlayerObject_ != NULL) { - (*bqPlayerObject_)->Destroy(bqPlayerObject_); - bqPlayerObject_ = NULL; - - // invalidate any interfaces - bqPlayerPlay_ = NULL; - } + // invalidate any interfaces + mPlayInterface = NULL; return AudioStreamOpenSLES::close(); } Result AudioOutputStreamOpenSLES::setPlayState(SLuint32 newState) { Result result = Result::OK; LOGD("AudioOutputStreamOpenSLES(): setPlayState()"); - if (bqPlayerPlay_ == NULL) { + if (mPlayInterface == NULL) { return Result::ErrorInvalidState; } - SLresult slResult = (*bqPlayerPlay_)->SetPlayState(bqPlayerPlay_, newState); + SLresult slResult = (*mPlayInterface)->SetPlayState(mPlayInterface, newState); if(SL_RESULT_SUCCESS != slResult) { LOGD("AudioOutputStreamOpenSLES(): setPlayState() returned %s", getSLErrStr(slResult)); result = Result::ErrorInvalidState; // TODO review @@ -191,7 +170,7 @@ Result AudioOutputStreamOpenSLES::requestStart() { if(result != Result::OK) { result = Result::ErrorInvalidState; // TODO review } else { - enqueueBuffer(); + processBufferCallback(mSimpleBufferQueueInterface); setState(StreamState::Starting); } return result; @@ -210,7 +189,7 @@ Result AudioOutputStreamOpenSLES::requestPause() { Result AudioOutputStreamOpenSLES::requestFlush() { LOGD("AudioOutputStreamOpenSLES(): requestFlush()"); - if (bqPlayerPlay_ == NULL) { + if (mPlayInterface == NULL) { return Result::ErrorInvalidState; } return Result::ErrorUnimplemented; // TODO @@ -231,7 +210,7 @@ Result AudioOutputStreamOpenSLES::waitForStateChange(StreamState currentState, StreamState *nextState, int64_t timeoutNanoseconds) { LOGD("AudioOutputStreamOpenSLES::waitForStateChange()"); - if (bqPlayerPlay_ == NULL) { + if (mPlayInterface == NULL) { return Result::ErrorInvalidState; } return Result::ErrorUnimplemented; // TODO diff --git a/src/opensles/AudioOutputStreamOpenSLES.h b/src/opensles/AudioOutputStreamOpenSLES.h index 964b09252..c5c6de926 100644 --- a/src/opensles/AudioOutputStreamOpenSLES.h +++ b/src/opensles/AudioOutputStreamOpenSLES.h @@ -57,8 +57,8 @@ class AudioOutputStreamOpenSLES : public AudioStreamOpenSLES { */ Result setPlayState(SLuint32 newState); - SLObjectItf bqPlayerObject_ = nullptr; - SLPlayItf bqPlayerPlay_ = nullptr; + SLPlayItf mPlayInterface = nullptr; + }; } // namespace oboe diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp index fd08605cd..d6a7b2a74 100644 --- a/src/opensles/AudioStreamOpenSLES.cpp +++ b/src/opensles/AudioStreamOpenSLES.cpp @@ -103,12 +103,26 @@ SLresult OpenSLEngine::createAudioPlayer(SLObjectItf *objectItf, SLDataSink *audioSink) { const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE}; - const SLboolean req[] = {SL_BOOLEAN_TRUE}; + const SLboolean reqs[] = {SL_BOOLEAN_TRUE}; // The Player return (*sEngineEngine)->CreateAudioPlayer(sEngineEngine, objectItf, audioSource, audioSink, - sizeof(ids) / sizeof(ids[0]), ids, req); + sizeof(ids) / sizeof(ids[0]), ids, reqs); +} + +SLresult OpenSLEngine::createAudioRecorder(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink) { + + const SLInterfaceID ids[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + SL_IID_ANDROIDCONFIGURATION }; + const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + + // The Player + return (*sEngineEngine)->CreateAudioRecorder(sEngineEngine, objectItf, audioSource, + audioSink, + sizeof(ids) / sizeof(ids[0]), ids, reqs); } OpenSLEngine *OpenSLEngine::sInstance = nullptr; @@ -172,21 +186,10 @@ SLresult OpenSLOutputMixer::createAudioPlayer(SLObjectItf *objectItf, OpenSLOutputMixer *OpenSLOutputMixer::sInstance = nullptr; -SLresult AudioStreamOpenSLES::enqueueBuffer() { - // Ask the callback to fill the output buffer with data. - Result result = fireCallback(mCallbackBuffer, mFramesPerCallback); - if (result != Result::OK) { - LOGE("Oboe callback returned %d", result); - return SL_RESULT_INTERNAL_ERROR; - } else { - // Pass the data to OpenSLES. - return (*bq_)->Enqueue(bq_, mCallbackBuffer, mBytesPerCallback); - } -} AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder) : AudioStreamBuffered(builder) { - bq_ = NULL; + mSimpleBufferQueueInterface = NULL; mFramesPerBurst = builder.getDefaultFramesPerBurst(); OpenSLEngine::getInstance()->open(); LOGD("AudioStreamOpenSLES(): after OpenSLContext()"); @@ -258,10 +261,53 @@ Result AudioStreamOpenSLES::open() { } Result AudioStreamOpenSLES::close() { - bq_ = NULL; + if (mObjectInterface != NULL) { + (*mObjectInterface)->Destroy(mObjectInterface); + mObjectInterface = NULL; + + } + mSimpleBufferQueueInterface = NULL; return Result::OK; } +SLresult AudioStreamOpenSLES::enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq) { + return (*bq)->Enqueue(bq, mCallbackBuffer, mBytesPerCallback); +} + +SLresult AudioStreamOpenSLES::processBufferCallback(SLAndroidSimpleBufferQueueItf bq) { + // Ask the callback to fill the output buffer with data. + Result result = fireCallback(mCallbackBuffer, mFramesPerCallback); + if (result != Result::OK) { + LOGE("Oboe callback returned %d", result); + return SL_RESULT_INTERNAL_ERROR; + } else { + // Pass the data to OpenSLES. + return enqueueCallbackBuffer(bq); + } +} + +// this callback handler is called every time a buffer needs processing +static void bqCallbackGlue(SLAndroidSimpleBufferQueueItf bq, void *context) { + ((AudioStreamOpenSLES *) context)->processBufferCallback(bq); +} + +SLresult AudioStreamOpenSLES::registerBufferQueueCallback() { + // The BufferQueue + SLresult result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &mSimpleBufferQueueInterface); + if (SL_RESULT_SUCCESS != result) { + LOGE("get bufferqueue interface:%p result:%s", mSimpleBufferQueueInterface, getSLErrStr(result)); + } else { + // Register the BufferQueue callback + result = (*mSimpleBufferQueueInterface)->RegisterCallback(mSimpleBufferQueueInterface, + bqCallbackGlue, this); + if (SL_RESULT_SUCCESS != result) { + LOGE("RegisterCallback result:%s", getSLErrStr(result)); + } + } + return result; +} + int32_t AudioStreamOpenSLES::getFramesPerBurst() { return mFramesPerBurst; } diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h index 653048c5b..9a8fa95ab 100644 --- a/src/opensles/AudioStreamOpenSLES.h +++ b/src/opensles/AudioStreamOpenSLES.h @@ -25,6 +25,8 @@ namespace oboe { +#define OBOE_BITS_PER_BYTE 8 // common value TODO modernize + class OpenSLEngine { public: static OpenSLEngine *getInstance(); @@ -38,6 +40,9 @@ class OpenSLEngine { SLresult createAudioPlayer(SLObjectItf *objectItf, SLDataSource *audioSource, SLDataSink *audioSink); + SLresult createAudioRecorder(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink); private: static OpenSLEngine *sInstance; @@ -86,8 +91,6 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { virtual Result open() override; virtual Result close() override; - // public, but don't call directly (called by the OSLES callback) - SLresult enqueueBuffer(); /** * Query the current state, eg. OBOE_STREAM_STATE_PAUSING @@ -102,7 +105,20 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { virtual int chanCountToChanMask(int chanCount) = 0; + /** + * Process next OpenSL ES buffer. + * Called by by OpenSL ES framework. + * + * This is public, but don't call it directly. + */ + SLresult processBufferCallback(SLAndroidSimpleBufferQueueItf bq); + protected: + + SLresult registerBufferQueueCallback(); + + SLresult enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq); + /** * Internal use only. * Use this instead of directly setting the internal state variable. @@ -111,14 +127,15 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { mState = state; } + // OpenSLES stuff + SLObjectItf mObjectInterface = nullptr; + SLAndroidSimpleBufferQueueItf mSimpleBufferQueueInterface = nullptr; + uint8_t *mCallbackBuffer; int32_t mBytesPerCallback; int32_t mFramesPerBurst = 0; int32_t mBurstsPerBuffer = 2; // Double buffered StreamState mState = StreamState::Uninitialized; - - // OpenSLES stuff - SLAndroidSimpleBufferQueueItf bq_ = nullptr; }; } // namespace oboe From 420715bce234f28050c92a3f716db2c4a6f47731 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 14 Dec 2017 12:26:38 -0800 Subject: [PATCH 4/7] oboe: cleanup OpenSL ES input Use mutex. Better singleton behavior. Separate files for each class. --- src/opensles/AudioInputStreamOpenSLES.cpp | 2 +- src/opensles/AudioInputStreamOpenSLES.h | 4 + src/opensles/AudioOutputStreamOpenSLES.cpp | 7 +- src/opensles/AudioOutputStreamOpenSLES.h | 3 + src/opensles/AudioStreamOpenSLES.cpp | 154 +-------------------- src/opensles/AudioStreamOpenSLES.h | 54 +------- src/opensles/EngineOpenSLES.cpp | 102 ++++++++++++++ src/opensles/EngineOpenSLES.h | 58 ++++++++ src/opensles/OutputMixerOpenSLES.cpp | 74 ++++++++++ src/opensles/OutputMixerOpenSLES.h | 51 +++++++ 10 files changed, 306 insertions(+), 203 deletions(-) create mode 100644 src/opensles/EngineOpenSLES.cpp create mode 100644 src/opensles/EngineOpenSLES.h create mode 100644 src/opensles/OutputMixerOpenSLES.cpp create mode 100644 src/opensles/OutputMixerOpenSLES.h diff --git a/src/opensles/AudioInputStreamOpenSLES.cpp b/src/opensles/AudioInputStreamOpenSLES.cpp index 5c9ecd78f..3d82b5e87 100644 --- a/src/opensles/AudioInputStreamOpenSLES.cpp +++ b/src/opensles/AudioInputStreamOpenSLES.cpp @@ -100,7 +100,7 @@ Result AudioInputStreamOpenSLES::open() { NULL }; SLDataSource audioSrc = {&loc_dev, NULL }; - SLresult result = OpenSLEngine::getInstance()->createAudioRecorder(&mObjectInterface, + SLresult result = EngineOpenSLES::getInstance()->createAudioRecorder(&mObjectInterface, &audioSrc, &audioSink); if (SL_RESULT_SUCCESS != result) { diff --git a/src/opensles/AudioInputStreamOpenSLES.h b/src/opensles/AudioInputStreamOpenSLES.h index 83f608cff..9fb79f576 100644 --- a/src/opensles/AudioInputStreamOpenSLES.h +++ b/src/opensles/AudioInputStreamOpenSLES.h @@ -26,6 +26,10 @@ namespace oboe { +/** + * INTERNAL USE ONLY + */ + class AudioInputStreamOpenSLES : public AudioStreamOpenSLES { public: AudioInputStreamOpenSLES(); diff --git a/src/opensles/AudioOutputStreamOpenSLES.cpp b/src/opensles/AudioOutputStreamOpenSLES.cpp index d963ed421..76d8bc4a1 100644 --- a/src/opensles/AudioOutputStreamOpenSLES.cpp +++ b/src/opensles/AudioOutputStreamOpenSLES.cpp @@ -23,16 +23,17 @@ #include "AudioOutputStreamOpenSLES.h" #include "AudioStreamOpenSLES.h" #include "OpenSLESUtilities.h" +#include "OutputMixerOpenSLES.h" using namespace oboe; AudioOutputStreamOpenSLES::AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder) : AudioStreamOpenSLES(builder) { - OpenSLOutputMixer::getInstance()->open(); + OutputMixerOpenSL::getInstance()->open(); } AudioOutputStreamOpenSLES::~AudioOutputStreamOpenSLES() { - OpenSLOutputMixer::getInstance()->close(); + OutputMixerOpenSL::getInstance()->close(); } // These will wind up in @@ -111,7 +112,7 @@ Result AudioOutputStreamOpenSLES::open() { audioSrc.pFormat = &format_pcm_ex; } - SLresult result = OpenSLOutputMixer::getInstance()->createAudioPlayer(&mObjectInterface, + SLresult result = OutputMixerOpenSL::getInstance()->createAudioPlayer(&mObjectInterface, &audioSrc); if (SL_RESULT_SUCCESS != result) { LOGE("createAudioPlayer() result:%s", getSLErrStr(result)); diff --git a/src/opensles/AudioOutputStreamOpenSLES.h b/src/opensles/AudioOutputStreamOpenSLES.h index c5c6de926..9e73f2ae3 100644 --- a/src/opensles/AudioOutputStreamOpenSLES.h +++ b/src/opensles/AudioOutputStreamOpenSLES.h @@ -26,6 +26,9 @@ namespace oboe { +/** + * INTERNAL USE ONLY + */ class AudioOutputStreamOpenSLES : public AudioStreamOpenSLES { public: AudioOutputStreamOpenSLES(); diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp index d6a7b2a74..115e4983b 100644 --- a/src/opensles/AudioStreamOpenSLES.cpp +++ b/src/opensles/AudioStreamOpenSLES.cpp @@ -36,167 +36,17 @@ using namespace oboe; -OpenSLEngine *OpenSLEngine::getInstance() { - // TODO mutex - if (sInstance == nullptr) { - sInstance = new OpenSLEngine(); - } - return sInstance; -} - -SLresult OpenSLEngine::open() { - SLresult result = SL_RESULT_SUCCESS; - if (sOpenCount > 0) { - ++sOpenCount; - return SL_RESULT_SUCCESS; - } - - // create engine - result = slCreateEngine(&sEngineObject, 0, NULL, 0, NULL, NULL); - if (SL_RESULT_SUCCESS != result) { - LOGE("OpenSLEngine() - slCreateEngine() result:%s", getSLErrStr(result)); - return result; - } - - // realize the engine - result = (*sEngineObject)->Realize(sEngineObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("OpenSLEngine() - Realize() engine result:%s", getSLErrStr(result)); - goto error; - } - - // get the engine interface, which is needed in order to create other objects - result = (*sEngineObject)->GetInterface(sEngineObject, SL_IID_ENGINE, &sEngineEngine); - if (SL_RESULT_SUCCESS != result) { - LOGE("OpenSLEngine() - GetInterface() engine result:%s", getSLErrStr(result)); - goto error; - } - - ++sOpenCount; - return result; - - error: - close(); - return result; -} - -void OpenSLEngine::close() { -// __android_log_print(ANDROID_LOG_INFO, TAG, "CloseSLEngine()"); - --sOpenCount; - if (sOpenCount > 0) { - return; - } - - if (sEngineObject != NULL) { - (*sEngineObject)->Destroy(sEngineObject); - sEngineObject = NULL; - sEngineEngine = NULL; - } -} - -SLresult OpenSLEngine::createOutputMix(SLObjectItf *objectItf) { - return (*sEngineEngine)->CreateOutputMix(sEngineEngine, objectItf, 0, 0, 0); -} - -SLresult OpenSLEngine::createAudioPlayer(SLObjectItf *objectItf, - SLDataSource *audioSource, - SLDataSink *audioSink) { - - const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE}; - const SLboolean reqs[] = {SL_BOOLEAN_TRUE}; - - // The Player - return (*sEngineEngine)->CreateAudioPlayer(sEngineEngine, objectItf, audioSource, - audioSink, - sizeof(ids) / sizeof(ids[0]), ids, reqs); -} - -SLresult OpenSLEngine::createAudioRecorder(SLObjectItf *objectItf, - SLDataSource *audioSource, - SLDataSink *audioSink) { - - const SLInterfaceID ids[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - SL_IID_ANDROIDCONFIGURATION }; - const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; - - // The Player - return (*sEngineEngine)->CreateAudioRecorder(sEngineEngine, objectItf, audioSource, - audioSink, - sizeof(ids) / sizeof(ids[0]), ids, reqs); -} - -OpenSLEngine *OpenSLEngine::sInstance = nullptr; - -OpenSLOutputMixer *OpenSLOutputMixer::getInstance() { - // TODO mutex - if (sInstance == nullptr) { - sInstance = new OpenSLOutputMixer(); - } - return sInstance; -} - -SLresult OpenSLOutputMixer::open() { - SLresult result = SL_RESULT_SUCCESS; - if (sOpenCount > 0) { - ++sOpenCount; - return SL_RESULT_SUCCESS; - } - - // get the output mixer - result = OpenSLEngine::getInstance()->createOutputMix(&sOutputMixObject); - if (SL_RESULT_SUCCESS != result) { - LOGE("OpenSLOutputMixer() - createOutputMix() result:%s", getSLErrStr(result)); - goto error; - } - - // realize the output mix - result = (*sOutputMixObject)->Realize(sOutputMixObject, SL_BOOLEAN_FALSE); - if (SL_RESULT_SUCCESS != result) { - LOGE("OpenSLOutputMixer() - Realize() sOutputMixObject result:%s", getSLErrStr(result)); - goto error; - } - - ++sOpenCount; - return result; - - error: - close(); - return result; -} - -void OpenSLOutputMixer::close() { -// __android_log_print(ANDROID_LOG_INFO, TAG, "CloseSLEngine()"); - --sOpenCount; - if (sOpenCount > 0) { - return; - } - // destroy output mix object, and invalidate all associated interfaces - if (sOutputMixObject != NULL) { - (*sOutputMixObject)->Destroy(sOutputMixObject); - sOutputMixObject = NULL; - } -} - -SLresult OpenSLOutputMixer::createAudioPlayer(SLObjectItf *objectItf, - SLDataSource *audioSource) { - SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, sOutputMixObject}; - SLDataSink audioSink = {&loc_outmix, NULL}; - return OpenSLEngine::getInstance()->createAudioPlayer(objectItf, audioSource, &audioSink); -} - -OpenSLOutputMixer *OpenSLOutputMixer::sInstance = nullptr; - AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder) : AudioStreamBuffered(builder) { mSimpleBufferQueueInterface = NULL; mFramesPerBurst = builder.getDefaultFramesPerBurst(); - OpenSLEngine::getInstance()->open(); + EngineOpenSLES::getInstance()->open(); LOGD("AudioStreamOpenSLES(): after OpenSLContext()"); } AudioStreamOpenSLES::~AudioStreamOpenSLES() { - OpenSLEngine::getInstance()->close(); + EngineOpenSLES::getInstance()->close(); delete[] mCallbackBuffer; } diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h index 9a8fa95ab..6895b731e 100644 --- a/src/opensles/AudioStreamOpenSLES.h +++ b/src/opensles/AudioStreamOpenSLES.h @@ -14,72 +14,32 @@ * limitations under the License. */ -#ifndef AUDIO_STREAM_OPENSL_ES_H_ -#define AUDIO_STREAM_OPENSL_ES_H_ +#ifndef OBOE_AUDIO_STREAM_OPENSL_ES_H_ +#define OBOE_AUDIO_STREAM_OPENSL_ES_H_ #include #include #include "oboe/Oboe.h" #include "AudioStreamBuffered.h" +#include "EngineOpenSLES.h" namespace oboe { #define OBOE_BITS_PER_BYTE 8 // common value TODO modernize -class OpenSLEngine { -public: - static OpenSLEngine *getInstance(); - - SLresult open(); - - void close(); - - SLresult createOutputMix(SLObjectItf *objectItf); - - SLresult createAudioPlayer(SLObjectItf *objectItf, - SLDataSource *audioSource, - SLDataSink *audioSink); - SLresult createAudioRecorder(SLObjectItf *objectItf, - SLDataSource *audioSource, - SLDataSink *audioSink); - -private: - static OpenSLEngine *sInstance; - -// engine interfaces - int32_t sOpenCount = 0; - SLObjectItf sEngineObject = 0; - SLEngineItf sEngineEngine; -}; -class OpenSLOutputMixer { -public: - static OpenSLOutputMixer *getInstance(); - - SLresult open(); - - void close(); - - SLresult createAudioPlayer(SLObjectItf *objectItf, - SLDataSource *audioSource); -private: - static OpenSLOutputMixer *sInstance; - -// engine interfaces - int32_t sOpenCount = 0; -// output mix interfaces - SLObjectItf sOutputMixObject = 0; -}; /** + * INTERNAL USE ONLY + * * A stream that wraps OpenSL ES. * * Do not instantiate this class directly. * Use an OboeStreamBuilder to create one. */ -// + class AudioStreamOpenSLES : public AudioStreamBuffered { public: @@ -140,4 +100,4 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { } // namespace oboe -#endif // AUDIO_STREAM_OPENSL_ES_H_ +#endif // OBOE_AUDIO_STREAM_OPENSL_ES_H_ diff --git a/src/opensles/EngineOpenSLES.cpp b/src/opensles/EngineOpenSLES.cpp new file mode 100644 index 000000000..cf87ecc87 --- /dev/null +++ b/src/opensles/EngineOpenSLES.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common/OboeDebug.h" +#include "EngineOpenSLES.h" +#include "OpenSLESUtilities.h" + +using namespace oboe; + +EngineOpenSLES *EngineOpenSLES::getInstance() { + static EngineOpenSLES sInstance; + return &sInstance; +} + +SLresult EngineOpenSLES::open() { + std::lock_guard lock(mLock); + + SLresult result = SL_RESULT_SUCCESS; + if (mOpenCount++ == 0) { + + // create engine + result = slCreateEngine(&mEngineObject, 0, NULL, 0, NULL, NULL); + if (SL_RESULT_SUCCESS != result) { + LOGE("EngineOpenSLES - slCreateEngine() result:%s", getSLErrStr(result)); + goto error; + } + + // realize the engine + result = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("EngineOpenSLES - Realize() engine result:%s", getSLErrStr(result)); + goto error; + } + + // get the engine interface, which is needed in order to create other objects + result = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface); + if (SL_RESULT_SUCCESS != result) { + LOGE("EngineOpenSLES - GetInterface() engine result:%s", getSLErrStr(result)); + goto error; + } + } + + return result; + +error: + close(); + return result; +} + +void EngineOpenSLES::close() { + std::lock_guard lock(mLock); + if (--mOpenCount == 0) { + if (mEngineObject != NULL) { + (*mEngineObject)->Destroy(mEngineObject); + mEngineObject = NULL; + mEngineInterface = NULL; + } + } +} + +SLresult EngineOpenSLES::createOutputMix(SLObjectItf *objectItf) { + return (*mEngineInterface)->CreateOutputMix(mEngineInterface, objectItf, 0, 0, 0); +} + +SLresult EngineOpenSLES::createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink) { + + const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE}; + const SLboolean reqs[] = {SL_BOOLEAN_TRUE}; + + return (*mEngineInterface)->CreateAudioPlayer(mEngineInterface, objectItf, audioSource, + audioSink, + sizeof(ids) / sizeof(ids[0]), ids, reqs); +} + +SLresult EngineOpenSLES::createAudioRecorder(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink) { + + const SLInterfaceID ids[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + SL_IID_ANDROIDCONFIGURATION }; + const SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; + + return (*mEngineInterface)->CreateAudioRecorder(mEngineInterface, objectItf, audioSource, + audioSink, + sizeof(ids) / sizeof(ids[0]), ids, reqs); +} + diff --git a/src/opensles/EngineOpenSLES.h b/src/opensles/EngineOpenSLES.h new file mode 100644 index 000000000..20564f95f --- /dev/null +++ b/src/opensles/EngineOpenSLES.h @@ -0,0 +1,58 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_ENGINE_OPENSLES_H +#define OBOE_ENGINE_OPENSLES_H + +#include +#include + +#include +#include + +namespace oboe { + +/** + * INTERNAL USE ONLY + */ +class EngineOpenSLES { +public: + static EngineOpenSLES *getInstance(); + + SLresult open(); + + void close(); + + SLresult createOutputMix(SLObjectItf *objectItf); + + SLresult createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink); + SLresult createAudioRecorder(SLObjectItf *objectItf, + SLDataSource *audioSource, + SLDataSink *audioSink); + +private: + std::mutex mLock; + std::atomic mOpenCount{0}; + SLObjectItf mEngineObject = 0; + SLEngineItf mEngineInterface; +}; + +} // namespace oboe + + +#endif //OBOE_ENGINE_OPENSLES_H diff --git a/src/opensles/OutputMixerOpenSLES.cpp b/src/opensles/OutputMixerOpenSLES.cpp new file mode 100644 index 000000000..db36ec175 --- /dev/null +++ b/src/opensles/OutputMixerOpenSLES.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include "common/OboeDebug.h" +#include "EngineOpenSLES.h" +#include "OpenSLESUtilities.h" +#include "OutputMixerOpenSLES.h" + +using namespace oboe; + +OutputMixerOpenSL *OutputMixerOpenSL::getInstance() { + static OutputMixerOpenSL sInstance; + return &sInstance; +} + +SLresult OutputMixerOpenSL::open() { + std::lock_guard lock(mLock); + + SLresult result = SL_RESULT_SUCCESS; + if (mOpenCount++ == 0) { + // get the output mixer + result = EngineOpenSLES::getInstance()->createOutputMix(&mOutputMixObject); + if (SL_RESULT_SUCCESS != result) { + LOGE("OutputMixerOpenSL() - createOutputMix() result:%s", getSLErrStr(result)); + goto error; + } + + // realize the output mix + result = (*mOutputMixObject)->Realize(mOutputMixObject, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + LOGE("OutputMixerOpenSL() - Realize() mOutputMixObject result:%s", getSLErrStr(result)); + goto error; + } + } + + return result; + +error: + close(); + return result; +} + +void OutputMixerOpenSL::close() { + std::lock_guard lock(mLock); + + if (--mOpenCount == 0) { + // destroy output mix object, and invalidate all associated interfaces + if (mOutputMixObject != NULL) { + (*mOutputMixObject)->Destroy(mOutputMixObject); + mOutputMixObject = NULL; + } + } +} + +SLresult OutputMixerOpenSL::createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource) { + SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, mOutputMixObject}; + SLDataSink audioSink = {&loc_outmix, NULL}; + return EngineOpenSLES::getInstance()->createAudioPlayer(objectItf, audioSource, &audioSink); +} diff --git a/src/opensles/OutputMixerOpenSLES.h b/src/opensles/OutputMixerOpenSLES.h new file mode 100644 index 000000000..5a414df0b --- /dev/null +++ b/src/opensles/OutputMixerOpenSLES.h @@ -0,0 +1,51 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef OBOE_OUTPUT_MIXER_OPENSLES_H +#define OBOE_OUTPUT_MIXER_OPENSLES_H + +#include +#include + +#include +#include + +namespace oboe { + +/** + * INTERNAL USE ONLY + */ + +class OutputMixerOpenSL { +public: + static OutputMixerOpenSL *getInstance(); + + SLresult open(); + + void close(); + + SLresult createAudioPlayer(SLObjectItf *objectItf, + SLDataSource *audioSource); + +private: + std::mutex mLock; + std::atomic mOpenCount{0}; + SLObjectItf mOutputMixObject = 0; +}; + +} // namespace oboe + +#endif //OBOE_OUTPUT_MIXER_OPENSLES_H From d9703b0b72d1f26207a2b313768cb335d3e0fc9a Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Thu, 14 Dec 2017 12:40:48 -0800 Subject: [PATCH 5/7] oboe: OPenSL ES opens moved into stream open --- src/opensles/AudioInputStreamOpenSLES.cpp | 2 +- src/opensles/AudioOutputStreamOpenSLES.cpp | 11 ++++++++--- src/opensles/AudioStreamOpenSLES.cpp | 11 +++++++---- src/opensles/AudioStreamOpenSLES.h | 4 ++-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/opensles/AudioInputStreamOpenSLES.cpp b/src/opensles/AudioInputStreamOpenSLES.cpp index 3d82b5e87..23e1c66b3 100644 --- a/src/opensles/AudioInputStreamOpenSLES.cpp +++ b/src/opensles/AudioInputStreamOpenSLES.cpp @@ -37,7 +37,7 @@ AudioInputStreamOpenSLES::~AudioInputStreamOpenSLES() { #define SL_ANDROID_UNKNOWN_CHANNELMASK 0 int AudioInputStreamOpenSLES::chanCountToChanMask(int channelCount) { - // stolen from sles_channel_in_mask_from_count(chanCount); + // from internal sles_channel_in_mask_from_count(chanCount); switch (channelCount) { case 1: return SL_SPEAKER_FRONT_LEFT; diff --git a/src/opensles/AudioOutputStreamOpenSLES.cpp b/src/opensles/AudioOutputStreamOpenSLES.cpp index 76d8bc4a1..b31d62735 100644 --- a/src/opensles/AudioOutputStreamOpenSLES.cpp +++ b/src/opensles/AudioOutputStreamOpenSLES.cpp @@ -29,11 +29,9 @@ using namespace oboe; AudioOutputStreamOpenSLES::AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder) : AudioStreamOpenSLES(builder) { - OutputMixerOpenSL::getInstance()->open(); } AudioOutputStreamOpenSLES::~AudioOutputStreamOpenSLES() { - OutputMixerOpenSL::getInstance()->close(); } // These will wind up in @@ -78,6 +76,12 @@ Result AudioOutputStreamOpenSLES::open() { Result oboeResult = AudioStreamOpenSLES::open(); if (Result::OK != oboeResult) return oboeResult; + SLresult result = OutputMixerOpenSL::getInstance()->open(); + if (SL_RESULT_SUCCESS != result) { + AudioStreamOpenSLES::close(); + return Result::ErrorInternal; + } + SLuint32 bitsPerSample = getBytesPerSample() * OBOE_BITS_PER_BYTE; // configure audio source @@ -112,7 +116,7 @@ Result AudioOutputStreamOpenSLES::open() { audioSrc.pFormat = &format_pcm_ex; } - SLresult result = OutputMixerOpenSL::getInstance()->createAudioPlayer(&mObjectInterface, + result = OutputMixerOpenSL::getInstance()->createAudioPlayer(&mObjectInterface, &audioSrc); if (SL_RESULT_SUCCESS != result) { LOGE("createAudioPlayer() result:%s", getSLErrStr(result)); @@ -146,6 +150,7 @@ Result AudioOutputStreamOpenSLES::close() { requestPause(); // invalidate any interfaces mPlayInterface = NULL; + OutputMixerOpenSL::getInstance()->close(); return AudioStreamOpenSLES::close(); } diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp index 115e4983b..0a674af48 100644 --- a/src/opensles/AudioStreamOpenSLES.cpp +++ b/src/opensles/AudioStreamOpenSLES.cpp @@ -35,18 +35,14 @@ using namespace oboe; - - AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder) : AudioStreamBuffered(builder) { mSimpleBufferQueueInterface = NULL; mFramesPerBurst = builder.getDefaultFramesPerBurst(); - EngineOpenSLES::getInstance()->open(); LOGD("AudioStreamOpenSLES(): after OpenSLContext()"); } AudioStreamOpenSLES::~AudioStreamOpenSLES() { - EngineOpenSLES::getInstance()->close(); delete[] mCallbackBuffer; } @@ -69,6 +65,11 @@ Result AudioStreamOpenSLES::open() { return Result::ErrorInvalidFormat; } + SLresult result = EngineOpenSLES::getInstance()->open(); + if (SL_RESULT_SUCCESS != result) { + return Result::ErrorInternal; + } + // If audio format is unspecified then choose a suitable default. // API 21+: FLOAT // API <21: INT16 @@ -100,6 +101,7 @@ Result AudioStreamOpenSLES::open() { } mBytesPerCallback = mFramesPerCallback * getBytesPerFrame(); + delete[] mCallbackBuffer; // to prevent memory leaks mCallbackBuffer = new uint8_t[mBytesPerCallback]; LOGD("AudioStreamOpenSLES(): mFramesPerCallback = %d", mFramesPerCallback); LOGD("AudioStreamOpenSLES(): mBytesPerCallback = %d", mBytesPerCallback); @@ -117,6 +119,7 @@ Result AudioStreamOpenSLES::close() { } mSimpleBufferQueueInterface = NULL; + EngineOpenSLES::getInstance()->close(); return Result::OK; } diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h index 6895b731e..1688c1e34 100644 --- a/src/opensles/AudioStreamOpenSLES.h +++ b/src/opensles/AudioStreamOpenSLES.h @@ -91,8 +91,8 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { SLObjectItf mObjectInterface = nullptr; SLAndroidSimpleBufferQueueItf mSimpleBufferQueueInterface = nullptr; - uint8_t *mCallbackBuffer; - int32_t mBytesPerCallback; + uint8_t *mCallbackBuffer = nullptr; + int32_t mBytesPerCallback = oboe::kUnspecified; int32_t mFramesPerBurst = 0; int32_t mBurstsPerBuffer = 2; // Double buffered StreamState mState = StreamState::Uninitialized; From 7040a9dcaa3313fbbd0cbe48196a9aadd538309d Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Fri, 15 Dec 2017 10:54:38 -0800 Subject: [PATCH 6/7] oboe: address review comments, better singleton Use constexpr. Add new files to CMakeLists.txt --- CMakeLists.txt | 6 ++++- src/opensles/AudioInputStreamOpenSLES.cpp | 8 +++---- src/opensles/AudioOutputStreamOpenSLES.cpp | 28 +++++++++++----------- src/opensles/AudioStreamOpenSLES.cpp | 8 ++++--- src/opensles/AudioStreamOpenSLES.h | 10 +++----- src/opensles/EngineOpenSLES.cpp | 4 ++-- src/opensles/EngineOpenSLES.h | 11 +++++++-- src/opensles/OpenSLESUtilities.cpp | 8 ++++--- src/opensles/OutputMixerOpenSLES.cpp | 8 +++---- src/opensles/OutputMixerOpenSLES.h | 11 +++++++-- 10 files changed, 60 insertions(+), 42 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a4bfadbf..1b82a0fda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,9 +11,13 @@ set (oboe_sources src/fifo/FifoController.cpp src/fifo/FifoControllerBase.cpp src/fifo/FifoControllerIndirect.cpp + src/opensles/AudioInputStreamOpenSLES.cpp + src/opensles/AudioOutputStreamOpenSLES.cpp src/opensles/AudioStreamBuffered.cpp src/opensles/AudioStreamOpenSLES.cpp + src/opensles/EngineOpenSLES.cpp src/opensles/OpenSLESUtilities.cpp + src/opensles/OutputMixerOpenSLES.cpp ) add_library(oboe STATIC ${oboe_sources}) @@ -25,4 +29,4 @@ target_compile_options(oboe PRIVATE -std=c++11 PRIVATE -Wall PRIVATE "$<$:-Werror>") # Only include -Werror when building debug config -target_link_libraries(oboe PRIVATE log OpenSLES) \ No newline at end of file +target_link_libraries(oboe PRIVATE log OpenSLES) diff --git a/src/opensles/AudioInputStreamOpenSLES.cpp b/src/opensles/AudioInputStreamOpenSLES.cpp index 23e1c66b3..3ff10e64f 100644 --- a/src/opensles/AudioInputStreamOpenSLES.cpp +++ b/src/opensles/AudioInputStreamOpenSLES.cpp @@ -59,7 +59,7 @@ Result AudioInputStreamOpenSLES::open() { Result oboeResult = AudioStreamOpenSLES::open(); if (Result::OK != oboeResult) return oboeResult; - SLuint32 bitsPerSample = getBytesPerSample() * OBOE_BITS_PER_BYTE; + SLuint32 bitsPerSample = getBytesPerSample() * kBitsPerByte; // configure audio sink SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { @@ -100,7 +100,7 @@ Result AudioInputStreamOpenSLES::open() { NULL }; SLDataSource audioSrc = {&loc_dev, NULL }; - SLresult result = EngineOpenSLES::getInstance()->createAudioRecorder(&mObjectInterface, + SLresult result = EngineOpenSLES::getInstance().createAudioRecorder(&mObjectInterface, &audioSrc, &audioSink); if (SL_RESULT_SUCCESS != result) { @@ -130,17 +130,17 @@ Result AudioInputStreamOpenSLES::open() { result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_RECORD, &mRecordInterface); if (SL_RESULT_SUCCESS != result) { - LOGE("get recorder interface result:%s", getSLErrStr(result)); + LOGE("GetInterface RECORD result:%s", getSLErrStr(result)); goto error; } result = AudioStreamOpenSLES::registerBufferQueueCallback(); if (SL_RESULT_SUCCESS != result) { - LOGE("get bufferqueue interface:%p result:%s", mSimpleBufferQueueInterface, getSLErrStr(result)); goto error; } return Result::OK; + error: return Result::ErrorInternal; // TODO convert error from SLES to OBOE } diff --git a/src/opensles/AudioOutputStreamOpenSLES.cpp b/src/opensles/AudioOutputStreamOpenSLES.cpp index b31d62735..21297661e 100644 --- a/src/opensles/AudioOutputStreamOpenSLES.cpp +++ b/src/opensles/AudioOutputStreamOpenSLES.cpp @@ -35,15 +35,16 @@ AudioOutputStreamOpenSLES::~AudioOutputStreamOpenSLES() { } // These will wind up in -#define SL_ANDROID_SPEAKER_QUAD (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \ - | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT) +constexpr int SL_ANDROID_SPEAKER_STEREO = (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); -#define SL_ANDROID_SPEAKER_5DOT1 (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT \ - | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY| SL_SPEAKER_BACK_LEFT \ - | SL_SPEAKER_BACK_RIGHT) +constexpr int SL_ANDROID_SPEAKER_QUAD = (SL_ANDROID_SPEAKER_STEREO + | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT); -#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT \ - |SL_SPEAKER_SIDE_RIGHT) +constexpr int SL_ANDROID_SPEAKER_5DOT1 = (SL_ANDROID_SPEAKER_QUAD + | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY); + +constexpr int SL_ANDROID_SPEAKER_7DOT1 = (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT + | SL_SPEAKER_SIDE_RIGHT); int AudioOutputStreamOpenSLES::chanCountToChanMask(int chanCount) { int channelMask = 0; @@ -54,7 +55,7 @@ int AudioOutputStreamOpenSLES::chanCountToChanMask(int chanCount) { break; case 2: - channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + channelMask = SL_ANDROID_SPEAKER_STEREO; break; case 4: // Quad @@ -76,13 +77,13 @@ Result AudioOutputStreamOpenSLES::open() { Result oboeResult = AudioStreamOpenSLES::open(); if (Result::OK != oboeResult) return oboeResult; - SLresult result = OutputMixerOpenSL::getInstance()->open(); + SLresult result = OutputMixerOpenSL::getInstance().open(); if (SL_RESULT_SUCCESS != result) { AudioStreamOpenSLES::close(); return Result::ErrorInternal; } - SLuint32 bitsPerSample = getBytesPerSample() * OBOE_BITS_PER_BYTE; + SLuint32 bitsPerSample = getBytesPerSample() * kBitsPerByte; // configure audio source SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { @@ -116,7 +117,7 @@ Result AudioOutputStreamOpenSLES::open() { audioSrc.pFormat = &format_pcm_ex; } - result = OutputMixerOpenSL::getInstance()->createAudioPlayer(&mObjectInterface, + result = OutputMixerOpenSL::getInstance().createAudioPlayer(&mObjectInterface, &audioSrc); if (SL_RESULT_SUCCESS != result) { LOGE("createAudioPlayer() result:%s", getSLErrStr(result)); @@ -131,13 +132,12 @@ Result AudioOutputStreamOpenSLES::open() { result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_PLAY, &mPlayInterface); if (SL_RESULT_SUCCESS != result) { - LOGE("get player interface result:%s", getSLErrStr(result)); + LOGE("GetInterface PLAY result:%s", getSLErrStr(result)); goto error; } result = AudioStreamOpenSLES::registerBufferQueueCallback(); if (SL_RESULT_SUCCESS != result) { - LOGE("get bufferqueue interface:%p result:%s", mSimpleBufferQueueInterface, getSLErrStr(result)); goto error; } @@ -150,7 +150,7 @@ Result AudioOutputStreamOpenSLES::close() { requestPause(); // invalidate any interfaces mPlayInterface = NULL; - OutputMixerOpenSL::getInstance()->close(); + OutputMixerOpenSL::getInstance().close(); return AudioStreamOpenSLES::close(); } diff --git a/src/opensles/AudioStreamOpenSLES.cpp b/src/opensles/AudioStreamOpenSLES.cpp index 0a674af48..3830eeb7c 100644 --- a/src/opensles/AudioStreamOpenSLES.cpp +++ b/src/opensles/AudioStreamOpenSLES.cpp @@ -65,7 +65,7 @@ Result AudioStreamOpenSLES::open() { return Result::ErrorInvalidFormat; } - SLresult result = EngineOpenSLES::getInstance()->open(); + SLresult result = EngineOpenSLES::getInstance().open(); if (SL_RESULT_SUCCESS != result) { return Result::ErrorInternal; } @@ -119,7 +119,7 @@ Result AudioStreamOpenSLES::close() { } mSimpleBufferQueueInterface = NULL; - EngineOpenSLES::getInstance()->close(); + EngineOpenSLES::getInstance().close(); return Result::OK; } @@ -149,7 +149,9 @@ SLresult AudioStreamOpenSLES::registerBufferQueueCallback() { SLresult result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mSimpleBufferQueueInterface); if (SL_RESULT_SUCCESS != result) { - LOGE("get bufferqueue interface:%p result:%s", mSimpleBufferQueueInterface, getSLErrStr(result)); + LOGE("get buffer queue interface:%p result:%s", + mSimpleBufferQueueInterface, + getSLErrStr(result)); } else { // Register the BufferQueue callback result = (*mSimpleBufferQueueInterface)->RegisterCallback(mSimpleBufferQueueInterface, diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h index 1688c1e34..da4a9adeb 100644 --- a/src/opensles/AudioStreamOpenSLES.h +++ b/src/opensles/AudioStreamOpenSLES.h @@ -26,10 +26,7 @@ namespace oboe { -#define OBOE_BITS_PER_BYTE 8 // common value TODO modernize - - - +constexpr int kBitsPerByte = 8; /** * INTERNAL USE ONLY @@ -51,7 +48,6 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { virtual Result open() override; virtual Result close() override; - /** * Query the current state, eg. OBOE_STREAM_STATE_PAUSING * @@ -61,8 +57,6 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { int32_t getFramesPerBurst() override; - static SLuint32 getDefaultByteOrder(); - virtual int chanCountToChanMask(int chanCount) = 0; /** @@ -75,6 +69,8 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { protected: + static SLuint32 getDefaultByteOrder(); + SLresult registerBufferQueueCallback(); SLresult enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq); diff --git a/src/opensles/EngineOpenSLES.cpp b/src/opensles/EngineOpenSLES.cpp index cf87ecc87..8df3fed91 100644 --- a/src/opensles/EngineOpenSLES.cpp +++ b/src/opensles/EngineOpenSLES.cpp @@ -20,9 +20,9 @@ using namespace oboe; -EngineOpenSLES *EngineOpenSLES::getInstance() { +EngineOpenSLES &EngineOpenSLES::getInstance() { static EngineOpenSLES sInstance; - return &sInstance; + return sInstance; } SLresult EngineOpenSLES::open() { diff --git a/src/opensles/EngineOpenSLES.h b/src/opensles/EngineOpenSLES.h index 20564f95f..7658c955a 100644 --- a/src/opensles/EngineOpenSLES.h +++ b/src/opensles/EngineOpenSLES.h @@ -30,7 +30,7 @@ namespace oboe { */ class EngineOpenSLES { public: - static EngineOpenSLES *getInstance(); + static EngineOpenSLES &getInstance(); SLresult open(); @@ -46,8 +46,15 @@ class EngineOpenSLES { SLDataSink *audioSink); private: + // Make this a safe Singleton + EngineOpenSLES()= default; + ~EngineOpenSLES()= default; + EngineOpenSLES(const EngineOpenSLES&)= delete; + EngineOpenSLES& operator=(const EngineOpenSLES&)= delete; + std::mutex mLock; - std::atomic mOpenCount{0}; + int32_t mOpenCount = 0; + SLObjectItf mEngineObject = 0; SLEngineItf mEngineInterface; }; diff --git a/src/opensles/OpenSLESUtilities.cpp b/src/opensles/OpenSLESUtilities.cpp index c764af382..45f1c1880 100644 --- a/src/opensles/OpenSLESUtilities.cpp +++ b/src/opensles/OpenSLESUtilities.cpp @@ -61,13 +61,15 @@ SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat( SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format) { switch(format) { - case AudioFormat::Invalid: - case AudioFormat::Unspecified: - return 0; case AudioFormat::I16: return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; case AudioFormat::Float: return SL_ANDROID_PCM_REPRESENTATION_FLOAT; + case AudioFormat::Invalid: + case AudioFormat::Unspecified: + default: + return 0; } } + } // namespace oboe \ No newline at end of file diff --git a/src/opensles/OutputMixerOpenSLES.cpp b/src/opensles/OutputMixerOpenSLES.cpp index db36ec175..901e9a75e 100644 --- a/src/opensles/OutputMixerOpenSLES.cpp +++ b/src/opensles/OutputMixerOpenSLES.cpp @@ -22,9 +22,9 @@ using namespace oboe; -OutputMixerOpenSL *OutputMixerOpenSL::getInstance() { +OutputMixerOpenSL &OutputMixerOpenSL::getInstance() { static OutputMixerOpenSL sInstance; - return &sInstance; + return sInstance; } SLresult OutputMixerOpenSL::open() { @@ -33,7 +33,7 @@ SLresult OutputMixerOpenSL::open() { SLresult result = SL_RESULT_SUCCESS; if (mOpenCount++ == 0) { // get the output mixer - result = EngineOpenSLES::getInstance()->createOutputMix(&mOutputMixObject); + result = EngineOpenSLES::getInstance().createOutputMix(&mOutputMixObject); if (SL_RESULT_SUCCESS != result) { LOGE("OutputMixerOpenSL() - createOutputMix() result:%s", getSLErrStr(result)); goto error; @@ -70,5 +70,5 @@ SLresult OutputMixerOpenSL::createAudioPlayer(SLObjectItf *objectItf, SLDataSource *audioSource) { SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, mOutputMixObject}; SLDataSink audioSink = {&loc_outmix, NULL}; - return EngineOpenSLES::getInstance()->createAudioPlayer(objectItf, audioSource, &audioSink); + return EngineOpenSLES::getInstance().createAudioPlayer(objectItf, audioSource, &audioSink); } diff --git a/src/opensles/OutputMixerOpenSLES.h b/src/opensles/OutputMixerOpenSLES.h index 5a414df0b..1fc77e512 100644 --- a/src/opensles/OutputMixerOpenSLES.h +++ b/src/opensles/OutputMixerOpenSLES.h @@ -31,7 +31,7 @@ namespace oboe { class OutputMixerOpenSL { public: - static OutputMixerOpenSL *getInstance(); + static OutputMixerOpenSL &getInstance(); SLresult open(); @@ -41,8 +41,15 @@ class OutputMixerOpenSL { SLDataSource *audioSource); private: + // Make this a safe Singleton + OutputMixerOpenSL()= default; + ~OutputMixerOpenSL()= default; + OutputMixerOpenSL(const OutputMixerOpenSL&)= delete; + OutputMixerOpenSL& operator=(const OutputMixerOpenSL&)= delete; + std::mutex mLock; - std::atomic mOpenCount{0}; + int32_t mOpenCount = 0; + SLObjectItf mOutputMixObject = 0; }; From 9b1c2ce6cbc2f6812c727affb9418bf639237b05 Mon Sep 17 00:00:00 2001 From: Phil Burk Date: Fri, 15 Dec 2017 11:47:56 -0800 Subject: [PATCH 7/7] oboe: make chanCountToChanMask non-virtual --- src/opensles/AudioInputStreamOpenSLES.h | 3 ++- src/opensles/AudioOutputStreamOpenSLES.h | 2 +- src/opensles/AudioStreamOpenSLES.h | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/opensles/AudioInputStreamOpenSLES.h b/src/opensles/AudioInputStreamOpenSLES.h index 9fb79f576..89c03d07d 100644 --- a/src/opensles/AudioInputStreamOpenSLES.h +++ b/src/opensles/AudioInputStreamOpenSLES.h @@ -49,7 +49,8 @@ class AudioInputStreamOpenSLES : public AudioStreamOpenSLES { StreamState *nextState, int64_t timeoutNanoseconds) override; - int chanCountToChanMask(int chanCount) override; + int chanCountToChanMask(int chanCount); + private: Result setRecordState(SLuint32 newState); diff --git a/src/opensles/AudioOutputStreamOpenSLES.h b/src/opensles/AudioOutputStreamOpenSLES.h index 9e73f2ae3..f8b83de9f 100644 --- a/src/opensles/AudioOutputStreamOpenSLES.h +++ b/src/opensles/AudioOutputStreamOpenSLES.h @@ -48,7 +48,7 @@ class AudioOutputStreamOpenSLES : public AudioStreamOpenSLES { StreamState *nextState, int64_t timeoutNanoseconds) override; - int chanCountToChanMask(int chanCount) override; + int chanCountToChanMask(int chanCount); private: diff --git a/src/opensles/AudioStreamOpenSLES.h b/src/opensles/AudioStreamOpenSLES.h index da4a9adeb..7056e57b4 100644 --- a/src/opensles/AudioStreamOpenSLES.h +++ b/src/opensles/AudioStreamOpenSLES.h @@ -57,8 +57,6 @@ class AudioStreamOpenSLES : public AudioStreamBuffered { int32_t getFramesPerBurst() override; - virtual int chanCountToChanMask(int chanCount) = 0; - /** * Process next OpenSL ES buffer. * Called by by OpenSL ES framework.