📄 replaygain.c
字号:
if(instance->channels != 1 && instance->channels != 2) {
instance->error = true;
return;
}
if(!grabbag__replaygain_is_valid_sample_frequency(instance->sample_rate)) {
instance->error = true;
return;
}
}
}
static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
{
DecoderInstance *instance = (DecoderInstance*)client_data;
(void)decoder, (void)status;
instance->error = true;
}
const char *grabbag__replaygain_analyze_file(const char *filename, float *title_gain, float *title_peak)
{
DecoderInstance instance;
FLAC__FileDecoder *decoder = FLAC__file_decoder_new();
if(0 == decoder)
return "memory allocation error";
instance.error = false;
/* It does these three by default but lets be explicit: */
FLAC__file_decoder_set_md5_checking(decoder, false);
FLAC__file_decoder_set_metadata_ignore_all(decoder);
FLAC__file_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
FLAC__file_decoder_set_filename(decoder, filename);
FLAC__file_decoder_set_write_callback(decoder, write_callback_);
FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_);
FLAC__file_decoder_set_error_callback(decoder, error_callback_);
FLAC__file_decoder_set_client_data(decoder, &instance);
if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK) {
FLAC__file_decoder_delete(decoder);
return "initializing decoder";
}
if(!FLAC__file_decoder_process_until_end_of_file(decoder) || instance.error) {
FLAC__file_decoder_delete(decoder);
return "decoding file";
}
FLAC__file_decoder_delete(decoder);
grabbag__replaygain_get_title(title_gain, title_peak);
return 0;
}
const char *grabbag__replaygain_store_to_vorbiscomment(FLAC__StreamMetadata *block, float album_gain, float album_peak, float title_gain, float title_peak)
{
const char *error;
if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak)))
return error;
if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak)))
return error;
return 0;
}
const char *grabbag__replaygain_store_to_vorbiscomment_album(FLAC__StreamMetadata *block, float album_gain, float album_peak)
{
FLAC__ASSERT(0 != block);
FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
if(
FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_album_gain_) < 0 ||
FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_album_peak_) < 0
)
return "memory allocation error";
if(
!append_tag_(block, peak_format_, tag_album_peak_, album_peak) ||
!append_tag_(block, gain_format_, tag_album_gain_, album_gain)
)
return "memory allocation error";
return 0;
}
const char *grabbag__replaygain_store_to_vorbiscomment_title(FLAC__StreamMetadata *block, float title_gain, float title_peak)
{
FLAC__ASSERT(0 != block);
FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
if(
FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_title_gain_) < 0 ||
FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, (const char *)tag_title_peak_) < 0
)
return "memory allocation error";
if(
!append_tag_(block, peak_format_, tag_title_peak_, title_peak) ||
!append_tag_(block, gain_format_, tag_title_gain_, title_gain)
)
return "memory allocation error";
return 0;
}
static const char *store_to_file_pre_(const char *filename, FLAC__Metadata_Chain **chain, FLAC__StreamMetadata **block)
{
FLAC__Metadata_Iterator *iterator;
const char *error;
FLAC__bool found_vc_block = false;
if(0 == (*chain = FLAC__metadata_chain_new()))
return "memory allocation error";
if(!FLAC__metadata_chain_read(*chain, filename)) {
error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)];
FLAC__metadata_chain_delete(*chain);
return error;
}
if(0 == (iterator = FLAC__metadata_iterator_new())) {
FLAC__metadata_chain_delete(*chain);
return "memory allocation error";
}
FLAC__metadata_iterator_init(iterator, *chain);
do {
*block = FLAC__metadata_iterator_get_block(iterator);
if((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
found_vc_block = true;
} while(!found_vc_block && FLAC__metadata_iterator_next(iterator));
if(!found_vc_block) {
/* create a new block */
*block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
if(0 == *block) {
FLAC__metadata_chain_delete(*chain);
FLAC__metadata_iterator_delete(iterator);
return "memory allocation error";
}
while(FLAC__metadata_iterator_next(iterator))
;
if(!FLAC__metadata_iterator_insert_block_after(iterator, *block)) {
error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)];
FLAC__metadata_chain_delete(*chain);
FLAC__metadata_iterator_delete(iterator);
return error;
}
/* iterator is left pointing to new block */
FLAC__ASSERT(FLAC__metadata_iterator_get_block(iterator) == *block);
}
FLAC__metadata_iterator_delete(iterator);
FLAC__ASSERT(0 != *block);
FLAC__ASSERT((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
return 0;
}
static const char *store_to_file_post_(const char *filename, FLAC__Metadata_Chain *chain, FLAC__bool preserve_modtime)
{
struct stat stats;
const FLAC__bool have_stats = get_file_stats_(filename, &stats);
(void)grabbag__file_change_stats(filename, /*read_only=*/false);
FLAC__metadata_chain_sort_padding(chain);
if(!FLAC__metadata_chain_write(chain, /*use_padding=*/true, preserve_modtime)) {
FLAC__metadata_chain_delete(chain);
return FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)];
}
FLAC__metadata_chain_delete(chain);
if(have_stats)
set_file_stats_(filename, &stats);
return 0;
}
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)
{
FLAC__Metadata_Chain *chain;
FLAC__StreamMetadata *block;
const char *error;
if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
return error;
if(0 != (error = grabbag__replaygain_store_to_vorbiscomment(block, album_gain, album_peak, title_gain, title_peak))) {
FLAC__metadata_chain_delete(chain);
return error;
}
if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
return error;
return 0;
}
const char *grabbag__replaygain_store_to_file_album(const char *filename, float album_gain, float album_peak, FLAC__bool preserve_modtime)
{
FLAC__Metadata_Chain *chain;
FLAC__StreamMetadata *block;
const char *error;
if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
return error;
if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_album(block, album_gain, album_peak))) {
FLAC__metadata_chain_delete(chain);
return error;
}
if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
return error;
return 0;
}
const char *grabbag__replaygain_store_to_file_title(const char *filename, float title_gain, float title_peak, FLAC__bool preserve_modtime)
{
FLAC__Metadata_Chain *chain;
FLAC__StreamMetadata *block;
const char *error;
if(0 != (error = store_to_file_pre_(filename, &chain, &block)))
return error;
if(0 != (error = grabbag__replaygain_store_to_vorbiscomment_title(block, title_gain, title_peak))) {
FLAC__metadata_chain_delete(chain);
return error;
}
if(0 != (error = store_to_file_post_(filename, chain, preserve_modtime)))
return error;
return 0;
}
static FLAC__bool parse_double_(const FLAC__StreamMetadata_VorbisComment_Entry *entry, double *val)
{
char s[32], *end;
const char *p, *q;
double v;
FLAC__ASSERT(0 != entry);
FLAC__ASSERT(0 != val);
p = (const char *)entry->entry;
q = strchr(p, '=');
if(0 == q)
return false;
q++;
memset(s, 0, sizeof(s)-1);
strncpy(s, q, local_min(sizeof(s)-1, entry->length - (q-p)));
v = strtod(s, &end);
if(end == s)
return false;
*val = v;
return true;
}
FLAC__bool grabbag__replaygain_load_from_vorbiscomment(const FLAC__StreamMetadata *block, FLAC__bool album_mode, double *gain, double *peak)
{
int gain_offset, peak_offset;
FLAC__ASSERT(0 != block);
FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
if(0 > (gain_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? tag_album_gain_ : tag_title_gain_))))
return false;
if(0 > (peak_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block, /*offset=*/0, (const char *)(album_mode? tag_album_peak_ : tag_title_peak_))))
return false;
if(!parse_double_(block->data.vorbis_comment.comments + gain_offset, gain))
return false;
if(!parse_double_(block->data.vorbis_comment.comments + peak_offset, peak))
return false;
return true;
}
double grabbag__replaygain_compute_scale_factor(double peak, double gain, double preamp, FLAC__bool prevent_clipping)
{
double scale;
FLAC__ASSERT(peak >= 0.0);
gain += preamp;
scale = (float) pow(10.0, gain * 0.05);
if(prevent_clipping && peak > 0.0) {
const double max_scale = (float)(1.0 / peak);
if(scale > max_scale)
scale = max_scale;
}
return scale;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -