X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=audio_mixer.cpp;h=678f3d9736b870689e6ec5b6bdd5367f6583c4c5;hb=1062c5403b57859c219558e736564a3d0bbecfd5;hp=13eed33048fe5fcf659bf1ed762bb522ef4adfcc;hpb=9ee239a073dc7aa1f0e82695694df332a8634547;p=nageru diff --git a/audio_mixer.cpp b/audio_mixer.cpp index 13eed33..678f3d9 100644 --- a/audio_mixer.cpp +++ b/audio_mixer.cpp @@ -164,6 +164,10 @@ AudioMixer::AudioMixer(unsigned num_cards) for (unsigned bus_index = 0; bus_index < MAX_BUSES; ++bus_index) { locut[bus_index].init(FILTER_HPF, 2); locut_enabled[bus_index] = global_flags.locut_enabled; + eq[bus_index][EQ_BAND_BASS].init(FILTER_LOW_SHELF, 1); + // Note: EQ_BAND_MID isn't used (see comments in apply_eq()). + eq[bus_index][EQ_BAND_TREBLE].init(FILTER_HIGH_SHELF, 1); + gain_staging_db[bus_index] = global_flags.initial_gain_staging_db; compressor[bus_index].reset(new StereoCompressor(OUTPUT_FREQUENCY)); compressor_threshold_dbfs[bus_index] = ref_level_dbfs - 12.0f; // -12 dB. @@ -409,14 +413,7 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin 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]); - - // Cut away everything under 120 Hz (or whatever the cutoff is); - // we don't need it for voice, and it will reduce headroom - // and confuse the compressor. (In particular, any hums at 50 or 60 Hz - // should be dampened.) - if (locut_enabled[bus_index]) { - locut[bus_index].render(samples_bus.data(), samples_bus.size() / 2, locut_cutoff_hz * 2.0 * M_PI / OUTPUT_FREQUENCY, 0.5f); - } + apply_eq(bus_index, &samples_bus); { lock_guard lock(compressor_mutex); @@ -462,20 +459,9 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin } } - // TODO: We should measure post-fader. + add_bus_to_master(bus_index, samples_bus, &samples_out); 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) { - samples_out[i] = samples_bus[i] * volume; - } - } else { - for (unsigned i = 0; i < num_samples * 2; ++i) { - samples_out[i] += samples_bus[i] * volume; - } - } } { @@ -542,12 +528,122 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin return samples_out; } +void AudioMixer::apply_eq(unsigned bus_index, vector *samples_bus) +{ + constexpr float bass_freq_hz = 200.0f; + constexpr float treble_freq_hz = 4700.0f; + + // Cut away everything under 120 Hz (or whatever the cutoff is); + // we don't need it for voice, and it will reduce headroom + // and confuse the compressor. (In particular, any hums at 50 or 60 Hz + // should be dampened.) + if (locut_enabled[bus_index]) { + locut[bus_index].render(samples_bus->data(), samples_bus->size() / 2, locut_cutoff_hz * 2.0 * M_PI / OUTPUT_FREQUENCY, 0.5f); + } + + // Apply the rest of the EQ. Since we only have a simple three-band EQ, + // we can implement it with two shelf filters. We use a simple gain to + // set the mid-level filter, and then offset the low and high bands + // from that if we need to. (We could perhaps have folded the gain into + // the next part, but it's so cheap that the trouble isn't worth it.) + if (fabs(eq_level_db[bus_index][EQ_BAND_MID]) > 0.01f) { + float g = from_db(eq_level_db[bus_index][EQ_BAND_MID]); + for (size_t i = 0; i < samples_bus->size(); ++i) { + (*samples_bus)[i] *= g; + } + } + + float bass_adj_db = eq_level_db[bus_index][EQ_BAND_BASS] - eq_level_db[bus_index][EQ_BAND_MID]; + if (fabs(bass_adj_db) > 0.01f) { + eq[bus_index][EQ_BAND_BASS].render(samples_bus->data(), samples_bus->size() / 2, + bass_freq_hz * 2.0 * M_PI / OUTPUT_FREQUENCY, 0.5f, bass_adj_db / 40.0f); + } + + float treble_adj_db = eq_level_db[bus_index][EQ_BAND_TREBLE] - eq_level_db[bus_index][EQ_BAND_MID]; + if (fabs(treble_adj_db) > 0.01f) { + eq[bus_index][EQ_BAND_TREBLE].render(samples_bus->data(), samples_bus->size() / 2, + treble_freq_hz * 2.0 * M_PI / OUTPUT_FREQUENCY, 0.5f, treble_adj_db / 40.0f); + } +} + +void AudioMixer::add_bus_to_master(unsigned bus_index, const vector &samples_bus, vector *samples_out) +{ + assert(samples_bus.size() == samples_out->size()); + assert(samples_bus.size() % 2 == 0); + unsigned num_samples = samples_bus.size() / 2; + if (fabs(fader_volume_db[bus_index] - last_fader_volume_db[bus_index]) > 1e-3) { + // The volume has changed; do a fade over the course of this frame. + // (We might have some numerical issues here, but it seems to sound OK.) + // For the purpose of fading here, the silence floor is set to -90 dB + // (the fader only goes to -84). + float old_volume = from_db(max(last_fader_volume_db[bus_index], -90.0f)); + float volume = from_db(max(fader_volume_db[bus_index], -90.0f)); + + float volume_inc = pow(volume / old_volume, 1.0 / num_samples); + volume = old_volume; + if (bus_index == 0) { + for (unsigned i = 0; i < num_samples; ++i) { + (*samples_out)[i * 2 + 0] = samples_bus[i * 2 + 0] * volume; + (*samples_out)[i * 2 + 1] = samples_bus[i * 2 + 1] * volume; + volume *= volume_inc; + } + } else { + for (unsigned i = 0; i < num_samples; ++i) { + (*samples_out)[i * 2 + 0] += samples_bus[i * 2 + 0] * volume; + (*samples_out)[i * 2 + 1] += samples_bus[i * 2 + 1] * volume; + volume *= volume_inc; + } + } + } else { + float volume = from_db(fader_volume_db[bus_index]); + if (bus_index == 0) { + for (unsigned i = 0; i < num_samples; ++i) { + (*samples_out)[i * 2 + 0] = samples_bus[i * 2 + 0] * volume; + (*samples_out)[i * 2 + 1] = samples_bus[i * 2 + 1] * volume; + } + } else { + for (unsigned i = 0; i < num_samples; ++i) { + (*samples_out)[i * 2 + 0] += samples_bus[i * 2 + 0] * volume; + (*samples_out)[i * 2 + 1] += samples_bus[i * 2 + 1] * volume; + } + } + } + + last_fader_volume_db[bus_index] = fader_volume_db[bus_index]; +} + 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)); + assert(left.size() == right.size()); + const float volume = from_db(fader_volume_db[bus_index]); + const float peak_levels[2] = { + find_peak(left.data(), left.size()) * volume, + find_peak(right.data(), right.size()) * volume + }; + for (unsigned channel = 0; channel < 2; ++channel) { + // Compute the current value, including hold and falloff. + // The constants are borrowed from zita-mu1 by Fons Adriaensen. + static constexpr float hold_sec = 0.5f; + static constexpr float falloff_db_sec = 15.0f; // dB/sec falloff after hold. + float current_peak; + PeakHistory &history = peak_history[bus_index][channel]; + history.historic_peak = max(history.historic_peak, peak_levels[channel]); + if (history.age_seconds < hold_sec) { + current_peak = history.last_peak; + } else { + current_peak = history.last_peak * from_db(-falloff_db_sec * (history.age_seconds - hold_sec)); + } + + // See if we have a new peak to replace the old (possibly falling) one. + if (peak_levels[channel] > current_peak) { + history.last_peak = peak_levels[channel]; + history.age_seconds = 0.0f; // Not 100% correct, but more than good enough given our frame sizes. + current_peak = peak_levels[channel]; + } else { + history.age_seconds += float(left.size()) / OUTPUT_FREQUENCY; + } + history.current_level = peak_levels[channel]; + history.current_peak = current_peak; } } @@ -611,8 +707,14 @@ void AudioMixer::send_audio_level_callback() bus_levels.resize(input_mapping.buses.size()); { lock_guard lock(compressor_mutex); - for (unsigned bus_index = 0; bus_index < bus_r128.size(); ++bus_index) { - bus_levels[bus_index].loudness_lufs = bus_r128[bus_index]->loudness_S(); + for (unsigned bus_index = 0; bus_index < bus_levels.size(); ++bus_index) { + bus_levels[bus_index].current_level_dbfs[0] = to_db(peak_history[bus_index][0].current_level); + bus_levels[bus_index].current_level_dbfs[1] = to_db(peak_history[bus_index][1].current_level); + bus_levels[bus_index].peak_level_dbfs[0] = to_db(peak_history[bus_index][0].current_peak); + bus_levels[bus_index].peak_level_dbfs[1] = to_db(peak_history[bus_index][1].current_peak); + bus_levels[bus_index].historic_peak_dbfs = to_db( + max(peak_history[bus_index][0].historic_peak, + peak_history[bus_index][1].historic_peak)); bus_levels[bus_index].gain_staging_db = gain_staging_db[bus_index]; if (compressor_enabled[bus_index]) { bus_levels[bus_index].compressor_attenuation_db = -to_db(compressor[bus_index]->get_attenuation()); @@ -693,17 +795,6 @@ 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; } @@ -712,3 +803,16 @@ InputMapping AudioMixer::get_input_mapping() const lock_guard lock(audio_mutex); return input_mapping; } + +void AudioMixer::reset_peak(unsigned bus_index) +{ + lock_guard lock(audio_mutex); + for (unsigned channel = 0; channel < 2; ++channel) { + PeakHistory &history = peak_history[bus_index][channel]; + history.current_level = 0.0f; + history.historic_peak = 0.0f; + history.current_peak = 0.0f; + history.last_peak = 0.0f; + history.age_seconds = 0.0f; + } +}