From da2a138787797bbae1c1f030a9ddf660d85f08bd Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Sun, 6 Nov 2016 20:44:48 +0200 Subject: Hooked up printing FFT result to screen It's wrong, but it's showing it on the GUI. I'm calling it a win. --- src/gui.rs | 88 +++++++++++++++++++++++++++++++++++++------------------ src/transforms.rs | 5 ++++ 2 files changed, 64 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/gui.rs b/src/gui.rs index 42e062d..2b79f06 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -3,7 +3,6 @@ use gtk::prelude::*; use std::cell::RefCell; use portaudio as pa; use std::rc::Rc; -use std::sync::Arc; use std::io; use std::io::Write; use std::thread; @@ -14,7 +13,16 @@ const GUI_XML: &'static str = r#" Rusty Microphone - + + + + + + + + Hello world + + @@ -40,36 +48,27 @@ pub fn start_gui() -> Result<(), String> { let gtk_builder = try!(create_window(microphones)); - { - let state_for_dropdown = state.clone(); + connect_dropdown_choose_microphone(>k_builder, mic_sender, state.clone()); + + let (pitch_sender, pitch_receiver) = channel(); + + start_processing_audio(mic_receiver, pitch_sender); - let dropdown: gtk::ComboBoxText = try!( - gtk_builder.get_object("dropdown").ok_or("GUI does not contain an object with id 'dropdown'") - ); - dropdown.connect_changed(move |dropdown: >k::ComboBoxText| { - match state_for_dropdown.borrow_mut().pa_stream { - Some(ref mut stream) => {stream.stop().ok();}, - _ => {} + 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; } - let selected_mic = dropdown.get_active_id().and_then(|id| id.parse().ok()).expect("Dropdown did not change to a valid value"); - let stream = ::audio::start_listening(&state_for_dropdown.borrow().pa, selected_mic, mic_sender.clone()).ok(); - if stream.is_none() { - writeln!(io::stderr(), "Failed to open audio channel").ok(); - } - state_for_dropdown.borrow_mut().pa_stream = stream; - }); - } - - let async_thread = thread::spawn(move || { - for samples in mic_receiver { - let frequency_domain = ::transforms::fft(samples, 44100.0); - - let max_frequency = frequency_domain.iter() - .fold(None as Option<::transforms::FrequencyBucket>, |max, next| - if max.is_none() || max.clone().unwrap().intensity < next.intensity { Some(next.clone()) } else { max } - ).unwrap().max_freq; - println!("{}Hz", max_frequency.floor()); + pitch = next; } + match pitch { + Some(pitch) => {pitch_label.set_label(pitch.as_ref());}, + None => {} + }; + gtk::Continue(true) }); gtk::main(); @@ -103,3 +102,34 @@ fn set_dropdown_items(dropdown: >k::ComboBoxText, microphones: Vec<(u32, Strin dropdown.append(Some(format!("{}", index).as_ref()), name.as_ref()); } } + +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'"); + 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 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(); + } + state.borrow_mut().pa_stream = stream; + }); +} + +fn start_processing_audio(mic_receiver: Receiver>, pitch_sender: Sender) { + thread::spawn(move || { + for samples in mic_receiver { + let frequency_domain = ::transforms::fft(samples, 44100.0); + + let max_frequency = frequency_domain.iter() + .fold(None as Option<::transforms::FrequencyBucket>, |max, next| + if max.is_none() || max.clone().unwrap().intensity < next.intensity { Some(next.clone()) } else { max } + ).unwrap().max_freq; + let pitch = ::transforms::hz_to_pitch(max_frequency); + pitch_sender.send(pitch).ok(); + } + }); +} diff --git a/src/transforms.rs b/src/transforms.rs index 6b491ac..16478df 100644 --- a/src/transforms.rs +++ b/src/transforms.rs @@ -51,3 +51,8 @@ fn fft_on_sine_wave() { assert!(peak.min_freq <= frequency); assert!(peak.max_freq >= frequency); } + +pub fn hz_to_pitch(hz: f64) -> String { + let pitch_number = 49.0 + 12.0 * (hz / 440.0).log2(); + pitch_number.floor().to_string() +} -- cgit v1.2.3