summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Everton <dan@iocaine.org>2006-07-20 10:35:20 +0000
committerDan Everton <dan@iocaine.org>2006-07-20 10:35:20 +0000
commit7b5af8c045d1db6e805981e1c6d1b32de200e3f3 (patch)
tree669e1b044c9681203ef5fb72ebac466c701447a7
parent928c33c255e47c39c5f549b3e1aa40fbb3aaa09a (diff)
downloadrockbox-7b5af8c045d1db6e805981e1c6d1b32de200e3f3.tar.gz
rockbox-7b5af8c045d1db6e805981e1c6d1b32de200e3f3.zip
Add support for parsing binary data ID3 tags. Add support for parsing RVA2 ID3 tags for replaygain values instead of TXXX tags when playing files tagged with ID3v2.4. Patch by psyche78 (task #2937)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10259 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--docs/CREDITS1
-rw-r--r--firmware/export/replaygain.h4
-rw-r--r--firmware/id3.c223
-rw-r--r--firmware/replaygain.c110
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
215Daniel Ankers 215Daniel Ankers
216Paul Louden 216Paul Louden
217Rainer Sinsch 217Rainer Sinsch
218Plá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 @@
25long get_replaygain_int(long int_gain); 25long get_replaygain_int(long int_gain);
26long get_replaygain(const char* str); 26long get_replaygain(const char* str);
27long get_replaypeak(const char* str); 27long get_replaypeak(const char* str);
28long parse_replaygain(const char* key, const char* value, 28long parse_replaygain(const char* key, const char* value,
29 struct mp3entry* entry, char* buffer, int length);
30long 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
190static bool global_ff_found; 192static 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. */
346static 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
342static const struct tag_resolver taglist[] = { 370static 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 */
452static bool setid3v1title(int fd, struct mp3entry *entry) 481static 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 */
528static void setid3v2title(int fd, struct mp3entry *entry) 557static 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 */
835int getid3v2len(int fd) 868int 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 */
868static int getsonglength(int fd, struct mp3entry *entry) 901static 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
308long get_replaygain_int(long int_gain) 312long 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
320long get_replaygain(const char* str) 324long 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
333long get_replaypeak(const char* str) 337long 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 */
352long parse_replaygain(const char* key, const char* value, 356long 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
402static 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
455long 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}