1 /*
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 #define LOG_TAG "audioplay"
24
25 #include <string.h>
26
27 #include <utils/Log.h>
28
29 // for native audio
30 #include <SLES/OpenSLES.h>
31 #include <SLES/OpenSLES_Android.h>
32
33 namespace audioplay {
34 namespace {
35
36 // engine interfaces
37 static SLObjectItf engineObject = NULL;
38 static SLEngineItf engineEngine;
39
40 // output mix interfaces
41 static SLObjectItf outputMixObject = NULL;
42
43 // buffer queue player interfaces
44 static SLObjectItf bqPlayerObject = NULL;
45 static SLPlayItf bqPlayerPlay;
46 static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
47 static SLMuteSoloItf bqPlayerMuteSolo;
48 static SLVolumeItf bqPlayerVolume;
49
50 // pointer and size of the next player buffer to enqueue, and number of remaining buffers
51 static const uint8_t* nextBuffer;
52 static unsigned nextSize;
53
54 static const uint32_t ID_RIFF = 0x46464952;
55 static const uint32_t ID_WAVE = 0x45564157;
56 static const uint32_t ID_FMT = 0x20746d66;
57 static const uint32_t ID_DATA = 0x61746164;
58
59 struct RiffWaveHeader {
60 uint32_t riff_id;
61 uint32_t riff_sz;
62 uint32_t wave_id;
63 };
64
65 struct ChunkHeader {
66 uint32_t id;
67 uint32_t sz;
68 };
69
70 struct ChunkFormat {
71 uint16_t audio_format;
72 uint16_t num_channels;
73 uint32_t sample_rate;
74 uint32_t byte_rate;
75 uint16_t block_align;
76 uint16_t bits_per_sample;
77 };
78
79 // this callback handler is called every time a buffer finishes playing
bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq,void * context)80 void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
81 (void)bq;
82 (void)context;
83 audioplay::setPlaying(false);
84 }
85
hasPlayer()86 bool hasPlayer() {
87 return (engineObject != NULL && bqPlayerObject != NULL);
88 }
89
90 // create the engine and output mix objects
createEngine()91 bool createEngine() {
92 SLresult result;
93
94 // create engine
95 result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
96 if (result != SL_RESULT_SUCCESS) {
97 ALOGE("slCreateEngine failed with result %d", result);
98 return false;
99 }
100 (void)result;
101
102 // realize the engine
103 result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
104 if (result != SL_RESULT_SUCCESS) {
105 ALOGE("sl engine Realize failed with result %d", result);
106 return false;
107 }
108 (void)result;
109
110 // get the engine interface, which is needed in order to create other objects
111 result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
112 if (result != SL_RESULT_SUCCESS) {
113 ALOGE("sl engine GetInterface failed with result %d", result);
114 return false;
115 }
116 (void)result;
117
118 // create output mix
119 result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
120 if (result != SL_RESULT_SUCCESS) {
121 ALOGE("sl engine CreateOutputMix failed with result %d", result);
122 return false;
123 }
124 (void)result;
125
126 // realize the output mix
127 result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
128 if (result != SL_RESULT_SUCCESS) {
129 ALOGE("sl outputMix Realize failed with result %d", result);
130 return false;
131 }
132 (void)result;
133
134 return true;
135 }
136
137 // create buffer queue audio player
createBufferQueueAudioPlayer(const ChunkFormat * chunkFormat)138 bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
139 SLresult result;
140
141 // configure audio source
142 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
143
144 SLDataFormat_PCM format_pcm = {
145 SL_DATAFORMAT_PCM,
146 chunkFormat->num_channels,
147 chunkFormat->sample_rate * 1000, // convert to milliHz
148 chunkFormat->bits_per_sample,
149 16,
150 SL_SPEAKER_FRONT_CENTER,
151 SL_BYTEORDER_LITTLEENDIAN
152 };
153 SLDataSource audioSrc = {&loc_bufq, &format_pcm};
154
155 // configure audio sink
156 SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
157 SLDataSink audioSnk = {&loc_outmix, NULL};
158
159 // create audio player
160 const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
161 const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
162 result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
163 3, ids, req);
164 if (result != SL_RESULT_SUCCESS) {
165 ALOGE("sl CreateAudioPlayer failed with result %d", result);
166 return false;
167 }
168 (void)result;
169
170 // Use the System stream for boot sound playback.
171 SLAndroidConfigurationItf playerConfig;
172 result = (*bqPlayerObject)->GetInterface(bqPlayerObject,
173 SL_IID_ANDROIDCONFIGURATION, &playerConfig);
174 if (result != SL_RESULT_SUCCESS) {
175 ALOGE("config GetInterface failed with result %d", result);
176 return false;
177 }
178 SLint32 streamType = SL_ANDROID_STREAM_SYSTEM;
179 result = (*playerConfig)->SetConfiguration(playerConfig,
180 SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
181 if (result != SL_RESULT_SUCCESS) {
182 ALOGE("SetConfiguration failed with result %d", result);
183 return false;
184 }
185 // use normal performance mode as low latency is not needed. This is not mandatory so
186 // do not bail if we fail
187 SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE;
188 result = (*playerConfig)->SetConfiguration(
189 playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(SLuint32));
190 ALOGW_IF(result != SL_RESULT_SUCCESS,
191 "could not set performance mode on player, error %d", result);
192 (void)result;
193
194 // realize the player
195 result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
196 if (result != SL_RESULT_SUCCESS) {
197 ALOGE("sl player Realize failed with result %d", result);
198 return false;
199 }
200 (void)result;
201
202 // get the play interface
203 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
204 if (result != SL_RESULT_SUCCESS) {
205 ALOGE("sl player GetInterface failed with result %d", result);
206 return false;
207 }
208 (void)result;
209
210 // get the buffer queue interface
211 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
212 &bqPlayerBufferQueue);
213 if (result != SL_RESULT_SUCCESS) {
214 ALOGE("sl playberBufferQueue GetInterface failed with result %d", result);
215 return false;
216 }
217 (void)result;
218
219 // register callback on the buffer queue
220 result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
221 if (result != SL_RESULT_SUCCESS) {
222 ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
223 return false;
224 }
225 (void)result;
226
227 // get the volume interface
228 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
229 if (result != SL_RESULT_SUCCESS) {
230 ALOGE("sl volume GetInterface failed with result %d", result);
231 return false;
232 }
233 (void)result;
234
235 // set the player's state to playing
236 audioplay::setPlaying(true);
237 CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue);
238 return true;
239 }
240
parseClipBuf(const uint8_t * clipBuf,int clipBufSize,const ChunkFormat ** oChunkFormat,const uint8_t ** oSoundBuf,unsigned * oSoundBufSize)241 bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** oChunkFormat,
242 const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
243 *oSoundBuf = clipBuf;
244 *oSoundBufSize = clipBufSize;
245 *oChunkFormat = NULL;
246 const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
247 if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
248 (wavHeader->wave_id != ID_WAVE)) {
249 ALOGE("Error: audio file is not a riff/wave file\n");
250 return false;
251 }
252 *oSoundBuf += sizeof(*wavHeader);
253 *oSoundBufSize -= sizeof(*wavHeader);
254
255 while (true) {
256 const ChunkHeader* chunkHeader = (const ChunkHeader*)*oSoundBuf;
257 if (*oSoundBufSize < sizeof(*chunkHeader)) {
258 ALOGE("EOF reading chunk headers");
259 return false;
260 }
261
262 *oSoundBuf += sizeof(*chunkHeader);
263 *oSoundBufSize -= sizeof(*chunkHeader);
264
265 bool endLoop = false;
266 switch (chunkHeader->id) {
267 case ID_FMT:
268 *oChunkFormat = (const ChunkFormat*)*oSoundBuf;
269 *oSoundBuf += chunkHeader->sz;
270 *oSoundBufSize -= chunkHeader->sz;
271 break;
272 case ID_DATA:
273 /* Stop looking for chunks */
274 *oSoundBufSize = chunkHeader->sz;
275 endLoop = true;
276 break;
277 default:
278 /* Unknown chunk, skip bytes */
279 *oSoundBuf += chunkHeader->sz;
280 *oSoundBufSize -= chunkHeader->sz;
281 }
282 if (endLoop) {
283 break;
284 }
285 }
286
287 if (*oChunkFormat == NULL) {
288 ALOGE("format not found in WAV file");
289 return false;
290 }
291 return true;
292 }
293
294 } // namespace
295
create(const uint8_t * exampleClipBuf,int exampleClipBufSize)296 bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
297 if (!createEngine()) {
298 return false;
299 }
300
301 // Parse the example clip.
302 const ChunkFormat* chunkFormat;
303 const uint8_t* soundBuf;
304 unsigned soundBufSize;
305 if (!parseClipBuf(exampleClipBuf, exampleClipBufSize, &chunkFormat, &soundBuf, &soundBufSize)) {
306 return false;
307 }
308
309 // Initialize the BufferQueue based on this clip's format.
310 if (!createBufferQueueAudioPlayer(chunkFormat)) {
311 return false;
312 }
313 return true;
314 }
315
playClip(const uint8_t * buf,int size)316 bool playClip(const uint8_t* buf, int size) {
317 // Parse the WAV header
318 const ChunkFormat* chunkFormat;
319 if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) {
320 return false;
321 }
322
323 if (!hasPlayer()) {
324 ALOGD("cannot play clip %p without a player", buf);
325 return false;
326 }
327
328 CHATTY("playClip on player %p: buf=%p size=%d nextSize %d",
329 bqPlayerBufferQueue, buf, size, nextSize);
330
331 if (nextSize > 0) {
332 // here we only enqueue one buffer because it is a long clip,
333 // but for streaming playback we would typically enqueue at least 2 buffers to start
334 SLresult result;
335 result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
336 if (SL_RESULT_SUCCESS != result) {
337 return false;
338 }
339 audioplay::setPlaying(true);
340 }
341
342 return true;
343 }
344
345 // set the playing state for the buffer queue audio player
setPlaying(bool isPlaying)346 void setPlaying(bool isPlaying) {
347 if (!hasPlayer()) return;
348
349 SLresult result;
350
351 if (NULL != bqPlayerPlay) {
352 // set the player's state
353 result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
354 isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
355 }
356
357 }
358
destroy()359 void destroy() {
360 // destroy buffer queue audio player object, and invalidate all associated interfaces
361 if (bqPlayerObject != NULL) {
362 CHATTY("destroying audio player");
363 (*bqPlayerObject)->Destroy(bqPlayerObject);
364 bqPlayerObject = NULL;
365 bqPlayerPlay = NULL;
366 bqPlayerBufferQueue = NULL;
367 bqPlayerMuteSolo = NULL;
368 bqPlayerVolume = NULL;
369 }
370
371 // destroy output mix object, and invalidate all associated interfaces
372 if (outputMixObject != NULL) {
373 (*outputMixObject)->Destroy(outputMixObject);
374 outputMixObject = NULL;
375 }
376
377 // destroy engine object, and invalidate all associated interfaces
378 if (engineObject != NULL) {
379 CHATTY("destroying audio engine");
380 (*engineObject)->Destroy(engineObject);
381 engineObject = NULL;
382 engineEngine = NULL;
383 }
384 }
385
386 } // namespace audioplay
387