Update the discussion of themeing in the manual, and put a note in the wps tags appen...
[kugel-rb.git] / apps / replaygain.c
blob81f1a45ad77fd600863279fcef95959cb282f585
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)
40 static long fp_atof(const char* s, int precision)
42 long int_part = 0;
43 long int_one = BIT_N(precision);
44 long frac_part = 0;
45 long frac_count = 0;
46 long frac_max = ((precision * 4) + 12) / 13;
47 long frac_max_int = 1;
48 long sign = 1;
49 bool point = false;
51 while ((*s != '\0') && isspace(*s))
53 s++;
56 if (*s == '-')
58 sign = -1;
59 s++;
61 else if (*s == '+')
63 s++;
66 while (*s != '\0')
68 if (*s == '.')
70 if (point)
72 break;
75 point = true;
77 else if (isdigit(*s))
79 if (point)
81 if (frac_count < frac_max)
83 frac_part = frac_part * 10 + (*s - '0');
84 frac_count++;
85 frac_max_int *= 10;
88 else
90 int_part = int_part * 10 + (*s - '0');
93 else
95 break;
98 s++;
101 while (frac_count < frac_max)
103 frac_part *= 10;
104 frac_count++;
105 frac_max_int *= 10;
108 return sign * ((int_part * int_one)
109 + (((int64_t) frac_part * int_one) / frac_max_int));
112 static long convert_gain(long gain)
114 /* Don't allow unreasonably low or high gain changes.
115 * Our math code can't handle it properly anyway. :)
117 if (gain < (-48 * FP_ONE))
119 gain = -48 * FP_ONE;
122 if (gain > (17 * FP_ONE))
124 gain = 17 * FP_ONE;
127 gain = fp_factor(gain, FP_BITS) << (24 - FP_BITS);
129 return gain;
132 /* Get the sample scale factor in Q7.24 format from a gain value. Returns 0
133 * for no gain.
135 * str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored.
137 static long get_replaygain(const char* str)
139 long gain = 0;
141 if (str)
143 gain = fp_atof(str, FP_BITS);
144 gain = convert_gain(gain);
147 return gain;
150 /* Get the peak volume in Q7.24 format.
152 * str Peak volume. Full scale is specified as "1.0". Returns 0 for no peak.
154 static long get_replaypeak(const char* str)
156 long peak = 0;
158 if (str)
160 peak = fp_atof(str, 24);
163 return peak;
166 /* Get a sample scale factor in Q7.24 format from a gain value.
168 * int_gain Gain in dB, multiplied by 100.
170 long get_replaygain_int(long int_gain)
172 return convert_gain(int_gain * FP_ONE / 100);
175 /* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a
176 * valid tag is found, update mp3entry struct accordingly. Existing values
177 * are not overwritten. Returns number of bytes written to buffer.
179 * key Name of the tag.
180 * value Value of the tag.
181 * entry mp3entry struct to update.
182 * buffer Where to store the text for gain values (for later display).
183 * length Bytes left in buffer.
185 long parse_replaygain(const char* key, const char* value,
186 struct mp3entry* entry, char* buffer, int length)
188 char **p = NULL;
190 if (((strcasecmp(key, "replaygain_track_gain") == 0)
191 || (strcasecmp(key, "rg_radio") == 0)) && !entry->track_gain)
193 entry->track_gain = get_replaygain(value);
194 p = &(entry->track_gain_string);
196 else if (((strcasecmp(key, "replaygain_album_gain") == 0)
197 || (strcasecmp(key, "rg_audiophile") == 0)) && !entry->album_gain)
199 entry->album_gain = get_replaygain(value);
200 p = &(entry->album_gain_string);
202 else if (((strcasecmp(key, "replaygain_track_peak") == 0)
203 || (strcasecmp(key, "rg_peak") == 0)) && !entry->track_peak)
205 entry->track_peak = get_replaypeak(value);
207 else if ((strcasecmp(key, "replaygain_album_peak") == 0)
208 && !entry->album_peak)
210 entry->album_peak = get_replaypeak(value);
213 if (p)
215 int len = strlen(value);
217 len = MIN(len, length - 1);
219 /* A few characters just isn't interesting... */
220 if (len > 1)
222 strlcpy(buffer, value, len + 1);
223 *p = buffer;
224 return len + 1;
228 return 0;
231 /* Set ReplayGain values from integers. Existing values are not overwritten.
232 * Returns number of bytes written to buffer.
234 * album If true, set album values, otherwise set track values.
235 * gain Gain value in dB, multiplied by 512. 0 for no gain.
236 * peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no
237 * peak volume.
238 * buffer Where to store the text for gain values (for later display).
239 * length Bytes left in buffer.
241 long parse_replaygain_int(bool album, long gain, long peak,
242 struct mp3entry* entry, char* buffer, int length)
244 long len = 0;
246 if (buffer != NULL)
248 len = snprintf(buffer, length, "%ld.%02d dB", gain / 512,
249 ((abs(gain) & 0x01ff) * 100 + 256) / 512);
250 len++;
253 if (gain != 0)
255 gain = convert_gain(gain * FP_ONE / 512);
258 if (album)
260 entry->album_gain = gain;
261 entry->album_gain_string = buffer;
263 if (peak)
265 entry->album_peak = peak;
268 else
270 entry->track_gain = gain;
271 entry->track_gain_string = buffer;
273 if (peak)
275 entry->track_peak = peak;
279 return len;