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