Skip to content

Commit

Permalink
oboe: implement input stream
Browse files Browse the repository at this point in the history
Also fix some bugs in OpenSL ES implementation
and replace asserts with error returns.
  • Loading branch information
Phil Burk authored and dturner committed Feb 12, 2018
1 parent 41e1a5f commit 05a98c7
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 72 deletions.
175 changes: 162 additions & 13 deletions src/opensles/AudioInputStreamOpenSLES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,15 @@
* limitations under the License.
*/

#include <cassert>

#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>

#include "oboe/AudioStreamBuilder.h"
#include "AudioInputStreamOpenSLES.h"
#include "AudioStreamOpenSLES.h"
#include "OpenSLESUtilities.h"

using namespace oboe;

Expand All @@ -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<SLuint32>(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
}
4 changes: 4 additions & 0 deletions src/opensles/AudioInputStreamOpenSLES.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
53 changes: 16 additions & 37 deletions src/opensles/AudioOutputStreamOpenSLES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}

Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/opensles/AudioOutputStreamOpenSLES.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ class AudioOutputStreamOpenSLES : public AudioStreamOpenSLES {
*/
Result setPlayState(SLuint32 newState);

SLObjectItf bqPlayerObject_ = nullptr;
SLPlayItf bqPlayerPlay_ = nullptr;
SLPlayItf mPlayInterface = nullptr;

};

} // namespace oboe
Expand Down
Loading

0 comments on commit 05a98c7

Please sign in to comment.