summaryrefslogtreecommitdiff
path: root/Source/Core/AudioCommon/OpenSLESStream.cpp
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2025-06-07 23:07:37 -0400
committerGitHub <noreply@github.com>2025-06-07 23:07:37 -0400
commit24b0bf01d54cf15977e7e2f4d2b529bb8230b060 (patch)
treebfbe0b3fb7fc0c0d31dbbc66621b9ea7f1ef654a /Source/Core/AudioCommon/OpenSLESStream.cpp
parent19fbbf0dba32ad98fe791804cfd464ced91464f5 (diff)
parentf99d3dbd5ccd80bcd071cf49e689f6b6724df499 (diff)
Merge pull request #12836 from JosJuice/opensles-buffer-size
Android: Ask system for optimal audio buffer size and sample rate
Diffstat (limited to 'Source/Core/AudioCommon/OpenSLESStream.cpp')
-rw-r--r--Source/Core/AudioCommon/OpenSLESStream.cpp136
1 files changed, 70 insertions, 66 deletions
diff --git a/Source/Core/AudioCommon/OpenSLESStream.cpp b/Source/Core/AudioCommon/OpenSLESStream.cpp
index a3f6308187..3fc71e75df 100644
--- a/Source/Core/AudioCommon/OpenSLESStream.cpp
+++ b/Source/Core/AudioCommon/OpenSLESStream.cpp
@@ -8,40 +8,27 @@
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
+#include <jni.h>
#include "Common/Assert.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Core/ConfigManager.h"
+#include "jni/AndroidCommon/IDCache.h"
-// engine interfaces
-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)
+void OpenSLESStream::BQPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{
- ASSERT(bq == bqPlayerBufferQueue);
- ASSERT(nullptr == context);
+ reinterpret_cast<OpenSLESStream*>(context)->PushSamples(bq);
+}
+
+void OpenSLESStream::PushSamples(SLAndroidSimpleBufferQueueItf bq)
+{
+ ASSERT(bq == m_bq_player_buffer_queue);
// Render to the fresh buffer
- g_mixer->Mix(reinterpret_cast<short*>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES);
- SLresult result =
- (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[0]));
- curBuffer ^= 1; // Switch buffer
+ m_mixer->Mix(m_buffer[m_current_buffer].data(), m_frames_per_buffer);
+ SLresult result = (*bq)->Enqueue(bq, m_buffer[m_current_buffer].data(), m_bytes_per_buffer);
+ m_current_buffer ^= 1; // Switch buffer
// Comment from sample code:
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
@@ -51,61 +38,78 @@ static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
bool OpenSLESStream::Init()
{
+ JNIEnv* env = IDCache::GetEnvForThread();
+ jclass audio_utils = IDCache::GetAudioUtilsClass();
+ const SLuint32 sample_rate =
+ env->CallStaticIntMethod(audio_utils, IDCache::GetAudioUtilsGetSampleRate());
+ m_frames_per_buffer =
+ env->CallStaticIntMethod(audio_utils, IDCache::GetAudioUtilsGetFramesPerBuffer());
+
+ INFO_LOG_FMT(AUDIO, "OpenSLES configuration: {} Hz, {} frames per buffer", sample_rate,
+ m_frames_per_buffer);
+
+ constexpr SLuint32 channels = 2;
+ const SLuint32 samples_per_buffer = m_frames_per_buffer * channels;
+ m_bytes_per_buffer = m_frames_per_buffer * channels * sizeof(m_buffer[0][0]);
+
+ for (std::vector<short>& buffer : m_buffer)
+ buffer.resize(samples_per_buffer);
+
SLresult result;
// 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);
- result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
+ result = (*m_engine_object)->Realize(m_engine_object, SL_BOOLEAN_FALSE);
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);
- 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);
- 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);
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
- SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,
- 2,
- m_mixer->GetSampleRate() * 1000,
- SL_PCMSAMPLEFORMAT_FIXED_16,
- SL_PCMSAMPLEFORMAT_FIXED_16,
- SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
- SL_BYTEORDER_LITTLEENDIAN};
+ SLDataFormat_PCM format_pcm = {
+ SL_DATAFORMAT_PCM, channels,
+ sample_rate * 1000, SL_PCMSAMPLEFORMAT_FIXED_16,
+ SL_PCMSAMPLEFORMAT_FIXED_16, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
+ SL_BYTEORDER_LITTLEENDIAN};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// 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};
// create audio player
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
- result =
- (*engineEngine)
- ->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
+ result = (*m_engine_engine)
+ ->CreateAudioPlayer(m_engine_engine, &m_bq_player_object, &audioSrc, &audioSnk, 2,
+ ids, req);
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);
- 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 =
- (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
+ result = (*m_bq_player_object)
+ ->GetInterface(m_bq_player_object, SL_IID_BUFFERQUEUE, &m_bq_player_buffer_queue);
ASSERT(SL_RESULT_SUCCESS == result);
- result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
+ result =
+ (*m_bq_player_object)->GetInterface(m_bq_player_object, SL_IID_VOLUME, &m_bq_player_volume);
ASSERT(SL_RESULT_SUCCESS == result);
- result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
+ result = (*m_bq_player_buffer_queue)
+ ->RegisterCallback(m_bq_player_buffer_queue, BQPlayerCallback, this);
ASSERT(SL_RESULT_SUCCESS == result);
- result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
+ result = (*m_bq_player_play)->SetPlayState(m_bq_player_play, SL_PLAYSTATE_PLAYING);
ASSERT(SL_RESULT_SUCCESS == result);
// Render and enqueue a first buffer.
- curBuffer ^= 1;
- g_mixer = m_mixer.get();
+ m_current_buffer ^= 1;
- result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[0], sizeof(buffer[0]));
+ result = (*m_bq_player_buffer_queue)
+ ->Enqueue(m_bq_player_buffer_queue, m_buffer[0].data(), m_bytes_per_buffer);
if (SL_RESULT_SUCCESS != result)
return false;
@@ -114,39 +118,39 @@ bool OpenSLESStream::Init()
OpenSLESStream::~OpenSLESStream()
{
- if (bqPlayerObject != nullptr)
+ if (m_bq_player_object != nullptr)
{
- (*bqPlayerObject)->Destroy(bqPlayerObject);
- bqPlayerObject = nullptr;
- bqPlayerPlay = nullptr;
- bqPlayerBufferQueue = nullptr;
- bqPlayerVolume = nullptr;
+ (*m_bq_player_object)->Destroy(m_bq_player_object);
+ m_bq_player_object = nullptr;
+ m_bq_player_play = nullptr;
+ m_bq_player_buffer_queue = nullptr;
+ m_bq_player_volume = nullptr;
}
- if (outputMixObject != nullptr)
+ if (m_output_mix_object != nullptr)
{
- (*outputMixObject)->Destroy(outputMixObject);
- outputMixObject = nullptr;
+ (*m_output_mix_object)->Destroy(m_output_mix_object);
+ m_output_mix_object = nullptr;
}
- if (engineObject != nullptr)
+ if (m_engine_object != nullptr)
{
- (*engineObject)->Destroy(engineObject);
- engineObject = nullptr;
- engineEngine = nullptr;
+ (*m_engine_object)->Destroy(m_engine_object);
+ m_engine_object = nullptr;
+ m_engine_engine = nullptr;
}
}
bool OpenSLESStream::SetRunning(bool running)
{
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)
{
const SLmillibel attenuation =
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