static void analyze_audio( mlt_filter filter, void* buffer, int samples, int frequency ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; int result = -1; double in_loudness = 0.0; mlt_profile profile = mlt_service_profile( MLT_FILTER_SERVICE(filter) ); double fps = mlt_profile_fps( profile ); ebur128_add_frames_float( pdata->r128, buffer, samples ); if( pdata->time_elapsed_ms < 400 ) { // Waiting for first program loudness measurement. // Use window loudness as initial guess. result = ebur128_loudness_window( pdata->r128, pdata->time_elapsed_ms, &in_loudness ); pdata->time_elapsed_ms += samples * 1000 / frequency; } else { result = ebur128_loudness_global( pdata->r128, &in_loudness ); } if( result == EBUR128_SUCCESS && in_loudness != HUGE_VAL && in_loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "in_loudness", in_loudness ); double target_loudness = mlt_properties_get_double( properties, "target_loudness" ); pdata->target_gain = target_loudness - in_loudness; // Make sure gain limits are not exceeded. double max_gain = mlt_properties_get_double( properties, "max_gain" ); double min_gain = mlt_properties_get_double( properties, "min_gain" ); if( pdata->target_gain > max_gain ) { pdata->target_gain = max_gain; } else if ( pdata->target_gain < min_gain ) { pdata->target_gain = min_gain; } } // Make sure gain does not change too quickly. pdata->start_gain = pdata->end_gain; pdata->end_gain = pdata->target_gain; double max_frame_gain = mlt_properties_get_double( properties, "max_rate" ) / fps; if( pdata->start_gain - pdata->end_gain > max_frame_gain ) { pdata->end_gain = pdata->start_gain - max_frame_gain; } else if( pdata->end_gain - pdata->start_gain > max_frame_gain ) { pdata->end_gain = pdata->start_gain + max_frame_gain; } mlt_properties_set_double( properties, "out_gain", pdata->end_gain ); }
double test_global_loudness(const char* filename) { SF_INFO file_info; SNDFILE* file; sf_count_t nr_frames_read; ebur128_state* st = NULL; double gated_loudness; double* buffer; memset(&file_info, '\0', sizeof(file_info)); file = sf_open(filename, SFM_READ, &file_info); if (!file) { fprintf(stderr, "Could not open file %s!\n", filename); return 0.0; } st = ebur128_init((unsigned) file_info.channels, (unsigned) file_info.samplerate, EBUR128_MODE_I); if (file_info.channels == 5) { ebur128_set_channel(st, 0, EBUR128_LEFT); ebur128_set_channel(st, 1, EBUR128_RIGHT); ebur128_set_channel(st, 2, EBUR128_CENTER); ebur128_set_channel(st, 3, EBUR128_LEFT_SURROUND); ebur128_set_channel(st, 4, EBUR128_RIGHT_SURROUND); } buffer = (double*) malloc(st->samplerate * st->channels * sizeof(double)); while ((nr_frames_read = sf_readf_double(file, buffer, (sf_count_t) st->samplerate))) { ebur128_add_frames_double(st, buffer, (size_t) nr_frames_read); } ebur128_loudness_global(st, &gated_loudness); /* clean up */ ebur128_destroy(&st); free(buffer); buffer = NULL; if (sf_close(file)) { fprintf(stderr, "Could not close input file!\n"); } return gated_loudness; }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_filter_ebur128_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_filter_ebur128 *upipe_filter_ebur128 = upipe_filter_ebur128_from_upipe(upipe); double loud = 0, lra = 0, global = 0; size_t samples; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, NULL)))) { upipe_warn(upipe, "invalid sound buffer"); uref_free(uref); return; } const char *channel = NULL; const int16_t *buf = NULL; if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) { if (unlikely(!ubase_check(uref_sound_plane_read_int16_t(uref, channel, 0, -1, &buf)))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } if (unlikely((uintptr_t)buf & 1)) { upipe_warn(upipe, "unaligned buffer"); } ebur128_add_frames_short(upipe_filter_ebur128->st, buf, samples); uref_sound_plane_unmap(uref, channel, 0, -1); } ebur128_loudness_momentary(upipe_filter_ebur128->st, &loud); ebur128_loudness_range(upipe_filter_ebur128->st, &lra); ebur128_loudness_global(upipe_filter_ebur128->st, &global); uref_ebur128_set_momentary(uref, loud); uref_ebur128_set_lra(uref, lra); uref_ebur128_set_global(uref, global); upipe_verbose_va(upipe, "loud %f lra %f global %f", loud, lra, global); upipe_filter_ebur128_output(upipe, uref, upump_p); }
int main(int ac, const char* av[]) { SF_INFO file_info; SNDFILE* file; sf_count_t nr_frames_read; ebur128_state* state = NULL; double* buffer; double loudness; if (ac != 2) { exit(1); //ERROR CODE 1: Exactly one input file is expected. } state = malloc((size_t) sizeof(ebur128_state*)); memset(&file_info, '\0', sizeof(file_info)); file = sf_open(av[1], SFM_READ, &file_info); state = ebur128_init((unsigned) file_info.channels, (unsigned) file_info.samplerate, EBUR128_MODE_I); buffer = (double*) malloc(state->samplerate * state->channels * sizeof(double)); while ((nr_frames_read = sf_readf_double(file, buffer, (sf_count_t) state->samplerate))) { ebur128_add_frames_double(state, buffer, (size_t) nr_frames_read); } ebur128_loudness_global(state, &loudness); fprintf(stdout, "%.2f\n", loudness); free(buffer); buffer = NULL; if (sf_close(file)) { exit(2); //ERROR CODE 2: File wasn't able to be closed. } ebur128_destroy(&state); free(state); return 0; // ERROR CODE 0: No errors! }
static void analyze_audio( mlt_filter filter, void* buffer, int samples ) { mlt_properties properties = MLT_FILTER_PROPERTIES( filter ); private_data* pdata = (private_data*)filter->child; int result = -1; double loudness = 0.0; ebur128_add_frames_float( pdata->r128, buffer, samples ); if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_program" ) ) { result = ebur128_loudness_global( pdata->r128, &loudness ); if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "program", loudness ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_shortterm" ) ) { result = ebur128_loudness_shortterm( pdata->r128, &loudness ); if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "shortterm", loudness ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_momentary" ) ) { result = ebur128_loudness_momentary( pdata->r128, &loudness ); if( result == EBUR128_SUCCESS && loudness != HUGE_VAL && loudness != -HUGE_VAL ) { mlt_properties_set_double( properties, "momentary", loudness ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_range" ) ) { double range = 0; result = ebur128_loudness_range( pdata->r128, &range ); if( result == EBUR128_SUCCESS && range != HUGE_VAL && range != -HUGE_VAL ) { mlt_properties_set_double( properties, "range", range ); } } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_peak" ) ) { double prev_peak = 0.0; double max_peak = 0.0; int c = 0; for( c = 0; c < pdata->r128->channels; c++ ) { double peak; result = ebur128_sample_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak ) { max_peak = peak; } result = ebur128_prev_sample_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak ) { prev_peak = peak; } } mlt_properties_set_double( properties, "max_peak", 20 * log10(max_peak) ); mlt_properties_set_double( properties, "peak", 20 * log10(prev_peak) ); } if( mlt_properties_get_int( MLT_FILTER_PROPERTIES(filter), "calc_true_peak" ) ) { double prev_peak = 0.0; double max_peak = 0.0; int c = 0; for( c = 0; c < pdata->r128->channels; c++ ) { double peak; result = ebur128_true_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > max_peak ) { max_peak = peak; } result = ebur128_prev_true_peak( pdata->r128, c, &peak ); if( result == EBUR128_SUCCESS && peak != HUGE_VAL && peak > prev_peak ) { prev_peak = peak; } } mlt_properties_set_double( properties, "max_true_peak", 20 * log10(max_peak) ); mlt_properties_set_double( properties, "true_peak", 20 * log10(prev_peak) ); } mlt_properties_set_position( properties, "frames_processed", mlt_properties_get_position( properties, "frames_processed" ) + 1 ); }
/** @internal @This handles input. * * @param upipe description structure of the pipe * @param uref uref structure * @param upump_p reference to upump structure */ static void upipe_filter_ebur128_input(struct upipe *upipe, struct uref *uref, struct upump **upump_p) { struct upipe_filter_ebur128 *upipe_filter_ebur128 = upipe_filter_ebur128_from_upipe(upipe); double loud = 0, lra = 0, global = 0; if (unlikely(upipe_filter_ebur128->output_flow == NULL)) { upipe_err_va(upipe, "invalid input"); uref_free(uref); return; } size_t samples; uint8_t sample_size; if (unlikely(!ubase_check(uref_sound_size(uref, &samples, &sample_size)))) { upipe_warn(upipe, "invalid sound buffer"); uref_free(uref); return; } void *buf = NULL; const char *channel = NULL; if (upipe_filter_ebur128->planes == 1) { if (ubase_check(uref_sound_plane_iterate(uref, &channel)) && channel) { if (unlikely(!ubase_check(uref_sound_plane_read_void(uref, channel, 0, -1, (const void **)&buf)))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } } } else { buf = malloc(sample_size * upipe_filter_ebur128->channels * samples); if (buf == NULL) { upipe_throw_fatal(upipe, UBASE_ERR_ALLOC); uref_free(uref); return; } if (!ubase_check(uref_sound_interleave(uref, (uint8_t *)buf, 0, samples, sample_size, upipe_filter_ebur128->planes))) { upipe_warn(upipe, "error mapping sound buffer"); uref_free(uref); return; } } if (unlikely((uintptr_t)buf & 1)) upipe_warn(upipe, "unaligned buffer"); switch (upipe_filter_ebur128->fmt) { case UPIPE_FILTER_EBUR128_SHORT: ebur128_add_frames_short(upipe_filter_ebur128->st, (short *)buf, samples); break; case UPIPE_FILTER_EBUR128_INT: ebur128_add_frames_int(upipe_filter_ebur128->st, (int *)buf, samples); break; case UPIPE_FILTER_EBUR128_FLOAT: ebur128_add_frames_float(upipe_filter_ebur128->st, (float *)buf, samples); break; case UPIPE_FILTER_EBUR128_DOUBLE: ebur128_add_frames_double(upipe_filter_ebur128->st, (double *)buf, samples); break; default: upipe_warn_va(upipe, "unknown sample format %d", upipe_filter_ebur128->fmt); break; } if (upipe_filter_ebur128->planes == 1) uref_sound_plane_unmap(uref, channel, 0, -1); else free(buf); ebur128_loudness_momentary(upipe_filter_ebur128->st, &loud); ebur128_loudness_range(upipe_filter_ebur128->st, &lra); ebur128_loudness_global(upipe_filter_ebur128->st, &global); uref_ebur128_set_momentary(uref, loud); uref_ebur128_set_lra(uref, lra); uref_ebur128_set_global(uref, global); upipe_verbose_va(upipe, "loud %f lra %f global %f", loud, lra, global); upipe_filter_ebur128_output(upipe, uref, upump_p); }
void init_state_and_scan_work_item(struct filename_list_node *fln, struct scan_opts *opts) { struct file_data *fd = (struct file_data *) fln->d; struct input_ops* ops = NULL; struct input_handle* ih = NULL; int r128_mode = EBUR128_MODE_I; unsigned int i; int *channel_map; int result; float *buffer = NULL; size_t nr_frames_read; #ifdef USE_SNDFILE SNDFILE *outfile = NULL; #endif result = open_plugin(fln->fr->raw, fln->fr->display, &ops, &ih); if (result) { g_mutex_lock(progress_mutex); elapsed_frames += fd->number_of_frames; g_cond_broadcast(progress_cond); g_mutex_unlock(progress_mutex); goto free; } if (opts->lra) r128_mode |= EBUR128_MODE_LRA; if (opts->peak) { if (!strcmp(opts->peak, "sample") || !strcmp(opts->peak, "all")) r128_mode |= EBUR128_MODE_SAMPLE_PEAK; #ifdef USE_SPEEX_RESAMPLER if (!strcmp(opts->peak, "true") || !strcmp(opts->peak, "dbtp") || !strcmp(opts->peak, "all")) r128_mode |= EBUR128_MODE_TRUE_PEAK; #endif } if (opts->histogram) r128_mode |= EBUR128_MODE_HISTOGRAM; fd->st = ebur128_init(ops->get_channels(ih), ops->get_samplerate(ih), r128_mode); channel_map = g_malloc(fd->st->channels * sizeof(int)); if (!ops->set_channel_map(ih, channel_map)) { for (i = 0; i < fd->st->channels; ++i) { ebur128_set_channel(fd->st, i, channel_map[i]); } } free(channel_map); if (fd->st->channels == 1 && opts->force_dual_mono) { ebur128_set_channel(fd->st, 0, EBUR128_DUAL_MONO); } result = ops->allocate_buffer(ih); if (result) abort(); buffer = ops->get_buffer(ih); #ifdef USE_SNDFILE if (opts->decode_file) { SF_INFO sf_info; memset(&sf_info, '\0', sizeof sf_info); sf_info.samplerate = (int) fd->st->samplerate; sf_info.channels = (int) fd->st->channels; sf_info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; outfile = sf_open(opts->decode_file, SFM_WRITE, &sf_info); if (!outfile) { fprintf(stderr, "output file could not be opened\n"); exit(EXIT_FAILURE); } } #endif while ((nr_frames_read = ops->read_frames(ih))) { g_mutex_lock(progress_mutex); elapsed_frames += nr_frames_read; g_cond_broadcast(progress_cond); g_mutex_unlock(progress_mutex); fd->number_of_elapsed_frames += nr_frames_read; result = ebur128_add_frames_float(fd->st, buffer, nr_frames_read); #ifdef USE_SNDFILE if (opts->decode_file) { if (sf_writef_float(outfile, buffer, (sf_count_t) nr_frames_read) != (sf_count_t) nr_frames_read) sf_perror(outfile); } #endif if (result) abort(); } #ifdef USE_SNDFILE if (opts->decode_file) { sf_close(outfile); } #endif if (fd->number_of_elapsed_frames != fd->number_of_frames) { if (verbose) { fprintf(stderr, "Warning: Could not read full file" " or determine right length: " "Expected: %lu Got: %lu", fd->number_of_frames, fd->number_of_elapsed_frames); } g_mutex_lock(progress_mutex); total_frames = total_frames + fd->number_of_elapsed_frames - fd->number_of_frames; g_cond_broadcast(progress_cond); g_mutex_unlock(progress_mutex); } ebur128_loudness_global(fd->st, &fd->loudness); if (opts->lra) { result = ebur128_loudness_range(fd->st, &fd->lra); if (result) abort(); } if ((fd->st->mode & EBUR128_MODE_SAMPLE_PEAK) == EBUR128_MODE_SAMPLE_PEAK) { for (i = 0; i < fd->st->channels; ++i) { double sp; ebur128_sample_peak(fd->st, i, &sp); if (sp > fd->peak) { fd->peak = sp; } } } #ifdef USE_SPEEX_RESAMPLER if ((fd->st->mode & EBUR128_MODE_TRUE_PEAK) == EBUR128_MODE_TRUE_PEAK) { for (i = 0; i < fd->st->channels; ++i) { double tp; ebur128_true_peak(fd->st, i, &tp); if (tp > fd->true_peak) { fd->true_peak = tp; } } } #endif fd->scanned = TRUE; if (ih) ops->free_buffer(ih); free: if (!result) ops->close_file(ih); if (ih) ops->handle_destroy(&ih); }
int main(int ac, const char* av[]) { SF_INFO file_info; SNDFILE* file; sf_count_t nr_frames_read; ebur128_state** sts = NULL; double* buffer; double loudness; int i; if (ac < 2) { fprintf(stderr, "usage: %s FILENAME...\n", av[0]); exit(1); } sts = malloc((size_t) (ac - 1) * sizeof(ebur128_state*)); for (i = 0; i < ac - 1; ++i) { memset(&file_info, '\0', sizeof(file_info)); file = sf_open(av[i + 1], SFM_READ, &file_info); sts[i] = ebur128_init((unsigned) file_info.channels, (unsigned) file_info.samplerate, EBUR128_MODE_I); /* example: set channel map (note: see ebur128.h for the default map) */ if (file_info.channels == 5) { ebur128_set_channel(sts[i], 0, EBUR128_LEFT); ebur128_set_channel(sts[i], 1, EBUR128_RIGHT); ebur128_set_channel(sts[i], 2, EBUR128_CENTER); ebur128_set_channel(sts[i], 3, EBUR128_LEFT_SURROUND); ebur128_set_channel(sts[i], 4, EBUR128_RIGHT_SURROUND); } buffer = (double*) malloc(sts[i]->samplerate * sts[i]->channels * sizeof(double)); while ((nr_frames_read = sf_readf_double(file, buffer, (sf_count_t) sts[i]->samplerate))) { ebur128_add_frames_double(sts[i], buffer, (size_t) nr_frames_read); } ebur128_loudness_global(sts[i], &loudness); fprintf(stderr, "%.2f LUFS, %s\n", loudness, av[i + 1]); free(buffer); buffer = NULL; if (sf_close(file)) { fprintf(stderr, "Could not close input file!\n"); } } ebur128_loudness_global_multiple(sts, (size_t) ac - 1, &loudness); fprintf(stderr, "-----------\n%.2f LUFS\n", loudness); /* clean up */ for (i = 0; i < ac - 1; ++i) { ebur128_destroy(&sts[i]); } free(sts); return 0; }
void TrackScanner::run() { bool ffmpegIsFloat=false; #ifdef FFMPEG_FOUND FfmpegInput *ffmpeg=new FfmpegInput(file); if (*ffmpeg) { input=ffmpeg; ffmpegIsFloat=ffmpeg->isFloatCodec(); } else { delete ffmpeg; ffmpeg=0; } #endif #if MPG123_FOUND if (file.endsWith(".mp3", Qt::CaseInsensitive) && (!input || !ffmpegIsFloat)) { Mpg123Input *mpg123=new Mpg123Input(file); if (*mpg123) { input=mpg123; #ifdef FFMPEG_FOUND if (ffmpeg) { delete ffmpeg; } #endif } else { delete mpg123; } } #endif if (!input) { setFinishedStatus(false); return; } state=ebur128_init(input->channels(), input->sampleRate(), EBUR128_MODE_M|EBUR128_MODE_I|EBUR128_MODE_SAMPLE_PEAK); int *channelMap=new int [state->channels]; if (input->setChannelMap(channelMap)) { for (unsigned int i = 0; i < state->channels; ++i) { ebur128_set_channel(state, i, channelMap[i]); } } delete [] channelMap; //if (1==state->channels && opts->force_dual_mono) { // ebur128_set_channel(state, 0, EBUR128_DUAL_MONO); //} size_t numFramesRead=0; size_t totalRead=0; input->allocateBuffer(); while ((numFramesRead = input->readFrames())) { if (abortRequested) { setFinishedStatus(false); return; } totalRead+=numFramesRead; emit progress((int)((totalRead*100.0/input->totalFrames())+0.5)); if (ebur128_add_frames_float(state, input->buffer(), numFramesRead)) { setFinishedStatus(false); return; } } if (abortRequested) { setFinishedStatus(false); return; } ebur128_loudness_global(state, &data.loudness); // if (opts->lra) { // result = ebur128_loudness_range(ebur, &lra); // if (result) abort(); // } if (EBUR128_MODE_SAMPLE_PEAK==(state->mode & EBUR128_MODE_SAMPLE_PEAK)) { for (unsigned i = 0; i < state->channels; ++i) { double sp; ebur128_sample_peak(state, i, &sp); if (sp > data.peak) { data.peak = sp; } } } if (EBUR128_MODE_TRUE_PEAK==(state->mode & EBUR128_MODE_TRUE_PEAK)) { for (unsigned i = 0; i < state->channels; ++i) { double tp; ebur128_true_peak(state, i, &tp); if (tp > data.truePeak) { data.truePeak = tp; } } } setFinishedStatus(true); }
void rg_calc_thread(void *ctx) { DB_decoder_t *dec = NULL; DB_fileinfo_t *fileinfo = NULL; char *buffer = NULL; char *bufferf = NULL; track_state_t *st = (track_state_t *)ctx; if (st->settings->pabort && *(st->settings->pabort)) { return; } if (deadbeef->pl_get_item_duration (st->settings->tracks[st->track_index]) <= 0) { st->settings->results[st->track_index].scan_result = DDB_RG_SCAN_RESULT_INVALID_FILE; return; } deadbeef->pl_lock (); dec = (DB_decoder_t *)deadbeef->plug_get_for_id (deadbeef->pl_find_meta (st->settings->tracks[st->track_index], ":DECODER")); deadbeef->pl_unlock (); if (dec) { fileinfo = dec->open (DDB_DECODER_HINT_RAW_SIGNAL); if (fileinfo && dec->init (fileinfo, DB_PLAYITEM (st->settings->tracks[st->track_index])) != 0) { st->settings->results[st->track_index].scan_result = DDB_RG_SCAN_RESULT_FILE_NOT_FOUND; goto error; } if (fileinfo) { st->gain_state[st->track_index] = ebur128_init(fileinfo->fmt.channels, fileinfo->fmt.samplerate, EBUR128_MODE_I); st->peak_state[st->track_index] = ebur128_init(fileinfo->fmt.channels, fileinfo->fmt.samplerate, EBUR128_MODE_SAMPLE_PEAK); // speaker mask mapping from WAV to EBUR128 static const int chmap[18] = { EBUR128_LEFT, EBUR128_RIGHT, EBUR128_CENTER, EBUR128_UNUSED, EBUR128_LEFT_SURROUND, EBUR128_RIGHT_SURROUND, EBUR128_LEFT_SURROUND, EBUR128_RIGHT_SURROUND, EBUR128_CENTER, EBUR128_LEFT_SURROUND, EBUR128_RIGHT_SURROUND, EBUR128_CENTER, EBUR128_LEFT_SURROUND, EBUR128_CENTER, EBUR128_RIGHT_SURROUND, EBUR128_LEFT_SURROUND, EBUR128_CENTER, EBUR128_RIGHT_SURROUND, }; uint32_t channelmask = fileinfo->fmt.channelmask; // first 18 speaker positions are known, the rest will be marked as UNUSED int ch = 0; for (int i = 0; i < 32 && ch < fileinfo->fmt.channels; i++) { if (i < 18) { if (channelmask & (1<<i)) { ebur128_set_channel (st->gain_state[st->track_index], ch, chmap[i]); ebur128_set_channel (st->peak_state[st->track_index], ch, chmap[i]); ch++; } } else { ebur128_set_channel (st->gain_state[st->track_index], ch, EBUR128_UNUSED); ebur128_set_channel (st->peak_state[st->track_index], ch, EBUR128_UNUSED); ch++; } } int samplesize = fileinfo->fmt.channels * fileinfo->fmt.bps / 8; int bs = 2000 * samplesize; ddb_waveformat_t fmt; buffer = malloc (bs); if (!fileinfo->fmt.is_float) { bufferf = malloc (2000 * sizeof (float) * fileinfo->fmt.channels); memcpy (&fmt, &fileinfo->fmt, sizeof (fmt)); fmt.bps = 32; fmt.is_float = 1; } else { bufferf = buffer; } int eof = 0; for (;;) { if (eof) { break; } if (st->settings->pabort && *(st->settings->pabort)) { break; } int sz = dec->read (fileinfo, buffer, bs); // read one block deadbeef->mutex_lock (st->settings->sync_mutex); int samplesize = fileinfo->fmt.channels * (fileinfo->fmt.bps >> 3); int numsamples = sz / samplesize; st->settings->cd_samples_processed += numsamples * 44100 / fileinfo->fmt.samplerate; deadbeef->mutex_unlock (st->settings->sync_mutex); if (sz != bs) { eof = 1; } // convert from native output to float, // only if the input is not float already if (!fileinfo->fmt.is_float) { deadbeef->pcm_convert (&fileinfo->fmt, buffer, &fmt, bufferf, sz); } int frames = sz / samplesize; ebur128_add_frames_float (st->gain_state[st->track_index], (float*) bufferf, frames); // collect data ebur128_add_frames_float (st->peak_state[st->track_index], (float*) bufferf, frames); // collect data } } if (!st->settings->pabort || !(*(st->settings->pabort))) { // calculating track peak // libEBUR128 calculates peak per channel, so we have to pick the highest value double tr_peak = 0; double ch_peak = 0; int res; for (int ch = 0; ch < fileinfo->fmt.channels; ++ch) { res = ebur128_sample_peak (st->peak_state[st->track_index], ch, &ch_peak); //trace ("rg_scanner: peak for ch %d: %f\n", ch, ch_peak); if (ch_peak > tr_peak) { //trace ("rg_scanner: %f > %f\n", ch_peak, tr_peak); tr_peak = ch_peak; } } st->settings->results[st->track_index].track_peak = (float) tr_peak; // calculate track loudness double loudness = st->settings->ref_loudness; ebur128_loudness_global (st->gain_state[st->track_index], &loudness); /* * EBUR128 sets the target level to -23 LUFS = 84dB * -> -23 - loudness = track gain to get to 84dB * * The old implementation of RG used 89dB, most people still use that * -> the above + (loudness - 84) = track gain to get to 89dB (or user specified) */ st->settings->results[st->track_index].track_gain = -23 - loudness + st->settings->ref_loudness - 84; } } error: // clean up if (fileinfo) { dec->free (fileinfo); } if (buffer && buffer != bufferf) { free (buffer); buffer = NULL; } if (bufferf) { free (bufferf); bufferf = NULL; } }