From: Steinar H. Gunderson Date: Sun, 28 Aug 2016 22:55:31 +0000 (+0200) Subject: Implement the EQ in AudioMixer. (No UI yet.) X-Git-Tag: 1.4.0~67 X-Git-Url: https://git.sesse.net/?p=nageru;a=commitdiff_plain;h=1062c5403b57859c219558e736564a3d0bbecfd5 Implement the EQ in AudioMixer. (No UI yet.) --- diff --git a/audio_mixer.cpp b/audio_mixer.cpp index 193d221..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); @@ -531,6 +528,44 @@ 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()); diff --git a/audio_mixer.h b/audio_mixer.h index 88c2f88..c332671 100644 --- a/audio_mixer.h +++ b/audio_mixer.h @@ -52,6 +52,13 @@ struct DeviceInfo { unsigned num_channels; }; +enum EQBand { + EQ_BAND_BASS = 0, + EQ_BAND_MID, + EQ_BAND_TREBLE, + NUM_EQ_BANDS +}; + static inline uint64_t DeviceSpec_to_key(const DeviceSpec &device_spec) { return (uint64_t(device_spec.type) << 32) | device_spec.index; @@ -111,6 +118,12 @@ public: return locut_enabled[bus]; } + void set_eq(unsigned bus_index, EQBand band, float db_gain) + { + assert(band >= 0 && band < NUM_EQ_BANDS); + eq_level_db[bus_index][band] = db_gain; + } + float get_limiter_threshold_dbfs() const { return limiter_threshold_dbfs; @@ -239,6 +252,7 @@ private: void reset_resampler_mutex_held(DeviceSpec device_spec); void reset_alsa_mutex_held(DeviceSpec device_spec); std::map get_devices_mutex_held() const; + void apply_eq(unsigned bus_index, std::vector *samples_bus); void update_meters(const std::vector &samples); void add_bus_to_master(unsigned bus_index, const std::vector &samples_bus, std::vector *samples_out); void measure_bus_levels(unsigned bus_index, const std::vector &left, const std::vector &right); @@ -257,6 +271,7 @@ private: std::atomic locut_cutoff_hz; StereoFilter locut[MAX_BUSES]; // Default cutoff 120 Hz, 24 dB/oct. std::atomic locut_enabled[MAX_BUSES]; + StereoFilter eq[MAX_BUSES][NUM_EQ_BANDS]; // The one for EQBand::MID isn't actually used (see comments in apply_eq()). // First compressor; takes us up to about -12 dBFS. mutable std::mutex compressor_mutex; @@ -290,6 +305,7 @@ private: InputMapping input_mapping; // Under audio_mutex. std::atomic fader_volume_db[MAX_BUSES] {{ 0.0f }}; float last_fader_volume_db[MAX_BUSES] { 0.0f }; // Under audio_mutex. + std::atomic eq_level_db[MAX_BUSES][NUM_EQ_BANDS] {{{ 0.0f }}}; audio_level_callback_t audio_level_callback = nullptr; mutable std::mutex audio_measure_mutex;