mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-27 04:08:37 +01:00
Some renaming and cleanup in the Android audio code
This commit is contained in:
1 parent
b6f6e954a3
commit
aebbff55f1
10 files changed
+84
-77
No files matched your search
@@ -0,0 +1,191 @@
|
||||
// Minimal audio streaming using OpenSL.
|
||||
//
|
||||
// Loosely based on the Android NDK sample code.
|
||||
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
|
||||
// for native audio
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "Common/Log.h"
|
||||
#include "OpenSLContext.h"
|
||||
|
||||
// This callback handler is called every time a buffer finishes playing.
|
||||
// The documentation available is very unclear about how to best manage buffers.
|
||||
// I've chosen to this approach: Instantly enqueue a buffer that was rendered to the last time,
|
||||
// and then render the next. Hopefully it's okay to spend time in this callback after having enqueued.
|
||||
void OpenSLContext::bqPlayerCallbackWrap(SLAndroidSimpleBufferQueueItf bq, void *context) {
|
||||
OpenSLContext *ctx = (OpenSLContext *)context;
|
||||
ctx->BqPlayerCallback(bq);
|
||||
}
|
||||
|
||||
void OpenSLContext::BqPlayerCallback(SLAndroidSimpleBufferQueueItf bq) {
|
||||
if (bq != bqPlayerBufferQueue) {
|
||||
ELOG("OpenSL: Wrong bq!");
|
||||
return;
|
||||
}
|
||||
|
||||
int renderedFrames = audioCallback(buffer[curBuffer], framesPerBuffer);
|
||||
|
||||
int sizeInBytes = framesPerBuffer * 2 * sizeof(short);
|
||||
int byteCount = (framesPerBuffer - renderedFrames) * 4;
|
||||
if (byteCount > 0) {
|
||||
memset(buffer[curBuffer] + renderedFrames * 2, 0, byteCount);
|
||||
}
|
||||
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeInBytes);
|
||||
|
||||
// Comment from sample code:
|
||||
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
|
||||
// which for this code example would indicate a programming error
|
||||
if (result != SL_RESULT_SUCCESS) {
|
||||
ELOG("OpenSL: Failed to enqueue! %i %i", renderedFrames, sizeInBytes);
|
||||
}
|
||||
|
||||
curBuffer += 1; // Switch buffer
|
||||
if (curBuffer == NUM_BUFFERS)
|
||||
curBuffer = 0;
|
||||
}
|
||||
|
||||
// create the engine and output mix objects
|
||||
OpenSLContext::OpenSLContext(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate)
|
||||
: AudioContext(cb, _FramesPerBuffer, _SampleRate) {}
|
||||
|
||||
bool OpenSLContext::Init() {
|
||||
SLresult result;
|
||||
// create engine
|
||||
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
|
||||
if (result != SL_RESULT_SUCCESS) {
|
||||
ELOG("OpenSL: Failed to create the engine: %d", (int)result);
|
||||
engineObject = nullptr;
|
||||
return false;
|
||||
}
|
||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
|
||||
_assert_(SL_RESULT_SUCCESS == result);
|
||||
|
||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
|
||||
_assert_(SL_RESULT_SUCCESS == result);
|
||||
|
||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
|
||||
if (result != SL_RESULT_SUCCESS) {
|
||||
ELOG("OpenSL: Failed to create output mix: %d", (int)result);
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineEngine = nullptr;
|
||||
engineObject = nullptr;
|
||||
return false;
|
||||
}
|
||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
|
||||
_assert_(SL_RESULT_SUCCESS == result);
|
||||
|
||||
// The constants, such as SL_SAMPLINGRATE_44_1, are just 44100000.
|
||||
SLuint32 sr = (SLuint32)sampleRate * 1000;
|
||||
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS};
|
||||
SLDataFormat_PCM format_pcm = {
|
||||
SL_DATAFORMAT_PCM,
|
||||
2,
|
||||
sr,
|
||||
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};
|
||||
SLDataSink audioSnk = {&loc_outmix, NULL};
|
||||
|
||||
// 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);
|
||||
if (result != SL_RESULT_SUCCESS) {
|
||||
ELOG("OpenSL: CreateAudioPlayer failed: %d", (int)result);
|
||||
(*outputMixObject)->Destroy(outputMixObject);
|
||||
outputMixObject = nullptr;
|
||||
|
||||
// Should really tear everything down here. Sigh.
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineEngine = nullptr;
|
||||
engineObject = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
|
||||
_assert_(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
|
||||
_assert_(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
|
||||
&bqPlayerBufferQueue);
|
||||
_assert_(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, &bqPlayerCallbackWrap, this);
|
||||
_assert_(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
|
||||
_assert_(SL_RESULT_SUCCESS == result);
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
|
||||
_assert_(SL_RESULT_SUCCESS == result);
|
||||
|
||||
// Allocate and enqueue N empty buffers.
|
||||
for (int i = 0; i < NUM_BUFFERS; i++) {
|
||||
buffer[i] = new short[framesPerBuffer * 2]{};
|
||||
}
|
||||
|
||||
int sizeInBytes = framesPerBuffer * 2 * sizeof(short);
|
||||
for (int i = 0; i < NUM_BUFFERS; i++) {
|
||||
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[i], sizeInBytes);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
curBuffer = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// shut down the native audio system
|
||||
OpenSLContext::~OpenSLContext() {
|
||||
if (bqPlayerPlay) {
|
||||
ILOG("OpenSL: Shutdown - stopping playback");
|
||||
SLresult result;
|
||||
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
|
||||
if (SL_RESULT_SUCCESS != result) {
|
||||
ELOG("SetPlayState failed");
|
||||
}
|
||||
}
|
||||
|
||||
ILOG("OpenSL: Shutdown - deleting player object");
|
||||
|
||||
if (bqPlayerObject) {
|
||||
(*bqPlayerObject)->Destroy(bqPlayerObject);
|
||||
bqPlayerObject = nullptr;
|
||||
bqPlayerPlay = nullptr;
|
||||
bqPlayerBufferQueue = nullptr;
|
||||
bqPlayerVolume = nullptr;
|
||||
}
|
||||
|
||||
ILOG("OpenSL: Shutdown - deleting mix object");
|
||||
|
||||
if (outputMixObject) {
|
||||
(*outputMixObject)->Destroy(outputMixObject);
|
||||
outputMixObject = nullptr;
|
||||
}
|
||||
|
||||
ILOG("OpenSL: Shutdown - deleting engine object");
|
||||
|
||||
if (engineObject) {
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
engineObject = nullptr;
|
||||
engineEngine = nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NUM_BUFFERS; i++) {
|
||||
delete[] buffer[i];
|
||||
buffer[i] = nullptr;
|
||||
}
|
||||
ILOG("OpenSL: Shutdown - finished");
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user