~ubuntu-branches/ubuntu/feisty/flac/feisty

« back to all changes in this revision

Viewing changes to src/share/grabbag/replaygain.c

  • Committer: Bazaar Package Importer
  • Author(s): Matt Zimmerman
  • Date: 2004-04-16 15:14:31 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20040416151431-eyloggqxpwbwpogz
Tags: 1.1.0-11
Ensure that libFLAC is linked with -lm on all architectures, and
regardless of whether nasm is present

Show diffs side-by-side

added added

removed removed

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