Add an analysis-only function to StftHelper
This commit is contained in:
parent
b06e67bde7
commit
f05d209169
1 changed files with 102 additions and 38 deletions
|
@ -255,7 +255,6 @@ impl<const NUM_SIDECHAIN_INPUTS: usize> StftHelper<NUM_SIDECHAIN_INPUTS> {
|
|||
/// channels as this [`StftHelper`], if the sidechain buffers do not contain the same number of
|
||||
/// samples as the main buffer, or if the window function does not match the block size.
|
||||
///
|
||||
/// TODO: And also introduce that aforementioned read-only process function (`analyze()?`)
|
||||
/// TODO: Add more useful ways to do STFT and other buffered operations. I just went with this
|
||||
/// approach because it's what I needed myself, but generic combinators like this could
|
||||
/// also be useful for other operations.
|
||||
|
@ -323,11 +322,10 @@ impl<const NUM_SIDECHAIN_INPUTS: usize> StftHelper<NUM_SIDECHAIN_INPUTS> {
|
|||
// Copy the input from `main_buffer` to the ring buffer while copying last block's
|
||||
// result from the buffer to `main_buffer`
|
||||
// TODO: This might be able to be sped up a bit with SIMD
|
||||
{
|
||||
|
||||
// For the main buffer
|
||||
for sample_offset in 0..samples_to_process {
|
||||
for channel_idx in 0..num_channels {
|
||||
// let main_buffer = main_buffer.as_slice();
|
||||
let sample = unsafe {
|
||||
main_buffer.get_sample_unchecked_mut(
|
||||
channel_idx,
|
||||
|
@ -373,7 +371,6 @@ impl<const NUM_SIDECHAIN_INPUTS: usize> StftHelper<NUM_SIDECHAIN_INPUTS> {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_processed_samples += samples_to_process;
|
||||
self.current_pos = (self.current_pos + samples_to_process) % block_size;
|
||||
|
@ -428,6 +425,73 @@ impl<const NUM_SIDECHAIN_INPUTS: usize> StftHelper<NUM_SIDECHAIN_INPUTS> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to [`process_overlap_add()`][Self::process_overlap_add()], but without the inverse
|
||||
/// STFT part. `buffer` will only ever be read from. This can be useful for providing FFT data
|
||||
/// for a spectrum analyzer in a plugin GUI. These is still a delay to the analysis equal to the
|
||||
/// blcok size.
|
||||
pub fn process_analyze_only<B, F>(
|
||||
&mut self,
|
||||
buffer: &B,
|
||||
window_function: &[f32],
|
||||
overlap_times: usize,
|
||||
mut analyze_cb: F,
|
||||
) where
|
||||
B: StftInput,
|
||||
F: FnMut(usize, &mut [f32]),
|
||||
{
|
||||
assert_eq!(buffer.num_channels(), self.main_input_ring_buffers.len());
|
||||
assert_eq!(window_function.len(), self.main_input_ring_buffers[0].len());
|
||||
assert!(overlap_times > 0);
|
||||
|
||||
// See `process_overlap_add_sidechain` for an annotated version
|
||||
let main_buffer_len = buffer.num_samples();
|
||||
let num_channels = buffer.num_channels();
|
||||
let block_size = self.main_input_ring_buffers[0].len();
|
||||
let window_interval = (block_size / overlap_times) as i32;
|
||||
let mut already_processed_samples = 0;
|
||||
while already_processed_samples < main_buffer_len {
|
||||
let remaining_samples = main_buffer_len - already_processed_samples;
|
||||
let samples_until_next_window = ((window_interval - self.current_pos as i32 - 1)
|
||||
.rem_euclid(window_interval)
|
||||
+ 1) as usize;
|
||||
let samples_to_process = samples_until_next_window.min(remaining_samples);
|
||||
|
||||
for sample_offset in 0..samples_to_process {
|
||||
for channel_idx in 0..num_channels {
|
||||
let sample = unsafe {
|
||||
buffer.get_sample_unchecked(
|
||||
channel_idx,
|
||||
already_processed_samples + sample_offset,
|
||||
)
|
||||
};
|
||||
let input_ring_buffer_sample = unsafe {
|
||||
self.main_input_ring_buffers
|
||||
.get_unchecked_mut(channel_idx)
|
||||
.get_unchecked_mut(self.current_pos + sample_offset)
|
||||
};
|
||||
*input_ring_buffer_sample = sample;
|
||||
}
|
||||
}
|
||||
|
||||
already_processed_samples += samples_to_process;
|
||||
self.current_pos = (self.current_pos + samples_to_process) % block_size;
|
||||
|
||||
if samples_to_process == samples_until_next_window {
|
||||
for (channel_idx, input_ring_buffer) in
|
||||
self.main_input_ring_buffers.iter().enumerate()
|
||||
{
|
||||
copy_ring_to_scratch_buffer(
|
||||
&mut self.scratch_buffer,
|
||||
self.current_pos,
|
||||
input_ring_buffer,
|
||||
);
|
||||
multiply_with_window(&mut self.scratch_buffer, window_function);
|
||||
analyze_cb(channel_idx, &mut self.scratch_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy data from the the specified ring buffer (borrowed from `self`) to the scratch buffers at
|
||||
|
|
Loading…
Add table
Reference in a new issue