Fix a dircache NULL-pointer dereference.
[kugel-rb.git] / apps / replaygain.c
blob6ece1104b43fca14f095db33efeb11cb5a323aa6
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 <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 "metadata.h"
31 #include "debug.h"
32 #include "replaygain.h"
33 #include "fixedpoint.h"
35 #define FP_BITS (12)
36 #define FP_ONE (1 << FP_BITS)
39 static long fp_atof(const char* s, int precision)
41 long int_part = 0;
42 long int_one = BIT_N(precision);
43 long frac_part = 0;
44 long frac_count = 0;
45 long frac_max = ((precision * 4) + 12) / 13;
46 long frac_max_int = 1;
47 long sign = 1;
48 bool point = false;
50 while ((*s != '\0') && isspace(*s))
52 s++;
55 if (*s == '-')
57 sign = -1;
58 s++;
60 else if (*s == '+')
62 s++;
65 while (*s != '\0')
67 if (*s == '.')
69 if (point)
71 break;
74 point = true;
76 else if (isdigit(*s))
78 if (point)
80 if (frac_count < frac_max)
82 frac_part = frac_part * 10 + (*s - '0');
83 frac_count++;
84 frac_max_int *= 10;
87 else
89 int_part = int_part * 10 + (*s - '0');
92 else
94 break;
97 s++;
100 while (frac_count < frac_max)
102 frac_part *= 10;
103 frac_count++;
104 frac_max_int *= 10;
107 return sign * ((int_part * int_one)
108 + (((int64_t) frac_part * int_one) / frac_max_int));
111 static long convert_gain(long gain)
113 /* Don't allow unreasonably low or high gain changes.
114 * Our math code can't handle it properly anyway. :)
116 if (gain < (-48 * FP_ONE))
118 gain = -48 * FP_ONE;
121 if (gain > (17 * FP_ONE))
123 gain = 17 * FP_ONE;
126 gain = fp_factor(gain, FP_BITS) << (24 - FP_BITS);
128 return gain;
131 /* Get the sample scale factor in Q7.24 format from a gain value. Returns 0
132 * for no gain.
134 * str Gain in dB as a string. E.g., "-3.45 dB"; the "dB" part is ignored.
136 static long get_replaygain(const char* str)
138 long gain = 0;
140 if (str)
142 gain = fp_atof(str, FP_BITS);
143 gain = convert_gain(gain);
146 return gain;
149 /* Get the peak volume in Q7.24 format.
151 * str Peak volume. Full scale is specified as "1.0". Returns 0 for no peak.
153 static long get_replaypeak(const char* str)
155 long peak = 0;
157 if (str)
159 peak = fp_atof(str, 24);
162 return peak;
165 /* Get a sample scale factor in Q7.24 format from a gain value.
167 * int_gain Gain in dB, multiplied by 100.
169 long get_replaygain_int(long int_gain)
171 return convert_gain(int_gain * FP_ONE / 100);
174 /* Parse a ReplayGain tag conforming to the "VorbisGain standard". If a
175 * valid tag is found, update mp3entry struct accordingly. Existing values
176 * are not overwritten. Returns number of bytes written to buffer.
178 * key Name of the tag.
179 * value Value of the tag.
180 * entry mp3entry struct to update.
181 * buffer Where to store the text for gain values (for later display).
182 * length Bytes left in buffer.
184 long parse_replaygain(const char* key, const char* value,
185 struct mp3entry* entry, char* buffer, int length)
187 char **p = NULL;
189 if (((strcasecmp(key, "replaygain_track_gain") == 0)
190 || (strcasecmp(key, "rg_radio") == 0)) && !entry->track_gain)
192 entry->track_gain = get_replaygain(value);
193 p = &(entry->track_gain_string);
195 else if (((strcasecmp(key, "replaygain_album_gain") == 0)
196 || (strcasecmp(key, "rg_audiophile") == 0)) && !entry->album_gain)
198 entry->album_gain = get_replaygain(value);
199 p = &(entry->album_gain_string);
201 else if (((strcasecmp(key, "replaygain_track_peak") == 0)
202 || (strcasecmp(key, "rg_peak") == 0)) && !entry->track_peak)
204 entry->track_peak = get_replaypeak(value);
206 else if ((strcasecmp(key, "replaygain_album_peak") == 0)
207 && !entry->album_peak)
209 entry->album_peak = get_replaypeak(value);
212 if (p)
214 int len = strlen(value);
216 len = MIN(len, length - 1);
218 /* A few characters just isn't interesting... */
219 if (len > 1)
221 strlcpy(buffer, value, len + 1);
222 *p = buffer;
223 return len + 1;
227 return 0;
230 /* Set ReplayGain values from integers. Existing values are not overwritten.
231 * Returns number of bytes written to buffer.
233 * album If true, set album values, otherwise set track values.
234 * gain Gain value in dB, multiplied by 512. 0 for no gain.
235 * peak Peak volume in Q7.24 format, where 1.0 is full scale. 0 for no
236 * peak volume.
237 * buffer Where to store the text for gain values (for later display).
238 * length Bytes left in buffer.
240 long parse_replaygain_int(bool album, long gain, long peak,
241 struct mp3entry* entry, char* buffer, int length)
243 long len = 0;
245 if (buffer != NULL)
247 len = snprintf(buffer, length, "%d.%02d dB", gain / 512,
248 ((abs(gain) & 0x01ff) * 100 + 256) / 512);
249 len++;
252 if (gain != 0)
254 gain = convert_gain(gain * FP_ONE / 512);
257 if (album)
259 entry->album_gain = gain;
260 entry->album_gain_string = buffer;
262 if (peak)
264 entry->album_peak = peak;
267 else
269 entry->track_gain = gain;
270 entry->track_gain_string = buffer;
272 if (peak)
274 entry->track_peak = peak;
278 return len;