From 12d372a646370d01c7f2e0038d98bf04b45c2bed Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Tue, 17 Jan 2017 21:08:36 +0200 Subject: Added drawing area for correlation, but hid all graphs The current method of sending all that data with channels turns out to be terrible for performance. On the other hand, I need to get the data to the GTK main thread if I want to do anything with it. So I have a conundrum. --- src/gui.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++++++------ src/transforms.rs | 7 ++++- 2 files changed, 83 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/gui.rs b/src/gui.rs index 797a088..89cc644 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -12,13 +12,15 @@ struct RustyUi { window: gtk::Window, dropdown: gtk::ComboBoxText, pitch_label: gtk::Label, - freq_chart: gtk::DrawingArea + freq_chart: gtk::DrawingArea, + correlation_chart: gtk::DrawingArea } struct ApplicationState { pa: pa::PortAudio, pa_stream: Option>>, freq_spectrum: Vec<::transforms::FrequencyBucket>, + correlation: Vec, ui: RustyUi } @@ -32,6 +34,7 @@ pub fn start_gui() -> Result<(), String> { pa: pa, pa_stream: None, freq_spectrum: Vec::new(), + correlation: Vec::new(), ui: create_window(microphones) })); @@ -40,12 +43,14 @@ pub fn start_gui() -> Result<(), String> { let (mic_sender, mic_receiver) = channel(); let (pitch_sender, pitch_receiver) = channel(); let (freq_sender, freq_receiver) = channel(); + let (correlation_sender, correlation_receiver) = channel(); connect_dropdown_choose_microphone(mic_sender, state.clone()); - start_processing_audio(mic_receiver, pitch_sender, freq_sender); + start_processing_audio(mic_receiver, pitch_sender, freq_sender, correlation_sender); setup_pitch_label_callbacks(pitch_receiver, state.clone()); - setup_drawing_area_callbacks(freq_receiver, state.clone()); + setup_freq_drawing_area_callbacks(freq_receiver, state.clone()); + setup_correlation_drawing_area_callbacks(correlation_receiver, state.clone()); gtk::main(); Ok(()) @@ -72,6 +77,12 @@ fn create_window(microphones: Vec<(u32, String)>) -> RustyUi { let freq_chart = gtk::DrawingArea::new(); freq_chart.set_size_request(600, 400); layout_box.add(&freq_chart); + freq_chart.set_no_show_all(true); + + let correlation_chart = gtk::DrawingArea::new(); + correlation_chart.set_size_request(600, 400); + layout_box.add(&correlation_chart); + correlation_chart.set_no_show_all(true); window.show_all(); @@ -79,7 +90,8 @@ fn create_window(microphones: Vec<(u32, String)>) -> RustyUi { window: window, dropdown: dropdown, pitch_label: pitch_label, - freq_chart: freq_chart + freq_chart: freq_chart, + correlation_chart: correlation_chart } } @@ -109,11 +121,14 @@ fn connect_dropdown_choose_microphone(mic_sender: Sender>, state: Rc>, pitch_sender: Sender, freq_sender: Sender>) { +fn start_processing_audio(mic_receiver: Receiver>, pitch_sender: Sender, freq_sender: Sender>, correlation_sender: Sender>) { thread::spawn(move || { for samples in mic_receiver { - let frequency_domain = ::transforms::fft(&samples, 44100.0); - freq_sender.send(frequency_domain.clone()).ok(); + //let frequency_domain = ::transforms::fft(&samples, 44100.0); + //freq_sender.send(frequency_domain).ok(); + + //let correlation = ::transforms::correlation(&samples); + //correlation_sender.send(correlation).ok(); let fundamental = ::transforms::find_fundamental_frequency_correlation(&samples, 44100.0); let pitch = match fundamental { @@ -144,7 +159,7 @@ fn setup_pitch_label_callbacks(pitch_receiver: Receiver, state: Rc>, state: Rc>) { +fn setup_freq_drawing_area_callbacks(spectrum_receiver: Receiver>, state: Rc>) { setup_frequency_spectrum_callback(spectrum_receiver, state.clone()); let outer_state = state.clone(); @@ -157,7 +172,7 @@ fn setup_drawing_area_callbacks(spectrum_receiver: Receiver { state.borrow_mut().freq_spectrum = spectrum; + state.borrow().ui.freq_chart.queue_draw(); + }, + None => {} + }; + gtk::Continue(true) + }); +} + +fn setup_correlation_drawing_area_callbacks(correlation_receiver: Receiver>, state: Rc>) { + setup_correlation_callback(correlation_receiver, state.clone()); + + let outer_state = state.clone(); + let ref canvas = outer_state.borrow().ui.correlation_chart; + canvas.connect_draw(move |ref canvas, ref context| { + let ref correlation = state.borrow().correlation; + if correlation.len() == 0 { + return gtk::Inhibit(false); + } + + let width = canvas.get_allocated_width() as f64; + let height = canvas.get_allocated_height() as f64; + let max = correlation[0]; + let len = correlation.len() as f64; + + context.new_path(); + context.move_to(0.0, height); + + for (i, val) in correlation.iter().enumerate() { + let x = i as f64 * width / len; + let y = height - (val * height / max); + context.line_to(x, y); + } + + context.stroke(); + + gtk::Inhibit(false) + }); +} + +fn setup_correlation_callback(correlation_receiver: Receiver>, state: Rc>) { + gtk::timeout_add(100, move || { + let mut correlation = None; + loop { + let next = correlation_receiver.try_recv().ok(); + if next.is_none() { + break; + } + correlation = next; + } + match correlation { + Some(correlation) => { + state.borrow_mut().correlation = correlation; + state.borrow().ui.correlation_chart.queue_draw(); }, None => {} }; diff --git a/src/transforms.rs b/src/transforms.rs index d84692f..9eee406 100644 --- a/src/transforms.rs +++ b/src/transforms.rs @@ -48,7 +48,7 @@ pub fn find_fundamental_frequency(frequency_domain: &Vec) -> Op Some(max_bucket.ave_freq()) } -pub fn find_fundamental_frequency_correlation(input: &Vec, sample_rate: f64) -> Option { +pub fn correlation(input: &Vec) -> Vec { let mut correlation = Vec::with_capacity(input.len()); for offset in 0..input.len() { let mut c = 0.0; @@ -58,6 +58,11 @@ pub fn find_fundamental_frequency_correlation(input: &Vec, sample_rate: f64 } correlation.push(c); } + correlation +} + +pub fn find_fundamental_frequency_correlation(input: &Vec, sample_rate: f64) -> Option { + let mut correlation = correlation(&input); //at offset = 0, we have union, so we want to remove that peak for offset in 1..correlation.len() { -- cgit v1.2.3