From 58eb60171cf3d58aec043a4d05830af720f5eb52 Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Thu, 12 Jan 2017 10:15:23 +0200 Subject: Started refactoring gui creation to include drawing area The drawing area will eventually be used for a debugging graph of the frequency spectrum. --- src/gui.rs | 138 ++++++++++++++++++++++++++++++++++++------------------------- src/lib.rs | 2 +- 2 files changed, 82 insertions(+), 58 deletions(-) (limited to 'src') diff --git a/src/gui.rs b/src/gui.rs index 5501e33..f116c45 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -8,26 +8,12 @@ use std::io::Write; use std::thread; use std::sync::mpsc::*; -const GUI_XML: &'static str = r#" - - - Rusty Microphone - - - - - - - - - Hello world - - - - - - -"#; +struct RustyUi { + window: gtk::Window, + dropdown: gtk::ComboBoxText, + pitch_label: gtk::Label, + freq_chart: gtk::DrawingArea +} struct ApplicationState { pa: pa::PortAudio, @@ -46,55 +32,52 @@ pub fn start_gui() -> Result<(), String> { try!(gtk::init().map_err(|_| "Failed to initialize GTK.")); - let gtk_builder = try!(create_window(microphones)); + let ui = create_window(microphones); - connect_dropdown_choose_microphone(>k_builder, mic_sender, state.clone()); + connect_dropdown_choose_microphone(&ui.dropdown, mic_sender, state.clone()); let (pitch_sender, pitch_receiver) = channel(); + let (freq_sender, freq_receiver) = channel(); - start_processing_audio(mic_receiver, pitch_sender); - - let pitch_label: gtk::Label = gtk_builder.get_object("pitch-label").expect("GUI does not contain an object with id 'pitch-label'"); - gtk::timeout_add(100, move || { - let mut pitch = None; - loop { - let next = pitch_receiver.try_recv().ok(); - if next.is_none() { - break; - } - pitch = next; - } - match pitch { - Some(pitch) => {pitch_label.set_label(pitch.as_ref());}, - None => {} - }; - gtk::Continue(true) - }); + start_processing_audio(mic_receiver, pitch_sender, freq_sender); + + setup_pitch_label_callbacks(ui.pitch_label, pitch_receiver); + setup_drawing_area_callbacks(ui.freq_chart, freq_receiver); gtk::main(); Ok(()) } -fn create_window(microphones: Vec<(u32, String)>) -> Result { - let gtk_builder = gtk::Builder::new_from_string(GUI_XML); - let window: gtk::Window = try!( - gtk_builder.get_object("window") - .ok_or("GUI does not contain an object with id 'window'") - ); - window.set_default_size(300, 300); +fn create_window(microphones: Vec<(u32, String)>) -> RustyUi { + let window = gtk::Window::new(gtk::WindowType::Toplevel); + window.set_title("Rusty Microphone"); window.connect_delete_event(|_, _| { gtk::main_quit(); Inhibit(false) }); - window.show_all(); - let dropdown: gtk::ComboBoxText = try!( - gtk_builder.get_object("dropdown") - .ok_or("GUI does not contain an object with id 'dropdown'") - ); + let layout_box = gtk::Box::new(gtk::Orientation::Vertical, 5); + window.add(&layout_box); + + let dropdown = gtk::ComboBoxText::new(); set_dropdown_items(&dropdown, microphones); + layout_box.add(&dropdown); + + let pitch_label = gtk::Label::new(None); + layout_box.add(&pitch_label); - Ok(gtk_builder) + let freq_chart = gtk::DrawingArea::new(); + freq_chart.set_size_request(600, 400); + layout_box.add(&freq_chart); + + window.show_all(); + + RustyUi { + window: window, + dropdown: dropdown, + pitch_label: pitch_label, + freq_chart: freq_chart + } } fn set_dropdown_items(dropdown: >k::ComboBoxText, microphones: Vec<(u32, String)>) { @@ -103,14 +86,16 @@ fn set_dropdown_items(dropdown: >k::ComboBoxText, microphones: Vec<(u32, Strin } } -fn connect_dropdown_choose_microphone(gtk_builder: >k::Builder, mic_sender: Sender>, state: Rc>) { - let dropdown: gtk::ComboBoxText = gtk_builder.get_object("dropdown").expect("GUI does not contain an object with id 'dropdown'"); +fn connect_dropdown_choose_microphone(dropdown: >k::ComboBoxText, mic_sender: Sender>, state: Rc>) { dropdown.connect_changed(move |dropdown: >k::ComboBoxText| { match state.borrow_mut().pa_stream { Some(ref mut stream) => {stream.stop().ok();}, _ => {} } - let selected_mic = dropdown.get_active_id().and_then(|id| id.parse().ok()).expect("Dropdown did not change to a valid value"); + let selected_mic = match dropdown.get_active_id().and_then(|id| id.parse().ok()) { + Some(mic) => mic, + None => {return;} + }; let stream = ::audio::start_listening(&state.borrow().pa, selected_mic, mic_sender.clone()).ok(); if stream.is_none() { writeln!(io::stderr(), "Failed to open audio channel").ok(); @@ -119,10 +104,11 @@ fn connect_dropdown_choose_microphone(gtk_builder: >k::Builder, mic_sender: Se }); } -fn start_processing_audio(mic_receiver: Receiver>, pitch_sender: Sender) { +fn start_processing_audio(mic_receiver: Receiver>, pitch_sender: Sender, freq_sender: Sender>) { thread::spawn(move || { for samples in mic_receiver { let frequency_domain = ::transforms::fft(samples, 44100.0); + freq_sender.send(frequency_domain.clone()); let fundamental = ::transforms::find_fundamental_frequency(&frequency_domain); let pitch = match fundamental { Some(fundamental) => ::transforms::hz_to_pitch(fundamental), @@ -132,3 +118,41 @@ fn start_processing_audio(mic_receiver: Receiver>, pitch_sender: Sender } }); } + +fn setup_pitch_label_callbacks(pitch_label: gtk::Label, pitch_receiver: Receiver) { + gtk::timeout_add(100, move || { + let mut pitch = None; + loop { + let next = pitch_receiver.try_recv().ok(); + if next.is_none() { + break; + } + pitch = next; + } + match pitch { + Some(pitch) => {pitch_label.set_label(pitch.as_ref());}, + None => {} + }; + gtk::Continue(true) + }); +} + +fn setup_drawing_area_callbacks(canvas: gtk::DrawingArea, freq_receiver: Receiver>) { + canvas.connect_draw(move |ref canvas, ref context| { + let mut last_signal = Vec::new(); + loop { + let next = freq_receiver.try_recv().ok(); + if next.is_none() { + break; + } + last_signal = next.unwrap(); + } + + context.new_path(); + context.move_to(0.0, 0.0); + context.line_to(canvas.get_allocated_width() as f64, canvas.get_allocated_height() as f64); + context.stroke(); + + gtk::Inhibit(false) + }); +} diff --git a/src/lib.rs b/src/lib.rs index b361502..45b1f91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ extern crate gtk; -extern crate gdk; +extern crate cairo; extern crate portaudio; pub mod transforms; -- cgit v1.2.3