summaryrefslogtreecommitdiff
path: root/firmware/replaygain.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/replaygain.c')
-rw-r--r--firmware/replaygain.c110
1 files changed, 93 insertions, 17 deletions
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 @@
#include "id3.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
* on oMathFP by Dan Carter (http://orbisstudios.com).
*/
@@ -278,7 +282,7 @@ static long fp_atof(const char* s, int precision)
frac_max_int *= 10;
}
- return sign * ((int_part * int_one)
+ return sign * ((int_part * int_one)
+ (((int64_t) frac_part * int_one) / frac_max_int));
}
@@ -286,14 +290,14 @@ static long convert_gain(long gain)
{
if (gain != 0)
{
- /* 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. :)
*/
if (gain < (-48 * FP_ONE))
{
gain = -48 * FP_ONE;
}
-
+
if (gain > (17 * FP_ONE))
{
gain = 17 * FP_ONE;
@@ -301,55 +305,55 @@ static long convert_gain(long gain)
gain = fp_exp10(gain / 20) << (24 - FP_BITS);
}
-
+
return gain;
}
long get_replaygain_int(long int_gain)
{
long gain = 0;
-
+
if (int_gain)
{
gain = convert_gain(int_gain * FP_ONE / 100);
}
-
+
return gain;
}
long get_replaygain(const char* str)
{
long gain = 0;
-
+
if (str)
{
gain = fp_atof(str, FP_BITS);
gain = convert_gain(gain);
}
-
+
return gain;
}
long get_replaypeak(const char* str)
{
long peak = 0;
-
+
if (str)
{
peak = fp_atof(str, 24);
}
-
+
return peak;
}
-/* Check for a ReplayGain tag conforming to the "VorbisGain standard". If
+/* Check for a ReplayGain tag conforming to the "VorbisGain standard". If
* 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
- * written. Returns number of bytes written to the tag text buffer, or zero
+ * contents of the gain tags; up to length bytes (including end nil) can be
+ * 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
* other reasons).
*/
-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)
{
char **p = NULL;
@@ -359,7 +363,7 @@ long parse_replaygain(const char* key, const char* value,
{
entry->track_gain = get_replaygain(value);
p = &(entry->track_gain_string);
- }
+ }
else if ((strcasecmp(key, "replaygain_album_gain") == 0)
|| ((strcasecmp(key, "rg_audiophile") == 0) && !entry->album_gain))
{
@@ -370,7 +374,7 @@ long parse_replaygain(const char* key, const char* value,
|| ((strcasecmp(key, "rg_peak") == 0) && !entry->track_peak))
{
entry->track_peak = get_replaypeak(value);
- }
+ }
else if (strcasecmp(key, "replaygain_album_peak") == 0)
{
entry->album_peak = get_replaypeak(value);
@@ -379,7 +383,7 @@ long parse_replaygain(const char* key, const char* value,
if (p)
{
int len = strlen(value);
-
+
len = MIN(len, length - 1);
/* A few characters just isn't interesting... */
@@ -394,3 +398,75 @@ long parse_replaygain(const char* key, const char* value,
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;
+}