]> git.sesse.net Git - nageru/commitdiff
Implement the EQ in AudioMixer. (No UI yet.)
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 28 Aug 2016 22:55:31 +0000 (00:55 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Wed, 19 Oct 2016 22:55:44 +0000 (00:55 +0200)
audio_mixer.cpp
audio_mixer.h

index 193d221627cfce48738795d5200476bf972d3c76..678f3d9736b870689e6ec5b6bdd5367f6583c4c5 100644 (file)
@@ -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;
        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.
                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<float> 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]);
        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<mutex> lock(compressor_mutex);
 
                {
                        lock_guard<mutex> lock(compressor_mutex);
@@ -531,6 +528,44 @@ vector<float> AudioMixer::get_output(double pts, unsigned num_samples, Resamplin
        return samples_out;
 }
 
        return samples_out;
 }
 
+void AudioMixer::apply_eq(unsigned bus_index, vector<float> *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<float> &samples_bus, vector<float> *samples_out)
 {
        assert(samples_bus.size() == samples_out->size());
 void AudioMixer::add_bus_to_master(unsigned bus_index, const vector<float> &samples_bus, vector<float> *samples_out)
 {
        assert(samples_bus.size() == samples_out->size());
index 88c2f880cac0fee500d087abec37d1ff9eed1230..c332671e0bebd6f385d66b8ddc40fd426fd765ad 100644 (file)
@@ -52,6 +52,13 @@ struct DeviceInfo {
        unsigned num_channels;
 };
 
        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;
 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];
        }
 
                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;
        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<DeviceSpec, DeviceInfo> get_devices_mutex_held() const;
        void reset_resampler_mutex_held(DeviceSpec device_spec);
        void reset_alsa_mutex_held(DeviceSpec device_spec);
        std::map<DeviceSpec, DeviceInfo> get_devices_mutex_held() const;
+       void apply_eq(unsigned bus_index, std::vector<float> *samples_bus);
        void update_meters(const std::vector<float> &samples);
        void add_bus_to_master(unsigned bus_index, const std::vector<float> &samples_bus, std::vector<float> *samples_out);
        void measure_bus_levels(unsigned bus_index, const std::vector<float> &left, const std::vector<float> &right);
        void update_meters(const std::vector<float> &samples);
        void add_bus_to_master(unsigned bus_index, const std::vector<float> &samples_bus, std::vector<float> *samples_out);
        void measure_bus_levels(unsigned bus_index, const std::vector<float> &left, const std::vector<float> &right);
@@ -257,6 +271,7 @@ private:
        std::atomic<float> locut_cutoff_hz;
        StereoFilter locut[MAX_BUSES];  // Default cutoff 120 Hz, 24 dB/oct.
        std::atomic<bool> locut_enabled[MAX_BUSES];
        std::atomic<float> locut_cutoff_hz;
        StereoFilter locut[MAX_BUSES];  // Default cutoff 120 Hz, 24 dB/oct.
        std::atomic<bool> 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;
 
        // 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<float> fader_volume_db[MAX_BUSES] {{ 0.0f }};
        float last_fader_volume_db[MAX_BUSES] { 0.0f };  // Under audio_mutex.
        InputMapping input_mapping;  // Under audio_mutex.
        std::atomic<float> fader_volume_db[MAX_BUSES] {{ 0.0f }};
        float last_fader_volume_db[MAX_BUSES] { 0.0f };  // Under audio_mutex.
+       std::atomic<float> eq_level_db[MAX_BUSES][NUM_EQ_BANDS] {{{ 0.0f }}};
 
        audio_level_callback_t audio_level_callback = nullptr;
        mutable std::mutex audio_measure_mutex;
 
        audio_level_callback_t audio_level_callback = nullptr;
        mutable std::mutex audio_measure_mutex;