1*600f14f4SXin Li /* grabbag - Convenience lib for various routines common to several tools
2*600f14f4SXin Li * Copyright (C) 2002-2009 Josh Coalson
3*600f14f4SXin Li * Copyright (C) 2011-2023 Xiph.Org Foundation
4*600f14f4SXin Li *
5*600f14f4SXin Li * This library is free software; you can redistribute it and/or
6*600f14f4SXin Li * modify it under the terms of the GNU Lesser General Public
7*600f14f4SXin Li * License as published by the Free Software Foundation; either
8*600f14f4SXin Li * version 2.1 of the License, or (at your option) any later version.
9*600f14f4SXin Li *
10*600f14f4SXin Li * This library is distributed in the hope that it will be useful,
11*600f14f4SXin Li * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*600f14f4SXin Li * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13*600f14f4SXin Li * Lesser General Public License for more details.
14*600f14f4SXin Li *
15*600f14f4SXin Li * You should have received a copy of the GNU Lesser General Public
16*600f14f4SXin Li * License along with this library; if not, write to the Free Software
17*600f14f4SXin Li * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*600f14f4SXin Li */
19*600f14f4SXin Li
20*600f14f4SXin Li #ifdef HAVE_CONFIG_H
21*600f14f4SXin Li # include <config.h>
22*600f14f4SXin Li #endif
23*600f14f4SXin Li
24*600f14f4SXin Li #include <locale.h>
25*600f14f4SXin Li #include <math.h>
26*600f14f4SXin Li #include <stdio.h>
27*600f14f4SXin Li #include <stdlib.h>
28*600f14f4SXin Li #include <string.h>
29*600f14f4SXin Li #if defined _MSC_VER || defined __MINGW32__
30*600f14f4SXin Li #include <io.h> /* for chmod() */
31*600f14f4SXin Li #endif
32*600f14f4SXin Li #include <sys/stat.h> /* for stat(), maybe chmod() */
33*600f14f4SXin Li
34*600f14f4SXin Li #include "FLAC/assert.h"
35*600f14f4SXin Li #include "FLAC/metadata.h"
36*600f14f4SXin Li #include "FLAC/stream_decoder.h"
37*600f14f4SXin Li #include "share/grabbag.h"
38*600f14f4SXin Li #include "share/replaygain_analysis.h"
39*600f14f4SXin Li #include "share/safe_str.h"
40*600f14f4SXin Li
41*600f14f4SXin Li #ifdef local_min
42*600f14f4SXin Li #undef local_min
43*600f14f4SXin Li #endif
44*600f14f4SXin Li #define local_min(a,b) ((a)<(b)?(a):(b))
45*600f14f4SXin Li
46*600f14f4SXin Li #ifdef local_max
47*600f14f4SXin Li #undef local_max
48*600f14f4SXin Li #endif
49*600f14f4SXin Li #define local_max(a,b) ((a)>(b)?(a):(b))
50*600f14f4SXin Li
51*600f14f4SXin Li #ifdef abs32
52*600f14f4SXin Li #undef abs32
53*600f14f4SXin Li #endif
54*600f14f4SXin Li #define abs32(a) (((a)==INT32_MIN)?INT32_MAX:abs(a))
55*600f14f4SXin Li
56*600f14f4SXin Li static const char *reference_format_ = "%s=%2.1f dB";
57*600f14f4SXin Li static const char *gain_format_ = "%s=%+2.2f dB";
58*600f14f4SXin Li static const char *peak_format_ = "%s=%1.8f";
59*600f14f4SXin Li
60*600f14f4SXin Li static double album_peak_, title_peak_;
61*600f14f4SXin Li
62*600f14f4SXin Li const uint32_t GRABBAG__REPLAYGAIN_MAX_TAG_SPACE_REQUIRED = 190;
63*600f14f4SXin Li /*
64*600f14f4SXin Li FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 29 + 1 + 8 +
65*600f14f4SXin Li FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 +
66*600f14f4SXin Li FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12 +
67*600f14f4SXin Li FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 10 +
68*600f14f4SXin Li FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN/8 + 21 + 1 + 12
69*600f14f4SXin Li */
70*600f14f4SXin Li
71*600f14f4SXin Li const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS = (const FLAC__byte * const)"REPLAYGAIN_REFERENCE_LOUDNESS";
72*600f14f4SXin Li const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN = (const FLAC__byte * const)"REPLAYGAIN_TRACK_GAIN";
73*600f14f4SXin Li const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK = (const FLAC__byte * const)"REPLAYGAIN_TRACK_PEAK";
74*600f14f4SXin Li const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN = (const FLAC__byte * const)"REPLAYGAIN_ALBUM_GAIN";
75*600f14f4SXin Li const FLAC__byte * const GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK = (const FLAC__byte * const)"REPLAYGAIN_ALBUM_PEAK";
76*600f14f4SXin Li
77*600f14f4SXin Li
get_file_stats_(const char * filename,struct flac_stat_s * stats)78*600f14f4SXin Li static FLAC__bool get_file_stats_(const char *filename, struct flac_stat_s *stats)
79*600f14f4SXin Li {
80*600f14f4SXin Li FLAC__ASSERT(0 != filename);
81*600f14f4SXin Li FLAC__ASSERT(0 != stats);
82*600f14f4SXin Li return (0 == flac_stat(filename, stats));
83*600f14f4SXin Li }
84*600f14f4SXin Li
set_file_stats_(const char * filename,struct flac_stat_s * stats)85*600f14f4SXin Li static void set_file_stats_(const char *filename, struct flac_stat_s *stats)
86*600f14f4SXin Li {
87*600f14f4SXin Li FLAC__ASSERT(0 != filename);
88*600f14f4SXin Li FLAC__ASSERT(0 != stats);
89*600f14f4SXin Li
90*600f14f4SXin Li (void)flac_chmod(filename, stats->st_mode);
91*600f14f4SXin Li }
92*600f14f4SXin Li
append_tag_(FLAC__StreamMetadata * block,const char * format,const FLAC__byte * name,float value)93*600f14f4SXin Li static FLAC__bool append_tag_(FLAC__StreamMetadata *block, const char *format, const FLAC__byte *name, float value)
94*600f14f4SXin Li {
95*600f14f4SXin Li char buffer[256];
96*600f14f4SXin Li char *saved_locale;
97*600f14f4SXin Li FLAC__StreamMetadata_VorbisComment_Entry entry;
98*600f14f4SXin Li
99*600f14f4SXin Li FLAC__ASSERT(0 != block);
100*600f14f4SXin Li FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
101*600f14f4SXin Li FLAC__ASSERT(0 != format);
102*600f14f4SXin Li FLAC__ASSERT(0 != name);
103*600f14f4SXin Li
104*600f14f4SXin Li buffer[sizeof(buffer)-1] = '\0';
105*600f14f4SXin Li /*
106*600f14f4SXin Li * We need to save the old locale and switch to "C" because the locale
107*600f14f4SXin Li * influences the formatting of %f and we want it a certain way.
108*600f14f4SXin Li */
109*600f14f4SXin Li saved_locale = strdup(setlocale(LC_ALL, 0));
110*600f14f4SXin Li if (0 == saved_locale)
111*600f14f4SXin Li return false;
112*600f14f4SXin Li setlocale(LC_ALL, "C");
113*600f14f4SXin Li flac_snprintf(buffer, sizeof(buffer), format, name, value);
114*600f14f4SXin Li setlocale(LC_ALL, saved_locale);
115*600f14f4SXin Li free(saved_locale);
116*600f14f4SXin Li
117*600f14f4SXin Li entry.entry = (FLAC__byte *)buffer;
118*600f14f4SXin Li entry.length = strlen(buffer);
119*600f14f4SXin Li
120*600f14f4SXin Li return FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true);
121*600f14f4SXin Li }
122*600f14f4SXin Li
grabbag__replaygain_is_valid_sample_frequency(uint32_t sample_frequency)123*600f14f4SXin Li FLAC__bool grabbag__replaygain_is_valid_sample_frequency(uint32_t sample_frequency)
124*600f14f4SXin Li {
125*600f14f4SXin Li return ValidGainFrequency( sample_frequency );
126*600f14f4SXin Li }
127*600f14f4SXin Li
grabbag__replaygain_init(uint32_t sample_frequency)128*600f14f4SXin Li FLAC__bool grabbag__replaygain_init(uint32_t sample_frequency)
129*600f14f4SXin Li {
130*600f14f4SXin Li title_peak_ = album_peak_ = 0.0;
131*600f14f4SXin Li return InitGainAnalysis((long)sample_frequency) == INIT_GAIN_ANALYSIS_OK;
132*600f14f4SXin Li }
133*600f14f4SXin Li
grabbag__replaygain_analyze(const FLAC__int32 * const input[],FLAC__bool is_stereo,uint32_t bps,uint32_t samples)134*600f14f4SXin Li FLAC__bool grabbag__replaygain_analyze(const FLAC__int32 * const input[], FLAC__bool is_stereo, uint32_t bps, uint32_t samples)
135*600f14f4SXin Li {
136*600f14f4SXin Li /* using a small buffer improves data locality; we'd like it to fit easily in the dcache */
137*600f14f4SXin Li static flac_float_t lbuffer[2048], rbuffer[2048];
138*600f14f4SXin Li static const uint32_t nbuffer = sizeof(lbuffer) / sizeof(lbuffer[0]);
139*600f14f4SXin Li FLAC__int32 block_peak = 0, s;
140*600f14f4SXin Li uint32_t i, j;
141*600f14f4SXin Li
142*600f14f4SXin Li FLAC__ASSERT(bps >= FLAC__MIN_BITS_PER_SAMPLE && bps <= FLAC__MAX_BITS_PER_SAMPLE);
143*600f14f4SXin Li FLAC__ASSERT(FLAC__MIN_BITS_PER_SAMPLE == 4 && FLAC__MAX_BITS_PER_SAMPLE == 32);
144*600f14f4SXin Li
145*600f14f4SXin Li if(bps == 16) {
146*600f14f4SXin Li if(is_stereo) {
147*600f14f4SXin Li j = 0;
148*600f14f4SXin Li while(samples > 0) {
149*600f14f4SXin Li const uint32_t n = local_min(samples, nbuffer);
150*600f14f4SXin Li for(i = 0; i < n; i++, j++) {
151*600f14f4SXin Li s = input[0][j];
152*600f14f4SXin Li lbuffer[i] = (flac_float_t)s;
153*600f14f4SXin Li s = abs(s);
154*600f14f4SXin Li block_peak = local_max(block_peak, s);
155*600f14f4SXin Li
156*600f14f4SXin Li s = input[1][j];
157*600f14f4SXin Li rbuffer[i] = (flac_float_t)s;
158*600f14f4SXin Li s = abs(s);
159*600f14f4SXin Li block_peak = local_max(block_peak, s);
160*600f14f4SXin Li }
161*600f14f4SXin Li samples -= n;
162*600f14f4SXin Li if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK)
163*600f14f4SXin Li return false;
164*600f14f4SXin Li }
165*600f14f4SXin Li }
166*600f14f4SXin Li else {
167*600f14f4SXin Li j = 0;
168*600f14f4SXin Li while(samples > 0) {
169*600f14f4SXin Li const uint32_t n = local_min(samples, nbuffer);
170*600f14f4SXin Li for(i = 0; i < n; i++, j++) {
171*600f14f4SXin Li s = input[0][j];
172*600f14f4SXin Li lbuffer[i] = (flac_float_t)s;
173*600f14f4SXin Li s = abs(s);
174*600f14f4SXin Li block_peak = local_max(block_peak, s);
175*600f14f4SXin Li }
176*600f14f4SXin Li samples -= n;
177*600f14f4SXin Li if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK)
178*600f14f4SXin Li return false;
179*600f14f4SXin Li }
180*600f14f4SXin Li }
181*600f14f4SXin Li }
182*600f14f4SXin Li else {
183*600f14f4SXin Li const double scale = (
184*600f14f4SXin Li (bps > 16)?
185*600f14f4SXin Li (double)1. / (double)(1u << (bps - 16)) :
186*600f14f4SXin Li (double)(1u << (16 - bps))
187*600f14f4SXin Li );
188*600f14f4SXin Li
189*600f14f4SXin Li if(is_stereo) {
190*600f14f4SXin Li j = 0;
191*600f14f4SXin Li while(samples > 0) {
192*600f14f4SXin Li const uint32_t n = local_min(samples, nbuffer);
193*600f14f4SXin Li for(i = 0; i < n; i++, j++) {
194*600f14f4SXin Li s = input[0][j];
195*600f14f4SXin Li lbuffer[i] = (flac_float_t)(scale * (double)s);
196*600f14f4SXin Li s = abs32(s);
197*600f14f4SXin Li block_peak = local_max(block_peak, s);
198*600f14f4SXin Li
199*600f14f4SXin Li s = input[1][j];
200*600f14f4SXin Li rbuffer[i] = (flac_float_t)(scale * (double)s);
201*600f14f4SXin Li s = abs32(s);
202*600f14f4SXin Li block_peak = local_max(block_peak, s);
203*600f14f4SXin Li }
204*600f14f4SXin Li samples -= n;
205*600f14f4SXin Li if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK)
206*600f14f4SXin Li return false;
207*600f14f4SXin Li }
208*600f14f4SXin Li }
209*600f14f4SXin Li else {
210*600f14f4SXin Li j = 0;
211*600f14f4SXin Li while(samples > 0) {
212*600f14f4SXin Li const uint32_t n = local_min(samples, nbuffer);
213*600f14f4SXin Li for(i = 0; i < n; i++, j++) {
214*600f14f4SXin Li s = input[0][j];
215*600f14f4SXin Li lbuffer[i] = (flac_float_t)(scale * (double)s);
216*600f14f4SXin Li s = abs32(s);
217*600f14f4SXin Li block_peak = local_max(block_peak, s);
218*600f14f4SXin Li }
219*600f14f4SXin Li samples -= n;
220*600f14f4SXin Li if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK)
221*600f14f4SXin Li return false;
222*600f14f4SXin Li }
223*600f14f4SXin Li }
224*600f14f4SXin Li }
225*600f14f4SXin Li
226*600f14f4SXin Li {
227*600f14f4SXin Li const double peak_scale = (double)(1u << (bps - 1));
228*600f14f4SXin Li double peak = (double)block_peak / peak_scale;
229*600f14f4SXin Li if(peak > title_peak_)
230*600f14f4SXin Li title_peak_ = peak;
231*600f14f4SXin Li if(peak > album_peak_)
232*600f14f4SXin Li album_peak_ = peak;
233*600f14f4SXin Li }
234*600f14f4SXin Li
235*600f14f4SXin Li return true;
236*600f14f4SXin Li }
237*600f14f4SXin Li
grabbag__replaygain_get_album(float * gain,float * peak)238*600f14f4SXin Li void grabbag__replaygain_get_album(float *gain, float *peak)
239*600f14f4SXin Li {
240*600f14f4SXin Li *gain = (float)GetAlbumGain();
241*600f14f4SXin Li *peak = (float)album_peak_;
242*600f14f4SXin Li album_peak_ = 0.0;
243*600f14f4SXin Li }
244*600f14f4SXin Li
grabbag__replaygain_get_title(float * gain,float * peak)245*600f14f4SXin Li void grabbag__replaygain_get_title(float *gain, float *peak)
246*600f14f4SXin Li {
247*600f14f4SXin Li *gain = (float)GetTitleGain();
248*600f14f4SXin Li *peak = (float)title_peak_;
249*600f14f4SXin Li title_peak_ = 0.0;
250*600f14f4SXin Li }
251*600f14f4SXin Li
252*600f14f4SXin Li
253*600f14f4SXin Li typedef struct {
254*600f14f4SXin Li uint32_t channels;
255*600f14f4SXin Li uint32_t bits_per_sample;
256*600f14f4SXin Li uint32_t sample_rate;
257*600f14f4SXin Li FLAC__bool error;
258*600f14f4SXin Li } DecoderInstance;
259*600f14f4SXin Li
write_callback_(const FLAC__StreamDecoder * decoder,const FLAC__Frame * frame,const FLAC__int32 * const buffer[],void * client_data)260*600f14f4SXin Li static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
261*600f14f4SXin Li {
262*600f14f4SXin Li DecoderInstance *instance = (DecoderInstance*)client_data;
263*600f14f4SXin Li const uint32_t bits_per_sample = frame->header.bits_per_sample;
264*600f14f4SXin Li const uint32_t channels = frame->header.channels;
265*600f14f4SXin Li const uint32_t sample_rate = frame->header.sample_rate;
266*600f14f4SXin Li const uint32_t samples = frame->header.blocksize;
267*600f14f4SXin Li
268*600f14f4SXin Li (void)decoder;
269*600f14f4SXin Li
270*600f14f4SXin Li if(
271*600f14f4SXin Li !instance->error &&
272*600f14f4SXin Li (channels == 2 || channels == 1) &&
273*600f14f4SXin Li bits_per_sample == instance->bits_per_sample &&
274*600f14f4SXin Li channels == instance->channels &&
275*600f14f4SXin Li sample_rate == instance->sample_rate
276*600f14f4SXin Li ) {
277*600f14f4SXin Li instance->error = !grabbag__replaygain_analyze(buffer, channels==2, bits_per_sample, samples);
278*600f14f4SXin Li }
279*600f14f4SXin Li else {
280*600f14f4SXin Li instance->error = true;
281*600f14f4SXin Li }
282*600f14f4SXin Li
283*600f14f4SXin Li if(!instance->error)
284*600f14f4SXin Li return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
285*600f14f4SXin Li else
286*600f14f4SXin Li return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
287*600f14f4SXin Li }
288*600f14f4SXin Li
metadata_callback_(const FLAC__StreamDecoder * decoder,const FLAC__StreamMetadata * metadata,void * client_data)289*600f14f4SXin Li static void metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
290*600f14f4SXin Li {
291*600f14f4SXin Li DecoderInstance *instance = (DecoderInstance*)client_data;
292*600f14f4SXin Li
293*600f14f4SXin Li (void)decoder;
294*600f14f4SXin Li
295*600f14f4SXin Li if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
296*600f14f4SXin Li instance->bits_per_sample = metadata->data.stream_info.bits_per_sample;
297*600f14f4SXin Li instance->channels = metadata->data.stream_info.channels;
298*600f14f4SXin Li instance->sample_rate = metadata->data.stream_info.sample_rate;
299*600f14f4SXin Li
300*600f14f4SXin Li if(instance->channels != 1 && instance->channels != 2) {
301*600f14f4SXin Li instance->error = true;
302*600f14f4SXin Li return;
303*600f14f4SXin Li }
304*600f14f4SXin Li
305*600f14f4SXin Li if(!grabbag__replaygain_is_valid_sample_frequency(instance->sample_rate)) {
306*600f14f4SXin Li instance->error = true;
307*600f14f4SXin Li return;
308*600f14f4SXin Li }
309*600f14f4SXin Li }
310*600f14f4SXin Li }
311*600f14f4SXin Li
error_callback_(const FLAC__StreamDecoder * decoder,FLAC__StreamDecoderErrorStatus status,void * client_data)312*600f14f4SXin Li static void error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
313*600f14f4SXin Li {
314*600f14f4SXin Li DecoderInstance *instance = (DecoderInstance*)client_data;
315*600f14f4SXin Li
316*600f14f4SXin Li (void)decoder, (void)status;
317*600f14f4SXin Li
318*600f14f4SXin Li instance->error = true;
319*600f14f4SXin Li }
320*600f14f4SXin Li
grabbag__replaygain_analyze_file(const char * filename,float * title_gain,float * title_peak)321*600f14f4SXin Li const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak)
322*600f14f4SXin Li {
323*600f14f4SXin Li DecoderInstance instance;
324*600f14f4SXin Li FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new();
325*600f14f4SXin Li
326*600f14f4SXin Li if(0 == decoder)
327*600f14f4SXin Li return "memory allocation error";
328*600f14f4SXin Li
329*600f14f4SXin Li instance.error = false;
330*600f14f4SXin Li
331*600f14f4SXin Li /* It does these three by default but lets be explicit: */
332*600f14f4SXin Li FLAC__stream_decoder_set_md5_checking(decoder, false);
333*600f14f4SXin Li FLAC__stream_decoder_set_metadata_ignore_all(decoder);
334*600f14f4SXin Li FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
335*600f14f4SXin Li
336*600f14f4SXin Li if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &instance) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
337*600f14f4SXin Li FLAC__stream_decoder_delete(decoder);
338*600f14f4SXin Li return "initializing decoder";
339*600f14f4SXin Li }
340*600f14f4SXin Li
341*600f14f4SXin Li if(!FLAC__stream_decoder_process_until_end_of_stream(decoder) || instance.error) {
342*600f14f4SXin Li FLAC__stream_decoder_delete(decoder);
343*600f14f4SXin Li return "decoding file";
344*600f14f4SXin Li }
345*600f14f4SXin Li
346*600f14f4SXin Li FLAC__stream_decoder_delete(decoder);
347*600f14f4SXin Li
348*600f14f4SXin Li grabbag__replaygain_get_title(title_gain, title_peak);
349*600f14f4SXin Li
350*600f14f4SXin Li return 0;
351*600f14f4SXin Li }
352*600f14f4SXin Li
grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata * block,float album_gain,float album_peak,float title_gain,float title_peak)353*600f14f4SXin Li const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak)
354*600f14f4SXin Li {
355*600f14f4SXin Li const char *error;
356*600f14f4SXin Li
357*600f14f4SXin Li if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_reference(block)))
358*600f14f4SXin Li return error;
359*600f14f4SXin Li
360*600f14f4SXin Li if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak)))
361*600f14f4SXin Li return error;
362*600f14f4SXin Li
363*600f14f4SXin Li if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak)))
364*600f14f4SXin Li return error;
365*600f14f4SXin Li
366*600f14f4SXin Li return 0;
367*600f14f4SXin Li }
368*600f14f4SXin Li
grabbag__replaygain_store_to_vorbiscomment_reference(FLAC__StreamMetadata * block)369*600f14f4SXin Li const char *grabbag__replaygain_store_to_vorbiscomment_reference(FLAC__StreamMetadata *block)
370*600f14f4SXin Li {
371*600f14f4SXin Li FLAC__ASSERT(0 != block);
372*600f14f4SXin Li FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
373*600f14f4SXin Li
374*600f14f4SXin Li if(FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS) < 0)
375*600f14f4SXin Li return "memory allocation error";
376*600f14f4SXin Li
377*600f14f4SXin Li if(!append_tag_(block, reference_format_, GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS, ReplayGainReferenceLoudness))
378*600f14f4SXin Li return "memory allocation error";
379*600f14f4SXin Li
380*600f14f4SXin Li return 0;
381*600f14f4SXin Li }
382*600f14f4SXin Li
grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata * block,float album_gain,float album_peak)383*600f14f4SXin Li const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak)
384*600f14f4SXin Li {
385*600f14f4SXin Li FLAC__ASSERT(0 != block);
386*600f14f4SXin Li FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
387*600f14f4SXin Li
388*600f14f4SXin Li if(
389*600f14f4SXin Li FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN) < 0 ||
390*600f14f4SXin Li FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK) < 0
391*600f14f4SXin Li )
392*600f14f4SXin Li return "memory allocation error";
393*600f14f4SXin Li
394*600f14f4SXin Li if(
395*600f14f4SXin Li !append_tag_(block, gain_format_, GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN, album_gain) ||
396*600f14f4SXin Li !append_tag_(block, peak_format_, GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK, album_peak)
397*600f14f4SXin Li )
398*600f14f4SXin Li return "memory allocation error";
399*600f14f4SXin Li
400*600f14f4SXin Li return 0;
401*600f14f4SXin Li }
402*600f14f4SXin Li
grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata * block,float title_gain,float title_peak)403*600f14f4SXin Li const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak)
404*600f14f4SXin Li {
405*600f14f4SXin Li FLAC__ASSERT(0 != block);
406*600f14f4SXin Li FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
407*600f14f4SXin Li
408*600f14f4SXin Li if(
409*600f14f4SXin Li FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN) < 0 ||
410*600f14f4SXin Li FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK) < 0
411*600f14f4SXin Li )
412*600f14f4SXin Li return "memory allocation error";
413*600f14f4SXin Li
414*600f14f4SXin Li if(
415*600f14f4SXin Li !append_tag_(block, gain_format_, GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN, title_gain) ||
416*600f14f4SXin Li !append_tag_(block, peak_format_, GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK, title_peak)
417*600f14f4SXin Li )
418*600f14f4SXin Li return "memory allocation error";
419*600f14f4SXin Li
420*600f14f4SXin Li return 0;
421*600f14f4SXin Li }
422*600f14f4SXin Li
store_to_file_pre_(const char * filename,FLAC__Metadata_Chain ** chain,FLAC__StreamMetadata ** block)423*600f14f4SXin Li static const char *store_to_file_pre_(const char *filename, FLAC__Metadata_Chain **chain, FLAC__StreamMetadata **block)
424*600f14f4SXin Li {
425*600f14f4SXin Li FLAC__Metadata_Iterator *iterator;
426*600f14f4SXin Li const char *error;
427*600f14f4SXin Li FLAC__bool found_vc_block = false;
428*600f14f4SXin Li
429*600f14f4SXin Li if(0 == (*chain = FLAC__metadata_chain_new()))
430*600f14f4SXin Li return "memory allocation error";
431*600f14f4SXin Li
432*600f14f4SXin Li if(!FLAC__metadata_chain_read(*chain, filename)) {
433*600f14f4SXin Li error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)];
434*600f14f4SXin Li FLAC__metadata_chain_delete(*chain);
435*600f14f4SXin Li return error;
436*600f14f4SXin Li }
437*600f14f4SXin Li
438*600f14f4SXin Li if(0 == (iterator = FLAC__metadata_iterator_new())) {
439*600f14f4SXin Li FLAC__metadata_chain_delete(*chain);
440*600f14f4SXin Li return "memory allocation error";
441*600f14f4SXin Li }
442*600f14f4SXin Li
443*600f14f4SXin Li FLAC__metadata_iterator_init(iterator, *chain);
444*600f14f4SXin Li
445*600f14f4SXin Li do {
446*600f14f4SXin Li *block = FLAC__metadata_iterator_get_block(iterator);
447*600f14f4SXin Li if((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
448*600f14f4SXin Li found_vc_block = true;
449*600f14f4SXin Li } while(!found_vc_block && FLAC__metadata_iterator_next(iterator));
450*600f14f4SXin Li
451*600f14f4SXin Li if(!found_vc_block) {
452*600f14f4SXin Li /* create a new block */
453*600f14f4SXin Li *block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
454*600f14f4SXin Li if(0 == *block) {
455*600f14f4SXin Li FLAC__metadata_chain_delete(*chain);
456*600f14f4SXin Li FLAC__metadata_iterator_delete(iterator);
457*600f14f4SXin Li return "memory allocation error";
458*600f14f4SXin Li }
459*600f14f4SXin Li while(FLAC__metadata_iterator_next(iterator))
460*600f14f4SXin Li ;
461*600f14f4SXin Li if(!FLAC__metadata_iterator_insert_block_after(iterator, *block)) {
462*600f14f4SXin Li error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)];
463*600f14f4SXin Li FLAC__metadata_chain_delete(*chain);
464*600f14f4SXin Li FLAC__metadata_iterator_delete(iterator);
465*600f14f4SXin Li return error;
466*600f14f4SXin Li }
467*600f14f4SXin Li /* iterator is left pointing to new block */
468*600f14f4SXin Li FLAC__ASSERT(FLAC__metadata_iterator_get_block(iterator) == *block);
469*600f14f4SXin Li }
470*600f14f4SXin Li
471*600f14f4SXin Li FLAC__metadata_iterator_delete(iterator);
472*600f14f4SXin Li
473*600f14f4SXin Li FLAC__ASSERT(0 != *block);
474*600f14f4SXin Li FLAC__ASSERT((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
475*600f14f4SXin Li
476*600f14f4SXin Li return 0;
477*600f14f4SXin Li }
478*600f14f4SXin Li
store_to_file_post_(const char * filename,FLAC__Metadata_Chain * chain,FLAC__bool preserve_modtime)479*600f14f4SXin Li static const char *store_to_file_post_(const char *filename, FLAC__Metadata_Chain *chain, FLAC__bool preserve_modtime)
480*600f14f4SXin Li {
481*600f14f4SXin Li struct flac_stat_s stats;
482*600f14f4SXin Li const FLAC__bool have_stats = get_file_stats_(filename, &stats);
483*600f14f4SXin Li
484*600f14f4SXin Li (void)grabbag__file_change_stats(filename, /*read_only=*/false);
485*600f14f4SXin Li
486*600f14f4SXin Li FLAC__metadata_chain_sort_padding(chain);
487*600f14f4SXin Li if(!FLAC__metadata_chain_write(chain, /*use_padding=*/true, preserve_modtime)) {
488*600f14f4SXin Li const char *error;
489*600f14f4SXin Li error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)];
490*600f14f4SXin Li FLAC__metadata_chain_delete(chain);
491*600f14f4SXin Li return error;
492*600f14f4SXin Li }
493*600f14f4SXin Li
494*600f14f4SXin Li FLAC__metadata_chain_delete(chain);
495*600f14f4SXin Li
496*600f14f4SXin Li if(have_stats)
497*600f14f4SXin Li set_file_stats_(filename, &stats);
498*600f14f4SXin Li
499*600f14f4SXin Li return 0;
500*600f14f4SXin Li }
501*600f14f4SXin Li
grabbag__replaygain_store_to_file(const char * filename,float album_gain,float album_peak,float title_gain,float title_peak,FLAC__bool preserve_modtime)502*600f14f4SXin Li const char *grabbag__replaygain_store_to_file(const char *filename, float album_gain, float album_peak, float title_gain, float title_peak, FLAC__bool preserve_modtime)
503*600f14f4SXin Li {
504*600f14f4SXin Li FLAC__Metadata_Chain *chain;
505*600f14f4SXin Li FLAC__StreamMetadata *block = NULL;
506*600f14f4SXin Li const char *error;
507*600f14f4SXin Li
508*600f14f4SXin Li if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
509*600f14f4SXin Li return error;
510*600f14f4SXin Li
511*600f14f4SXin Li if(0 != (error = grabbag__replaygain_store_to_vorbiscomment(block, album_gain, album_peak, title_gain, title_peak))) {
512*600f14f4SXin Li FLAC__metadata_chain_delete(chain);
513*600f14f4SXin Li return error;
514*600f14f4SXin Li }
515*600f14f4SXin Li
516*600f14f4SXin Li if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
517*600f14f4SXin Li return error;
518*600f14f4SXin Li
519*600f14f4SXin Li return 0;
520*600f14f4SXin Li }
521*600f14f4SXin Li
grabbag__replaygain_store_to_file_reference(const char * filename,FLAC__bool preserve_modtime)522*600f14f4SXin Li const char *grabbag__replaygain_store_to_file_reference(const char *filename, FLAC__bool preserve_modtime)
523*600f14f4SXin Li {
524*600f14f4SXin Li FLAC__Metadata_Chain *chain;
525*600f14f4SXin Li FLAC__StreamMetadata *block = NULL;
526*600f14f4SXin Li const char *error;
527*600f14f4SXin Li
528*600f14f4SXin Li if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
529*600f14f4SXin Li return error;
530*600f14f4SXin Li
531*600f14f4SXin Li if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_reference(block))) {
532*600f14f4SXin Li FLAC__metadata_chain_delete(chain);
533*600f14f4SXin Li return error;
534*600f14f4SXin Li }
535*600f14f4SXin Li
536*600f14f4SXin Li if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
537*600f14f4SXin Li return error;
538*600f14f4SXin Li
539*600f14f4SXin Li return 0;
540*600f14f4SXin Li }
541*600f14f4SXin Li
grabbag__replaygain_store_to_file_album(const char * filename,float album_gain,float album_peak,FLAC__bool preserve_modtime)542*600f14f4SXin Li const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime)
543*600f14f4SXin Li {
544*600f14f4SXin Li FLAC__Metadata_Chain *chain;
545*600f14f4SXin Li FLAC__StreamMetadata *block = NULL;
546*600f14f4SXin Li const char *error;
547*600f14f4SXin Li
548*600f14f4SXin Li if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
549*600f14f4SXin Li return error;
550*600f14f4SXin Li
551*600f14f4SXin Li if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak))) {
552*600f14f4SXin Li FLAC__metadata_chain_delete(chain);
553*600f14f4SXin Li return error;
554*600f14f4SXin Li }
555*600f14f4SXin Li
556*600f14f4SXin Li if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
557*600f14f4SXin Li return error;
558*600f14f4SXin Li
559*600f14f4SXin Li return 0;
560*600f14f4SXin Li }
561*600f14f4SXin Li
grabbag__replaygain_store_to_file_title(const char * filename,float title_gain,float title_peak,FLAC__bool preserve_modtime)562*600f14f4SXin Li const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime)
563*600f14f4SXin Li {
564*600f14f4SXin Li FLAC__Metadata_Chain *chain;
565*600f14f4SXin Li FLAC__StreamMetadata *block = NULL;
566*600f14f4SXin Li const char *error;
567*600f14f4SXin Li
568*600f14f4SXin Li if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
569*600f14f4SXin Li return error;
570*600f14f4SXin Li
571*600f14f4SXin Li if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak))) {
572*600f14f4SXin Li FLAC__metadata_chain_delete(chain);
573*600f14f4SXin Li return error;
574*600f14f4SXin Li }
575*600f14f4SXin Li
576*600f14f4SXin Li if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
577*600f14f4SXin Li return error;
578*600f14f4SXin Li
579*600f14f4SXin Li return 0;
580*600f14f4SXin Li }
581*600f14f4SXin Li
parse_double_(const FLAC__StreamMetadata_VorbisComment_Entry * entry,double * val)582*600f14f4SXin Li static FLAC__bool parse_double_(const FLAC__StreamMetadata_VorbisComment_Entry *entry, double *val)
583*600f14f4SXin Li {
584*600f14f4SXin Li char s[32], *end;
585*600f14f4SXin Li const char *p, *q;
586*600f14f4SXin Li double v;
587*600f14f4SXin Li
588*600f14f4SXin Li FLAC__ASSERT(0 != entry);
589*600f14f4SXin Li FLAC__ASSERT(0 != val);
590*600f14f4SXin Li
591*600f14f4SXin Li p = (const char *)entry->entry;
592*600f14f4SXin Li q = strchr(p, '=');
593*600f14f4SXin Li if(0 == q)
594*600f14f4SXin Li return false;
595*600f14f4SXin Li q++;
596*600f14f4SXin Li safe_strncpy(s, q, local_min(sizeof(s), (size_t) (entry->length - (q-p))));
597*600f14f4SXin Li
598*600f14f4SXin Li v = strtod(s, &end);
599*600f14f4SXin Li if(end == s)
600*600f14f4SXin Li return false;
601*600f14f4SXin Li
602*600f14f4SXin Li *val = v;
603*600f14f4SXin Li return true;
604*600f14f4SXin Li }
605*600f14f4SXin Li
grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata * block,FLAC__bool album_mode,FLAC__bool strict,double * reference,double * gain,double * peak)606*600f14f4SXin Li FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, FLAC__bool strict, double *reference, double *gain, double *peak)
607*600f14f4SXin Li {
608*600f14f4SXin Li int reference_offset, gain_offset, peak_offset;
609*600f14f4SXin Li char *saved_locale;
610*600f14f4SXin Li FLAC__bool res = true;
611*600f14f4SXin Li
612*600f14f4SXin Li FLAC__ASSERT(0 != block);
613*600f14f4SXin Li FLAC__ASSERT(0 != reference);
614*600f14f4SXin Li FLAC__ASSERT(0 != gain);
615*600f14f4SXin Li FLAC__ASSERT(0 != peak);
616*600f14f4SXin Li FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
617*600f14f4SXin Li
618*600f14f4SXin Li /* Default to current level until overridden by a detected tag; this
619*600f14f4SXin Li * will always be true until we change replaygain_analysis.c
620*600f14f4SXin Li */
621*600f14f4SXin Li *reference = ReplayGainReferenceLoudness;
622*600f14f4SXin Li
623*600f14f4SXin Li /*
624*600f14f4SXin Li * We need to save the old locale and switch to "C" because the locale
625*600f14f4SXin Li * influences the behaviour of strtod and we want it a certain way.
626*600f14f4SXin Li */
627*600f14f4SXin Li saved_locale = strdup(setlocale(LC_ALL, 0));
628*600f14f4SXin Li if (0 == saved_locale)
629*600f14f4SXin Li return false;
630*600f14f4SXin Li setlocale(LC_ALL, "C");
631*600f14f4SXin Li
632*600f14f4SXin Li if(0 <= (reference_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS)))
633*600f14f4SXin Li (void)parse_double_(block->data.vorbis_comment.comments + reference_offset, reference);
634*600f14f4SXin Li
635*600f14f4SXin Li if(0 > (gain_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN : GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN))))
636*600f14f4SXin Li res = false;
637*600f14f4SXin Li if(0 > (peak_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK : GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK))))
638*600f14f4SXin Li res = false;
639*600f14f4SXin Li
640*600f14f4SXin Li if(res && !parse_double_(block->data.vorbis_comment.comments + gain_offset, gain))
641*600f14f4SXin Li res = false;
642*600f14f4SXin Li if(res && !parse_double_(block->data.vorbis_comment.comments + peak_offset, peak))
643*600f14f4SXin Li res = false;
644*600f14f4SXin Li if(res && *peak < 0.0)
645*600f14f4SXin Li res = false;
646*600f14f4SXin Li
647*600f14f4SXin Li setlocale(LC_ALL, saved_locale);
648*600f14f4SXin Li free(saved_locale);
649*600f14f4SXin Li
650*600f14f4SXin Li /* something failed; retry with strict */
651*600f14f4SXin Li if (!res && !strict)
652*600f14f4SXin Li res = grabbag__replaygain_load_from_vorbiscomment(block, !album_mode, /*strict=*/true, reference, gain, peak);
653*600f14f4SXin Li
654*600f14f4SXin Li return res;
655*600f14f4SXin Li }
656*600f14f4SXin Li
grabbag__replaygain_compute_scale_factor(double peak,double gain,double preamp,FLAC__bool prevent_clipping)657*600f14f4SXin Li double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping)
658*600f14f4SXin Li {
659*600f14f4SXin Li double scale;
660*600f14f4SXin Li FLAC__ASSERT(peak >= 0.0);
661*600f14f4SXin Li gain += preamp;
662*600f14f4SXin Li scale = (float) pow(10.0, gain * 0.05);
663*600f14f4SXin Li if(prevent_clipping && peak > 0.0) {
664*600f14f4SXin Li const double max_scale = (float)(1.0 / peak);
665*600f14f4SXin Li if(scale > max_scale)
666*600f14f4SXin Li scale = max_scale;
667*600f14f4SXin Li }
668*600f14f4SXin Li return scale;
669*600f14f4SXin Li }
670