FS#11964. Rework replaygain handling to save metadata buffer and binsize. Remove string representation of replaygain and use a dedicated ftoa implementation for WPS/screen info.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29388 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Andree Buschmann 2011-02-24 19:10:59 +00:00
parent 6510973223
commit 71ceac0b74
12 changed files with 65 additions and 158 deletions

View file

@ -1475,22 +1475,22 @@ intptr_t dsp_configure(struct dsp_config *dsp, int setting, intptr_t value)
case DSP_SET_TRACK_GAIN: case DSP_SET_TRACK_GAIN:
if (dsp == &AUDIO_DSP) if (dsp == &AUDIO_DSP)
dsp_set_gain_var(&track_gain, value); dsp_set_gain_var(&track_gain, convert_gain(value));
break; break;
case DSP_SET_ALBUM_GAIN: case DSP_SET_ALBUM_GAIN:
if (dsp == &AUDIO_DSP) if (dsp == &AUDIO_DSP)
dsp_set_gain_var(&album_gain, value); dsp_set_gain_var(&album_gain, convert_gain(value));
break; break;
case DSP_SET_TRACK_PEAK: case DSP_SET_TRACK_PEAK:
if (dsp == &AUDIO_DSP) if (dsp == &AUDIO_DSP)
dsp_set_gain_var(&track_peak, value); dsp_set_gain_var(&track_peak, convert_gain(value));
break; break;
case DSP_SET_ALBUM_PEAK: case DSP_SET_ALBUM_PEAK:
if (dsp == &AUDIO_DSP) if (dsp == &AUDIO_DSP)
dsp_set_gain_var(&album_peak, value); dsp_set_gain_var(&album_peak, convert_gain(value));
break; break;
default: default:

View file

@ -35,6 +35,7 @@
#include "sound.h" #include "sound.h"
#include "debug.h" #include "debug.h"
#include "cuesheet.h" #include "cuesheet.h"
#include "replaygain.h"
#ifdef HAVE_LCD_CHARCELLS #ifdef HAVE_LCD_CHARCELLS
#include "hwcompat.h" #include "hwcompat.h"
#endif #endif
@ -1305,8 +1306,8 @@ const char *get_token_value(struct gui_wps *gwps,
{ {
int type; int type;
if (LIKELY(id3)) if (LIKELY(id3))
type = get_replaygain_mode(id3->track_gain_string != NULL, type = get_replaygain_mode(id3->track_gain != 0,
id3->album_gain_string != NULL); id3->album_gain != 0);
else else
type = -1; type = -1;
@ -1331,11 +1332,11 @@ const char *get_token_value(struct gui_wps *gwps,
/* due to above, coming here with !id3 shouldn't be possible */ /* due to above, coming here with !id3 shouldn't be possible */
case 2: case 2:
case 4: case 4:
strlcpy(buf, id3->track_gain_string, buf_size); replaygain_itoa(buf, buf_size, id3->track_gain);
break; break;
case 3: case 3:
case 5: case 5:
strlcpy(buf, id3->album_gain_string, buf_size); replaygain_itoa(buf, buf_size, id3->album_gain);
break; break;
} }
return buf; return buf;

View file

@ -411,12 +411,6 @@ void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig)
entry->albumartist += offset; entry->albumartist += offset;
if (entry->grouping) if (entry->grouping)
entry->grouping += offset; entry->grouping += offset;
#if CONFIG_CODEC == SWCODEC
if (entry->track_gain_string)
entry->track_gain_string += offset;
if (entry->album_gain_string)
entry->album_gain_string += offset;
#endif
if (entry->mb_track_id) if (entry->mb_track_id)
entry->mb_track_id += offset; entry->mb_track_id += offset;
} }

View file

@ -283,13 +283,10 @@ struct mp3entry {
#endif #endif
/* replaygain support */ /* replaygain support */
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
char* track_gain_string; long track_gain; /* s19.12 signed fixed point. 0 for no gain. */
char* album_gain_string;
long track_gain; /* 7.24 signed fixed point. 0 for no gain. */
long album_gain; long album_gain;
long track_peak; /* 7.24 signed fixed point. 0 for no peak. */ long track_peak; /* s19.12 signed fixed point. 0 for no peak. */
long album_peak; long album_peak;
#endif #endif

View file

@ -456,18 +456,8 @@ static int asf_parse_header(int fd, struct mp3entry* id3,
lseek(fd, length, SEEK_CUR); lseek(fd, length, SEEK_CUR);
} }
} else if (!strncmp("replaygain_", utf8buf, 11)) { } else if (!strncmp("replaygain_", utf8buf, 11)) {
char* value = id3buf;
int buf_len = id3buf_remaining;
int len;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);
len = parse_replaygain(utf8buf, value, id3, parse_replaygain(utf8buf, id3buf, id3);
value, buf_len);
if (len == 0) {
/* Don't need to keep the value */
id3buf = value;
id3buf_remaining = buf_len;
}
} else if (!strcmp("MusicBrainz/Track Id", utf8buf)) { } else if (!strcmp("MusicBrainz/Track Id", utf8buf)) {
id3->mb_track_id = id3buf; id3->mb_track_id = id3buf;
asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining);

View file

@ -370,14 +370,8 @@ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos )
entry->albumartist = tag; entry->albumartist = tag;
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
} else { } else {
/* Calculate residual buffer size in bytes which can be used by /* Call parse_replaygain(). */
* parse_replaygain() to save the string representation of parse_replaygain(tag, value, entry);
* replaygain data.*/
length = sizeof(entry->id3v2buf) - (tag - entry->id3v2buf);
/* Call parse_replaygain(), returns length in bytes used by the
* string representation of replaygain data. */
length = parse_replaygain(tag, value, entry, tag, length);
#endif #endif
} }
} }
@ -387,12 +381,11 @@ static int parseuser( struct mp3entry* entry, char* tag, int bufferpos )
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
/* 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)
{ {
int desc_len = strlen(tag); int desc_len = strlen(tag);
int start_pos = tag - entry->id3v2buf; int start_pos = tag - entry->id3v2buf;
int end_pos = start_pos + desc_len + 5; int end_pos = start_pos + desc_len + 5;
int value_len = 0;
unsigned char* value = tag + desc_len + 1; unsigned char* value = tag + desc_len + 1;
/* Only parse RVA2 replaygain tags if tag version == 2.4 and channel /* Only parse RVA2 replaygain tags if tag version == 2.4 and channel
@ -447,11 +440,10 @@ static int parserva2( struct mp3entry* entry, char* tag, int bufferpos )
} }
} }
value_len = parse_replaygain_int(album, gain, peak * 2, entry, parse_replaygain_int(album, gain, peak * 2, entry);
tag, sizeof(entry->id3v2buf) - start_pos);
} }
return start_pos + value_len; return start_pos;
} }
#endif #endif

View file

@ -333,7 +333,7 @@ long parse_tag(const char* name, char* value, struct mp3entry* id3,
} }
else else
{ {
len = parse_replaygain(name, value, id3, buf, buf_remaining); parse_replaygain(name, value, id3);
p = NULL; p = NULL;
} }

View file

@ -528,13 +528,7 @@ static bool read_mp4_tags(int fd, struct mp3entry* id3,
buffer -= length; buffer -= length;
buffer_left += length; buffer_left += length;
if (parse_replaygain(tag_name, buffer, id3, parse_replaygain(tag_name, buffer, id3);
buffer, buffer_left) > 0)
{
/* Data used, keep it. */
buffer += length;
buffer_left -= length;
}
} }
} }
} }

View file

@ -46,8 +46,7 @@ static int set_replaygain_sv7(struct mp3entry* id3,
/* We use a peak value of 0 to indicate a given gain type isn't used. */ /* We use a peak value of 0 to indicate a given gain type isn't used. */
if (peak != 0) { if (peak != 0) {
/* Save the ReplayGain data to id3-structure for further processing. */ /* Save the ReplayGain data to id3-structure for further processing. */
used += parse_replaygain_int(album, gain * 512 / 100, peak << 9, parse_replaygain_int(album, gain * 512 / 100, peak << 9, id3);
id3, id3->toc + used, sizeof(id3->toc) - used);
} }
return used; return used;
@ -73,8 +72,7 @@ static int set_replaygain_sv8(struct mp3entry* id3,
/* We use a peak value of 0 to indicate a given gain type isn't used. */ /* We use a peak value of 0 to indicate a given gain type isn't used. */
if (peak != 0) { if (peak != 0) {
/* Save the ReplayGain data to id3-structure for further processing. */ /* Save the ReplayGain data to id3-structure for further processing. */
used += parse_replaygain_int(album, gain * 512 / 100, peak, parse_replaygain_int(album, gain * 512 / 100, peak, id3);
id3, id3->toc + used, sizeof(id3->toc) - used);
} }
return used; return used;

View file

@ -36,6 +36,13 @@
#define FP_BITS (12) #define FP_BITS (12)
#define FP_ONE (1 << FP_BITS) #define FP_ONE (1 << FP_BITS)
void replaygain_itoa(char* buffer, int length, long int_gain)
{
/* int_gain uses Q19.12 format. */
int one = abs(int_gain) >> FP_BITS;
int cent = ((abs(int_gain) & 0x0fff) * 100 + (FP_ONE/2)) >> FP_BITS;
snprintf(buffer, length, "%d.%02d dB", (int_gain<0) ? -one : one, cent);
}
static long fp_atof(const char* s, int precision) static long fp_atof(const char* s, int precision)
{ {
@ -109,42 +116,25 @@ static long fp_atof(const char* s, int precision)
+ (((int64_t) frac_part * int_one) / frac_max_int)); + (((int64_t) frac_part * int_one) / frac_max_int));
} }
static long convert_gain(long gain) long convert_gain(long gain)
{ {
/* Don't allow unreasonably low or high gain changes. /* Don't allow unreasonably low or high gain changes.
* Our math code can't handle it properly anyway. :) * Our math code can't handle it properly anyway. :)
*/ */
if (gain < (-48 * FP_ONE)) gain = MAX(gain,-48 * FP_ONE);
{ gain = MIN(gain, 17 * FP_ONE);
gain = -48 * FP_ONE;
}
if (gain > (17 * FP_ONE)) return fp_factor(gain, FP_BITS) << (24 - FP_BITS);
{
gain = 17 * FP_ONE;
}
gain = fp_factor(gain, FP_BITS) << (24 - FP_BITS);
return gain;
} }
/* Get the sample scale factor in Q7.24 format from a gain value. Returns 0 /* Get the sample scale factor in Q19.12 format from a gain value. Returns 0
* for no gain. * for no gain.
* *
* str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored. * str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored.
*/ */
static long get_replaygain(const char* str) static long get_replaygain(const char* str)
{ {
long gain = 0; return fp_atof(str, FP_BITS);
if (str)
{
gain = fp_atof(str, FP_BITS);
gain = convert_gain(gain);
}
return gain;
} }
/* Get the peak volume in Q7.24 format. /* Get the peak volume in Q7.24 format.
@ -153,14 +143,7 @@ static long get_replaygain(const char* str)
*/ */
static long get_replaypeak(const char* str) static long get_replaypeak(const char* str)
{ {
long peak = 0; return fp_atof(str, 24);
if (str)
{
peak = fp_atof(str, 24);
}
return peak;
} }
/* Get a sample scale factor in Q7.24 format from a gain value. /* Get a sample scale factor in Q7.24 format from a gain value.
@ -174,107 +157,60 @@ long get_replaygain_int(long int_gain)
/* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a /* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a
* valid tag is found, update mp3entry struct accordingly. Existing values * valid tag is found, update mp3entry struct accordingly. Existing values
* are not overwritten. Returns number of bytes written to buffer. * are not overwritten.
* *
* key Name of the tag. * key Name of the tag.
* value Value of the tag. * value Value of the tag.
* entry mp3entry struct to update. * 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, void parse_replaygain(const char* key, const char* value,
struct mp3entry* entry, char* buffer, int length) struct mp3entry* entry)
{ {
char **p = NULL; if (((strcasecmp(key, "replaygain_track_gain") == 0) ||
(strcasecmp(key, "rg_radio") == 0)) &&
if (((strcasecmp(key, "replaygain_track_gain") == 0) !entry->track_gain)
|| (strcasecmp(key, "rg_radio") == 0)) && !entry->track_gain)
{ {
entry->track_gain = get_replaygain(value); entry->track_gain = get_replaygain(value);
p = &(entry->track_gain_string);
} }
else if (((strcasecmp(key, "replaygain_album_gain") == 0) else if (((strcasecmp(key, "replaygain_album_gain") == 0) ||
|| (strcasecmp(key, "rg_audiophile") == 0)) && !entry->album_gain) (strcasecmp(key, "rg_audiophile") == 0)) &&
!entry->album_gain)
{ {
entry->album_gain = get_replaygain(value); entry->album_gain = get_replaygain(value);
p = &(entry->album_gain_string);
} }
else if (((strcasecmp(key, "replaygain_track_peak") == 0) else if (((strcasecmp(key, "replaygain_track_peak") == 0) ||
|| (strcasecmp(key, "rg_peak") == 0)) && !entry->track_peak) (strcasecmp(key, "rg_peak") == 0)) &&
!entry->track_peak)
{ {
entry->track_peak = get_replaypeak(value); entry->track_peak = get_replaypeak(value);
} }
else if ((strcasecmp(key, "replaygain_album_peak") == 0) else if ((strcasecmp(key, "replaygain_album_peak") == 0) &&
&& !entry->album_peak) !entry->album_peak)
{ {
entry->album_peak = get_replaypeak(value); entry->album_peak = get_replaypeak(value);
} }
if (p)
{
int len = strlen(value);
len = MIN(len, length - 1);
/* A few characters just isn't interesting... */
if (len > 1)
{
strlcpy(buffer, value, len + 1);
*p = buffer;
return len + 1;
}
}
return 0;
} }
/* Set ReplayGain values from integers. Existing values are not overwritten. /* Set ReplayGain values from integers. Existing values are not overwritten.
* Returns number of bytes written to buffer.
* *
* album If true, set album values, otherwise set track values. * album If true, set album values, otherwise set track values.
* gain Gain value in dB, multiplied by 512. 0 for no gain. * gain Gain value in dB, multiplied by 512. 0 for no gain.
* peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no * peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no
* peak volume. * peak volume.
* buffer Where to store the text for gain values (for later display).
* length Bytes left in buffer.
*/ */
long parse_replaygain_int(bool album, long gain, long peak, void parse_replaygain_int(bool album, long gain, long peak,
struct mp3entry* entry, char* buffer, int length) struct mp3entry* entry)
{ {
long len = 0; gain = gain * FP_ONE / 512;
if (buffer != NULL)
{
len = snprintf(buffer, length, "%ld.%02d dB", gain / 512,
((abs(gain) & 0x01ff) * 100 + 256) / 512);
len++;
}
if (gain != 0)
{
gain = convert_gain(gain * FP_ONE / 512);
}
if (album) if (album)
{ {
entry->album_gain = gain; entry->album_gain = gain;
entry->album_gain_string = buffer; entry->album_peak = peak;
if (peak)
{
entry->album_peak = peak;
}
} }
else else
{ {
entry->track_gain = gain; entry->track_gain = gain;
entry->track_gain_string = buffer; entry->track_peak = peak;
if (peak)
{
entry->track_peak = peak;
}
} }
return len;
} }

View file

@ -25,9 +25,11 @@
#include "metadata.h" #include "metadata.h"
long get_replaygain_int(long int_gain); long get_replaygain_int(long int_gain);
long parse_replaygain(const char* key, const char* value, void parse_replaygain(const char* key, const char* value,
struct mp3entry* entry, char* buffer, int length); struct mp3entry* entry);
long parse_replaygain_int(bool album, long gain, long peak, void parse_replaygain_int(bool album, long gain, long peak,
struct mp3entry* entry, char* buffer, int length); struct mp3entry* entry);
void replaygain_itoa(char* buffer, int length, long int_gain);
long convert_gain(long gain);
#endif #endif

View file

@ -53,6 +53,7 @@
#include "backdrop.h" #include "backdrop.h"
#include "viewport.h" #include "viewport.h"
#include "language.h" #include "language.h"
#include "replaygain.h"
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
#include "dsp.h" #include "dsp.h"
@ -728,10 +729,12 @@ static const char* id3_get_info(int selected_item, void* data,
break; break;
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
case LANG_ID3_TRACK_GAIN: case LANG_ID3_TRACK_GAIN:
val=id3->track_gain_string; replaygain_itoa(buffer, buffer_len, id3->track_gain);
val=(id3->track_gain) ? buffer : NULL; /* only show gains!=0 */
break; break;
case LANG_ID3_ALBUM_GAIN: case LANG_ID3_ALBUM_GAIN:
val=id3->album_gain_string; replaygain_itoa(buffer, buffer_len, id3->album_gain);
val=(id3->album_gain) ? buffer : NULL; /* only show gains!=0 */
break; break;
#endif #endif
case LANG_ID3_PATH: case LANG_ID3_PATH: