X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=audio_mixer.cpp;h=769dabfa74a0831cb6678a52f15d245d2c4ca6f2;hb=4c879d05be9112b33245de1a694713dc59ced565;hp=1c570fa870468e54134ac892c5eac016372754ce;hpb=01181e6e22e5cfc9d0cb17231f2c1866cc53b04a;p=nageru diff --git a/audio_mixer.cpp b/audio_mixer.cpp index 1c570fa..769dabf 100644 --- a/audio_mixer.cpp +++ b/audio_mixer.cpp @@ -77,13 +77,38 @@ void convert_fixed32_to_fp32(float *dst, size_t out_channel, size_t out_num_chan } } +float find_peak(const float *samples, size_t num_samples) +{ + float m = fabs(samples[0]); + for (size_t i = 1; i < num_samples; ++i) { + m = max(m, fabs(samples[i])); + } + return m; +} + +void deinterleave_samples(const vector &in, vector *out_l, vector *out_r) +{ + size_t num_samples = in.size() / 2; + out_l->resize(num_samples); + out_r->resize(num_samples); + + const float *inptr = in.data(); + float *lptr = &(*out_l)[0]; + float *rptr = &(*out_r)[0]; + for (size_t i = 0; i < num_samples; ++i) { + *lptr++ = *inptr++; + *rptr++ = *inptr++; + } +} + } // namespace AudioMixer::AudioMixer(unsigned num_cards) : num_cards(num_cards), level_compressor(OUTPUT_FREQUENCY), limiter(OUTPUT_FREQUENCY), - compressor(OUTPUT_FREQUENCY) + compressor(OUTPUT_FREQUENCY), + correlation(OUTPUT_FREQUENCY) { locut.init(FILTER_HPF, 2); @@ -108,6 +133,13 @@ AudioMixer::AudioMixer(unsigned num_cards) // Look for ALSA cards. available_alsa_cards = ALSAInput::enumerate_devices(); + + r128.init(2, OUTPUT_FREQUENCY); + r128.integr_start(); + + // hlen=16 is pretty low quality, but we use quite a bit of CPU otherwise, + // and there's a limit to how important the peak meter is. + peak_resampler.setup(OUTPUT_FREQUENCY, OUTPUT_FREQUENCY * 4, /*num_channels=*/2, /*hlen=*/16, /*frel=*/1.0); } AudioMixer::~AudioMixer() @@ -153,7 +185,8 @@ void AudioMixer::reset_alsa_mutex_held(DeviceSpec device_spec) if (device->interesting_channels.empty()) { device->alsa_device.reset(); } else { - device->alsa_device.reset(new ALSAInput(available_alsa_cards[card_index].address.c_str(), OUTPUT_FREQUENCY, 2, bind(&AudioMixer::add_audio, this, device_spec, _1, _2, _3, _4))); + const ALSAInput::Device &alsa_dev = available_alsa_cards[card_index]; + device->alsa_device.reset(new ALSAInput(alsa_dev.address.c_str(), OUTPUT_FREQUENCY, alsa_dev.num_channels, bind(&AudioMixer::add_audio, this, device_spec, _1, _2, _3, _4))); device->capture_frequency = device->alsa_device->get_sample_rate(); device->alsa_device->start_capture_thread(); } @@ -318,12 +351,16 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin } // TODO: Move lo-cut etc. into each bus. - vector samples_out; + vector samples_out, left, right; samples_out.resize(num_samples * 2); samples_bus.resize(num_samples * 2); for (unsigned bus_index = 0; bus_index < input_mapping.buses.size(); ++bus_index) { fill_audio_bus(samples_card, input_mapping.buses[bus_index], num_samples, &samples_bus[0]); + // TODO: We should measure post-fader. + deinterleave_samples(samples_bus, &left, &right); + measure_bus_levels(bus_index, left, right); + float volume = from_db(fader_volume_db[bus_index]); if (bus_index == 0) { for (unsigned i = 0; i < num_samples * 2; ++i) { @@ -418,7 +455,7 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin // Note that there's a feedback loop here, so we choose a very slow filter // (half-time of 30 seconds). double target_loudness_factor, alpha; - double loudness_lu = loudness_lufs - ref_level_lufs; + double loudness_lu = r128.loudness_M() - ref_level_lufs; double current_makeup_lu = to_db(final_makeup_gain); target_loudness_factor = final_makeup_gain * from_db(-loudness_lu); @@ -446,9 +483,89 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin final_makeup_gain = m; } + update_meters(samples_out); + return samples_out; } +void AudioMixer::measure_bus_levels(unsigned bus_index, const vector &left, const vector &right) +{ + const float *ptrs[] = { left.data(), right.data() }; + { + lock_guard lock(audio_measure_mutex); + bus_r128[bus_index]->process(left.size(), const_cast(ptrs)); + } +} + +void AudioMixer::update_meters(const vector &samples) +{ + // Upsample 4x to find interpolated peak. + peak_resampler.inp_data = const_cast(samples.data()); + peak_resampler.inp_count = samples.size() / 2; + + vector interpolated_samples; + interpolated_samples.resize(samples.size()); + { + lock_guard lock(audio_measure_mutex); + + while (peak_resampler.inp_count > 0) { // About four iterations. + peak_resampler.out_data = &interpolated_samples[0]; + peak_resampler.out_count = interpolated_samples.size() / 2; + peak_resampler.process(); + size_t out_stereo_samples = interpolated_samples.size() / 2 - peak_resampler.out_count; + peak = max(peak, find_peak(interpolated_samples.data(), out_stereo_samples * 2)); + peak_resampler.out_data = nullptr; + } + } + + // Find R128 levels and L/R correlation. + vector left, right; + deinterleave_samples(samples, &left, &right); + float *ptrs[] = { left.data(), right.data() }; + { + lock_guard lock(audio_measure_mutex); + r128.process(left.size(), ptrs); + correlation.process_samples(samples); + } + + send_audio_level_callback(); +} + +void AudioMixer::reset_meters() +{ + lock_guard lock(audio_measure_mutex); + peak_resampler.reset(); + peak = 0.0f; + r128.reset(); + r128.integr_start(); + correlation.reset(); +} + +void AudioMixer::send_audio_level_callback() +{ + if (audio_level_callback == nullptr) { + return; + } + + lock_guard lock(audio_measure_mutex); + double loudness_s = r128.loudness_S(); + double loudness_i = r128.integrated(); + double loudness_range_low = r128.range_min(); + double loudness_range_high = r128.range_max(); + + vector bus_loudness; + bus_loudness.resize(input_mapping.buses.size()); + for (unsigned bus_index = 0; bus_index < bus_r128.size(); ++bus_index) { + bus_loudness[bus_index] = bus_r128[bus_index]->loudness_S(); + } + + audio_level_callback(loudness_s, to_db(peak), bus_loudness, + loudness_i, loudness_range_low, loudness_range_high, + gain_staging_db, + to_db(final_makeup_gain), + correlation.get_correlation()); +} + map AudioMixer::get_devices() const { lock_guard lock(audio_mutex); @@ -514,6 +631,17 @@ void AudioMixer::set_input_mapping(const InputMapping &new_input_mapping) } } + { + lock_guard lock(audio_measure_mutex); + bus_r128.resize(new_input_mapping.buses.size()); + for (unsigned bus_index = 0; bus_index < bus_r128.size(); ++bus_index) { + if (bus_r128[bus_index] == nullptr) { + bus_r128[bus_index].reset(new Ebu_r128_proc); + } + bus_r128[bus_index]->init(2, OUTPUT_FREQUENCY); + } + } + input_mapping = new_input_mapping; }