1
0
Fork 0
forked from len0rd/rockbox

Small ReplayGain cleanup. Move RVA2 specific code to id3.c and don't do the parsing via strings. Generalize parts of the code for later use by MPC. Make some local functions static. Add and update some comments for clarity.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14453 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Magnus Holmgren 2007-08-25 10:25:13 +00:00
parent 163d72ff1f
commit 4e68732769
3 changed files with 141 additions and 108 deletions

View file

@ -23,11 +23,9 @@
#include "id3.h" #include "id3.h"
long get_replaygain_int(long int_gain); long get_replaygain_int(long int_gain);
long get_replaygain(const char* str);
long get_replaypeak(const char* str);
long parse_replaygain(const char* key, const char* value, long parse_replaygain(const char* key, const char* value,
struct mp3entry* entry, char* buffer, int length); struct mp3entry* entry, char* buffer, int length);
long parse_replaygain_rva(const char* key, const char* value, long parse_replaygain_int(bool album, long gain, long peak,
struct mp3entry* entry, char* buffer, int length); struct mp3entry* entry, char* buffer, int length);
#endif #endif

View file

@ -400,8 +400,6 @@ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos )
int desc_len = strlen(tag); int desc_len = strlen(tag);
int value_len = 0; int value_len = 0;
/* Note: for ID3v2.4, parse_replaygain will not overwrite replaygain
values already parsed from RVA2 tags */
if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) { if ((tag - entry->id3v2buf + desc_len + 2) < bufferpos) {
/* At least part of the value was read, so we can safely try to /* At least part of the value was read, so we can safely try to
* parse it * parse it
@ -411,37 +409,71 @@ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos )
bufferpos - (tag - entry->id3v2buf)); bufferpos - (tag - entry->id3v2buf));
} }
if (value_len) { return tag - entry->id3v2buf + value_len;
bufferpos = tag - entry->id3v2buf + value_len;
} else {
bufferpos = tag - entry->id3v2buf;
}
return bufferpos;
} }
/* parse RVA2 binary data and convert to replaygain information. */ /* parse RVA2 binary data and convert to replaygain information. */
static int parserva2( struct mp3entry* entry, char* tag, int bufferpos ) static int parserva2( struct mp3entry* entry, char* tag, int bufferpos )
{ {
char* value = NULL;
int desc_len = strlen(tag); int desc_len = strlen(tag);
int end_pos = tag - entry->id3v2buf + desc_len + 5;
int value_len = 0; int value_len = 0;
unsigned char* value = tag + desc_len + 1;
/* Only parse RVA2 replaygain tags if tag version == 2.4 */ /* Only parse RVA2 replaygain tags if tag version == 2.4 and channel
if (entry->id3version == ID3_VER_2_4 && * type is master volume.
(tag - entry->id3v2buf + desc_len + 2) < bufferpos) { */
value = tag + desc_len + 1; if (entry->id3version == ID3_VER_2_4 && end_pos < bufferpos
value_len = parse_replaygain_rva(tag, value, entry, tag, && *value++ == 1) {
bufferpos - (tag - entry->id3v2buf)); long gain = 0;
long peak = 0;
long peakbits;
long peakbytes;
bool album = false;
/* The RVA2 specification is unclear on some things (id string and
* peak volume), but this matches how Quod Libet use them.
*/
gain = (int16_t) ((value[0] << 8) | value[1]);
value += 2;
peakbits = *value++;
peakbytes = (peakbits + 7) / 8;
/* Only use the topmost 24 bits for peak volume */
if (peakbytes > 3) {
peakbytes = 3;
}
/* Make sure the peak bits were read */
if (end_pos + peakbytes < bufferpos) {
long shift = ((8 - (peakbits & 7)) & 7) + (3 - peakbytes) * 8;
for ( ; peakbytes; peakbytes--) {
peak <<= 8;
peak += *value++;
}
peak <<= shift;
if (peakbits > 24) {
peak += *value >> (8 - shift);
}
}
if (strcasecmp(tag, "album") == 0) {
album = true;
} else if (strcasecmp(tag, "track") != 0) {
gain = 0;
}
if (gain) {
value_len = parse_replaygain_int(album, gain, peak * 2, entry,
tag, sizeof(entry->id3v2buf) - (tag - entry->id3v2buf));
}
} }
if (value_len) { return tag - entry->id3v2buf + value_len;
bufferpos = tag - entry->id3v2buf + value_len;
} else {
bufferpos = tag - entry->id3v2buf;
}
return bufferpos;
} }
#endif #endif

View file

@ -28,10 +28,6 @@
#include "id3.h" #include "id3.h"
#include "debug.h" #include "debug.h"
/* Type of channel for RVA2 frame. There are more than this defined in the spec
but we don't use them. */
#define MASTER_CHANNEL 1
/* The fixed point math routines (with the exception of fp_atof) are based /* The fixed point math routines (with the exception of fp_atof) are based
* on oMathFP by Dan Carter (http://orbisstudios.com). * on oMathFP by Dan Carter (http://orbisstudios.com).
*/ */
@ -306,12 +302,12 @@ static long convert_gain(long gain)
return gain; return gain;
} }
long get_replaygain_int(long int_gain) /* Get the sample scale factor in Q7.24 format from a gain value. Returns 0
{ * for no gain.
return convert_gain(int_gain * FP_ONE / 100); *
} * str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored.
*/
long get_replaygain(const char* str) static long get_replaygain(const char* str)
{ {
long gain = 0; long gain = 0;
@ -324,7 +320,11 @@ long get_replaygain(const char* str)
return gain; return gain;
} }
long get_replaypeak(const char* str) /* Get the peak volume in Q7.24 format.
*
* str Peak volume. Full scale is specified as "1.0". Returns 0 for no peak.
*/
static long get_replaypeak(const char* str)
{ {
long peak = 0; long peak = 0;
@ -336,12 +336,24 @@ long get_replaypeak(const char* str)
return peak; return peak;
} }
/* Check for a ReplayGain tag conforming to the "VorbisGain standard". If /* Get a sample scale factor in Q7.24 format from a gain value.
* found, set the mp3entry accordingly. buffer is where to store the text *
* contents of the gain tags; up to length bytes (including end nil) can be * int_gain Gain in dB, multiplied by 100.
* written. Returns number of bytes written to the tag text buffer, or zero */
* if no ReplayGain tag was found (or nothing was copied to the buffer for long get_replaygain_int(long int_gain)
* other reasons). {
return convert_gain(int_gain * FP_ONE / 100);
}
/* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a
* valid tag is found, update mp3entry struct accordingly. Existing values
* are not overwritten. Returns number of bytes written to buffer.
*
* key Name of the tag.
* value Value of the tag.
* entry mp3entry struct to update.
* buffer Where to store the text for gain values (for later display).
* length Bytes left in buffer.
*/ */
long parse_replaygain(const char* key, const char* value, long parse_replaygain(const char* key, const char* value,
struct mp3entry* entry, char* buffer, int length) struct mp3entry* entry, char* buffer, int length)
@ -390,74 +402,65 @@ long parse_replaygain(const char* key, const char* value,
return 0; return 0;
} }
static long get_rva_values(const char *frame, long *gain, long *peak, /* Set ReplayGain values from integers. Existing values are not overwritten.
char **string, char *buffer, int length) * Returns number of bytes written to buffer.
{ *
long value, len; * album If true, set album values, otherwise set track values.
int negative = 0; * gain Gain value in dB, multiplied by 512. 0 for no gain.
char tmpbuf[10]; * peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no
int peakbits, peakbytes, shift; * peak volume.
unsigned long peakvalue = 0; * buffer Where to store the text for gain values (for later display).
* length Bytes left in buffer.
value = 256 * ((unsigned char)*frame) + ((unsigned char)*(frame + 1)); */
if (value & 0x8000) long parse_replaygain_int(bool album, long gain, long peak,
{
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) struct mp3entry* entry, char* buffer, int length)
{ {
/* Values will be overwritten if they already exist. This gives priority to long len = 0;
replaygain in RVA2 fields over TXXX fields for ID3v2.4. */
if ((strcasecmp(key, "track") == 0) && *value == MASTER_CHANNEL) if (buffer != NULL)
{ {
return get_rva_values(value + 1, &(entry->track_gain), &(entry->track_peak), len = snprintf(buffer, length, "%d.%02d dB", gain / 512,
&(entry->track_gain_string), buffer, length); ((abs(gain) & 0x01ff) * 100 + 256) / 512);
} len++;
else if ((strcasecmp(key, "album") == 0) && *value == MASTER_CHANNEL)
{
return get_rva_values(value + 1, &(entry->album_gain), &(entry->album_peak),
&(entry->album_gain_string), buffer, length);
} }
return 0; if (gain != 0)
{
gain = convert_gain(gain * FP_ONE / 512);
}
DEBUGF(" Album: %d\n", album);
DEBUGF(" Gain: %ld.%06ld\n", gain >> 24,
(long) (((long long) (abs(gain) & 0x00ffffff) * 1000000) / 0x01000000));
DEBUGF(" Peak: %ld.%06ld\n", peak >> 24,
(long) (((long long) (abs(peak) & 0x00ffffff) * 1000000) / 0x01000000));
if (album)
{
if (!entry->album_gain)
{
entry->album_gain = gain;
entry->album_gain_string = buffer;
}
if (!entry->album_peak)
{
entry->album_peak = peak;
}
}
else
{
if (!entry->track_gain)
{
entry->track_gain = gain;
entry->track_gain_string = buffer;
}
if (!entry->track_peak)
{
entry->track_peak = peak;
}
}
return len;
} }