CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/android/jni/OpenSLContext.cpp
Views: 1401
1
// Minimal audio streaming using OpenSL.
2
//
3
// Loosely based on the Android NDK sample code.
4
5
#include <cstring>
6
#include <unistd.h>
7
8
// for native audio
9
#include <SLES/OpenSLES.h>
10
#include <SLES/OpenSLES_Android.h>
11
12
#include "Common/Log.h"
13
#include "OpenSLContext.h"
14
#include "Core/HLE/sceUsbMic.h"
15
16
void OpenSLContext::bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
17
OpenSLContext *ctx = (OpenSLContext *)context;
18
SLresult result;
19
20
SLuint32 recordsState;
21
result = (*ctx->recorderRecord)->GetRecordState(ctx->recorderRecord, &recordsState);
22
if (!CheckResultStatic(result, "GetRecordState error: %d"))
23
return;
24
25
Microphone::addAudioData((uint8_t*) ctx->recordBuffer[ctx->activeRecordBuffer], ctx->recordBufferSize);
26
27
if (recordsState == SL_RECORDSTATE_RECORDING) {
28
result = (*ctx->recorderBufferQueue)->Enqueue(ctx->recorderBufferQueue, ctx->recordBuffer[ctx->activeRecordBuffer], ctx->recordBufferSize);
29
CheckResultStatic(result, "Enqueue error");
30
}
31
32
ctx->activeRecordBuffer += 1; // Switch buffer
33
if (ctx->activeRecordBuffer == NUM_BUFFERS)
34
ctx->activeRecordBuffer = 0;
35
}
36
37
// This callback handler is called every time a buffer finishes playing.
38
// The documentation available is very unclear about how to best manage buffers.
39
// I've chosen to this approach: Instantly enqueue a buffer that was rendered to the last time,
40
// and then render the next. Hopefully it's okay to spend time in this callback after having enqueued.
41
void OpenSLContext::bqPlayerCallbackWrap(SLAndroidSimpleBufferQueueItf bq, void *context) {
42
OpenSLContext *ctx = (OpenSLContext *)context;
43
ctx->BqPlayerCallback(bq);
44
}
45
46
void OpenSLContext::BqPlayerCallback(SLAndroidSimpleBufferQueueItf bq) {
47
if (bq != bqPlayerBufferQueue) {
48
ERROR_LOG(Log::Audio, "OpenSL: Wrong bq!");
49
return;
50
}
51
52
int renderedFrames = audioCallback(buffer[curBuffer], framesPerBuffer, SampleRate());
53
54
int sizeInBytes = framesPerBuffer * 2 * sizeof(short);
55
int byteCount = (framesPerBuffer - renderedFrames) * 4;
56
if (byteCount > 0) {
57
memset(buffer[curBuffer] + renderedFrames * 2, 0, byteCount);
58
}
59
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeInBytes);
60
61
// TODO: get rid of this snprintf too
62
CheckResult(result, "Failed to enqueue");
63
// Comment from sample code:
64
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
65
// which for this code example would indicate a programming error
66
if (result != SL_RESULT_SUCCESS) {
67
ERROR_LOG(Log::Audio, "OpenSL: Failed to enqueue! %i %i", renderedFrames, sizeInBytes);
68
}
69
70
curBuffer += 1; // Switch buffer
71
if (curBuffer == NUM_BUFFERS)
72
curBuffer = 0;
73
}
74
75
// create the engine and output mix objects
76
OpenSLContext::OpenSLContext(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate)
77
: AudioContext(cb, _FramesPerBuffer, _SampleRate) {}
78
79
bool OpenSLContext::Init() {
80
SLresult result;
81
// create engine
82
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
83
if (!CheckResult(result, "slCreateEngine")) {
84
engineObject = nullptr;
85
return false;
86
}
87
88
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
89
if (!CheckResult(result, "engine->Realize"))
90
return false;
91
92
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
93
if (!CheckResult(result, "engine->GetInterface(ENGINE)"))
94
return false;
95
96
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
97
if (!CheckResult(result, "engine->CreateOutputMix(ENGINE)")) {
98
(*engineObject)->Destroy(engineObject);
99
engineEngine = nullptr;
100
engineObject = nullptr;
101
return false;
102
}
103
104
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
105
if (!CheckResult(result, "outputMix->Realize"))
106
return false;
107
108
// The constants, such as SL_SAMPLINGRATE_44_1, are just 44100000.
109
SLuint32 sr = (SLuint32)sampleRate * 1000;
110
111
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS};
112
SLDataFormat_PCM format_pcm = {
113
SL_DATAFORMAT_PCM,
114
2,
115
sr,
116
SL_PCMSAMPLEFORMAT_FIXED_16,
117
SL_PCMSAMPLEFORMAT_FIXED_16,
118
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
119
SL_BYTEORDER_LITTLEENDIAN
120
};
121
122
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
123
124
// configure audio sink
125
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
126
SLDataSink audioSnk = {&loc_outmix, NULL};
127
128
// create audio player
129
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
130
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
131
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
132
sizeof(ids)/sizeof(ids[0]), ids, req);
133
if (result != SL_RESULT_SUCCESS) {
134
ERROR_LOG(Log::Audio, "OpenSL: CreateAudioPlayer failed: %d", (int)result);
135
(*outputMixObject)->Destroy(outputMixObject);
136
outputMixObject = nullptr;
137
138
// Should really tear everything down here. Sigh.
139
(*engineObject)->Destroy(engineObject);
140
engineEngine = nullptr;
141
engineObject = nullptr;
142
return false;
143
}
144
145
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
146
if (!CheckResult(result, "player->Realize"))
147
return false; // TODO: Release stuff!
148
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
149
if (!CheckResult(result, "player->GetInterface(PLAY)"))
150
return false; // TODO: Release stuff!
151
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
152
if (!CheckResult(result, "player->GetInterface(BUFFER_QUEUE)"))
153
return false; // TODO: Release stuff!
154
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, &bqPlayerCallbackWrap, this);
155
if (!CheckResult(result, "playerbq->RegisterCallback()"))
156
return false; // TODO: Release stuff!
157
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
158
if (!CheckResult(result, "playerbq->GetInterface()"))
159
return false; // TODO: Release stuff!
160
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
161
if (!CheckResult(result, "playerbq->SetPlayState(PLAYING)"))
162
return false; // TODO: Release stuff!
163
164
// Allocate and enqueue N empty buffers.
165
for (int i = 0; i < NUM_BUFFERS; i++) {
166
buffer[i] = new short[framesPerBuffer * 2]{};
167
}
168
169
int sizeInBytes = framesPerBuffer * 2 * sizeof(short);
170
for (int i = 0; i < NUM_BUFFERS; i++) {
171
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[i], sizeInBytes);
172
if (SL_RESULT_SUCCESS != result) {
173
return false;
174
}
175
}
176
177
curBuffer = 0;
178
return true;
179
}
180
181
bool OpenSLContext::AudioRecord_Start(int sampleRate) {
182
SLresult result;
183
184
if (!engineEngine) {
185
SetErrorString("AudioRecord_Start: No engine");
186
return false;
187
}
188
189
// configure audio source
190
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
191
SLDataSource audioSrc = {&loc_dev, NULL};
192
193
// configure audio sink
194
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
195
SLDataFormat_PCM format_pcm = {
196
SL_DATAFORMAT_PCM,
197
1,
198
(SLuint32) sampleRate * 1000, // The constants such as SL_SAMPLINGRATE_44_1 are 44100000
199
SL_PCMSAMPLEFORMAT_FIXED_16,
200
SL_PCMSAMPLEFORMAT_FIXED_16,
201
SL_SPEAKER_FRONT_CENTER,
202
SL_BYTEORDER_LITTLEENDIAN
203
};
204
SLDataSink audioSnk = {&loc_bq, &format_pcm};
205
206
// create audio recorder
207
const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
208
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
209
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk,
210
sizeof(id)/sizeof(id[0]), id, req);
211
if (!CheckResult(result, "CreateAudioRecorder failed"))
212
return false;
213
214
// realize the audio recorder
215
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
216
if (!CheckResult(result, "recorderObject->Realize failed"))
217
return false;
218
219
220
// get the record interface
221
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
222
if (!CheckResult(result, "GetInterface(recorderObject) failed"))
223
return false;
224
225
// get the buffer queue interface
226
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, (void*) &recorderBufferQueue);
227
if (!CheckResult(result, "GetInterface(queue interface) failed"))
228
return false;
229
230
// register callback on the buffer queue
231
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, &bqRecorderCallback, this);
232
if (!CheckResult(result, "RegisterCallback failed"))
233
return false;
234
235
recordBufferSize = (44100 * 20 / 1000 * 2);
236
for (int i = 0; i < NUM_BUFFERS; i++) {
237
recordBuffer[i] = new short[recordBufferSize];
238
}
239
for (int i = 0; i < NUM_BUFFERS; i++) {
240
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recordBuffer[i], recordBufferSize);
241
if (!CheckResult(result, "Enqueue failed"))
242
return false;
243
}
244
245
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
246
return CheckResult(result, "SetRecordState(recording) failed");
247
}
248
249
bool OpenSLContext::AudioRecord_Stop() {
250
if (recorderRecord != nullptr) {
251
SLresult result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
252
CheckResult(result, "SetRecordState(stopped) failed");
253
}
254
if (recorderObject != nullptr) {
255
(*recorderObject)->Destroy(recorderObject);
256
recorderObject = nullptr;
257
recorderRecord = nullptr;
258
recorderBufferQueue = nullptr;
259
}
260
if (recordBuffer[0] != nullptr) {
261
delete [] recordBuffer[0];
262
delete [] recordBuffer[1];
263
recordBuffer[0] = nullptr;
264
recordBuffer[1] = nullptr;
265
}
266
return true;
267
}
268
269
// shut down the native audio system
270
OpenSLContext::~OpenSLContext() {
271
if (bqPlayerPlay) {
272
INFO_LOG(Log::Audio, "OpenSL: Shutdown - stopping playback");
273
SLresult result;
274
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
275
CheckResult(result, "SetPlayState(stopped) failed");
276
}
277
278
INFO_LOG(Log::Audio, "OpenSL: Shutdown - deleting player object");
279
280
if (bqPlayerObject) {
281
(*bqPlayerObject)->Destroy(bqPlayerObject);
282
bqPlayerObject = nullptr;
283
bqPlayerPlay = nullptr;
284
bqPlayerBufferQueue = nullptr;
285
bqPlayerVolume = nullptr;
286
}
287
288
INFO_LOG(Log::Audio, "OpenSL: Shutdown - deleting mix object");
289
290
if (outputMixObject) {
291
(*outputMixObject)->Destroy(outputMixObject);
292
outputMixObject = nullptr;
293
}
294
AudioRecord_Stop();
295
296
INFO_LOG(Log::Audio, "OpenSL: Shutdown - deleting engine object");
297
298
if (engineObject) {
299
(*engineObject)->Destroy(engineObject);
300
engineObject = nullptr;
301
engineEngine = nullptr;
302
}
303
304
for (int i = 0; i < NUM_BUFFERS; i++) {
305
delete[] buffer[i];
306
buffer[i] = nullptr;
307
}
308
INFO_LOG(Log::Audio, "OpenSL: Shutdown - finished");
309
}
310
311
bool OpenSLContext::CheckResult(SLresult result, const char *str) {
312
if (result != SL_RESULT_SUCCESS) {
313
ERROR_LOG(Log::Audio, "OpenSL failure (%s): %d", str, result);
314
SetErrorString(str);
315
return false;
316
} else {
317
return true;
318
}
319
}
320
321
bool OpenSLContext::CheckResultStatic(SLresult result, const char *str) {
322
if (result != SL_RESULT_SUCCESS) {
323
ERROR_LOG(Log::Audio, "OpenSL failure (%s): %d", str, result);
324
return false;
325
} else {
326
return true;
327
}
328
}
329
330