1d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch/*
2d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch * Copyright (C) 2016 The Android Open Source Project
3d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch *
4d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch * Licensed under the Apache License, Version 2.0 (the "License");
5d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch * you may not use this file except in compliance with the License.
6d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch * You may obtain a copy of the License at
7d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch *
8d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch *      http://www.apache.org/licenses/LICENSE-2.0
9d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch *
10d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch * Unless required by applicable law or agreed to in writing, software
11d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch * distributed under the License is distributed on an "AS IS" BASIS,
12d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch * See the License for the specific language governing permissions and
14d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch * limitations under the License.
15d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch *
16d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch */
17d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
18d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// cribbed from samples/native-audio
19d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
20d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch#include "audioplay.h"
21d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
22d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch#define CHATTY ALOGD
23197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent#define LOG_TAG "audioplay"
24d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
25d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch#include <string.h>
26d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
27d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch#include <utils/Log.h>
28d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
29d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// for native audio
30d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch#include <SLES/OpenSLES.h>
31d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch#include <SLES/OpenSLES_Android.h>
32d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
33d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschnamespace audioplay {
34d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschnamespace {
35d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
36d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// engine interfaces
37d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic SLObjectItf engineObject = NULL;
38d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic SLEngineItf engineEngine;
39d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
40d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// output mix interfaces
41d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic SLObjectItf outputMixObject = NULL;
42d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
43d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// buffer queue player interfaces
44d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic SLObjectItf bqPlayerObject = NULL;
45d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic SLPlayItf bqPlayerPlay;
46d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
47d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic SLMuteSoloItf bqPlayerMuteSolo;
48d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic SLVolumeItf bqPlayerVolume;
49d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
50d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// pointer and size of the next player buffer to enqueue, and number of remaining buffers
51d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic const uint8_t* nextBuffer;
52d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic unsigned nextSize;
53d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
54d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic const uint32_t ID_RIFF = 0x46464952;
55d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic const uint32_t ID_WAVE = 0x45564157;
56d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic const uint32_t ID_FMT  = 0x20746d66;
57d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstatic const uint32_t ID_DATA = 0x61746164;
58d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
59d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstruct RiffWaveHeader {
60d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint32_t riff_id;
61d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint32_t riff_sz;
62d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint32_t wave_id;
63d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch};
64d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
65d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstruct ChunkHeader {
66d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint32_t id;
67d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint32_t sz;
68d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch};
69d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
70d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschstruct ChunkFormat {
71d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint16_t audio_format;
72d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint16_t num_channels;
73d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint32_t sample_rate;
74d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint32_t byte_rate;
75d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint16_t block_align;
76d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    uint16_t bits_per_sample;
77d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch};
78d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
79d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// this callback handler is called every time a buffer finishes playing
80d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschvoid bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
81d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)bq;
82d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)context;
83d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    audioplay::setPlaying(false);
84d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch}
85d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
86d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschbool hasPlayer() {
87d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    return (engineObject != NULL && bqPlayerObject != NULL);
88d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch}
89d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
90d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// create the engine and output mix objects
91a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitschbool createEngine() {
92d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    SLresult result;
93d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
94d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // create engine
95d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
96a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
97a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("slCreateEngine failed with result %d", result);
98a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
99a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
100d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
101d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
102d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // realize the engine
103d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
104a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
105a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl engine Realize failed with result %d", result);
106a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
107a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
108d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
109d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
110d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // get the engine interface, which is needed in order to create other objects
111d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
112a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
113a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl engine GetInterface failed with result %d", result);
114a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
115a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
116d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
117d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
118197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent    // create output mix
119197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
120a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
121a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl engine CreateOutputMix failed with result %d", result);
122a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
123a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
124d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
125d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
126d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // realize the output mix
127d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
128a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
129a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl outputMix Realize failed with result %d", result);
130a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
131a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
132d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
133a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch
134a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    return true;
135d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch}
136d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
137d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// create buffer queue audio player
138a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitschbool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
139d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    SLresult result;
140d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
141d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // configure audio source
142d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
143d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
144db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch    // Determine channelMask from num_channels
145db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch    SLuint32 channelMask;
146db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch    switch (chunkFormat->num_channels) {
147db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch        case 1:
148db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch            channelMask = SL_SPEAKER_FRONT_CENTER;
149db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch            break;
150db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch        case 2:
151db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch            channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
152db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch            break;
153db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch        default:
154db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch            // Default of 0 will derive mask from num_channels and log a warning.
155db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch            channelMask = 0;
156db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch    }
157db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch
158d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    SLDataFormat_PCM format_pcm = {
159d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        SL_DATAFORMAT_PCM,
160d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        chunkFormat->num_channels,
161d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        chunkFormat->sample_rate * 1000,  // convert to milliHz
162d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        chunkFormat->bits_per_sample,
163d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        16,
164db90897b5fa23aa46ce50494e0fb4808948f4524Geoffrey Pitsch        channelMask,
165d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        SL_BYTEORDER_LITTLEENDIAN
166d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    };
167d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    SLDataSource audioSrc = {&loc_bufq, &format_pcm};
168d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
169d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // configure audio sink
170d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
171d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    SLDataSink audioSnk = {&loc_outmix, NULL};
172d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
173d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // create audio player
1745613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
1755613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
176d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
1775613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch            3, ids, req);
178a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
179a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl CreateAudioPlayer failed with result %d", result);
180a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
181a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
182d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
183d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
1845613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    // Use the System stream for boot sound playback.
1855613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    SLAndroidConfigurationItf playerConfig;
1865613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    result = (*bqPlayerObject)->GetInterface(bqPlayerObject,
1875613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch        SL_IID_ANDROIDCONFIGURATION, &playerConfig);
1885613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
1895613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch        ALOGE("config GetInterface failed with result %d", result);
1905613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch        return false;
1915613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    }
1925613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    SLint32 streamType = SL_ANDROID_STREAM_SYSTEM;
1935613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    result = (*playerConfig)->SetConfiguration(playerConfig,
1945613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch        SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
1955613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
1965613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch        ALOGE("SetConfiguration failed with result %d", result);
1975613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch        return false;
1985613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch    }
199197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent    // use normal performance mode as low latency is not needed. This is not mandatory so
200197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent    // do not bail if we fail
201197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent    SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_NONE;
202197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent    result = (*playerConfig)->SetConfiguration(
203197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent           playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(SLuint32));
204197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent    ALOGW_IF(result != SL_RESULT_SUCCESS,
205197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent            "could not set performance mode on player, error %d", result);
206197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent    (void)result;
2075613313533e28c1c95e91b52e9997d657bf2a479Geoffrey Pitsch
208d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // realize the player
209d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
210a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
211a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl player Realize failed with result %d", result);
212a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
213a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
214d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
215d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
216d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // get the play interface
217d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
218a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
219a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl player GetInterface failed with result %d", result);
220a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
221a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
222d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
223d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
224d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // get the buffer queue interface
225d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
226d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch            &bqPlayerBufferQueue);
227a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
228a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl playberBufferQueue GetInterface failed with result %d", result);
229a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
230a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
231d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
232d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
233d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // register callback on the buffer queue
234d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
235a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
236a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
237a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
238a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
239d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
240d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
241d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // get the volume interface
242d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
243a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (result != SL_RESULT_SUCCESS) {
244a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        ALOGE("sl volume GetInterface failed with result %d", result);
245a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
246a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
247d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    (void)result;
248d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
249d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // set the player's state to playing
250d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    audioplay::setPlaying(true);
251d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue);
252a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    return true;
253d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch}
254d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
255a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitschbool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** oChunkFormat,
256a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch                  const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
257a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    *oSoundBuf = clipBuf;
258a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    *oSoundBufSize = clipBufSize;
259a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    *oChunkFormat = NULL;
260a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
261a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
262d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        (wavHeader->wave_id != ID_WAVE)) {
263d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        ALOGE("Error: audio file is not a riff/wave file\n");
264d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        return false;
265d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
266a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    *oSoundBuf += sizeof(*wavHeader);
267a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    *oSoundBufSize -= sizeof(*wavHeader);
268d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
269d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    while (true) {
270a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        const ChunkHeader* chunkHeader = (const ChunkHeader*)*oSoundBuf;
271a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        if (*oSoundBufSize < sizeof(*chunkHeader)) {
272d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch            ALOGE("EOF reading chunk headers");
273d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch            return false;
274d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        }
275d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
276a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        *oSoundBuf += sizeof(*chunkHeader);
277a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        *oSoundBufSize -= sizeof(*chunkHeader);
278d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
279d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        bool endLoop = false;
280d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        switch (chunkHeader->id) {
281d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch            case ID_FMT:
282a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch                *oChunkFormat = (const ChunkFormat*)*oSoundBuf;
283a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch                *oSoundBuf += chunkHeader->sz;
284a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch                *oSoundBufSize -= chunkHeader->sz;
285d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch                break;
286d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch            case ID_DATA:
287d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch                /* Stop looking for chunks */
288197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent                *oSoundBufSize = chunkHeader->sz;
289d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch                endLoop = true;
290d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch                break;
291d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch            default:
292d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch                /* Unknown chunk, skip bytes */
293a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch                *oSoundBuf += chunkHeader->sz;
294a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch                *oSoundBufSize -= chunkHeader->sz;
295d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        }
296d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        if (endLoop) {
297d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch            break;
298d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        }
299d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
300d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
301a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (*oChunkFormat == NULL) {
302d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        ALOGE("format not found in WAV file");
303d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        return false;
304d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
305a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    return true;
306a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch}
307d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
308a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch} // namespace
309a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch
310a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitschbool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
311a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (!createEngine()) {
312a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
313d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
314d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
315a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    // Parse the example clip.
316a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    const ChunkFormat* chunkFormat;
317a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    const uint8_t* soundBuf;
318a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    unsigned soundBufSize;
319a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (!parseClipBuf(exampleClipBuf, exampleClipBufSize, &chunkFormat, &soundBuf, &soundBufSize)) {
320a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
321a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
322a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch
323a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    // Initialize the BufferQueue based on this clip's format.
324a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (!createBufferQueueAudioPlayer(chunkFormat)) {
325a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
326a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
327a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    return true;
328a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch}
329a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch
330a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitschbool playClip(const uint8_t* buf, int size) {
331a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    // Parse the WAV header
332a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    const ChunkFormat* chunkFormat;
333a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) {
334a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch        return false;
335a91a2d737586ebd0040129333055d8093899751bGeoffrey Pitsch    }
336d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
337d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    if (!hasPlayer()) {
338d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        ALOGD("cannot play clip %p without a player", buf);
339d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        return false;
340d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
341d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
342197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent    CHATTY("playClip on player %p: buf=%p size=%d nextSize %d",
343197e47979a5c76d817d40039678afdd4f68c2a27Eric Laurent           bqPlayerBufferQueue, buf, size, nextSize);
344d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
345d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    if (nextSize > 0) {
346d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        // here we only enqueue one buffer because it is a long clip,
347d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        // but for streaming playback we would typically enqueue at least 2 buffers to start
348d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        SLresult result;
349d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
350d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        if (SL_RESULT_SUCCESS != result) {
351d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch            return false;
352d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        }
353d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        audioplay::setPlaying(true);
354d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
355d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
356d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    return true;
357d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch}
358d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
359d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch// set the playing state for the buffer queue audio player
360d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschvoid setPlaying(bool isPlaying) {
361d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    if (!hasPlayer()) return;
362d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
363d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    SLresult result;
364d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
365d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    if (NULL != bqPlayerPlay) {
366d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        // set the player's state
367d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
368d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch            isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
369d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
370d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
371d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch}
372d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
373d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitschvoid destroy() {
374d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // destroy buffer queue audio player object, and invalidate all associated interfaces
375d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    if (bqPlayerObject != NULL) {
376d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        CHATTY("destroying audio player");
377d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        (*bqPlayerObject)->Destroy(bqPlayerObject);
378d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        bqPlayerObject = NULL;
379d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        bqPlayerPlay = NULL;
380d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        bqPlayerBufferQueue = NULL;
381d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        bqPlayerMuteSolo = NULL;
382d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        bqPlayerVolume = NULL;
383d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
384d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
385d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // destroy output mix object, and invalidate all associated interfaces
386d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    if (outputMixObject != NULL) {
387d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        (*outputMixObject)->Destroy(outputMixObject);
388d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        outputMixObject = NULL;
389d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
390d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
391d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    // destroy engine object, and invalidate all associated interfaces
392d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    if (engineObject != NULL) {
393d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        CHATTY("destroying audio engine");
394d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        (*engineObject)->Destroy(engineObject);
395d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        engineObject = NULL;
396d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch        engineEngine = NULL;
397d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch    }
398d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch}
399d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch
400d6d9a1d0b9cf6fa740d9fe410015b094475c5a4cGeoffrey Pitsch}  // namespace audioplay
401