Android: Get rid of OpenSLESStream's global state

Not sure if we're ever going to want to have more than one of these at
the same time, but these global variables are a code smell nonetheless.

I'm also deleting the existing member variables because they were
unused.
This commit is contained in:
JosJuice 2024-06-06 13:32:26 +02:00
parent ca22d0af57
commit 7d80b009d8
2 changed files with 70 additions and 63 deletions

View File

@ -14,34 +14,19 @@
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
// engine interfaces void OpenSLESStream::BQPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
static SLObjectItf engineObject;
static SLEngineItf engineEngine;
static SLObjectItf outputMixObject;
// buffer queue player interfaces
static SLObjectItf bqPlayerObject = nullptr;
static SLPlayItf bqPlayerPlay;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
static SLVolumeItf bqPlayerVolume;
static Mixer* g_mixer;
#define BUFFER_SIZE 512
#define BUFFER_SIZE_IN_SAMPLES (BUFFER_SIZE / 2)
// Double buffering.
static short buffer[2][BUFFER_SIZE];
static int curBuffer = 0;
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{ {
ASSERT(bq == bqPlayerBufferQueue); reinterpret_cast<OpenSLESStream*>(context)->PushSamples(bq);
ASSERT(nullptr == context); }
void OpenSLESStream::PushSamples(SLAndroidSimpleBufferQueueItf bq)
{
ASSERT(bq == m_bq_player_buffer_queue);
// Render to the fresh buffer // Render to the fresh buffer
g_mixer->Mix(reinterpret_cast<short*>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES); m_mixer->Mix(reinterpret_cast<short*>(m_buffer[m_current_buffer]), BUFFER_SIZE_IN_SAMPLES);
SLresult result = SLresult result = (*bq)->Enqueue(bq, m_buffer[m_current_buffer], sizeof(m_buffer[0]));
(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[0])); m_current_buffer ^= 1; // Switch buffer
curBuffer ^= 1; // Switch buffer
// Comment from sample code: // Comment from sample code:
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT, // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
@ -53,15 +38,15 @@ bool OpenSLESStream::Init()
{ {
SLresult result; SLresult result;
// create engine // create engine
result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr); result = slCreateEngine(&m_engine_object, 0, nullptr, 0, nullptr, nullptr);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); result = (*m_engine_object)->Realize(m_engine_object, SL_BOOLEAN_FALSE);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); result = (*m_engine_object)->GetInterface(m_engine_object, SL_IID_ENGINE, &m_engine_engine);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0); result = (*m_engine_engine)->CreateOutputMix(m_engine_engine, &m_output_mix_object, 0, 0, 0);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); result = (*m_output_mix_object)->Realize(m_output_mix_object, SL_BOOLEAN_FALSE);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
@ -76,36 +61,38 @@ bool OpenSLESStream::Init()
SLDataSource audioSrc = {&loc_bufq, &format_pcm}; SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// configure audio sink // configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, m_output_mix_object};
SLDataSink audioSnk = {&loc_outmix, nullptr}; SLDataSink audioSnk = {&loc_outmix, nullptr};
// create audio player // create audio player
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME}; const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = result = (*m_engine_engine)
(*engineEngine) ->CreateAudioPlayer(m_engine_engine, &m_bq_player_object, &audioSrc, &audioSnk, 2,
->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req); ids, req);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); result = (*m_bq_player_object)->Realize(m_bq_player_object, SL_BOOLEAN_FALSE);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); result = (*m_bq_player_object)->GetInterface(m_bq_player_object, SL_IID_PLAY, &m_bq_player_play);
ASSERT(SL_RESULT_SUCCESS == result);
result = (*m_bq_player_object)
->GetInterface(m_bq_player_object, SL_IID_BUFFERQUEUE, &m_bq_player_buffer_queue);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
result = result =
(*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue); (*m_bq_player_object)->GetInterface(m_bq_player_object, SL_IID_VOLUME, &m_bq_player_volume);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); result = (*m_bq_player_buffer_queue)
->RegisterCallback(m_bq_player_buffer_queue, BQPlayerCallback, this);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr); result = (*m_bq_player_play)->SetPlayState(m_bq_player_play, SL_PLAYSTATE_PLAYING);
ASSERT(SL_RESULT_SUCCESS == result);
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
ASSERT(SL_RESULT_SUCCESS == result); ASSERT(SL_RESULT_SUCCESS == result);
// Render and enqueue a first buffer. // Render and enqueue a first buffer.
curBuffer ^= 1; m_current_buffer ^= 1;
g_mixer = m_mixer.get();
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[0], sizeof(buffer[0])); result = (*m_bq_player_buffer_queue)
->Enqueue(m_bq_player_buffer_queue, m_buffer[0], sizeof(m_buffer[0]));
if (SL_RESULT_SUCCESS != result) if (SL_RESULT_SUCCESS != result)
return false; return false;
@ -114,39 +101,39 @@ bool OpenSLESStream::Init()
OpenSLESStream::~OpenSLESStream() OpenSLESStream::~OpenSLESStream()
{ {
if (bqPlayerObject != nullptr) if (m_bq_player_object != nullptr)
{ {
(*bqPlayerObject)->Destroy(bqPlayerObject); (*m_bq_player_object)->Destroy(m_bq_player_object);
bqPlayerObject = nullptr; m_bq_player_object = nullptr;
bqPlayerPlay = nullptr; m_bq_player_play = nullptr;
bqPlayerBufferQueue = nullptr; m_bq_player_buffer_queue = nullptr;
bqPlayerVolume = nullptr; m_bq_player_volume = nullptr;
} }
if (outputMixObject != nullptr) if (m_output_mix_object != nullptr)
{ {
(*outputMixObject)->Destroy(outputMixObject); (*m_output_mix_object)->Destroy(m_output_mix_object);
outputMixObject = nullptr; m_output_mix_object = nullptr;
} }
if (engineObject != nullptr) if (m_engine_object != nullptr)
{ {
(*engineObject)->Destroy(engineObject); (*m_engine_object)->Destroy(m_engine_object);
engineObject = nullptr; m_engine_object = nullptr;
engineEngine = nullptr; m_engine_engine = nullptr;
} }
} }
bool OpenSLESStream::SetRunning(bool running) bool OpenSLESStream::SetRunning(bool running)
{ {
SLuint32 new_state = running ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_PAUSED; SLuint32 new_state = running ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_PAUSED;
return (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, new_state) == SL_RESULT_SUCCESS; return (*m_bq_player_play)->SetPlayState(m_bq_player_play, new_state) == SL_RESULT_SUCCESS;
} }
void OpenSLESStream::SetVolume(int volume) void OpenSLESStream::SetVolume(int volume)
{ {
const SLmillibel attenuation = const SLmillibel attenuation =
volume <= 0 ? SL_MILLIBEL_MIN : static_cast<SLmillibel>(2000 * std::log10(volume / 100.0f)); volume <= 0 ? SL_MILLIBEL_MIN : static_cast<SLmillibel>(2000 * std::log10(volume / 100.0f));
(*bqPlayerVolume)->SetVolumeLevel(bqPlayerVolume, attenuation); (*m_bq_player_volume)->SetVolumeLevel(m_bq_player_volume, attenuation);
} }
#endif // HAVE_OPENSL_ES #endif // HAVE_OPENSL_ES

View File

@ -3,10 +3,12 @@
#pragma once #pragma once
#include <thread> #ifdef HAVE_OPENSL_ES
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#endif // HAVE_OPENSL_ES
#include "AudioCommon/SoundStream.h" #include "AudioCommon/SoundStream.h"
#include "Common/Event.h"
class OpenSLESStream final : public SoundStream class OpenSLESStream final : public SoundStream
{ {
@ -19,7 +21,25 @@ public:
static bool IsValid() { return true; } static bool IsValid() { return true; }
private: private:
std::thread thread; static void BQPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* context);
Common::Event soundSyncEvent; void PushSamples(SLAndroidSimpleBufferQueueItf bq);
// engine interfaces
SLObjectItf m_engine_object;
SLEngineItf m_engine_engine;
SLObjectItf m_output_mix_object;
// buffer queue player interfaces
SLObjectItf m_bq_player_object = nullptr;
SLPlayItf m_bq_player_play;
SLAndroidSimpleBufferQueueItf m_bq_player_buffer_queue;
SLVolumeItf m_bq_player_volume;
static constexpr int BUFFER_SIZE = 512;
static constexpr int BUFFER_SIZE_IN_SAMPLES = BUFFER_SIZE / 2;
// Double buffering.
short m_buffer[2][BUFFER_SIZE];
int m_current_buffer = 0;
#endif // HAVE_OPENSL_ES #endif // HAVE_OPENSL_ES
}; };