diff options
author | Justin Worthe <justin@worthe-it.co.za> | 2017-07-03 21:03:17 +0200 |
---|---|---|
committer | Justin Worthe <justin@worthe-it.co.za> | 2017-07-03 21:03:17 +0200 |
commit | a215f3c16192e8fd4b74bd2a53927763f27d9b8e (patch) | |
tree | 0a8efcdf74fcab7f82f5cb85369822f52fd6f7eb | |
parent | f7b208ff5c3c9d77465e745bc9ded1d87ed32335 (diff) |
Removed FFT chart
I was only using the correlation anyway, so this simplifies a bit
-rw-r--r-- | Cargo.lock | 19 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/gui.rs | 49 | ||||
-rw-r--r-- | src/transforms.rs | 72 |
4 files changed, 2 insertions, 139 deletions
@@ -3,7 +3,6 @@ name = "rusty_microphone" version = "0.1.0" dependencies = [ "cairo-rs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dft 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "gtk 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "portaudio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -61,14 +60,6 @@ dependencies = [ ] [[package]] -name = "dft" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-complex 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "gdk" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -236,14 +227,6 @@ dependencies = [ ] [[package]] -name = "num-complex" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "num-integer" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -311,7 +294,6 @@ dependencies = [ "checksum c_vec 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "aa9e1d9f7d49e289f36f19effbf3d5a5e30163ecf9c7a3c9be94d5374dec5b9a" "checksum cairo-rs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5b3e2dae876fba33394353e985bff24e011a18250cf0021d07b86900b77388b0" "checksum cairo-sys-rs 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e377d5ccba12485dbdd1d459d711b948bbbed867f5808b25e0e2f6c8a45935f6" -"checksum dft 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3883b2ea3b807bcb59f1edfc2170ee82203e324739a80a30274e07a242a27ad2" "checksum gdk 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "151196bd3a35bc157b7dca6ee98bb701dca59439dedd19d8a85b2d8759e0afaf" "checksum gdk-pixbuf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f893bde751ef13bae431c5643021d0b9365b5455ab6b2febbc492bbe431d573b" "checksum gdk-pixbuf-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f04793815a1e18b12de3b5bf41820c64ff84323510ecf3261a7f329eccd59500" @@ -325,7 +307,6 @@ dependencies = [ "checksum gtk-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ecdca043a321a1c19a16ae0efb9fcab6922051711fe238413b80f301e9caf17d" "checksum libc 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)" = "9e030dc72013ed68994d1b2cbf36a94dd0e58418ba949c4b0db7eeb70a7a6352" "checksum num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "bde7c03b09e7c6a301ee81f6ddf66d7a28ec305699e3d3b056d2fc56470e3120" -"checksum num-complex 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c78e054dd19c3fd03419ade63fa661e9c49bb890ce3beb4eee5b7baf93f92f" "checksum num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "fb24d9bfb3f222010df27995441ded1e954f8f69cd35021f6bef02ca9552fb92" "checksum num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "287a1c9969a847055e1122ec0ea7a5c5d6f72aad97934e131c83d5c08ab4e45c" "checksum num-traits 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a16a42856a256b39c6d3484f097f6713e14feacd9bfb02290917904fae46c81c" @@ -5,6 +5,5 @@ authors = ["Justin Worthe <justin.worthe@gmail.com>"] [dependencies] portaudio = "0.7.0" -dft = "0.4.3" gtk = "0.1.1" cairo-rs = "0.1.1" @@ -17,10 +17,8 @@ struct RustyUi { pitch_label: gtk::Label, pitch_error_indicator: gtk::DrawingArea, oscilloscope_chart: gtk::DrawingArea, - freq_chart: gtk::DrawingArea, correlation_chart: gtk::DrawingArea, oscilloscope_toggle_button: gtk::Button, - freq_toggle_button: gtk::Button, correlation_toggle_button: gtk::Button } @@ -35,7 +33,6 @@ struct CrossThreadState { pitch: String, error: f64, signal: Vec<f64>, - freq_spectrum: Vec<::transforms::FrequencyBucket>, correlation: Vec<f64> } @@ -56,7 +53,6 @@ pub fn start_gui() -> Result<(), String> { pitch: String::new(), error: 0.0, signal: Vec::new(), - freq_spectrum: Vec::new(), correlation: Vec::new() })); @@ -68,7 +64,6 @@ pub fn start_gui() -> Result<(), String> { setup_pitch_label_callbacks(state.clone(), cross_thread_state.clone()); setup_pitch_error_indicator_callbacks(state.clone(), cross_thread_state.clone()); setup_oscilloscope_drawing_area_callbacks(state.clone(), cross_thread_state.clone()); - setup_freq_drawing_area_callbacks(state.clone(), cross_thread_state.clone()); setup_correlation_drawing_area_callbacks(state.clone(), cross_thread_state.clone()); setup_chart_visibility_callbacks(state.clone()); @@ -97,8 +92,6 @@ fn create_window(microphones: Vec<(u32, String)>) -> RustyUi { let oscilloscope_toggle_button = gtk::Button::new_with_label("Osc"); hbox.add(&oscilloscope_toggle_button); - let freq_toggle_button = gtk::Button::new_with_label("FFT"); - hbox.add(&freq_toggle_button); let correlation_toggle_button = gtk::Button::new_with_label("Corr"); hbox.add(&correlation_toggle_button); @@ -114,11 +107,6 @@ fn create_window(microphones: Vec<(u32, String)>) -> RustyUi { oscilloscope_chart.set_vexpand(true); vbox.add(&oscilloscope_chart); - let freq_chart = gtk::DrawingArea::new(); - freq_chart.set_size_request(600, 250); - freq_chart.set_vexpand(true); - vbox.add(&freq_chart); - let correlation_chart = gtk::DrawingArea::new(); correlation_chart.set_size_request(600, 250); correlation_chart.set_vexpand(true); @@ -131,10 +119,8 @@ fn create_window(microphones: Vec<(u32, String)>) -> RustyUi { pitch_label: pitch_label, pitch_error_indicator: pitch_error_indicator, oscilloscope_chart: oscilloscope_chart, - freq_chart: freq_chart, correlation_chart: correlation_chart, oscilloscope_toggle_button: oscilloscope_toggle_button, - freq_toggle_button: freq_toggle_button, correlation_toggle_button: correlation_toggle_button } } @@ -182,7 +168,6 @@ fn start_processing_audio(mic_receiver: Receiver<Vec<f64>>, cross_thread_state: }; let signal = ::transforms::align_to_rising_edge(&samples); - let frequency_domain = ::transforms::fft(&samples, ::audio::SAMPLE_RATE); let correlation = ::transforms::correlation(&samples); let fundamental = ::transforms::find_fundamental_frequency_correlation(&samples, ::audio::SAMPLE_RATE); let (pitch, error) = match fundamental { @@ -195,7 +180,6 @@ fn start_processing_audio(mic_receiver: Receiver<Vec<f64>>, cross_thread_state: state.fundamental_frequency = fundamental.unwrap_or(1.0); state.pitch = pitch; state.signal = signal; - state.freq_spectrum = frequency_domain; state.correlation = correlation; state.error = error }, @@ -213,7 +197,6 @@ fn setup_pitch_label_callbacks(state: Rc<RefCell<ApplicationState>>, cross_threa ui.pitch_error_indicator.queue_draw(); ui.oscilloscope_chart.queue_draw(); ui.correlation_chart.queue_draw(); - ui.freq_chart.queue_draw(); gtk::Continue(true) }); @@ -278,31 +261,6 @@ fn setup_oscilloscope_drawing_area_callbacks(state: Rc<RefCell<ApplicationState> }); } -fn setup_freq_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) { - let outer_state = state.clone(); - let ref canvas = outer_state.borrow().ui.freq_chart; - canvas.connect_draw(move |ref canvas, ref context| { - let ref spectrum = cross_thread_state.read().unwrap().freq_spectrum; - let width = canvas.get_allocated_width() as f64; - let height = canvas.get_allocated_height() as f64; - let max = spectrum.iter().map(|x| x.intensity).fold(0.0, |max, x| if max > x { max } else { x }); - let len = spectrum.len() as f64; - - context.new_path(); - context.move_to(0.0, height); - - for (i, bucket) in spectrum.iter().enumerate() { - let x = i as f64 * width / len; - let y = height - (bucket.intensity * height / max); - context.line_to(x, y); - } - - context.stroke(); - - gtk::Inhibit(false) - }); -} - fn setup_correlation_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) { let outer_state = state.clone(); let ref canvas = outer_state.borrow().ui.correlation_chart; @@ -349,7 +307,6 @@ fn setup_correlation_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>> fn setup_chart_visibility_callbacks(state: Rc<RefCell<ApplicationState>>) { let outer_state = state.clone(); let ref oscilloscope_toggle_button = outer_state.borrow().ui.oscilloscope_toggle_button; - let ref freq_toggle_button = outer_state.borrow().ui.freq_toggle_button; let ref correlation_toggle_button = outer_state.borrow().ui.correlation_toggle_button; let oscilloscope_state = state.clone(); @@ -357,12 +314,6 @@ fn setup_chart_visibility_callbacks(state: Rc<RefCell<ApplicationState>>) { let ref chart = oscilloscope_state.borrow().ui.oscilloscope_chart; chart.set_visible(!chart.get_visible()); }); - - let freq_state = state.clone(); - freq_toggle_button.connect_clicked(move |_| { - let ref chart = freq_state.borrow().ui.freq_chart; - chart.set_visible(!chart.get_visible()); - }); let correlation_state = state.clone(); correlation_toggle_button.connect_clicked(move |_| { diff --git a/src/transforms.rs b/src/transforms.rs index eeb175e..229af23 100644 --- a/src/transforms.rs +++ b/src/transforms.rs @@ -1,42 +1,8 @@ -extern crate dft; - -#[derive(Clone, Debug)] -pub struct FrequencyBucket { - pub min_freq: f64, - pub max_freq: f64, - pub intensity: f64 -} - -impl FrequencyBucket { - pub fn ave_freq(&self) -> f64 { - (self.min_freq + self.max_freq) / 2.0 - } -} - pub fn remove_mean_offset(input: &Vec<f64>) -> Vec<f64> { let mean_input = input.iter().sum::<f64>()/input.len() as f64; input.iter().map(|x|x-mean_input).collect() } -pub fn fft(input: &Vec<f64>, sample_rate: f64) -> Vec<FrequencyBucket> { - let mut intensities = remove_mean_offset(&input); - - let frames = intensities.len(); - let plan = dft::Plan::new(dft::Operation::Forward, frames); - let frequency_resolution = sample_rate / 2.0 / frames as f64; - - dft::transform(&mut intensities, &plan); - - intensities.iter().enumerate().map(|(index, &value)| { - let index = index as f64; - FrequencyBucket { - min_freq: index * frequency_resolution, - max_freq: (index+1.0) * frequency_resolution, - intensity: value - } - }).collect() -} - pub fn correlation(input: &Vec<f64>) -> Vec<f64> { let mut correlation = Vec::with_capacity(input.len()); for offset in 0..input.len() { @@ -173,34 +139,6 @@ mod tests { } #[test] - fn fft_on_sine_wave() { - let frequency = 440.0 as f64; //concert A - - let samples = sample_sinusoud(1.0, frequency, 0.0); - let frequency_domain = fft(&samples, SAMPLE_RATE); - let fundamental = find_fundamental_frequency(&frequency_domain).unwrap(); - - assert!((fundamental-frequency).abs() < frequency_resolution(), "expected={}, actual={}", frequency, fundamental); - } - - #[test] - fn fft_on_two_sine_waves() { - //Unfortunately, real signals won't be this neat - let samples1a = sample_sinusoud(2.0, 440.0, 0.0); - let samples2a = sample_sinusoud(1.0, 880.0, 0.0); - let expected_fundamental = 440.0; - - let samples = samples1a.iter().zip(samples2a.iter()) - .map(|(a, b)| a+b) - .collect(); - let frequency_domain = fft(&samples, SAMPLE_RATE); - - let fundamental = find_fundamental_frequency(&frequency_domain).unwrap(); - - assert!((fundamental-expected_fundamental).abs() < frequency_resolution(), "expected_fundamental={}, actual={}", expected_fundamental, fundamental); - } - - #[test] fn correlation_on_sine_wave() { let frequency = 440.0 as f64; //concert A @@ -272,14 +210,7 @@ pub fn hz_to_pitch(hz: f64) -> String { return "< C1".to_string(); } - let mut cents = ((midi_number * 100.0).round() % 100.0) as i32; - if cents >= 50 { - //don't need to adjust pitch here, that's already been done by the round above - cents -= 100; - } - format!("{}{}", name, octave) -// format!("{}{} {:+}", name, octave, cents) } #[test] @@ -304,7 +235,8 @@ fn f5_is_correct() { pub fn align_to_rising_edge(samples: &Vec<f64>) -> Vec<f64> { - samples.iter() + remove_mean_offset(&samples) + .iter() .skip_while(|x| !x.is_sign_negative()) .skip_while(|x| x.is_sign_negative()) .cloned() |