diff options
author | Yoshihisa Uchida <uchida@rockbox.org> | 2010-05-15 09:15:24 +0000 |
---|---|---|
committer | Yoshihisa Uchida <uchida@rockbox.org> | 2010-05-15 09:15:24 +0000 |
commit | 7a6a8c64fe27cae8efddc8f803af22c302cca4e7 (patch) | |
tree | f44e19620d733a445d78c0945bed42fc9563ecf1 /apps/metadata/mp3.c | |
parent | 6bb9384b709c1e143bb7444317dde20bf9f5cb7f (diff) | |
download | rockbox-7a6a8c64fe27cae8efddc8f803af22c302cca4e7.tar.gz rockbox-7a6a8c64fe27cae8efddc8f803af22c302cca4e7.zip |
ID3 tags parser separates from metadata/mp3.c
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26044 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/metadata/mp3.c')
-rw-r--r-- | apps/metadata/mp3.c | 1029 |
1 files changed, 4 insertions, 1025 deletions
diff --git a/apps/metadata/mp3.c b/apps/metadata/mp3.c index 0f786bd52b..15c0a5059b 100644 --- a/apps/metadata/mp3.c +++ b/apps/metadata/mp3.c | |||
@@ -30,983 +30,17 @@ | |||
30 | 30 | ||
31 | #include <stdio.h> | 31 | #include <stdio.h> |
32 | #include <stdlib.h> | 32 | #include <stdlib.h> |
33 | #include <errno.h> | 33 | #include <string.h> |
34 | #include <stdbool.h> | 34 | #include <stdbool.h> |
35 | #include <stddef.h> | ||
36 | #include <ctype.h> | ||
37 | #include "string-extra.h" | ||
38 | #include "config.h" | 35 | #include "config.h" |
39 | #include "file.h" | 36 | #include "file.h" |
40 | #include "logf.h" | 37 | #include "logf.h" |
41 | 38 | ||
42 | #include "mp3data.h" | ||
43 | #include "system.h" | 39 | #include "system.h" |
44 | #include "replaygain.h" | 40 | #include "metadata.h" |
45 | #include "rbunicode.h" | 41 | #include "mp3data.h" |
46 | #include "metadata_parsers.h" | ||
47 | #if CONFIG_CODEC == SWCODEC | ||
48 | #include "metadata_common.h" | 42 | #include "metadata_common.h" |
49 | #endif | 43 | #include "metadata_parsers.h" |
50 | |||
51 | static unsigned long unsync(unsigned long b0, | ||
52 | unsigned long b1, | ||
53 | unsigned long b2, | ||
54 | unsigned long b3) | ||
55 | { | ||
56 | return (((long)(b0 & 0x7F) << (3*7)) | | ||
57 | ((long)(b1 & 0x7F) << (2*7)) | | ||
58 | ((long)(b2 & 0x7F) << (1*7)) | | ||
59 | ((long)(b3 & 0x7F) << (0*7))); | ||
60 | } | ||
61 | |||
62 | static const char* const genres[] = { | ||
63 | "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", | ||
64 | "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", | ||
65 | "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", | ||
66 | "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", | ||
67 | "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", | ||
68 | "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "AlternRock", | ||
69 | "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", | ||
70 | "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", | ||
71 | "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", | ||
72 | "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", | ||
73 | "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", | ||
74 | "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", | ||
75 | "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", | ||
76 | |||
77 | /* winamp extensions */ | ||
78 | "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion", "Bebob", | ||
79 | "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", | ||
80 | "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", | ||
81 | "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", | ||
82 | "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", | ||
83 | "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", | ||
84 | "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", | ||
85 | "Duet", "Punk Rock", "Drum Solo", "A capella", "Euro-House", "Dance Hall", | ||
86 | "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", | ||
87 | "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", | ||
88 | "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", | ||
89 | "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "Jpop", | ||
90 | "Synthpop" | ||
91 | }; | ||
92 | |||
93 | char* id3_get_num_genre(unsigned int genre_num) | ||
94 | { | ||
95 | if (genre_num < ARRAYLEN(genres)) | ||
96 | return (char*)genres[genre_num]; | ||
97 | return NULL; | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | HOW TO ADD ADDITIONAL ID3 VERSION 2 TAGS | ||
102 | Code and comments by Thomas Paul Diffenbach | ||
103 | |||
104 | To add another ID3v2 Tag, do the following: | ||
105 | 1. add a char* named for the tag to struct mp3entry in id3.h, | ||
106 | (I (tpd) prefer to use char* rather than ints, even for what seems like | ||
107 | numerical values, for cases where a number won't do, e.g., | ||
108 | YEAR: "circa 1765", "1790/1977" (composed/performed), "28 Feb 1969" | ||
109 | TRACK: "1/12", "1 of 12", GENRE: "Freeform genre name" | ||
110 | Text is more flexible, and as the main use of id3 data is to | ||
111 | display it, converting it to an int just means reconverting to | ||
112 | display it, at a runtime cost.) | ||
113 | |||
114 | 2. If any special processing beyond copying the tag value from the Id3 | ||
115 | block to the struct mp3entry is rrequired (such as converting to an | ||
116 | int), write a function to perform this special processing. | ||
117 | |||
118 | This function's prototype must match that of | ||
119 | typedef tagPostProcessFunc, that is it must be: | ||
120 | int func( struct mp3entry*, char* tag, int bufferpos ) | ||
121 | the first argument is a pointer to the current mp3entry structure the | ||
122 | second argument is a pointer to the null terminated string value of the | ||
123 | tag found the third argument is the offset of the next free byte in the | ||
124 | mp3entry's buffer your function should return the corrected offset; if | ||
125 | you don't lengthen or shorten the tag string, you can return the third | ||
126 | argument unchanged. | ||
127 | |||
128 | Unless you have a good reason no to, make the function static. | ||
129 | TO JUST COPY THE TAG NO SPECIAL PROCESSING FUNCTION IS NEEDED. | ||
130 | |||
131 | 3. add one or more entries to the tagList array, using the format: | ||
132 | char* ID3 Tag symbolic name -- see the ID3 specification for these, | ||
133 | sizeof() that name minus 1, | ||
134 | offsetof( struct mp3entry, variable_name_in_struct_mp3entry ), | ||
135 | pointer to your special processing function or NULL | ||
136 | if you need no special processing | ||
137 | flag indicating if this tag is binary or textual | ||
138 | Many ID3 symbolic names come in more than one form. You can add both | ||
139 | forms, each referencing the same variable in struct mp3entry. | ||
140 | If both forms are present, the last found will be used. | ||
141 | Note that the offset can be zero, in which case no entry will be set | ||
142 | in the mp3entry struct; the frame is still read into the buffer and | ||
143 | the special processing function is called (several times, if there | ||
144 | are several frames with the same name). | ||
145 | |||
146 | 4. Alternately, use the TAG_LIST_ENTRY macro with | ||
147 | ID3 tag symbolic name, | ||
148 | variable in struct mp3entry, | ||
149 | special processing function address | ||
150 | |||
151 | 5. Add code to wps-display.c function get_tag to assign a printf-like | ||
152 | format specifier for the tag */ | ||
153 | |||
154 | /* Structure for ID3 Tag extraction information */ | ||
155 | struct tag_resolver { | ||
156 | const char* tag; | ||
157 | int tag_length; | ||
158 | size_t offset; | ||
159 | int (*ppFunc)(struct mp3entry*, char* tag, int bufferpos); | ||
160 | bool binary; | ||
161 | }; | ||
162 | |||
163 | static bool global_ff_found; | ||
164 | |||
165 | static int unsynchronize(char* tag, int len, bool *ff_found) | ||
166 | { | ||
167 | int i; | ||
168 | unsigned char c; | ||
169 | unsigned char *rp, *wp; | ||
170 | |||
171 | wp = rp = (unsigned char *)tag; | ||
172 | |||
173 | rp = (unsigned char *)tag; | ||
174 | for(i = 0;i < len;i++) { | ||
175 | /* Read the next byte and write it back, but don't increment the | ||
176 | write pointer */ | ||
177 | c = *rp++; | ||
178 | *wp = c; | ||
179 | if(*ff_found) { | ||
180 | /* Increment the write pointer if it isn't an unsynch pattern */ | ||
181 | if(c != 0) | ||
182 | wp++; | ||
183 | *ff_found = false; | ||
184 | } else { | ||
185 | if(c == 0xff) | ||
186 | *ff_found = true; | ||
187 | wp++; | ||
188 | } | ||
189 | } | ||
190 | return (long)wp - (long)tag; | ||
191 | } | ||
192 | |||
193 | static int unsynchronize_frame(char* tag, int len) | ||
194 | { | ||
195 | bool ff_found = false; | ||
196 | |||
197 | return unsynchronize(tag, len, &ff_found); | ||
198 | } | ||
199 | |||
200 | static int read_unsynched(int fd, void *buf, int len) | ||
201 | { | ||
202 | int i; | ||
203 | int rc; | ||
204 | int remaining = len; | ||
205 | char *wp; | ||
206 | char *rp; | ||
207 | |||
208 | wp = buf; | ||
209 | |||
210 | while(remaining) { | ||
211 | rp = wp; | ||
212 | rc = read(fd, rp, remaining); | ||
213 | if(rc <= 0) | ||
214 | return rc; | ||
215 | |||
216 | i = unsynchronize(wp, remaining, &global_ff_found); | ||
217 | remaining -= i; | ||
218 | wp += i; | ||
219 | } | ||
220 | |||
221 | return len; | ||
222 | } | ||
223 | |||
224 | static int skip_unsynched(int fd, int len) | ||
225 | { | ||
226 | int rc; | ||
227 | int remaining = len; | ||
228 | int rlen; | ||
229 | char buf[32]; | ||
230 | |||
231 | while(remaining) { | ||
232 | rlen = MIN(sizeof(buf), (unsigned int)remaining); | ||
233 | rc = read(fd, buf, rlen); | ||
234 | if(rc <= 0) | ||
235 | return rc; | ||
236 | |||
237 | remaining -= unsynchronize(buf, rlen, &global_ff_found); | ||
238 | } | ||
239 | |||
240 | return len; | ||
241 | } | ||
242 | |||
243 | /* parse numeric value from string */ | ||
244 | static int parsetracknum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
245 | { | ||
246 | entry->tracknum = atoi( tag ); | ||
247 | return bufferpos; | ||
248 | } | ||
249 | |||
250 | /* parse numeric value from string */ | ||
251 | static int parsediscnum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
252 | { | ||
253 | entry->discnum = atoi( tag ); | ||
254 | return bufferpos; | ||
255 | } | ||
256 | |||
257 | /* parse numeric value from string */ | ||
258 | static int parseyearnum( struct mp3entry* entry, char* tag, int bufferpos ) | ||
259 | { | ||
260 | entry->year = atoi( tag ); | ||
261 | return bufferpos; | ||
262 | } | ||
263 | |||
264 | /* parse numeric genre from string, version 2.2 and 2.3 */ | ||
265 | static int parsegenre( struct mp3entry* entry, char* tag, int bufferpos ) | ||
266 | { | ||
267 | if(entry->id3version >= ID3_VER_2_4) { | ||
268 | /* In version 2.4 and up, there are no parentheses, and the genre frame | ||
269 | is a list of strings, either numbers or text. */ | ||
270 | |||
271 | /* Is it a number? */ | ||
272 | if(isdigit(tag[0])) { | ||
273 | entry->genre_string = id3_get_num_genre(atoi( tag )); | ||
274 | return tag - entry->id3v2buf; | ||
275 | } else { | ||
276 | entry->genre_string = tag; | ||
277 | return bufferpos; | ||
278 | } | ||
279 | } else { | ||
280 | if( tag[0] == '(' && tag[1] != '(' ) { | ||
281 | entry->genre_string = id3_get_num_genre(atoi( tag + 1 )); | ||
282 | return tag - entry->id3v2buf; | ||
283 | } | ||
284 | else { | ||
285 | entry->genre_string = tag; | ||
286 | return bufferpos; | ||
287 | } | ||
288 | } | ||
289 | } | ||
290 | |||
291 | /* parse user defined text, looking for album artist and replaygain | ||
292 | * information. | ||
293 | */ | ||
294 | static int parseuser( struct mp3entry* entry, char* tag, int bufferpos ) | ||
295 | { | ||
296 | char* value = NULL; | ||
297 | int desc_len = strlen(tag); | ||
298 | int value_len = 0; | ||
299 | |||
300 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { | ||
301 | /* At least part of the value was read, so we can safely try to | ||
302 | * parse it | ||
303 | */ | ||
304 | value = tag + desc_len + 1; | ||
305 | value_len = bufferpos - (tag - entry->id3v2buf); | ||
306 | |||
307 | if (!strcasecmp(tag, "ALBUM ARTIST")) { | ||
308 | strlcpy(tag, value, value_len); | ||
309 | entry->albumartist = tag; | ||
310 | #if CONFIG_CODEC == SWCODEC | ||
311 | } else { | ||
312 | value_len = parse_replaygain(tag, value, entry, tag, | ||
313 | value_len); | ||
314 | #endif | ||
315 | } | ||
316 | } | ||
317 | |||
318 | return tag - entry->id3v2buf + value_len; | ||
319 | } | ||
320 | |||
321 | #if CONFIG_CODEC == SWCODEC | ||
322 | /* parse RVA2 binary data and convert to replaygain information. */ | ||
323 | static int parserva2( struct mp3entry* entry, char* tag, int bufferpos ) | ||
324 | { | ||
325 | int desc_len = strlen(tag); | ||
326 | int start_pos = tag - entry->id3v2buf; | ||
327 | int end_pos = start_pos + desc_len + 5; | ||
328 | int value_len = 0; | ||
329 | unsigned char* value = tag + desc_len + 1; | ||
330 | |||
331 | /* Only parse RVA2 replaygain tags if tag version == 2.4 and channel | ||
332 | * type is master volume. | ||
333 | */ | ||
334 | if (entry->id3version == ID3_VER_2_4 && end_pos < bufferpos | ||
335 | && *value++ == 1) { | ||
336 | long gain = 0; | ||
337 | long peak = 0; | ||
338 | long peakbits; | ||
339 | long peakbytes; | ||
340 | bool album = false; | ||
341 | |||
342 | /* The RVA2 specification is unclear on some things (id string and | ||
343 | * peak volume), but this matches how Quod Libet use them. | ||
344 | */ | ||
345 | |||
346 | gain = (int16_t) ((value[0] << 8) | value[1]); | ||
347 | value += 2; | ||
348 | peakbits = *value++; | ||
349 | peakbytes = (peakbits + 7) / 8; | ||
350 | |||
351 | /* Only use the topmost 24 bits for peak volume */ | ||
352 | if (peakbytes > 3) { | ||
353 | peakbytes = 3; | ||
354 | } | ||
355 | |||
356 | /* Make sure the peak bits were read */ | ||
357 | if (end_pos + peakbytes < bufferpos) { | ||
358 | long shift = ((8 - (peakbits & 7)) & 7) + (3 - peakbytes) * 8; | ||
359 | |||
360 | for ( ; peakbytes; peakbytes--) { | ||
361 | peak <<= 8; | ||
362 | peak += *value++; | ||
363 | } | ||
364 | |||
365 | peak <<= shift; | ||
366 | |||
367 | if (peakbits > 24) { | ||
368 | peak += *value >> (8 - shift); | ||
369 | } | ||
370 | } | ||
371 | |||
372 | if (strcasecmp(tag, "album") == 0) { | ||
373 | album = true; | ||
374 | } else if (strcasecmp(tag, "track") != 0) { | ||
375 | /* Only accept non-track values if we don't have any previous | ||
376 | * value. | ||
377 | */ | ||
378 | if (entry->track_gain != 0) { | ||
379 | return start_pos; | ||
380 | } | ||
381 | } | ||
382 | |||
383 | value_len = parse_replaygain_int(album, gain, peak * 2, entry, | ||
384 | tag, sizeof(entry->id3v2buf) - start_pos); | ||
385 | } | ||
386 | |||
387 | return start_pos + value_len; | ||
388 | } | ||
389 | #endif | ||
390 | |||
391 | static int parsembtid( struct mp3entry* entry, char* tag, int bufferpos ) | ||
392 | { | ||
393 | char* value = NULL; | ||
394 | int desc_len = strlen(tag); | ||
395 | /*DEBUGF("MBID len: %d\n", desc_len);*/ | ||
396 | /* Musicbrainz track IDs are always 36 chars long */ | ||
397 | const size_t mbtid_len = 36; | ||
398 | |||
399 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) | ||
400 | { | ||
401 | value = tag + desc_len + 1; | ||
402 | |||
403 | if (strcasecmp(tag, "http://musicbrainz.org") == 0) | ||
404 | { | ||
405 | if (mbtid_len == strlen(value)) | ||
406 | { | ||
407 | entry->mb_track_id = value; | ||
408 | return bufferpos + mbtid_len + 1; | ||
409 | } | ||
410 | } | ||
411 | } | ||
412 | |||
413 | return bufferpos; | ||
414 | } | ||
415 | |||
416 | static const struct tag_resolver taglist[] = { | ||
417 | { "TPE1", 4, offsetof(struct mp3entry, artist), NULL, false }, | ||
418 | { "TP1", 3, offsetof(struct mp3entry, artist), NULL, false }, | ||
419 | { "TIT2", 4, offsetof(struct mp3entry, title), NULL, false }, | ||
420 | { "TT2", 3, offsetof(struct mp3entry, title), NULL, false }, | ||
421 | { "TALB", 4, offsetof(struct mp3entry, album), NULL, false }, | ||
422 | { "TAL", 3, offsetof(struct mp3entry, album), NULL, false }, | ||
423 | { "TRK", 3, offsetof(struct mp3entry, track_string), &parsetracknum, false }, | ||
424 | { "TPOS", 4, offsetof(struct mp3entry, disc_string), &parsediscnum, false }, | ||
425 | { "TRCK", 4, offsetof(struct mp3entry, track_string), &parsetracknum, false }, | ||
426 | { "TDRC", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
427 | { "TYER", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
428 | { "TYE", 3, offsetof(struct mp3entry, year_string), &parseyearnum, false }, | ||
429 | { "TCOM", 4, offsetof(struct mp3entry, composer), NULL, false }, | ||
430 | { "TPE2", 4, offsetof(struct mp3entry, albumartist), NULL, false }, | ||
431 | { "TP2", 3, offsetof(struct mp3entry, albumartist), NULL, false }, | ||
432 | { "TIT1", 4, offsetof(struct mp3entry, grouping), NULL, false }, | ||
433 | { "TT1", 3, offsetof(struct mp3entry, grouping), NULL, false }, | ||
434 | { "COMM", 4, offsetof(struct mp3entry, comment), NULL, false }, | ||
435 | { "COM", 3, offsetof(struct mp3entry, comment), NULL, false }, | ||
436 | { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre, false }, | ||
437 | { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre, false }, | ||
438 | { "TXXX", 4, 0, &parseuser, false }, | ||
439 | #if CONFIG_CODEC == SWCODEC | ||
440 | { "RVA2", 4, 0, &parserva2, true }, | ||
441 | #endif | ||
442 | { "UFID", 4, 0, &parsembtid, false }, | ||
443 | }; | ||
444 | |||
445 | #define TAGLIST_SIZE ((int)ARRAYLEN(taglist)) | ||
446 | |||
447 | /* Get the length of an ID3 string in the given encoding. Returns the length | ||
448 | * in bytes, including end nil, or -1 if the encoding is unknown. | ||
449 | */ | ||
450 | static int unicode_len(char encoding, const void* string) | ||
451 | { | ||
452 | int len = 0; | ||
453 | |||
454 | if (encoding == 0x01 || encoding == 0x02) { | ||
455 | char first; | ||
456 | const char *s = string; | ||
457 | /* string might be unaligned, so using short* can crash on ARM and SH1 */ | ||
458 | do { | ||
459 | first = *s++; | ||
460 | } while ((first | *s++) != 0); | ||
461 | |||
462 | len = s - (const char*) string; | ||
463 | } else { | ||
464 | len = strlen((char*) string) + 1; | ||
465 | } | ||
466 | |||
467 | return len; | ||
468 | } | ||
469 | |||
470 | /* Checks to see if the passed in string is a 16-bit wide Unicode v2 | ||
471 | string. If it is, we convert it to a UTF-8 string. If it's not unicode, | ||
472 | we convert from the default codepage */ | ||
473 | static int unicode_munge(char* string, char* utf8buf, int *len) { | ||
474 | long tmp; | ||
475 | bool le = false; | ||
476 | int i = 0; | ||
477 | unsigned char *str = (unsigned char *)string; | ||
478 | int templen = 0; | ||
479 | unsigned char* utf8 = (unsigned char *)utf8buf; | ||
480 | |||
481 | switch (str[0]) { | ||
482 | case 0x00: /* Type 0x00 is ordinary ISO 8859-1 */ | ||
483 | str++; | ||
484 | (*len)--; | ||
485 | utf8 = iso_decode(str, utf8, -1, *len); | ||
486 | *utf8 = 0; | ||
487 | *len = (unsigned long)utf8 - (unsigned long)utf8buf; | ||
488 | break; | ||
489 | |||
490 | case 0x01: /* Unicode with or without BOM */ | ||
491 | case 0x02: | ||
492 | (*len)--; | ||
493 | str++; | ||
494 | |||
495 | /* Handle frames with more than one string | ||
496 | (needed for TXXX frames).*/ | ||
497 | do { | ||
498 | tmp = bytes2int(0, 0, str[0], str[1]); | ||
499 | |||
500 | /* Now check if there is a BOM | ||
501 | (zero-width non-breaking space, 0xfeff) | ||
502 | and if it is in little or big endian format */ | ||
503 | if(tmp == 0xfffe) { /* Little endian? */ | ||
504 | le = true; | ||
505 | str += 2; | ||
506 | (*len)-=2; | ||
507 | } else if(tmp == 0xfeff) { /* Big endian? */ | ||
508 | str += 2; | ||
509 | (*len)-=2; | ||
510 | } else | ||
511 | /* If there is no BOM (which is a specification violation), | ||
512 | let's try to guess it. If one of the bytes is 0x00, it is | ||
513 | probably the most significant one. */ | ||
514 | if(str[1] == 0) | ||
515 | le = true; | ||
516 | |||
517 | do { | ||
518 | if(le) | ||
519 | utf8 = utf16LEdecode(str, utf8, 1); | ||
520 | else | ||
521 | utf8 = utf16BEdecode(str, utf8, 1); | ||
522 | |||
523 | str+=2; | ||
524 | i += 2; | ||
525 | } while((str[0] || str[1]) && (i < *len)); | ||
526 | |||
527 | *utf8++ = 0; /* Terminate the string */ | ||
528 | templen += (strlen(&utf8buf[templen]) + 1); | ||
529 | str += 2; | ||
530 | i+=2; | ||
531 | } while(i < *len); | ||
532 | *len = templen - 1; | ||
533 | break; | ||
534 | |||
535 | case 0x03: /* UTF-8 encoded string */ | ||
536 | for(i=0; i < *len; i++) | ||
537 | utf8[i] = str[i+1]; | ||
538 | (*len)--; | ||
539 | break; | ||
540 | |||
541 | default: /* Plain old string */ | ||
542 | utf8 = iso_decode(str, utf8, -1, *len); | ||
543 | *utf8 = 0; | ||
544 | *len = (unsigned long)utf8 - (unsigned long)utf8buf; | ||
545 | break; | ||
546 | } | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | /* | ||
551 | * Sets the title of an MP3 entry based on its ID3v1 tag. | ||
552 | * | ||
553 | * Arguments: file - the MP3 file to scen for a ID3v1 tag | ||
554 | * entry - the entry to set the title in | ||
555 | * | ||
556 | * Returns: true if a title was found and created, else false | ||
557 | */ | ||
558 | bool setid3v1title(int fd, struct mp3entry *entry) | ||
559 | { | ||
560 | unsigned char buffer[128]; | ||
561 | static const char offsets[] = {3, 33, 63, 97, 93, 125, 127}; | ||
562 | int i, j; | ||
563 | unsigned char* utf8; | ||
564 | |||
565 | if (-1 == lseek(fd, -128, SEEK_END)) | ||
566 | return false; | ||
567 | |||
568 | if (read(fd, buffer, sizeof buffer) != sizeof buffer) | ||
569 | return false; | ||
570 | |||
571 | if (strncmp((char *)buffer, "TAG", 3)) | ||
572 | return false; | ||
573 | |||
574 | entry->id3v1len = 128; | ||
575 | entry->id3version = ID3_VER_1_0; | ||
576 | |||
577 | for (i=0; i < (int)sizeof offsets; i++) { | ||
578 | unsigned char* ptr = (unsigned char *)buffer + offsets[i]; | ||
579 | |||
580 | switch(i) { | ||
581 | case 0: | ||
582 | case 1: | ||
583 | case 2: | ||
584 | /* kill trailing space in strings */ | ||
585 | for (j=29; j && (ptr[j]==0 || ptr[j]==' '); j--) | ||
586 | ptr[j] = 0; | ||
587 | /* convert string to utf8 */ | ||
588 | utf8 = (unsigned char *)entry->id3v1buf[i]; | ||
589 | utf8 = iso_decode(ptr, utf8, -1, 30); | ||
590 | /* make sure string is terminated */ | ||
591 | *utf8 = 0; | ||
592 | break; | ||
593 | |||
594 | case 3: | ||
595 | /* kill trailing space in strings */ | ||
596 | for (j=27; j && (ptr[j]==0 || ptr[j]==' '); j--) | ||
597 | ptr[j] = 0; | ||
598 | /* convert string to utf8 */ | ||
599 | utf8 = (unsigned char *)entry->id3v1buf[3]; | ||
600 | utf8 = iso_decode(ptr, utf8, -1, 28); | ||
601 | /* make sure string is terminated */ | ||
602 | *utf8 = 0; | ||
603 | break; | ||
604 | |||
605 | case 4: | ||
606 | ptr[4] = 0; | ||
607 | entry->year = atoi((char *)ptr); | ||
608 | break; | ||
609 | |||
610 | case 5: | ||
611 | /* id3v1.1 uses last two bytes of comment field for track | ||
612 | number: first must be 0 and second is track num */ | ||
613 | if (!ptr[0] && ptr[1]) { | ||
614 | entry->tracknum = ptr[1]; | ||
615 | entry->id3version = ID3_VER_1_1; | ||
616 | } | ||
617 | break; | ||
618 | |||
619 | case 6: | ||
620 | /* genre */ | ||
621 | entry->genre_string = id3_get_num_genre(ptr[0]); | ||
622 | break; | ||
623 | } | ||
624 | } | ||
625 | |||
626 | entry->title = entry->id3v1buf[0]; | ||
627 | entry->artist = entry->id3v1buf[1]; | ||
628 | entry->album = entry->id3v1buf[2]; | ||
629 | entry->comment = entry->id3v1buf[3]; | ||
630 | |||
631 | return true; | ||
632 | } | ||
633 | |||
634 | |||
635 | /* | ||
636 | * Sets the title of an MP3 entry based on its ID3v2 tag. | ||
637 | * | ||
638 | * Arguments: file - the MP3 file to scan for a ID3v2 tag | ||
639 | * entry - the entry to set the title in | ||
640 | * | ||
641 | * Returns: true if a title was found and created, else false | ||
642 | */ | ||
643 | void setid3v2title(int fd, struct mp3entry *entry) | ||
644 | { | ||
645 | int minframesize; | ||
646 | int size; | ||
647 | long bufferpos = 0, totframelen, framelen; | ||
648 | char header[10]; | ||
649 | char tmp[4]; | ||
650 | unsigned char version; | ||
651 | char *buffer = entry->id3v2buf; | ||
652 | int bytesread = 0; | ||
653 | int buffersize = sizeof(entry->id3v2buf); | ||
654 | unsigned char global_flags; | ||
655 | int flags; | ||
656 | int skip; | ||
657 | bool global_unsynch = false; | ||
658 | bool unsynch = false; | ||
659 | int i, j; | ||
660 | int rc; | ||
661 | #if CONFIG_CODEC == SWCODEC | ||
662 | bool itunes_gapless = false; | ||
663 | #endif | ||
664 | |||
665 | global_ff_found = false; | ||
666 | |||
667 | /* Bail out if the tag is shorter than 10 bytes */ | ||
668 | if(entry->id3v2len < 10) | ||
669 | return; | ||
670 | |||
671 | /* Read the ID3 tag version from the header */ | ||
672 | lseek(fd, 0, SEEK_SET); | ||
673 | if(10 != read(fd, header, 10)) | ||
674 | return; | ||
675 | |||
676 | /* Get the total ID3 tag size */ | ||
677 | size = entry->id3v2len - 10; | ||
678 | |||
679 | version = header[3]; | ||
680 | switch ( version ) { | ||
681 | case 2: | ||
682 | version = ID3_VER_2_2; | ||
683 | minframesize = 8; | ||
684 | break; | ||
685 | |||
686 | case 3: | ||
687 | version = ID3_VER_2_3; | ||
688 | minframesize = 12; | ||
689 | break; | ||
690 | |||
691 | case 4: | ||
692 | version = ID3_VER_2_4; | ||
693 | minframesize = 12; | ||
694 | break; | ||
695 | |||
696 | default: | ||
697 | /* unsupported id3 version */ | ||
698 | return; | ||
699 | } | ||
700 | entry->id3version = version; | ||
701 | entry->tracknum = entry->year = entry->discnum = 0; | ||
702 | entry->title = entry->artist = entry->album = NULL; /* FIXME incomplete */ | ||
703 | |||
704 | global_flags = header[5]; | ||
705 | |||
706 | /* Skip the extended header if it is present */ | ||
707 | if(global_flags & 0x40) { | ||
708 | if(version == ID3_VER_2_3) { | ||
709 | if(10 != read(fd, header, 10)) | ||
710 | return; | ||
711 | /* The 2.3 extended header size doesn't include the header size | ||
712 | field itself. Also, it is not unsynched. */ | ||
713 | framelen = | ||
714 | bytes2int(header[0], header[1], header[2], header[3]) + 4; | ||
715 | |||
716 | /* Skip the rest of the header */ | ||
717 | lseek(fd, framelen - 10, SEEK_CUR); | ||
718 | } | ||
719 | |||
720 | if(version >= ID3_VER_2_4) { | ||
721 | if(4 != read(fd, header, 4)) | ||
722 | return; | ||
723 | |||
724 | /* The 2.4 extended header size does include the entire header, | ||
725 | so here we can just skip it. This header is unsynched. */ | ||
726 | framelen = unsync(header[0], header[1], | ||
727 | header[2], header[3]); | ||
728 | |||
729 | lseek(fd, framelen - 4, SEEK_CUR); | ||
730 | } | ||
731 | } | ||
732 | |||
733 | /* Is unsynchronization applied? */ | ||
734 | if(global_flags & 0x80) { | ||
735 | global_unsynch = true; | ||
736 | } | ||
737 | |||
738 | /* | ||
739 | * We must have at least minframesize bytes left for the | ||
740 | * remaining frames to be interesting | ||
741 | */ | ||
742 | while (size >= minframesize && bufferpos < buffersize - 1) { | ||
743 | flags = 0; | ||
744 | |||
745 | /* Read frame header and check length */ | ||
746 | if(version >= ID3_VER_2_3) { | ||
747 | if(global_unsynch && version <= ID3_VER_2_3) | ||
748 | rc = read_unsynched(fd, header, 10); | ||
749 | else | ||
750 | rc = read(fd, header, 10); | ||
751 | if(rc != 10) | ||
752 | return; | ||
753 | /* Adjust for the 10 bytes we read */ | ||
754 | size -= 10; | ||
755 | |||
756 | flags = bytes2int(0, 0, header[8], header[9]); | ||
757 | |||
758 | if (version >= ID3_VER_2_4) { | ||
759 | framelen = unsync(header[4], header[5], | ||
760 | header[6], header[7]); | ||
761 | } else { | ||
762 | /* version .3 files don't use synchsafe ints for | ||
763 | * size */ | ||
764 | framelen = bytes2int(header[4], header[5], | ||
765 | header[6], header[7]); | ||
766 | } | ||
767 | } else { | ||
768 | if(6 != read(fd, header, 6)) | ||
769 | return; | ||
770 | /* Adjust for the 6 bytes we read */ | ||
771 | size -= 6; | ||
772 | |||
773 | framelen = bytes2int(0, header[3], header[4], header[5]); | ||
774 | } | ||
775 | |||
776 | logf("framelen = %ld, flags = 0x%04x", framelen, flags); | ||
777 | if(framelen == 0){ | ||
778 | if (header[0] == 0 && header[1] == 0 && header[2] == 0) | ||
779 | return; | ||
780 | else | ||
781 | continue; | ||
782 | } | ||
783 | |||
784 | unsynch = false; | ||
785 | |||
786 | if(flags) | ||
787 | { | ||
788 | skip = 0; | ||
789 | |||
790 | if (version >= ID3_VER_2_4) { | ||
791 | if(flags & 0x0040) { /* Grouping identity */ | ||
792 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ | ||
793 | framelen--; | ||
794 | } | ||
795 | } else { | ||
796 | if(flags & 0x0020) { /* Grouping identity */ | ||
797 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ | ||
798 | framelen--; | ||
799 | } | ||
800 | } | ||
801 | |||
802 | if(flags & 0x000c) /* Compression or encryption */ | ||
803 | { | ||
804 | /* Skip it */ | ||
805 | size -= framelen; | ||
806 | lseek(fd, framelen, SEEK_CUR); | ||
807 | continue; | ||
808 | } | ||
809 | |||
810 | if(flags & 0x0002) /* Unsynchronization */ | ||
811 | unsynch = true; | ||
812 | |||
813 | if (version >= ID3_VER_2_4) { | ||
814 | if(flags & 0x0001) { /* Data length indicator */ | ||
815 | if(4 != read(fd, tmp, 4)) | ||
816 | return; | ||
817 | |||
818 | /* We don't need the data length */ | ||
819 | framelen -= 4; | ||
820 | } | ||
821 | } | ||
822 | } | ||
823 | |||
824 | if (framelen == 0) | ||
825 | continue; | ||
826 | |||
827 | if (framelen < 0) | ||
828 | return; | ||
829 | |||
830 | /* Keep track of the remaining frame size */ | ||
831 | totframelen = framelen; | ||
832 | |||
833 | /* If the frame is larger than the remaining buffer space we try | ||
834 | to read as much as would fit in the buffer */ | ||
835 | if(framelen >= buffersize - bufferpos) | ||
836 | framelen = buffersize - bufferpos - 1; | ||
837 | |||
838 | logf("id3v2 frame: %.4s", header); | ||
839 | |||
840 | /* Check for certain frame headers | ||
841 | |||
842 | 'size' is the amount of frame bytes remaining. We decrement it by | ||
843 | the amount of bytes we read. If we fail to read as many bytes as | ||
844 | we expect, we assume that we can't read from this file, and bail | ||
845 | out. | ||
846 | |||
847 | For each frame. we will iterate over the list of supported tags, | ||
848 | and read the tag into entry's buffer. All tags will be kept as | ||
849 | strings, for cases where a number won't do, e.g., YEAR: "circa | ||
850 | 1765", "1790/1977" (composed/performed), "28 Feb 1969" TRACK: | ||
851 | "1/12", "1 of 12", GENRE: "Freeform genre name" Text is more | ||
852 | flexible, and as the main use of id3 data is to display it, | ||
853 | converting it to an int just means reconverting to display it, at a | ||
854 | runtime cost. | ||
855 | |||
856 | For tags that the current code does convert to ints, a post | ||
857 | processing function will be called via a pointer to function. */ | ||
858 | |||
859 | for (i=0; i<TAGLIST_SIZE; i++) { | ||
860 | const struct tag_resolver* tr = &taglist[i]; | ||
861 | char** ptag = tr->offset ? (char**) (((char*)entry) + tr->offset) | ||
862 | : NULL; | ||
863 | char* tag; | ||
864 | |||
865 | /* Only ID3_VER_2_2 uses frames with three-character names. */ | ||
866 | if (((version == ID3_VER_2_2) && (tr->tag_length != 3)) | ||
867 | || ((version > ID3_VER_2_2) && (tr->tag_length != 4))) { | ||
868 | continue; | ||
869 | } | ||
870 | |||
871 | if( !memcmp( header, tr->tag, tr->tag_length ) ) { | ||
872 | |||
873 | /* found a tag matching one in tagList, and not yet filled */ | ||
874 | tag = buffer + bufferpos; | ||
875 | |||
876 | if(global_unsynch && version <= ID3_VER_2_3) | ||
877 | bytesread = read_unsynched(fd, tag, framelen); | ||
878 | else | ||
879 | bytesread = read(fd, tag, framelen); | ||
880 | |||
881 | if( bytesread != framelen ) | ||
882 | return; | ||
883 | |||
884 | size -= bytesread; | ||
885 | |||
886 | if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) | ||
887 | bytesread = unsynchronize_frame(tag, bytesread); | ||
888 | |||
889 | /* the COMM frame has a 3 char field to hold an ISO-639-1 | ||
890 | * language string and an optional short description; | ||
891 | * remove them so unicode_munge can work correctly | ||
892 | */ | ||
893 | |||
894 | if((tr->tag_length == 4 && !memcmp( header, "COMM", 4)) || | ||
895 | (tr->tag_length == 3 && !memcmp( header, "COM", 3))) { | ||
896 | int offset; | ||
897 | if(!strncmp(tag+4, "iTun", 4)) { | ||
898 | #if CONFIG_CODEC == SWCODEC | ||
899 | /* check for iTunes gapless information */ | ||
900 | if(!strncmp(tag+4, "iTunSMPB", 8)) | ||
901 | itunes_gapless = true; | ||
902 | else | ||
903 | #endif | ||
904 | /* ignore other with iTunes tags */ | ||
905 | break; | ||
906 | } | ||
907 | |||
908 | offset = 3 + unicode_len(*tag, tag + 4); | ||
909 | if(bytesread > offset) { | ||
910 | bytesread -= offset; | ||
911 | memmove(tag + 1, tag + 1 + offset, bytesread - 1); | ||
912 | } | ||
913 | } | ||
914 | |||
915 | /* Attempt to parse Unicode string only if the tag contents | ||
916 | aren't binary */ | ||
917 | if(!tr->binary) { | ||
918 | /* UTF-8 could potentially be 3 times larger */ | ||
919 | /* so we need to create a new buffer */ | ||
920 | char utf8buf[(3 * bytesread) + 1]; | ||
921 | |||
922 | unicode_munge( tag, utf8buf, &bytesread ); | ||
923 | |||
924 | if(bytesread >= buffersize - bufferpos) | ||
925 | bytesread = buffersize - bufferpos - 1; | ||
926 | |||
927 | for (j = 0; j < bytesread; j++) | ||
928 | tag[j] = utf8buf[j]; | ||
929 | |||
930 | /* remove trailing spaces */ | ||
931 | while ( bytesread > 0 && isspace(tag[bytesread-1])) | ||
932 | bytesread--; | ||
933 | } | ||
934 | |||
935 | tag[bytesread] = 0; | ||
936 | bufferpos += bytesread + 1; | ||
937 | |||
938 | #if CONFIG_CODEC == SWCODEC | ||
939 | /* parse the tag if it contains iTunes gapless info */ | ||
940 | if (itunes_gapless) | ||
941 | { | ||
942 | itunes_gapless = false; | ||
943 | entry->lead_trim = get_itunes_int32(tag, 1); | ||
944 | entry->tail_trim = get_itunes_int32(tag, 2); | ||
945 | } | ||
946 | #endif | ||
947 | |||
948 | /* Note that parser functions sometimes set *ptag to NULL, so | ||
949 | * the "!*ptag" check here doesn't always have the desired | ||
950 | * effect. Should the parser functions (parsegenre in | ||
951 | * particular) be updated to handle the case of being called | ||
952 | * multiple times, or should the "*ptag" check be removed? | ||
953 | */ | ||
954 | if (ptag && !*ptag) | ||
955 | *ptag = tag; | ||
956 | |||
957 | if( tr->ppFunc ) | ||
958 | bufferpos = tr->ppFunc(entry, tag, bufferpos); | ||
959 | |||
960 | /* Seek to the next frame */ | ||
961 | if(framelen < totframelen) | ||
962 | lseek(fd, totframelen - framelen, SEEK_CUR); | ||
963 | break; | ||
964 | } | ||
965 | } | ||
966 | |||
967 | if( i == TAGLIST_SIZE ) { | ||
968 | /* no tag in tagList was found, or it was a repeat. | ||
969 | skip it using the total size */ | ||
970 | |||
971 | if(global_unsynch && version <= ID3_VER_2_3) { | ||
972 | size -= skip_unsynched(fd, totframelen); | ||
973 | } else { | ||
974 | size -= totframelen; | ||
975 | if( lseek(fd, totframelen, SEEK_CUR) == -1 ) | ||
976 | return; | ||
977 | } | ||
978 | } | ||
979 | } | ||
980 | } | ||
981 | |||
982 | /* | ||
983 | * Calculates the size of the ID3v2 tag. | ||
984 | * | ||
985 | * Arguments: file - the file to search for a tag. | ||
986 | * | ||
987 | * Returns: the size of the tag or 0 if none was found | ||
988 | */ | ||
989 | int getid3v2len(int fd) | ||
990 | { | ||
991 | char buf[6]; | ||
992 | int offset; | ||
993 | |||
994 | /* Make sure file has a ID3 tag */ | ||
995 | if((-1 == lseek(fd, 0, SEEK_SET)) || | ||
996 | (read(fd, buf, 6) != 6) || | ||
997 | (strncmp(buf, "ID3", strlen("ID3")) != 0)) | ||
998 | offset = 0; | ||
999 | |||
1000 | /* Now check what the ID3v2 size field says */ | ||
1001 | else | ||
1002 | if(read(fd, buf, 4) != 4) | ||
1003 | offset = 0; | ||
1004 | else | ||
1005 | offset = unsync(buf[0], buf[1], buf[2], buf[3]) + 10; | ||
1006 | |||
1007 | logf("ID3V2 Length: 0x%x", offset); | ||
1008 | return offset; | ||
1009 | } | ||
1010 | 44 | ||
1011 | /* | 45 | /* |
1012 | * Calculates the length (in milliseconds) of an MP3 file. | 46 | * Calculates the length (in milliseconds) of an MP3 file. |
@@ -1165,58 +199,3 @@ bool get_mp3_metadata(int fd, struct mp3entry *entry, const char *filename) | |||
1165 | 199 | ||
1166 | return true; | 200 | return true; |
1167 | } | 201 | } |
1168 | |||
1169 | #ifdef DEBUG_STANDALONE | ||
1170 | |||
1171 | char *secs2str(int ms) | ||
1172 | { | ||
1173 | static char buffer[32]; | ||
1174 | int secs = ms/1000; | ||
1175 | ms %= 1000; | ||
1176 | snprintf(buffer, sizeof(buffer), "%d:%02d.%d", secs/60, secs%60, ms/100); | ||
1177 | return buffer; | ||
1178 | } | ||
1179 | |||
1180 | int main(int argc, char **argv) | ||
1181 | { | ||
1182 | int i; | ||
1183 | for(i=1; i<argc; i++) { | ||
1184 | struct mp3entry mp3; | ||
1185 | mp3.album = "Bogus"; | ||
1186 | if(mp3info(&mp3, argv[i], false)) { | ||
1187 | printf("Failed to get %s\n", argv[i]); | ||
1188 | return 0; | ||
1189 | } | ||
1190 | |||
1191 | printf("****** File: %s\n" | ||
1192 | " Title: %s\n" | ||
1193 | " Artist: %s\n" | ||
1194 | " Album: %s\n" | ||
1195 | " Genre: %s (%d) \n" | ||
1196 | " Composer: %s\n" | ||
1197 | " Year: %s (%d)\n" | ||
1198 | " Track: %s (%d)\n" | ||
1199 | " Length: %s / %d s\n" | ||
1200 | " Bitrate: %d\n" | ||
1201 | " Frequency: %d\n", | ||
1202 | argv[i], | ||
1203 | mp3.title?mp3.title:"<blank>", | ||
1204 | mp3.artist?mp3.artist:"<blank>", | ||
1205 | mp3.album?mp3.album:"<blank>", | ||
1206 | mp3.genre_string?mp3.genre_string:"<blank>", | ||
1207 | mp3.genre, | ||
1208 | mp3.composer?mp3.composer:"<blank>", | ||
1209 | mp3.year_string?mp3.year_string:"<blank>", | ||
1210 | mp3.year, | ||
1211 | mp3.track_string?mp3.track_string:"<blank>", | ||
1212 | mp3.tracknum, | ||
1213 | secs2str(mp3.length), | ||
1214 | mp3.length/1000, | ||
1215 | mp3.bitrate, | ||
1216 | mp3.frequency); | ||
1217 | } | ||
1218 | |||
1219 | return 0; | ||
1220 | } | ||
1221 | |||
1222 | #endif | ||