/* * 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 #include #include #include "oboe_common_AudioClock_android.h" #include "oboe_common_OboeDebug_android.h" #include "oboe_oboe_AudioStreamBuilder_android.h" #include "oboe_opensles_AudioOutputStreamOpenSLES_android.h" #include "oboe_opensles_AudioStreamOpenSLES_android.h" #include "oboe_opensles_OpenSLESUtilities_android.h" #include "oboe_opensles_OutputMixerOpenSLES_android.h" using namespace oboe; static SLuint32 OpenSLES_convertOutputUsage(Usage oboeUsage) { SLuint32 openslStream = SL_ANDROID_STREAM_MEDIA; switch(oboeUsage) { case Usage::Media: 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::NotificationRingtone: case Usage::NotificationEvent: openslStream = SL_ANDROID_STREAM_NOTIFICATION; break; case Usage::AssistanceAccessibility: case Usage::AssistanceNavigationGuidance: case Usage::AssistanceSonification: openslStream = SL_ANDROID_STREAM_SYSTEM; break; case Usage::Game: openslStream = SL_ANDROID_STREAM_MEDIA; break; case Usage::Assistant: default: openslStream = SL_ANDROID_STREAM_SYSTEM; break; } return openslStream; } AudioOutputStreamOpenSLES::AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder) : AudioStreamOpenSLES(builder) { } // These will wind up in 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: // 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; default: channelMask = channelCountToChannelMaskDefault(channelCount); break; } return channelMask; } Result AudioOutputStreamOpenSLES::open() { logUnsupportedAttributes(); SLAndroidConfigurationItf configItf = nullptr; if (getSdkVersion() < __ANDROID_API_L__ && mFormat == AudioFormat::Float){ // TODO: Allow floating point format on API <21 using float->int16 converter return Result::ErrorInvalidFormat; } // If audio format is unspecified then choose a suitable default. // API 21+: FLOAT // API <21: INT16 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(getBytesPerSample() * kBitsPerByte); // configure audio source mBufferQueueLength = calculateOptimalBufferQueueLength(); SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType static_cast(mBufferQueueLength)}; // numBuffers // Define the audio data format. SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM, // formatType static_cast(mChannelCount), // numChannels static_cast(mSampleRate * kMillisPerSecond), // milliSamplesPerSec bitsPerSample, // bitsPerSample bitsPerSample, // containerSize; channelCountToChannelMask(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. */ SLAndroidDataFormat_PCM_EX format_pcm_ex; if (getSdkVersion() >= __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. audioSrc.pFormat = &format_pcm_ex; } result = OutputMixerOpenSL::getInstance().createAudioPlayer(&mObjectInterface, &audioSrc); if (SL_RESULT_SUCCESS != result) { LOGE("createAudioPlayer() result:%s", getSLErrStr(result)); goto error; } // Configure the stream. 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(); // Clean up various OpenSL objects and prevent resource leaks. return Result::ErrorInternal; // TODO convert error from SLES to OBOE } Result AudioOutputStreamOpenSLES::onAfterDestroy() { OutputMixerOpenSL::getInstance().close(); return Result::OK; } Result AudioOutputStreamOpenSLES::close() { LOGD("AudioOutputStreamOpenSLES::%s()", __func__); std::lock_guard lock(mLock); Result result = Result::OK; if (getState() == StreamState::Closed){ result = Result::ErrorClosed; } else { (void) requestPause_l(); if (OboeGlobals::areWorkaroundsEnabled()) { sleepBeforeClose(); } // invalidate any interfaces 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; // TODO convert slResult to Result::Error } 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; } // We use a callback if the user requests one // OR if we have an internal callback to read the blocking IO buffer. setDataCallbackEnabled(true); setState(StreamState::Starting); if (getBufferDepth(mSimpleBufferQueueInterface) == 0) { // Enqueue the first buffer if needed to start the streaming. // We may need to stop the current stream. 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 lock(mLock); return requestPause_l(); } // Call under mLock 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) { // Note that OpenSL ES does NOT reset its millisecond position when OUTPUT is paused. int64_t framesWritten = getFramesWritten(); if (framesWritten >= 0) { setFramesRead(framesWritten); } setState(StreamState::Paused); } else { setState(initialState); } return result; } /** * Flush/clear the queue buffers */ Result AudioOutputStreamOpenSLES::requestFlush() { std::lock_guard 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 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) { // Also clear the buffer queue so the old data won't be played if the stream is restarted. // Call the _l function that expects to already be under a lock. if (requestFlush_l() != Result::OK) { LOGW("Failed to flush the stream. Error %s", convertToText(flush())); } mPositionMillis.reset32(); // OpenSL ES resets its millisecond position when stopped. 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; // Avoid deadlock if another thread is trying to stop or close this stream // and this is being called from a callback. 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)); // set result based on SLresult result = Result::ErrorInternal; } else { mPositionMillis.update32(msec); } mLock.unlock(); } return result; }