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 
36 struct riff_wave_header {
37     uint32_t riff_id;
38     uint32_t riff_sz;
39     uint32_t wave_id;
40 };
41 
42 struct chunk_header {
43     uint32_t id;
44     uint32_t sz;
45 };
46 
47 struct 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 
57 namespace android {
58 
AudioPlayer()59 AudioPlayer::AudioPlayer()
60     :   mCard(-1),
61         mDevice(-1),
62         mPeriodSize(0),
63         mPeriodCount(0),
64         mCurrentFile(NULL)
65 {
66 }
67 
~AudioPlayer()68 AudioPlayer::~AudioPlayer() {
69 }
70 
setMixerValue(struct mixer * mixer,const char * name,const char * values)71 static 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 %s", values, 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 %s", values, 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  */
init(const char * config)140 bool 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 
playFile(FileMap * fileMap)196 void AudioPlayer::playFile(FileMap* fileMap) {
197     // stop any currently playing sound
198     requestExitAndWait();
199 
200     mCurrentFile = fileMap;
201     run("bootanim audio", PRIORITY_URGENT_AUDIO);
202 }
203 
threadLoop()204 bool 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     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;
274     config.start_threshold = mPeriodSize / 4;
275     config.stop_threshold = INT_MAX;
276     config.avail_min = config.start_threshold;
277     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 
305 exit:
306     if (pcm)
307         pcm_close(pcm);
308     delete mCurrentFile;
309     mCurrentFile = NULL;
310     return false;
311 }
312 
313 } // namespace android
314