blob: c5e16c6b7deb68ccf51d8e73c4600dfee911c69b [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
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070020#define CHATTY ALOGD
Eric Laurent197e4792016-07-21 18:17:15 -070021#define LOG_TAG "audioplay"
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070022
Ed Coyne33f4b7d2018-04-10 13:39:09 -070023#include "audioplay.h"
24
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070025#include <string.h>
26
27#include <utils/Log.h>
Ed Coyne33f4b7d2018-04-10 13:39:09 -070028#include <utils/threads.h>
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070029
30// for native audio
31#include <SLES/OpenSLES.h>
32#include <SLES/OpenSLES_Android.h>
33
Ed Coyne33f4b7d2018-04-10 13:39:09 -070034#include "BootAnimationUtil.h"
35
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070036namespace audioplay {
37namespace {
38
Ed Coyne33f4b7d2018-04-10 13:39:09 -070039using namespace android;
40
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070041// engine interfaces
Yi Kong911ac232019-03-24 01:49:02 -070042static SLObjectItf engineObject = nullptr;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070043static SLEngineItf engineEngine;
44
45// output mix interfaces
Yi Kong911ac232019-03-24 01:49:02 -070046static SLObjectItf outputMixObject = nullptr;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070047
48// buffer queue player interfaces
Yi Kong911ac232019-03-24 01:49:02 -070049static SLObjectItf bqPlayerObject = nullptr;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070050static SLPlayItf bqPlayerPlay;
51static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
52static SLMuteSoloItf bqPlayerMuteSolo;
53static SLVolumeItf bqPlayerVolume;
54
55// pointer and size of the next player buffer to enqueue, and number of remaining buffers
56static const uint8_t* nextBuffer;
57static unsigned nextSize;
58
59static const uint32_t ID_RIFF = 0x46464952;
60static const uint32_t ID_WAVE = 0x45564157;
61static const uint32_t ID_FMT = 0x20746d66;
62static const uint32_t ID_DATA = 0x61746164;
63
64struct RiffWaveHeader {
65 uint32_t riff_id;
66 uint32_t riff_sz;
67 uint32_t wave_id;
68};
69
70struct ChunkHeader {
71 uint32_t id;
72 uint32_t sz;
73};
74
75struct 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
85void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
86 (void)bq;
87 (void)context;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070088 audioplay::setPlaying(false);
89}
90
91bool hasPlayer() {
Yi Kong911ac232019-03-24 01:49:02 -070092 return (engineObject != nullptr && bqPlayerObject != nullptr);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070093}
94
95// create the engine and output mix objects
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -040096bool createEngine() {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -070097 SLresult result;
98
99 // create engine
Yi Kong911ac232019-03-24 01:49:02 -0700100 result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400101 if (result != SL_RESULT_SUCCESS) {
102 ALOGE("slCreateEngine failed with result %d", result);
103 return false;
104 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700105 (void)result;
106
107 // realize the engine
108 result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400109 if (result != SL_RESULT_SUCCESS) {
110 ALOGE("sl engine Realize failed with result %d", result);
111 return false;
112 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700113 (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);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400117 if (result != SL_RESULT_SUCCESS) {
118 ALOGE("sl engine GetInterface failed with result %d", result);
119 return false;
120 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700121 (void)result;
122
Eric Laurent197e4792016-07-21 18:17:15 -0700123 // create output mix
Yi Kong911ac232019-03-24 01:49:02 -0700124 result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, nullptr, nullptr);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400125 if (result != SL_RESULT_SUCCESS) {
126 ALOGE("sl engine CreateOutputMix failed with result %d", result);
127 return false;
128 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700129 (void)result;
130
131 // realize the output mix
132 result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400133 if (result != SL_RESULT_SUCCESS) {
134 ALOGE("sl outputMix Realize failed with result %d", result);
135 return false;
136 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700137 (void)result;
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400138
139 return true;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700140}
141
142// create buffer queue audio player
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400143bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700144 SLresult result;
145
146 // configure audio source
147 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
148
Geoffrey Pitschdb908972016-08-24 14:35:09 -0400149 // 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
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700163 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,
Geoffrey Pitschdb908972016-08-24 14:35:09 -0400169 channelMask,
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700170 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};
Yi Kong911ac232019-03-24 01:49:02 -0700176 SLDataSink audioSnk = {&loc_outmix, nullptr};
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700177
178 // create audio player
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400179 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};
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700181 result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400182 3, ids, req);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400183 if (result != SL_RESULT_SUCCESS) {
184 ALOGE("sl CreateAudioPlayer failed with result %d", result);
185 return false;
186 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700187 (void)result;
188
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400189 // 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 }
Eric Laurent197e4792016-07-21 18:17:15 -0700204 // 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;
Geoffrey Pitsch56133132016-07-15 10:50:04 -0400212
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700213 // realize the player
214 result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400215 if (result != SL_RESULT_SUCCESS) {
216 ALOGE("sl player Realize failed with result %d", result);
217 return false;
218 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700219 (void)result;
220
221 // get the play interface
222 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400223 if (result != SL_RESULT_SUCCESS) {
224 ALOGE("sl player GetInterface failed with result %d", result);
225 return false;
226 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700227 (void)result;
228
229 // get the buffer queue interface
230 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
231 &bqPlayerBufferQueue);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400232 if (result != SL_RESULT_SUCCESS) {
233 ALOGE("sl playberBufferQueue GetInterface failed with result %d", result);
234 return false;
235 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700236 (void)result;
237
238 // register callback on the buffer queue
Yi Kong911ac232019-03-24 01:49:02 -0700239 result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400240 if (result != SL_RESULT_SUCCESS) {
241 ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
242 return false;
243 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700244 (void)result;
245
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700246 // get the volume interface
247 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400248 if (result != SL_RESULT_SUCCESS) {
249 ALOGE("sl volume GetInterface failed with result %d", result);
250 return false;
251 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700252 (void)result;
253
254 // set the player's state to playing
255 audioplay::setPlaying(true);
256 CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue);
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400257 return true;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700258}
259
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400260bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** oChunkFormat,
261 const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
262 *oSoundBuf = clipBuf;
263 *oSoundBufSize = clipBufSize;
Yi Kong911ac232019-03-24 01:49:02 -0700264 *oChunkFormat = nullptr;
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400265 const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
266 if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700267 (wavHeader->wave_id != ID_WAVE)) {
268 ALOGE("Error: audio file is not a riff/wave file\n");
269 return false;
270 }
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400271 *oSoundBuf += sizeof(*wavHeader);
272 *oSoundBufSize -= sizeof(*wavHeader);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700273
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700274 while (true) {
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400275 const ChunkHeader* chunkHeader = (const ChunkHeader*)*oSoundBuf;
276 if (*oSoundBufSize < sizeof(*chunkHeader)) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700277 ALOGE("EOF reading chunk headers");
278 return false;
279 }
280
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400281 *oSoundBuf += sizeof(*chunkHeader);
282 *oSoundBufSize -= sizeof(*chunkHeader);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700283
284 bool endLoop = false;
285 switch (chunkHeader->id) {
286 case ID_FMT:
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400287 *oChunkFormat = (const ChunkFormat*)*oSoundBuf;
288 *oSoundBuf += chunkHeader->sz;
289 *oSoundBufSize -= chunkHeader->sz;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700290 break;
291 case ID_DATA:
292 /* Stop looking for chunks */
Eric Laurent197e4792016-07-21 18:17:15 -0700293 *oSoundBufSize = chunkHeader->sz;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700294 endLoop = true;
295 break;
296 default:
297 /* Unknown chunk, skip bytes */
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400298 *oSoundBuf += chunkHeader->sz;
299 *oSoundBufSize -= chunkHeader->sz;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700300 }
301 if (endLoop) {
302 break;
303 }
304 }
305
Yi Kong911ac232019-03-24 01:49:02 -0700306 if (*oChunkFormat == nullptr) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700307 ALOGE("format not found in WAV file");
308 return false;
309 }
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400310 return true;
311}
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700312
Ed Coyne33f4b7d2018-04-10 13:39:09 -0700313class InitAudioThread : public Thread {
314public:
315 InitAudioThread(uint8_t* exampleAudioData, int exampleAudioLength)
316 : Thread(false),
317 mExampleAudioData(exampleAudioData),
318 mExampleAudioLength(exampleAudioLength) {}
319private:
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.
331typedef android::BootAnimation::Animation Animation;
332
333class AudioAnimationCallbacks : public android::BootAnimation::Callbacks {
334public:
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
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
371 void shutdown() override {
372 // we've finally played everything we're going to play
373 audioplay::setPlaying(false);
374 audioplay::destroy();
375 };
376
377private:
378 sp<InitAudioThread> initAudioThread = nullptr;
379};
380
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400381} // namespace
382
383bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
384 if (!createEngine()) {
385 return false;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700386 }
387
Geoffrey Pitscha91a2d72016-07-12 14:46:19 -0400388 // 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
403bool 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 }
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700409
410 if (!hasPlayer()) {
411 ALOGD("cannot play clip %p without a player", buf);
412 return false;
413 }
414
Eric Laurent197e4792016-07-21 18:17:15 -0700415 CHATTY("playClip on player %p: buf=%p size=%d nextSize %d",
416 bqPlayerBufferQueue, buf, size, nextSize);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700417
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
433void setPlaying(bool isPlaying) {
434 if (!hasPlayer()) return;
435
436 SLresult result;
437
Yi Kong911ac232019-03-24 01:49:02 -0700438 if (nullptr != bqPlayerPlay) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700439 // set the player's state
440 result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
441 isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700442 }
443
444}
445
446void destroy() {
447 // destroy buffer queue audio player object, and invalidate all associated interfaces
Yi Kong911ac232019-03-24 01:49:02 -0700448 if (bqPlayerObject != nullptr) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700449 CHATTY("destroying audio player");
450 (*bqPlayerObject)->Destroy(bqPlayerObject);
Yi Kong911ac232019-03-24 01:49:02 -0700451 bqPlayerObject = nullptr;
452 bqPlayerPlay = nullptr;
453 bqPlayerBufferQueue = nullptr;
454 bqPlayerMuteSolo = nullptr;
455 bqPlayerVolume = nullptr;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700456 }
457
458 // destroy output mix object, and invalidate all associated interfaces
Yi Kong911ac232019-03-24 01:49:02 -0700459 if (outputMixObject != nullptr) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700460 (*outputMixObject)->Destroy(outputMixObject);
Yi Kong911ac232019-03-24 01:49:02 -0700461 outputMixObject = nullptr;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700462 }
463
464 // destroy engine object, and invalidate all associated interfaces
Yi Kong911ac232019-03-24 01:49:02 -0700465 if (engineObject != nullptr) {
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700466 CHATTY("destroying audio engine");
467 (*engineObject)->Destroy(engineObject);
Yi Kong911ac232019-03-24 01:49:02 -0700468 engineObject = nullptr;
469 engineEngine = nullptr;
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700470 }
471}
472
Ed Coyne33f4b7d2018-04-10 13:39:09 -0700473sp<BootAnimation::Callbacks> createAnimationCallbacks() {
474 return new AudioAnimationCallbacks();
475}
476
Geoffrey Pitschd6d9a1d2016-06-08 00:38:58 -0700477} // namespace audioplay