blob: dbb76dcd27cdc364e414d2e950abefda0e6a75ff [file] [log] [blame]
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18// cribbed from samples/native-audio
19
20#include "audioplay.h"
21
22#define CHATTY ALOGD
23
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070024#include <string.h>
25
26#include <utils/Log.h>
27
28// for native audio
29#include <SLES/OpenSLES.h>
30#include <SLES/OpenSLES_Android.h>
31
32namespace audioplay {
33namespace {
34
35// engine interfaces
36static SLObjectItf engineObject = NULL;
37static SLEngineItf engineEngine;
38
39// output mix interfaces
40static SLObjectItf outputMixObject = NULL;
41
42// buffer queue player interfaces
43static SLObjectItf bqPlayerObject = NULL;
44static SLPlayItf bqPlayerPlay;
45static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
46static SLMuteSoloItf bqPlayerMuteSolo;
47static SLVolumeItf bqPlayerVolume;
48
49// pointer and size of the next player buffer to enqueue, and number of remaining buffers
50static const uint8_t* nextBuffer;
51static unsigned nextSize;
52
53static const uint32_t ID_RIFF = 0x46464952;
54static const uint32_t ID_WAVE = 0x45564157;
55static const uint32_t ID_FMT = 0x20746d66;
56static const uint32_t ID_DATA = 0x61746164;
57
58struct RiffWaveHeader {
59 uint32_t riff_id;
60 uint32_t riff_sz;
61 uint32_t wave_id;
62};
63
64struct ChunkHeader {
65 uint32_t id;
66 uint32_t sz;
67};
68
69struct ChunkFormat {
70 uint16_t audio_format;
71 uint16_t num_channels;
72 uint32_t sample_rate;
73 uint32_t byte_rate;
74 uint16_t block_align;
75 uint16_t bits_per_sample;
76};
77
78// this callback handler is called every time a buffer finishes playing
79void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
80 (void)bq;
81 (void)context;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070082 audioplay::setPlaying(false);
83}
84
85bool hasPlayer() {
86 return (engineObject != NULL && bqPlayerObject != NULL);
87}
88
89// create the engine and output mix objects
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -040090bool createEngine() {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070091 SLresult result;
92
93 // create engine
94 result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -040095 if (result != SL_RESULT_SUCCESS) {
96 ALOGE("slCreateEngine failed with result %d", result);
97 return false;
98 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070099 (void)result;
100
101 // realize the engine
102 result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400103 if (result != SL_RESULT_SUCCESS) {
104 ALOGE("sl engine Realize failed with result %d", result);
105 return false;
106 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700107 (void)result;
108
109 // get the engine interface, which is needed in order to create other objects
110 result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400111 if (result != SL_RESULT_SUCCESS) {
112 ALOGE("sl engine GetInterface failed with result %d", result);
113 return false;
114 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700115 (void)result;
116
117 // create output mix, with environmental reverb specified as a non-required interface
118 const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};
119 const SLboolean req[1] = {SL_BOOLEAN_FALSE};
120 result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400121 if (result != SL_RESULT_SUCCESS) {
122 ALOGE("sl engine CreateOutputMix failed with result %d", result);
123 return false;
124 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700125 (void)result;
126
127 // realize the output mix
128 result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400129 if (result != SL_RESULT_SUCCESS) {
130 ALOGE("sl outputMix Realize failed with result %d", result);
131 return false;
132 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700133 (void)result;
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400134
135 return true;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700136}
137
138// create buffer queue audio player
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400139bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700140 SLresult result;
141
142 // configure audio source
143 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
144
145 SLDataFormat_PCM format_pcm = {
146 SL_DATAFORMAT_PCM,
147 chunkFormat->num_channels,
148 chunkFormat->sample_rate * 1000, // convert to milliHz
149 chunkFormat->bits_per_sample,
150 16,
151 SL_SPEAKER_FRONT_CENTER,
152 SL_BYTEORDER_LITTLEENDIAN
153 };
154 SLDataSource audioSrc = {&loc_bufq, &format_pcm};
155
156 // configure audio sink
157 SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
158 SLDataSink audioSnk = {&loc_outmix, NULL};
159
160 // create audio player
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400161 const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
162 const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700163 result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400164 3, ids, req);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400165 if (result != SL_RESULT_SUCCESS) {
166 ALOGE("sl CreateAudioPlayer failed with result %d", result);
167 return false;
168 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700169 (void)result;
170
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400171 // Use the System stream for boot sound playback.
172 SLAndroidConfigurationItf playerConfig;
173 result = (*bqPlayerObject)->GetInterface(bqPlayerObject,
174 SL_IID_ANDROIDCONFIGURATION, &playerConfig);
175 if (result != SL_RESULT_SUCCESS) {
176 ALOGE("config GetInterface failed with result %d", result);
177 return false;
178 }
179 SLint32 streamType = SL_ANDROID_STREAM_SYSTEM;
180 result = (*playerConfig)->SetConfiguration(playerConfig,
181 SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
182 if (result != SL_RESULT_SUCCESS) {
183 ALOGE("SetConfiguration failed with result %d", result);
184 return false;
185 }
186
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700187 // realize the player
188 result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400189 if (result != SL_RESULT_SUCCESS) {
190 ALOGE("sl player Realize failed with result %d", result);
191 return false;
192 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700193 (void)result;
194
195 // get the play interface
196 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400197 if (result != SL_RESULT_SUCCESS) {
198 ALOGE("sl player GetInterface failed with result %d", result);
199 return false;
200 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700201 (void)result;
202
203 // get the buffer queue interface
204 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
205 &bqPlayerBufferQueue);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400206 if (result != SL_RESULT_SUCCESS) {
207 ALOGE("sl playberBufferQueue GetInterface failed with result %d", result);
208 return false;
209 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700210 (void)result;
211
212 // register callback on the buffer queue
213 result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400214 if (result != SL_RESULT_SUCCESS) {
215 ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
216 return false;
217 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700218 (void)result;
219
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700220 // get the volume interface
221 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400222 if (result != SL_RESULT_SUCCESS) {
223 ALOGE("sl volume GetInterface failed with result %d", result);
224 return false;
225 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700226 (void)result;
227
228 // set the player's state to playing
229 audioplay::setPlaying(true);
230 CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400231 return true;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700232}
233
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400234bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** oChunkFormat,
235 const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
236 *oSoundBuf = clipBuf;
237 *oSoundBufSize = clipBufSize;
238 *oChunkFormat = NULL;
239 const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
240 if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700241 (wavHeader->wave_id != ID_WAVE)) {
242 ALOGE("Error: audio file is not a riff/wave file\n");
243 return false;
244 }
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400245 *oSoundBuf += sizeof(*wavHeader);
246 *oSoundBufSize -= sizeof(*wavHeader);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700247
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700248 while (true) {
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400249 const ChunkHeader* chunkHeader = (const ChunkHeader*)*oSoundBuf;
250 if (*oSoundBufSize < sizeof(*chunkHeader)) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700251 ALOGE("EOF reading chunk headers");
252 return false;
253 }
254
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400255 *oSoundBuf += sizeof(*chunkHeader);
256 *oSoundBufSize -= sizeof(*chunkHeader);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700257
258 bool endLoop = false;
259 switch (chunkHeader->id) {
260 case ID_FMT:
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400261 *oChunkFormat = (const ChunkFormat*)*oSoundBuf;
262 *oSoundBuf += chunkHeader->sz;
263 *oSoundBufSize -= chunkHeader->sz;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700264 break;
265 case ID_DATA:
266 /* Stop looking for chunks */
267 endLoop = true;
268 break;
269 default:
270 /* Unknown chunk, skip bytes */
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400271 *oSoundBuf += chunkHeader->sz;
272 *oSoundBufSize -= chunkHeader->sz;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700273 }
274 if (endLoop) {
275 break;
276 }
277 }
278
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400279 if (*oChunkFormat == NULL) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700280 ALOGE("format not found in WAV file");
281 return false;
282 }
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400283 return true;
284}
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700285
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400286} // namespace
287
288bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
289 if (!createEngine()) {
290 return false;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700291 }
292
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400293 // Parse the example clip.
294 const ChunkFormat* chunkFormat;
295 const uint8_t* soundBuf;
296 unsigned soundBufSize;
297 if (!parseClipBuf(exampleClipBuf, exampleClipBufSize, &chunkFormat, &soundBuf, &soundBufSize)) {
298 return false;
299 }
300
301 // Initialize the BufferQueue based on this clip's format.
302 if (!createBufferQueueAudioPlayer(chunkFormat)) {
303 return false;
304 }
305 return true;
306}
307
308bool playClip(const uint8_t* buf, int size) {
309 // Parse the WAV header
310 const ChunkFormat* chunkFormat;
311 if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) {
312 return false;
313 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700314
315 if (!hasPlayer()) {
316 ALOGD("cannot play clip %p without a player", buf);
317 return false;
318 }
319
320 CHATTY("playClip on player %p: buf=%p size=%d", bqPlayerBufferQueue, buf, size);
321
322 if (nextSize > 0) {
323 // here we only enqueue one buffer because it is a long clip,
324 // but for streaming playback we would typically enqueue at least 2 buffers to start
325 SLresult result;
326 result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
327 if (SL_RESULT_SUCCESS != result) {
328 return false;
329 }
330 audioplay::setPlaying(true);
331 }
332
333 return true;
334}
335
336// set the playing state for the buffer queue audio player
337void setPlaying(bool isPlaying) {
338 if (!hasPlayer()) return;
339
340 SLresult result;
341
342 if (NULL != bqPlayerPlay) {
343 // set the player's state
344 result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
345 isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700346 }
347
348}
349
350void destroy() {
351 // destroy buffer queue audio player object, and invalidate all associated interfaces
352 if (bqPlayerObject != NULL) {
353 CHATTY("destroying audio player");
354 (*bqPlayerObject)->Destroy(bqPlayerObject);
355 bqPlayerObject = NULL;
356 bqPlayerPlay = NULL;
357 bqPlayerBufferQueue = NULL;
358 bqPlayerMuteSolo = NULL;
359 bqPlayerVolume = NULL;
360 }
361
362 // destroy output mix object, and invalidate all associated interfaces
363 if (outputMixObject != NULL) {
364 (*outputMixObject)->Destroy(outputMixObject);
365 outputMixObject = NULL;
366 }
367
368 // destroy engine object, and invalidate all associated interfaces
369 if (engineObject != NULL) {
370 CHATTY("destroying audio engine");
371 (*engineObject)->Destroy(engineObject);
372 engineObject = NULL;
373 engineEngine = NULL;
374 }
375}
376
377} // namespace audioplay