Compute the analyzer data for Spectral Compressor
This is not yet shown anywhere.
This commit is contained in:
parent
9647f8f3dd
commit
a18d70401b
|
@ -560,6 +560,22 @@ impl CompressorBank {
|
||||||
) {
|
) {
|
||||||
nih_debug_assert_eq!(buffer.len(), self.log2_freqs.len());
|
nih_debug_assert_eq!(buffer.len(), self.log2_freqs.len());
|
||||||
|
|
||||||
|
// The gain reduction amounts are accumulated in `self.analyzer_input_data`. When processing
|
||||||
|
// the last channel, this data is divided by the channel count, the envelope follower data
|
||||||
|
// is added, and the data is then sent to the editor so it can be displayed.
|
||||||
|
// `analyzer_input_data` contains excess capacity so it can handle any supported window
|
||||||
|
// size, so all operations on it are limited to the actual number of used bins.
|
||||||
|
let num_bins = buffer.len();
|
||||||
|
let num_channels = self.sidechain_spectrum_magnitudes.len();
|
||||||
|
let should_update_analyzer_data = params.editor_state.is_open();
|
||||||
|
if should_update_analyzer_data && channel_idx == 0 {
|
||||||
|
// NOTE: This may briefly show a huge amount of accumulated data when the editor has
|
||||||
|
// just been opened. If this doesn't look too obvious or too jarring this is
|
||||||
|
// probably worth letting it be like this.
|
||||||
|
let analyzer_input_data = self.analyzer_input_data.input_buffer();
|
||||||
|
analyzer_input_data.gain_reduction_db[..num_bins].fill(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
self.update_if_needed(params);
|
self.update_if_needed(params);
|
||||||
match params.threshold.mode.value() {
|
match params.threshold.mode.value() {
|
||||||
ThresholdMode::Internal => {
|
ThresholdMode::Internal => {
|
||||||
|
@ -578,6 +594,44 @@ impl CompressorBank {
|
||||||
self.compress(buffer, channel_idx, params, first_non_dc_bin)
|
self.compress(buffer, channel_idx, params, first_non_dc_bin)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// When processing the last channel we can finalize the spectrum analyzer data and send it
|
||||||
|
// to the editor for display
|
||||||
|
if should_update_analyzer_data && channel_idx == num_channels - 1 {
|
||||||
|
let analyzer_input_data = self.analyzer_input_data.input_buffer();
|
||||||
|
|
||||||
|
// The gain reduction data needs to be averaged, see above
|
||||||
|
let channel_multiplier = (num_channels as f32).recip();
|
||||||
|
for gain_reduction_db in &mut analyzer_input_data.gain_reduction_db[..num_bins] {
|
||||||
|
*gain_reduction_db *= channel_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The spectrum analyzer data has not yet been added
|
||||||
|
assert!(self.envelopes.len() == num_channels);
|
||||||
|
assert!(self.envelopes[0].len() >= num_bins);
|
||||||
|
for (bin_idx, spectrum_data) in analyzer_input_data.spectrum[..num_bins]
|
||||||
|
.iter_mut()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
*spectrum_data = 0.0;
|
||||||
|
for channel_idx in 0..num_channels {
|
||||||
|
// SAFETY: These bounds are already checked
|
||||||
|
*spectrum_data += unsafe {
|
||||||
|
self.envelopes
|
||||||
|
.get_unchecked(channel_idx)
|
||||||
|
.get_unchecked(bin_idx)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
*spectrum_data *= channel_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After filling the object with data it can be sent to the editor. This happens
|
||||||
|
// automatically when using the `.write()` interface, but since `AnalyzerData` contains
|
||||||
|
// a lot of padding and we only use the first `num_bins` of the arrays that would be a
|
||||||
|
// bit wasteful.
|
||||||
|
self.analyzer_input_data.publish();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the sidechain frequency spectrum magnitudes just before a [`process()`][Self::process()]
|
/// Set the sidechain frequency spectrum magnitudes just before a [`process()`][Self::process()]
|
||||||
|
@ -714,15 +768,20 @@ impl CompressorBank {
|
||||||
/// Panics if the buffer does not have the same length as the one that was passed to the last
|
/// Panics if the buffer does not have the same length as the one that was passed to the last
|
||||||
/// `resize()` call.
|
/// `resize()` call.
|
||||||
fn compress(
|
fn compress(
|
||||||
&self,
|
&mut self,
|
||||||
buffer: &mut [Complex32],
|
buffer: &mut [Complex32],
|
||||||
channel_idx: usize,
|
channel_idx: usize,
|
||||||
params: &SpectralCompressorParams,
|
params: &SpectralCompressorParams,
|
||||||
first_non_dc_bin: usize,
|
first_non_dc_bin: usize,
|
||||||
) {
|
) {
|
||||||
|
// The gain reduction values are always added to the arrays stored in this object. This
|
||||||
|
// makes it possible to visualize the gain reduction without a lot of conditionals.
|
||||||
|
let analyzer_input_data = self.analyzer_input_data.input_buffer();
|
||||||
|
|
||||||
let downwards_knee_width_db = params.compressors.downwards.knee_width_db.value();
|
let downwards_knee_width_db = params.compressors.downwards.knee_width_db.value();
|
||||||
let upwards_knee_width_db = params.compressors.upwards.knee_width_db.value();
|
let upwards_knee_width_db = params.compressors.upwards.knee_width_db.value();
|
||||||
|
|
||||||
|
assert!(analyzer_input_data.gain_reduction_db.len() >= buffer.len());
|
||||||
assert!(self.downwards_thresholds_db.len() == buffer.len());
|
assert!(self.downwards_thresholds_db.len() == buffer.len());
|
||||||
assert!(self.downwards_ratios.len() == buffer.len());
|
assert!(self.downwards_ratios.len() == buffer.len());
|
||||||
assert!(self.downwards_knee_parabola_scale.len() == buffer.len());
|
assert!(self.downwards_knee_parabola_scale.len() == buffer.len());
|
||||||
|
@ -787,9 +846,14 @@ impl CompressorBank {
|
||||||
|
|
||||||
// If the comprssed output is -10 dBFS and the envelope follower was at -6 dBFS, then we
|
// If the comprssed output is -10 dBFS and the envelope follower was at -6 dBFS, then we
|
||||||
// want to apply -4 dB of gain to the bin
|
// want to apply -4 dB of gain to the bin
|
||||||
*bin *= util::db_to_gain_fast(
|
let gain_reduction_db = downwards_compressed + upwards_compressed - (envelope_db * 2.0);
|
||||||
downwards_compressed + upwards_compressed - (envelope_db * 2.0),
|
unsafe {
|
||||||
);
|
*analyzer_input_data
|
||||||
|
.gain_reduction_db
|
||||||
|
.get_unchecked_mut(bin_idx) += gain_reduction_db;
|
||||||
|
}
|
||||||
|
|
||||||
|
*bin *= util::db_to_gain_fast(gain_reduction_db);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,12 +865,15 @@ impl CompressorBank {
|
||||||
/// Panics if the buffer does not have the same length as the one that was passed to the last
|
/// Panics if the buffer does not have the same length as the one that was passed to the last
|
||||||
/// `resize()` call.
|
/// `resize()` call.
|
||||||
fn compress_sidechain_match(
|
fn compress_sidechain_match(
|
||||||
&self,
|
&mut self,
|
||||||
buffer: &mut [Complex32],
|
buffer: &mut [Complex32],
|
||||||
channel_idx: usize,
|
channel_idx: usize,
|
||||||
params: &SpectralCompressorParams,
|
params: &SpectralCompressorParams,
|
||||||
first_non_dc_bin: usize,
|
first_non_dc_bin: usize,
|
||||||
) {
|
) {
|
||||||
|
// See `compress()`
|
||||||
|
let analyzer_input_data = self.analyzer_input_data.input_buffer();
|
||||||
|
|
||||||
let downwards_knee_width_db = params.compressors.downwards.knee_width_db.value();
|
let downwards_knee_width_db = params.compressors.downwards.knee_width_db.value();
|
||||||
let upwards_knee_width_db = params.compressors.upwards.knee_width_db.value();
|
let upwards_knee_width_db = params.compressors.upwards.knee_width_db.value();
|
||||||
|
|
||||||
|
@ -815,6 +882,7 @@ impl CompressorBank {
|
||||||
let other_channels_t = params.threshold.sc_channel_link.value() / num_channels;
|
let other_channels_t = params.threshold.sc_channel_link.value() / num_channels;
|
||||||
let this_channel_t = 1.0 - (other_channels_t * (num_channels - 1.0));
|
let this_channel_t = 1.0 - (other_channels_t * (num_channels - 1.0));
|
||||||
|
|
||||||
|
assert!(analyzer_input_data.gain_reduction_db.len() >= buffer.len());
|
||||||
assert!(self.sidechain_spectrum_magnitudes[channel_idx].len() == buffer.len());
|
assert!(self.sidechain_spectrum_magnitudes[channel_idx].len() == buffer.len());
|
||||||
assert!(self.downwards_thresholds_db.len() == buffer.len());
|
assert!(self.downwards_thresholds_db.len() == buffer.len());
|
||||||
assert!(self.downwards_ratios.len() == buffer.len());
|
assert!(self.downwards_ratios.len() == buffer.len());
|
||||||
|
@ -893,9 +961,16 @@ impl CompressorBank {
|
||||||
envelope_db
|
envelope_db
|
||||||
};
|
};
|
||||||
|
|
||||||
*bin *= util::db_to_gain_fast(
|
// If the comprssed output is -10 dBFS and the envelope follower was at -6 dBFS, then we
|
||||||
downwards_compressed + upwards_compressed - (envelope_db * 2.0),
|
// want to apply -4 dB of gain to the bin
|
||||||
);
|
let gain_reduction_db = downwards_compressed + upwards_compressed - (envelope_db * 2.0);
|
||||||
|
unsafe {
|
||||||
|
*analyzer_input_data
|
||||||
|
.gain_reduction_db
|
||||||
|
.get_unchecked_mut(bin_idx) += gain_reduction_db;
|
||||||
|
}
|
||||||
|
|
||||||
|
*bin *= util::db_to_gain_fast(gain_reduction_db);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue