1/*
2 * Copyright (C) 2014 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#define LOG_NDEBUG 0
18#define LOG_TAG "BootAnim_AudioPlayer"
19
20#include "AudioPlayer.h"
21
22#include <androidfw/ZipFileRO.h>
23#include <tinyalsa/asoundlib.h>
24#include <utils/Log.h>
25#include <utils/String8.h>
26
27#define ID_RIFF 0x46464952
28#define ID_WAVE 0x45564157
29#define ID_FMT  0x20746d66
30#define ID_DATA 0x61746164
31
32// Maximum line length for audio_conf.txt
33// We only accept lines less than this length to avoid overflows using sscanf()
34#define MAX_LINE_LENGTH 1024
35
36struct riff_wave_header {
37    uint32_t riff_id;
38    uint32_t riff_sz;
39    uint32_t wave_id;
40};
41
42struct chunk_header {
43    uint32_t id;
44    uint32_t sz;
45};
46
47struct chunk_fmt {
48    uint16_t audio_format;
49    uint16_t num_channels;
50    uint32_t sample_rate;
51    uint32_t byte_rate;
52    uint16_t block_align;
53    uint16_t bits_per_sample;
54};
55
56
57namespace android {
58
59AudioPlayer::AudioPlayer()
60    :   mCard(-1),
61        mDevice(-1),
62        mPeriodSize(0),
63        mPeriodCount(0),
64        mCurrentFile(NULL)
65{
66}
67
68AudioPlayer::~AudioPlayer() {
69}
70
71static bool setMixerValue(struct mixer* mixer, const char* name, const char* values)
72{
73    if (!mixer) {
74        ALOGE("no mixer in setMixerValue");
75        return false;
76    }
77    struct mixer_ctl *ctl = mixer_get_ctl_by_name(mixer, name);
78    if (!ctl) {
79        ALOGE("mixer_get_ctl_by_name failed for %s", name);
80        return false;
81    }
82
83    enum mixer_ctl_type type = mixer_ctl_get_type(ctl);
84    int numValues = mixer_ctl_get_num_values(ctl);
85    int intValue;
86    char stringValue[MAX_LINE_LENGTH];
87
88    for (int i = 0; i < numValues && values; i++) {
89        // strip leading space
90        while (*values == ' ') values++;
91        if (*values == 0) break;
92
93        switch (type) {
94            case MIXER_CTL_TYPE_BOOL:
95            case MIXER_CTL_TYPE_INT:
96                if (sscanf(values, "%d", &intValue) == 1) {
97                    if (mixer_ctl_set_value(ctl, i, intValue) != 0) {
98                        ALOGE("mixer_ctl_set_value failed for %s %d", name, intValue);
99                    }
100                } else {
101                    ALOGE("Could not parse %s as int for %d", intValue, name);
102                }
103                break;
104            case MIXER_CTL_TYPE_ENUM:
105                if (sscanf(values, "%s", stringValue) == 1) {
106                    if (mixer_ctl_set_enum_by_string(ctl, stringValue) != 0) {
107                        ALOGE("mixer_ctl_set_enum_by_string failed for %s %%s", name, stringValue);
108                    }
109                } else {
110                    ALOGE("Could not parse %s as enum for %d", stringValue, name);
111                }
112                break;
113            default:
114                ALOGE("unsupported mixer type %d for %s", type, name);
115                break;
116        }
117
118        values = strchr(values, ' ');
119    }
120
121    return true;
122}
123
124
125/*
126 * Parse the audio configuration file.
127 * The file is named audio_conf.txt and must begin with the following header:
128 *
129 * card=<ALSA card number>
130 * device=<ALSA device number>
131 * period_size=<period size>
132 * period_count=<period count>
133 *
134 * This header is followed by zero or more mixer settings, each with the format:
135 * mixer "<name>" = <value list>
136 * Since mixer names can contain spaces, the name must be enclosed in double quotes.
137 * The values in the value list can be integers, booleans (represented by 0 or 1)
138 * or strings for enum values.
139 */
140bool AudioPlayer::init(const char* config)
141{
142    int tempInt;
143    struct mixer* mixer = NULL;
144    char    name[MAX_LINE_LENGTH];
145
146    for (;;) {
147        const char* endl = strstr(config, "\n");
148        if (!endl) break;
149        String8 line(config, endl - config);
150        if (line.length() >= MAX_LINE_LENGTH) {
151            ALOGE("Line too long in audio_conf.txt");
152            return false;
153        }
154        const char* l = line.string();
155
156        if (sscanf(l, "card=%d", &tempInt) == 1) {
157            ALOGD("card=%d", tempInt);
158            mCard = tempInt;
159
160            mixer = mixer_open(mCard);
161            if (!mixer) {
162                ALOGE("could not open mixer for card %d", mCard);
163                return false;
164            }
165        } else if (sscanf(l, "device=%d", &tempInt) == 1) {
166            ALOGD("device=%d", tempInt);
167            mDevice = tempInt;
168        } else if (sscanf(l, "period_size=%d", &tempInt) == 1) {
169            ALOGD("period_size=%d", tempInt);
170            mPeriodSize = tempInt;
171        } else if (sscanf(l, "period_count=%d", &tempInt) == 1) {
172            ALOGD("period_count=%d", tempInt);
173            mPeriodCount = tempInt;
174        } else if (sscanf(l, "mixer \"%[0-9a-zA-Z _]s\"", name) == 1) {
175            const char* values = strchr(l, '=');
176            if (values) {
177                values++;   // skip '='
178                ALOGD("name: \"%s\" = %s", name, values);
179                setMixerValue(mixer, name, values);
180            } else {
181                ALOGE("values missing for name: \"%s\"", name);
182            }
183        }
184        config = ++endl;
185    }
186
187    mixer_close(mixer);
188
189    if (mCard >= 0 && mDevice >= 0) {
190        return true;
191    }
192
193    return false;
194}
195
196void AudioPlayer::playFile(struct FileMap* fileMap) {
197    // stop any currently playing sound
198    requestExitAndWait();
199
200    mCurrentFile = fileMap;
201    run("bootanim audio", PRIORITY_URGENT_AUDIO);
202}
203
204bool AudioPlayer::threadLoop()
205{
206    struct pcm_config config;
207    struct pcm *pcm = NULL;
208    bool moreChunks = true;
209    const struct chunk_fmt* chunkFmt = NULL;
210    void* buffer = NULL;
211    int bufferSize;
212    const uint8_t* wavData;
213    size_t wavLength;
214    const struct riff_wave_header* wavHeader;
215
216    if (mCurrentFile == NULL) {
217        ALOGE("mCurrentFile is NULL");
218        return false;
219     }
220
221    wavData = (const uint8_t *)mCurrentFile->getDataPtr();
222    if (!wavData) {
223        ALOGE("Could not access WAV file data");
224        goto exit;
225    }
226    wavLength = mCurrentFile->getDataLength();
227
228    wavHeader = (const struct riff_wave_header *)wavData;
229    if (wavLength < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
230        (wavHeader->wave_id != ID_WAVE)) {
231        ALOGE("Error: audio file is not a riff/wave file\n");
232        goto exit;
233    }
234    wavData += sizeof(*wavHeader);
235    wavLength -= sizeof(*wavHeader);
236
237    do {
238        const struct chunk_header* chunkHeader = (const struct chunk_header*)wavData;
239        if (wavLength < sizeof(*chunkHeader)) {
240            ALOGE("EOF reading chunk headers");
241            goto exit;
242        }
243
244        wavData += sizeof(*chunkHeader);
245        wavLength -=  sizeof(*chunkHeader);
246
247        switch (chunkHeader->id) {
248            case ID_FMT:
249                chunkFmt = (const struct chunk_fmt *)wavData;
250                wavData += chunkHeader->sz;
251                wavLength -= chunkHeader->sz;
252                break;
253            case ID_DATA:
254                /* Stop looking for chunks */
255                moreChunks = 0;
256                break;
257            default:
258                /* Unknown chunk, skip bytes */
259                wavData += chunkHeader->sz;
260                wavLength -= chunkHeader->sz;
261        }
262    } while (moreChunks);
263
264    if (!chunkFmt) {
265        ALOGE("format not found in WAV file");
266        goto exit;
267    }
268
269
270    memset(&config, 0, sizeof(config));
271    config.channels = chunkFmt->num_channels;
272    config.rate = chunkFmt->sample_rate;
273    config.period_size = mPeriodSize;
274    config.period_count = mPeriodCount;
275    config.start_threshold = mPeriodSize / 4;
276    config.stop_threshold = INT_MAX;
277    config.avail_min = config.start_threshold;
278    if (chunkFmt->bits_per_sample != 16) {
279        ALOGE("only 16 bit WAV files are supported");
280        goto exit;
281    }
282    config.format = PCM_FORMAT_S16_LE;
283
284    pcm = pcm_open(mCard, mDevice, PCM_OUT, &config);
285    if (!pcm || !pcm_is_ready(pcm)) {
286        ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(pcm));
287        goto exit;
288    }
289
290    bufferSize = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
291
292    while (wavLength > 0) {
293        if (exitPending()) goto exit;
294        size_t count = bufferSize;
295        if (count > wavLength)
296            count = wavLength;
297
298        if (pcm_write(pcm, wavData, count)) {
299            ALOGE("pcm_write failed (%s)", pcm_get_error(pcm));
300            goto exit;
301        }
302        wavData += count;
303        wavLength -= count;
304    }
305
306exit:
307    if (pcm)
308        pcm_close(pcm);
309    mCurrentFile->release();
310    mCurrentFile = NULL;
311    return false;
312}
313
314} // namespace android
315