Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/android/jni/OpenSLContext.cpp
3185 views
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
audioCallback(buffer[curBuffer], framesPerBuffer, SampleRate(), nullptr);
53
54
const int sizeInBytes = framesPerBuffer * 2 * sizeof(short);
55
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeInBytes);
56
57
// TODO: get rid of this snprintf too
58
CheckResult(result, "Failed to enqueue");
59
// Comment from sample code:
60
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
61
// which for this code example would indicate a programming error
62
if (result != SL_RESULT_SUCCESS) {
63
ERROR_LOG(Log::Audio, "OpenSL: Failed to enqueue! %i", sizeInBytes);
64
}
65
66
curBuffer += 1; // Switch buffer
67
if (curBuffer == NUM_BUFFERS)
68
curBuffer = 0;
69
}
70
71
// create the engine and output mix objects
72
OpenSLContext::OpenSLContext(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate)
73
: AudioContext(cb, _FramesPerBuffer, _SampleRate) {}
74
75
bool OpenSLContext::Init() {
76
SLresult result;
77
// create engine
78
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
79
if (!CheckResult(result, "slCreateEngine")) {
80
engineObject = nullptr;
81
return false;
82
}
83
84
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
85
if (!CheckResult(result, "engine->Realize"))
86
return false;
87
88
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
89
if (!CheckResult(result, "engine->GetInterface(ENGINE)"))
90
return false;
91
92
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
93
if (!CheckResult(result, "engine->CreateOutputMix(ENGINE)")) {
94
(*engineObject)->Destroy(engineObject);
95
engineEngine = nullptr;
96
engineObject = nullptr;
97
return false;
98
}
99
100
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
101
if (!CheckResult(result, "outputMix->Realize"))
102
return false;
103
104
// The constants, such as SL_SAMPLINGRATE_44_1, are just 44100000.
105
SLuint32 sr = (SLuint32)sampleRate * 1000;
106
107
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS};
108
SLDataFormat_PCM format_pcm = {
109
SL_DATAFORMAT_PCM,
110
2,
111
sr,
112
SL_PCMSAMPLEFORMAT_FIXED_16,
113
SL_PCMSAMPLEFORMAT_FIXED_16,
114
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
115
SL_BYTEORDER_LITTLEENDIAN
116
};
117
118
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
119
120
// configure audio sink
121
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
122
SLDataSink audioSnk = {&loc_outmix, NULL};
123
124
// create audio player
125
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
126
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
127
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
128
sizeof(ids)/sizeof(ids[0]), ids, req);
129
if (result != SL_RESULT_SUCCESS) {
130
ERROR_LOG(Log::Audio, "OpenSL: CreateAudioPlayer failed: %d", (int)result);
131
(*outputMixObject)->Destroy(outputMixObject);
132
outputMixObject = nullptr;
133
134
// Should really tear everything down here. Sigh.
135
(*engineObject)->Destroy(engineObject);
136
engineEngine = nullptr;
137
engineObject = nullptr;
138
return false;
139
}
140
141
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
142
if (!CheckResult(result, "player->Realize"))
143
return false; // TODO: Release stuff!
144
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
145
if (!CheckResult(result, "player->GetInterface(PLAY)"))
146
return false; // TODO: Release stuff!
147
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
148
if (!CheckResult(result, "player->GetInterface(BUFFER_QUEUE)"))
149
return false; // TODO: Release stuff!
150
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, &bqPlayerCallbackWrap, this);
151
if (!CheckResult(result, "playerbq->RegisterCallback()"))
152
return false; // TODO: Release stuff!
153
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
154
if (!CheckResult(result, "playerbq->GetInterface()"))
155
return false; // TODO: Release stuff!
156
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
157
if (!CheckResult(result, "playerbq->SetPlayState(PLAYING)"))
158
return false; // TODO: Release stuff!
159
160
// Allocate and enqueue N empty buffers.
161
for (int i = 0; i < NUM_BUFFERS; i++) {
162
buffer[i] = new short[framesPerBuffer * 2]{};
163
}
164
165
int sizeInBytes = framesPerBuffer * 2 * sizeof(short);
166
for (int i = 0; i < NUM_BUFFERS; i++) {
167
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[i], sizeInBytes);
168
if (SL_RESULT_SUCCESS != result) {
169
return false;
170
}
171
}
172
173
curBuffer = 0;
174
return true;
175
}
176
177
bool OpenSLContext::AudioRecord_Start(int sampleRate) {
178
SLresult result;
179
180
if (!engineEngine) {
181
SetErrorString("AudioRecord_Start: No engine");
182
return false;
183
}
184
185
// configure audio source
186
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
187
SLDataSource audioSrc = {&loc_dev, NULL};
188
189
// configure audio sink
190
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
191
SLDataFormat_PCM format_pcm = {
192
SL_DATAFORMAT_PCM,
193
1,
194
(SLuint32) sampleRate * 1000, // The constants such as SL_SAMPLINGRATE_44_1 are 44100000
195
SL_PCMSAMPLEFORMAT_FIXED_16,
196
SL_PCMSAMPLEFORMAT_FIXED_16,
197
SL_SPEAKER_FRONT_CENTER,
198
SL_BYTEORDER_LITTLEENDIAN
199
};
200
SLDataSink audioSnk = {&loc_bq, &format_pcm};
201
202
// create audio recorder
203
const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
204
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
205
result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk,
206
sizeof(id)/sizeof(id[0]), id, req);
207
if (!CheckResult(result, "CreateAudioRecorder failed"))
208
return false;
209
210
// realize the audio recorder
211
result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
212
if (!CheckResult(result, "recorderObject->Realize failed"))
213
return false;
214
215
216
// get the record interface
217
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
218
if (!CheckResult(result, "GetInterface(recorderObject) failed"))
219
return false;
220
221
// get the buffer queue interface
222
result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, (void*) &recorderBufferQueue);
223
if (!CheckResult(result, "GetInterface(queue interface) failed"))
224
return false;
225
226
// register callback on the buffer queue
227
result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, &bqRecorderCallback, this);
228
if (!CheckResult(result, "RegisterCallback failed"))
229
return false;
230
231
recordBufferSize = (44100 * 20 / 1000 * 2);
232
for (int i = 0; i < NUM_BUFFERS; i++) {
233
recordBuffer[i] = new short[recordBufferSize];
234
}
235
for (int i = 0; i < NUM_BUFFERS; i++) {
236
result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, recordBuffer[i], recordBufferSize);
237
if (!CheckResult(result, "Enqueue failed"))
238
return false;
239
}
240
241
result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
242
return CheckResult(result, "SetRecordState(recording) failed");
243
}
244
245
bool OpenSLContext::AudioRecord_Stop() {
246
if (recorderRecord != nullptr) {
247
SLresult result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
248
CheckResult(result, "SetRecordState(stopped) failed");
249
}
250
if (recorderObject != nullptr) {
251
(*recorderObject)->Destroy(recorderObject);
252
recorderObject = nullptr;
253
recorderRecord = nullptr;
254
recorderBufferQueue = nullptr;
255
}
256
if (recordBuffer[0] != nullptr) {
257
delete [] recordBuffer[0];
258
delete [] recordBuffer[1];
259
recordBuffer[0] = nullptr;
260
recordBuffer[1] = nullptr;
261
}
262
return true;
263
}
264
265
// shut down the native audio system
266
OpenSLContext::~OpenSLContext() {
267
if (bqPlayerPlay) {
268
INFO_LOG(Log::Audio, "OpenSL: Shutdown - stopping playback");
269
SLresult result;
270
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
271
CheckResult(result, "SetPlayState(stopped) failed");
272
}
273
274
INFO_LOG(Log::Audio, "OpenSL: Shutdown - deleting player object");
275
276
if (bqPlayerObject) {
277
(*bqPlayerObject)->Destroy(bqPlayerObject);
278
bqPlayerObject = nullptr;
279
bqPlayerPlay = nullptr;
280
bqPlayerBufferQueue = nullptr;
281
bqPlayerVolume = nullptr;
282
}
283
284
INFO_LOG(Log::Audio, "OpenSL: Shutdown - deleting mix object");
285
286
if (outputMixObject) {
287
(*outputMixObject)->Destroy(outputMixObject);
288
outputMixObject = nullptr;
289
}
290
OpenSLContext::AudioRecord_Stop();
291
292
INFO_LOG(Log::Audio, "OpenSL: Shutdown - deleting engine object");
293
294
if (engineObject) {
295
(*engineObject)->Destroy(engineObject);
296
engineObject = nullptr;
297
engineEngine = nullptr;
298
}
299
300
for (int i = 0; i < NUM_BUFFERS; i++) {
301
delete[] buffer[i];
302
buffer[i] = nullptr;
303
}
304
INFO_LOG(Log::Audio, "OpenSL: Shutdown - finished");
305
}
306
307
bool OpenSLContext::CheckResult(SLresult result, const char *str) {
308
if (result != SL_RESULT_SUCCESS) {
309
ERROR_LOG(Log::Audio, "OpenSL failure (%s): %d", str, result);
310
SetErrorString(str);
311
return false;
312
} else {
313
return true;
314
}
315
}
316
317
bool OpenSLContext::CheckResultStatic(SLresult result, const char *str) {
318
if (result != SL_RESULT_SUCCESS) {
319
ERROR_LOG(Log::Audio, "OpenSL failure (%s): %d", str, result);
320
return false;
321
} else {
322
return true;
323
}
324
}
325
326