• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define CHATTY ALOGD
21 #define LOG_TAG "audioplay"
22 
23 #include "audioplay.h"
24 
25 #include <string.h>
26 
27 #include <utils/Log.h>
28 #include <utils/threads.h>
29 
30 // for native audio
31 #include <SLES/OpenSLES.h>
32 #include <SLES/OpenSLES_Android.h>
33 
34 #include "BootAnimationUtil.h"
35 
36 namespace audioplay {
37 namespace {
38 
39 using namespace android;
40 
41 // engine interfaces
42 static SLObjectItf engineObject = nullptr;
43 static SLEngineItf engineEngine;
44 
45 // output mix interfaces
46 static SLObjectItf outputMixObject = nullptr;
47 
48 // buffer queue player interfaces
49 static SLObjectItf bqPlayerObject = nullptr;
50 static SLPlayItf bqPlayerPlay;
51 static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
52 static SLMuteSoloItf bqPlayerMuteSolo;
53 static SLVolumeItf bqPlayerVolume;
54 
55 // pointer and size of the next player buffer to enqueue, and number of remaining buffers
56 static const uint8_t* nextBuffer;
57 static unsigned nextSize;
58 
59 static const uint32_t ID_RIFF = 0x46464952;
60 static const uint32_t ID_WAVE = 0x45564157;
61 static const uint32_t ID_FMT  = 0x20746d66;
62 static const uint32_t ID_DATA = 0x61746164;
63 
64 struct RiffWaveHeader {
65     uint32_t riff_id;
66     uint32_t riff_sz;
67     uint32_t wave_id;
68 };
69 
70 struct ChunkHeader {
71     uint32_t id;
72     uint32_t sz;
73 };
74 
75 struct ChunkFormat {
76     uint16_t audio_format;
77     uint16_t num_channels;
78     uint32_t sample_rate;
79     uint32_t byte_rate;
80     uint16_t block_align;
81     uint16_t bits_per_sample;
82 };
83 
84 // this callback handler is called every time a buffer finishes playing
bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq,void * context)85 void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
86     (void)bq;
87     (void)context;
88     audioplay::setPlaying(false);
89 }
90 
hasPlayer()91 bool hasPlayer() {
92     return (engineObject != nullptr && bqPlayerObject != nullptr);
93 }
94 
95 // create the engine and output mix objects
createEngine()96 bool createEngine() {
97     SLresult result;
98 
99     // create engine
100     result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
101     if (result != SL_RESULT_SUCCESS) {
102         ALOGE("slCreateEngine failed with result %d", result);
103         return false;
104     }
105     (void)result;
106 
107     // realize the engine
108     result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
109     if (result != SL_RESULT_SUCCESS) {
110         ALOGE("sl engine Realize failed with result %d", result);
111         return false;
112     }
113     (void)result;
114 
115     // get the engine interface, which is needed in order to create other objects
116     result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
117     if (result != SL_RESULT_SUCCESS) {
118         ALOGE("sl engine GetInterface failed with result %d", result);
119         return false;
120     }
121     (void)result;
122 
123     // create output mix
124     result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, nullptr, nullptr);
125     if (result != SL_RESULT_SUCCESS) {
126         ALOGE("sl engine CreateOutputMix failed with result %d", result);
127         return false;
128     }
129     (void)result;
130 
131     // realize the output mix
132     result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
133     if (result != SL_RESULT_SUCCESS) {
134         ALOGE("sl outputMix Realize failed with result %d", result);
135         return false;
136     }
137     (void)result;
138 
139     return true;
140 }
141 
142 // create buffer queue audio player
createBufferQueueAudioPlayer(const ChunkFormat * chunkFormat)143 bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
144     SLresult result;
145 
146     // configure audio source
147     SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
148 
149     // Determine channelMask from num_channels
150     SLuint32 channelMask;
151     switch (chunkFormat->num_channels) {
152         case 1:
153             channelMask = SL_SPEAKER_FRONT_CENTER;
154             break;
155         case 2:
156             channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
157             break;
158         default:
159             // Default of 0 will derive mask from num_channels and log a warning.
160             channelMask = 0;
161     }
162 
163     SLDataFormat_PCM format_pcm = {
164         SL_DATAFORMAT_PCM,
165         chunkFormat->num_channels,
166         chunkFormat->sample_rate * 1000,  // convert to milliHz
167         chunkFormat->bits_per_sample,
168         16,
169         channelMask,
170         SL_BYTEORDER_LITTLEENDIAN
171     };
172     SLDataSource audioSrc = {&loc_bufq, &format_pcm};
173 
174     // configure audio sink
175     SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
176     SLDataSink audioSnk = {&loc_outmix, nullptr};
177 
178     // create audio player
179     const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
180     const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
181     result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
182             3, ids, req);
183     if (result != SL_RESULT_SUCCESS) {
184         ALOGE("sl CreateAudioPlayer failed with result %d", result);
185         return false;
186     }
187     (void)result;
188 
189     // Use the System stream for boot sound playback.
190     SLAndroidConfigurationItf playerConfig;
191     result = (*bqPlayerObject)->GetInterface(bqPlayerObject,
192         SL_IID_ANDROIDCONFIGURATION, &playerConfig);
193     if (result != SL_RESULT_SUCCESS) {
194         ALOGE("config GetInterface failed with result %d", result);
195         return false;
196     }
197     SLint32 streamType = SL_ANDROID_STREAM_SYSTEM;
198     result = (*playerConfig)->SetConfiguration(playerConfig,
199         SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
200     if (result != SL_RESULT_SUCCESS) {
201         ALOGE("SetConfiguration failed with result %d", result);
202         return false;
203     }
204     // use normal performance mode as low latency is not needed. This is not mandatory so
205     // do not bail if we fail
206     SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE;
207     result = (*playerConfig)->SetConfiguration(
208            playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(SLuint32));
209     ALOGW_IF(result != SL_RESULT_SUCCESS,
210             "could not set performance mode on player, error %d", result);
211     (void)result;
212 
213     // realize the player
214     result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
215     if (result != SL_RESULT_SUCCESS) {
216         ALOGE("sl player Realize failed with result %d", result);
217         return false;
218     }
219     (void)result;
220 
221     // get the play interface
222     result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
223     if (result != SL_RESULT_SUCCESS) {
224         ALOGE("sl player GetInterface failed with result %d", result);
225         return false;
226     }
227     (void)result;
228 
229     // get the buffer queue interface
230     result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
231             &bqPlayerBufferQueue);
232     if (result != SL_RESULT_SUCCESS) {
233         ALOGE("sl playberBufferQueue GetInterface failed with result %d", result);
234         return false;
235     }
236     (void)result;
237 
238     // register callback on the buffer queue
239     result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
240     if (result != SL_RESULT_SUCCESS) {
241         ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
242         return false;
243     }
244     (void)result;
245 
246     // get the volume interface
247     result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
248     if (result != SL_RESULT_SUCCESS) {
249         ALOGE("sl volume GetInterface failed with result %d", result);
250         return false;
251     }
252     (void)result;
253 
254     // set the player's state to playing
255     audioplay::setPlaying(true);
256     CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue);
257     return true;
258 }
259 
parseClipBuf(const uint8_t * clipBuf,int clipBufSize,const ChunkFormat ** oChunkFormat,const uint8_t ** oSoundBuf,unsigned * oSoundBufSize)260 bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** oChunkFormat,
261                   const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
262     *oSoundBuf = clipBuf;
263     *oSoundBufSize = clipBufSize;
264     *oChunkFormat = nullptr;
265     const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
266     if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
267         (wavHeader->wave_id != ID_WAVE)) {
268         ALOGE("Error: audio file is not a riff/wave file\n");
269         return false;
270     }
271     *oSoundBuf += sizeof(*wavHeader);
272     *oSoundBufSize -= sizeof(*wavHeader);
273 
274     while (true) {
275         const ChunkHeader* chunkHeader = (const ChunkHeader*)*oSoundBuf;
276         if (*oSoundBufSize < sizeof(*chunkHeader)) {
277             ALOGE("EOF reading chunk headers");
278             return false;
279         }
280 
281         *oSoundBuf += sizeof(*chunkHeader);
282         *oSoundBufSize -= sizeof(*chunkHeader);
283 
284         bool endLoop = false;
285         switch (chunkHeader->id) {
286             case ID_FMT:
287                 *oChunkFormat = (const ChunkFormat*)*oSoundBuf;
288                 *oSoundBuf += chunkHeader->sz;
289                 *oSoundBufSize -= chunkHeader->sz;
290                 break;
291             case ID_DATA:
292                 /* Stop looking for chunks */
293                 *oSoundBufSize = chunkHeader->sz;
294                 endLoop = true;
295                 break;
296             default:
297                 /* Unknown chunk, skip bytes */
298                 *oSoundBuf += chunkHeader->sz;
299                 *oSoundBufSize -= chunkHeader->sz;
300         }
301         if (endLoop) {
302             break;
303         }
304     }
305 
306     if (*oChunkFormat == nullptr) {
307         ALOGE("format not found in WAV file");
308         return false;
309     }
310     return true;
311 }
312 
313 class InitAudioThread : public Thread {
314 public:
InitAudioThread(uint8_t * exampleAudioData,int exampleAudioLength)315     InitAudioThread(uint8_t* exampleAudioData, int exampleAudioLength)
316         : Thread(false),
317           mExampleAudioData(exampleAudioData),
318           mExampleAudioLength(exampleAudioLength) {}
319 private:
threadLoop()320     virtual bool threadLoop() {
321         audioplay::create(mExampleAudioData, mExampleAudioLength);
322         // Exit immediately
323         return false;
324     }
325 
326     uint8_t* mExampleAudioData;
327     int mExampleAudioLength;
328 };
329 
330 // Typedef to aid readability.
331 typedef android::BootAnimation::Animation Animation;
332 
333 class AudioAnimationCallbacks : public android::BootAnimation::Callbacks {
334 public:
init(const Vector<Animation::Part> & parts)335     void init(const Vector<Animation::Part>& parts) override {
336         const Animation::Part* partWithAudio = nullptr;
337         for (const Animation::Part& part : parts) {
338             if (part.audioData != nullptr) {
339                 partWithAudio = &part;
340                 break;
341             }
342         }
343 
344         if (partWithAudio == nullptr) {
345             return;
346         }
347 
348         ALOGD("found audio.wav, creating playback engine");
349         // The audioData is used to initialize the audio system. Different data
350         // can be played later for other parts BUT the assumption is that they
351         // will all be the same format and only the format of this audioData
352         // will work correctly.
353         initAudioThread = new InitAudioThread(partWithAudio->audioData,
354                 partWithAudio->audioLength);
355         initAudioThread->run("BootAnimation::InitAudioThread", PRIORITY_NORMAL);
356     };
357 
playPart(int partNumber,const Animation::Part & part,int playNumber)358     void playPart(int partNumber, const Animation::Part& part, int playNumber) override {
359         // only play audio file the first time we animate the part
360         if (playNumber == 0 && part.audioData && playSoundsAllowed()) {
361             ALOGD("playing clip for part%d, size=%d",
362                   partNumber, part.audioLength);
363             // Block until the audio engine is finished initializing.
364             if (initAudioThread != nullptr) {
365                 initAudioThread->join();
366             }
367             audioplay::playClip(part.audioData, part.audioLength);
368         }
369     };
370 
shutdown()371     void shutdown() override {
372         // we've finally played everything we're going to play
373         audioplay::setPlaying(false);
374         audioplay::destroy();
375     };
376 
377 private:
378     sp<InitAudioThread> initAudioThread = nullptr;
379 };
380 
381 } // namespace
382 
create(const uint8_t * exampleClipBuf,int exampleClipBufSize)383 bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
384     if (!createEngine()) {
385         return false;
386     }
387 
388     // Parse the example clip.
389     const ChunkFormat* chunkFormat;
390     const uint8_t* soundBuf;
391     unsigned soundBufSize;
392     if (!parseClipBuf(exampleClipBuf, exampleClipBufSize, &chunkFormat, &soundBuf, &soundBufSize)) {
393         return false;
394     }
395 
396     // Initialize the BufferQueue based on this clip's format.
397     if (!createBufferQueueAudioPlayer(chunkFormat)) {
398         return false;
399     }
400     return true;
401 }
402 
playClip(const uint8_t * buf,int size)403 bool playClip(const uint8_t* buf, int size) {
404     // Parse the WAV header
405     const ChunkFormat* chunkFormat;
406     if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) {
407         return false;
408     }
409 
410     if (!hasPlayer()) {
411         ALOGD("cannot play clip %p without a player", buf);
412         return false;
413     }
414 
415     CHATTY("playClip on player %p: buf=%p size=%d nextSize %d",
416            bqPlayerBufferQueue, buf, size, nextSize);
417 
418     if (nextSize > 0) {
419         // here we only enqueue one buffer because it is a long clip,
420         // but for streaming playback we would typically enqueue at least 2 buffers to start
421         SLresult result;
422         result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
423         if (SL_RESULT_SUCCESS != result) {
424             return false;
425         }
426         audioplay::setPlaying(true);
427     }
428 
429     return true;
430 }
431 
432 // set the playing state for the buffer queue audio player
setPlaying(bool isPlaying)433 void setPlaying(bool isPlaying) {
434     if (!hasPlayer()) return;
435 
436     SLresult result;
437 
438     if (nullptr != bqPlayerPlay) {
439         // set the player's state
440         result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
441             isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
442     }
443 
444 }
445 
destroy()446 void destroy() {
447     // destroy buffer queue audio player object, and invalidate all associated interfaces
448     if (bqPlayerObject != nullptr) {
449         CHATTY("destroying audio player");
450         (*bqPlayerObject)->Destroy(bqPlayerObject);
451         bqPlayerObject = nullptr;
452         bqPlayerPlay = nullptr;
453         bqPlayerBufferQueue = nullptr;
454         bqPlayerMuteSolo = nullptr;
455         bqPlayerVolume = nullptr;
456     }
457 
458     // destroy output mix object, and invalidate all associated interfaces
459     if (outputMixObject != nullptr) {
460         (*outputMixObject)->Destroy(outputMixObject);
461         outputMixObject = nullptr;
462     }
463 
464     // destroy engine object, and invalidate all associated interfaces
465     if (engineObject != nullptr) {
466         CHATTY("destroying audio engine");
467         (*engineObject)->Destroy(engineObject);
468         engineObject = nullptr;
469         engineEngine = nullptr;
470     }
471 }
472 
createAnimationCallbacks()473 sp<BootAnimation::Callbacks> createAnimationCallbacks() {
474   return new AudioAnimationCallbacks();
475 }
476 
477 }  // namespace audioplay
478