blob: c546072e733a17618ecaed19e6e5bca563ae65bc [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
Eric Laurent197e4792016-07-21 18:17:15 -070023#define LOG_TAG "audioplay"
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070024
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070025#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
33namespace audioplay {
34namespace {
35
36// engine interfaces
37static SLObjectItf engineObject = NULL;
38static SLEngineItf engineEngine;
39
40// output mix interfaces
41static SLObjectItf outputMixObject = NULL;
42
43// buffer queue player interfaces
44static SLObjectItf bqPlayerObject = NULL;
45static SLPlayItf bqPlayerPlay;
46static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
47static SLMuteSoloItf bqPlayerMuteSolo;
48static SLVolumeItf bqPlayerVolume;
49
50// pointer and size of the next player buffer to enqueue, and number of remaining buffers
51static const uint8_t* nextBuffer;
52static unsigned nextSize;
53
54static const uint32_t ID_RIFF = 0x46464952;
55static const uint32_t ID_WAVE = 0x45564157;
56static const uint32_t ID_FMT = 0x20746d66;
57static const uint32_t ID_DATA = 0x61746164;
58
59struct RiffWaveHeader {
60 uint32_t riff_id;
61 uint32_t riff_sz;
62 uint32_t wave_id;
63};
64
65struct ChunkHeader {
66 uint32_t id;
67 uint32_t sz;
68};
69
70struct 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
80void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
81 (void)bq;
82 (void)context;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070083 audioplay::setPlaying(false);
84}
85
86bool hasPlayer() {
87 return (engineObject != NULL && bqPlayerObject != NULL);
88}
89
90// create the engine and output mix objects
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -040091bool createEngine() {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070092 SLresult result;
93
94 // create engine
95 result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -040096 if (result != SL_RESULT_SUCCESS) {
97 ALOGE("slCreateEngine failed with result %d", result);
98 return false;
99 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700100 (void)result;
101
102 // realize the engine
103 result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400104 if (result != SL_RESULT_SUCCESS) {
105 ALOGE("sl engine Realize failed with result %d", result);
106 return false;
107 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700108 (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);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400112 if (result != SL_RESULT_SUCCESS) {
113 ALOGE("sl engine GetInterface failed with result %d", result);
114 return false;
115 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700116 (void)result;
117
Eric Laurent197e4792016-07-21 18:17:15 -0700118 // create output mix
119 result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400120 if (result != SL_RESULT_SUCCESS) {
121 ALOGE("sl engine CreateOutputMix failed with result %d", result);
122 return false;
123 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700124 (void)result;
125
126 // realize the output mix
127 result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400128 if (result != SL_RESULT_SUCCESS) {
129 ALOGE("sl outputMix Realize failed with result %d", result);
130 return false;
131 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700132 (void)result;
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400133
134 return true;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700135}
136
137// create buffer queue audio player
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400138bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700139 SLresult result;
140
141 // configure audio source
142 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
143
Geoffrey Pitschdb908972016-08-24 14:35:09 -0400144 // Determine channelMask from num_channels
145 SLuint32 channelMask;
146 switch (chunkFormat->num_channels) {
147 case 1:
148 channelMask = SL_SPEAKER_FRONT_CENTER;
149 break;
150 case 2:
151 channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
152 break;
153 default:
154 // Default of 0 will derive mask from num_channels and log a warning.
155 channelMask = 0;
156 }
157
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700158 SLDataFormat_PCM format_pcm = {
159 SL_DATAFORMAT_PCM,
160 chunkFormat->num_channels,
161 chunkFormat->sample_rate * 1000, // convert to milliHz
162 chunkFormat->bits_per_sample,
163 16,
Geoffrey Pitschdb908972016-08-24 14:35:09 -0400164 channelMask,
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700165 SL_BYTEORDER_LITTLEENDIAN
166 };
167 SLDataSource audioSrc = {&loc_bufq, &format_pcm};
168
169 // configure audio sink
170 SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
171 SLDataSink audioSnk = {&loc_outmix, NULL};
172
173 // create audio player
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400174 const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
175 const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700176 result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400177 3, ids, req);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400178 if (result != SL_RESULT_SUCCESS) {
179 ALOGE("sl CreateAudioPlayer failed with result %d", result);
180 return false;
181 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700182 (void)result;
183
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400184 // Use the System stream for boot sound playback.
185 SLAndroidConfigurationItf playerConfig;
186 result = (*bqPlayerObject)->GetInterface(bqPlayerObject,
187 SL_IID_ANDROIDCONFIGURATION, &playerConfig);
188 if (result != SL_RESULT_SUCCESS) {
189 ALOGE("config GetInterface failed with result %d", result);
190 return false;
191 }
192 SLint32 streamType = SL_ANDROID_STREAM_SYSTEM;
193 result = (*playerConfig)->SetConfiguration(playerConfig,
194 SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
195 if (result != SL_RESULT_SUCCESS) {
196 ALOGE("SetConfiguration failed with result %d", result);
197 return false;
198 }
Eric Laurent197e4792016-07-21 18:17:15 -0700199 // use normal performance mode as low latency is not needed. This is not mandatory so
200 // do not bail if we fail
201 SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE;
202 result = (*playerConfig)->SetConfiguration(
203 playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(SLuint32));
204 ALOGW_IF(result != SL_RESULT_SUCCESS,
205 "could not set performance mode on player, error %d", result);
206 (void)result;
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400207
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700208 // realize the player
209 result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400210 if (result != SL_RESULT_SUCCESS) {
211 ALOGE("sl player Realize failed with result %d", result);
212 return false;
213 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700214 (void)result;
215
216 // get the play interface
217 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400218 if (result != SL_RESULT_SUCCESS) {
219 ALOGE("sl player GetInterface failed with result %d", result);
220 return false;
221 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700222 (void)result;
223
224 // get the buffer queue interface
225 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
226 &bqPlayerBufferQueue);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400227 if (result != SL_RESULT_SUCCESS) {
228 ALOGE("sl playberBufferQueue GetInterface failed with result %d", result);
229 return false;
230 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700231 (void)result;
232
233 // register callback on the buffer queue
234 result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400235 if (result != SL_RESULT_SUCCESS) {
236 ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
237 return false;
238 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700239 (void)result;
240
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700241 // get the volume interface
242 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400243 if (result != SL_RESULT_SUCCESS) {
244 ALOGE("sl volume GetInterface failed with result %d", result);
245 return false;
246 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700247 (void)result;
248
249 // set the player's state to playing
250 audioplay::setPlaying(true);
251 CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400252 return true;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700253}
254
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400255bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** oChunkFormat,
256 const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
257 *oSoundBuf = clipBuf;
258 *oSoundBufSize = clipBufSize;
259 *oChunkFormat = NULL;
260 const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
261 if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700262 (wavHeader->wave_id != ID_WAVE)) {
263 ALOGE("Error: audio file is not a riff/wave file\n");
264 return false;
265 }
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400266 *oSoundBuf += sizeof(*wavHeader);
267 *oSoundBufSize -= sizeof(*wavHeader);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700268
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700269 while (true) {
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400270 const ChunkHeader* chunkHeader = (const ChunkHeader*)*oSoundBuf;
271 if (*oSoundBufSize < sizeof(*chunkHeader)) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700272 ALOGE("EOF reading chunk headers");
273 return false;
274 }
275
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400276 *oSoundBuf += sizeof(*chunkHeader);
277 *oSoundBufSize -= sizeof(*chunkHeader);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700278
279 bool endLoop = false;
280 switch (chunkHeader->id) {
281 case ID_FMT:
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400282 *oChunkFormat = (const ChunkFormat*)*oSoundBuf;
283 *oSoundBuf += chunkHeader->sz;
284 *oSoundBufSize -= chunkHeader->sz;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700285 break;
286 case ID_DATA:
287 /* Stop looking for chunks */
Eric Laurent197e4792016-07-21 18:17:15 -0700288 *oSoundBufSize = chunkHeader->sz;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700289 endLoop = true;
290 break;
291 default:
292 /* Unknown chunk, skip bytes */
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400293 *oSoundBuf += chunkHeader->sz;
294 *oSoundBufSize -= chunkHeader->sz;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700295 }
296 if (endLoop) {
297 break;
298 }
299 }
300
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400301 if (*oChunkFormat == NULL) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700302 ALOGE("format not found in WAV file");
303 return false;
304 }
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400305 return true;
306}
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700307
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400308} // namespace
309
310bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
311 if (!createEngine()) {
312 return false;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700313 }
314
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400315 // Parse the example clip.
316 const ChunkFormat* chunkFormat;
317 const uint8_t* soundBuf;
318 unsigned soundBufSize;
319 if (!parseClipBuf(exampleClipBuf, exampleClipBufSize, &chunkFormat, &soundBuf, &soundBufSize)) {
320 return false;
321 }
322
323 // Initialize the BufferQueue based on this clip's format.
324 if (!createBufferQueueAudioPlayer(chunkFormat)) {
325 return false;
326 }
327 return true;
328}
329
330bool playClip(const uint8_t* buf, int size) {
331 // Parse the WAV header
332 const ChunkFormat* chunkFormat;
333 if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) {
334 return false;
335 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700336
337 if (!hasPlayer()) {
338 ALOGD("cannot play clip %p without a player", buf);
339 return false;
340 }
341
Eric Laurent197e4792016-07-21 18:17:15 -0700342 CHATTY("playClip on player %p: buf=%p size=%d nextSize %d",
343 bqPlayerBufferQueue, buf, size, nextSize);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700344
345 if (nextSize > 0) {
346 // here we only enqueue one buffer because it is a long clip,
347 // but for streaming playback we would typically enqueue at least 2 buffers to start
348 SLresult result;
349 result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
350 if (SL_RESULT_SUCCESS != result) {
351 return false;
352 }
353 audioplay::setPlaying(true);
354 }
355
356 return true;
357}
358
359// set the playing state for the buffer queue audio player
360void setPlaying(bool isPlaying) {
361 if (!hasPlayer()) return;
362
363 SLresult result;
364
365 if (NULL != bqPlayerPlay) {
366 // set the player's state
367 result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
368 isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700369 }
370
371}
372
373void destroy() {
374 // destroy buffer queue audio player object, and invalidate all associated interfaces
375 if (bqPlayerObject != NULL) {
376 CHATTY("destroying audio player");
377 (*bqPlayerObject)->Destroy(bqPlayerObject);
378 bqPlayerObject = NULL;
379 bqPlayerPlay = NULL;
380 bqPlayerBufferQueue = NULL;
381 bqPlayerMuteSolo = NULL;
382 bqPlayerVolume = NULL;
383 }
384
385 // destroy output mix object, and invalidate all associated interfaces
386 if (outputMixObject != NULL) {
387 (*outputMixObject)->Destroy(outputMixObject);
388 outputMixObject = NULL;
389 }
390
391 // destroy engine object, and invalidate all associated interfaces
392 if (engineObject != NULL) {
393 CHATTY("destroying audio engine");
394 (*engineObject)->Destroy(engineObject);
395 engineObject = NULL;
396 engineEngine = NULL;
397 }
398}
399
400} // namespace audioplay