diff options
author | Björn Stenberg <bjorn@haxx.se> | 2008-10-10 13:12:28 +0000 |
---|---|---|
committer | Björn Stenberg <bjorn@haxx.se> | 2008-10-10 13:12:28 +0000 |
commit | e76c69f3e4b9075db979145a60157d8cd968f537 (patch) | |
tree | 84ac8d57c687d1fdd10d8f205aa83a048edfd875 /firmware/id3.c | |
parent | 98fa3913f9618a09269e9ab39abb9a53274f5676 (diff) | |
download | rockbox-e76c69f3e4b9075db979145a60157d8cd968f537.tar.gz rockbox-e76c69f3e4b9075db979145a60157d8cd968f537.zip |
Moved id3.c, mpeg.c and replaygain.c from firmware/ to apps/. This is the first step in separating the generic metadata code and the id3-specific code.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18760 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/id3.c')
-rw-r--r-- | firmware/id3.c | 1353 |
1 files changed, 0 insertions, 1353 deletions
diff --git a/firmware/id3.c b/firmware/id3.c deleted file mode 100644 index c1541e30df..0000000000 --- a/firmware/id3.c +++ /dev/null | |||
@@ -1,1353 +0,0 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2002 by Daniel Stenberg | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | /* | ||
22 | * Parts of this code has been stolen from the Ample project and was written | ||
23 | * by David H�deman. It has since been extended and enhanced pretty much by | ||
24 | * all sorts of friendly Rockbox people. | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | /* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach | ||
29 | */ | ||
30 | |||
31 | #include <stdio.h> | ||
32 | #include <stdlib.h> | ||
33 | #include <string.h> | ||
34 | #include <errno.h> | ||
35 | #include <stdbool.h> | ||
36 | #include <stddef.h> | ||
37 | #include <ctype.h> | ||
38 | #include "config.h" | ||
39 | #include "file.h" | ||
40 | #include "logf.h" | ||
41 | |||
42 | #include "id3.h" | ||
43 | #include "mp3data.h" | ||
44 | #include "system.h" | ||
45 | #include "replaygain.h" | ||
46 | #include "rbunicode.h" | ||
47 | |||
48 | /** Database of audio formats **/ | ||
49 | const struct afmt_entry audio_formats[AFMT_NUM_CODECS] = | ||
50 | { | ||
51 | /* Unknown file format */ | ||
52 | [AFMT_UNKNOWN] = | ||
53 | AFMT_ENTRY("???", NULL, NULL, NULL ), | ||
54 | |||
55 | /* MPEG Audio layer 1 */ | ||
56 | [AFMT_MPA_L1] = | ||
57 | AFMT_ENTRY("MP1", "mpa", NULL, "mp1\0" ), | ||
58 | /* MPEG Audio layer 2 */ | ||
59 | [AFMT_MPA_L2] = | ||
60 | AFMT_ENTRY("MP2", "mpa", NULL, "mpa\0mp2\0" ), | ||
61 | /* MPEG Audio layer 3 */ | ||
62 | [AFMT_MPA_L3] = | ||
63 | AFMT_ENTRY("MP3", "mpa", "mp3_enc", "mp3\0" ), | ||
64 | |||
65 | #if CONFIG_CODEC == SWCODEC | ||
66 | /* Audio Interchange File Format */ | ||
67 | [AFMT_AIFF] = | ||
68 | AFMT_ENTRY("AIFF", "aiff", "aiff_enc", "aiff\0aif\0"), | ||
69 | /* Uncompressed PCM in a WAV file */ | ||
70 | [AFMT_PCM_WAV] = | ||
71 | AFMT_ENTRY("WAV", "wav", "wav_enc", "wav\0" ), | ||
72 | /* Ogg Vorbis */ | ||
73 | [AFMT_OGG_VORBIS] = | ||
74 | AFMT_ENTRY("Ogg", "vorbis", NULL, "ogg\0" ), | ||
75 | /* FLAC */ | ||
76 | [AFMT_FLAC] = | ||
77 | AFMT_ENTRY("FLAC", "flac", NULL, "flac\0" ), | ||
78 | /* Musepack */ | ||
79 | [AFMT_MPC] = | ||
80 | AFMT_ENTRY("MPC", "mpc", NULL, "mpc\0" ), | ||
81 | /* A/52 (aka AC3) audio */ | ||
82 | [AFMT_A52] = | ||
83 | AFMT_ENTRY("AC3", "a52", NULL, "a52\0ac3\0" ), | ||
84 | /* WavPack */ | ||
85 | [AFMT_WAVPACK] = | ||
86 | AFMT_ENTRY("WV", "wavpack", "wavpack_enc", "wv\0" ), | ||
87 | /* Apple Lossless Audio Codec */ | ||
88 | [AFMT_ALAC] = | ||
89 | AFMT_ENTRY("ALAC", "alac", NULL, "m4a\0m4b\0" ), | ||
90 | /* Advanced Audio Coding in M4A container */ | ||
91 | [AFMT_AAC] = | ||
92 | AFMT_ENTRY("AAC", "aac", NULL, "mp4\0" ), | ||
93 | /* Shorten */ | ||
94 | [AFMT_SHN] = | ||
95 | AFMT_ENTRY("SHN", "shorten", NULL, "shn\0" ), | ||
96 | /* SID File Format */ | ||
97 | [AFMT_SID] = | ||
98 | AFMT_ENTRY("SID", "sid", NULL, "sid\0" ), | ||
99 | /* ADX File Format */ | ||
100 | [AFMT_ADX] = | ||
101 | AFMT_ENTRY("ADX", "adx", NULL, "adx\0" ), | ||
102 | /* NESM (NES Sound Format) */ | ||
103 | [AFMT_NSF] = | ||
104 | AFMT_ENTRY("NSF", "nsf", NULL, "nsf\0nsfe\0" ), | ||
105 | /* Speex File Format */ | ||
106 | [AFMT_SPEEX] = | ||
107 | AFMT_ENTRY("Speex","speex", NULL, "spx\0" ), | ||
108 | /* SPC700 Save State */ | ||
109 | [AFMT_SPC] = | ||
110 | AFMT_ENTRY("SPC", "spc", NULL, "spc\0" ), | ||
111 | /* APE (Monkey's Audio) */ | ||
112 | [AFMT_APE] = | ||
113 | AFMT_ENTRY("APE", "ape", NULL, "ape\0mac\0" ), | ||
114 | /* WMA (WMAV1/V2 in ASF) */ | ||
115 | [AFMT_WMA] = | ||
116 | AFMT_ENTRY("WMA", "wma", NULL, "wma\0wmv\0asf\0" ), | ||
117 | /* Amiga MOD File */ | ||
118 | [AFMT_MOD] = | ||
119 | AFMT_ENTRY("MOD", "mod", NULL, "mod\0" ), | ||
120 | /* Amiga SAP File */ | ||
121 | [AFMT_SAP] = | ||
122 | AFMT_ENTRY("SAP", "asap", NULL, "sap\0" ), | ||
123 | #endif | ||
124 | }; | ||
125 | |||
126 | #if CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) | ||
127 | /* get REC_FORMAT_* corresponding AFMT_* */ | ||
128 | const int rec_format_afmt[REC_NUM_FORMATS] = | ||
129 | { | ||
130 | /* give AFMT_UNKNOWN by default */ | ||
131 | [0 ... REC_NUM_FORMATS-1] = AFMT_UNKNOWN, | ||
132 | /* add new entries below this line */ | ||
133 | [REC_FORMAT_AIFF] = AFMT_AIFF, | ||
134 | [REC_FORMAT_MPA_L3] = AFMT_MPA_L3, | ||
135 | [REC_FORMAT_WAVPACK] = AFMT_WAVPACK, | ||
136 | [REC_FORMAT_PCM_WAV] = AFMT_PCM_WAV, | ||
137 | }; | ||
138 | |||
139 | /* get AFMT_* corresponding REC_FORMAT_* */ | ||
140 | const int afmt_rec_format[AFMT_NUM_CODECS] = | ||
141 | { | ||
142 | /* give -1 by default */ | ||
143 | [0 ... AFMT_NUM_CODECS-1] = -1, | ||
144 | /* add new entries below this line */ | ||
145 | [AFMT_AIFF] = REC_FORMAT_AIFF, | ||
146 | [AFMT_MPA_L3] = REC_FORMAT_MPA_L3, | ||
147 | [AFMT_WAVPACK] = REC_FORMAT_WAVPACK, | ||
148 | [AFMT_PCM_WAV] = REC_FORMAT_PCM_WAV, | ||
149 | }; | ||
150 | #endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */ | ||
151 | /****/ | ||
152 | |||
153 | static unsigned long unsync(unsigned long b0, | ||
154 | unsigned long b1, | ||
155 | unsigned long b2, | ||
156 | unsigned long b3) | ||
157 | { | ||
158 | return (((long)(b0 & 0x7F) << (3*7)) | | ||
159 | ((long)(b1 & 0x7F) << (2*7)) | | ||
160 | ((long)(b2 & 0x7F) << (1*7)) | | ||
161 | ((long)(b3 & 0x7F) << (0*7))); | ||
162 | } | ||
163 | |||
164 | static const char* const genres[] = { | ||
165 | "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", | ||
166 | "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", | ||
167 | "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", | ||
168 | "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", | ||
169 | "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", | ||
170 | "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", | ||
171 | "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", | ||
172 | "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", | ||
173 | "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", | ||
174 | "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", | ||
175 | "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", | ||
176 | "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", | ||
177 | "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", | ||
178 | |||
179 | /* winamp extensions */ | ||
180 | "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", | ||
181 | "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", | ||
182 | "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", | ||
183 | "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", | ||
184 | "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", | ||
185 | "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", | ||
186 | "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", | ||
187 | "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", | ||
188 | "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", | ||
189 | "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", | ||
190 | "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", | ||
191 | "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "Jpop", | ||
192 | "Synthpop" | ||
193 | }; | ||
194 | |||
195 | char* id3_get_num_genre(unsigned int genre_num) | ||
196 | { | ||
197 | if (genre_num < sizeof(genres)/sizeof(char*)) | ||
198 | return (char*)genres[genre_num]; | ||
199 | return NULL; | ||
200 | } | ||
201 | |||
202 | /* True if the string is from the "genres" array */ | ||
203 | static bool id3_is_genre_string(const char *string) | ||
204 | { | ||
205 | return ( string >= genres[0] && | ||
206 | string <= genres[sizeof(genres)/sizeof(char*) - 1] ); | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | HOW TO ADD ADDITIONAL ID3 VERSION 2 TAGS | ||
211 | Code and comments by Thomas Paul Diffenbach | ||
212 | |||
213 | To add another ID3v2 Tag, do the following: | ||
214 | 1. add a char* named for the tag to struct mp3entry in id3.h, | ||
215 | (I (tpd) prefer to use char* rather than ints, even for what seems like | ||
216 | numerical values, for cases where a number won't do, e.g., | ||
217 | YEAR: "circa 1765", "1790/1977" (composed/performed), "28 Feb 1969" | ||
218 | TRACK: "1/12", "1 of 12", GENRE: "Freeform genre name" | ||
219 | Text is more flexible, and as the main use of id3 data is to | ||
220 | display it, converting it to an int just means reconverting to | ||
221 | display it, at a runtime cost.) | ||
222 | |||
223 | 2. If any special processing beyond copying the tag value from the Id3 | ||
224 | block to the struct mp3entry is rrequired (such as converting to an | ||
225 | int), write a function to perform this special processing. | ||
226 | |||
227 | This function's prototype must match that of | ||
228 | typedef tagPostProcessFunc, that is it must be: | ||
229 | int func( struct mp3entry*, char* tag, int bufferpos ) | ||
230 | the first argument is a pointer to the current mp3entry structure the | ||
231 | second argument is a pointer to the null terminated string value of the | ||
232 | tag found the third argument is the offset of the next free byte in the | ||
233 | mp3entry's buffer your function should return the corrected offset; if | ||
234 | you don't lengthen or shorten the tag string, you can return the third | ||
235 | argument unchanged. | ||
236 | |||
237 | Unless you have a good reason no to, make the function static. | ||
238 | TO JUST COPY THE TAG NO SPECIAL PROCESSING FUNCTION IS NEEDED. | ||
239 | |||
240 | 3. add one or more entries to the tagList array, using the format: | ||
241 | char* ID3 Tag symbolic name -- see the ID3 specification for these, | ||
242 | sizeof() that name minus 1, | ||
243 | offsetof( struct mp3entry, variable_name_in_struct_mp3entry ), | ||
244 | pointer to your special processing function or NULL | ||
245 | if you need no special processing | ||
246 | flag indicating if this tag is binary or textual | ||
247 | Many ID3 symbolic names come in more than one form. You can add both | ||
248 | forms, each referencing the same variable in struct mp3entry. | ||
249 | If both forms are present, the last found will be used. | ||
250 | Note that the offset can be zero, in which case no entry will be set | ||
251 | in the mp3entry struct; the frame is still read into the buffer and | ||
252 | the special processing function is called (several times, if there | ||
253 | are several frames with the same name). | ||
254 | |||
255 | 4. Alternately, use the TAG_LIST_ENTRY macro with | ||
256 | ID3 tag symbolic name, | ||
257 | variable in struct mp3entry, | ||
258 | special processing function address | ||
259 | |||
260 | 5. Add code to wps-display.c function get_tag to assign a printf-like | ||
261 | format specifier for the tag */ | ||
262 | |||
263 | /* Structure for ID3 Tag extraction information */ | ||
264 | struct tag_resolver { | ||
265 | const char* tag; | ||
266 | int tag_length; | ||
267 | size_t offset; | ||
268 | int (*ppFunc)(struct mp3entry*, char* tag, int bufferpos); | ||
269 | bool binary; | ||
270 | }; | ||
271 | |||
272 | static bool global_ff_found; | ||
273 | |||
274 | static int unsynchronize(char* tag, int len, bool *ff_found) | ||
275 | { | ||
276 | int i; | ||
277 | unsigned char c; | ||
278 | unsigned char *rp, *wp; | ||
279 | |||
280 | wp = rp = (unsigned char *)tag; | ||
281 | |||
282 | rp = (unsigned char *)tag; | ||
283 | for(i = 0;i < len;i++) { | ||
284 | /* Read the next byte and write it back, but don't increment the | ||
285 | write pointer */ | ||
286 | c = *rp++; | ||
287 | *wp = c; | ||
288 | if(*ff_found) { | ||
289 | /* Increment the write pointer if it isn't an unsynch pattern */ | ||
290 | if(c != 0) | ||
291 | wp++; | ||
292 | *ff_found = false; | ||
293 | } else { | ||
294 | if(c == 0xff) | ||
295 | *ff_found = true; | ||
296 | wp++; | ||
297 | } | ||
298 | } | ||
299 | return (long)wp - (long)tag; | ||
300 | } | ||
301 | |||
302 | static int unsynchronize_frame(char* tag, int len) | ||
303 | { | ||
304 | bool ff_found = false; | ||
305 | |||
306 | return unsynchronize(tag, len, &ff_found); | ||
307 | } | ||
308 | |||
309 | static int read_unsynched(int fd, void *buf, int len) | ||
310 | { | ||
311 | int i; | ||
312 | int rc; | ||
313 | int remaining = len; | ||
314 | char *wp; | ||
315 | char *rp; | ||
316 | |||
317 | wp = buf; | ||
318 | |||
319 | while(remaining) { | ||
320 | rp = wp; | ||
321 | rc = read(fd, rp, remaining); | ||
322 | if(rc <= 0) | ||
323 | return rc; | ||
324 | |||
325 | i = unsynchronize(wp, remaining, &global_ff_found); | ||
326 | remaining -= i; | ||
327 | wp += i; | ||
328 | } | ||
329 | |||
330 | return len; | ||
331 | } | ||
332 | |||
333 | static int skip_unsynched(int fd, int len) | ||
334 | { | ||
335 | int rc; | ||
336 | int remaining = len; | ||
337 | int rlen; | ||
338 | char buf[32]; | ||
339 | |||
340 | while(remaining) { | ||
341 | rlen = MIN(sizeof(buf), (unsigned int)remaining); | ||
342 | rc = read(fd, buf, rlen); | ||
343 | if(rc <= 0) | ||
344 | return rc; | ||
345 | |||
346 | remaining -= unsynchronize(buf, rlen, &global_ff_found); | ||
347 | } | ||
348 | |||
349 | return len; | ||
350 | } | ||
351 | |||
352 | /* parse numeric value from string */ | ||
353 | static int parsetracknum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
354 | { | ||
355 | entry->tracknum = atoi( tag ); | ||
356 | return bufferpos; | ||
357 | } | ||
358 | |||
359 | /* parse numeric value from string */ | ||
360 | static int parsediscnum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
361 | { | ||
362 | entry->discnum = atoi( tag ); | ||
363 | return bufferpos; | ||
364 | } | ||
365 | |||
366 | /* parse numeric value from string */ | ||
367 | static int parseyearnum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
368 | { | ||
369 | entry->year = atoi( tag ); | ||
370 | return bufferpos; | ||
371 | } | ||
372 | |||
373 | /* parse numeric genre from string, version 2.2 and 2.3 */ | ||
374 | static int parsegenre( struct mp3entry* entry, char* tag, int bufferpos ) | ||
375 | { | ||
376 | if(entry->id3version >= ID3_VER_2_4) { | ||
377 | /* In version 2.4 and up, there are no parentheses, and the genre frame | ||
378 | is a list of strings, either numbers or text. */ | ||
379 | |||
380 | /* Is it a number? */ | ||
381 | if(isdigit(tag[0])) { | ||
382 | entry->genre_string = id3_get_num_genre(atoi( tag )); | ||
383 | return tag - entry->id3v2buf; | ||
384 | } else { | ||
385 | entry->genre_string = tag; | ||
386 | return bufferpos; | ||
387 | } | ||
388 | } else { | ||
389 | if( tag[0] == '(' && tag[1] != '(' ) { | ||
390 | entry->genre_string = id3_get_num_genre(atoi( tag + 1 )); | ||
391 | return tag - entry->id3v2buf; | ||
392 | } | ||
393 | else { | ||
394 | entry->genre_string = tag; | ||
395 | return bufferpos; | ||
396 | } | ||
397 | } | ||
398 | } | ||
399 | |||
400 | #if CONFIG_CODEC == SWCODEC | ||
401 | /* parse user defined text, looking for replaygain information. */ | ||
402 | static int parseuser( struct mp3entry* entry, char* tag, int bufferpos ) | ||
403 | { | ||
404 | char* value = NULL; | ||
405 | int desc_len = strlen(tag); | ||
406 | int value_len = 0; | ||
407 | |||
408 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { | ||
409 | /* At least part of the value was read, so we can safely try to | ||
410 | * parse it | ||
411 | */ | ||
412 | value = tag + desc_len + 1; | ||
413 | value_len = parse_replaygain(tag, value, entry, tag, | ||
414 | bufferpos - (tag - entry->id3v2buf)); | ||
415 | } | ||
416 | |||
417 | return tag - entry->id3v2buf + value_len; | ||
418 | } | ||
419 | |||
420 | /* parse RVA2 binary data and convert to replaygain information. */ | ||
421 | static int parserva2( struct mp3entry* entry, char* tag, int bufferpos ) | ||
422 | { | ||
423 | int desc_len = strlen(tag); | ||
424 | int start_pos = tag - entry->id3v2buf; | ||
425 | int end_pos = start_pos + desc_len + 5; | ||
426 | int value_len = 0; | ||
427 | unsigned char* value = tag + desc_len + 1; | ||
428 | |||
429 | /* Only parse RVA2 replaygain tags if tag version == 2.4 and channel | ||
430 | * type is master volume. | ||
431 | */ | ||
432 | if (entry->id3version == ID3_VER_2_4 && end_pos < bufferpos | ||
433 | && *value++ == 1) { | ||
434 | long gain = 0; | ||
435 | long peak = 0; | ||
436 | long peakbits; | ||
437 | long peakbytes; | ||
438 | bool album = false; | ||
439 | |||
440 | /* The RVA2 specification is unclear on some things (id string and | ||
441 | * peak volume), but this matches how Quod Libet use them. | ||
442 | */ | ||
443 | |||
444 | gain = (int16_t) ((value[0] << 8) | value[1]); | ||
445 | value += 2; | ||
446 | peakbits = *value++; | ||
447 | peakbytes = (peakbits + 7) / 8; | ||
448 | |||
449 | /* Only use the topmost 24 bits for peak volume */ | ||
450 | if (peakbytes > 3) { | ||
451 | peakbytes = 3; | ||
452 | } | ||
453 | |||
454 | /* Make sure the peak bits were read */ | ||
455 | if (end_pos + peakbytes < bufferpos) { | ||
456 | long shift = ((8 - (peakbits & 7)) & 7) + (3 - peakbytes) * 8; | ||
457 | |||
458 | for ( ; peakbytes; peakbytes--) { | ||
459 | peak <<= 8; | ||
460 | peak += *value++; | ||
461 | } | ||
462 | |||
463 | peak <<= shift; | ||
464 | |||
465 | if (peakbits > 24) { | ||
466 | peak += *value >> (8 - shift); | ||
467 | } | ||
468 | } | ||
469 | |||
470 | if (strcasecmp(tag, "album") == 0) { | ||
471 | album = true; | ||
472 | } else if (strcasecmp(tag, "track") != 0) { | ||
473 | /* Only accept non-track values if we don't have any previous | ||
474 | * value. | ||
475 | */ | ||
476 | if (entry->track_gain != 0) { | ||
477 | return start_pos; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | value_len = parse_replaygain_int(album, gain, peak * 2, entry, | ||
482 | tag, sizeof(entry->id3v2buf) - start_pos); | ||
483 | } | ||
484 | |||
485 | return start_pos + value_len; | ||
486 | } | ||
487 | #endif | ||
488 | |||
489 | static int parsembtid( struct mp3entry* entry, char* tag, int bufferpos ) | ||
490 | { | ||
491 | char* value = NULL; | ||
492 | int desc_len = strlen(tag); | ||
493 | /*DEBUGF("MBID len: %d\n", desc_len);*/ | ||
494 | int value_len = 0; | ||
495 | |||
496 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) | ||
497 | { | ||
498 | value = tag + desc_len + 1; | ||
499 | |||
500 | if (strcasecmp(tag, "http://musicbrainz.org") == 0) | ||
501 | { | ||
502 | /* Musicbrainz track IDs are always 36 chars long plus null */ | ||
503 | value_len = 37; | ||
504 | |||
505 | entry->mb_track_id = value; | ||
506 | |||
507 | /*DEBUGF("ENTRY: %s LEN: %d\n", entry->mb_track_id, strlen(entry->mb_track_id));*/ | ||
508 | } | ||
509 | } | ||
510 | |||
511 | return tag - entry->id3v2buf + value_len; | ||
512 | } | ||
513 | |||
514 | static const struct tag_resolver taglist[] = { | ||
515 | { "TPE1", 4, offsetof(struct mp3entry, artist), NULL, false }, | ||
516 | { "TP1", 3, offsetof(struct mp3entry, artist), NULL, false }, | ||
517 | { "TIT2", 4, offsetof(struct mp3entry, title), NULL, false }, | ||
518 | { "TT2", 3, offsetof(struct mp3entry, title), NULL, false }, | ||
519 | { "TALB", 4, offsetof(struct mp3entry, album), NULL, false }, | ||
520 | { "TAL", 3, offsetof(struct mp3entry, album), NULL, false }, | ||
521 | { "TRK", 3, offsetof(struct mp3entry, track_string), &parsetracknum, false }, | ||
522 | { "TPOS", 4, offsetof(struct mp3entry, disc_string), &parsediscnum, false }, | ||
523 | { "TRCK", 4, offsetof(struct mp3entry, track_string), &parsetracknum, false }, | ||
524 | { "TDRC", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
525 | { "TYER", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
526 | { "TYE", 3, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
527 | { "TCOM", 4, offsetof(struct mp3entry, composer), NULL, false }, | ||
528 | { "TPE2", 4, offsetof(struct mp3entry, albumartist), NULL, false }, | ||
529 | { "TP2", 3, offsetof(struct mp3entry, albumartist), NULL, false }, | ||
530 | { "TIT1", 4, offsetof(struct mp3entry, grouping), NULL, false }, | ||
531 | { "TT1", 3, offsetof(struct mp3entry, grouping), NULL, false }, | ||
532 | { "COMM", 4, offsetof(struct mp3entry, comment), NULL, false }, | ||
533 | { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre, false }, | ||
534 | { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre, false }, | ||
535 | #if CONFIG_CODEC == SWCODEC | ||
536 | { "TXXX", 4, 0, &parseuser, false }, | ||
537 | { "RVA2", 4, 0, &parserva2, true }, | ||
538 | #endif | ||
539 | { "UFID", 4, 0, &parsembtid, false }, | ||
540 | }; | ||
541 | |||
542 | #define TAGLIST_SIZE ((int)(sizeof(taglist) / sizeof(taglist[0]))) | ||
543 | |||
544 | /* Get the length of an ID3 string in the given encoding. Returns the length | ||
545 | * in bytes, including end nil, or -1 if the encoding is unknown. | ||
546 | */ | ||
547 | static int unicode_len(char encoding, const void* string) | ||
548 | { | ||
549 | int len = 0; | ||
550 | |||
551 | if (encoding == 0x01 || encoding == 0x02) { | ||
552 | char first; | ||
553 | const char *s = string; | ||
554 | /* string might be unaligned, so using short* can crash on ARM and SH1 */ | ||
555 | do { | ||
556 | first = *s++; | ||
557 | } while ((first | *s++) != 0); | ||
558 | |||
559 | len = s - (const char*) string; | ||
560 | } else { | ||
561 | len = strlen((char*) string) + 1; | ||
562 | } | ||
563 | |||
564 | return len; | ||
565 | } | ||
566 | |||
567 | /* Checks to see if the passed in string is a 16-bit wide Unicode v2 | ||
568 | string. If it is, we convert it to a UTF-8 string. If it's not unicode, | ||
569 | we convert from the default codepage */ | ||
570 | static int unicode_munge(char* string, char* utf8buf, int *len) { | ||
571 | long tmp; | ||
572 | bool le = false; | ||
573 | int i = 0; | ||
574 | unsigned char *str = (unsigned char *)string; | ||
575 | int templen = 0; | ||
576 | unsigned char* utf8 = (unsigned char *)utf8buf; | ||
577 | |||
578 | switch (str[0]) { | ||
579 | case 0x00: /* Type 0x00 is ordinary ISO 8859-1 */ | ||
580 | str++; | ||
581 | (*len)--; | ||
582 | utf8 = iso_decode(str, utf8, -1, *len); | ||
583 | *utf8 = 0; | ||
584 | *len = (unsigned long)utf8 - (unsigned long)utf8buf; | ||
585 | break; | ||
586 | |||
587 | case 0x01: /* Unicode with or without BOM */ | ||
588 | case 0x02: | ||
589 | (*len)--; | ||
590 | str++; | ||
591 | |||
592 | /* Handle frames with more than one string | ||
593 | (needed for TXXX frames).*/ | ||
594 | do { | ||
595 | tmp = bytes2int(0, 0, str[0], str[1]); | ||
596 | |||
597 | /* Now check if there is a BOM | ||
598 | (zero-width non-breaking space, 0xfeff) | ||
599 | and if it is in little or big endian format */ | ||
600 | if(tmp == 0xfffe) { /* Little endian? */ | ||
601 | le = true; | ||
602 | str += 2; | ||
603 | (*len)-=2; | ||
604 | } else if(tmp == 0xfeff) { /* Big endian? */ | ||
605 | str += 2; | ||
606 | (*len)-=2; | ||
607 | } else | ||
608 | /* If there is no BOM (which is a specification violation), | ||
609 | let's try to guess it. If one of the bytes is 0x00, it is | ||
610 | probably the most significant one. */ | ||
611 | if(str[1] == 0) | ||
612 | le = true; | ||
613 | |||
614 | do { | ||
615 | if(le) | ||
616 | utf8 = utf16LEdecode(str, utf8, 1); | ||
617 | else | ||
618 | utf8 = utf16BEdecode(str, utf8, 1); | ||
619 | |||
620 | str+=2; | ||
621 | i += 2; | ||
622 | } while((str[0] || str[1]) && (i < *len)); | ||
623 | |||
624 | *utf8++ = 0; /* Terminate the string */ | ||
625 | templen += (strlen(&utf8buf[templen]) + 1); | ||
626 | str += 2; | ||
627 | i+=2; | ||
628 | } while(i < *len); | ||
629 | *len = templen - 1; | ||
630 | break; | ||
631 | |||
632 | case 0x03: /* UTF-8 encoded string */ | ||
633 | for(i=0; i < *len; i++) | ||
634 | utf8[i] = str[i+1]; | ||
635 | (*len)--; | ||
636 | break; | ||
637 | |||
638 | default: /* Plain old string */ | ||
639 | utf8 = iso_decode(str, utf8, -1, *len); | ||
640 | *utf8 = 0; | ||
641 | *len = (unsigned long)utf8 - (unsigned long)utf8buf; | ||
642 | break; | ||
643 | } | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | /* | ||
648 | * Sets the title of an MP3 entry based on its ID3v1 tag. | ||
649 | * | ||
650 | * Arguments: file - the MP3 file to scen for a ID3v1 tag | ||
651 | * entry - the entry to set the title in | ||
652 | * | ||
653 | * Returns: true if a title was found and created, else false | ||
654 | */ | ||
655 | static bool setid3v1title(int fd, struct mp3entry *entry) | ||
656 | { | ||
657 | unsigned char buffer[128]; | ||
658 | static const char offsets[] = {3, 33, 63, 97, 93, 125, 127}; | ||
659 | int i, j; | ||
660 | unsigned char* utf8; | ||
661 | |||
662 | if (-1 == lseek(fd, -128, SEEK_END)) | ||
663 | return false; | ||
664 | |||
665 | if (read(fd, buffer, sizeof buffer) != sizeof buffer) | ||
666 | return false; | ||
667 | |||
668 | if (strncmp((char *)buffer, "TAG", 3)) | ||
669 | return false; | ||
670 | |||
671 | entry->id3v1len = 128; | ||
672 | entry->id3version = ID3_VER_1_0; | ||
673 | |||
674 | for (i=0; i < (int)sizeof offsets; i++) { | ||
675 | unsigned char* ptr = (unsigned char *)buffer + offsets[i]; | ||
676 | |||
677 | switch(i) { | ||
678 | case 0: | ||
679 | case 1: | ||
680 | case 2: | ||
681 | /* kill trailing space in strings */ | ||
682 | for (j=29; j && (ptr[j]==0 || ptr[j]==' '); j--) | ||
683 | ptr[j] = 0; | ||
684 | /* convert string to utf8 */ | ||
685 | utf8 = (unsigned char *)entry->id3v1buf[i]; | ||
686 | utf8 = iso_decode(ptr, utf8, -1, 30); | ||
687 | /* make sure string is terminated */ | ||
688 | *utf8 = 0; | ||
689 | break; | ||
690 | |||
691 | case 3: | ||
692 | /* kill trailing space in strings */ | ||
693 | for (j=27; j && (ptr[j]==0 || ptr[j]==' '); j--) | ||
694 | ptr[j] = 0; | ||
695 | /* convert string to utf8 */ | ||
696 | utf8 = (unsigned char *)entry->id3v1buf[3]; | ||
697 | utf8 = iso_decode(ptr, utf8, -1, 28); | ||
698 | /* make sure string is terminated */ | ||
699 | *utf8 = 0; | ||
700 | break; | ||
701 | |||
702 | case 4: | ||
703 | ptr[4] = 0; | ||
704 | entry->year = atoi((char *)ptr); | ||
705 | break; | ||
706 | |||
707 | case 5: | ||
708 | /* id3v1.1 uses last two bytes of comment field for track | ||
709 | number: first must be 0 and second is track num */ | ||
710 | if (!ptr[0] && ptr[1]) { | ||
711 | entry->tracknum = ptr[1]; | ||
712 | entry->id3version = ID3_VER_1_1; | ||
713 | } | ||
714 | break; | ||
715 | |||
716 | case 6: | ||
717 | /* genre */ | ||
718 | entry->genre_string = id3_get_num_genre(ptr[0]); | ||
719 | break; | ||
720 | } | ||
721 | } | ||
722 | |||
723 | entry->title = entry->id3v1buf[0]; | ||
724 | entry->artist = entry->id3v1buf[1]; | ||
725 | entry->album = entry->id3v1buf[2]; | ||
726 | entry->comment = entry->id3v1buf[3]; | ||
727 | |||
728 | return true; | ||
729 | } | ||
730 | |||
731 | |||
732 | /* | ||
733 | * Sets the title of an MP3 entry based on its ID3v2 tag. | ||
734 | * | ||
735 | * Arguments: file - the MP3 file to scan for a ID3v2 tag | ||
736 | * entry - the entry to set the title in | ||
737 | * | ||
738 | * Returns: true if a title was found and created, else false | ||
739 | */ | ||
740 | static void setid3v2title(int fd, struct mp3entry *entry) | ||
741 | { | ||
742 | int minframesize; | ||
743 | int size; | ||
744 | long bufferpos = 0, totframelen, framelen; | ||
745 | char header[10]; | ||
746 | char tmp[4]; | ||
747 | unsigned char version; | ||
748 | char *buffer = entry->id3v2buf; | ||
749 | int bytesread = 0; | ||
750 | int buffersize = sizeof(entry->id3v2buf); | ||
751 | unsigned char global_flags; | ||
752 | int flags; | ||
753 | int skip; | ||
754 | bool global_unsynch = false; | ||
755 | bool unsynch = false; | ||
756 | int i, j; | ||
757 | int rc; | ||
758 | |||
759 | global_ff_found = false; | ||
760 | |||
761 | /* Bail out if the tag is shorter than 10 bytes */ | ||
762 | if(entry->id3v2len < 10) | ||
763 | return; | ||
764 | |||
765 | /* Read the ID3 tag version from the header */ | ||
766 | lseek(fd, 0, SEEK_SET); | ||
767 | if(10 != read(fd, header, 10)) | ||
768 | return; | ||
769 | |||
770 | /* Get the total ID3 tag size */ | ||
771 | size = entry->id3v2len - 10; | ||
772 | |||
773 | version = header[3]; | ||
774 | switch ( version ) { | ||
775 | case 2: | ||
776 | version = ID3_VER_2_2; | ||
777 | minframesize = 8; | ||
778 | break; | ||
779 | |||
780 | case 3: | ||
781 | version = ID3_VER_2_3; | ||
782 | minframesize = 12; | ||
783 | break; | ||
784 | |||
785 | case 4: | ||
786 | version = ID3_VER_2_4; | ||
787 | minframesize = 12; | ||
788 | break; | ||
789 | |||
790 | default: | ||
791 | /* unsupported id3 version */ | ||
792 | return; | ||
793 | } | ||
794 | entry->id3version = version; | ||
795 | entry->tracknum = entry->year = entry->discnum = 0; | ||
796 | entry->title = entry->artist = entry->album = NULL; /* FIXME incomplete */ | ||
797 | |||
798 | global_flags = header[5]; | ||
799 | |||
800 | /* Skip the extended header if it is present */ | ||
801 | if(global_flags & 0x40) { | ||
802 | if(version == ID3_VER_2_3) { | ||
803 | if(10 != read(fd, header, 10)) | ||
804 | return; | ||
805 | /* The 2.3 extended header size doesn't include the header size | ||
806 | field itself. Also, it is not unsynched. */ | ||
807 | framelen = | ||
808 | bytes2int(header[0], header[1], header[2], header[3]) + 4; | ||
809 | |||
810 | /* Skip the rest of the header */ | ||
811 | lseek(fd, framelen - 10, SEEK_CUR); | ||
812 | } | ||
813 | |||
814 | if(version >= ID3_VER_2_4) { | ||
815 | if(4 != read(fd, header, 4)) | ||
816 | return; | ||
817 | |||
818 | /* The 2.4 extended header size does include the entire header, | ||
819 | so here we can just skip it. This header is unsynched. */ | ||
820 | framelen = unsync(header[0], header[1], | ||
821 | header[2], header[3]); | ||
822 | |||
823 | lseek(fd, framelen - 4, SEEK_CUR); | ||
824 | } | ||
825 | } | ||
826 | |||
827 | /* Is unsynchronization applied? */ | ||
828 | if(global_flags & 0x80) { | ||
829 | global_unsynch = true; | ||
830 | } | ||
831 | |||
832 | /* | ||
833 | * We must have at least minframesize bytes left for the | ||
834 | * remaining frames to be interesting | ||
835 | */ | ||
836 | while (size >= minframesize && bufferpos < buffersize - 1) { | ||
837 | flags = 0; | ||
838 | |||
839 | /* Read frame header and check length */ | ||
840 | if(version >= ID3_VER_2_3) { | ||
841 | if(global_unsynch && version <= ID3_VER_2_3) | ||
842 | rc = read_unsynched(fd, header, 10); | ||
843 | else | ||
844 | rc = read(fd, header, 10); | ||
845 | if(rc != 10) | ||
846 | return; | ||
847 | /* Adjust for the 10 bytes we read */ | ||
848 | size -= 10; | ||
849 | |||
850 | flags = bytes2int(0, 0, header[8], header[9]); | ||
851 | |||
852 | if (version >= ID3_VER_2_4) { | ||
853 | framelen = unsync(header[4], header[5], | ||
854 | header[6], header[7]); | ||
855 | } else { | ||
856 | /* version .3 files don't use synchsafe ints for | ||
857 | * size */ | ||
858 | framelen = bytes2int(header[4], header[5], | ||
859 | header[6], header[7]); | ||
860 | } | ||
861 | } else { | ||
862 | if(6 != read(fd, header, 6)) | ||
863 | return; | ||
864 | /* Adjust for the 6 bytes we read */ | ||
865 | size -= 6; | ||
866 | |||
867 | framelen = bytes2int(0, header[3], header[4], header[5]); | ||
868 | } | ||
869 | |||
870 | logf("framelen = %ld", framelen); | ||
871 | if(framelen == 0){ | ||
872 | if (header[0] == 0 && header[1] == 0 && header[2] == 0) | ||
873 | return; | ||
874 | else | ||
875 | continue; | ||
876 | } | ||
877 | |||
878 | unsynch = false; | ||
879 | |||
880 | if(flags) | ||
881 | { | ||
882 | skip = 0; | ||
883 | |||
884 | if (version >= ID3_VER_2_4) { | ||
885 | if(flags & 0x0040) { /* Grouping identity */ | ||
886 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ | ||
887 | framelen--; | ||
888 | } | ||
889 | } else { | ||
890 | if(flags & 0x0020) { /* Grouping identity */ | ||
891 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ | ||
892 | framelen--; | ||
893 | } | ||
894 | } | ||
895 | |||
896 | if(flags & 0x000c) /* Compression or encryption */ | ||
897 | { | ||
898 | /* Skip it */ | ||
899 | size -= framelen; | ||
900 | lseek(fd, framelen, SEEK_CUR); | ||
901 | continue; | ||
902 | } | ||
903 | |||
904 | if(flags & 0x0002) /* Unsynchronization */ | ||
905 | unsynch = true; | ||
906 | |||
907 | if (version >= ID3_VER_2_4) { | ||
908 | if(flags & 0x0001) { /* Data length indicator */ | ||
909 | if(4 != read(fd, tmp, 4)) | ||
910 | return; | ||
911 | |||
912 | /* We don't need the data length */ | ||
913 | framelen -= 4; | ||
914 | } | ||
915 | } | ||
916 | } | ||
917 | |||
918 | /* Keep track of the remaining frame size */ | ||
919 | totframelen = framelen; | ||
920 | |||
921 | /* If the frame is larger than the remaining buffer space we try | ||
922 | to read as much as would fit in the buffer */ | ||
923 | if(framelen >= buffersize - bufferpos) | ||
924 | framelen = buffersize - bufferpos - 1; | ||
925 | |||
926 | logf("id3v2 frame: %.4s", header); | ||
927 | |||
928 | /* Check for certain frame headers | ||
929 | |||
930 | 'size' is the amount of frame bytes remaining. We decrement it by | ||
931 | the amount of bytes we read. If we fail to read as many bytes as | ||
932 | we expect, we assume that we can't read from this file, and bail | ||
933 | out. | ||
934 | |||
935 | For each frame. we will iterate over the list of supported tags, | ||
936 | and read the tag into entry's buffer. All tags will be kept as | ||
937 | strings, for cases where a number won't do, e.g., YEAR: "circa | ||
938 | 1765", "1790/1977" (composed/performed), "28 Feb 1969" TRACK: | ||
939 | "1/12", "1 of 12", GENRE: "Freeform genre name" Text is more | ||
940 | flexible, and as the main use of id3 data is to display it, | ||
941 | converting it to an int just means reconverting to display it, at a | ||
942 | runtime cost. | ||
943 | |||
944 | For tags that the current code does convert to ints, a post | ||
945 | processing function will be called via a pointer to function. */ | ||
946 | |||
947 | for (i=0; i<TAGLIST_SIZE; i++) { | ||
948 | const struct tag_resolver* tr = &taglist[i]; | ||
949 | char** ptag = tr->offset ? (char**) (((char*)entry) + tr->offset) | ||
950 | : NULL; | ||
951 | char* tag; | ||
952 | |||
953 | /* Only ID3_VER_2_2 uses frames with three-character names. */ | ||
954 | if (((version == ID3_VER_2_2) && (tr->tag_length != 3)) | ||
955 | || ((version > ID3_VER_2_2) && (tr->tag_length != 4))) { | ||
956 | continue; | ||
957 | } | ||
958 | |||
959 | /* Note that parser functions sometimes set *ptag to NULL, so | ||
960 | * the "!*ptag" check here doesn't always have the desired | ||
961 | * effect. Should the parser functions (parsegenre in | ||
962 | * particular) be updated to handle the case of being called | ||
963 | * multiple times, or should the "*ptag" check be removed? | ||
964 | */ | ||
965 | if( (!ptag || !*ptag) && !memcmp( header, tr->tag, tr->tag_length ) ) { | ||
966 | |||
967 | /* found a tag matching one in tagList, and not yet filled */ | ||
968 | tag = buffer + bufferpos; | ||
969 | |||
970 | if(global_unsynch && version <= ID3_VER_2_3) | ||
971 | bytesread = read_unsynched(fd, tag, framelen); | ||
972 | else | ||
973 | bytesread = read(fd, tag, framelen); | ||
974 | |||
975 | if( bytesread != framelen ) | ||
976 | return; | ||
977 | |||
978 | size -= bytesread; | ||
979 | |||
980 | if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) | ||
981 | bytesread = unsynchronize_frame(tag, bytesread); | ||
982 | |||
983 | /* the COMM frame has a 3 char field to hold an ISO-639-1 | ||
984 | * language string and an optional short description; | ||
985 | * remove them so unicode_munge can work correctly | ||
986 | */ | ||
987 | |||
988 | if(!memcmp( header, "COMM", 4 )) { | ||
989 | int offset; | ||
990 | /* ignore comments with iTunes 7 soundcheck/gapless data */ | ||
991 | if(!strncmp(tag+4, "iTun", 4)) | ||
992 | break; | ||
993 | offset = 3 + unicode_len(*tag, tag + 4); | ||
994 | if(bytesread > offset) { | ||
995 | bytesread -= offset; | ||
996 | memmove(tag + 1, tag + 1 + offset, bytesread - 1); | ||
997 | } | ||
998 | } | ||
999 | |||
1000 | /* Attempt to parse Unicode string only if the tag contents | ||
1001 | aren't binary */ | ||
1002 | if(!tr->binary) { | ||
1003 | /* UTF-8 could potentially be 3 times larger */ | ||
1004 | /* so we need to create a new buffer */ | ||
1005 | char utf8buf[(3 * bytesread) + 1]; | ||
1006 | |||
1007 | unicode_munge( tag, utf8buf, &bytesread ); | ||
1008 | |||
1009 | if(bytesread >= buffersize - bufferpos) | ||
1010 | bytesread = buffersize - bufferpos - 1; | ||
1011 | |||
1012 | for (j = 0; j < bytesread; j++) | ||
1013 | tag[j] = utf8buf[j]; | ||
1014 | |||
1015 | /* remove trailing spaces */ | ||
1016 | while ( bytesread > 0 && isspace(tag[bytesread-1])) | ||
1017 | bytesread--; | ||
1018 | } | ||
1019 | |||
1020 | tag[bytesread] = 0; | ||
1021 | bufferpos += bytesread + 1; | ||
1022 | |||
1023 | if (ptag) | ||
1024 | *ptag = tag; | ||
1025 | |||
1026 | if( tr->ppFunc ) | ||
1027 | bufferpos = tr->ppFunc(entry, tag, bufferpos); | ||
1028 | |||
1029 | /* Seek to the next frame */ | ||
1030 | if(framelen < totframelen) | ||
1031 | lseek(fd, totframelen - framelen, SEEK_CUR); | ||
1032 | break; | ||
1033 | } | ||
1034 | } | ||
1035 | |||
1036 | if( i == TAGLIST_SIZE ) { | ||
1037 | /* no tag in tagList was found, or it was a repeat. | ||
1038 | skip it using the total size */ | ||
1039 | |||
1040 | if(global_unsynch && version <= ID3_VER_2_3) { | ||
1041 | size -= skip_unsynched(fd, totframelen); | ||
1042 | } else { | ||
1043 | size -= totframelen; | ||
1044 | if( lseek(fd, totframelen, SEEK_CUR) == -1 ) | ||
1045 | return; | ||
1046 | } | ||
1047 | } | ||
1048 | } | ||
1049 | } | ||
1050 | |||
1051 | /* | ||
1052 | * Calculates the size of the ID3v2 tag. | ||
1053 | * | ||
1054 | * Arguments: file - the file to search for a tag. | ||
1055 | * | ||
1056 | * Returns: the size of the tag or 0 if none was found | ||
1057 | */ | ||
1058 | int getid3v2len(int fd) | ||
1059 | { | ||
1060 | char buf[6]; | ||
1061 | int offset; | ||
1062 | |||
1063 | /* Make sure file has a ID3 tag */ | ||
1064 | if((-1 == lseek(fd, 0, SEEK_SET)) || | ||
1065 | (read(fd, buf, 6) != 6) || | ||
1066 | (strncmp(buf, "ID3", strlen("ID3")) != 0)) | ||
1067 | offset = 0; | ||
1068 | |||
1069 | /* Now check what the ID3v2 size field says */ | ||
1070 | else | ||
1071 | if(read(fd, buf, 4) != 4) | ||
1072 | offset = 0; | ||
1073 | else | ||
1074 | offset = unsync(buf[0], buf[1], buf[2], buf[3]) + 10; | ||
1075 | |||
1076 | logf("ID3V2 Length: 0x%x", offset); | ||
1077 | return offset; | ||
1078 | } | ||
1079 | |||
1080 | /* | ||
1081 | * Calculates the length (in milliseconds) of an MP3 file. | ||
1082 | * | ||
1083 | * Modified to only use integers. | ||
1084 | * | ||
1085 | * Arguments: file - the file to calculate the length upon | ||
1086 | * entry - the entry to update with the length | ||
1087 | * | ||
1088 | * Returns: the song length in milliseconds, | ||
1089 | * 0 means that it couldn't be calculated | ||
1090 | */ | ||
1091 | static int getsonglength(int fd, struct mp3entry *entry) | ||
1092 | { | ||
1093 | unsigned long filetime = 0; | ||
1094 | struct mp3info info; | ||
1095 | long bytecount; | ||
1096 | |||
1097 | /* Start searching after ID3v2 header */ | ||
1098 | if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) | ||
1099 | return 0; | ||
1100 | |||
1101 | bytecount = get_mp3file_info(fd, &info); | ||
1102 | |||
1103 | logf("Space between ID3V2 tag and first audio frame: 0x%lx bytes", | ||
1104 | bytecount); | ||
1105 | |||
1106 | if(bytecount < 0) | ||
1107 | return -1; | ||
1108 | |||
1109 | bytecount += entry->id3v2len; | ||
1110 | |||
1111 | /* Validate byte count, in case the file has been edited without | ||
1112 | * updating the header. | ||
1113 | */ | ||
1114 | if (info.byte_count) | ||
1115 | { | ||
1116 | const unsigned long expected = entry->filesize - entry->id3v1len | ||
1117 | - entry->id3v2len; | ||
1118 | const unsigned long diff = MAX(10240, info.byte_count / 20); | ||
1119 | |||
1120 | if ((info.byte_count > expected + diff) | ||
1121 | || (info.byte_count < expected - diff)) | ||
1122 | { | ||
1123 | logf("Note: info.byte_count differs from expected value by " | ||
1124 | "%ld bytes", labs((long) (expected - info.byte_count))); | ||
1125 | info.byte_count = 0; | ||
1126 | info.frame_count = 0; | ||
1127 | info.file_time = 0; | ||
1128 | info.enc_padding = 0; | ||
1129 | |||
1130 | /* Even if the bitrate was based on "known bad" values, it | ||
1131 | * should still be better for VBR files than using the bitrate | ||
1132 | * of the first audio frame. | ||
1133 | */ | ||
1134 | } | ||
1135 | } | ||
1136 | |||
1137 | entry->bitrate = info.bitrate; | ||
1138 | entry->frequency = info.frequency; | ||
1139 | entry->version = info.version; | ||
1140 | entry->layer = info.layer; | ||
1141 | switch(entry->layer) { | ||
1142 | #if CONFIG_CODEC==SWCODEC | ||
1143 | case 0: | ||
1144 | entry->codectype=AFMT_MPA_L1; | ||
1145 | break; | ||
1146 | #endif | ||
1147 | case 1: | ||
1148 | entry->codectype=AFMT_MPA_L2; | ||
1149 | break; | ||
1150 | case 2: | ||
1151 | entry->codectype=AFMT_MPA_L3; | ||
1152 | break; | ||
1153 | } | ||
1154 | |||
1155 | /* If the file time hasn't been established, this may be a fixed | ||
1156 | rate MP3, so just use the default formula */ | ||
1157 | |||
1158 | filetime = info.file_time; | ||
1159 | |||
1160 | if(filetime == 0) | ||
1161 | { | ||
1162 | /* Prevent a division by zero */ | ||
1163 | if (info.bitrate < 8) | ||
1164 | filetime = 0; | ||
1165 | else | ||
1166 | filetime = (entry->filesize - bytecount) / (info.bitrate / 8); | ||
1167 | /* bitrate is in kbps so this delivers milliseconds. Doing bitrate / 8 | ||
1168 | * instead of filesize * 8 is exact, because mpeg audio bitrates are | ||
1169 | * always multiples of 8, and it avoids overflows. */ | ||
1170 | } | ||
1171 | |||
1172 | entry->frame_count = info.frame_count; | ||
1173 | |||
1174 | entry->vbr = info.is_vbr; | ||
1175 | entry->has_toc = info.has_toc; | ||
1176 | |||
1177 | #if CONFIG_CODEC==SWCODEC | ||
1178 | entry->lead_trim = info.enc_delay; | ||
1179 | entry->tail_trim = info.enc_padding; | ||
1180 | #endif | ||
1181 | |||
1182 | memcpy(entry->toc, info.toc, sizeof(info.toc)); | ||
1183 | |||
1184 | entry->vbr_header_pos = info.vbr_header_pos; | ||
1185 | |||
1186 | /* Update the seek point for the first playable frame */ | ||
1187 | entry->first_frame_offset = bytecount; | ||
1188 | logf("First frame is at %lx", entry->first_frame_offset); | ||
1189 | |||
1190 | return filetime; | ||
1191 | } | ||
1192 | |||
1193 | /* | ||
1194 | * Checks all relevant information (such as ID3v1 tag, ID3v2 tag, length etc) | ||
1195 | * about an MP3 file and updates it's entry accordingly. | ||
1196 | * | ||
1197 | Note, that this returns true for successful, false for error! */ | ||
1198 | bool get_mp3_metadata(int fd, struct mp3entry *entry, const char *filename) | ||
1199 | { | ||
1200 | #if CONFIG_CODEC != SWCODEC | ||
1201 | memset(entry, 0, sizeof(struct mp3entry)); | ||
1202 | #endif | ||
1203 | |||
1204 | strncpy(entry->path, filename, sizeof(entry->path)); | ||
1205 | |||
1206 | entry->title = NULL; | ||
1207 | entry->filesize = filesize(fd); | ||
1208 | entry->id3v2len = getid3v2len(fd); | ||
1209 | entry->tracknum = 0; | ||
1210 | entry->discnum = 0; | ||
1211 | |||
1212 | if (entry->id3v2len) | ||
1213 | setid3v2title(fd, entry); | ||
1214 | int len = getsonglength(fd, entry); | ||
1215 | if (len < 0) | ||
1216 | return false; | ||
1217 | entry->length = len; | ||
1218 | |||
1219 | /* Subtract the meta information from the file size to get | ||
1220 | the true size of the MP3 stream */ | ||
1221 | entry->filesize -= entry->first_frame_offset; | ||
1222 | |||
1223 | /* only seek to end of file if no id3v2 tags were found */ | ||
1224 | if (!entry->id3v2len) { | ||
1225 | setid3v1title(fd, entry); | ||
1226 | } | ||
1227 | |||
1228 | if(!entry->length || (entry->filesize < 8 )) | ||
1229 | /* no song length or less than 8 bytes is hereby considered to be an | ||
1230 | invalid mp3 and won't be played by us! */ | ||
1231 | return false; | ||
1232 | |||
1233 | return true; | ||
1234 | } | ||
1235 | |||
1236 | /* Note, that this returns false for successful, true for error! */ | ||
1237 | bool mp3info(struct mp3entry *entry, const char *filename) | ||
1238 | { | ||
1239 | int fd; | ||
1240 | bool result; | ||
1241 | |||
1242 | fd = open(filename, O_RDONLY); | ||
1243 | if (fd < 0) | ||
1244 | return true; | ||
1245 | |||
1246 | result = !get_mp3_metadata(fd, entry, filename); | ||
1247 | |||
1248 | close(fd); | ||
1249 | |||
1250 | return result; | ||
1251 | } | ||
1252 | |||
1253 | void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig) | ||
1254 | { | ||
1255 | long offset; | ||
1256 | if (orig > dest) | ||
1257 | offset = - ((size_t)orig - (size_t)dest); | ||
1258 | else | ||
1259 | offset = (size_t)dest - (size_t)orig; | ||
1260 | |||
1261 | if (entry->title) | ||
1262 | entry->title += offset; | ||
1263 | if (entry->artist) | ||
1264 | entry->artist += offset; | ||
1265 | if (entry->album) | ||
1266 | entry->album += offset; | ||
1267 | if (entry->genre_string && !id3_is_genre_string(entry->genre_string)) | ||
1268 | /* Don't adjust that if it points to an entry of the "genres" array */ | ||
1269 | entry->genre_string += offset; | ||
1270 | if (entry->track_string) | ||
1271 | entry->track_string += offset; | ||
1272 | if (entry->disc_string) | ||
1273 | entry->disc_string += offset; | ||
1274 | if (entry->year_string) | ||
1275 | entry->year_string += offset; | ||
1276 | if (entry->composer) | ||
1277 | entry->composer += offset; | ||
1278 | if (entry->comment) | ||
1279 | entry->comment += offset; | ||
1280 | if (entry->albumartist) | ||
1281 | entry->albumartist += offset; | ||
1282 | if (entry->grouping) | ||
1283 | entry->grouping += offset; | ||
1284 | #if CONFIG_CODEC == SWCODEC | ||
1285 | if (entry->track_gain_string) | ||
1286 | entry->track_gain_string += offset; | ||
1287 | if (entry->album_gain_string) | ||
1288 | entry->album_gain_string += offset; | ||
1289 | #endif | ||
1290 | if (entry->mb_track_id) | ||
1291 | entry->mb_track_id += offset; | ||
1292 | } | ||
1293 | |||
1294 | void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig) | ||
1295 | { | ||
1296 | memcpy(dest, orig, sizeof(struct mp3entry)); | ||
1297 | adjust_mp3entry(dest, dest, orig); | ||
1298 | } | ||
1299 | |||
1300 | #ifdef DEBUG_STANDALONE | ||
1301 | |||
1302 | char *secs2str(int ms) | ||
1303 | { | ||
1304 | static char buffer[32]; | ||
1305 | int secs = ms/1000; | ||
1306 | ms %= 1000; | ||
1307 | snprintf(buffer, sizeof(buffer), "%d:%02d.%d", secs/60, secs%60, ms/100); | ||
1308 | return buffer; | ||
1309 | } | ||
1310 | |||
1311 | int main(int argc, char **argv) | ||
1312 | { | ||
1313 | int i; | ||
1314 | for(i=1; i<argc; i++) { | ||
1315 | struct mp3entry mp3; | ||
1316 | mp3.album = "Bogus"; | ||
1317 | if(mp3info(&mp3, argv[i], false)) { | ||
1318 | printf("Failed to get %s\n", argv[i]); | ||
1319 | return 0; | ||
1320 | } | ||
1321 | |||
1322 | printf("****** File: %s\n" | ||
1323 | " Title: %s\n" | ||
1324 | " Artist: %s\n" | ||
1325 | " Album: %s\n" | ||
1326 | " Genre: %s (%d) \n" | ||
1327 | " Composer: %s\n" | ||
1328 | " Year: %s (%d)\n" | ||
1329 | " Track: %s (%d)\n" | ||
1330 | " Length: %s / %d s\n" | ||
1331 | " Bitrate: %d\n" | ||
1332 | " Frequency: %d\n", | ||
1333 | argv[i], | ||
1334 | mp3.title?mp3.title:"<blank>", | ||
1335 | mp3.artist?mp3.artist:"<blank>", | ||
1336 | mp3.album?mp3.album:"<blank>", | ||
1337 | mp3.genre_string?mp3.genre_string:"<blank>", | ||
1338 | mp3.genre, | ||
1339 | mp3.composer?mp3.composer:"<blank>", | ||
1340 | mp3.year_string?mp3.year_string:"<blank>", | ||
1341 | mp3.year, | ||
1342 | mp3.track_string?mp3.track_string:"<blank>", | ||
1343 | mp3.tracknum, | ||
1344 | secs2str(mp3.length), | ||
1345 | mp3.length/1000, | ||
1346 | mp3.bitrate, | ||
1347 | mp3.frequency); | ||
1348 | } | ||
1349 | |||
1350 | return 0; | ||
1351 | } | ||
1352 | |||
1353 | #endif | ||