mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2025-10-23 03:57:50 +00:00
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
parent
c0cceff365
commit
6c655321e6
4081 changed files with 1185566 additions and 45 deletions
457
externals/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp
vendored
Normal file
457
externals/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp
vendored
Normal file
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
* 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 <cassert>
|
||||
|
||||
#include "common/OboeDebug.h"
|
||||
#include "oboe/AudioClock.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) {
|
||||
}
|
||||
|
||||
// These will wind up in <SLES/OpenSLES_Android.h>
|
||||
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<SLuint32>(getBytesPerSample() * kBitsPerByte);
|
||||
|
||||
// configure audio source
|
||||
mBufferQueueLength = calculateOptimalBufferQueueLength();
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
|
||||
SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, // locatorType
|
||||
static_cast<SLuint32>(mBufferQueueLength)}; // numBuffers
|
||||
|
||||
// Define the audio data format.
|
||||
SLDataFormat_PCM format_pcm = {
|
||||
SL_DATAFORMAT_PCM, // formatType
|
||||
static_cast<SLuint32>(mChannelCount), // numChannels
|
||||
static_cast<SLuint32>(mSampleRate * kMillisPerSecond), // milliSamplesPerSec
|
||||
bitsPerSample, // mBitsPerSample
|
||||
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,
|
||||
EngineOpenSLES::getInstance().getIidAndroidConfiguration(),
|
||||
(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,
|
||||
EngineOpenSLES::getInstance().getIidPlay(),
|
||||
&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<std::mutex> 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(%d) called", __func__, newState);
|
||||
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);
|
||||
closePerformanceHint();
|
||||
|
||||
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<std::mutex> 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<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: %s", getSLErrStr(slResult));
|
||||
result = Result::ErrorInternal;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Result AudioOutputStreamOpenSLES::requestStop() {
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
return requestStop_l();
|
||||
}
|
||||
|
||||
Result AudioOutputStreamOpenSLES::requestStop_l() {
|
||||
StreamState initialState = getState();
|
||||
LOGD("AudioOutputStreamOpenSLES::%s() called, initialState = %d", __func__, initialState);
|
||||
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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue