blob: 293213008d582ca6d5048a1a585da9fb4f570d62 [file] [log] [blame]
Mike Lockwoodebf9a0d2014-10-02 16:08:47 -07001/*
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 {
Bernhard Rosenkränzer09993f72014-11-17 20:25:28 +0100101 ALOGE("Could not parse %s as int for %s", values, name);
Mike Lockwoodebf9a0d2014-10-02 16:08:47 -0700102 }
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) {
Bernhard Rosenkränzer09993f72014-11-17 20:25:28 +0100107 ALOGE("mixer_ctl_set_enum_by_string failed for %s %s", name, stringValue);
Mike Lockwoodebf9a0d2014-10-02 16:08:47 -0700108 }
109 } else {
Bernhard Rosenkränzer09993f72014-11-17 20:25:28 +0100110 ALOGE("Could not parse %s as enum for %s", values, name);
Mike Lockwoodebf9a0d2014-10-02 16:08:47 -0700111 }
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
Bernhard Rosenkränzer99d61ed2014-11-17 21:15:30 +0100196void AudioPlayer::playFile(FileMap* fileMap) {
Mike Lockwoodebf9a0d2014-10-02 16:08:47 -0700197 // 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;
Mike Lockwoodebf9a0d2014-10-02 16:08:47 -0700210 int bufferSize;
211 const uint8_t* wavData;
212 size_t wavLength;
213 const struct riff_wave_header* wavHeader;
214
215 if (mCurrentFile == NULL) {
216 ALOGE("mCurrentFile is NULL");
217 return false;
218 }
219
220 wavData = (const uint8_t *)mCurrentFile->getDataPtr();
221 if (!wavData) {
222 ALOGE("Could not access WAV file data");
223 goto exit;
224 }
225 wavLength = mCurrentFile->getDataLength();
226
227 wavHeader = (const struct riff_wave_header *)wavData;
228 if (wavLength < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
229 (wavHeader->wave_id != ID_WAVE)) {
230 ALOGE("Error: audio file is not a riff/wave file\n");
231 goto exit;
232 }
233 wavData += sizeof(*wavHeader);
234 wavLength -= sizeof(*wavHeader);
235
236 do {
237 const struct chunk_header* chunkHeader = (const struct chunk_header*)wavData;
238 if (wavLength < sizeof(*chunkHeader)) {
239 ALOGE("EOF reading chunk headers");
240 goto exit;
241 }
242
243 wavData += sizeof(*chunkHeader);
244 wavLength -= sizeof(*chunkHeader);
245
246 switch (chunkHeader->id) {
247 case ID_FMT:
248 chunkFmt = (const struct chunk_fmt *)wavData;
249 wavData += chunkHeader->sz;
250 wavLength -= chunkHeader->sz;
251 break;
252 case ID_DATA:
253 /* Stop looking for chunks */
254 moreChunks = 0;
255 break;
256 default:
257 /* Unknown chunk, skip bytes */
258 wavData += chunkHeader->sz;
259 wavLength -= chunkHeader->sz;
260 }
261 } while (moreChunks);
262
263 if (!chunkFmt) {
264 ALOGE("format not found in WAV file");
265 goto exit;
266 }
267
268
269 memset(&config, 0, sizeof(config));
270 config.channels = chunkFmt->num_channels;
271 config.rate = chunkFmt->sample_rate;
272 config.period_size = mPeriodSize;
273 config.period_count = mPeriodCount;
Mike Lockwood28138582014-10-07 14:47:26 -0700274 config.start_threshold = mPeriodSize / 4;
275 config.stop_threshold = INT_MAX;
276 config.avail_min = config.start_threshold;
Mike Lockwoodebf9a0d2014-10-02 16:08:47 -0700277 if (chunkFmt->bits_per_sample != 16) {
278 ALOGE("only 16 bit WAV files are supported");
279 goto exit;
280 }
281 config.format = PCM_FORMAT_S16_LE;
282
283 pcm = pcm_open(mCard, mDevice, PCM_OUT, &config);
284 if (!pcm || !pcm_is_ready(pcm)) {
285 ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(pcm));
286 goto exit;
287 }
288
289 bufferSize = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
290
291 while (wavLength > 0) {
292 if (exitPending()) goto exit;
293 size_t count = bufferSize;
294 if (count > wavLength)
295 count = wavLength;
296
297 if (pcm_write(pcm, wavData, count)) {
298 ALOGE("pcm_write failed (%s)", pcm_get_error(pcm));
299 goto exit;
300 }
301 wavData += count;
302 wavLength -= count;
303 }
304
305exit:
306 if (pcm)
307 pcm_close(pcm);
Narayan Kamath688ff4c2015-02-23 15:47:54 +0000308 delete mCurrentFile;
Mike Lockwoodebf9a0d2014-10-02 16:08:47 -0700309 mCurrentFile = NULL;
310 return false;
311}
312
313} // namespace android