diff options
-rw-r--r-- | docs/CREDITS | 1 | ||||
-rw-r--r-- | firmware/export/replaygain.h | 4 | ||||
-rw-r--r-- | firmware/id3.c | 223 | ||||
-rw-r--r-- | firmware/replaygain.c | 110 |
4 files changed, 225 insertions, 113 deletions
diff --git a/docs/CREDITS b/docs/CREDITS index 75260a1fa9..fa6ec5d9ff 100644 --- a/docs/CREDITS +++ b/docs/CREDITS | |||
@@ -215,3 +215,4 @@ Andreas Mattsson | |||
215 | Daniel Ankers | 215 | Daniel Ankers |
216 | Paul Louden | 216 | Paul Louden |
217 | Rainer Sinsch | 217 | Rainer Sinsch |
218 | Plácido Revilla | ||
diff --git a/firmware/export/replaygain.h b/firmware/export/replaygain.h index c29d4b6921..ca9b654ce9 100644 --- a/firmware/export/replaygain.h +++ b/firmware/export/replaygain.h | |||
@@ -25,7 +25,9 @@ | |||
25 | long get_replaygain_int(long int_gain); | 25 | long get_replaygain_int(long int_gain); |
26 | long get_replaygain(const char* str); | 26 | long get_replaygain(const char* str); |
27 | long get_replaypeak(const char* str); | 27 | long get_replaypeak(const char* str); |
28 | long parse_replaygain(const char* key, const char* value, | 28 | long parse_replaygain(const char* key, const char* value, |
29 | struct mp3entry* entry, char* buffer, int length); | ||
30 | long parse_replaygain_rva(const char* key, const char* value, | ||
29 | struct mp3entry* entry, char* buffer, int length); | 31 | struct mp3entry* entry, char* buffer, int length); |
30 | 32 | ||
31 | #endif | 33 | #endif |
diff --git a/firmware/id3.c b/firmware/id3.c index 5b5b7c98a3..b3c09f2e6b 100644 --- a/firmware/id3.c +++ b/firmware/id3.c | |||
@@ -22,7 +22,7 @@ | |||
22 | * all sorts of friendly Rockbox people. | 22 | * all sorts of friendly Rockbox people. |
23 | * | 23 | * |
24 | */ | 24 | */ |
25 | 25 | ||
26 | /* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach | 26 | /* tagResolver and associated code copyright 2003 Thomas Paul Diffenbach |
27 | */ | 27 | */ |
28 | 28 | ||
@@ -136,8 +136,8 @@ char* id3_get_codec(const struct mp3entry* id3) | |||
136 | numerical values, for cases where a number won't do, e.g., | 136 | numerical values, for cases where a number won't do, e.g., |
137 | YEAR: "circa 1765", "1790/1977" (composed/performed), "28 Feb 1969" | 137 | YEAR: "circa 1765", "1790/1977" (composed/performed), "28 Feb 1969" |
138 | TRACK: "1/12", "1 of 12", GENRE: "Freeform genre name" | 138 | TRACK: "1/12", "1 of 12", GENRE: "Freeform genre name" |
139 | Text is more flexible, and as the main use of id3 data is to | 139 | Text is more flexible, and as the main use of id3 data is to |
140 | display it, converting it to an int just means reconverting to | 140 | display it, converting it to an int just means reconverting to |
141 | display it, at a runtime cost.) | 141 | display it, at a runtime cost.) |
142 | 142 | ||
143 | 2. If any special processing beyond copying the tag value from the Id3 | 143 | 2. If any special processing beyond copying the tag value from the Id3 |
@@ -161,22 +161,23 @@ char* id3_get_codec(const struct mp3entry* id3) | |||
161 | char* ID3 Tag symbolic name -- see the ID3 specification for these, | 161 | char* ID3 Tag symbolic name -- see the ID3 specification for these, |
162 | sizeof() that name minus 1, | 162 | sizeof() that name minus 1, |
163 | offsetof( struct mp3entry, variable_name_in_struct_mp3entry ), | 163 | offsetof( struct mp3entry, variable_name_in_struct_mp3entry ), |
164 | pointer to your special processing function or NULL | 164 | pointer to your special processing function or NULL |
165 | if you need no special processing | 165 | if you need no special processing |
166 | Many ID3 symbolic names come in more than one form. You can add both | 166 | flag indicating if this tag is binary or textual |
167 | forms, each referencing the same variable in struct mp3entry. | 167 | Many ID3 symbolic names come in more than one form. You can add both |
168 | forms, each referencing the same variable in struct mp3entry. | ||
168 | If both forms are present, the last found will be used. | 169 | If both forms are present, the last found will be used. |
169 | Note that the offset can be zero, in which case no entry will be set | 170 | Note that the offset can be zero, in which case no entry will be set |
170 | in the mp3entry struct; the frame is still read into the buffer and | 171 | in the mp3entry struct; the frame is still read into the buffer and |
171 | the special processing function is called (several times, if there | 172 | the special processing function is called (several times, if there |
172 | are several frames with the same name). | 173 | are several frames with the same name). |
173 | 174 | ||
174 | 4. Alternately, use the TAG_LIST_ENTRY macro with | 175 | 4. Alternately, use the TAG_LIST_ENTRY macro with |
175 | ID3 tag symbolic name, | 176 | ID3 tag symbolic name, |
176 | variable in struct mp3entry, | 177 | variable in struct mp3entry, |
177 | special processing function address | 178 | special processing function address |
178 | 179 | ||
179 | 5. Add code to wps-display.c function get_tag to assign a printf-like | 180 | 5. Add code to wps-display.c function get_tag to assign a printf-like |
180 | format specifier for the tag */ | 181 | format specifier for the tag */ |
181 | 182 | ||
182 | /* Structure for ID3 Tag extraction information */ | 183 | /* Structure for ID3 Tag extraction information */ |
@@ -185,6 +186,7 @@ struct tag_resolver { | |||
185 | int tag_length; | 186 | int tag_length; |
186 | size_t offset; | 187 | size_t offset; |
187 | int (*ppFunc)(struct mp3entry*, char* tag, int bufferpos); | 188 | int (*ppFunc)(struct mp3entry*, char* tag, int bufferpos); |
189 | bool binary; | ||
188 | }; | 190 | }; |
189 | 191 | ||
190 | static bool global_ff_found; | 192 | static bool global_ff_found; |
@@ -196,7 +198,7 @@ static int unsynchronize(char* tag, int len, bool *ff_found) | |||
196 | unsigned char *rp, *wp; | 198 | unsigned char *rp, *wp; |
197 | 199 | ||
198 | wp = rp = (unsigned char *)tag; | 200 | wp = rp = (unsigned char *)tag; |
199 | 201 | ||
200 | rp = (unsigned char *)tag; | 202 | rp = (unsigned char *)tag; |
201 | for(i = 0;i < len;i++) { | 203 | for(i = 0;i < len;i++) { |
202 | /* Read the next byte and write it back, but don't increment the | 204 | /* Read the next byte and write it back, but don't increment the |
@@ -233,7 +235,7 @@ static int read_unsynched(int fd, void *buf, int len) | |||
233 | char *rp; | 235 | char *rp; |
234 | 236 | ||
235 | wp = buf; | 237 | wp = buf; |
236 | 238 | ||
237 | while(remaining) { | 239 | while(remaining) { |
238 | rp = wp; | 240 | rp = wp; |
239 | rc = read(fd, rp, remaining); | 241 | rc = read(fd, rp, remaining); |
@@ -319,16 +321,42 @@ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos ) | |||
319 | char* value = NULL; | 321 | char* value = NULL; |
320 | int desc_len = strlen(tag); | 322 | int desc_len = strlen(tag); |
321 | int value_len = 0; | 323 | int value_len = 0; |
322 | 324 | ||
323 | if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { | 325 | /* Only parse TXXX replaygain tags if tag version < 2.4 */ |
324 | /* At least part of the value was read, so we can safely try to | 326 | if (entry->id3version < ID3_VER_2_4 && |
325 | * parse it | 327 | (tag - entry->id3v2buf + desc_len + 2) < bufferpos) { |
328 | /* At least part of the value was read, so we can safely try to | ||
329 | * parse it | ||
326 | */ | 330 | */ |
327 | value = tag + desc_len + 1; | 331 | value = tag + desc_len + 1; |
328 | value_len = parse_replaygain(tag, value, entry, tag, | 332 | value_len = parse_replaygain(tag, value, entry, tag, |
329 | bufferpos - (tag - entry->id3v2buf)); | 333 | bufferpos - (tag - entry->id3v2buf)); |
330 | } | 334 | } |
331 | 335 | ||
336 | if (value_len) { | ||
337 | bufferpos = tag - entry->id3v2buf + value_len; | ||
338 | } else { | ||
339 | bufferpos = tag - entry->id3v2buf; | ||
340 | } | ||
341 | |||
342 | return bufferpos; | ||
343 | } | ||
344 | |||
345 | /* parse RVA2 binary data and convert to replaygain information. */ | ||
346 | static int parserva2( struct mp3entry* entry, char* tag, int bufferpos ) | ||
347 | { | ||
348 | char* value = NULL; | ||
349 | int desc_len = strlen(tag); | ||
350 | int value_len = 0; | ||
351 | |||
352 | /* Only parse RVA2 replaygain tags if tag version == 2.4 */ | ||
353 | if (entry->id3version == ID3_VER_2_4 && | ||
354 | (tag - entry->id3v2buf + desc_len + 2) < bufferpos) { | ||
355 | value = tag + desc_len + 1; | ||
356 | value_len = parse_replaygain_rva(tag, value, entry, tag, | ||
357 | bufferpos - (tag - entry->id3v2buf)); | ||
358 | } | ||
359 | |||
332 | if (value_len) { | 360 | if (value_len) { |
333 | bufferpos = tag - entry->id3v2buf + value_len; | 361 | bufferpos = tag - entry->id3v2buf + value_len; |
334 | } else { | 362 | } else { |
@@ -340,22 +368,23 @@ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos ) | |||
340 | #endif | 368 | #endif |
341 | 369 | ||
342 | static const struct tag_resolver taglist[] = { | 370 | static const struct tag_resolver taglist[] = { |
343 | { "TPE1", 4, offsetof(struct mp3entry, artist), NULL }, | 371 | { "TPE1", 4, offsetof(struct mp3entry, artist), NULL, false }, |
344 | { "TP1", 3, offsetof(struct mp3entry, artist), NULL }, | 372 | { "TP1", 3, offsetof(struct mp3entry, artist), NULL, false }, |
345 | { "TIT2", 4, offsetof(struct mp3entry, title), NULL }, | 373 | { "TIT2", 4, offsetof(struct mp3entry, title), NULL, false }, |
346 | { "TT2", 3, offsetof(struct mp3entry, title), NULL }, | 374 | { "TT2", 3, offsetof(struct mp3entry, title), NULL, false }, |
347 | { "TALB", 4, offsetof(struct mp3entry, album), NULL }, | 375 | { "TALB", 4, offsetof(struct mp3entry, album), NULL, false }, |
348 | { "TAL", 3, offsetof(struct mp3entry, album), NULL }, | 376 | { "TAL", 3, offsetof(struct mp3entry, album), NULL, false }, |
349 | { "TRK", 3, offsetof(struct mp3entry, track_string), &parsetracknum }, | 377 | { "TRK", 3, offsetof(struct mp3entry, track_string), &parsetracknum, false }, |
350 | { "TRCK", 4, offsetof(struct mp3entry, track_string), &parsetracknum }, | 378 | { "TRCK", 4, offsetof(struct mp3entry, track_string), &parsetracknum, false }, |
351 | { "TDRC", 4, offsetof(struct mp3entry, year_string), &parseyearnum }, | 379 | { "TDRC", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, |
352 | { "TYER", 4, offsetof(struct mp3entry, year_string), &parseyearnum }, | 380 | { "TYER", 4, offsetof(struct mp3entry, year_string), &parseyearnum, false }, |
353 | { "TYE", 3, offsetof(struct mp3entry, year_string), &parseyearnum }, | 381 | { "TYE", 3, offsetof(struct mp3entry, year_string), &parseyearnum, false }, |
354 | { "TCOM", 4, offsetof(struct mp3entry, composer), NULL }, | 382 | { "TCOM", 4, offsetof(struct mp3entry, composer), NULL, false }, |
355 | { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre }, | 383 | { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre, false }, |
356 | { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre }, | 384 | { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre, false }, |
357 | #if CONFIG_CODEC == SWCODEC | 385 | #if CONFIG_CODEC == SWCODEC |
358 | { "TXXX", 4, 0, &parseuser }, | 386 | { "TXXX", 4, 0, &parseuser, false }, |
387 | { "RVA2", 4, 0, &parserva2, true }, | ||
359 | #endif | 388 | #endif |
360 | }; | 389 | }; |
361 | 390 | ||
@@ -449,7 +478,7 @@ static int unicode_munge(char* string, char* utf8buf, int *len) { | |||
449 | * | 478 | * |
450 | * Returns: true if a title was found and created, else false | 479 | * Returns: true if a title was found and created, else false |
451 | */ | 480 | */ |
452 | static bool setid3v1title(int fd, struct mp3entry *entry) | 481 | static bool setid3v1title(int fd, struct mp3entry *entry) |
453 | { | 482 | { |
454 | unsigned char buffer[128]; | 483 | unsigned char buffer[128]; |
455 | static const char offsets[] = {3, 33, 63, 93, 125, 127}; | 484 | static const char offsets[] = {3, 33, 63, 93, 125, 127}; |
@@ -525,7 +554,7 @@ static bool setid3v1title(int fd, struct mp3entry *entry) | |||
525 | * | 554 | * |
526 | * Returns: true if a title was found and created, else false | 555 | * Returns: true if a title was found and created, else false |
527 | */ | 556 | */ |
528 | static void setid3v2title(int fd, struct mp3entry *entry) | 557 | static void setid3v2title(int fd, struct mp3entry *entry) |
529 | { | 558 | { |
530 | int minframesize; | 559 | int minframesize; |
531 | int size; | 560 | int size; |
@@ -546,7 +575,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
546 | int rc; | 575 | int rc; |
547 | 576 | ||
548 | global_ff_found = false; | 577 | global_ff_found = false; |
549 | 578 | ||
550 | /* Bail out if the tag is shorter than 10 bytes */ | 579 | /* Bail out if the tag is shorter than 10 bytes */ |
551 | if(entry->id3v2len < 10) | 580 | if(entry->id3v2len < 10) |
552 | return; | 581 | return; |
@@ -586,7 +615,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
586 | entry->title = entry->artist = entry->album = NULL; | 615 | entry->title = entry->artist = entry->album = NULL; |
587 | 616 | ||
588 | global_flags = header[5]; | 617 | global_flags = header[5]; |
589 | 618 | ||
590 | /* Skip the extended header if it is present */ | 619 | /* Skip the extended header if it is present */ |
591 | if(global_flags & 0x40) { | 620 | if(global_flags & 0x40) { |
592 | if(version == ID3_VER_2_3) { | 621 | if(version == ID3_VER_2_3) { |
@@ -603,14 +632,14 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
603 | 632 | ||
604 | lseek(fd, framelen - 10, SEEK_CUR); | 633 | lseek(fd, framelen - 10, SEEK_CUR); |
605 | } | 634 | } |
606 | 635 | ||
607 | if(version >= ID3_VER_2_4) { | 636 | if(version >= ID3_VER_2_4) { |
608 | if(4 != read(fd, header, 4)) | 637 | if(4 != read(fd, header, 4)) |
609 | return; | 638 | return; |
610 | 639 | ||
611 | /* The 2.4 extended header size does include the entire header, | 640 | /* The 2.4 extended header size does include the entire header, |
612 | so here we can just skip it. This header is unsynched. */ | 641 | so here we can just skip it. This header is unsynched. */ |
613 | framelen = UNSYNC(header[0], header[1], | 642 | framelen = UNSYNC(header[0], header[1], |
614 | header[2], header[3]); | 643 | header[2], header[3]); |
615 | 644 | ||
616 | lseek(fd, framelen - 4, SEEK_CUR); | 645 | lseek(fd, framelen - 4, SEEK_CUR); |
@@ -621,14 +650,14 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
621 | if(global_flags & 0x80) { | 650 | if(global_flags & 0x80) { |
622 | global_unsynch = true; | 651 | global_unsynch = true; |
623 | } | 652 | } |
624 | 653 | ||
625 | /* | 654 | /* |
626 | * We must have at least minframesize bytes left for the | 655 | * We must have at least minframesize bytes left for the |
627 | * remaining frames to be interesting | 656 | * remaining frames to be interesting |
628 | */ | 657 | */ |
629 | while (size >= minframesize && bufferpos < buffersize - 1) { | 658 | while (size >= minframesize && bufferpos < buffersize - 1) { |
630 | flags = 0; | 659 | flags = 0; |
631 | 660 | ||
632 | /* Read frame header and check length */ | 661 | /* Read frame header and check length */ |
633 | if(version >= ID3_VER_2_3) { | 662 | if(version >= ID3_VER_2_3) { |
634 | if(global_unsynch && version <= ID3_VER_2_3) | 663 | if(global_unsynch && version <= ID3_VER_2_3) |
@@ -641,14 +670,14 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
641 | size -= 10; | 670 | size -= 10; |
642 | 671 | ||
643 | flags = BYTES2INT(0, 0, header[8], header[9]); | 672 | flags = BYTES2INT(0, 0, header[8], header[9]); |
644 | 673 | ||
645 | if (version >= ID3_VER_2_4) { | 674 | if (version >= ID3_VER_2_4) { |
646 | framelen = UNSYNC(header[4], header[5], | 675 | framelen = UNSYNC(header[4], header[5], |
647 | header[6], header[7]); | 676 | header[6], header[7]); |
648 | } else { | 677 | } else { |
649 | /* version .3 files don't use synchsafe ints for | 678 | /* version .3 files don't use synchsafe ints for |
650 | * size */ | 679 | * size */ |
651 | framelen = BYTES2INT(header[4], header[5], | 680 | framelen = BYTES2INT(header[4], header[5], |
652 | header[6], header[7]); | 681 | header[6], header[7]); |
653 | } | 682 | } |
654 | } else { | 683 | } else { |
@@ -656,17 +685,17 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
656 | return; | 685 | return; |
657 | /* Adjust for the 6 bytes we read */ | 686 | /* Adjust for the 6 bytes we read */ |
658 | size -= 6; | 687 | size -= 6; |
659 | 688 | ||
660 | framelen = BYTES2INT(0, header[3], header[4], header[5]); | 689 | framelen = BYTES2INT(0, header[3], header[4], header[5]); |
661 | } | 690 | } |
662 | 691 | ||
663 | /* Keep track of the total size */ | 692 | /* Keep track of the total size */ |
664 | totframelen = framelen; | 693 | totframelen = framelen; |
665 | 694 | ||
666 | DEBUGF("framelen = %d\n", framelen); | 695 | DEBUGF("framelen = %d\n", framelen); |
667 | if(framelen == 0){ | 696 | if(framelen == 0){ |
668 | if (header[0] == 0 && header[1] == 0 && header[2] == 0) | 697 | if (header[0] == 0 && header[1] == 0 && header[2] == 0) |
669 | return; | 698 | return; |
670 | else | 699 | else |
671 | continue; | 700 | continue; |
672 | } | 701 | } |
@@ -677,7 +706,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
677 | if(flags) | 706 | if(flags) |
678 | { | 707 | { |
679 | skip = 0; | 708 | skip = 0; |
680 | 709 | ||
681 | if (version >= ID3_VER_2_4) { | 710 | if (version >= ID3_VER_2_4) { |
682 | if(flags & 0x0040) { /* Grouping identity */ | 711 | if(flags & 0x0040) { /* Grouping identity */ |
683 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ | 712 | lseek(fd, 1, SEEK_CUR); /* Skip 1 byte */ |
@@ -689,7 +718,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
689 | framelen--; | 718 | framelen--; |
690 | } | 719 | } |
691 | } | 720 | } |
692 | 721 | ||
693 | if(flags & 0x000c) /* Compression or encryption */ | 722 | if(flags & 0x000c) /* Compression or encryption */ |
694 | { | 723 | { |
695 | /* Skip it using the total size in case | 724 | /* Skip it using the total size in case |
@@ -712,7 +741,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
712 | } | 741 | } |
713 | } | 742 | } |
714 | } | 743 | } |
715 | 744 | ||
716 | /* If the frame is larger than the remaining buffer space we try | 745 | /* If the frame is larger than the remaining buffer space we try |
717 | to read as much as would fit in the buffer */ | 746 | to read as much as would fit in the buffer */ |
718 | if(framelen >= buffersize - bufferpos) | 747 | if(framelen >= buffersize - bufferpos) |
@@ -726,7 +755,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
726 | the amount of bytes we read. If we fail to read as many bytes as | 755 | the amount of bytes we read. If we fail to read as many bytes as |
727 | we expect, we assume that we can't read from this file, and bail | 756 | we expect, we assume that we can't read from this file, and bail |
728 | out. | 757 | out. |
729 | 758 | ||
730 | For each frame. we will iterate over the list of supported tags, | 759 | For each frame. we will iterate over the list of supported tags, |
731 | and read the tag into entry's buffer. All tags will be kept as | 760 | and read the tag into entry's buffer. All tags will be kept as |
732 | strings, for cases where a number won't do, e.g., YEAR: "circa | 761 | strings, for cases where a number won't do, e.g., YEAR: "circa |
@@ -735,7 +764,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
735 | flexible, and as the main use of id3 data is to display it, | 764 | flexible, and as the main use of id3 data is to display it, |
736 | converting it to an int just means reconverting to display it, at a | 765 | converting it to an int just means reconverting to display it, at a |
737 | runtime cost. | 766 | runtime cost. |
738 | 767 | ||
739 | For tags that the current code does convert to ints, a post | 768 | For tags that the current code does convert to ints, a post |
740 | processing function will be called via a pointer to function. */ | 769 | processing function will be called via a pointer to function. */ |
741 | 770 | ||
@@ -744,7 +773,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
744 | char** ptag = tr->offset ? (char**) (((char*)entry) + tr->offset) | 773 | char** ptag = tr->offset ? (char**) (((char*)entry) + tr->offset) |
745 | : NULL; | 774 | : NULL; |
746 | char* tag; | 775 | char* tag; |
747 | 776 | ||
748 | /* Only ID3_VER_2_2 uses frames with three-character names. */ | 777 | /* Only ID3_VER_2_2 uses frames with three-character names. */ |
749 | if (((version == ID3_VER_2_2) && (tr->tag_length != 3)) | 778 | if (((version == ID3_VER_2_2) && (tr->tag_length != 3)) |
750 | || ((version > ID3_VER_2_2) && (tr->tag_length != 4))) { | 779 | || ((version > ID3_VER_2_2) && (tr->tag_length != 4))) { |
@@ -757,8 +786,8 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
757 | * particular) be updated to handle the case of being called | 786 | * particular) be updated to handle the case of being called |
758 | * multiple times, or should the "*ptag" check be removed? | 787 | * multiple times, or should the "*ptag" check be removed? |
759 | */ | 788 | */ |
760 | if( (!ptag || !*ptag) && !memcmp( header, tr->tag, tr->tag_length ) ) { | 789 | if( (!ptag || !*ptag) && !memcmp( header, tr->tag, tr->tag_length ) ) { |
761 | 790 | ||
762 | /* found a tag matching one in tagList, and not yet filled */ | 791 | /* found a tag matching one in tagList, and not yet filled */ |
763 | tag = buffer + bufferpos; | 792 | tag = buffer + bufferpos; |
764 | 793 | ||
@@ -766,37 +795,41 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
766 | bytesread = read_unsynched(fd, tag, framelen); | 795 | bytesread = read_unsynched(fd, tag, framelen); |
767 | else | 796 | else |
768 | bytesread = read(fd, tag, framelen); | 797 | bytesread = read(fd, tag, framelen); |
769 | 798 | ||
770 | if( bytesread != framelen ) | 799 | if( bytesread != framelen ) |
771 | return; | 800 | return; |
772 | 801 | ||
773 | size -= bytesread; | 802 | size -= bytesread; |
774 | 803 | ||
775 | if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) | 804 | if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) |
776 | bytesread = unsynchronize_frame(tag, bytesread); | 805 | bytesread = unsynchronize_frame(tag, bytesread); |
777 | |||
778 | 806 | ||
779 | /* UTF-8 could potentially be 3 times larger */ | 807 | /* Attempt to parse Unicode string only if the tag contents |
780 | /* so we need to create a new buffer */ | 808 | aren't binary */ |
781 | char utf8buf[(3 * bytesread) + 1]; | 809 | if(!tr->binary) { |
810 | /* UTF-8 could potentially be 3 times larger */ | ||
811 | /* so we need to create a new buffer */ | ||
812 | char utf8buf[(3 * bytesread) + 1]; | ||
782 | 813 | ||
783 | unicode_munge( tag, utf8buf, &bytesread ); | 814 | unicode_munge( tag, utf8buf, &bytesread ); |
784 | 815 | ||
785 | if(bytesread >= buffersize - bufferpos) | 816 | if(bytesread >= buffersize - bufferpos) |
786 | bytesread = buffersize - bufferpos - 1; | 817 | bytesread = buffersize - bufferpos - 1; |
787 | 818 | ||
788 | for (j = 0; j < bytesread; j++) | 819 | for (j = 0; j < bytesread; j++) |
789 | tag[j] = utf8buf[j]; | 820 | tag[j] = utf8buf[j]; |
790 | 821 | ||
791 | if (ptag) | 822 | /* remove trailing spaces */ |
792 | *ptag = tag; | 823 | while ( bytesread > 0 && isspace(tag[bytesread-1])) |
824 | bytesread--; | ||
825 | } | ||
793 | 826 | ||
794 | /* remove trailing spaces */ | ||
795 | while ( bytesread > 0 && isspace(tag[bytesread-1])) | ||
796 | bytesread--; | ||
797 | tag[bytesread] = 0; | 827 | tag[bytesread] = 0; |
798 | bufferpos += bytesread + 1; | 828 | bufferpos += bytesread + 1; |
799 | 829 | ||
830 | if (ptag) | ||
831 | *ptag = tag; | ||
832 | |||
800 | if( tr->ppFunc ) | 833 | if( tr->ppFunc ) |
801 | bufferpos = tr->ppFunc(entry, tag, bufferpos); | 834 | bufferpos = tr->ppFunc(entry, tag, bufferpos); |
802 | 835 | ||
@@ -816,7 +849,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
816 | } else { | 849 | } else { |
817 | if(data_length_ind) | 850 | if(data_length_ind) |
818 | totframelen = data_length_ind; | 851 | totframelen = data_length_ind; |
819 | 852 | ||
820 | size -= totframelen; | 853 | size -= totframelen; |
821 | if( lseek(fd, totframelen, SEEK_CUR) == -1 ) | 854 | if( lseek(fd, totframelen, SEEK_CUR) == -1 ) |
822 | return; | 855 | return; |
@@ -832,7 +865,7 @@ static void setid3v2title(int fd, struct mp3entry *entry) | |||
832 | * | 865 | * |
833 | * Returns: the size of the tag or 0 if none was found | 866 | * Returns: the size of the tag or 0 if none was found |
834 | */ | 867 | */ |
835 | int getid3v2len(int fd) | 868 | int getid3v2len(int fd) |
836 | { | 869 | { |
837 | char buf[6]; | 870 | char buf[6]; |
838 | int offset; | 871 | int offset; |
@@ -862,7 +895,7 @@ int getid3v2len(int fd) | |||
862 | * Arguments: file - the file to calculate the length upon | 895 | * Arguments: file - the file to calculate the length upon |
863 | * entry - the entry to update with the length | 896 | * entry - the entry to update with the length |
864 | * | 897 | * |
865 | * Returns: the song length in milliseconds, | 898 | * Returns: the song length in milliseconds, |
866 | * 0 means that it couldn't be calculated | 899 | * 0 means that it couldn't be calculated |
867 | */ | 900 | */ |
868 | static int getsonglength(int fd, struct mp3entry *entry) | 901 | static int getsonglength(int fd, struct mp3entry *entry) |
@@ -871,7 +904,7 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
871 | struct mp3info info; | 904 | struct mp3info info; |
872 | long bytecount; | 905 | long bytecount; |
873 | 906 | ||
874 | /* Start searching after ID3v2 header */ | 907 | /* Start searching after ID3v2 header */ |
875 | if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) | 908 | if(-1 == lseek(fd, entry->id3v2len, SEEK_SET)) |
876 | return 0; | 909 | return 0; |
877 | 910 | ||
@@ -882,7 +915,7 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
882 | 915 | ||
883 | if(bytecount < 0) | 916 | if(bytecount < 0) |
884 | return -1; | 917 | return -1; |
885 | 918 | ||
886 | bytecount += entry->id3v2len; | 919 | bytecount += entry->id3v2len; |
887 | 920 | ||
888 | /* Validate byte count, in case the file has been edited without | 921 | /* Validate byte count, in case the file has been edited without |
@@ -890,11 +923,11 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
890 | */ | 923 | */ |
891 | if (info.byte_count) | 924 | if (info.byte_count) |
892 | { | 925 | { |
893 | const unsigned long expected = entry->filesize - entry->id3v1len | 926 | const unsigned long expected = entry->filesize - entry->id3v1len |
894 | - entry->id3v2len; | 927 | - entry->id3v2len; |
895 | const unsigned long diff = MAX(10240, info.byte_count / 20); | 928 | const unsigned long diff = MAX(10240, info.byte_count / 20); |
896 | 929 | ||
897 | if ((info.byte_count > expected + diff) | 930 | if ((info.byte_count > expected + diff) |
898 | || (info.byte_count < expected - diff)) | 931 | || (info.byte_count < expected - diff)) |
899 | { | 932 | { |
900 | DEBUGF("Note: info.byte_count differs from expected value by " | 933 | DEBUGF("Note: info.byte_count differs from expected value by " |
@@ -933,7 +966,7 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
933 | rate MP3, so just use the default formula */ | 966 | rate MP3, so just use the default formula */ |
934 | 967 | ||
935 | filetime = info.file_time; | 968 | filetime = info.file_time; |
936 | 969 | ||
937 | if(filetime == 0) | 970 | if(filetime == 0) |
938 | { | 971 | { |
939 | filetime = (entry->filesize - bytecount) / (info.bitrate / 8); | 972 | filetime = (entry->filesize - bytecount) / (info.bitrate / 8); |
@@ -949,11 +982,11 @@ static int getsonglength(int fd, struct mp3entry *entry) | |||
949 | 982 | ||
950 | entry->lead_trim = info.enc_delay; | 983 | entry->lead_trim = info.enc_delay; |
951 | entry->tail_trim = info.enc_padding; | 984 | entry->tail_trim = info.enc_padding; |
952 | 985 | ||
953 | memcpy(entry->toc, info.toc, sizeof(info.toc)); | 986 | memcpy(entry->toc, info.toc, sizeof(info.toc)); |
954 | 987 | ||
955 | entry->vbr_header_pos = info.vbr_header_pos; | 988 | entry->vbr_header_pos = info.vbr_header_pos; |
956 | 989 | ||
957 | /* Update the seek point for the first playable frame */ | 990 | /* Update the seek point for the first playable frame */ |
958 | entry->first_frame_offset = bytecount; | 991 | entry->first_frame_offset = bytecount; |
959 | DEBUGF("First frame is at %x\n", entry->first_frame_offset); | 992 | DEBUGF("First frame is at %x\n", entry->first_frame_offset); |
@@ -970,7 +1003,7 @@ bool mp3info(struct mp3entry *entry, const char *filename, bool v1first) | |||
970 | { | 1003 | { |
971 | int fd; | 1004 | int fd; |
972 | int v1found = false; | 1005 | int v1found = false; |
973 | 1006 | ||
974 | fd = open(filename, O_RDONLY); | 1007 | fd = open(filename, O_RDONLY); |
975 | if(-1 == fd) | 1008 | if(-1 == fd) |
976 | return true; | 1009 | return true; |
@@ -980,7 +1013,7 @@ bool mp3info(struct mp3entry *entry, const char *filename, bool v1first) | |||
980 | #endif | 1013 | #endif |
981 | 1014 | ||
982 | strncpy(entry->path, filename, sizeof(entry->path)); | 1015 | strncpy(entry->path, filename, sizeof(entry->path)); |
983 | 1016 | ||
984 | entry->title = NULL; | 1017 | entry->title = NULL; |
985 | entry->filesize = filesize(fd); | 1018 | entry->filesize = filesize(fd); |
986 | entry->id3v2len = getid3v2len(fd); | 1019 | entry->id3v2len = getid3v2len(fd); |
@@ -989,7 +1022,7 @@ bool mp3info(struct mp3entry *entry, const char *filename, bool v1first) | |||
989 | 1022 | ||
990 | if(v1first) | 1023 | if(v1first) |
991 | v1found = setid3v1title(fd, entry); | 1024 | v1found = setid3v1title(fd, entry); |
992 | 1025 | ||
993 | if (!v1found && entry->id3v2len) | 1026 | if (!v1found && entry->id3v2len) |
994 | setid3v2title(fd, entry); | 1027 | setid3v2title(fd, entry); |
995 | entry->length = getsonglength(fd, entry); | 1028 | entry->length = getsonglength(fd, entry); |
@@ -997,7 +1030,7 @@ bool mp3info(struct mp3entry *entry, const char *filename, bool v1first) | |||
997 | /* Subtract the meta information from the file size to get | 1030 | /* Subtract the meta information from the file size to get |
998 | the true size of the MP3 stream */ | 1031 | the true size of the MP3 stream */ |
999 | entry->filesize -= entry->first_frame_offset; | 1032 | entry->filesize -= entry->first_frame_offset; |
1000 | 1033 | ||
1001 | /* only seek to end of file if no id3v2 tags were found, | 1034 | /* only seek to end of file if no id3v2 tags were found, |
1002 | and we already haven't looked for a v1 tag */ | 1035 | and we already haven't looked for a v1 tag */ |
1003 | if (!v1first && !entry->id3v2len) { | 1036 | if (!v1first && !entry->id3v2len) { |
@@ -1076,10 +1109,10 @@ int main(int argc, char **argv) | |||
1076 | " Title: %s\n" | 1109 | " Title: %s\n" |
1077 | " Artist: %s\n" | 1110 | " Artist: %s\n" |
1078 | " Album: %s\n" | 1111 | " Album: %s\n" |
1079 | " Genre: %s (%d) \n" | 1112 | " Genre: %s (%d) \n" |
1080 | " Composer: %s\n" | 1113 | " Composer: %s\n" |
1081 | " Year: %s (%d)\n" | 1114 | " Year: %s (%d)\n" |
1082 | " Track: %s (%d)\n" | 1115 | " Track: %s (%d)\n" |
1083 | " Length: %s / %d s\n" | 1116 | " Length: %s / %d s\n" |
1084 | " Bitrate: %d\n" | 1117 | " Bitrate: %d\n" |
1085 | " Frequency: %d\n", | 1118 | " Frequency: %d\n", |
@@ -1099,7 +1132,7 @@ int main(int argc, char **argv) | |||
1099 | mp3.bitrate, | 1132 | mp3.bitrate, |
1100 | mp3.frequency); | 1133 | mp3.frequency); |
1101 | } | 1134 | } |
1102 | 1135 | ||
1103 | return 0; | 1136 | return 0; |
1104 | } | 1137 | } |
1105 | 1138 | ||
diff --git a/firmware/replaygain.c b/firmware/replaygain.c index a8331ae483..ffabb4b2ac 100644 --- a/firmware/replaygain.c +++ b/firmware/replaygain.c | |||
@@ -28,6 +28,10 @@ | |||
28 | #include "id3.h" | 28 | #include "id3.h" |
29 | #include "debug.h" | 29 | #include "debug.h" |
30 | 30 | ||
31 | /* Type of channel for RVA2 frame. There are more than this defined in the spec | ||
32 | but we don't use them. */ | ||
33 | #define MASTER_CHANNEL 1 | ||
34 | |||
31 | /* The fixed point math routines (with the exception of fp_atof) are based | 35 | /* The fixed point math routines (with the exception of fp_atof) are based |
32 | * on oMathFP by Dan Carter (http://orbisstudios.com). | 36 | * on oMathFP by Dan Carter (http://orbisstudios.com). |
33 | */ | 37 | */ |
@@ -278,7 +282,7 @@ static long fp_atof(const char* s, int precision) | |||
278 | frac_max_int *= 10; | 282 | frac_max_int *= 10; |
279 | } | 283 | } |
280 | 284 | ||
281 | return sign * ((int_part * int_one) | 285 | return sign * ((int_part * int_one) |
282 | + (((int64_t) frac_part * int_one) / frac_max_int)); | 286 | + (((int64_t) frac_part * int_one) / frac_max_int)); |
283 | } | 287 | } |
284 | 288 | ||
@@ -286,14 +290,14 @@ static long convert_gain(long gain) | |||
286 | { | 290 | { |
287 | if (gain != 0) | 291 | if (gain != 0) |
288 | { | 292 | { |
289 | /* Don't allow unreasonably low or high gain changes. | 293 | /* Don't allow unreasonably low or high gain changes. |
290 | * Our math code can't handle it properly anyway. :) | 294 | * Our math code can't handle it properly anyway. :) |
291 | */ | 295 | */ |
292 | if (gain < (-48 * FP_ONE)) | 296 | if (gain < (-48 * FP_ONE)) |
293 | { | 297 | { |
294 | gain = -48 * FP_ONE; | 298 | gain = -48 * FP_ONE; |
295 | } | 299 | } |
296 | 300 | ||
297 | if (gain > (17 * FP_ONE)) | 301 | if (gain > (17 * FP_ONE)) |
298 | { | 302 | { |
299 | gain = 17 * FP_ONE; | 303 | gain = 17 * FP_ONE; |
@@ -301,55 +305,55 @@ static long convert_gain(long gain) | |||
301 | 305 | ||
302 | gain = fp_exp10(gain / 20) << (24 - FP_BITS); | 306 | gain = fp_exp10(gain / 20) << (24 - FP_BITS); |
303 | } | 307 | } |
304 | 308 | ||
305 | return gain; | 309 | return gain; |
306 | } | 310 | } |
307 | 311 | ||
308 | long get_replaygain_int(long int_gain) | 312 | long get_replaygain_int(long int_gain) |
309 | { | 313 | { |
310 | long gain = 0; | 314 | long gain = 0; |
311 | 315 | ||
312 | if (int_gain) | 316 | if (int_gain) |
313 | { | 317 | { |
314 | gain = convert_gain(int_gain * FP_ONE / 100); | 318 | gain = convert_gain(int_gain * FP_ONE / 100); |
315 | } | 319 | } |
316 | 320 | ||
317 | return gain; | 321 | return gain; |
318 | } | 322 | } |
319 | 323 | ||
320 | long get_replaygain(const char* str) | 324 | long get_replaygain(const char* str) |
321 | { | 325 | { |
322 | long gain = 0; | 326 | long gain = 0; |
323 | 327 | ||
324 | if (str) | 328 | if (str) |
325 | { | 329 | { |
326 | gain = fp_atof(str, FP_BITS); | 330 | gain = fp_atof(str, FP_BITS); |
327 | gain = convert_gain(gain); | 331 | gain = convert_gain(gain); |
328 | } | 332 | } |
329 | 333 | ||
330 | return gain; | 334 | return gain; |
331 | } | 335 | } |
332 | 336 | ||
333 | long get_replaypeak(const char* str) | 337 | long get_replaypeak(const char* str) |
334 | { | 338 | { |
335 | long peak = 0; | 339 | long peak = 0; |
336 | 340 | ||
337 | if (str) | 341 | if (str) |
338 | { | 342 | { |
339 | peak = fp_atof(str, 24); | 343 | peak = fp_atof(str, 24); |
340 | } | 344 | } |
341 | 345 | ||
342 | return peak; | 346 | return peak; |
343 | } | 347 | } |
344 | 348 | ||
345 | /* Check for a ReplayGain tag conforming to the "VorbisGain standard". If | 349 | /* Check for a ReplayGain tag conforming to the "VorbisGain standard". If |
346 | * found, set the mp3entry accordingly. buffer is where to store the text | 350 | * found, set the mp3entry accordingly. buffer is where to store the text |
347 | * contents of the gain tags; up to length bytes (including end nil) can be | 351 | * contents of the gain tags; up to length bytes (including end nil) can be |
348 | * written. Returns number of bytes written to the tag text buffer, or zero | 352 | * written. Returns number of bytes written to the tag text buffer, or zero |
349 | * if no ReplayGain tag was found (or nothing was copied to the buffer for | 353 | * if no ReplayGain tag was found (or nothing was copied to the buffer for |
350 | * other reasons). | 354 | * other reasons). |
351 | */ | 355 | */ |
352 | long parse_replaygain(const char* key, const char* value, | 356 | long parse_replaygain(const char* key, const char* value, |
353 | struct mp3entry* entry, char* buffer, int length) | 357 | struct mp3entry* entry, char* buffer, int length) |
354 | { | 358 | { |
355 | char **p = NULL; | 359 | char **p = NULL; |
@@ -359,7 +363,7 @@ long parse_replaygain(const char* key, const char* value, | |||
359 | { | 363 | { |
360 | entry->track_gain = get_replaygain(value); | 364 | entry->track_gain = get_replaygain(value); |
361 | p = &(entry->track_gain_string); | 365 | p = &(entry->track_gain_string); |
362 | } | 366 | } |
363 | else if ((strcasecmp(key, "replaygain_album_gain") == 0) | 367 | else if ((strcasecmp(key, "replaygain_album_gain") == 0) |
364 | || ((strcasecmp(key, "rg_audiophile") == 0) && !entry->album_gain)) | 368 | || ((strcasecmp(key, "rg_audiophile") == 0) && !entry->album_gain)) |
365 | { | 369 | { |
@@ -370,7 +374,7 @@ long parse_replaygain(const char* key, const char* value, | |||
370 | || ((strcasecmp(key, "rg_peak") == 0) && !entry->track_peak)) | 374 | || ((strcasecmp(key, "rg_peak") == 0) && !entry->track_peak)) |
371 | { | 375 | { |
372 | entry->track_peak = get_replaypeak(value); | 376 | entry->track_peak = get_replaypeak(value); |
373 | } | 377 | } |
374 | else if (strcasecmp(key, "replaygain_album_peak") == 0) | 378 | else if (strcasecmp(key, "replaygain_album_peak") == 0) |
375 | { | 379 | { |
376 | entry->album_peak = get_replaypeak(value); | 380 | entry->album_peak = get_replaypeak(value); |
@@ -379,7 +383,7 @@ long parse_replaygain(const char* key, const char* value, | |||
379 | if (p) | 383 | if (p) |
380 | { | 384 | { |
381 | int len = strlen(value); | 385 | int len = strlen(value); |
382 | 386 | ||
383 | len = MIN(len, length - 1); | 387 | len = MIN(len, length - 1); |
384 | 388 | ||
385 | /* A few characters just isn't interesting... */ | 389 | /* A few characters just isn't interesting... */ |
@@ -394,3 +398,75 @@ long parse_replaygain(const char* key, const char* value, | |||
394 | 398 | ||
395 | return 0; | 399 | return 0; |
396 | } | 400 | } |
401 | |||
402 | static long get_rva_values(const char *frame, long *gain, long *peak, | ||
403 | char **string, char *buffer, int length) | ||
404 | { | ||
405 | long value, len; | ||
406 | int negative = 0; | ||
407 | char tmpbuf[10]; | ||
408 | int peakbits, peakbytes, shift; | ||
409 | unsigned long peakvalue = 0; | ||
410 | |||
411 | value = 256 * ((unsigned char)*frame) + ((unsigned char)*(frame + 1)); | ||
412 | if (value & 0x8000) | ||
413 | { | ||
414 | value = -(value | ~0xFFFF); | ||
415 | negative = 1; | ||
416 | } | ||
417 | |||
418 | len = snprintf(tmpbuf, sizeof(tmpbuf), "%s%d.%02d dB", negative ? "-" : "", | ||
419 | value / 512, (value & 0x1FF) * 195 / 1000); | ||
420 | |||
421 | *gain = get_replaygain(tmpbuf); | ||
422 | |||
423 | len = MIN(len, length - 1); | ||
424 | if (len > 1) | ||
425 | { | ||
426 | strncpy(buffer, tmpbuf, len); | ||
427 | buffer[len] = 0; | ||
428 | *string = buffer; | ||
429 | } | ||
430 | |||
431 | frame += 2; | ||
432 | peakbits = *(unsigned char *)frame++; | ||
433 | peakbytes = MIN(4, (peakbits + 7) >> 3); | ||
434 | shift = ((8 - (peakbits & 7)) & 7) + (4 - peakbytes) * 8; | ||
435 | |||
436 | for (; peakbytes; peakbytes--) | ||
437 | { | ||
438 | peakvalue <<= 8; | ||
439 | peakvalue += (unsigned long)*frame++; | ||
440 | } | ||
441 | |||
442 | peakvalue <<= shift; | ||
443 | |||
444 | if (peakbits > 32) | ||
445 | peakvalue += (unsigned long)*frame >> (8 - shift); | ||
446 | |||
447 | snprintf(tmpbuf, sizeof(tmpbuf), "%d.%06d", peakvalue >> 31, | ||
448 | (peakvalue & ~(1 << 31)) / 2147); | ||
449 | |||
450 | *peak = get_replaypeak(tmpbuf); | ||
451 | |||
452 | return len + 1; | ||
453 | } | ||
454 | |||
455 | long parse_replaygain_rva(const char* key, const char* value, | ||
456 | struct mp3entry* entry, char* buffer, int length) | ||
457 | { | ||
458 | if ((strcasecmp(key, "track") == 0) && *value == MASTER_CHANNEL | ||
459 | && !entry->track_gain && !entry->track_peak) | ||
460 | { | ||
461 | return get_rva_values(value + 1, &(entry->track_gain), &(entry->track_peak), | ||
462 | &(entry->track_gain_string), buffer, length); | ||
463 | } | ||
464 | else if ((strcasecmp(key, "album") == 0) && *value == MASTER_CHANNEL | ||
465 | && !entry->album_gain && !entry->album_peak) | ||
466 | { | ||
467 | return get_rva_values(value + 1, &(entry->album_gain), &(entry->album_peak), | ||
468 | &(entry->album_gain_string), buffer, length); | ||
469 | } | ||
470 | |||
471 | return 0; | ||
472 | } | ||