summaryrefslogtreecommitdiff
path: root/apps/replaygain.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/replaygain.c')
-rw-r--r--apps/replaygain.c457
1 files changed, 457 insertions, 0 deletions
diff --git a/apps/replaygain.c b/apps/replaygain.c
new file mode 100644
index 0000000000..e160a1b23d
--- /dev/null
+++ b/apps/replaygain.c
@@ -0,0 +1,457 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Magnus Holmgren
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <ctype.h>
23#include <inttypes.h>
24#include <math.h>
25#include <stdbool.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <system.h>
30#include "id3.h"
31#include "debug.h"
32#include "replaygain.h"
33
34/* The fixed point math routines (with the exception of fp_atof) are based
35 * on oMathFP by Dan Carter (http://orbisstudios.com).
36 */
37
38/* 12 bits of precision gives fairly accurate result, but still allows a
39 * compact implementation. The math code supports up to 13...
40 */
41
42#define FP_BITS (12)
43#define FP_MASK ((1 << FP_BITS) - 1)
44#define FP_ONE (1 << FP_BITS)
45#define FP_TWO (2 << FP_BITS)
46#define FP_HALF (1 << (FP_BITS - 1))
47#define FP_LN2 ( 45426 >> (16 - FP_BITS))
48#define FP_LN2_INV ( 94548 >> (16 - FP_BITS))
49#define FP_EXP_ZERO ( 10922 >> (16 - FP_BITS))
50#define FP_EXP_ONE ( -182 >> (16 - FP_BITS))
51#define FP_EXP_TWO ( 4 >> (16 - FP_BITS))
52#define FP_INF (0x7fffffff)
53#define FP_LN10 (150902 >> (16 - FP_BITS))
54
55#define FP_MAX_DIGITS (4)
56#define FP_MAX_DIGITS_INT (10000)
57
58#define FP_FAST_MUL_DIV
59
60#ifdef FP_FAST_MUL_DIV
61
62/* These macros can easily overflow, but they are good enough for our uses,
63 * and saves some code.
64 */
65#define fp_mul(x, y) (((x) * (y)) >> FP_BITS)
66#define fp_div(x, y) (((x) << FP_BITS) / (y))
67
68#else
69
70static long fp_mul(long x, long y)
71{
72 long x_neg = 0;
73 long y_neg = 0;
74 long rc;
75
76 if ((x == 0) || (y == 0))
77 {
78 return 0;
79 }
80
81 if (x < 0)
82 {
83 x_neg = 1;
84 x = -x;
85 }
86
87 if (y < 0)
88 {
89 y_neg = 1;
90 y = -y;
91 }
92
93 rc = (((x >> FP_BITS) * (y >> FP_BITS)) << FP_BITS)
94 + (((x & FP_MASK) * (y & FP_MASK)) >> FP_BITS)
95 + ((x & FP_MASK) * (y >> FP_BITS))
96 + ((x >> FP_BITS) * (y & FP_MASK));
97
98 if ((x_neg ^ y_neg) == 1)
99 {
100 rc = -rc;
101 }
102
103 return rc;
104}
105
106static long fp_div(long x, long y)
107{
108 long x_neg = 0;
109 long y_neg = 0;
110 long shifty;
111 long rc;
112 int msb = 0;
113 int lsb = 0;
114
115 if (x == 0)
116 {
117 return 0;
118 }
119
120 if (y == 0)
121 {
122 return (x < 0) ? -FP_INF : FP_INF;
123 }
124
125 if (x < 0)
126 {
127 x_neg = 1;
128 x = -x;
129 }
130
131 if (y < 0)
132 {
133 y_neg = 1;
134 y = -y;
135 }
136
137 while ((x & (1 << (30 - msb))) == 0)
138 {
139 msb++;
140 }
141
142 while ((y & (1 << lsb)) == 0)
143 {
144 lsb++;
145 }
146
147 shifty = FP_BITS - (msb + lsb);
148 rc = ((x << msb) / (y >> lsb));
149
150 if (shifty > 0)
151 {
152 rc <<= shifty;
153 }
154 else
155 {
156 rc >>= -shifty;
157 }
158
159 if ((x_neg ^ y_neg) == 1)
160 {
161 rc = -rc;
162 }
163
164 return rc;
165}
166
167#endif /* FP_FAST_MUL_DIV */
168
169static long fp_exp(long x)
170{
171 long k;
172 long z;
173 long R;
174 long xp;
175
176 if (x == 0)
177 {
178 return FP_ONE;
179 }
180
181 k = (fp_mul(abs(x), FP_LN2_INV) + FP_HALF) & ~FP_MASK;
182
183 if (x < 0)
184 {
185 k = -k;
186 }
187
188 x -= fp_mul(k, FP_LN2);
189 z = fp_mul(x, x);
190 R = FP_TWO + fp_mul(z, FP_EXP_ZERO + fp_mul(z, FP_EXP_ONE
191 + fp_mul(z, FP_EXP_TWO)));
192 xp = FP_ONE + fp_div(fp_mul(FP_TWO, x), R - x);
193
194 if (k < 0)
195 {
196 k = FP_ONE >> (-k >> FP_BITS);
197 }
198 else
199 {
200 k = FP_ONE << (k >> FP_BITS);
201 }
202
203 return fp_mul(k, xp);
204}
205
206static long fp_exp10(long x)
207{
208 if (x == 0)
209 {
210 return FP_ONE;
211 }
212
213 return fp_exp(fp_mul(FP_LN10, x));
214}
215
216static long fp_atof(const char* s, int precision)
217{
218 long int_part = 0;
219 long int_one = 1 << precision;
220 long frac_part = 0;
221 long frac_count = 0;
222 long frac_max = ((precision * 4) + 12) / 13;
223 long frac_max_int = 1;
224 long sign = 1;
225 bool point = false;
226
227 while ((*s != '\0') && isspace(*s))
228 {
229 s++;
230 }
231
232 if (*s == '-')
233 {
234 sign = -1;
235 s++;
236 }
237 else if (*s == '+')
238 {
239 s++;
240 }
241
242 while (*s != '\0')
243 {
244 if (*s == '.')
245 {
246 if (point)
247 {
248 break;
249 }
250
251 point = true;
252 }
253 else if (isdigit(*s))
254 {
255 if (point)
256 {
257 if (frac_count < frac_max)
258 {
259 frac_part = frac_part * 10 + (*s - '0');
260 frac_count++;
261 frac_max_int *= 10;
262 }
263 }
264 else
265 {
266 int_part = int_part * 10 + (*s - '0');
267 }
268 }
269 else
270 {
271 break;
272 }
273
274 s++;
275 }
276
277 while (frac_count < frac_max)
278 {
279 frac_part *= 10;
280 frac_count++;
281 frac_max_int *= 10;
282 }
283
284 return sign * ((int_part * int_one)
285 + (((int64_t) frac_part * int_one) / frac_max_int));
286}
287
288static long convert_gain(long gain)
289{
290 /* Don't allow unreasonably low or high gain changes.
291 * Our math code can't handle it properly anyway. :)
292 */
293 if (gain < (-48 * FP_ONE))
294 {
295 gain = -48 * FP_ONE;
296 }
297
298 if (gain > (17 * FP_ONE))
299 {
300 gain = 17 * FP_ONE;
301 }
302
303 gain = fp_exp10(gain / 20) << (24 - FP_BITS);
304
305 return gain;
306}
307
308/* Get the sample scale factor in Q7.24 format from a gain value. Returns 0
309 * for no gain.
310 *
311 * str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored.
312 */
313static long get_replaygain(const char* str)
314{
315 long gain = 0;
316
317 if (str)
318 {
319 gain = fp_atof(str, FP_BITS);
320 gain = convert_gain(gain);
321 }
322
323 return gain;
324}
325
326/* Get the peak volume in Q7.24 format.
327 *
328 * str Peak volume. Full scale is specified as "1.0". Returns 0 for no peak.
329 */
330static long get_replaypeak(const char* str)
331{
332 long peak = 0;
333
334 if (str)
335 {
336 peak = fp_atof(str, 24);
337 }
338
339 return peak;
340}
341
342/* Get a sample scale factor in Q7.24 format from a gain value.
343 *
344 * int_gain Gain in dB, multiplied by 100.
345 */
346long get_replaygain_int(long int_gain)
347{
348 return convert_gain(int_gain * FP_ONE / 100);
349}
350
351/* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a
352 * valid tag is found, update mp3entry struct accordingly. Existing values
353 * are not overwritten. Returns number of bytes written to buffer.
354 *
355 * key Name of the tag.
356 * value Value of the tag.
357 * entry mp3entry struct to update.
358 * buffer Where to store the text for gain values (for later display).
359 * length Bytes left in buffer.
360 */
361long parse_replaygain(const char* key, const char* value,
362 struct mp3entry* entry, char* buffer, int length)
363{
364 char **p = NULL;
365
366 if (((strcasecmp(key, "replaygain_track_gain") == 0)
367 || (strcasecmp(key, "rg_radio") == 0)) && !entry->track_gain)
368 {
369 entry->track_gain = get_replaygain(value);
370 p = &(entry->track_gain_string);
371 }
372 else if (((strcasecmp(key, "replaygain_album_gain") == 0)
373 || (strcasecmp(key, "rg_audiophile") == 0)) && !entry->album_gain)
374 {
375 entry->album_gain = get_replaygain(value);
376 p = &(entry->album_gain_string);
377 }
378 else if (((strcasecmp(key, "replaygain_track_peak") == 0)
379 || (strcasecmp(key, "rg_peak") == 0)) && !entry->track_peak)
380 {
381 entry->track_peak = get_replaypeak(value);
382 }
383 else if ((strcasecmp(key, "replaygain_album_peak") == 0)
384 && !entry->album_peak)
385 {
386 entry->album_peak = get_replaypeak(value);
387 }
388
389 if (p)
390 {
391 int len = strlen(value);
392
393 len = MIN(len, length - 1);
394
395 /* A few characters just isn't interesting... */
396 if (len > 1)
397 {
398 strncpy(buffer, value, len);
399 buffer[len] = 0;
400 *p = buffer;
401 return len + 1;
402 }
403 }
404
405 return 0;
406}
407
408/* Set ReplayGain values from integers. Existing values are not overwritten.
409 * Returns number of bytes written to buffer.
410 *
411 * album If true, set album values, otherwise set track values.
412 * gain Gain value in dB, multiplied by 512. 0 for no gain.
413 * peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no
414 * peak volume.
415 * buffer Where to store the text for gain values (for later display).
416 * length Bytes left in buffer.
417 */
418long parse_replaygain_int(bool album, long gain, long peak,
419 struct mp3entry* entry, char* buffer, int length)
420{
421 long len = 0;
422
423 if (buffer != NULL)
424 {
425 len = snprintf(buffer, length, "%d.%02d dB", gain / 512,
426 ((abs(gain) & 0x01ff) * 100 + 256) / 512);
427 len++;
428 }
429
430 if (gain != 0)
431 {
432 gain = convert_gain(gain * FP_ONE / 512);
433 }
434
435 if (album)
436 {
437 entry->album_gain = gain;
438 entry->album_gain_string = buffer;
439
440 if (peak)
441 {
442 entry->album_peak = peak;
443 }
444 }
445 else
446 {
447 entry->track_gain = gain;
448 entry->track_gain_string = buffer;
449
450 if (peak)
451 {
452 entry->track_peak = peak;
453 }
454 }
455
456 return len;
457}