• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 %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  */
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(struct FileMap * fileMap)196 void 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 
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     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 
306 exit:
307     if (pcm)
308         pcm_close(pcm);
309     mCurrentFile->release();
310     mCurrentFile = NULL;
311     return false;
312 }
313 
314 } // namespace android
315