#include <cassert>
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <common/AudioClock.h>
#include "common/OboeDebug.h"
#include "oboe/AudioStreamBuilder.h"
#include "AudioOutputStreamOpenSLES.h"
#include "AudioStreamOpenSLES.h"
#include "OpenSLESUtilities.h"
#include "OutputMixerOpenSLES.h"
using namespace oboe;
static SLuint32 OpenSLES_convertOutputUsage(Usage oboeUsage) {
SLuint32 openslStream;
switch(oboeUsage) {
case Usage::Media:
case Usage::Game:
openslStream = SL_ANDROID_STREAM_MEDIA;
break;
case Usage::VoiceCommunication:
case Usage::VoiceCommunicationSignalling:
openslStream = SL_ANDROID_STREAM_VOICE;
break;
case Usage::Alarm:
openslStream = SL_ANDROID_STREAM_ALARM;
break;
case Usage::Notification:
case Usage::NotificationEvent:
openslStream = SL_ANDROID_STREAM_NOTIFICATION;
break;
case Usage::NotificationRingtone:
openslStream = SL_ANDROID_STREAM_RING;
break;
case Usage::AssistanceAccessibility:
case Usage::AssistanceNavigationGuidance:
case Usage::AssistanceSonification:
case Usage::Assistant:
default:
openslStream = SL_ANDROID_STREAM_SYSTEM;
break;
}
return openslStream;
}
AudioOutputStreamOpenSLES::AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder)
: AudioStreamOpenSLES(builder) {
}
constexpr int SL_ANDROID_SPEAKER_STEREO = (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT);
constexpr int SL_ANDROID_SPEAKER_QUAD = (SL_ANDROID_SPEAKER_STEREO
| SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_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);
SLuint32 AudioOutputStreamOpenSLES::channelCountToChannelMask(int channelCount) const {
SLuint32 channelMask = 0;
switch (channelCount) {
case 1:
channelMask = SL_SPEAKER_FRONT_CENTER;
break;
case 2:
channelMask = SL_ANDROID_SPEAKER_STEREO;
break;
case 4: channelMask = SL_ANDROID_SPEAKER_QUAD;
break;
case 6: channelMask = SL_ANDROID_SPEAKER_5DOT1;
break;
case 8: channelMask = SL_ANDROID_SPEAKER_7DOT1;
break;
default:
channelMask = channelCountToChannelMaskDefault(channelCount);
break;
}
return channelMask;
}
Result AudioOutputStreamOpenSLES::open() {
logUnsupportedAttributes();
SLAndroidConfigurationItf configItf = nullptr;
if (getSdkVersion() < __ANDROID_API_L__ && mFormat == AudioFormat::Float){
return Result::ErrorInvalidFormat;
}
if (mFormat == AudioFormat::Unspecified){
mFormat = (getSdkVersion() < __ANDROID_API_L__) ?
AudioFormat::I16 : AudioFormat::Float;
}
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 = static_cast<SLuint32>(getBytesPerSample() * kBitsPerByte);
mBufferQueueLength = calculateOptimalBufferQueueLength();
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast<SLuint32>(mBufferQueueLength)};
SLDataFormat_PCM format_pcm = {
SL_DATAFORMAT_PCM, static_cast<SLuint32>(mChannelCount), static_cast<SLuint32>(mSampleRate * kMillisPerSecond), bitsPerSample, bitsPerSample, channelCountToChannelMask(mChannelCount), getDefaultByteOrder(),
};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
SLAndroidDataFormat_PCM_EX format_pcm_ex;
if (getSdkVersion() >= __ANDROID_API_L__) {
SLuint32 representation = OpenSLES_ConvertFormatToRepresentation(getFormat());
format_pcm_ex = OpenSLES_createExtendedFormat(format_pcm, representation);
audioSrc.pFormat = &format_pcm_ex;
}
result = OutputMixerOpenSL::getInstance().createAudioPlayer(&mObjectInterface,
&audioSrc);
if (SL_RESULT_SUCCESS != result) {
LOGE("createAudioPlayer() result:%s", getSLErrStr(result));
goto error;
}
result = (*mObjectInterface)->GetInterface(mObjectInterface,
SL_IID_ANDROIDCONFIGURATION,
(void *)&configItf);
if (SL_RESULT_SUCCESS != result) {
LOGW("%s() GetInterface(SL_IID_ANDROIDCONFIGURATION) failed with %s",
__func__, getSLErrStr(result));
} else {
result = configurePerformanceMode(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}
SLuint32 presetValue = OpenSLES_convertOutputUsage(getUsage());
result = (*configItf)->SetConfiguration(configItf,
SL_ANDROID_KEY_STREAM_TYPE,
&presetValue,
sizeof(presetValue));
if (SL_RESULT_SUCCESS != result) {
goto error;
}
}
result = (*mObjectInterface)->Realize(mObjectInterface, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("Realize player object result:%s", getSLErrStr(result));
goto error;
}
result = (*mObjectInterface)->GetInterface(mObjectInterface, SL_IID_PLAY, &mPlayInterface);
if (SL_RESULT_SUCCESS != result) {
LOGE("GetInterface PLAY result:%s", getSLErrStr(result));
goto error;
}
result = finishCommonOpen(configItf);
if (SL_RESULT_SUCCESS != result) {
goto error;
}
setState(StreamState::Open);
return Result::OK;
error:
close(); return Result::ErrorInternal; }
Result AudioOutputStreamOpenSLES::onAfterDestroy() {
OutputMixerOpenSL::getInstance().close();
return Result::OK;
}
Result AudioOutputStreamOpenSLES::close() {
LOGD("AudioOutputStreamOpenSLES::%s()", __func__);
std::lock_guard<std::mutex> lock(mLock);
Result result = Result::OK;
if (getState() == StreamState::Closed){
result = Result::ErrorClosed;
} else {
(void) requestPause_l();
if (OboeGlobals::areWorkaroundsEnabled()) {
sleepBeforeClose();
}
mPlayInterface = nullptr;
result = AudioStreamOpenSLES::close_l();
}
return result;
}
Result AudioOutputStreamOpenSLES::setPlayState_l(SLuint32 newState) {
LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__);
Result result = Result::OK;
if (mPlayInterface == nullptr){
LOGE("AudioOutputStreamOpenSLES::%s() mPlayInterface is null", __func__);
return Result::ErrorInvalidState;
}
SLresult slResult = (*mPlayInterface)->SetPlayState(mPlayInterface, newState);
if (SL_RESULT_SUCCESS != slResult) {
LOGW("AudioOutputStreamOpenSLES(): %s() returned %s", __func__, getSLErrStr(slResult));
result = Result::ErrorInternal; }
return result;
}
Result AudioOutputStreamOpenSLES::requestStart() {
LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__);
mLock.lock();
StreamState initialState = getState();
switch (initialState) {
case StreamState::Starting:
case StreamState::Started:
mLock.unlock();
return Result::OK;
case StreamState::Closed:
mLock.unlock();
return Result::ErrorClosed;
default:
break;
}
setDataCallbackEnabled(true);
setState(StreamState::Starting);
closePerformanceHint();
if (getBufferDepth(mSimpleBufferQueueInterface) == 0) {
bool shouldStopStream = processBufferCallback(mSimpleBufferQueueInterface);
if (shouldStopStream) {
LOGD("Stopping the current stream.");
if (requestStop_l() != Result::OK) {
LOGW("Failed to flush the stream. Error %s", convertToText(flush()));
}
setState(initialState);
mLock.unlock();
return Result::ErrorClosed;
}
}
Result result = setPlayState_l(SL_PLAYSTATE_PLAYING);
if (result == Result::OK) {
setState(StreamState::Started);
mLock.unlock();
} else {
setState(initialState);
mLock.unlock();
}
return result;
}
Result AudioOutputStreamOpenSLES::requestPause() {
LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__);
std::lock_guard<std::mutex> lock(mLock);
return requestPause_l();
}
Result AudioOutputStreamOpenSLES::requestPause_l() {
StreamState initialState = getState();
switch (initialState) {
case StreamState::Pausing:
case StreamState::Paused:
return Result::OK;
case StreamState::Uninitialized:
case StreamState::Closed:
return Result::ErrorClosed;
default:
break;
}
setState(StreamState::Pausing);
Result result = setPlayState_l(SL_PLAYSTATE_PAUSED);
if (result == Result::OK) {
int64_t framesWritten = getFramesWritten();
if (framesWritten >= 0) {
setFramesRead(framesWritten);
}
setState(StreamState::Paused);
} else {
setState(initialState);
}
return result;
}
Result AudioOutputStreamOpenSLES::requestFlush() {
std::lock_guard<std::mutex> lock(mLock);
return requestFlush_l();
}
Result AudioOutputStreamOpenSLES::requestFlush_l() {
LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__);
if (getState() == StreamState::Closed) {
return Result::ErrorClosed;
}
Result result = Result::OK;
if (mPlayInterface == nullptr || mSimpleBufferQueueInterface == nullptr) {
result = Result::ErrorInvalidState;
} else {
SLresult slResult = (*mSimpleBufferQueueInterface)->Clear(mSimpleBufferQueueInterface);
if (slResult != SL_RESULT_SUCCESS){
LOGW("Failed to clear buffer queue. OpenSLES error: %d", result);
result = Result::ErrorInternal;
}
}
return result;
}
Result AudioOutputStreamOpenSLES::requestStop() {
std::lock_guard<std::mutex> lock(mLock);
return requestStop_l();
}
Result AudioOutputStreamOpenSLES::requestStop_l() {
LOGD("AudioOutputStreamOpenSLES(): %s() called", __func__);
StreamState initialState = getState();
switch (initialState) {
case StreamState::Stopping:
case StreamState::Stopped:
return Result::OK;
case StreamState::Uninitialized:
case StreamState::Closed:
return Result::ErrorClosed;
default:
break;
}
setState(StreamState::Stopping);
Result result = setPlayState_l(SL_PLAYSTATE_STOPPED);
if (result == Result::OK) {
if (requestFlush_l() != Result::OK) {
LOGW("Failed to flush the stream. Error %s", convertToText(flush()));
}
mPositionMillis.reset32(); int64_t framesWritten = getFramesWritten();
if (framesWritten >= 0) {
setFramesRead(framesWritten);
}
setState(StreamState::Stopped);
} else {
setState(initialState);
}
return result;
}
void AudioOutputStreamOpenSLES::setFramesRead(int64_t framesRead) {
int64_t millisWritten = framesRead * kMillisPerSecond / getSampleRate();
mPositionMillis.set(millisWritten);
}
void AudioOutputStreamOpenSLES::updateFramesRead() {
if (usingFIFO()) {
AudioStreamBuffered::updateFramesRead();
} else {
mFramesRead = getFramesProcessedByServer();
}
}
Result AudioOutputStreamOpenSLES::updateServiceFrameCounter() {
Result result = Result::OK;
if (mLock.try_lock()) {
if (mPlayInterface == nullptr) {
mLock.unlock();
return Result::ErrorNull;
}
SLmillisecond msec = 0;
SLresult slResult = (*mPlayInterface)->GetPosition(mPlayInterface, &msec);
if (SL_RESULT_SUCCESS != slResult) {
LOGW("%s(): GetPosition() returned %s", __func__, getSLErrStr(slResult));
result = Result::ErrorInternal;
} else {
mPositionMillis.update32(msec);
}
mLock.unlock();
}
return result;
}