CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/android/jni/OpenSLContext.cpp
Views: 1401
// Minimal audio streaming using OpenSL.1//2// Loosely based on the Android NDK sample code.34#include <cstring>5#include <unistd.h>67// for native audio8#include <SLES/OpenSLES.h>9#include <SLES/OpenSLES_Android.h>1011#include "Common/Log.h"12#include "OpenSLContext.h"13#include "Core/HLE/sceUsbMic.h"1415void OpenSLContext::bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {16OpenSLContext *ctx = (OpenSLContext *)context;17SLresult result;1819SLuint32 recordsState;20result = (*ctx->recorderRecord)->GetRecordState(ctx->recorderRecord, &recordsState);21if (!CheckResultStatic(result, "GetRecordState error: %d"))22return;2324Microphone::addAudioData((uint8_t*) ctx->recordBuffer[ctx->activeRecordBuffer], ctx->recordBufferSize);2526if (recordsState == SL_RECORDSTATE_RECORDING) {27result = (*ctx->recorderBufferQueue)->Enqueue(ctx->recorderBufferQueue, ctx->recordBuffer[ctx->activeRecordBuffer], ctx->recordBufferSize);28CheckResultStatic(result, "Enqueue error");29}3031ctx->activeRecordBuffer += 1; // Switch buffer32if (ctx->activeRecordBuffer == NUM_BUFFERS)33ctx->activeRecordBuffer = 0;34}3536// This callback handler is called every time a buffer finishes playing.37// The documentation available is very unclear about how to best manage buffers.38// I've chosen to this approach: Instantly enqueue a buffer that was rendered to the last time,39// and then render the next. Hopefully it's okay to spend time in this callback after having enqueued.40void OpenSLContext::bqPlayerCallbackWrap(SLAndroidSimpleBufferQueueItf bq, void *context) {41OpenSLContext *ctx = (OpenSLContext *)context;42ctx->BqPlayerCallback(bq);43}4445void OpenSLContext::BqPlayerCallback(SLAndroidSimpleBufferQueueItf bq) {46if (bq != bqPlayerBufferQueue) {47ERROR_LOG(Log::Audio, "OpenSL: Wrong bq!");48return;49}5051int renderedFrames = audioCallback(buffer[curBuffer], framesPerBuffer, SampleRate());5253int sizeInBytes = framesPerBuffer * 2 * sizeof(short);54int byteCount = (framesPerBuffer - renderedFrames) * 4;55if (byteCount > 0) {56memset(buffer[curBuffer] + renderedFrames * 2, 0, byteCount);57}58SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeInBytes);5960// TODO: get rid of this snprintf too61CheckResult(result, "Failed to enqueue");62// Comment from sample code:63// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,64// which for this code example would indicate a programming error65if (result != SL_RESULT_SUCCESS) {66ERROR_LOG(Log::Audio, "OpenSL: Failed to enqueue! %i %i", renderedFrames, sizeInBytes);67}6869curBuffer += 1; // Switch buffer70if (curBuffer == NUM_BUFFERS)71curBuffer = 0;72}7374// create the engine and output mix objects75OpenSLContext::OpenSLContext(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate)76: AudioContext(cb, _FramesPerBuffer, _SampleRate) {}7778bool OpenSLContext::Init() {79SLresult result;80// create engine81result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);82if (!CheckResult(result, "slCreateEngine")) {83engineObject = nullptr;84return false;85}8687result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);88if (!CheckResult(result, "engine->Realize"))89return false;9091result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);92if (!CheckResult(result, "engine->GetInterface(ENGINE)"))93return false;9495result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);96if (!CheckResult(result, "engine->CreateOutputMix(ENGINE)")) {97(*engineObject)->Destroy(engineObject);98engineEngine = nullptr;99engineObject = nullptr;100return false;101}102103result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);104if (!CheckResult(result, "outputMix->Realize"))105return false;106107// The constants, such as SL_SAMPLINGRATE_44_1, are just 44100000.108SLuint32 sr = (SLuint32)sampleRate * 1000;109110SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS};111SLDataFormat_PCM format_pcm = {112SL_DATAFORMAT_PCM,1132,114sr,115SL_PCMSAMPLEFORMAT_FIXED_16,116SL_PCMSAMPLEFORMAT_FIXED_16,117SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,118SL_BYTEORDER_LITTLEENDIAN119};120121SLDataSource audioSrc = {&loc_bufq, &format_pcm};122123// configure audio sink124SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};125SLDataSink audioSnk = {&loc_outmix, NULL};126127// create audio player128const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};129const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};130result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,131sizeof(ids)/sizeof(ids[0]), ids, req);132if (result != SL_RESULT_SUCCESS) {133ERROR_LOG(Log::Audio, "OpenSL: CreateAudioPlayer failed: %d", (int)result);134(*outputMixObject)->Destroy(outputMixObject);135outputMixObject = nullptr;136137// Should really tear everything down here. Sigh.138(*engineObject)->Destroy(engineObject);139engineEngine = nullptr;140engineObject = nullptr;141return false;142}143144result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);145if (!CheckResult(result, "player->Realize"))146return false; // TODO: Release stuff!147result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);148if (!CheckResult(result, "player->GetInterface(PLAY)"))149return false; // TODO: Release stuff!150result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);151if (!CheckResult(result, "player->GetInterface(BUFFER_QUEUE)"))152return false; // TODO: Release stuff!153result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, &bqPlayerCallbackWrap, this);154if (!CheckResult(result, "playerbq->RegisterCallback()"))155return false; // TODO: Release stuff!156result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);157if (!CheckResult(result, "playerbq->GetInterface()"))158return false; // TODO: Release stuff!159result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);160if (!CheckResult(result, "playerbq->SetPlayState(PLAYING)"))161return false; // TODO: Release stuff!162163// Allocate and enqueue N empty buffers.164for (int i = 0; i < NUM_BUFFERS; i++) {165buffer[i] = new short[framesPerBuffer * 2]{};166}167168int sizeInBytes = framesPerBuffer * 2 * sizeof(short);169for (int i = 0; i < NUM_BUFFERS; i++) {170result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[i], sizeInBytes);171if (SL_RESULT_SUCCESS != result) {172return false;173}174}175176curBuffer = 0;177return true;178}179180bool OpenSLContext::AudioRecord_Start(int sampleRate) {181SLresult result;182183if (!engineEngine) {184SetErrorString("AudioRecord_Start: No engine");185return false;186}187188// configure audio source189SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};190SLDataSource audioSrc = {&loc_dev, NULL};191192// configure audio sink193SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};194SLDataFormat_PCM format_pcm = {195SL_DATAFORMAT_PCM,1961,197(SLuint32) sampleRate * 1000, // The constants such as SL_SAMPLINGRATE_44_1 are 44100000198SL_PCMSAMPLEFORMAT_FIXED_16,199SL_PCMSAMPLEFORMAT_FIXED_16,200SL_SPEAKER_FRONT_CENTER,201SL_BYTEORDER_LITTLEENDIAN202};203SLDataSink audioSnk = {&loc_bq, &format_pcm};204205// create audio recorder206const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};207const SLboolean req[1] = {SL_BOOLEAN_TRUE};208result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk,209sizeof(id)/sizeof(id[0]), id, req);210if (!CheckResult(result, "CreateAudioRecorder failed"))211return false;212213// realize the audio recorder214result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);215if (!CheckResult(result, "recorderObject->Realize failed"))216return false;217218219// get the record interface220result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);221if (!CheckResult(result, "GetInterface(recorderObject) failed"))222return false;223224// get the buffer queue interface225result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, (void*) &recorderBufferQueue);226if (!CheckResult(result, "GetInterface(queue interface) failed"))227return false;228229// register callback on the buffer queue230result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, &bqRecorderCallback, this);231if (!CheckResult(result, "RegisterCallback failed"))232return false;233234recordBufferSize = (44100 * 20 / 1000 * 2);235for (int i = 0; i < NUM_BUFFERS; i++) {236recordBuffer[i] = new short[recordBufferSize];237}238for (int i = 0; i < NUM_BUFFERS; i++) {239result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recordBuffer[i], recordBufferSize);240if (!CheckResult(result, "Enqueue failed"))241return false;242}243244result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);245return CheckResult(result, "SetRecordState(recording) failed");246}247248bool OpenSLContext::AudioRecord_Stop() {249if (recorderRecord != nullptr) {250SLresult result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);251CheckResult(result, "SetRecordState(stopped) failed");252}253if (recorderObject != nullptr) {254(*recorderObject)->Destroy(recorderObject);255recorderObject = nullptr;256recorderRecord = nullptr;257recorderBufferQueue = nullptr;258}259if (recordBuffer[0] != nullptr) {260delete [] recordBuffer[0];261delete [] recordBuffer[1];262recordBuffer[0] = nullptr;263recordBuffer[1] = nullptr;264}265return true;266}267268// shut down the native audio system269OpenSLContext::~OpenSLContext() {270if (bqPlayerPlay) {271INFO_LOG(Log::Audio, "OpenSL: Shutdown - stopping playback");272SLresult result;273result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);274CheckResult(result, "SetPlayState(stopped) failed");275}276277INFO_LOG(Log::Audio, "OpenSL: Shutdown - deleting player object");278279if (bqPlayerObject) {280(*bqPlayerObject)->Destroy(bqPlayerObject);281bqPlayerObject = nullptr;282bqPlayerPlay = nullptr;283bqPlayerBufferQueue = nullptr;284bqPlayerVolume = nullptr;285}286287INFO_LOG(Log::Audio, "OpenSL: Shutdown - deleting mix object");288289if (outputMixObject) {290(*outputMixObject)->Destroy(outputMixObject);291outputMixObject = nullptr;292}293AudioRecord_Stop();294295INFO_LOG(Log::Audio, "OpenSL: Shutdown - deleting engine object");296297if (engineObject) {298(*engineObject)->Destroy(engineObject);299engineObject = nullptr;300engineEngine = nullptr;301}302303for (int i = 0; i < NUM_BUFFERS; i++) {304delete[] buffer[i];305buffer[i] = nullptr;306}307INFO_LOG(Log::Audio, "OpenSL: Shutdown - finished");308}309310bool OpenSLContext::CheckResult(SLresult result, const char *str) {311if (result != SL_RESULT_SUCCESS) {312ERROR_LOG(Log::Audio, "OpenSL failure (%s): %d", str, result);313SetErrorString(str);314return false;315} else {316return true;317}318}319320bool OpenSLContext::CheckResultStatic(SLresult result, const char *str) {321if (result != SL_RESULT_SUCCESS) {322ERROR_LOG(Log::Audio, "OpenSL failure (%s): %d", str, result);323return false;324} else {325return true;326}327}328329330