X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=audio_mixer.cpp;h=a0628fe1f68a647675b09f998a4041a708cfffca;hb=7b1345be2443e68dd5d2d8687339a7a3bc66078e;hp=962877ed4647cf986e968e5868b4652608e89c7f;hpb=5cb4274907d32fb8946558988461224196c2be59;p=nageru diff --git a/audio_mixer.cpp b/audio_mixer.cpp index 962877e..a0628fe 100644 --- a/audio_mixer.cpp +++ b/audio_mixer.cpp @@ -167,34 +167,39 @@ 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. - compressor_enabled[bus_index] = global_flags.compressor_enabled; level_compressor[bus_index].reset(new StereoCompressor(OUTPUT_FREQUENCY)); - level_compressor_enabled[bus_index] = global_flags.gain_staging_auto; + + set_bus_settings(bus_index, get_default_bus_settings()); } set_limiter_enabled(global_flags.limiter_enabled); set_final_makeup_gain_auto(global_flags.final_makeup_gain_auto); - - // Generate a very simple, default input mapping. - InputMapping::Bus input; - input.name = "Main"; - input.device.type = InputSourceType::CAPTURE_CARD; - input.device.index = 0; - input.source_channel[0] = 0; - input.source_channel[1] = 1; + alsa_pool.init(); InputMapping new_input_mapping; - new_input_mapping.buses.push_back(input); - set_input_mapping(new_input_mapping); + if (!global_flags.input_mapping_filename.empty()) { + if (!load_input_mapping_from_file(get_devices(), + global_flags.input_mapping_filename, + &new_input_mapping)) { + fprintf(stderr, "Failed to load input mapping from '%s', exiting.\n", + global_flags.input_mapping_filename.c_str()); + exit(1); + } + } else { + // Generate a very simple, default input mapping. + InputMapping::Bus input; + input.name = "Main"; + input.device.type = InputSourceType::CAPTURE_CARD; + input.device.index = 0; + input.source_channel[0] = 0; + input.source_channel[1] = 1; - alsa_pool.init(); + new_input_mapping.buses.push_back(input); + } + set_input_mapping(new_input_mapping); r128.init(2, OUTPUT_FREQUENCY); r128.integr_start(); @@ -313,6 +318,51 @@ bool AudioMixer::silence_card(DeviceSpec device_spec, bool silence) return true; } +AudioMixer::BusSettings AudioMixer::get_default_bus_settings() +{ + BusSettings settings; + settings.fader_volume_db = 0.0f; + settings.locut_enabled = global_flags.locut_enabled; + for (unsigned band_index = 0; band_index < NUM_EQ_BANDS; ++band_index) { + settings.eq_level_db[band_index] = 0.0f; + } + settings.gain_staging_db = global_flags.initial_gain_staging_db; + settings.level_compressor_enabled = global_flags.gain_staging_auto; + settings.compressor_threshold_dbfs = ref_level_dbfs - 12.0f; // -12 dB. + settings.compressor_enabled = global_flags.compressor_enabled; + return settings; +} + +AudioMixer::BusSettings AudioMixer::get_bus_settings(unsigned bus_index) const +{ + lock_guard lock(audio_mutex); + BusSettings settings; + settings.fader_volume_db = fader_volume_db[bus_index]; + settings.locut_enabled = locut_enabled[bus_index]; + for (unsigned band_index = 0; band_index < NUM_EQ_BANDS; ++band_index) { + settings.eq_level_db[band_index] = eq_level_db[bus_index][band_index]; + } + settings.gain_staging_db = gain_staging_db[bus_index]; + settings.level_compressor_enabled = level_compressor_enabled[bus_index]; + settings.compressor_threshold_dbfs = compressor_threshold_dbfs[bus_index]; + settings.compressor_enabled = compressor_enabled[bus_index]; + return settings; +} + +void AudioMixer::set_bus_settings(unsigned bus_index, const AudioMixer::BusSettings &settings) +{ + lock_guard lock(audio_mutex); + fader_volume_db[bus_index] = settings.fader_volume_db; + locut_enabled[bus_index] = settings.locut_enabled; + for (unsigned band_index = 0; band_index < NUM_EQ_BANDS; ++band_index) { + eq_level_db[bus_index][band_index] = settings.eq_level_db[band_index]; + } + gain_staging_db[bus_index] = settings.gain_staging_db; + level_compressor_enabled[bus_index] = settings.level_compressor_enabled; + compressor_threshold_dbfs[bus_index] = settings.compressor_threshold_dbfs; + compressor_enabled[bus_index] = settings.compressor_enabled; +} + AudioMixer::AudioDevice *AudioMixer::find_audio_device(DeviceSpec device) { switch (device.type) { @@ -533,6 +583,36 @@ vector AudioMixer::get_output(double pts, unsigned num_samples, Resamplin return samples_out; } +namespace { + +void apply_filter_fade(StereoFilter *filter, float *data, unsigned num_samples, float cutoff_hz, float db, float last_db) +{ + // A granularity of 32 samples is an okay tradeoff between speed and + // smoothness; recalculating the filters is pretty expensive, so it's + // good that we don't do this all the time. + static constexpr unsigned filter_granularity_samples = 32; + + const float cutoff_linear = cutoff_hz * 2.0 * M_PI / OUTPUT_FREQUENCY; + if (fabs(db - last_db) < 1e-3) { + // Constant over this frame. + if (fabs(db) > 0.01f) { + filter->render(data, num_samples, cutoff_linear, 0.5f, db / 40.0f); + } + } else { + // We need to do a fade. (Rounding up avoids division by zero.) + unsigned num_blocks = (num_samples + filter_granularity_samples - 1) / filter_granularity_samples; + const float inc_db_norm = (db - last_db) / 40.0f / num_blocks; + float db_norm = db / 40.0f; + for (size_t i = 0; i < num_samples; i += filter_granularity_samples) { + size_t samples_this_block = std::min(num_samples - i, filter_granularity_samples); + filter->render(data + i * 2, samples_this_block, cutoff_linear, 0.5f, db_norm); + db_norm += inc_db_norm; + } + } +} + +} // namespace + void AudioMixer::apply_eq(unsigned bus_index, vector *samples_bus) { constexpr float bass_freq_hz = 200.0f; @@ -551,24 +631,43 @@ void AudioMixer::apply_eq(unsigned bus_index, vector *samples_bus) // 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]); + // + // If any part of the EQ has changed appreciably since last frame, + // we fade smoothly during the course of this frame. + const float bass_db = eq_level_db[bus_index][EQ_BAND_BASS]; + const float mid_db = eq_level_db[bus_index][EQ_BAND_MID]; + const float treble_db = eq_level_db[bus_index][EQ_BAND_TREBLE]; + + const float last_bass_db = last_eq_level_db[bus_index][EQ_BAND_BASS]; + const float last_mid_db = last_eq_level_db[bus_index][EQ_BAND_MID]; + const float last_treble_db = last_eq_level_db[bus_index][EQ_BAND_TREBLE]; + + assert(samples_bus->size() % 2 == 0); + const unsigned num_samples = samples_bus->size() / 2; + + if (fabs(mid_db - last_mid_db) < 1e-3) { + // Constant over this frame. + const float gain = from_db(mid_db); for (size_t i = 0; i < samples_bus->size(); ++i) { - (*samples_bus)[i] *= g; + (*samples_bus)[i] *= gain; + } + } else { + // We need to do a fade. + float gain = from_db(last_mid_db); + const float gain_inc = pow(from_db(mid_db - last_mid_db), 1.0 / num_samples); + for (size_t i = 0; i < num_samples; ++i) { + (*samples_bus)[i * 2 + 0] *= gain; + (*samples_bus)[i * 2 + 1] *= gain; + gain *= gain_inc; } } - 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); - } + apply_filter_fade(&eq[bus_index][EQ_BAND_BASS], samples_bus->data(), num_samples, bass_freq_hz, bass_db - mid_db, last_bass_db - last_mid_db); + apply_filter_fade(&eq[bus_index][EQ_BAND_TREBLE], samples_bus->data(), num_samples, treble_freq_hz, treble_db - mid_db, last_treble_db - last_mid_db); - 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); - } + last_eq_level_db[bus_index][EQ_BAND_BASS] = bass_db; + last_eq_level_db[bus_index][EQ_BAND_MID] = mid_db; + last_eq_level_db[bus_index][EQ_BAND_TREBLE] = treble_db; } void AudioMixer::add_bus_to_master(unsigned bus_index, const vector &samples_bus, vector *samples_out)