summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Holmgren <magnushol@gmail.com>2005-07-24 15:25:13 +0000
committerMagnus Holmgren <magnushol@gmail.com>2005-07-24 15:25:13 +0000
commit6bd8e5db08e42130d1a72377b3c0cec0b8d57a69 (patch)
treed76f9efb587f87dc715a93f36c1ce313c4de112c
parent795ce8b1a829eb36c051e0c5f2e62572b71ab70c (diff)
downloadrockbox-6bd8e5db08e42130d1a72377b3c0cec0b8d57a69.zip
rockbox-6bd8e5db08e42130d1a72377b3c0cec0b8d57a69.tar.gz
rockbox-6bd8e5db08e42130d1a72377b3c0cec0b8d57a69.tar.bz2
rockbox-6bd8e5db08e42130d1a72377b3c0cec0b8d57a69.tar.xz
ReplayGain support files.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7233 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/export/replaygain.h26
-rw-r--r--firmware/replaygain.c328
2 files changed, 354 insertions, 0 deletions
diff --git a/firmware/export/replaygain.h b/firmware/export/replaygain.h
new file mode 100644
index 0000000..09b0776
--- /dev/null
+++ b/firmware/export/replaygain.h
@@ -0,0 +1,26 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Magnus Holmgren
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _REPLAYGAIN_H
+#define _REPLAYGAIN_H
+
+long get_replaygain(const char* str);
+long get_replaypeak(const char* str);
+
+#endif
diff --git a/firmware/replaygain.c b/firmware/replaygain.c
new file mode 100644
index 0000000..23a25cc
--- /dev/null
+++ b/firmware/replaygain.c
@@ -0,0 +1,328 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Magnus Holmgren
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "debug.h"
+
+/* The fixed point math routines (with the exception of fp_atof) are based
+ * on oMathFP by Dan Carter (http://orbisstudios.com).
+ */
+
+/* 12 bits of precision gives fairly accurate result, but still allows a
+ * compact implementation. The math code supports up to 13...
+ */
+
+#define FP_BITS (12)
+#define FP_MASK ((1 << FP_BITS) - 1)
+#define FP_ONE (1 << FP_BITS)
+#define FP_TWO (2 << FP_BITS)
+#define FP_HALF (1 << (FP_BITS - 1))
+#define FP_LN2 ( 45426 >> (16 - FP_BITS))
+#define FP_LN2_INV ( 94548 >> (16 - FP_BITS))
+#define FP_EXP_ZERO ( 10922 >> (16 - FP_BITS))
+#define FP_EXP_ONE ( -182 >> (16 - FP_BITS))
+#define FP_EXP_TWO ( 4 >> (16 - FP_BITS))
+#define FP_INF (0x7fffffff)
+#define FP_LN10 (150902 >> (16 - FP_BITS))
+
+#define FP_MAX_DIGITS (4)
+#define FP_MAX_DIGITS_INT (10000)
+
+#define FP_FAST_MUL_DIV
+
+#ifdef FP_FAST_MUL_DIV
+
+/* These macros can easily overflow, but they are good enough for our uses,
+ * and saves some code.
+ */
+#define fp_mul(x, y) (((x) * (y)) >> FP_BITS)
+#define fp_div(x, y) (((x) << FP_BITS) / (y))
+
+#else
+
+static long fp_mul(long x, long y)
+{
+ long x_neg = 0;
+ long y_neg = 0;
+ long rc;
+
+ if ((x == 0) || (y == 0))
+ {
+ return 0;
+ }
+
+ if (x < 0)
+ {
+ x_neg = 1;
+ x = -x;
+ }
+
+ if (y < 0)
+ {
+ y_neg = 1;
+ y = -y;
+ }
+
+ rc = (((x >> FP_BITS) * (y >> FP_BITS)) << FP_BITS)
+ + (((x & FP_MASK) * (y & FP_MASK)) >> FP_BITS)
+ + ((x & FP_MASK) * (y >> FP_BITS))
+ + ((x >> FP_BITS) * (y & FP_MASK));
+
+ if ((x_neg ^ y_neg) == 1)
+ {
+ rc = -rc;
+ }
+
+ return rc;
+}
+
+static long fp_div(long x, long y)
+{
+ long x_neg = 0;
+ long y_neg = 0;
+ long shifty;
+ long rc;
+ int msb = 0;
+ int lsb = 0;
+
+ if (x == 0)
+ {
+ return 0;
+ }
+
+ if (y == 0)
+ {
+ return (x < 0) ? -FP_INF : FP_INF;
+ }
+
+ if (x < 0)
+ {
+ x_neg = 1;
+ x = -x;
+ }
+
+ if (y < 0)
+ {
+ y_neg = 1;
+ y = -y;
+ }
+
+ while ((x & (1 << (30 - msb))) == 0)
+ {
+ msb++;
+ }
+
+ while ((y & (1 << lsb)) == 0)
+ {
+ lsb++;
+ }
+
+ shifty = FP_BITS - (msb + lsb);
+ rc = ((x << msb) / (y >> lsb));
+
+ if (shifty > 0)
+ {
+ rc <<= shifty;
+ }
+ else
+ {
+ rc >>= -shifty;
+ }
+
+ if ((x_neg ^ y_neg) == 1)
+ {
+ rc = -rc;
+ }
+
+ return rc;
+}
+
+#endif /* FP_FAST_MUL_DIV */
+
+static long fp_exp(long x)
+{
+ long k;
+ long z;
+ long R;
+ long xp;
+
+ if (x == 0)
+ {
+ return FP_ONE;
+ }
+
+ k = (fp_mul(abs(x), FP_LN2_INV) + FP_HALF) & ~FP_MASK;
+
+ if (x < 0)
+ {
+ k = -k;
+ }
+
+ x -= fp_mul(k, FP_LN2);
+ z = fp_mul(x, x);
+ R = FP_TWO + fp_mul(z, FP_EXP_ZERO + fp_mul(z, FP_EXP_ONE
+ + fp_mul(z, FP_EXP_TWO)));
+ xp = FP_ONE + fp_div(fp_mul(FP_TWO, x), R - x);
+
+ if (k < 0)
+ {
+ k = FP_ONE >> (-k >> FP_BITS);
+ }
+ else
+ {
+ k = FP_ONE << (k >> FP_BITS);
+ }
+
+ return fp_mul(k, xp);
+}
+
+static long fp_exp10(long x)
+{
+ if (x == 0)
+ {
+ return FP_ONE;
+ }
+
+ return fp_exp(fp_mul(FP_LN10, x));
+}
+
+static long fp_atof(const char* s, int precision)
+{
+ long int_part = 0;
+ long int_one = 1 << precision;
+ long frac_part = 0;
+ long frac_count = 0;
+ long frac_max = ((precision * 4) + 12) / 13;
+ long frac_max_int = 1;
+ long sign = 1;
+ bool point = false;
+
+ while ((*s != '\0') && isspace(*s))
+ {
+ s++;
+ }
+
+ if (*s == '-')
+ {
+ sign = -1;
+ s++;
+ }
+ else if (*s == '+')
+ {
+ s++;
+ }
+
+ while (*s != '\0')
+ {
+ if (*s == '.')
+ {
+ if (point)
+ {
+ break;
+ }
+
+ point = true;
+ }
+ else if (isdigit(*s))
+ {
+ if (point)
+ {
+ if (frac_count < frac_max)
+ {
+ frac_part = frac_part * 10 + (*s - '0');
+ frac_count++;
+ frac_max_int *= 10;
+ }
+ }
+ else
+ {
+ int_part = int_part * 10 + (*s - '0');
+ }
+ }
+ else
+ {
+ break;
+ }
+
+ s++;
+ }
+
+ while (frac_count < frac_max)
+ {
+ frac_part *= 10;
+ frac_count++;
+ frac_max_int *= 10;
+ }
+
+ return sign * ((int_part * int_one)
+ + (((int64_t) frac_part * int_one) / frac_max_int));
+}
+
+static long convert_gain(long gain)
+{
+ if (gain != 0)
+ {
+ /* Don't allow unreasonably low or high gain changes.
+ * Our math code can't handle it properly anyway. :)
+ */
+ if (gain < (-23 * FP_ONE))
+ {
+ gain = -23 * FP_ONE;
+ }
+
+ if (gain > (17 * FP_ONE))
+ {
+ gain = 17 * FP_ONE;
+ }
+
+ gain = fp_exp10(gain / 20) << (24 - FP_BITS);
+ }
+
+ 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;
+}