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