Remove asserts from OpenSL init. Expose error inside audio settings

This commit is contained in:
Henrik Rydgård 2021-06-15 00:23:45 +02:00
parent 8ae3f5eb41
commit 90cb284837
6 changed files with 111 additions and 54 deletions

View File

@ -80,6 +80,14 @@
#include "Windows/W32Util/ShellUtil.h"
#endif
#if PPSSPP_PLATFORM(ANDROID)
#include "android/jni/AndroidAudio.h"
extern AndroidAudioState *g_audioState;
#endif
GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, bool editThenRestore)
: UIDialogScreenWithGameBackground(gamePath), gameID_(gameID), enableReports_(false), editThenRestore_(editThenRestore) {
lastVertical_ = UseVerticalLayout();
@ -640,6 +648,12 @@ void GameSettingsScreen::CreateViews() {
#if defined(__ANDROID__)
CheckBox *extraAudio = audioSettings->Add(new CheckBox(&g_Config.bExtraAudioBuffering, a->T("AudioBufferingForBluetooth", "Bluetooth-friendly buffer (slower)")));
extraAudio->SetEnabledPtr(&g_Config.bEnableSound);
// Show OpenSL debug info
const std::string audioErrorStr = AndroidAudio_GetErrorString(g_audioState);
if (!audioErrorStr.empty()) {
audioSettings->Add(new InfoItem(a->T("Audio Error"), audioErrorStr));
}
#endif
// Control

View File

@ -132,3 +132,13 @@ bool AndroidAudio_Shutdown(AndroidAudioState *state) {
INFO_LOG(AUDIO, "OpenSLWrap completely unloaded.");
return true;
}
const std::string AndroidAudio_GetErrorString(AndroidAudioState *state) {
if (!state) {
return "No state";
}
if (!state->ctx) {
return "No context";
}
return state->ctx->GetErrorString();
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <string>
#include <mutex>
typedef int (*AndroidAudioCallback)(short *buffer, int num_samples);
@ -8,19 +9,32 @@ class AudioContext {
public:
AudioContext(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate);
virtual bool Init() { return false; }
virtual int AudioRecord_Start(int sampleRate) { return false; };
virtual int AudioRecord_Stop() { return false; };
virtual bool AudioRecord_Start(int sampleRate) { return false; };
virtual bool AudioRecord_Stop() { return false; };
const std::string &GetErrorString() {
std::unique_lock<std::mutex> lock(errorMutex_);
return error_;
}
virtual ~AudioContext() {}
protected:
void SetErrorString(const std::string &error) {
std::unique_lock<std::mutex> lock(errorMutex_);
error_ = error;
}
AndroidAudioCallback audioCallback;
int framesPerBuffer;
int sampleRate;
std::string error_ = "";
std::mutex errorMutex_;
};
struct AndroidAudioState;
// TODO: Get rid of this unnecessary wrapper layer from the old .so days
// It's okay for optimalFramesPerBuffer and optimalSampleRate to be 0. Defaults will be used.
AndroidAudioState *AndroidAudio_Init(AndroidAudioCallback cb, int optimalFramesPerBuffer, int optimalSampleRate);
bool AndroidAudio_Recording_SetSampleRate(AndroidAudioState *state, int sampleRate);
@ -30,3 +44,4 @@ bool AndroidAudio_Recording_State(AndroidAudioState *state);
bool AndroidAudio_Pause(AndroidAudioState *state);
bool AndroidAudio_Resume(AndroidAudioState *state);
bool AndroidAudio_Shutdown(AndroidAudioState *state);
const std::string AndroidAudio_GetErrorString(AndroidAudioState *state);

View File

@ -10,6 +10,7 @@
#include <SLES/OpenSLES_Android.h>
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "OpenSLContext.h"
#include "Core/HLE/sceUsbMic.h"
@ -19,18 +20,14 @@ void OpenSLContext::bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *c
SLuint32 recordsState;
result = (*ctx->recorderRecord)->GetRecordState(ctx->recorderRecord, &recordsState);
if(result != SL_RESULT_SUCCESS) {
ERROR_LOG(AUDIO, "GetRecordState error: %d", result);
if (!CheckResultStatic(result, "GetRecordState error: %d"))
return;
}
Microphone::addAudioData((uint8_t*) ctx->recordBuffer[ctx->activeRecordBuffer], ctx->recordBufferSize);
if (recordsState == SL_RECORDSTATE_RECORDING) {
result = (*ctx->recorderBufferQueue)->Enqueue(ctx->recorderBufferQueue, ctx->recordBuffer[ctx->activeRecordBuffer], ctx->recordBufferSize);
if (result != SL_RESULT_SUCCESS) {
ERROR_LOG(AUDIO, "Enqueue error: %d", result);
}
CheckResultStatic(result, "Enqueue error");
}
ctx->activeRecordBuffer += 1; // Switch buffer
@ -61,7 +58,7 @@ void OpenSLContext::BqPlayerCallback(SLAndroidSimpleBufferQueueItf bq) {
memset(buffer[curBuffer] + renderedFrames * 2, 0, byteCount);
}
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeInBytes);
CheckResult(result, StringFromFormat("Failed to enqueue: %d %d", renderedFrames, sizeInBytes).c_str());
// Comment from sample code:
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error
@ -82,27 +79,30 @@ bool OpenSLContext::Init() {
SLresult result;
// create engine
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS) {
ERROR_LOG(AUDIO, "OpenSL: Failed to create the engine: %d", (int)result);
if (!CheckResult(result, "slCreateEngine")) {
engineObject = nullptr;
return false;
}
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
_assert_(SL_RESULT_SUCCESS == result);
if (!CheckResult(result, "engine->Realize"))
return false;
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
_assert_(SL_RESULT_SUCCESS == result);
if (!CheckResult(result, "engine->GetInterface(ENGINE)"))
return false;
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
if (result != SL_RESULT_SUCCESS) {
ERROR_LOG(AUDIO, "OpenSL: Failed to create output mix: %d", (int)result);
if (!CheckResult(result, "engine->CreateOutputMix(ENGINE)")) {
(*engineObject)->Destroy(engineObject);
engineEngine = nullptr;
engineObject = nullptr;
return false;
}
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
_assert_(SL_RESULT_SUCCESS == result);
if (!CheckResult(result, "outputMix->Realize"))
return false;
// The constants, such as SL_SAMPLINGRATE_44_1, are just 44100000.
SLuint32 sr = (SLuint32)sampleRate * 1000;
@ -142,18 +142,23 @@ bool OpenSLContext::Init() {
}
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
_assert_(SL_RESULT_SUCCESS == result);
if (!CheckResult(result, "player->Realize"))
return false; // TODO: Release stuff!
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);
if (!CheckResult(result, "player->GetInterface(PLAY)"))
return false; // TODO: Release stuff!
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
if (!CheckResult(result, "player->GetInterface(BUFFER_QUEUE)"))
return false; // TODO: Release stuff!
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, &bqPlayerCallbackWrap, this);
_assert_(SL_RESULT_SUCCESS == result);
if (!CheckResult(result, "playerbq->RegisterCallback()"))
return false; // TODO: Release stuff!
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
_assert_(SL_RESULT_SUCCESS == result);
if (!CheckResult(result, "playerbq->GetInterface()"))
return false; // TODO: Release stuff!
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
_assert_(SL_RESULT_SUCCESS == result);
if (!CheckResult(result, "playerbq->SetPlayState(PLAYING)"))
return false; // TODO: Release stuff!
// Allocate and enqueue N empty buffers.
for (int i = 0; i < NUM_BUFFERS; i++) {
@ -172,9 +177,14 @@ bool OpenSLContext::Init() {
return true;
}
int OpenSLContext::AudioRecord_Start(int sampleRate) {
bool OpenSLContext::AudioRecord_Start(int sampleRate) {
SLresult result;
if (!engineEngine) {
SetErrorString("AudioRecord_Start: No engine");
return false;
}
// configure audio source
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSrc = {&loc_dev, NULL};
@ -197,38 +207,29 @@ int OpenSLContext::AudioRecord_Start(int sampleRate) {
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk,
sizeof(id)/sizeof(id[0]), id, req);
if (SL_RESULT_SUCCESS != result) {
ERROR_LOG(AUDIO, "CreateAudioRecorder failed: %d", result);
if (!CheckResult(result, "CreateAudioRecorder failed"))
return false;
}
// realize the audio recorder
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
ERROR_LOG(AUDIO, "Realize failed: %d", result);
if (!CheckResult(result, "recorderObject->Realize failed"))
return false;
}
// get the record interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
if (SL_RESULT_SUCCESS != result) {
ERROR_LOG(AUDIO, "GetInterface(record) failed: %d", result);
if (!CheckResult(result, "GetInterface(recorderObject) failed"))
return false;
}
// get the buffer queue interface
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, (void*) &recorderBufferQueue);
if (SL_RESULT_SUCCESS != result) {
ERROR_LOG(AUDIO, "GetInterface(queue interface) failed: %d", result);
if (!CheckResult(result, "GetInterface(queue interface) failed"))
return false;
}
// register callback on the buffer queue
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, &bqRecorderCallback, this);
if (SL_RESULT_SUCCESS != result) {
ERROR_LOG(AUDIO, "RegisterCallback failed: %d", result);
if (!CheckResult(result, "RegisterCallback failed"))
return false;
}
recordBufferSize = (44100 * 20 / 1000 * 2);
for (int i = 0; i < NUM_BUFFERS; i++) {
@ -236,22 +237,18 @@ int OpenSLContext::AudioRecord_Start(int sampleRate) {
}
for (int i = 0; i < NUM_BUFFERS; i++) {
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recordBuffer[i], recordBufferSize);
if (SL_RESULT_SUCCESS != result) {
ERROR_LOG(AUDIO, "Enqueue failed: %d", result);
if (!CheckResult(result, "Enqueue failed"))
return false;
}
}
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
_assert_(SL_RESULT_SUCCESS == result);
return true;
return CheckResult(result, "SetRecordState(recording) failed");
}
int OpenSLContext::AudioRecord_Stop() {
bool OpenSLContext::AudioRecord_Stop() {
if (recorderRecord != nullptr) {
SLresult result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
_assert_(SL_RESULT_SUCCESS == result);
CheckResult(result, "SetRecordState(stopped) failed");
}
if (recorderObject != nullptr) {
(*recorderObject)->Destroy(recorderObject);
@ -274,9 +271,7 @@ OpenSLContext::~OpenSLContext() {
INFO_LOG(AUDIO, "OpenSL: Shutdown - stopping playback");
SLresult result;
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) {
ERROR_LOG(AUDIO, "SetPlayState failed");
}
CheckResult(result, "SetPlayState(stopped) failed");
}
INFO_LOG(AUDIO, "OpenSL: Shutdown - deleting player object");
@ -311,3 +306,22 @@ OpenSLContext::~OpenSLContext() {
}
INFO_LOG(AUDIO, "OpenSL: Shutdown - finished");
}
bool OpenSLContext::CheckResult(SLresult result, const char *str) {
if (result != SL_RESULT_SUCCESS) {
ERROR_LOG(AUDIO, "OpenSL failure (%s): %d", str, result);
SetErrorString(str);
return false;
} else {
return true;
}
}
bool OpenSLContext::CheckResultStatic(SLresult result, const char *str) {
if (result != SL_RESULT_SUCCESS) {
ERROR_LOG(AUDIO, "OpenSL failure (%s): %d", str, result);
return false;
} else {
return true;
}
}

View File

@ -10,11 +10,15 @@ public:
OpenSLContext(AndroidAudioCallback cb, int framesPerBuffer, int sampleRate);
bool Init() override;
int AudioRecord_Start(int sampleRate) override;
int AudioRecord_Stop() override;
bool AudioRecord_Start(int sampleRate) override;
bool AudioRecord_Stop() override;
~OpenSLContext();
private:
bool CheckResult(SLresult result, const char *str);
static bool CheckResultStatic(SLresult result, const char *str);
// Should be no reason to need more than two buffers, but make it clear in the code.
enum {
NUM_BUFFERS = 2,

View File

@ -113,7 +113,7 @@ static std::atomic<int> emuThreadState((int)EmuThreadState::DISABLED);
void UpdateRunLoopAndroid(JNIEnv *env);
static AndroidAudioState *g_audioState;
AndroidAudioState *g_audioState;
struct FrameCommand {
FrameCommand() {}