X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=audio_mixer.cpp;h=678f3d9736b870689e6ec5b6bdd5367f6583c4c5;hb=1062c5403b57859c219558e736564a3d0bbecfd5;hp=6b7ffeb0d6d28fb1ffdbfa8308ab341970893b20;hpb=54067dbc70999d936adf9d263b5ff2b1efb4dfd0;p=nageru diff --git a/audio_mixer.cpp b/audio_mixer.cpp index 6b7ffeb..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,19 +459,9 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin } } - 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; - } - } - + add_bus_to_master(bus_index, samples_bus, &samples_out); deinterleave_samples(samples_bus, &left, &right); - measure_bus_levels(bus_index, left, right, volume); + measure_bus_levels(bus_index, left, right); } { @@ -541,9 +528,94 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin return samples_out; } -void AudioMixer::measure_bus_levels(unsigned bus_index, const vector &left, const vector &right, float volume) +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) { 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 @@ -555,6 +627,7 @@ void AudioMixer::measure_bus_levels(unsigned bus_index, const vector &lef 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 { @@ -639,6 +712,9 @@ void AudioMixer::send_audio_level_callback() 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()); @@ -727,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; + } +}