Index: firmware/id3.c =================================================================== RCS file: /cvsroot/rockbox/firmware/id3.c,v retrieving revision 1.124 diff -u -r1.124 id3.c --- firmware/id3.c 8 Jan 2006 08:58:58 -0000 1.124 +++ firmware/id3.c 18 Jan 2006 21:23:34 -0000 @@ -161,6 +161,7 @@ offsetof( struct mp3entry, variable_name_in_struct_mp3entry ), pointer to your special processing function or NULL if you need no special processing + flag indicating if this tag is binary or textual Many ID3 symbolic names come in more than one form. You can add both forms, each referencing the same variable in struct mp3entry. If both forms are present, the last found will be used. @@ -183,6 +184,7 @@ int tag_length; size_t offset; int (*ppFunc)(struct mp3entry*, char* tag, int bufferpos); + int binary; }; static bool global_ff_found; @@ -318,7 +320,9 @@ int desc_len = strlen(tag); int value_len = 0; - if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { + /* Only parse TXXX replaygain tags if tag version < 2.4 */ + if (entry->id3version < ID3_VER_2_4 && + (tag - entry->id3v2buf + desc_len + 2) < bufferpos) { /* At least part of the value was read, so we can safely try to * parse it */ @@ -335,24 +339,49 @@ return bufferpos; } + +static int parse_rva( struct mp3entry* entry, char* tag, int bufferpos ) +{ + char* value = NULL; + int desc_len = strlen(tag); + int value_len = 0; + + /* Only parse RVA2 replaygain tags if tag version == 2.4 */ + if (entry->id3version == ID3_VER_2_4 && + (tag - entry->id3v2buf + desc_len + 2) < bufferpos) { + value = tag + desc_len + 1; + value_len = parse_replaygain_rva(tag, value, entry, tag, + bufferpos - (tag - entry->id3v2buf)); + } + + if (value_len) { + bufferpos = tag - entry->id3v2buf + value_len; + } else { + bufferpos = tag - entry->id3v2buf; + } + + return bufferpos; +} #endif static const struct tag_resolver taglist[] = { - { "TPE1", 4, offsetof(struct mp3entry, artist), NULL }, - { "TP1", 3, offsetof(struct mp3entry, artist), NULL }, - { "TIT2", 4, offsetof(struct mp3entry, title), NULL }, - { "TT2", 3, offsetof(struct mp3entry, title), NULL }, - { "TALB", 4, offsetof(struct mp3entry, album), NULL }, - { "TAL", 3, offsetof(struct mp3entry, album), NULL }, - { "TRK", 3, offsetof(struct mp3entry, track_string), &parsetracknum }, - { "TRCK", 4, offsetof(struct mp3entry, track_string), &parsetracknum }, - { "TYER", 4, offsetof(struct mp3entry, year_string), &parseyearnum }, - { "TYE", 3, offsetof(struct mp3entry, year_string), &parseyearnum }, - { "TCOM", 4, offsetof(struct mp3entry, composer), NULL }, - { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre }, - { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre }, + { "TPE1", 4, offsetof(struct mp3entry, artist), NULL, 0 }, + { "TP1", 3, offsetof(struct mp3entry, artist), NULL, 0 }, + { "TIT2", 4, offsetof(struct mp3entry, title), NULL, 0 }, + { "TT2", 3, offsetof(struct mp3entry, title), NULL, 0 }, + { "TALB", 4, offsetof(struct mp3entry, album), NULL, 0 }, + { "TAL", 3, offsetof(struct mp3entry, album), NULL, 0 }, + { "TRK", 3, offsetof(struct mp3entry, track_string), &parsetracknum, 0 }, + { "TRCK", 4, offsetof(struct mp3entry, track_string), &parsetracknum, 0 }, + { "TDRC", 4, offsetof(struct mp3entry, year_string), &parseyearnum, 0 }, + { "TYER", 4, offsetof(struct mp3entry, year_string), &parseyearnum, 0 }, + { "TYE", 3, offsetof(struct mp3entry, year_string), &parseyearnum, 0 }, + { "TCOM", 4, offsetof(struct mp3entry, composer), NULL, 0 }, + { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre, 0 }, + { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre, 0 }, #if CONFIG_CODEC == SWCODEC - { "TXXX", 4, 0, &parseuser }, + { "TXXX", 4, 0, &parseuser, 0 }, + { "RVA2", 4, 0, &parse_rva, 1 }, #endif }; @@ -772,28 +801,32 @@ if(unsynch || (global_unsynch && version >= ID3_VER_2_4)) bytesread = unsynchronize_frame(tag, bytesread); - - /* UTF-8 could potentially be 3 times larger */ - /* so we need to create a new buffer */ - char utf8buf[(3 * bytesread) + 1]; - - unicode_munge( tag, utf8buf, &bytesread ); - - if(bytesread >= buffersize - bufferpos) - bytesread = buffersize - bufferpos - 1; - - for (j = 0; j < bytesread; j++) - tag[j] = utf8buf[j]; + /* Mess with tag contents only if tag is not binary */ + if(!tr->binary) + { + /* UTF-8 could potentially be 3 times larger */ + /* so we need to create a new buffer */ + char utf8buf[(3 * bytesread) + 1]; + + unicode_munge( tag, utf8buf, &bytesread ); + + if(bytesread >= buffersize - bufferpos) + bytesread = buffersize - bufferpos - 1; + + for (j = 0; j < bytesread; j++) + tag[j] = utf8buf[j]; + + /* remove trailing spaces */ + while ( bytesread > 0 && isspace(tag[bytesread-1])) + bytesread--; + } + + tag[bytesread] = 0; + bufferpos += bytesread + 1; if (ptag) *ptag = tag; - /* remove trailing spaces */ - while ( bytesread > 0 && isspace(tag[bytesread-1])) - bytesread--; - tag[bytesread] = 0; - bufferpos += bytesread + 1; - if( tr->ppFunc ) bufferpos = tr->ppFunc(entry, tag, bufferpos); Index: firmware/replaygain.c =================================================================== RCS file: /cvsroot/rockbox/firmware/replaygain.c,v retrieving revision 1.4 diff -u -r1.4 replaygain.c --- firmware/replaygain.c 3 Oct 2005 18:35:02 -0000 1.4 +++ firmware/replaygain.c 18 Jan 2006 21:23:34 -0000 @@ -28,6 +28,9 @@ #include "id3.h" #include "debug.h" +/* Type of channel for RVA2 frame. */ +#define MASTER_CHANNEL 1 + /* The fixed point math routines (with the exception of fp_atof) are based * on oMathFP by Dan Carter (http://orbisstudios.com). */ @@ -394,3 +397,70 @@ return 0; } + +static long get_rva_values(const char *frame, long *gain, long *peak, + char **string, char *buffer, int length) +{ + long value, len; + int negative = 0; + char tmpbuf[10]; + int peakbits, peakbytes, shift; + unsigned long peakvalue = 0; + + value = 256 * ((unsigned char)*frame) + ((unsigned char)*(frame + 1)); + if (value & 0x8000) { + value = -(value | ~0xFFFF); + negative = 1; + } + + len = snprintf(tmpbuf, sizeof(tmpbuf), "%s%d.%02d dB", negative ? "-" : "", + value / 512, (value & 0x1FF) * 195 / 1000); + + *gain = get_replaygain(tmpbuf); + + len = MIN(len, length - 1); + if (len > 1) + { + strncpy(buffer, tmpbuf, len); + buffer[len] = 0; + *string = buffer; + } + + frame += 2; + peakbits = *(unsigned char *)frame++; + peakbytes = MIN(4, (peakbits + 7) >> 3); + shift = ((8 - (peakbits & 7)) & 7) + (4 - peakbytes) * 8; + + for (; peakbytes; peakbytes--) { + peakvalue <<= 8; + peakvalue += (unsigned long)*frame++; + } + peakvalue <<= shift; + if (peakbits > 32) + peakvalue += (unsigned long)*frame >> (8 - shift); + + snprintf(tmpbuf, sizeof(tmpbuf), "%d.%06d", peakvalue >> 31, + (peakvalue & ~(1 << 31)) / 2147); + *peak = get_replaypeak(tmpbuf); + + return len + 1; +} + +long parse_replaygain_rva(const char* key, const char* value, + struct mp3entry* entry, char* buffer, int length) +{ + if ((strcasecmp(key, "track") == 0) && *value == MASTER_CHANNEL + && !entry->track_gain && !entry->track_peak) + { + return get_rva_values(value + 1, &(entry->track_gain), &(entry->track_peak), + &(entry->track_gain_string), buffer, length); + } + else if ((strcasecmp(key, "album") == 0) && *value == MASTER_CHANNEL + && !entry->album_gain && !entry->album_peak) + { + return get_rva_values(value + 1, &(entry->album_gain), &(entry->album_peak), + &(entry->album_gain_string), buffer, length); + } + + return 0; +} Index: firmware/export/replaygain.h =================================================================== RCS file: /cvsroot/rockbox/firmware/export/replaygain.h,v retrieving revision 1.3 diff -u -r1.3 replaygain.h --- firmware/export/replaygain.h 11 Aug 2005 18:56:20 -0000 1.3 +++ firmware/export/replaygain.h 18 Jan 2006 21:23:34 -0000 @@ -27,5 +27,7 @@ long get_replaypeak(const char* str); long parse_replaygain(const char* key, const char* value, struct mp3entry* entry, char* buffer, int length); +long parse_replaygain_rva(const char* key, const char* value, + struct mp3entry* entry, char* buffer, int length); #endif