From 44f19c7df64757c5f31ef5f1721806cf7cb1c04b Mon Sep 17 00:00:00 2001 From: Pieter Penninckx Date: Mon, 6 Apr 2020 10:15:10 +0200 Subject: [PATCH 1/6] Add tests for multiple channels. --- src/lib.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ccb0772..98b39cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -306,13 +306,55 @@ fn identity(channels: usize, bins: usize, input: &[Vec], output: &mut [Vec< } } +#[cfg(test)] +fn test_data_is_reconstructed_two_channels( + mut pvoc: PhaseVocoder, + input_samples_left: &[f32], + input_samples_right: &[f32], +) { + let mut output_samples_left = vec![0.0; input_samples_left.len()]; + let mut output_samples_right = vec![0.0; input_samples_right.len()]; + let frame_size = pvoc.num_bins(); + // Pre-padding, not collecting any output. + pvoc.process( + &[&vec![0.0; frame_size], &vec![0.0; frame_size]], + &mut [&mut Vec::new(), &mut Vec::new()], + identity, + ); + // The data itself, collecting some output that we will discard + let mut scratch_left = vec![0.0; frame_size]; + let mut scratch_right = vec![0.0; frame_size]; + pvoc.process( + &[&input_samples_left, &input_samples_right], + &mut [&mut scratch_left, &mut scratch_right], + identity, + ); + // Post-padding and collecting all output + pvoc.process( + &[&vec![0.0; frame_size], &vec![0.0; frame_size]], + &mut [&mut output_samples_left, &mut output_samples_right], + identity, + ); + + assert_ulps_eq!( + input_samples_left, + output_samples_left.as_slice(), + epsilon = 1e-2 + ); + assert_ulps_eq!( + input_samples_right, + output_samples_right.as_slice(), + epsilon = 1e-2 + ); +} + #[cfg(test)] fn test_data_is_reconstructed(mut pvoc: PhaseVocoder, input_samples: &[f32]) { let mut output_samples = vec![0.0; input_samples.len()]; let frame_size = pvoc.num_bins(); // Pre-padding, not collecting any output. pvoc.process(&[&vec![0.0; frame_size]], &mut [&mut Vec::new()], identity); - // The data-itself, collecting some output that we will discard + // The data itself, collecting some output that we will discard let mut scratch = vec![0.0; frame_size]; pvoc.process(&[&input_samples], &mut [&mut scratch], identity); // Post-padding and collecting all output @@ -349,6 +391,15 @@ fn identity_transform_reconstructs_original_data_random_data() { test_data_is_reconstructed(pvoc, &input_samples); } +#[test] +fn identity_transform_reconstructs_original_data_random_data_with_two_channels() { + let pvoc = PhaseVocoder::new(2, 44100.0, 128, 128 / 4); + let input_samples_all = include!("./random_test_data.rs"); + let (input_samples_left, input_samples_right) = + input_samples_all.split_at(input_samples_all.len() / 2); + test_data_is_reconstructed_two_channels(pvoc, &input_samples_left, &input_samples_right); +} + #[test] fn process_works_with_sample_res_equal_to_window() { let mut pvoc = PhaseVocoder::new(1, 44100.0, 256, 256); @@ -358,17 +409,52 @@ fn process_works_with_sample_res_equal_to_window() { pvoc.process(&[&input_samples], &mut [&mut output_samples], identity); } +#[test] +fn process_works_with_sample_res_equal_to_window_two_channels() { + let mut pvoc = PhaseVocoder::new(2, 44100.0, 256, 256); + let input_len = 1024; + let input_samples_left = vec![0.0; input_len]; + let input_samples_right = vec![0.0; input_len]; + let mut output_samples_left = vec![0.0; input_len]; + let mut output_samples_right = vec![0.0; input_len]; + pvoc.process( + &[&input_samples_left, &input_samples_right], + &mut [&mut output_samples_left, &mut output_samples_right], + identity, + ); +} + #[test] fn process_works_when_reading_sample_by_sample() { let mut pvoc = PhaseVocoder::new(1, 44100.0, 8, 2); let input_len = 32; let input_samples = vec![0.0; input_len]; let mut output_samples = vec![0.0; input_len]; - for i in 0..input_samples.len() { + for i in 0..input_len { pvoc.process( - &[&input_samples[dbg!(i)..i + 1]], + &[&input_samples[i..i + 1]], &mut [&mut output_samples], identity, ); } } + +#[test] +fn process_works_when_reading_sample_by_sample_two_channels() { + let mut pvoc = PhaseVocoder::new(2, 44100.0, 8, 2); + let input_len = 32; + let input_samples_left = vec![0.0; input_len]; + let input_samples_right = vec![0.0; input_len]; + let mut output_samples_left = vec![0.0; input_len]; + let mut output_samples_right = vec![0.0; input_len]; + for i in 0..input_len { + pvoc.process( + &[ + &input_samples_left[i..i + 1], + &input_samples_right[i..i + 1], + ], + &mut [&mut output_samples_left, &mut output_samples_right], + identity, + ); + } +} From 7baf08941922e83895dee94b49cc4129ddb7f765 Mon Sep 17 00:00:00 2001 From: Pieter Penninckx Date: Mon, 6 Apr 2020 11:55:48 +0200 Subject: [PATCH 2/6] Separate analysis in different struct. --- src/lib.rs | 222 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 154 insertions(+), 68 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 98b39cd..add350f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,19 +35,130 @@ impl Bin { } } +#[derive(Clone, Copy, Debug)] +struct PhaseVocoderSettings { + sample_rate: f64, + frame_size: usize, + time_res: usize, +} + +impl PhaseVocoderSettings { + fn new(sample_rate: f64, frame_size: usize, time_res: usize) -> Self { + let mut frame_size = frame_size / time_res * time_res; + if frame_size == 0 { + frame_size = time_res; + } + + // If `frame_size == 1`, computing the window would panic. + assert!(frame_size > 1); + + PhaseVocoderSettings { + sample_rate, + frame_size, + time_res, + } + } + + fn phase_to_frequency(&self, bin: usize, phase: f64) -> f64 { + let frame_sizef = self.frame_size as f64; + let freq_per_bin = self.sample_rate / frame_sizef; + let time_resf = self.time_res as f64; + let step_size = frame_sizef / time_resf; + let expect = 2.0 * PI * step_size / frame_sizef; + let mut tmp = phase; + tmp -= (bin as f64) * expect; + let mut qpd = (tmp / PI) as i32; + if qpd >= 0 { + qpd += qpd & 1; + } else { + qpd -= qpd & 1; + } + tmp -= PI * (qpd as f64); + tmp = time_resf * tmp / (2.0 * PI); + tmp = (bin as f64) * freq_per_bin + tmp * freq_per_bin; + tmp + } + + fn frequency_to_phase(&self, freq: f64) -> f64 { + let step_size = self.frame_size as f64 / self.time_res as f64; + 2.0 * PI * freq / self.sample_rate * step_size + } + + pub fn stepsize(&self) -> usize { + self.frame_size / self.time_res + } +} + +pub struct PhaseVocoderAnalysis { + settings: PhaseVocoderSettings, + in_buf: VecDeque, + last_phase: Vec, +} + +impl PhaseVocoderAnalysis { + pub fn new(sample_rate: f64, frame_size: usize, time_res: usize) -> Self { + Self::from_settings(PhaseVocoderSettings::new(sample_rate, frame_size, time_res)) + } + + fn from_settings(settings: PhaseVocoderSettings) -> Self { + Self { + settings, + in_buf: VecDeque::new(), + last_phase: vec![0.0; settings.frame_size], + } + } + + pub fn push_samples(&mut self, samples: &[S]) { + for sample in samples.iter() { + self.in_buf.push_back(sample.to_f64().unwrap()); + } + } + + pub fn pop_samples(&mut self) { + for _ in 0..self.settings.stepsize() { + self.in_buf.pop_front(); + } + } + + pub fn analyse( + &mut self, + forward_fft: &dyn rustfft::FFT, + fft_in: &mut Vec, + fft_out: &mut Vec, + window: &Vec, + analysis_out: &mut Vec, + ) { + // read in + for i in 0..self.settings.frame_size { + fft_in[i] = c64::new(self.in_buf[i] * window[i], 0.0); + } + + forward_fft.process(fft_in, fft_out); + + for i in 0..self.settings.frame_size { + let x = fft_out[i]; + let (amp, phase) = x.to_polar(); + let freq = self + .settings + .phase_to_frequency(i, phase - self.last_phase[i]); + self.last_phase[i] = phase; + + analysis_out[i] = Bin::new(freq, amp * 2.0); + } + } +} + /// A phase vocoder. /// /// Roughly translated from http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/ pub struct PhaseVocoder { channels: usize, - sample_rate: f64, - frame_size: usize, - time_res: usize, + settings: PhaseVocoderSettings, + + analysis: Vec, samples_waiting: usize, - in_buf: Vec>, out_buf: Vec>, - last_phase: Vec>, sum_phase: Vec>, output_accum: Vec>, @@ -83,27 +194,28 @@ impl PhaseVocoder { frame_size: usize, time_res: usize, ) -> PhaseVocoder { - let mut frame_size = frame_size / time_res * time_res; - if frame_size == 0 { - frame_size = time_res; - } + let settings = PhaseVocoderSettings::new(sample_rate, frame_size, time_res); - // If `frame_size == 1`, computing the window would panic. - assert!(frame_size > 1); + Self::from_settings(channels, settings) + } + fn from_settings(channels: usize, settings: PhaseVocoderSettings) -> Self { let mut planner_forward = rustfft::FFTplanner::new(false); let mut planner_backward = rustfft::FFTplanner::new(true); + let mut analysis = Vec::new(); + for _ in 0..channels { + analysis.push(PhaseVocoderAnalysis::from_settings(settings)); + } + + let frame_size = settings.frame_size; + PhaseVocoder { channels, - sample_rate, - frame_size, - time_res, + settings, samples_waiting: 0, - in_buf: vec![VecDeque::new(); channels], out_buf: vec![VecDeque::new(); channels], - last_phase: vec![vec![0.0; frame_size]; channels], sum_phase: vec![vec![0.0; frame_size]; channels], output_accum: vec![VecDeque::new(); channels], @@ -118,6 +230,8 @@ impl PhaseVocoder { fft_out: vec![c64::new(0.0, 0.0); frame_size], analysis_out: vec![vec![Bin::empty(); frame_size]; channels], synthesis_in: vec![vec![Bin::empty(); frame_size]; channels], + + analysis, } } @@ -126,15 +240,15 @@ impl PhaseVocoder { } pub fn num_bins(&self) -> usize { - self.frame_size + self.settings.frame_size } pub fn time_res(&self) -> usize { - self.time_res + self.settings.time_res } pub fn sample_rate(&self) -> f64 { - self.sample_rate + self.settings.sample_rate } /// Reads samples from `input`, processes the samples, then resynthesizes as many samples as @@ -175,18 +289,16 @@ impl PhaseVocoder { // push samples to input queue for chan in 0..input.len() { - for sample in input[chan].iter() { - self.in_buf[chan].push_back(sample.to_f64().unwrap()); - self.samples_waiting += 1; - } + self.analysis[chan].push_samples(&input[chan]); + self.samples_waiting += input[chan].len(); } - while self.samples_waiting >= 2 * self.frame_size * self.channels { - let frame_sizef = self.frame_size as f64; - let time_resf = self.time_res as f64; - let step_size = frame_sizef / time_resf; + while self.samples_waiting >= 2 * self.num_bins() * self.channels { + let frame_sizef = self.num_bins() as f64; + let time_resf = self.time_res() as f64; + let step_size = self.settings.stepsize() as f64; - for _ in 0..self.time_res { + for _ in 0..self.time_res() { // Initialise the synthesis bins to empty bins. // This may be removed in a future release. for synthesis_channel in self.synthesis_in.iter_mut() { @@ -197,35 +309,26 @@ impl PhaseVocoder { // ANALYSIS for chan in 0..self.channels { - // read in - for i in 0..self.frame_size { - self.fft_in[i] = c64::new(self.in_buf[chan][i] * self.window[i], 0.0); - } - - self.forward_fft - .process(&mut self.fft_in, &mut self.fft_out); - - for i in 0..self.frame_size { - let x = self.fft_out[i]; - let (amp, phase) = x.to_polar(); - let freq = self.phase_to_frequency(i, phase - self.last_phase[chan][i]); - self.last_phase[chan][i] = phase; - - self.analysis_out[chan][i] = Bin::new(freq, amp * 2.0); - } + self.analysis[chan].analyse( + self.forward_fft.as_ref(), + &mut self.fft_in, + &mut self.fft_out, + &self.window, + &mut self.analysis_out[chan], + ); } // PROCESSING processor( self.channels, - self.frame_size, + self.num_bins(), &self.analysis_out, &mut self.synthesis_in, ); // SYNTHESIS for chan in 0..self.channels { - for i in 0..self.frame_size { + for i in 0..self.num_bins() { let amp = self.synthesis_in[chan][i].amp; let freq = self.synthesis_in[chan][i].freq; let phase = self.frequency_to_phase(freq); @@ -239,7 +342,7 @@ impl PhaseVocoder { .process(&mut self.fft_in, &mut self.fft_out); // accumulate - for i in 0..self.frame_size { + for i in 0..self.num_bins() { if i == self.output_accum[chan].len() { self.output_accum[chan].push_back(0.0); } @@ -250,11 +353,11 @@ impl PhaseVocoder { // write out for _ in 0..step_size as usize { self.out_buf[chan].push_back(self.output_accum[chan].pop_front().unwrap()); - self.in_buf[chan].pop_front(); } + self.analysis[chan].pop_samples(); } } - self.samples_waiting -= self.frame_size * self.channels; + self.samples_waiting -= self.num_bins() * self.channels; } // pop samples from output queue @@ -272,28 +375,11 @@ impl PhaseVocoder { } pub fn phase_to_frequency(&self, bin: usize, phase: f64) -> f64 { - let frame_sizef = self.frame_size as f64; - let freq_per_bin = self.sample_rate / frame_sizef; - let time_resf = self.time_res as f64; - let step_size = frame_sizef / time_resf; - let expect = 2.0 * PI * step_size / frame_sizef; - let mut tmp = phase; - tmp -= (bin as f64) * expect; - let mut qpd = (tmp / PI) as i32; - if qpd >= 0 { - qpd += qpd & 1; - } else { - qpd -= qpd & 1; - } - tmp -= PI * (qpd as f64); - tmp = time_resf * tmp / (2.0 * PI); - tmp = (bin as f64) * freq_per_bin + tmp * freq_per_bin; - tmp + self.settings.phase_to_frequency(bin, phase) } pub fn frequency_to_phase(&self, freq: f64) -> f64 { - let step_size = self.frame_size as f64 / self.time_res as f64; - 2.0 * PI * freq / self.sample_rate * step_size + self.settings.frequency_to_phase(freq) } } From ca57a0cdf11e1c248a9656400a3571d57871618e Mon Sep 17 00:00:00 2001 From: Pieter Penninckx Date: Mon, 6 Apr 2020 17:00:05 +0200 Subject: [PATCH 3/6] Split off the synthesis in a separate struct. --- src/lib.rs | 142 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 52 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index add350f..50efb05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,7 +84,7 @@ impl PhaseVocoderSettings { 2.0 * PI * freq / self.sample_rate * step_size } - pub fn stepsize(&self) -> usize { + pub fn step_size(&self) -> usize { self.frame_size / self.time_res } } @@ -115,7 +115,7 @@ impl PhaseVocoderAnalysis { } pub fn pop_samples(&mut self) { - for _ in 0..self.settings.stepsize() { + for _ in 0..self.settings.step_size() { self.in_buf.pop_front(); } } @@ -123,10 +123,10 @@ impl PhaseVocoderAnalysis { pub fn analyse( &mut self, forward_fft: &dyn rustfft::FFT, - fft_in: &mut Vec, - fft_out: &mut Vec, - window: &Vec, - analysis_out: &mut Vec, + fft_in: &mut [c64], + fft_out: &mut [c64], + window: &[f64], + analysis_out: &mut [Bin], ) { // read in for i in 0..self.settings.frame_size { @@ -148,6 +148,70 @@ impl PhaseVocoderAnalysis { } } +pub struct PhaseVocoderSynthesis { + settings: PhaseVocoderSettings, + out_buf: VecDeque, + sum_phase: Vec, + output_accum: VecDeque, +} + +impl PhaseVocoderSynthesis { + pub fn new(sample_rate: f64, frame_size: usize, time_res: usize) -> Self { + Self::from_settings(PhaseVocoderSettings::new(sample_rate, frame_size, time_res)) + } + + fn from_settings(settings: PhaseVocoderSettings) -> Self { + PhaseVocoderSynthesis { + settings, + out_buf: VecDeque::new(), + sum_phase: vec![0.0; settings.frame_size], + output_accum: VecDeque::new(), + } + } + + fn synthesize( + &mut self, + backward_fft: &dyn rustfft::FFT, + fft_in: &mut [c64], + fft_out: &mut [c64], + synthesis_in: &[Bin], + window: &[f64], + ) { + let frame_sizef = self.settings.frame_size as f64; + let time_resf = self.settings.time_res as f64; + + for i in 0..self.settings.frame_size { + let amp = synthesis_in[i].amp; + let freq = synthesis_in[i].freq; + let phase = self.settings.frequency_to_phase(freq); + self.sum_phase[i] += phase; + let phase = self.sum_phase[i]; + + fft_in[i] = c64::from_polar(&, &phase); + } + + backward_fft.process(fft_in, fft_out); + + // accumulate + for i in 0..self.settings.frame_size { + if i == self.output_accum.len() { + self.output_accum.push_back(0.0); + } + self.output_accum[i] += window[i] * fft_out[i].re / (frame_sizef * time_resf); + } + + // write out + for _ in 0..self.settings.step_size() { + self.out_buf + .push_back(self.output_accum.pop_front().unwrap()); + } + } + + pub fn pop_sample(&mut self) -> Option { + self.out_buf.pop_front() + } +} + /// A phase vocoder. /// /// Roughly translated from http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/ @@ -156,12 +220,9 @@ pub struct PhaseVocoder { settings: PhaseVocoderSettings, analysis: Vec, + synthesis: Vec, samples_waiting: usize, - out_buf: Vec>, - sum_phase: Vec>, - output_accum: Vec>, - forward_fft: Arc>, backward_fft: Arc>, @@ -204,8 +265,10 @@ impl PhaseVocoder { let mut planner_backward = rustfft::FFTplanner::new(true); let mut analysis = Vec::new(); + let mut synthesis = Vec::new(); for _ in 0..channels { analysis.push(PhaseVocoderAnalysis::from_settings(settings)); + synthesis.push(PhaseVocoderSynthesis::from_settings(settings)); } let frame_size = settings.frame_size; @@ -215,9 +278,6 @@ impl PhaseVocoder { settings, samples_waiting: 0, - out_buf: vec![VecDeque::new(); channels], - sum_phase: vec![vec![0.0; frame_size]; channels], - output_accum: vec![VecDeque::new(); channels], forward_fft: planner_forward.plan_fft(frame_size), backward_fft: planner_backward.plan_fft(frame_size), @@ -232,6 +292,7 @@ impl PhaseVocoder { synthesis_in: vec![vec![Bin::empty(); frame_size]; channels], analysis, + synthesis, } } @@ -294,19 +355,7 @@ impl PhaseVocoder { } while self.samples_waiting >= 2 * self.num_bins() * self.channels { - let frame_sizef = self.num_bins() as f64; - let time_resf = self.time_res() as f64; - let step_size = self.settings.stepsize() as f64; - for _ in 0..self.time_res() { - // Initialise the synthesis bins to empty bins. - // This may be removed in a future release. - for synthesis_channel in self.synthesis_in.iter_mut() { - for bin in synthesis_channel.iter_mut() { - *bin = Bin::empty(); - } - } - // ANALYSIS for chan in 0..self.channels { self.analysis[chan].analyse( @@ -318,6 +367,14 @@ impl PhaseVocoder { ); } + // Initialise the synthesis bins to empty bins. + // This may be removed in a future release. + for synthesis_channel in self.synthesis_in.iter_mut() { + for bin in synthesis_channel.iter_mut() { + *bin = Bin::empty(); + } + } + // PROCESSING processor( self.channels, @@ -328,32 +385,13 @@ impl PhaseVocoder { // SYNTHESIS for chan in 0..self.channels { - for i in 0..self.num_bins() { - let amp = self.synthesis_in[chan][i].amp; - let freq = self.synthesis_in[chan][i].freq; - let phase = self.frequency_to_phase(freq); - self.sum_phase[chan][i] += phase; - let phase = self.sum_phase[chan][i]; - - self.fft_in[i] = c64::from_polar(&, &phase); - } - - self.backward_fft - .process(&mut self.fft_in, &mut self.fft_out); - - // accumulate - for i in 0..self.num_bins() { - if i == self.output_accum[chan].len() { - self.output_accum[chan].push_back(0.0); - } - self.output_accum[chan][i] += - self.window[i] * self.fft_out[i].re / (frame_sizef * time_resf); - } - - // write out - for _ in 0..step_size as usize { - self.out_buf[chan].push_back(self.output_accum[chan].pop_front().unwrap()); - } + self.synthesis[chan].synthesize( + self.backward_fft.as_ref(), + &mut self.fft_in, + &mut self.fft_out, + &self.synthesis_in[chan], + &self.window, + ); self.analysis[chan].pop_samples(); } } @@ -364,7 +402,7 @@ impl PhaseVocoder { let mut n_written = 0; for chan in 0..self.channels { for samp in 0..output[chan].len() { - output[chan][samp] = match self.out_buf[chan].pop_front() { + output[chan][samp] = match self.synthesis[chan].pop_sample() { Some(x) => FromPrimitive::from_f64(x).unwrap(), None => break, }; From 750189fa6d926336b01715588da10e5f5cdfd032 Mon Sep 17 00:00:00 2001 From: Pieter Penninckx Date: Tue, 7 Apr 2020 09:33:21 +0200 Subject: [PATCH 4/6] Move popping of samples for analysis into 'analyse' method. --- src/lib.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 50efb05..12b4f12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,12 +114,6 @@ impl PhaseVocoderAnalysis { } } - pub fn pop_samples(&mut self) { - for _ in 0..self.settings.step_size() { - self.in_buf.pop_front(); - } - } - pub fn analyse( &mut self, forward_fft: &dyn rustfft::FFT, @@ -145,6 +139,10 @@ impl PhaseVocoderAnalysis { analysis_out[i] = Bin::new(freq, amp * 2.0); } + + for _ in 0..self.settings.step_size() { + self.in_buf.pop_front(); + } } } @@ -392,7 +390,6 @@ impl PhaseVocoder { &self.synthesis_in[chan], &self.window, ); - self.analysis[chan].pop_samples(); } } self.samples_waiting -= self.num_bins() * self.channels; From 931b1811d2c15bd7d1bd16e75c3ce98406086805 Mon Sep 17 00:00:00 2001 From: Pieter Penninckx Date: Tue, 7 Apr 2020 09:44:38 +0200 Subject: [PATCH 5/6] Allocate memory for the VecDeques during initialisation. --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 12b4f12..b271aa6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -103,12 +103,12 @@ impl PhaseVocoderAnalysis { fn from_settings(settings: PhaseVocoderSettings) -> Self { Self { settings, - in_buf: VecDeque::new(), + in_buf: VecDeque::with_capacity(settings.frame_size), last_phase: vec![0.0; settings.frame_size], } } - pub fn push_samples(&mut self, samples: &[S]) { + pub fn push_samples(&mut self, samples: &[S]) { for sample in samples.iter() { self.in_buf.push_back(sample.to_f64().unwrap()); } @@ -161,9 +161,9 @@ impl PhaseVocoderSynthesis { fn from_settings(settings: PhaseVocoderSettings) -> Self { PhaseVocoderSynthesis { settings, - out_buf: VecDeque::new(), + out_buf: VecDeque::with_capacity(settings.step_size()), sum_phase: vec![0.0; settings.frame_size], - output_accum: VecDeque::new(), + output_accum: VecDeque::with_capacity(settings.frame_size), } } From f88066a403ef37b0022d9694a8d9d7c12a90bec1 Mon Sep 17 00:00:00 2001 From: Pieter Penninckx Date: Fri, 10 Apr 2020 11:02:05 +0200 Subject: [PATCH 6/6] Return true or false from analyse method. --- src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index b271aa6..8d2d24d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,8 @@ impl PhaseVocoderAnalysis { } } + /// Returns `true` when data has been written to the `analysis_out` parameter. + /// Returns `false` when there are not enough samples available. pub fn analyse( &mut self, forward_fft: &dyn rustfft::FFT, @@ -121,7 +123,11 @@ impl PhaseVocoderAnalysis { fft_out: &mut [c64], window: &[f64], analysis_out: &mut [Bin], - ) { + ) -> bool { + if self.in_buf.len() < self.settings.frame_size { + return false; + } + // read in for i in 0..self.settings.frame_size { fft_in[i] = c64::new(self.in_buf[i] * window[i], 0.0); @@ -143,6 +149,7 @@ impl PhaseVocoderAnalysis { for _ in 0..self.settings.step_size() { self.in_buf.pop_front(); } + true } }