2013-04-21 00:17:03 -06:00
|
|
|
// Copyright 2013 Dolphin Emulator Project
|
2021-07-04 19:22:19 -06:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2013-02-26 12:49:00 -07:00
|
|
|
|
|
|
|
#ifdef ANDROID
|
2019-11-24 13:34:50 -07:00
|
|
|
#include "AudioCommon/OpenSLESStream.h"
|
|
|
|
|
|
|
|
#include <cmath>
|
2013-02-26 12:49:00 -07:00
|
|
|
|
|
|
|
#include <SLES/OpenSLES.h>
|
|
|
|
#include <SLES/OpenSLES_Android.h>
|
|
|
|
|
2015-09-26 15:13:07 -06:00
|
|
|
#include "Common/Assert.h"
|
2014-09-07 19:06:58 -06:00
|
|
|
#include "Common/CommonTypes.h"
|
2015-09-26 15:13:07 -06:00
|
|
|
#include "Common/Logging/Log.h"
|
2019-11-24 13:34:50 -07:00
|
|
|
#include "Core/ConfigManager.h"
|
2014-02-17 03:18:15 -07:00
|
|
|
|
2013-02-26 12:49:00 -07:00
|
|
|
// engine interfaces
|
|
|
|
static SLObjectItf engineObject;
|
|
|
|
static SLEngineItf engineEngine;
|
|
|
|
static SLObjectItf outputMixObject;
|
|
|
|
|
|
|
|
// buffer queue player interfaces
|
2014-03-09 14:14:26 -06:00
|
|
|
static SLObjectItf bqPlayerObject = nullptr;
|
2013-02-26 12:49:00 -07:00
|
|
|
static SLPlayItf bqPlayerPlay;
|
|
|
|
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
|
|
|
|
static SLVolumeItf bqPlayerVolume;
|
2017-06-26 15:41:12 -06:00
|
|
|
static Mixer* g_mixer;
|
2013-02-26 12:49:00 -07:00
|
|
|
#define BUFFER_SIZE 512
|
|
|
|
#define BUFFER_SIZE_IN_SAMPLES (BUFFER_SIZE / 2)
|
|
|
|
|
|
|
|
// Double buffering.
|
|
|
|
static short buffer[2][BUFFER_SIZE];
|
|
|
|
static int curBuffer = 0;
|
|
|
|
|
2014-08-30 14:29:15 -06:00
|
|
|
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
|
|
|
|
{
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(bq == bqPlayerBufferQueue);
|
|
|
|
ASSERT(nullptr == context);
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2016-01-03 10:29:55 -07:00
|
|
|
// 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
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2013-02-26 12:49:00 -07:00
|
|
|
// Comment from sample code:
|
|
|
|
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
|
|
|
|
// which for this code example would indicate a programming error
|
2018-03-14 18:34:35 -06:00
|
|
|
ASSERT_MSG(AUDIO, SL_RESULT_SUCCESS == result, "Couldn't enqueue audio stream.");
|
2013-02-26 12:49:00 -07:00
|
|
|
}
|
2014-08-30 14:29:15 -06:00
|
|
|
|
2017-10-21 17:23:40 -06:00
|
|
|
bool OpenSLESStream::Init()
|
2013-02-26 12:49:00 -07:00
|
|
|
{
|
|
|
|
SLresult result;
|
|
|
|
// create engine
|
2014-03-09 14:14:26 -06:00
|
|
|
result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2013-02-26 12:49:00 -07:00
|
|
|
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2013-02-26 12:49:00 -07:00
|
|
|
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2013-02-26 12:49:00 -07:00
|
|
|
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2013-02-26 12:49:00 -07:00
|
|
|
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2013-02-26 12:49:00 -07:00
|
|
|
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
|
|
|
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,
|
|
|
|
2,
|
2014-10-23 10:29:49 -06:00
|
|
|
m_mixer->GetSampleRate() * 1000,
|
2013-02-26 12:49:00 -07:00
|
|
|
SL_PCMSAMPLEFORMAT_FIXED_16,
|
|
|
|
SL_PCMSAMPLEFORMAT_FIXED_16,
|
|
|
|
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
|
|
|
|
SL_BYTEORDER_LITTLEENDIAN};
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2013-02-26 12:49:00 -07:00
|
|
|
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2013-02-26 12:49:00 -07:00
|
|
|
// configure audio sink
|
|
|
|
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
|
2014-03-09 14:14:26 -06:00
|
|
|
SLDataSink audioSnk = {&loc_outmix, nullptr};
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2013-02-26 12:49:00 -07:00
|
|
|
// 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);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2013-02-26 12:49:00 -07:00
|
|
|
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2013-02-26 12:49:00 -07:00
|
|
|
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2013-02-26 12:49:00 -07:00
|
|
|
result =
|
|
|
|
(*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2019-11-24 13:34:50 -07:00
|
|
|
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2014-03-09 14:14:26 -06:00
|
|
|
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2013-02-26 12:49:00 -07:00
|
|
|
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
2021-04-01 15:30:01 -06:00
|
|
|
ASSERT(SL_RESULT_SUCCESS == result);
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2014-10-18 07:32:14 -06:00
|
|
|
// Render and enqueue a first buffer.
|
|
|
|
curBuffer ^= 1;
|
2015-05-24 02:13:02 -06:00
|
|
|
g_mixer = m_mixer.get();
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2014-10-18 07:32:14 -06:00
|
|
|
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[0], sizeof(buffer[0]));
|
2014-08-30 14:29:15 -06:00
|
|
|
if (SL_RESULT_SUCCESS != result)
|
2013-02-26 12:49:00 -07:00
|
|
|
return false;
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2013-02-26 12:49:00 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-21 17:23:40 -06:00
|
|
|
OpenSLESStream::~OpenSLESStream()
|
2013-02-26 12:49:00 -07:00
|
|
|
{
|
2014-08-30 14:29:15 -06:00
|
|
|
if (bqPlayerObject != nullptr)
|
|
|
|
{
|
2013-02-26 12:49:00 -07:00
|
|
|
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
2014-03-09 14:14:26 -06:00
|
|
|
bqPlayerObject = nullptr;
|
|
|
|
bqPlayerPlay = nullptr;
|
|
|
|
bqPlayerBufferQueue = nullptr;
|
|
|
|
bqPlayerVolume = nullptr;
|
2013-02-26 12:49:00 -07:00
|
|
|
}
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2014-08-30 14:29:15 -06:00
|
|
|
if (outputMixObject != nullptr)
|
|
|
|
{
|
2013-02-26 12:49:00 -07:00
|
|
|
(*outputMixObject)->Destroy(outputMixObject);
|
2014-03-09 14:14:26 -06:00
|
|
|
outputMixObject = nullptr;
|
2013-02-26 12:49:00 -07:00
|
|
|
}
|
2016-06-24 02:43:46 -06:00
|
|
|
|
2014-08-30 14:29:15 -06:00
|
|
|
if (engineObject != nullptr)
|
|
|
|
{
|
2013-02-26 12:49:00 -07:00
|
|
|
(*engineObject)->Destroy(engineObject);
|
2014-03-09 14:14:26 -06:00
|
|
|
engineObject = nullptr;
|
|
|
|
engineEngine = nullptr;
|
2013-02-26 12:49:00 -07:00
|
|
|
}
|
|
|
|
}
|
2019-11-24 13:34:50 -07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2013-02-26 12:49:00 -07:00
|
|
|
#endif
|