Hopefully get shutdown/exit handling on SDL/maemo right.
[maemo-rb.git] / apps / replaygain.c
blob4b6bf29dcbdca4f80ca40cc70a5f2860cf2939c6
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Magnus Holmgren
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <ctype.h>
23 #include <math.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <inttypes.h>
28 #include "strlcpy.h"
29 #include "strcasecmp.h"
30 #include "system.h"
31 #include "metadata.h"
32 #include "debug.h"
33 #include "replaygain.h"
34 #include "fixedpoint.h"
36 #define FP_BITS (12)
37 #define FP_ONE (1 << FP_BITS)
39 void replaygain_itoa(char* buffer, int length, long int_gain)
41 /* int_gain uses Q19.12 format. */
42 int one = abs(int_gain) >> FP_BITS;
43 int cent = ((abs(int_gain) & 0x0fff) * 100 + (FP_ONE/2)) >> FP_BITS;
44 snprintf(buffer, length, "%d.%02d dB", (int_gain<0) ? -one : one, cent);
47 static long fp_atof(const char* s, int precision)
49 long int_part = 0;
50 long int_one = BIT_N(precision);
51 long frac_part = 0;
52 long frac_count = 0;
53 long frac_max = ((precision * 4) + 12) / 13;
54 long frac_max_int = 1;
55 long sign = 1;
56 bool point = false;
58 while ((*s != '\0') && isspace(*s))
60 s++;
63 if (*s == '-')
65 sign = -1;
66 s++;
68 else if (*s == '+')
70 s++;
73 while (*s != '\0')
75 if (*s == '.')
77 if (point)
79 break;
82 point = true;
84 else if (isdigit(*s))
86 if (point)
88 if (frac_count < frac_max)
90 frac_part = frac_part * 10 + (*s - '0');
91 frac_count++;
92 frac_max_int *= 10;
95 else
97 int_part = int_part * 10 + (*s - '0');
100 else
102 break;
105 s++;
108 while (frac_count < frac_max)
110 frac_part *= 10;
111 frac_count++;
112 frac_max_int *= 10;
115 return sign * ((int_part * int_one)
116 + (((int64_t) frac_part * int_one) / frac_max_int));
119 long convert_gain(long gain)
121 /* Don't allow unreasonably low or high gain changes.
122 * Our math code can't handle it properly anyway. :)
124 gain = MAX(gain,-48 * FP_ONE);
125 gain = MIN(gain, 17 * FP_ONE);
127 return fp_factor(gain, FP_BITS) << (24 - FP_BITS);
130 /* Get the sample scale factor in Q19.12 format from a gain value. Returns 0
131 * for no gain.
133 * str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored.
135 static long get_replaygain(const char* str)
137 return fp_atof(str, FP_BITS);
140 /* Get the peak volume in Q7.24 format.
142 * str Peak volume. Full scale is specified as "1.0". Returns 0 for no peak.
144 static long get_replaypeak(const char* str)
146 return fp_atof(str, 24);
149 /* Get a sample scale factor in Q7.24 format from a gain value.
151 * int_gain Gain in dB, multiplied by 100.
153 long get_replaygain_int(long int_gain)
155 return convert_gain(int_gain * FP_ONE / 100);
158 /* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a
159 * valid tag is found, update mp3entry struct accordingly. Existing values
160 * are not overwritten.
162 * key Name of the tag.
163 * value Value of the tag.
164 * entry mp3entry struct to update.
166 void parse_replaygain(const char* key, const char* value,
167 struct mp3entry* entry)
169 if (((strcasecmp(key, "replaygain_track_gain") == 0) ||
170 (strcasecmp(key, "rg_radio") == 0)) &&
171 !entry->track_gain)
173 entry->track_gain = get_replaygain(value);
175 else if (((strcasecmp(key, "replaygain_album_gain") == 0) ||
176 (strcasecmp(key, "rg_audiophile") == 0)) &&
177 !entry->album_gain)
179 entry->album_gain = get_replaygain(value);
181 else if (((strcasecmp(key, "replaygain_track_peak") == 0) ||
182 (strcasecmp(key, "rg_peak") == 0)) &&
183 !entry->track_peak)
185 entry->track_peak = get_replaypeak(value);
187 else if ((strcasecmp(key, "replaygain_album_peak") == 0) &&
188 !entry->album_peak)
190 entry->album_peak = get_replaypeak(value);
194 /* Set ReplayGain values from integers. Existing values are not overwritten.
196 * album If true, set album values, otherwise set track values.
197 * gain Gain value in dB, multiplied by 512. 0 for no gain.
198 * peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no
199 * peak volume.
200 * entry mp3entry struct to update.
202 void parse_replaygain_int(bool album, long gain, long peak,
203 struct mp3entry* entry)
205 gain = gain * FP_ONE / 512;
207 if (album)
209 entry->album_gain = gain;
210 entry->album_peak = peak;
212 else
214 entry->track_gain = gain;
215 entry->track_peak = peak;