forked from eden-emu/eden
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
359
externals/oboe/src/opensles/AudioInputStreamOpenSLES.cpp
vendored
Normal file
359
externals/oboe/src/opensles/AudioInputStreamOpenSLES.cpp
vendored
Normal file
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* 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/AudioStreamBuilder.h"
|
||||
#include "AudioInputStreamOpenSLES.h"
|
||||
#include "AudioStreamOpenSLES.h"
|
||||
#include "OpenSLESUtilities.h"
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
static SLuint32 OpenSLES_convertInputPreset(InputPreset oboePreset) {
|
||||
SLuint32 openslPreset = SL_ANDROID_RECORDING_PRESET_NONE;
|
||||
switch(oboePreset) {
|
||||
case InputPreset::Generic:
|
||||
openslPreset = SL_ANDROID_RECORDING_PRESET_GENERIC;
|
||||
break;
|
||||
case InputPreset::Camcorder:
|
||||
openslPreset = SL_ANDROID_RECORDING_PRESET_CAMCORDER;
|
||||
break;
|
||||
case InputPreset::VoiceRecognition:
|
||||
case InputPreset::VoicePerformance:
|
||||
openslPreset = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
|
||||
break;
|
||||
case InputPreset::VoiceCommunication:
|
||||
openslPreset = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
|
||||
break;
|
||||
case InputPreset::Unprocessed:
|
||||
openslPreset = SL_ANDROID_RECORDING_PRESET_UNPROCESSED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return openslPreset;
|
||||
}
|
||||
|
||||
AudioInputStreamOpenSLES::AudioInputStreamOpenSLES(const AudioStreamBuilder &builder)
|
||||
: AudioStreamOpenSLES(builder) {
|
||||
}
|
||||
|
||||
AudioInputStreamOpenSLES::~AudioInputStreamOpenSLES() {
|
||||
}
|
||||
|
||||
// Calculate masks specific to INPUT streams.
|
||||
SLuint32 AudioInputStreamOpenSLES::channelCountToChannelMask(int channelCount) const {
|
||||
// Derived from internal sles_channel_in_mask_from_count(chanCount);
|
||||
// in "frameworks/wilhelm/src/android/channels.cpp".
|
||||
// Yes, it seems strange to use SPEAKER constants to describe inputs.
|
||||
// But that is how OpenSL ES does it internally.
|
||||
switch (channelCount) {
|
||||
case 1:
|
||||
return SL_SPEAKER_FRONT_LEFT;
|
||||
case 2:
|
||||
return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||
default:
|
||||
return channelCountToChannelMaskDefault(channelCount);
|
||||
}
|
||||
}
|
||||
|
||||
Result AudioInputStreamOpenSLES::open() {
|
||||
logUnsupportedAttributes();
|
||||
|
||||
SLAndroidConfigurationItf configItf = nullptr;
|
||||
|
||||
if (getSdkVersion() < __ANDROID_API_M__ && mFormat == AudioFormat::Float){
|
||||
// TODO: Allow floating point format on API <23 using float->int16 converter
|
||||
return Result::ErrorInvalidFormat;
|
||||
}
|
||||
|
||||
// If audio format is unspecified then choose a suitable default.
|
||||
// API 23+: FLOAT
|
||||
// API <23: INT16
|
||||
if (mFormat == AudioFormat::Unspecified){
|
||||
mFormat = (getSdkVersion() < __ANDROID_API_M__) ?
|
||||
AudioFormat::I16 : AudioFormat::Float;
|
||||
}
|
||||
|
||||
Result oboeResult = AudioStreamOpenSLES::open();
|
||||
if (Result::OK != oboeResult) return oboeResult;
|
||||
|
||||
SLuint32 bitsPerSample = static_cast<SLuint32>(getBytesPerSample() * kBitsPerByte);
|
||||
|
||||
// configure audio sink
|
||||
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(),
|
||||
};
|
||||
|
||||
SLDataSink audioSink = {&loc_bufq, &format_pcm};
|
||||
|
||||
/**
|
||||
* API 23 (Marshmallow) introduced support for floating-point data representation and an
|
||||
* extended data format type: SLAndroidDataFormat_PCM_EX for recording streams (playback streams
|
||||
* got this in API 21). If running on API 23+ use this newer format type, creating it from our
|
||||
* original format.
|
||||
*/
|
||||
SLAndroidDataFormat_PCM_EX format_pcm_ex;
|
||||
if (getSdkVersion() >= __ANDROID_API_M__) {
|
||||
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 = EngineOpenSLES::getInstance().createAudioRecorder(&mObjectInterface,
|
||||
&audioSrc,
|
||||
&audioSink);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("createAudioRecorder() result:%s", getSLErrStr(result));
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Configure the stream.
|
||||
result = (*mObjectInterface)->GetInterface(mObjectInterface,
|
||||
EngineOpenSLES::getInstance().getIidAndroidConfiguration(),
|
||||
&configItf);
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGW("%s() GetInterface(SL_IID_ANDROIDCONFIGURATION) failed with %s",
|
||||
__func__, getSLErrStr(result));
|
||||
} else {
|
||||
if (getInputPreset() == InputPreset::VoicePerformance) {
|
||||
LOGD("OpenSL ES does not support InputPreset::VoicePerformance. Use VoiceRecognition.");
|
||||
mInputPreset = InputPreset::VoiceRecognition;
|
||||
}
|
||||
SLuint32 presetValue = OpenSLES_convertInputPreset(getInputPreset());
|
||||
result = (*configItf)->SetConfiguration(configItf,
|
||||
SL_ANDROID_KEY_RECORDING_PRESET,
|
||||
&presetValue,
|
||||
sizeof(SLuint32));
|
||||
if (SL_RESULT_SUCCESS != result
|
||||
&& presetValue != SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION) {
|
||||
presetValue = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
|
||||
LOGD("Setting InputPreset %d failed. Using VoiceRecognition instead.", getInputPreset());
|
||||
mInputPreset = InputPreset::VoiceRecognition;
|
||||
(*configItf)->SetConfiguration(configItf,
|
||||
SL_ANDROID_KEY_RECORDING_PRESET,
|
||||
&presetValue,
|
||||
sizeof(SLuint32));
|
||||
}
|
||||
|
||||
result = configurePerformanceMode(configItf);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
EngineOpenSLES::getInstance().getIidRecord(),
|
||||
&mRecordInterface);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("GetInterface RECORD 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 AudioInputStreamOpenSLES::close() {
|
||||
LOGD("AudioInputStreamOpenSLES::%s()", __func__);
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
Result result = Result::OK;
|
||||
if (getState() == StreamState::Closed) {
|
||||
result = Result::ErrorClosed;
|
||||
} else {
|
||||
(void) requestStop_l();
|
||||
if (OboeGlobals::areWorkaroundsEnabled()) {
|
||||
sleepBeforeClose();
|
||||
}
|
||||
// invalidate any interfaces
|
||||
mRecordInterface = nullptr;
|
||||
result = AudioStreamOpenSLES::close_l();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Result AudioInputStreamOpenSLES::setRecordState_l(SLuint32 newState) {
|
||||
LOGD("AudioInputStreamOpenSLES::%s(%u)", __func__, newState);
|
||||
Result result = Result::OK;
|
||||
|
||||
if (mRecordInterface == nullptr) {
|
||||
LOGW("AudioInputStreamOpenSLES::%s() mRecordInterface is null", __func__);
|
||||
return Result::ErrorInvalidState;
|
||||
}
|
||||
SLresult slResult = (*mRecordInterface)->SetRecordState(mRecordInterface, newState);
|
||||
//LOGD("AudioInputStreamOpenSLES::%s(%u) returned %u", __func__, newState, slResult);
|
||||
if (SL_RESULT_SUCCESS != slResult) {
|
||||
LOGE("AudioInputStreamOpenSLES::%s(%u) returned error %s",
|
||||
__func__, newState, getSLErrStr(slResult));
|
||||
result = Result::ErrorInternal; // TODO review
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Result AudioInputStreamOpenSLES::requestStart() {
|
||||
LOGD("AudioInputStreamOpenSLES(): %s() called", __func__);
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
StreamState initialState = getState();
|
||||
switch (initialState) {
|
||||
case StreamState::Starting:
|
||||
case StreamState::Started:
|
||||
return Result::OK;
|
||||
case StreamState::Closed:
|
||||
return Result::ErrorClosed;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// We use a callback if the user requests one
|
||||
// OR if we have an internal callback to fill the blocking IO buffer.
|
||||
setDataCallbackEnabled(true);
|
||||
|
||||
setState(StreamState::Starting);
|
||||
|
||||
closePerformanceHint();
|
||||
|
||||
if (getBufferDepth(mSimpleBufferQueueInterface) == 0) {
|
||||
// Enqueue the first buffer to start the streaming.
|
||||
// This does not call the callback function.
|
||||
enqueueCallbackBuffer(mSimpleBufferQueueInterface);
|
||||
}
|
||||
|
||||
Result result = setRecordState_l(SL_RECORDSTATE_RECORDING);
|
||||
if (result == Result::OK) {
|
||||
setState(StreamState::Started);
|
||||
} else {
|
||||
setState(initialState);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Result AudioInputStreamOpenSLES::requestPause() {
|
||||
LOGW("AudioInputStreamOpenSLES::%s() is intentionally not implemented for input "
|
||||
"streams", __func__);
|
||||
return Result::ErrorUnimplemented; // Matches AAudio behavior.
|
||||
}
|
||||
|
||||
Result AudioInputStreamOpenSLES::requestFlush() {
|
||||
LOGW("AudioInputStreamOpenSLES::%s() is intentionally not implemented for input "
|
||||
"streams", __func__);
|
||||
return Result::ErrorUnimplemented; // Matches AAudio behavior.
|
||||
}
|
||||
|
||||
Result AudioInputStreamOpenSLES::requestStop() {
|
||||
LOGD("AudioInputStreamOpenSLES(): %s() called", __func__);
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
return requestStop_l();
|
||||
}
|
||||
|
||||
// Call under mLock
|
||||
Result AudioInputStreamOpenSLES::requestStop_l() {
|
||||
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 = setRecordState_l(SL_RECORDSTATE_STOPPED);
|
||||
if (result == Result::OK) {
|
||||
mPositionMillis.reset32(); // OpenSL ES resets its millisecond position when stopped.
|
||||
setState(StreamState::Stopped);
|
||||
} else {
|
||||
setState(initialState);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void AudioInputStreamOpenSLES::updateFramesWritten() {
|
||||
if (usingFIFO()) {
|
||||
AudioStreamBuffered::updateFramesWritten();
|
||||
} else {
|
||||
mFramesWritten = getFramesProcessedByServer();
|
||||
}
|
||||
}
|
||||
|
||||
Result AudioInputStreamOpenSLES::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 (mRecordInterface == nullptr) {
|
||||
mLock.unlock();
|
||||
return Result::ErrorNull;
|
||||
}
|
||||
SLmillisecond msec = 0;
|
||||
SLresult slResult = (*mRecordInterface)->GetPosition(mRecordInterface, &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;
|
||||
}
|
64
externals/oboe/src/opensles/AudioInputStreamOpenSLES.h
vendored
Normal file
64
externals/oboe/src/opensles/AudioInputStreamOpenSLES.h
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 "oboe/Oboe.h"
|
||||
#include "EngineOpenSLES.h"
|
||||
#include "AudioStreamOpenSLES.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/**
|
||||
* INTERNAL USE ONLY
|
||||
*/
|
||||
|
||||
class AudioInputStreamOpenSLES : public AudioStreamOpenSLES {
|
||||
public:
|
||||
AudioInputStreamOpenSLES();
|
||||
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;
|
||||
|
||||
protected:
|
||||
Result requestStop_l();
|
||||
|
||||
Result updateServiceFrameCounter() override;
|
||||
|
||||
void updateFramesWritten() override;
|
||||
|
||||
private:
|
||||
|
||||
SLuint32 channelCountToChannelMask(int chanCount) const;
|
||||
|
||||
Result setRecordState_l(SLuint32 newState);
|
||||
|
||||
SLRecordItf mRecordInterface = nullptr;
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif //AUDIO_INPUT_STREAM_OPENSL_ES_H_
|
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;
|
||||
}
|
78
externals/oboe/src/opensles/AudioOutputStreamOpenSLES.h
vendored
Normal file
78
externals/oboe/src/opensles/AudioOutputStreamOpenSLES.h
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 "oboe/Oboe.h"
|
||||
#include "EngineOpenSLES.h"
|
||||
#include "AudioStreamOpenSLES.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/**
|
||||
* INTERNAL USE ONLY
|
||||
*/
|
||||
class AudioOutputStreamOpenSLES : public AudioStreamOpenSLES {
|
||||
public:
|
||||
AudioOutputStreamOpenSLES();
|
||||
explicit AudioOutputStreamOpenSLES(const AudioStreamBuilder &builder);
|
||||
|
||||
virtual ~AudioOutputStreamOpenSLES() = default;
|
||||
|
||||
Result open() override;
|
||||
Result close() override;
|
||||
|
||||
Result requestStart() override;
|
||||
Result requestPause() override;
|
||||
Result requestFlush() override;
|
||||
Result requestStop() override;
|
||||
|
||||
protected:
|
||||
Result requestPause_l();
|
||||
|
||||
void setFramesRead(int64_t framesRead);
|
||||
|
||||
Result updateServiceFrameCounter() override;
|
||||
|
||||
void updateFramesRead() override;
|
||||
|
||||
private:
|
||||
|
||||
SLuint32 channelCountToChannelMask(int chanCount) const;
|
||||
|
||||
Result onAfterDestroy() override;
|
||||
|
||||
Result requestFlush_l();
|
||||
|
||||
Result requestStop_l();
|
||||
|
||||
/**
|
||||
* Set OpenSL ES PLAYSTATE.
|
||||
*
|
||||
* @param newState SL_PLAYSTATE_PAUSED, SL_PLAYSTATE_PLAYING, SL_PLAYSTATE_STOPPED
|
||||
* @return
|
||||
*/
|
||||
Result setPlayState_l(SLuint32 newState);
|
||||
|
||||
SLPlayItf mPlayInterface = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif //AUDIO_OUTPUT_STREAM_OPENSL_ES_H_
|
285
externals/oboe/src/opensles/AudioStreamBuffered.cpp
vendored
Normal file
285
externals/oboe/src/opensles/AudioStreamBuffered.cpp
vendored
Normal file
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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 <memory>
|
||||
|
||||
#include "oboe/Oboe.h"
|
||||
|
||||
#include "common/OboeDebug.h"
|
||||
#include "oboe/AudioClock.h"
|
||||
#include "opensles/AudioStreamBuffered.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
constexpr int kDefaultBurstsPerBuffer = 16; // arbitrary, allows dynamic latency tuning
|
||||
constexpr int kMinBurstsPerBuffer = 4; // arbitrary, allows dynamic latency tuning
|
||||
constexpr int kMinFramesPerBuffer = 48 * 32; // arbitrary
|
||||
|
||||
/*
|
||||
* AudioStream with a FifoBuffer
|
||||
*/
|
||||
AudioStreamBuffered::AudioStreamBuffered(const AudioStreamBuilder &builder)
|
||||
: AudioStream(builder) {
|
||||
}
|
||||
|
||||
void AudioStreamBuffered::allocateFifo() {
|
||||
// If the caller does not provide a callback use our own internal
|
||||
// callback that reads data from the FIFO.
|
||||
if (usingFIFO()) {
|
||||
// FIFO is configured with the same format and channels as the stream.
|
||||
int32_t capacityFrames = getBufferCapacityInFrames();
|
||||
if (capacityFrames == oboe::kUnspecified) {
|
||||
capacityFrames = getFramesPerBurst() * kDefaultBurstsPerBuffer;
|
||||
} else {
|
||||
int32_t minFramesPerBufferByBursts = getFramesPerBurst() * kMinBurstsPerBuffer;
|
||||
if (capacityFrames <= minFramesPerBufferByBursts) {
|
||||
capacityFrames = minFramesPerBufferByBursts;
|
||||
} else {
|
||||
capacityFrames = std::max(kMinFramesPerBuffer, capacityFrames);
|
||||
// round up to nearest burst
|
||||
int32_t numBursts = (capacityFrames + getFramesPerBurst() - 1)
|
||||
/ getFramesPerBurst();
|
||||
capacityFrames = numBursts * getFramesPerBurst();
|
||||
}
|
||||
}
|
||||
|
||||
mFifoBuffer = std::make_unique<FifoBuffer>(getBytesPerFrame(), capacityFrames);
|
||||
mBufferCapacityInFrames = capacityFrames;
|
||||
mBufferSizeInFrames = mBufferCapacityInFrames;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamBuffered::updateFramesWritten() {
|
||||
if (mFifoBuffer) {
|
||||
mFramesWritten = static_cast<int64_t>(mFifoBuffer->getWriteCounter());
|
||||
} // or else it will get updated by processBufferCallback()
|
||||
}
|
||||
|
||||
void AudioStreamBuffered::updateFramesRead() {
|
||||
if (mFifoBuffer) {
|
||||
mFramesRead = static_cast<int64_t>(mFifoBuffer->getReadCounter());
|
||||
} // or else it will get updated by processBufferCallback()
|
||||
}
|
||||
|
||||
// This is called by the OpenSL ES callback to read or write the back end of the FIFO.
|
||||
DataCallbackResult AudioStreamBuffered::onDefaultCallback(void *audioData, int numFrames) {
|
||||
int32_t framesTransferred = 0;
|
||||
|
||||
if (getDirection() == oboe::Direction::Output) {
|
||||
// Read from the FIFO and write to audioData, clear part of buffer if not enough data.
|
||||
framesTransferred = mFifoBuffer->readNow(audioData, numFrames);
|
||||
} else {
|
||||
// Read from audioData and write to the FIFO
|
||||
framesTransferred = mFifoBuffer->write(audioData, numFrames); // There is no writeNow()
|
||||
}
|
||||
|
||||
if (framesTransferred < numFrames) {
|
||||
LOGD("AudioStreamBuffered::%s(): xrun! framesTransferred = %d, numFrames = %d",
|
||||
__func__, framesTransferred, numFrames);
|
||||
// TODO If we do not allow FIFO to wrap then our timestamps will drift when there is an XRun!
|
||||
incrementXRunCount();
|
||||
}
|
||||
markCallbackTime(static_cast<int32_t>(numFrames)); // so foreground knows how long to wait.
|
||||
return DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
void AudioStreamBuffered::markCallbackTime(int32_t numFrames) {
|
||||
mLastBackgroundSize = numFrames;
|
||||
mBackgroundRanAtNanoseconds = AudioClock::getNanoseconds();
|
||||
}
|
||||
|
||||
int64_t AudioStreamBuffered::predictNextCallbackTime() {
|
||||
if (mBackgroundRanAtNanoseconds == 0) {
|
||||
return 0;
|
||||
}
|
||||
int64_t nanosPerBuffer = (kNanosPerSecond * mLastBackgroundSize) / getSampleRate();
|
||||
const int64_t margin = 200 * kNanosPerMicrosecond; // arbitrary delay so we wake up just after
|
||||
return mBackgroundRanAtNanoseconds + nanosPerBuffer + margin;
|
||||
}
|
||||
|
||||
// Common code for read/write.
|
||||
// @return Result::OK with frames read/written, or Result::Error*
|
||||
ResultWithValue<int32_t> AudioStreamBuffered::transfer(
|
||||
void *readBuffer,
|
||||
const void *writeBuffer,
|
||||
int32_t numFrames,
|
||||
int64_t timeoutNanoseconds) {
|
||||
// Validate arguments.
|
||||
if (readBuffer != nullptr && writeBuffer != nullptr) {
|
||||
LOGE("AudioStreamBuffered::%s(): both buffers are not NULL", __func__);
|
||||
return ResultWithValue<int32_t>(Result::ErrorInternal);
|
||||
}
|
||||
if (getDirection() == Direction::Input && readBuffer == nullptr) {
|
||||
LOGE("AudioStreamBuffered::%s(): readBuffer is NULL", __func__);
|
||||
return ResultWithValue<int32_t>(Result::ErrorNull);
|
||||
}
|
||||
if (getDirection() == Direction::Output && writeBuffer == nullptr) {
|
||||
LOGE("AudioStreamBuffered::%s(): writeBuffer is NULL", __func__);
|
||||
return ResultWithValue<int32_t>(Result::ErrorNull);
|
||||
}
|
||||
if (numFrames < 0) {
|
||||
LOGE("AudioStreamBuffered::%s(): numFrames is negative", __func__);
|
||||
return ResultWithValue<int32_t>(Result::ErrorOutOfRange);
|
||||
} else if (numFrames == 0) {
|
||||
return ResultWithValue<int32_t>(numFrames);
|
||||
}
|
||||
if (timeoutNanoseconds < 0) {
|
||||
LOGE("AudioStreamBuffered::%s(): timeoutNanoseconds is negative", __func__);
|
||||
return ResultWithValue<int32_t>(Result::ErrorOutOfRange);
|
||||
}
|
||||
|
||||
int32_t result = 0;
|
||||
uint8_t *readData = reinterpret_cast<uint8_t *>(readBuffer);
|
||||
const uint8_t *writeData = reinterpret_cast<const uint8_t *>(writeBuffer);
|
||||
int32_t framesLeft = numFrames;
|
||||
int64_t timeToQuit = 0;
|
||||
bool repeat = true;
|
||||
|
||||
// Calculate when to timeout.
|
||||
if (timeoutNanoseconds > 0) {
|
||||
timeToQuit = AudioClock::getNanoseconds() + timeoutNanoseconds;
|
||||
}
|
||||
|
||||
// Loop until we get the data, or we have an error, or we timeout.
|
||||
do {
|
||||
// read or write
|
||||
if (getDirection() == Direction::Input) {
|
||||
result = mFifoBuffer->read(readData, framesLeft);
|
||||
if (result > 0) {
|
||||
readData += mFifoBuffer->convertFramesToBytes(result);
|
||||
framesLeft -= result;
|
||||
}
|
||||
} else {
|
||||
// between zero and capacity
|
||||
uint32_t fullFrames = mFifoBuffer->getFullFramesAvailable();
|
||||
// Do not write above threshold size.
|
||||
int32_t emptyFrames = getBufferSizeInFrames() - static_cast<int32_t>(fullFrames);
|
||||
int32_t framesToWrite = std::max(0, std::min(framesLeft, emptyFrames));
|
||||
result = mFifoBuffer->write(writeData, framesToWrite);
|
||||
if (result > 0) {
|
||||
writeData += mFifoBuffer->convertFramesToBytes(result);
|
||||
framesLeft -= result;
|
||||
}
|
||||
}
|
||||
|
||||
// If we need more data then sleep and try again.
|
||||
if (framesLeft > 0 && result >= 0 && timeoutNanoseconds > 0) {
|
||||
int64_t timeNow = AudioClock::getNanoseconds();
|
||||
if (timeNow >= timeToQuit) {
|
||||
LOGE("AudioStreamBuffered::%s(): TIMEOUT", __func__);
|
||||
repeat = false; // TIMEOUT
|
||||
} else {
|
||||
// Figure out how long to sleep.
|
||||
int64_t sleepForNanos;
|
||||
int64_t wakeTimeNanos = predictNextCallbackTime();
|
||||
if (wakeTimeNanos <= 0) {
|
||||
// No estimate available. Sleep for one burst.
|
||||
sleepForNanos = (getFramesPerBurst() * kNanosPerSecond) / getSampleRate();
|
||||
} else {
|
||||
// Don't sleep past timeout.
|
||||
if (wakeTimeNanos > timeToQuit) {
|
||||
wakeTimeNanos = timeToQuit;
|
||||
}
|
||||
sleepForNanos = wakeTimeNanos - timeNow;
|
||||
// Avoid rapid loop with no sleep.
|
||||
const int64_t minSleepTime = kNanosPerMillisecond; // arbitrary
|
||||
if (sleepForNanos < minSleepTime) {
|
||||
sleepForNanos = minSleepTime;
|
||||
}
|
||||
}
|
||||
|
||||
AudioClock::sleepForNanos(sleepForNanos);
|
||||
}
|
||||
|
||||
} else {
|
||||
repeat = false;
|
||||
}
|
||||
} while(repeat);
|
||||
|
||||
if (result < 0) {
|
||||
return ResultWithValue<int32_t>(static_cast<Result>(result));
|
||||
} else {
|
||||
int32_t framesWritten = numFrames - framesLeft;
|
||||
return ResultWithValue<int32_t>(framesWritten);
|
||||
}
|
||||
}
|
||||
|
||||
// Write to the FIFO so the callback can read from it.
|
||||
ResultWithValue<int32_t> AudioStreamBuffered::write(const void *buffer,
|
||||
int32_t numFrames,
|
||||
int64_t timeoutNanoseconds) {
|
||||
if (getState() == StreamState::Closed){
|
||||
return ResultWithValue<int32_t>(Result::ErrorClosed);
|
||||
}
|
||||
|
||||
if (getDirection() == Direction::Input) {
|
||||
return ResultWithValue<int32_t>(Result::ErrorUnavailable); // TODO review, better error code?
|
||||
}
|
||||
Result result = updateServiceFrameCounter();
|
||||
if (result != Result::OK) return ResultWithValue<int32_t>(static_cast<Result>(result));
|
||||
return transfer(nullptr, buffer, numFrames, timeoutNanoseconds);
|
||||
}
|
||||
|
||||
// Read data from the FIFO that was written by the callback.
|
||||
ResultWithValue<int32_t> AudioStreamBuffered::read(void *buffer,
|
||||
int32_t numFrames,
|
||||
int64_t timeoutNanoseconds) {
|
||||
if (getState() == StreamState::Closed){
|
||||
return ResultWithValue<int32_t>(Result::ErrorClosed);
|
||||
}
|
||||
|
||||
if (getDirection() == Direction::Output) {
|
||||
return ResultWithValue<int32_t>(Result::ErrorUnavailable); // TODO review, better error code?
|
||||
}
|
||||
Result result = updateServiceFrameCounter();
|
||||
if (result != Result::OK) return ResultWithValue<int32_t>(static_cast<Result>(result));
|
||||
return transfer(buffer, nullptr, numFrames, timeoutNanoseconds);
|
||||
}
|
||||
|
||||
// Only supported when we are not using a callback.
|
||||
ResultWithValue<int32_t> AudioStreamBuffered::setBufferSizeInFrames(int32_t requestedFrames)
|
||||
{
|
||||
if (getState() == StreamState::Closed){
|
||||
return ResultWithValue<int32_t>(Result::ErrorClosed);
|
||||
}
|
||||
|
||||
if (!mFifoBuffer) {
|
||||
return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
|
||||
}
|
||||
|
||||
if (requestedFrames > mFifoBuffer->getBufferCapacityInFrames()) {
|
||||
requestedFrames = mFifoBuffer->getBufferCapacityInFrames();
|
||||
} else if (requestedFrames < getFramesPerBurst()) {
|
||||
requestedFrames = getFramesPerBurst();
|
||||
}
|
||||
mBufferSizeInFrames = requestedFrames;
|
||||
return ResultWithValue<int32_t>(requestedFrames);
|
||||
}
|
||||
|
||||
int32_t AudioStreamBuffered::getBufferCapacityInFrames() const {
|
||||
if (mFifoBuffer) {
|
||||
return mFifoBuffer->getBufferCapacityInFrames();
|
||||
} else {
|
||||
return AudioStream::getBufferCapacityInFrames();
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioStreamBuffered::isXRunCountSupported() const {
|
||||
// XRun count is only supported if we're using blocking I/O (not callbacks)
|
||||
return (!isDataCallbackSpecified());
|
||||
}
|
||||
|
||||
} // namespace oboe
|
96
externals/oboe/src/opensles/AudioStreamBuffered.h
vendored
Normal file
96
externals/oboe/src/opensles/AudioStreamBuffered.h
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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_STREAM_BUFFERED_H
|
||||
#define OBOE_STREAM_BUFFERED_H
|
||||
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include "common/OboeDebug.h"
|
||||
#include "oboe/AudioStream.h"
|
||||
#include "oboe/AudioStreamCallback.h"
|
||||
#include "oboe/FifoBuffer.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
// A stream that contains a FIFO buffer.
|
||||
// This is used to implement blocking reads and writes.
|
||||
class AudioStreamBuffered : public AudioStream {
|
||||
public:
|
||||
|
||||
AudioStreamBuffered();
|
||||
explicit AudioStreamBuffered(const AudioStreamBuilder &builder);
|
||||
|
||||
void allocateFifo();
|
||||
|
||||
|
||||
ResultWithValue<int32_t> write(const void *buffer,
|
||||
int32_t numFrames,
|
||||
int64_t timeoutNanoseconds) override;
|
||||
|
||||
ResultWithValue<int32_t> read(void *buffer,
|
||||
int32_t numFrames,
|
||||
int64_t timeoutNanoseconds) override;
|
||||
|
||||
ResultWithValue<int32_t> setBufferSizeInFrames(int32_t requestedFrames) override;
|
||||
|
||||
int32_t getBufferCapacityInFrames() const override;
|
||||
|
||||
ResultWithValue<int32_t> getXRunCount() override {
|
||||
return ResultWithValue<int32_t>(mXRunCount);
|
||||
}
|
||||
|
||||
bool isXRunCountSupported() const override;
|
||||
|
||||
protected:
|
||||
|
||||
DataCallbackResult onDefaultCallback(void *audioData, int numFrames) override;
|
||||
|
||||
// If there is no callback then we need a FIFO between the App and OpenSL ES.
|
||||
bool usingFIFO() const { return !isDataCallbackSpecified(); }
|
||||
|
||||
virtual Result updateServiceFrameCounter() = 0;
|
||||
|
||||
void updateFramesRead() override;
|
||||
void updateFramesWritten() override;
|
||||
|
||||
private:
|
||||
|
||||
int64_t predictNextCallbackTime();
|
||||
|
||||
void markCallbackTime(int32_t numFrames);
|
||||
|
||||
// Read or write to the FIFO.
|
||||
// Only pass one pointer and set the other to nullptr.
|
||||
ResultWithValue<int32_t> transfer(void *readBuffer,
|
||||
const void *writeBuffer,
|
||||
int32_t numFrames,
|
||||
int64_t timeoutNanoseconds);
|
||||
|
||||
void incrementXRunCount() {
|
||||
++mXRunCount;
|
||||
}
|
||||
|
||||
std::unique_ptr<FifoBuffer> mFifoBuffer{};
|
||||
|
||||
int64_t mBackgroundRanAtNanoseconds = 0;
|
||||
int32_t mLastBackgroundSize = 0;
|
||||
int32_t mXRunCount = 0;
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif //OBOE_STREAM_BUFFERED_H
|
523
externals/oboe/src/opensles/AudioStreamOpenSLES.cpp
vendored
Normal file
523
externals/oboe/src/opensles/AudioStreamOpenSLES.cpp
vendored
Normal file
|
@ -0,0 +1,523 @@
|
|||
/* Copyright 2015 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 <sys/types.h>
|
||||
#include <cassert>
|
||||
#include <android/log.h>
|
||||
|
||||
#include "common/OboeDebug.h"
|
||||
#include "oboe/AudioClock.h"
|
||||
#include "oboe/AudioStream.h"
|
||||
#include "oboe/AudioStreamBuilder.h"
|
||||
#include "EngineOpenSLES.h"
|
||||
#include "AudioStreamOpenSLES.h"
|
||||
#include "OpenSLESUtilities.h"
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
AudioStreamOpenSLES::AudioStreamOpenSLES(const AudioStreamBuilder &builder)
|
||||
: AudioStreamBuffered(builder) {
|
||||
// OpenSL ES does not support device IDs. So overwrite value from builder.
|
||||
mDeviceIds.clear();
|
||||
// OpenSL ES does not support session IDs. So overwrite value from builder.
|
||||
mSessionId = SessionId::None;
|
||||
}
|
||||
|
||||
static constexpr int32_t kHighLatencyBufferSizeMillis = 20; // typical Android period
|
||||
static constexpr SLuint32 kAudioChannelCountMax = 30; // TODO Why 30?
|
||||
static constexpr SLuint32 SL_ANDROID_UNKNOWN_CHANNELMASK = 0; // Matches name used internally.
|
||||
|
||||
SLuint32 AudioStreamOpenSLES::channelCountToChannelMaskDefault(int channelCount) const {
|
||||
if (channelCount > kAudioChannelCountMax) {
|
||||
return SL_ANDROID_UNKNOWN_CHANNELMASK;
|
||||
}
|
||||
|
||||
SLuint32 bitfield = (1 << channelCount) - 1;
|
||||
|
||||
// Check for OS at run-time.
|
||||
if(getSdkVersion() >= __ANDROID_API_N__) {
|
||||
return SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(bitfield);
|
||||
}
|
||||
|
||||
// Indexed channels masks were added in N.
|
||||
// For before N, the best we can do is use a positional channel mask.
|
||||
return bitfield;
|
||||
}
|
||||
|
||||
static bool s_isLittleEndian() {
|
||||
static uint32_t value = 1;
|
||||
return (*reinterpret_cast<uint8_t *>(&value) == 1); // Does address point to LSB?
|
||||
}
|
||||
|
||||
SLuint32 AudioStreamOpenSLES::getDefaultByteOrder() {
|
||||
return s_isLittleEndian() ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
|
||||
}
|
||||
|
||||
Result AudioStreamOpenSLES::open() {
|
||||
#ifndef OBOE_SUPPRESS_LOG_SPAM
|
||||
LOGI("AudioStreamOpenSLES::open() chans=%d, rate=%d", mChannelCount, mSampleRate);
|
||||
#endif
|
||||
|
||||
// OpenSL ES only supports I16 and Float
|
||||
if (mFormat != AudioFormat::I16 && mFormat != AudioFormat::Float) {
|
||||
LOGW("%s() Android's OpenSL ES implementation only supports I16 and Float. Format: %s",
|
||||
__func__, oboe::convertToText(mFormat));
|
||||
return Result::ErrorInvalidFormat;
|
||||
}
|
||||
|
||||
SLresult result = EngineOpenSLES::getInstance().open();
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
return Result::ErrorInternal;
|
||||
}
|
||||
|
||||
Result oboeResult = AudioStreamBuffered::open();
|
||||
if (oboeResult != Result::OK) {
|
||||
EngineOpenSLES::getInstance().close();
|
||||
return oboeResult;
|
||||
}
|
||||
// Convert to defaults if UNSPECIFIED
|
||||
if (mSampleRate == kUnspecified) {
|
||||
mSampleRate = DefaultStreamValues::SampleRate;
|
||||
}
|
||||
if (mChannelCount == kUnspecified) {
|
||||
mChannelCount = DefaultStreamValues::ChannelCount;
|
||||
}
|
||||
if (mContentType == kUnspecified) {
|
||||
mContentType = ContentType::Music;
|
||||
}
|
||||
if (static_cast<const int32_t>(mUsage) == kUnspecified) {
|
||||
mUsage = Usage::Media;
|
||||
}
|
||||
|
||||
mSharingMode = SharingMode::Shared;
|
||||
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
|
||||
SLresult AudioStreamOpenSLES::finishCommonOpen(SLAndroidConfigurationItf configItf) {
|
||||
// Setting privacy sensitive mode and allowed capture policy are not supported for OpenSL ES.
|
||||
mPrivacySensitiveMode = PrivacySensitiveMode::Unspecified;
|
||||
mAllowedCapturePolicy = AllowedCapturePolicy::Unspecified;
|
||||
|
||||
// Spatialization Behavior is not supported for OpenSL ES.
|
||||
mSpatializationBehavior = SpatializationBehavior::Never;
|
||||
|
||||
SLresult result = registerBufferQueueCallback();
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = updateStreamParameters(configItf);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Result oboeResult = configureBufferSizes(mSampleRate);
|
||||
if (Result::OK != oboeResult) {
|
||||
return (SLresult) oboeResult;
|
||||
}
|
||||
|
||||
allocateFifo();
|
||||
|
||||
calculateDefaultDelayBeforeCloseMillis();
|
||||
|
||||
return SL_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static int32_t roundUpDivideByN(int32_t x, int32_t n) {
|
||||
return (x + n - 1) / n;
|
||||
}
|
||||
|
||||
int32_t AudioStreamOpenSLES::calculateOptimalBufferQueueLength() {
|
||||
int32_t queueLength = kBufferQueueLengthDefault;
|
||||
int32_t likelyFramesPerBurst = estimateNativeFramesPerBurst();
|
||||
int32_t minCapacity = mBufferCapacityInFrames; // specified by app or zero
|
||||
// The buffer capacity needs to be at least twice the size of the requested callbackSize
|
||||
// so that we can have double buffering.
|
||||
minCapacity = std::max(minCapacity, kDoubleBufferCount * mFramesPerCallback);
|
||||
if (minCapacity > 0) {
|
||||
int32_t queueLengthFromCapacity = roundUpDivideByN(minCapacity, likelyFramesPerBurst);
|
||||
queueLength = std::max(queueLength, queueLengthFromCapacity);
|
||||
}
|
||||
queueLength = std::min(queueLength, kBufferQueueLengthMax); // clip to max
|
||||
// TODO Investigate the effect of queueLength on latency for normal streams. (not low latency)
|
||||
return queueLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* The best information we have is if DefaultStreamValues::FramesPerBurst
|
||||
* was set by the app based on AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER.
|
||||
* Without that we just have to guess.
|
||||
* @return
|
||||
*/
|
||||
int32_t AudioStreamOpenSLES::estimateNativeFramesPerBurst() {
|
||||
int32_t framesPerBurst = DefaultStreamValues::FramesPerBurst;
|
||||
LOGD("AudioStreamOpenSLES:%s() DefaultStreamValues::FramesPerBurst = %d",
|
||||
__func__, DefaultStreamValues::FramesPerBurst);
|
||||
framesPerBurst = std::max(framesPerBurst, 16);
|
||||
// Calculate the size of a fixed duration high latency buffer based on sample rate.
|
||||
// Estimate sample based on default options in order of priority.
|
||||
int32_t sampleRate = 48000;
|
||||
sampleRate = (DefaultStreamValues::SampleRate > 0)
|
||||
? DefaultStreamValues::SampleRate : sampleRate;
|
||||
sampleRate = (mSampleRate > 0) ? mSampleRate : sampleRate;
|
||||
int32_t framesPerHighLatencyBuffer =
|
||||
(kHighLatencyBufferSizeMillis * sampleRate) / kMillisPerSecond;
|
||||
// For high latency streams, use a larger buffer size.
|
||||
// Performance Mode support was added in N_MR1 (7.1)
|
||||
if (getSdkVersion() >= __ANDROID_API_N_MR1__
|
||||
&& mPerformanceMode != PerformanceMode::LowLatency
|
||||
&& framesPerBurst < framesPerHighLatencyBuffer) {
|
||||
// Find a multiple of framesPerBurst >= framesPerHighLatencyBuffer.
|
||||
int32_t numBursts = roundUpDivideByN(framesPerHighLatencyBuffer, framesPerBurst);
|
||||
framesPerBurst *= numBursts;
|
||||
LOGD("AudioStreamOpenSLES:%s() NOT low latency, numBursts = %d, mSampleRate = %d, set framesPerBurst = %d",
|
||||
__func__, numBursts, mSampleRate, framesPerBurst);
|
||||
}
|
||||
return framesPerBurst;
|
||||
}
|
||||
|
||||
Result AudioStreamOpenSLES::configureBufferSizes(int32_t sampleRate) {
|
||||
LOGD("AudioStreamOpenSLES:%s(%d) initial mFramesPerBurst = %d, mFramesPerCallback = %d",
|
||||
__func__, mSampleRate, mFramesPerBurst, mFramesPerCallback);
|
||||
mFramesPerBurst = estimateNativeFramesPerBurst();
|
||||
mFramesPerCallback = (mFramesPerCallback > 0) ? mFramesPerCallback : mFramesPerBurst;
|
||||
LOGD("AudioStreamOpenSLES:%s(%d) final mFramesPerBurst = %d, mFramesPerCallback = %d",
|
||||
__func__, mSampleRate, mFramesPerBurst, mFramesPerCallback);
|
||||
|
||||
mBytesPerCallback = mFramesPerCallback * getBytesPerFrame();
|
||||
if (mBytesPerCallback <= 0) {
|
||||
LOGE("AudioStreamOpenSLES::open() bytesPerCallback < 0 = %d, bad format?",
|
||||
mBytesPerCallback);
|
||||
return Result::ErrorInvalidFormat; // causing bytesPerFrame == 0
|
||||
}
|
||||
|
||||
for (int i = 0; i < mBufferQueueLength; ++i) {
|
||||
mCallbackBuffer[i] = std::make_unique<uint8_t[]>(mBytesPerCallback);
|
||||
}
|
||||
|
||||
if (!usingFIFO()) {
|
||||
mBufferCapacityInFrames = mFramesPerBurst * mBufferQueueLength;
|
||||
// Check for overflow.
|
||||
if (mBufferCapacityInFrames <= 0) {
|
||||
mBufferCapacityInFrames = 0;
|
||||
LOGE("AudioStreamOpenSLES::open() numeric overflow because mFramesPerBurst = %d",
|
||||
mFramesPerBurst);
|
||||
return Result::ErrorOutOfRange;
|
||||
}
|
||||
mBufferSizeInFrames = mBufferCapacityInFrames;
|
||||
}
|
||||
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
SLuint32 AudioStreamOpenSLES::convertPerformanceMode(PerformanceMode oboeMode) const {
|
||||
SLuint32 openslMode = SL_ANDROID_PERFORMANCE_NONE;
|
||||
switch(oboeMode) {
|
||||
case PerformanceMode::None:
|
||||
openslMode = SL_ANDROID_PERFORMANCE_NONE;
|
||||
break;
|
||||
case PerformanceMode::LowLatency:
|
||||
openslMode = (getSessionId() == SessionId::None) ? SL_ANDROID_PERFORMANCE_LATENCY : SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS;
|
||||
break;
|
||||
case PerformanceMode::PowerSaving:
|
||||
openslMode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return openslMode;
|
||||
}
|
||||
|
||||
PerformanceMode AudioStreamOpenSLES::convertPerformanceMode(SLuint32 openslMode) const {
|
||||
PerformanceMode oboeMode = PerformanceMode::None;
|
||||
switch(openslMode) {
|
||||
case SL_ANDROID_PERFORMANCE_NONE:
|
||||
oboeMode = PerformanceMode::None;
|
||||
break;
|
||||
case SL_ANDROID_PERFORMANCE_LATENCY:
|
||||
case SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS:
|
||||
oboeMode = PerformanceMode::LowLatency;
|
||||
break;
|
||||
case SL_ANDROID_PERFORMANCE_POWER_SAVING:
|
||||
oboeMode = PerformanceMode::PowerSaving;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return oboeMode;
|
||||
}
|
||||
|
||||
void AudioStreamOpenSLES::logUnsupportedAttributes() {
|
||||
// Log unsupported attributes
|
||||
// only report if changed from the default
|
||||
|
||||
// Device ID
|
||||
if (!mDeviceIds.empty()) {
|
||||
LOGW("Device ID [AudioStreamBuilder::setDeviceId()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
// Sharing Mode
|
||||
if (mSharingMode != SharingMode::Shared) {
|
||||
LOGW("SharingMode [AudioStreamBuilder::setSharingMode()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
// Performance Mode
|
||||
int sdkVersion = getSdkVersion();
|
||||
if (mPerformanceMode != PerformanceMode::None && sdkVersion < __ANDROID_API_N_MR1__) {
|
||||
LOGW("PerformanceMode [AudioStreamBuilder::setPerformanceMode()] "
|
||||
"is not supported on OpenSLES streams running on pre-Android N-MR1 versions.");
|
||||
}
|
||||
// Content Type
|
||||
if (static_cast<const int32_t>(mContentType) != kUnspecified) {
|
||||
LOGW("ContentType [AudioStreamBuilder::setContentType()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
|
||||
// Session Id
|
||||
if (mSessionId != SessionId::None) {
|
||||
LOGW("SessionId [AudioStreamBuilder::setSessionId()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
|
||||
// Privacy Sensitive Mode
|
||||
if (mPrivacySensitiveMode != PrivacySensitiveMode::Unspecified) {
|
||||
LOGW("PrivacySensitiveMode [AudioStreamBuilder::setPrivacySensitiveMode()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
|
||||
// Spatialization Behavior
|
||||
if (mSpatializationBehavior != SpatializationBehavior::Unspecified) {
|
||||
LOGW("SpatializationBehavior [AudioStreamBuilder::setSpatializationBehavior()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
|
||||
if (mIsContentSpatialized) {
|
||||
LOGW("Boolean [AudioStreamBuilder::setIsContentSpatialized()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
|
||||
// Allowed Capture Policy
|
||||
if (mAllowedCapturePolicy != AllowedCapturePolicy::Unspecified) {
|
||||
LOGW("AllowedCapturePolicy [AudioStreamBuilder::setAllowedCapturePolicy()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
|
||||
// Package Name
|
||||
if (!mPackageName.empty()) {
|
||||
LOGW("PackageName [AudioStreamBuilder::setPackageName()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
|
||||
// Attribution Tag
|
||||
if (!mAttributionTag.empty()) {
|
||||
LOGW("AttributionTag [AudioStreamBuilder::setAttributionTag()] "
|
||||
"is not supported on OpenSLES streams.");
|
||||
}
|
||||
}
|
||||
|
||||
SLresult AudioStreamOpenSLES::configurePerformanceMode(SLAndroidConfigurationItf configItf) {
|
||||
|
||||
if (configItf == nullptr) {
|
||||
LOGW("%s() called with NULL configuration", __func__);
|
||||
mPerformanceMode = PerformanceMode::None;
|
||||
return SL_RESULT_INTERNAL_ERROR;
|
||||
}
|
||||
if (getSdkVersion() < __ANDROID_API_N_MR1__) {
|
||||
LOGW("%s() not supported until N_MR1", __func__);
|
||||
mPerformanceMode = PerformanceMode::None;
|
||||
return SL_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
SLresult result = SL_RESULT_SUCCESS;
|
||||
SLuint32 performanceMode = convertPerformanceMode(getPerformanceMode());
|
||||
result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE,
|
||||
&performanceMode, sizeof(performanceMode));
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGW("SetConfiguration(PERFORMANCE_MODE, SL %u) returned %s",
|
||||
performanceMode, getSLErrStr(result));
|
||||
mPerformanceMode = PerformanceMode::None;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SLresult AudioStreamOpenSLES::updateStreamParameters(SLAndroidConfigurationItf configItf) {
|
||||
SLresult result = SL_RESULT_SUCCESS;
|
||||
if(getSdkVersion() >= __ANDROID_API_N_MR1__ && configItf != nullptr) {
|
||||
SLuint32 performanceMode = 0;
|
||||
SLuint32 performanceModeSize = sizeof(performanceMode);
|
||||
result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_PERFORMANCE_MODE,
|
||||
&performanceModeSize, &performanceMode);
|
||||
// A bug in GetConfiguration() before P caused a wrong result code to be returned.
|
||||
if (getSdkVersion() <= __ANDROID_API_O_MR1__) {
|
||||
result = SL_RESULT_SUCCESS; // Ignore actual result before P.
|
||||
}
|
||||
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGW("GetConfiguration(SL_ANDROID_KEY_PERFORMANCE_MODE) returned %d", result);
|
||||
mPerformanceMode = PerformanceMode::None; // If we can't query it then assume None.
|
||||
} else {
|
||||
mPerformanceMode = convertPerformanceMode(performanceMode); // convert SL to Oboe mode
|
||||
}
|
||||
} else {
|
||||
mPerformanceMode = PerformanceMode::None; // If we can't query it then assume None.
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// This is called under mLock.
|
||||
Result AudioStreamOpenSLES::close_l() {
|
||||
LOGD("AudioOutputStreamOpenSLES::%s() called", __func__);
|
||||
if (mState == StreamState::Closed) {
|
||||
return Result::ErrorClosed;
|
||||
}
|
||||
|
||||
AudioStreamBuffered::close();
|
||||
|
||||
onBeforeDestroy();
|
||||
|
||||
// Mark as CLOSED before we unlock for the join.
|
||||
// This will prevent other threads from trying to close().
|
||||
setState(StreamState::Closed);
|
||||
|
||||
SLObjectItf tempObjectInterface = mObjectInterface;
|
||||
mObjectInterface = nullptr;
|
||||
if (tempObjectInterface != nullptr) {
|
||||
// Temporarily unlock so we can join() the callback thread.
|
||||
mLock.unlock();
|
||||
(*tempObjectInterface)->Destroy(tempObjectInterface); // Will join the callback!
|
||||
mLock.lock();
|
||||
}
|
||||
|
||||
onAfterDestroy();
|
||||
|
||||
mSimpleBufferQueueInterface = nullptr;
|
||||
EngineOpenSLES::getInstance().close();
|
||||
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
SLresult AudioStreamOpenSLES::enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq) {
|
||||
SLresult result = (*bq)->Enqueue(
|
||||
bq, mCallbackBuffer[mCallbackBufferIndex].get(), mBytesPerCallback);
|
||||
mCallbackBufferIndex = (mCallbackBufferIndex + 1) % mBufferQueueLength;
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t AudioStreamOpenSLES::getBufferDepth(SLAndroidSimpleBufferQueueItf bq) {
|
||||
SLAndroidSimpleBufferQueueState queueState;
|
||||
SLresult result = (*bq)->GetState(bq, &queueState);
|
||||
return (result == SL_RESULT_SUCCESS) ? queueState.count : -1;
|
||||
}
|
||||
|
||||
bool AudioStreamOpenSLES::processBufferCallback(SLAndroidSimpleBufferQueueItf bq) {
|
||||
bool shouldStopStream = false;
|
||||
// Ask the app callback to process the buffer.
|
||||
DataCallbackResult result =
|
||||
fireDataCallback(mCallbackBuffer[mCallbackBufferIndex].get(), mFramesPerCallback);
|
||||
if (result == DataCallbackResult::Continue) {
|
||||
// Pass the buffer to OpenSLES.
|
||||
SLresult enqueueResult = enqueueCallbackBuffer(bq);
|
||||
if (enqueueResult != SL_RESULT_SUCCESS) {
|
||||
LOGE("%s() returned %d", __func__, enqueueResult);
|
||||
shouldStopStream = true;
|
||||
}
|
||||
// Update Oboe client position with frames handled by the callback.
|
||||
if (getDirection() == Direction::Input) {
|
||||
mFramesRead += mFramesPerCallback;
|
||||
} else {
|
||||
mFramesWritten += mFramesPerCallback;
|
||||
}
|
||||
} else if (result == DataCallbackResult::Stop) {
|
||||
LOGD("Oboe callback returned Stop");
|
||||
shouldStopStream = true;
|
||||
} else {
|
||||
LOGW("Oboe callback returned unexpected value = %d", static_cast<int>(result));
|
||||
shouldStopStream = true;
|
||||
}
|
||||
if (shouldStopStream) {
|
||||
mCallbackBufferIndex = 0;
|
||||
}
|
||||
return shouldStopStream;
|
||||
}
|
||||
|
||||
// This callback handler is called every time a buffer has been processed by OpenSL ES.
|
||||
static void bqCallbackGlue(SLAndroidSimpleBufferQueueItf bq, void *context) {
|
||||
bool shouldStopStream = (reinterpret_cast<AudioStreamOpenSLES *>(context))
|
||||
->processBufferCallback(bq);
|
||||
if (shouldStopStream) {
|
||||
(reinterpret_cast<AudioStreamOpenSLES *>(context))->requestStop();
|
||||
}
|
||||
}
|
||||
|
||||
SLresult AudioStreamOpenSLES::registerBufferQueueCallback() {
|
||||
// The BufferQueue
|
||||
SLresult result = (*mObjectInterface)->GetInterface(mObjectInterface,
|
||||
EngineOpenSLES::getInstance().getIidAndroidSimpleBufferQueue(),
|
||||
&mSimpleBufferQueueInterface);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("get buffer queue 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;
|
||||
}
|
||||
|
||||
int64_t AudioStreamOpenSLES::getFramesProcessedByServer() {
|
||||
updateServiceFrameCounter();
|
||||
int64_t millis64 = mPositionMillis.get();
|
||||
int64_t framesProcessed = millis64 * getSampleRate() / kMillisPerSecond;
|
||||
return framesProcessed;
|
||||
}
|
||||
|
||||
Result AudioStreamOpenSLES::waitForStateChange(StreamState currentState,
|
||||
StreamState *nextState,
|
||||
int64_t timeoutNanoseconds) {
|
||||
Result oboeResult = Result::ErrorTimeout;
|
||||
int64_t sleepTimeNanos = 20 * kNanosPerMillisecond; // arbitrary
|
||||
int64_t timeLeftNanos = timeoutNanoseconds;
|
||||
|
||||
while (true) {
|
||||
const StreamState state = getState(); // this does not require a lock
|
||||
if (nextState != nullptr) {
|
||||
*nextState = state;
|
||||
}
|
||||
if (currentState != state) { // state changed?
|
||||
oboeResult = Result::OK;
|
||||
break;
|
||||
}
|
||||
|
||||
// Did we timeout or did user ask for non-blocking?
|
||||
if (timeLeftNanos <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sleepTimeNanos > timeLeftNanos){
|
||||
sleepTimeNanos = timeLeftNanos;
|
||||
}
|
||||
AudioClock::sleepForNanos(sleepTimeNanos);
|
||||
timeLeftNanos -= sleepTimeNanos;
|
||||
}
|
||||
|
||||
return oboeResult;
|
||||
}
|
145
externals/oboe/src/opensles/AudioStreamOpenSLES.h
vendored
Normal file
145
externals/oboe/src/opensles/AudioStreamOpenSLES.h
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright 2015 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_AUDIO_STREAM_OPENSL_ES_H_
|
||||
#define OBOE_AUDIO_STREAM_OPENSL_ES_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "oboe/Oboe.h"
|
||||
#include "common/MonotonicCounter.h"
|
||||
#include "opensles/AudioStreamBuffered.h"
|
||||
#include "opensles/EngineOpenSLES.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
constexpr int kBitsPerByte = 8;
|
||||
constexpr int kBufferQueueLengthDefault = 2; // double buffered for callbacks
|
||||
constexpr int kBufferQueueLengthMax = 8; // AudioFlinger won't use more than 8
|
||||
|
||||
/**
|
||||
* 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:
|
||||
|
||||
AudioStreamOpenSLES();
|
||||
explicit AudioStreamOpenSLES(const AudioStreamBuilder &builder);
|
||||
|
||||
virtual ~AudioStreamOpenSLES() = default;
|
||||
|
||||
virtual Result open() override;
|
||||
|
||||
/**
|
||||
* Query the current state, eg. OBOE_STREAM_STATE_PAUSING
|
||||
*
|
||||
* @return state or a negative error.
|
||||
*/
|
||||
StreamState getState() override { return mState.load(); }
|
||||
|
||||
AudioApi getAudioApi() const override {
|
||||
return AudioApi::OpenSLES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process next OpenSL ES buffer.
|
||||
* Called by by OpenSL ES framework.
|
||||
*
|
||||
* This is public, but don't call it directly.
|
||||
*
|
||||
* @return whether the current stream should be stopped.
|
||||
*/
|
||||
bool processBufferCallback(SLAndroidSimpleBufferQueueItf bq);
|
||||
|
||||
Result waitForStateChange(StreamState currentState,
|
||||
StreamState *nextState,
|
||||
int64_t timeoutNanoseconds) override;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Finish setting up the stream. Common for INPUT and OUTPUT.
|
||||
*
|
||||
* @param configItf
|
||||
* @return SL_RESULT_SUCCESS if OK.
|
||||
*/
|
||||
SLresult finishCommonOpen(SLAndroidConfigurationItf configItf);
|
||||
|
||||
// This must be called under mLock.
|
||||
Result close_l();
|
||||
|
||||
SLuint32 channelCountToChannelMaskDefault(int channelCount) const;
|
||||
|
||||
virtual Result onBeforeDestroy() { return Result::OK; }
|
||||
virtual Result onAfterDestroy() { return Result::OK; }
|
||||
|
||||
static SLuint32 getDefaultByteOrder();
|
||||
|
||||
int32_t getBufferDepth(SLAndroidSimpleBufferQueueItf bq);
|
||||
|
||||
int32_t calculateOptimalBufferQueueLength();
|
||||
int32_t estimateNativeFramesPerBurst();
|
||||
|
||||
SLresult enqueueCallbackBuffer(SLAndroidSimpleBufferQueueItf bq);
|
||||
|
||||
SLresult configurePerformanceMode(SLAndroidConfigurationItf configItf);
|
||||
|
||||
PerformanceMode convertPerformanceMode(SLuint32 openslMode) const;
|
||||
SLuint32 convertPerformanceMode(PerformanceMode oboeMode) const;
|
||||
|
||||
void logUnsupportedAttributes();
|
||||
|
||||
/**
|
||||
* Internal use only.
|
||||
* Use this instead of directly setting the internal state variable.
|
||||
*/
|
||||
void setState(StreamState state) {
|
||||
mState.store(state);
|
||||
}
|
||||
|
||||
int64_t getFramesProcessedByServer();
|
||||
|
||||
// OpenSLES stuff
|
||||
SLObjectItf mObjectInterface = nullptr;
|
||||
SLAndroidSimpleBufferQueueItf mSimpleBufferQueueInterface = nullptr;
|
||||
int mBufferQueueLength = 0;
|
||||
|
||||
int32_t mBytesPerCallback = oboe::kUnspecified;
|
||||
MonotonicCounter mPositionMillis; // for tracking OpenSL ES service position
|
||||
|
||||
private:
|
||||
|
||||
constexpr static int kDoubleBufferCount = 2;
|
||||
|
||||
SLresult registerBufferQueueCallback();
|
||||
SLresult updateStreamParameters(SLAndroidConfigurationItf configItf);
|
||||
Result configureBufferSizes(int32_t sampleRate);
|
||||
|
||||
std::unique_ptr<uint8_t[]> mCallbackBuffer[kBufferQueueLengthMax];
|
||||
int mCallbackBufferIndex = 0;
|
||||
std::atomic<StreamState> mState{StreamState::Uninitialized};
|
||||
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif // OBOE_AUDIO_STREAM_OPENSL_ES_H_
|
206
externals/oboe/src/opensles/EngineOpenSLES.cpp
vendored
Normal file
206
externals/oboe/src/opensles/EngineOpenSLES.cpp
vendored
Normal file
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* 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 <dlfcn.h>
|
||||
|
||||
#include "common/OboeDebug.h"
|
||||
#include "EngineOpenSLES.h"
|
||||
#include "OpenSLESUtilities.h"
|
||||
|
||||
using namespace oboe;
|
||||
|
||||
// OpenSL ES is deprecated in SDK 30.
|
||||
// So we use custom dynamic linking to access the library.
|
||||
#define LIB_OPENSLES_NAME "libOpenSLES.so"
|
||||
|
||||
EngineOpenSLES &EngineOpenSLES::getInstance() {
|
||||
static EngineOpenSLES sInstance;
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
// Satisfy extern in OpenSLES.h
|
||||
// These are required because of b/337360630, which was causing
|
||||
// Oboe to have link failures if libOpenSLES.so was not available.
|
||||
// If you are statically linking Oboe and libOpenSLES.so is a shared library
|
||||
// and you observe crashes, you can pass DO_NOT_DEFINE_OPENSL_ES_CONSTANTS to cmake.
|
||||
#ifndef DO_NOT_DEFINE_OPENSL_ES_CONSTANTS
|
||||
SL_API const SLInterfaceID SL_IID_ENGINE = nullptr;
|
||||
SL_API const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE = nullptr;
|
||||
SL_API const SLInterfaceID SL_IID_ANDROIDCONFIGURATION = nullptr;
|
||||
SL_API const SLInterfaceID SL_IID_RECORD = nullptr;
|
||||
SL_API const SLInterfaceID SL_IID_BUFFERQUEUE = nullptr;
|
||||
SL_API const SLInterfaceID SL_IID_VOLUME = nullptr;
|
||||
SL_API const SLInterfaceID SL_IID_PLAY = nullptr;
|
||||
#endif
|
||||
|
||||
static const char *getSafeDlerror() {
|
||||
static const char *defaultMessage = "not found?";
|
||||
char *errorMessage = dlerror();
|
||||
return (errorMessage == nullptr) ? defaultMessage : errorMessage;
|
||||
}
|
||||
|
||||
// Load the OpenSL ES library and the one primary entry point.
|
||||
// @return true if linked OK
|
||||
bool EngineOpenSLES::linkOpenSLES() {
|
||||
if (mDynamicLinkState == kLinkStateBad) {
|
||||
LOGE("%s(), OpenSL ES not available, based on previous link failure.", __func__);
|
||||
} else if (mDynamicLinkState == kLinkStateUninitialized) {
|
||||
// Set to BAD now in case we return because of an error.
|
||||
// This is safe form race conditions because this function is always called
|
||||
// under mLock amd the state is only accessed from this function.
|
||||
mDynamicLinkState = kLinkStateBad;
|
||||
// Use RTLD_NOW to avoid the unpredictable behavior that RTLD_LAZY can cause.
|
||||
// Also resolving all the links now will prevent a run-time penalty later.
|
||||
mLibOpenSlesLibraryHandle = dlopen(LIB_OPENSLES_NAME, RTLD_NOW);
|
||||
if (mLibOpenSlesLibraryHandle == nullptr) {
|
||||
LOGE("%s() could not dlopen(%s), %s", __func__, LIB_OPENSLES_NAME, getSafeDlerror());
|
||||
return false;
|
||||
} else {
|
||||
mFunction_slCreateEngine = (prototype_slCreateEngine) dlsym(
|
||||
mLibOpenSlesLibraryHandle,
|
||||
"slCreateEngine");
|
||||
LOGD("%s(): dlsym(%s) returned %p", __func__,
|
||||
"slCreateEngine", mFunction_slCreateEngine);
|
||||
if (mFunction_slCreateEngine == nullptr) {
|
||||
LOGE("%s(): dlsym(slCreateEngine) returned null, %s", __func__, getSafeDlerror());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load IID interfaces.
|
||||
LOCAL_SL_IID_ENGINE = getIidPointer("SL_IID_ENGINE");
|
||||
if (LOCAL_SL_IID_ENGINE == nullptr) return false;
|
||||
LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE = getIidPointer(
|
||||
"SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
|
||||
if (LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE == nullptr) return false;
|
||||
LOCAL_SL_IID_ANDROIDCONFIGURATION = getIidPointer(
|
||||
"SL_IID_ANDROIDCONFIGURATION");
|
||||
if (LOCAL_SL_IID_ANDROIDCONFIGURATION == nullptr) return false;
|
||||
LOCAL_SL_IID_RECORD = getIidPointer("SL_IID_RECORD");
|
||||
if (LOCAL_SL_IID_RECORD == nullptr) return false;
|
||||
LOCAL_SL_IID_BUFFERQUEUE = getIidPointer("SL_IID_BUFFERQUEUE");
|
||||
if (LOCAL_SL_IID_BUFFERQUEUE == nullptr) return false;
|
||||
LOCAL_SL_IID_VOLUME = getIidPointer("SL_IID_VOLUME");
|
||||
if (LOCAL_SL_IID_VOLUME == nullptr) return false;
|
||||
LOCAL_SL_IID_PLAY = getIidPointer("SL_IID_PLAY");
|
||||
if (LOCAL_SL_IID_PLAY == nullptr) return false;
|
||||
|
||||
mDynamicLinkState = kLinkStateGood;
|
||||
}
|
||||
}
|
||||
return (mDynamicLinkState == kLinkStateGood);
|
||||
}
|
||||
|
||||
// A symbol like SL_IID_PLAY is a pointer to a structure.
|
||||
// The dlsym() function returns the address of the pointer, not the structure.
|
||||
// To get the address of the structure we have to dereference the pointer.
|
||||
SLInterfaceID EngineOpenSLES::getIidPointer(const char *symbolName) {
|
||||
SLInterfaceID *iid_address = (SLInterfaceID *) dlsym(
|
||||
mLibOpenSlesLibraryHandle,
|
||||
symbolName);
|
||||
if (iid_address == nullptr) {
|
||||
LOGE("%s(): dlsym(%s) returned null, %s", __func__, symbolName, getSafeDlerror());
|
||||
return (SLInterfaceID) nullptr;
|
||||
}
|
||||
return *iid_address; // Get address of the structure.
|
||||
}
|
||||
|
||||
SLresult EngineOpenSLES::open() {
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
|
||||
SLresult result = SL_RESULT_SUCCESS;
|
||||
if (mOpenCount++ == 0) {
|
||||
// load the library and link to it
|
||||
if (!linkOpenSLES()) {
|
||||
result = SL_RESULT_FEATURE_UNSUPPORTED;
|
||||
goto error;
|
||||
};
|
||||
|
||||
// create engine
|
||||
result = (*mFunction_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,
|
||||
EngineOpenSLES::getInstance().getIidEngine(),
|
||||
&mEngineInterface);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
LOGE("EngineOpenSLES - GetInterface() engine result:%s", getSLErrStr(result));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
error:
|
||||
close_l();
|
||||
return result;
|
||||
}
|
||||
|
||||
void EngineOpenSLES::close() {
|
||||
std::lock_guard<std::mutex> lock(mLock);
|
||||
close_l();
|
||||
}
|
||||
|
||||
// This must be called under mLock
|
||||
void EngineOpenSLES::close_l() {
|
||||
if (--mOpenCount == 0) {
|
||||
if (mEngineObject != nullptr) {
|
||||
(*mEngineObject)->Destroy(mEngineObject);
|
||||
mEngineObject = nullptr;
|
||||
mEngineInterface = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SLresult EngineOpenSLES::createOutputMix(SLObjectItf *objectItf) {
|
||||
return (*mEngineInterface)->CreateOutputMix(mEngineInterface, objectItf, 0, 0, 0);
|
||||
}
|
||||
|
||||
SLresult EngineOpenSLES::createAudioPlayer(SLObjectItf *objectItf,
|
||||
SLDataSource *audioSource,
|
||||
SLDataSink *audioSink) {
|
||||
|
||||
SLInterfaceID ids[] = {LOCAL_SL_IID_BUFFERQUEUE, LOCAL_SL_IID_ANDROIDCONFIGURATION};
|
||||
SLboolean reqs[] = {SL_BOOLEAN_TRUE, 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) {
|
||||
|
||||
SLInterfaceID ids[] = {LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||
LOCAL_SL_IID_ANDROIDCONFIGURATION };
|
||||
SLboolean reqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
|
||||
return (*mEngineInterface)->CreateAudioRecorder(mEngineInterface, objectItf, audioSource,
|
||||
audioSink,
|
||||
sizeof(ids) / sizeof(ids[0]), ids, reqs);
|
||||
}
|
||||
|
107
externals/oboe/src/opensles/EngineOpenSLES.h
vendored
Normal file
107
externals/oboe/src/opensles/EngineOpenSLES.h
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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 <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
namespace oboe {
|
||||
|
||||
typedef SLresult (*prototype_slCreateEngine)(
|
||||
SLObjectItf *pEngine,
|
||||
SLuint32 numOptions,
|
||||
const SLEngineOption *pEngineOptions,
|
||||
SLuint32 numInterfaces,
|
||||
const SLInterfaceID *pInterfaceIds,
|
||||
const SLboolean *pInterfaceRequired
|
||||
);
|
||||
|
||||
/**
|
||||
* INTERNAL USE ONLY
|
||||
*/
|
||||
class EngineOpenSLES {
|
||||
public:
|
||||
static EngineOpenSLES &getInstance();
|
||||
|
||||
bool linkOpenSLES();
|
||||
|
||||
SLresult open();
|
||||
|
||||
void close();
|
||||
|
||||
SLresult createOutputMix(SLObjectItf *objectItf);
|
||||
|
||||
SLresult createAudioPlayer(SLObjectItf *objectItf,
|
||||
SLDataSource *audioSource,
|
||||
SLDataSink *audioSink);
|
||||
SLresult createAudioRecorder(SLObjectItf *objectItf,
|
||||
SLDataSource *audioSource,
|
||||
SLDataSink *audioSink);
|
||||
|
||||
SLInterfaceID getIidEngine() { return LOCAL_SL_IID_ENGINE; }
|
||||
SLInterfaceID getIidAndroidSimpleBufferQueue() { return LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE; }
|
||||
SLInterfaceID getIidAndroidConfiguration() { return LOCAL_SL_IID_ANDROIDCONFIGURATION; }
|
||||
SLInterfaceID getIidRecord() { return LOCAL_SL_IID_RECORD; }
|
||||
SLInterfaceID getIidBufferQueue() { return LOCAL_SL_IID_BUFFERQUEUE; }
|
||||
SLInterfaceID getIidVolume() { return LOCAL_SL_IID_VOLUME; }
|
||||
SLInterfaceID getIidPlay() { return LOCAL_SL_IID_PLAY; }
|
||||
|
||||
private:
|
||||
// Make this a safe Singleton
|
||||
EngineOpenSLES()= default;
|
||||
~EngineOpenSLES()= default;
|
||||
EngineOpenSLES(const EngineOpenSLES&)= delete;
|
||||
EngineOpenSLES& operator=(const EngineOpenSLES&)= delete;
|
||||
|
||||
SLInterfaceID getIidPointer(const char *symbolName);
|
||||
|
||||
/**
|
||||
* Close the OpenSL ES engine.
|
||||
* This must be called under mLock
|
||||
*/
|
||||
void close_l();
|
||||
|
||||
std::mutex mLock;
|
||||
int32_t mOpenCount = 0;
|
||||
|
||||
static constexpr int32_t kLinkStateUninitialized = 0;
|
||||
static constexpr int32_t kLinkStateGood = 1;
|
||||
static constexpr int32_t kLinkStateBad = 2;
|
||||
int32_t mDynamicLinkState = kLinkStateUninitialized;
|
||||
SLObjectItf mEngineObject = nullptr;
|
||||
SLEngineItf mEngineInterface = nullptr;
|
||||
|
||||
// These symbols are loaded using dlsym().
|
||||
prototype_slCreateEngine mFunction_slCreateEngine = nullptr;
|
||||
void *mLibOpenSlesLibraryHandle = nullptr;
|
||||
SLInterfaceID LOCAL_SL_IID_ENGINE = nullptr;
|
||||
SLInterfaceID LOCAL_SL_IID_ANDROIDSIMPLEBUFFERQUEUE = nullptr;
|
||||
SLInterfaceID LOCAL_SL_IID_ANDROIDCONFIGURATION = nullptr;
|
||||
SLInterfaceID LOCAL_SL_IID_RECORD = nullptr;
|
||||
SLInterfaceID LOCAL_SL_IID_BUFFERQUEUE = nullptr;
|
||||
SLInterfaceID LOCAL_SL_IID_VOLUME = nullptr;
|
||||
SLInterfaceID LOCAL_SL_IID_PLAY = nullptr;
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
|
||||
#endif //OBOE_ENGINE_OPENSLES_H
|
103
externals/oboe/src/opensles/OpenSLESUtilities.cpp
vendored
Normal file
103
externals/oboe/src/opensles/OpenSLESUtilities.cpp
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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 "OpenSLESUtilities.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/*
|
||||
* OSLES Helpers
|
||||
*/
|
||||
|
||||
const char *getSLErrStr(SLresult code) {
|
||||
switch (code) {
|
||||
case SL_RESULT_SUCCESS:
|
||||
return "SL_RESULT_SUCCESS";
|
||||
case SL_RESULT_PRECONDITIONS_VIOLATED:
|
||||
return "SL_RESULT_PRECONDITIONS_VIOLATED";
|
||||
case SL_RESULT_PARAMETER_INVALID:
|
||||
return "SL_RESULT_PARAMETER_INVALID";
|
||||
case SL_RESULT_MEMORY_FAILURE:
|
||||
return "SL_RESULT_MEMORY_FAILURE";
|
||||
case SL_RESULT_RESOURCE_ERROR:
|
||||
return "SL_RESULT_RESOURCE_ERROR";
|
||||
case SL_RESULT_RESOURCE_LOST:
|
||||
return "SL_RESULT_RESOURCE_LOST";
|
||||
case SL_RESULT_IO_ERROR:
|
||||
return "SL_RESULT_IO_ERROR";
|
||||
case SL_RESULT_BUFFER_INSUFFICIENT:
|
||||
return "SL_RESULT_BUFFER_INSUFFICIENT";
|
||||
case SL_RESULT_CONTENT_CORRUPTED:
|
||||
return "SL_RESULT_CONTENT_CORRUPTED";
|
||||
case SL_RESULT_CONTENT_UNSUPPORTED:
|
||||
return "SL_RESULT_CONTENT_UNSUPPORTED";
|
||||
case SL_RESULT_CONTENT_NOT_FOUND:
|
||||
return "SL_RESULT_CONTENT_NOT_FOUND";
|
||||
case SL_RESULT_PERMISSION_DENIED:
|
||||
return "SL_RESULT_PERMISSION_DENIED";
|
||||
case SL_RESULT_FEATURE_UNSUPPORTED:
|
||||
return "SL_RESULT_FEATURE_UNSUPPORTED";
|
||||
case SL_RESULT_INTERNAL_ERROR:
|
||||
return "SL_RESULT_INTERNAL_ERROR";
|
||||
case SL_RESULT_UNKNOWN_ERROR:
|
||||
return "SL_RESULT_UNKNOWN_ERROR";
|
||||
case SL_RESULT_OPERATION_ABORTED:
|
||||
return "SL_RESULT_OPERATION_ABORTED";
|
||||
case SL_RESULT_CONTROL_LOST:
|
||||
return "SL_RESULT_CONTROL_LOST";
|
||||
default:
|
||||
return "Unknown SL error";
|
||||
}
|
||||
}
|
||||
|
||||
SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat(
|
||||
SLDataFormat_PCM format, SLuint32 representation) {
|
||||
SLAndroidDataFormat_PCM_EX format_pcm_ex;
|
||||
format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
|
||||
format_pcm_ex.numChannels = format.numChannels;
|
||||
format_pcm_ex.sampleRate = format.samplesPerSec;
|
||||
format_pcm_ex.bitsPerSample = format.bitsPerSample;
|
||||
format_pcm_ex.containerSize = format.containerSize;
|
||||
format_pcm_ex.channelMask = format.channelMask;
|
||||
format_pcm_ex.endianness = format.endianness;
|
||||
format_pcm_ex.representation = representation;
|
||||
return format_pcm_ex;
|
||||
}
|
||||
|
||||
SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format) {
|
||||
switch(format) {
|
||||
case AudioFormat::I16:
|
||||
return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
|
||||
case AudioFormat::Float:
|
||||
return SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
||||
case AudioFormat::I24:
|
||||
case AudioFormat::I32:
|
||||
case AudioFormat::IEC61937:
|
||||
case AudioFormat::Invalid:
|
||||
case AudioFormat::Unspecified:
|
||||
case AudioFormat::MP3:
|
||||
case AudioFormat::AAC_LC:
|
||||
case AudioFormat::AAC_HE_V1:
|
||||
case AudioFormat::AAC_HE_V2:
|
||||
case AudioFormat::AAC_ELD:
|
||||
case AudioFormat::AAC_XHE:
|
||||
case AudioFormat::OPUS:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace oboe
|
44
externals/oboe/src/opensles/OpenSLESUtilities.h
vendored
Normal file
44
externals/oboe/src/opensles/OpenSLESUtilities.h
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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_OPENSLES_OPENSLESUTILITIES_H
|
||||
#define OBOE_OPENSLES_OPENSLESUTILITIES_H
|
||||
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#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
|
||||
* SLAndroidDataFormat_PCM_EX object which allows audio samples to be represented using
|
||||
* single precision floating-point.
|
||||
*
|
||||
* @param format
|
||||
* @param representation
|
||||
* @return the extended PCM format
|
||||
*/
|
||||
SLAndroidDataFormat_PCM_EX OpenSLES_createExtendedFormat(SLDataFormat_PCM format,
|
||||
SLuint32 representation);
|
||||
|
||||
SLuint32 OpenSLES_ConvertFormatToRepresentation(AudioFormat format);
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif //OBOE_OPENSLES_OPENSLESUTILITIES_H
|
74
externals/oboe/src/opensles/OutputMixerOpenSLES.cpp
vendored
Normal file
74
externals/oboe/src/opensles/OutputMixerOpenSLES.cpp
vendored
Normal file
|
@ -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<std::mutex> 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<std::mutex> lock(mLock);
|
||||
|
||||
if (--mOpenCount == 0) {
|
||||
// destroy output mix object, and invalidate all associated interfaces
|
||||
if (mOutputMixObject != nullptr) {
|
||||
(*mOutputMixObject)->Destroy(mOutputMixObject);
|
||||
mOutputMixObject = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
57
externals/oboe/src/opensles/OutputMixerOpenSLES.h
vendored
Normal file
57
externals/oboe/src/opensles/OutputMixerOpenSLES.h
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include "EngineOpenSLES.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/**
|
||||
* INTERNAL USE ONLY
|
||||
*/
|
||||
|
||||
class OutputMixerOpenSL {
|
||||
public:
|
||||
static OutputMixerOpenSL &getInstance();
|
||||
|
||||
SLresult open();
|
||||
|
||||
void close();
|
||||
|
||||
SLresult createAudioPlayer(SLObjectItf *objectItf,
|
||||
SLDataSource *audioSource);
|
||||
|
||||
private:
|
||||
// Make this a safe Singleton
|
||||
OutputMixerOpenSL()= default;
|
||||
~OutputMixerOpenSL()= default;
|
||||
OutputMixerOpenSL(const OutputMixerOpenSL&)= delete;
|
||||
OutputMixerOpenSL& operator=(const OutputMixerOpenSL&)= delete;
|
||||
|
||||
std::mutex mLock;
|
||||
int32_t mOpenCount = 0;
|
||||
|
||||
SLObjectItf mOutputMixObject = nullptr;
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif //OBOE_OUTPUT_MIXER_OPENSLES_H
|
Loading…
Add table
Add a link
Reference in a new issue