diff options
Diffstat (limited to 'apps/metadata/wave.c')
-rw-r--r-- | apps/metadata/wave.c | 432 |
1 files changed, 0 insertions, 432 deletions
diff --git a/apps/metadata/wave.c b/apps/metadata/wave.c deleted file mode 100644 index 45acea1fa1..0000000000 --- a/apps/metadata/wave.c +++ /dev/null | |||
@@ -1,432 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Dave Chapman | ||
11 | * Copyright (C) 2010 Yoshihisa Uchida | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * as published by the Free Software Foundation; either version 2 | ||
16 | * of the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
19 | * KIND, either express or implied. | ||
20 | * | ||
21 | ****************************************************************************/ | ||
22 | #include <stdio.h> | ||
23 | #include <string.h> | ||
24 | #include <inttypes.h> | ||
25 | |||
26 | #include "system.h" | ||
27 | #include "metadata.h" | ||
28 | #include "metadata_common.h" | ||
29 | #include "metadata_parsers.h" | ||
30 | #include "rbunicode.h" | ||
31 | #include "logf.h" | ||
32 | |||
33 | #ifdef DEBUGF | ||
34 | #undef DEBUGF | ||
35 | #define DEBUGF(...) | ||
36 | #endif | ||
37 | |||
38 | /* Wave(RIFF)/Wave64 format */ | ||
39 | |||
40 | |||
41 | # define AV_WL32(p, d) do { \ | ||
42 | ((uint8_t*)(p))[0] = (d); \ | ||
43 | ((uint8_t*)(p))[1] = (d)>>8; \ | ||
44 | ((uint8_t*)(p))[2] = (d)>>16; \ | ||
45 | ((uint8_t*)(p))[3] = (d)>>24; \ | ||
46 | } while(0) | ||
47 | # define AV_WL16(p, d) do { \ | ||
48 | ((uint8_t*)(p))[0] = (d); \ | ||
49 | ((uint8_t*)(p))[1] = (d)>>8; \ | ||
50 | } while(0) | ||
51 | |||
52 | enum { | ||
53 | RIFF_CHUNK = 0, | ||
54 | WAVE_CHUNK, | ||
55 | FMT_CHUNK, | ||
56 | FACT_CHUNK, | ||
57 | DATA_CHUNK, | ||
58 | LIST_CHUNK, | ||
59 | }; | ||
60 | |||
61 | /* Wave chunk names */ | ||
62 | #define WAVE_CHUNKNAME_LENGTH 4 | ||
63 | #define WAVE_CHUNKSIZE_LENGTH 4 | ||
64 | |||
65 | static const unsigned char * const wave_chunklist | ||
66 | = "RIFF" | ||
67 | "WAVE" | ||
68 | "fmt " | ||
69 | "fact" | ||
70 | "data" | ||
71 | "LIST"; | ||
72 | |||
73 | /* Wave64 GUIDs */ | ||
74 | #define WAVE64_CHUNKNAME_LENGTH 16 | ||
75 | #define WAVE64_CHUNKSIZE_LENGTH 8 | ||
76 | |||
77 | static const unsigned char * const wave64_chunklist | ||
78 | = "riff\x2e\x91\xcf\x11\xa5\xd6\x28\xdb\x04\xc1\x00\x00" | ||
79 | "wave\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" | ||
80 | "fmt \xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" | ||
81 | "fact\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" | ||
82 | "data\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a" | ||
83 | "\xbc\x94\x5f\x92\x5a\x52\xd2\x11\x86\xdc\x00\xc0\x4f\x8e\xdb\x8a"; | ||
84 | |||
85 | /* list/info chunk */ | ||
86 | |||
87 | struct info_chunk { | ||
88 | const unsigned char* tag; | ||
89 | size_t offset; | ||
90 | }; | ||
91 | |||
92 | /* info chunk names are common wave/wave64 */ | ||
93 | static const struct info_chunk info_chunks[] = { | ||
94 | { "INAM", offsetof(struct mp3entry, title), }, /* title */ | ||
95 | { "IART", offsetof(struct mp3entry, artist), }, /* artist */ | ||
96 | { "ISBJ", offsetof(struct mp3entry, albumartist), }, /* albumartist */ | ||
97 | { "IPRD", offsetof(struct mp3entry, album), }, /* album */ | ||
98 | { "IWRI", offsetof(struct mp3entry, composer), }, /* composer */ | ||
99 | { "ICMT", offsetof(struct mp3entry, comment), }, /* comment */ | ||
100 | { "ISRF", offsetof(struct mp3entry, grouping), }, /* grouping */ | ||
101 | { "IGNR", offsetof(struct mp3entry, genre_string), }, /* genre */ | ||
102 | { "ICRD", offsetof(struct mp3entry, year_string), }, /* date */ | ||
103 | { "IPRT", offsetof(struct mp3entry, track_string), }, /* track/trackcount */ | ||
104 | { "IFRM", offsetof(struct mp3entry, disc_string), }, /* disc/disccount */ | ||
105 | }; | ||
106 | |||
107 | #define INFO_CHUNK_COUNT ((int)ARRAYLEN(info_chunks)) | ||
108 | |||
109 | /* support formats */ | ||
110 | enum | ||
111 | { | ||
112 | WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */ | ||
113 | WAVE_FORMAT_ADPCM = 0x0002, /* Microsoft ADPCM Format */ | ||
114 | WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* IEEE Float */ | ||
115 | WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */ | ||
116 | WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */ | ||
117 | WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */ | ||
118 | WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic OKI ADPCM */ | ||
119 | WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha ADPCM */ | ||
120 | WAVE_FORMAT_XBOX_ADPCM = 0x0069, /* XBOX ADPCM */ | ||
121 | IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */ | ||
122 | IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */ | ||
123 | WAVE_FORMAT_ATRAC3 = 0x0270, /* Atrac3 stream */ | ||
124 | WAVE_FORMAT_SWF_ADPCM = 0x5346, /* Adobe SWF ADPCM */ | ||
125 | WAVE_FORMAT_EXTENSIBLE = 0xFFFE, | ||
126 | }; | ||
127 | |||
128 | struct wave_fmt { | ||
129 | unsigned int formattag; | ||
130 | unsigned int channels; | ||
131 | unsigned int blockalign; | ||
132 | unsigned int bitspersample; | ||
133 | unsigned int samplesperblock; | ||
134 | uint32_t totalsamples; | ||
135 | uint64_t numbytes; | ||
136 | }; | ||
137 | |||
138 | static unsigned char *convert_utf8(const unsigned char *src, unsigned char *dst, | ||
139 | int size, bool is_64) | ||
140 | { | ||
141 | if (is_64) | ||
142 | { | ||
143 | /* Note: wave64: metadata codepage is UTF-16 only */ | ||
144 | return utf16LEdecode(src, dst, size); | ||
145 | } | ||
146 | return iso_decode(src, dst, -1, size); | ||
147 | } | ||
148 | |||
149 | static void set_totalsamples(struct wave_fmt *fmt, struct mp3entry* id3) | ||
150 | { | ||
151 | switch (fmt->formattag) | ||
152 | { | ||
153 | case WAVE_FORMAT_PCM: | ||
154 | case WAVE_FORMAT_IEEE_FLOAT: | ||
155 | case WAVE_FORMAT_ALAW: | ||
156 | case WAVE_FORMAT_MULAW: | ||
157 | case IBM_FORMAT_ALAW: | ||
158 | case IBM_FORMAT_MULAW: | ||
159 | fmt->blockalign = fmt->bitspersample * fmt->channels >> 3; | ||
160 | fmt->samplesperblock = 1; | ||
161 | break; | ||
162 | case WAVE_FORMAT_YAMAHA_ADPCM: | ||
163 | if (id3->channels != 0) | ||
164 | { | ||
165 | fmt->samplesperblock = | ||
166 | (fmt->blockalign == ((id3->frequency / 60) + 4) * fmt->channels)? | ||
167 | id3->frequency / 30 : (fmt->blockalign << 1) / fmt->channels; | ||
168 | } | ||
169 | break; | ||
170 | case WAVE_FORMAT_DIALOGIC_OKI_ADPCM: | ||
171 | fmt->blockalign = 1; | ||
172 | fmt->samplesperblock = 2; | ||
173 | break; | ||
174 | case WAVE_FORMAT_SWF_ADPCM: | ||
175 | if (fmt->bitspersample != 0 && id3->channels != 0) | ||
176 | { | ||
177 | fmt->samplesperblock | ||
178 | = (((fmt->blockalign << 3) - 2) / fmt->channels - 22) | ||
179 | / fmt->bitspersample + 1; | ||
180 | } | ||
181 | break; | ||
182 | default: | ||
183 | break; | ||
184 | } | ||
185 | |||
186 | if (fmt->blockalign != 0) | ||
187 | fmt->totalsamples = (fmt->numbytes / fmt->blockalign) * fmt->samplesperblock; | ||
188 | } | ||
189 | |||
190 | static void parse_riff_format(unsigned char* buf, int fmtsize, struct wave_fmt *fmt, | ||
191 | struct mp3entry* id3) | ||
192 | { | ||
193 | /* wFormatTag */ | ||
194 | fmt->formattag = buf[0] | (buf[1] << 8); | ||
195 | /* wChannels */ | ||
196 | fmt->channels = buf[2] | (buf[3] << 8); | ||
197 | /* dwSamplesPerSec */ | ||
198 | id3->frequency = get_long_le(&buf[4]); | ||
199 | /* dwAvgBytesPerSec */ | ||
200 | id3->bitrate = (get_long_le(&buf[8]) * 8) / 1000; | ||
201 | /* wBlockAlign */ | ||
202 | fmt->blockalign = buf[12] | (buf[13] << 8); | ||
203 | /* wBitsPerSample */ | ||
204 | fmt->bitspersample = buf[14] | (buf[15] << 8); | ||
205 | |||
206 | if (fmt->formattag != WAVE_FORMAT_EXTENSIBLE) | ||
207 | { | ||
208 | if (fmtsize > 19) | ||
209 | { | ||
210 | /* wSamplesPerBlock */ | ||
211 | fmt->samplesperblock = buf[18] | (buf[19] << 8); | ||
212 | } | ||
213 | } | ||
214 | else if (fmtsize > 25) | ||
215 | { | ||
216 | /* wValidBitsPerSample */ | ||
217 | fmt->bitspersample = buf[18] | (buf[19] << 8); | ||
218 | /* SubFormat */ | ||
219 | fmt->formattag = buf[24] | (buf[25] << 8); | ||
220 | } | ||
221 | |||
222 | /* Check for ATRAC3 stream */ | ||
223 | if (fmt->formattag == WAVE_FORMAT_ATRAC3) | ||
224 | { | ||
225 | int jsflag = 0; | ||
226 | if(id3->bitrate == 66 || id3->bitrate == 94) | ||
227 | jsflag = 1; | ||
228 | |||
229 | id3->extradata_size = 14; | ||
230 | id3->channels = 2; | ||
231 | id3->codectype = AFMT_OMA_ATRAC3; | ||
232 | id3->bytesperframe = fmt->blockalign; | ||
233 | |||
234 | /* Store the extradata for the codec */ | ||
235 | AV_WL16(&id3->id3v2buf[0], 1); // always 1 | ||
236 | AV_WL32(&id3->id3v2buf[2], id3->frequency);// samples rate | ||
237 | AV_WL16(&id3->id3v2buf[6], jsflag); // coding mode | ||
238 | AV_WL16(&id3->id3v2buf[8], jsflag); // coding mode | ||
239 | AV_WL16(&id3->id3v2buf[10], 1); // always 1 | ||
240 | AV_WL16(&id3->id3v2buf[12], 0); // always 0 | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static void parse_list_chunk(int fd, struct mp3entry* id3, int chunksize, bool is_64) | ||
245 | { | ||
246 | unsigned char tmpbuf[ID3V2_BUF_SIZE]; | ||
247 | unsigned char *bp = tmpbuf; | ||
248 | unsigned char *endp; | ||
249 | unsigned char *data_pos; | ||
250 | unsigned char *tag_pos = id3->id3v2buf; | ||
251 | int datasize; | ||
252 | int infosize; | ||
253 | int remain; | ||
254 | int i; | ||
255 | |||
256 | if (is_64) | ||
257 | lseek(fd, 4, SEEK_CUR); | ||
258 | else if (read(fd, bp, 4) < 4 || memcmp(bp, "INFO", 4)) | ||
259 | return; | ||
260 | |||
261 | /* decrease skip bytes */ | ||
262 | chunksize -= 4; | ||
263 | |||
264 | infosize = read(fd, bp, (ID3V2_BUF_SIZE > chunksize)? chunksize : ID3V2_BUF_SIZE); | ||
265 | if (infosize <= 8) | ||
266 | return; | ||
267 | |||
268 | endp = bp + infosize; | ||
269 | while (bp < endp) | ||
270 | { | ||
271 | datasize = get_long_le(bp + 4); | ||
272 | data_pos = bp + 8; | ||
273 | remain = ID3V2_BUF_SIZE - (tag_pos - (unsigned char*)id3->id3v2buf); | ||
274 | if (remain < 1) | ||
275 | break; | ||
276 | |||
277 | for (i = 0; i < INFO_CHUNK_COUNT; i++) | ||
278 | { | ||
279 | if (memcmp(bp, info_chunks[i].tag, 4) == 0) | ||
280 | { | ||
281 | *((char **)(((char*)id3) + info_chunks[i].offset)) = tag_pos; | ||
282 | tag_pos = convert_utf8(data_pos, tag_pos, | ||
283 | (datasize + 1 >= remain )? remain - 1 : datasize, | ||
284 | is_64); | ||
285 | *tag_pos++ = 0; | ||
286 | break; | ||
287 | } | ||
288 | } | ||
289 | bp = data_pos + datasize + (datasize & 1); | ||
290 | }; | ||
291 | } | ||
292 | |||
293 | static bool read_header(int fd, struct mp3entry* id3, const unsigned char *chunknames, | ||
294 | bool is_64) | ||
295 | { | ||
296 | /* Use the temporary buffer */ | ||
297 | unsigned char* buf = (unsigned char *)id3->path; | ||
298 | |||
299 | struct wave_fmt fmt; | ||
300 | |||
301 | const unsigned int namelen = (is_64)? WAVE64_CHUNKNAME_LENGTH : WAVE_CHUNKNAME_LENGTH; | ||
302 | const unsigned int sizelen = (is_64)? WAVE64_CHUNKSIZE_LENGTH : WAVE_CHUNKSIZE_LENGTH; | ||
303 | const unsigned int len = namelen + sizelen; | ||
304 | uint64_t chunksize; | ||
305 | uint64_t offset = len + namelen; | ||
306 | int read_data; | ||
307 | |||
308 | memset(&fmt, 0, sizeof(struct wave_fmt)); | ||
309 | |||
310 | id3->vbr = false; /* All Wave/Wave64 files are CBR */ | ||
311 | id3->filesize = filesize(fd); | ||
312 | |||
313 | /* get RIFF chunk header */ | ||
314 | lseek(fd, 0, SEEK_SET); | ||
315 | read(fd, buf, offset); | ||
316 | |||
317 | if ((memcmp(buf, chunknames + RIFF_CHUNK * namelen, namelen) != 0) || | ||
318 | (memcmp(buf + len, chunknames + WAVE_CHUNK * namelen, namelen) != 0)) | ||
319 | { | ||
320 | DEBUGF("metadata error: missing riff header.\n"); | ||
321 | return false; | ||
322 | } | ||
323 | |||
324 | /* iterate over WAVE chunks until 'data' chunk */ | ||
325 | while (read(fd, buf, len) > 0) | ||
326 | { | ||
327 | offset += len; | ||
328 | |||
329 | /* get chunk size (when the header is wave64, chunksize includes GUID and data length) */ | ||
330 | chunksize = (is_64) ? get_uint64_le(buf + namelen) - len : | ||
331 | get_long_le(buf + namelen); | ||
332 | |||
333 | read_data = 0; | ||
334 | if (memcmp(buf, chunknames + FMT_CHUNK * namelen, namelen) == 0) | ||
335 | { | ||
336 | DEBUGF("find 'fmt ' chunk\n"); | ||
337 | |||
338 | if (chunksize < 16) | ||
339 | { | ||
340 | DEBUGF("metadata error: 'fmt ' chunk is too small: %d\n", (int)chunksize); | ||
341 | return false; | ||
342 | } | ||
343 | |||
344 | /* get and parse format */ | ||
345 | read_data = (chunksize > 25)? 26 : chunksize; | ||
346 | |||
347 | read(fd, buf, read_data); | ||
348 | parse_riff_format(buf, read_data, &fmt, id3); | ||
349 | } | ||
350 | else if (memcmp(buf, chunknames + FACT_CHUNK * namelen, namelen) == 0) | ||
351 | { | ||
352 | DEBUGF("find 'fact' chunk\n"); | ||
353 | |||
354 | /* dwSampleLength */ | ||
355 | if (chunksize >= sizelen) | ||
356 | { | ||
357 | /* get totalsamples */ | ||
358 | read_data = sizelen; | ||
359 | read(fd, buf, read_data); | ||
360 | fmt.totalsamples = (is_64)? get_uint64_le(buf) : get_long_le(buf); | ||
361 | } | ||
362 | } | ||
363 | else if (memcmp(buf, chunknames + DATA_CHUNK * namelen, namelen) == 0) | ||
364 | { | ||
365 | DEBUGF("find 'data' chunk\n"); | ||
366 | fmt.numbytes = chunksize; | ||
367 | if (fmt.formattag == WAVE_FORMAT_ATRAC3) | ||
368 | id3->first_frame_offset = offset; | ||
369 | } | ||
370 | else if (memcmp(buf, chunknames + LIST_CHUNK * namelen, namelen) == 0) | ||
371 | { | ||
372 | DEBUGF("find 'LIST' chunk\n"); | ||
373 | parse_list_chunk(fd, id3, chunksize, is_64); | ||
374 | lseek(fd, offset, SEEK_SET); | ||
375 | } | ||
376 | |||
377 | /* padded to next chunk */ | ||
378 | chunksize += ((is_64)? ((1 + ~chunksize) & 0x07) : (chunksize & 1)); | ||
379 | |||
380 | offset += chunksize; | ||
381 | if (offset >= id3->filesize) | ||
382 | break; | ||
383 | |||
384 | lseek(fd, chunksize - read_data, SEEK_CUR); | ||
385 | } | ||
386 | |||
387 | if (fmt.numbytes == 0) | ||
388 | { | ||
389 | DEBUGF("metadata error: read error or missing 'data' chunk.\n"); | ||
390 | return false; | ||
391 | } | ||
392 | |||
393 | if (fmt.totalsamples == 0) | ||
394 | set_totalsamples(&fmt, id3); | ||
395 | |||
396 | if (id3->frequency == 0 || id3->bitrate == 0) | ||
397 | { | ||
398 | DEBUGF("metadata error: frequency or bitrate is 0\n"); | ||
399 | return false; | ||
400 | } | ||
401 | |||
402 | /* Calculate track length (in ms) and estimate the bitrate (in kbit/s) */ | ||
403 | id3->length = (fmt.formattag != WAVE_FORMAT_ATRAC3)? | ||
404 | (uint64_t)fmt.totalsamples * 1000 / id3->frequency : | ||
405 | ((id3->filesize - id3->first_frame_offset) * 8) / id3->bitrate; | ||
406 | |||
407 | /* output header/id3 info (for debug) */ | ||
408 | DEBUGF("%s header info ----\n", (is_64)? "wave64" : "wave"); | ||
409 | DEBUGF(" format: %04x\n", (int)fmt.formattag); | ||
410 | DEBUGF(" channels: %u\n", fmt.channels); | ||
411 | DEBUGF(" blockalign: %u\n", fmt.blockalign); | ||
412 | DEBUGF(" bitspersample: %u\n", fmt.bitspersample); | ||
413 | DEBUGF(" samplesperblock: %u\n", fmt.samplesperblock); | ||
414 | DEBUGF(" totalsamples: %u\n", (unsigned int)fmt.totalsamples); | ||
415 | DEBUGF(" numbytes: %u\n", (unsigned int)fmt.numbytes); | ||
416 | DEBUGF("id3 info ----\n"); | ||
417 | DEBUGF(" frequency: %u\n", (unsigned int)id3->frequency); | ||
418 | DEBUGF(" bitrate: %d\n", id3->bitrate); | ||
419 | DEBUGF(" length: %u\n", (unsigned int)id3->length); | ||
420 | |||
421 | return true; | ||
422 | } | ||
423 | |||
424 | bool get_wave_metadata(int fd, struct mp3entry* id3) | ||
425 | { | ||
426 | return read_header(fd, id3, wave_chunklist, false); | ||
427 | } | ||
428 | |||
429 | bool get_wave64_metadata(int fd, struct mp3entry* id3) | ||
430 | { | ||
431 | return read_header(fd, id3, wave64_chunklist, true); | ||
432 | } | ||