From bbce5d68e2a6007fc8b023bf694ec058373b718c Mon Sep 17 00:00:00 2001 From: Justin Worthe Date: Tue, 26 Dec 2017 13:04:37 +0200 Subject: Moved overall model updating logic out of the GUI layer This will be useful eventually for sharing a bit more overall application logic with the wasm build. --- Cargo.lock | 20 ++++++++++---------- src/gui.rs | 45 +++++++++++---------------------------------- src/lib.rs | 2 ++ src/model.rs | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 44 deletions(-) create mode 100644 src/model.rs diff --git a/Cargo.lock b/Cargo.lock index 7e72f64..b55058d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,13 +1,3 @@ -[root] -name = "rusty_microphone" -version = "0.1.0" -dependencies = [ - "bencher 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cairo-rs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "gtk 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "portaudio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "atk-sys" version = "0.3.4" @@ -303,6 +293,16 @@ dependencies = [ "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rusty_microphone" +version = "0.1.0" +dependencies = [ + "bencher 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cairo-rs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "gtk 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "portaudio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "winapi" version = "0.2.8" diff --git a/src/gui.rs b/src/gui.rs index c25826a..f0d998e 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -10,6 +10,8 @@ use std::io::Write; use std::thread; use std::sync::mpsc::*; +use model::Model; + const FPS: u32 = 60; struct RustyUi { @@ -28,14 +30,6 @@ struct ApplicationState { ui: RustyUi } -struct CrossThreadState { - fundamental_frequency: Option, - pitch: String, - error: Option, - signal: Vec, - correlation: Vec -} - pub fn start_gui() -> Result<(), String> { let pa = try!(::audio::init().map_err(|e| e.to_string())); let microphones = try!(::audio::get_device_list(&pa).map_err(|e| e.to_string())); @@ -49,13 +43,7 @@ pub fn start_gui() -> Result<(), String> { ui: create_window(microphones, default_microphone) })); - let cross_thread_state = Arc::new(RwLock::new(CrossThreadState { - fundamental_frequency: None, - pitch: String::new(), - error: None, - signal: Vec::new(), - correlation: Vec::new() - })); + let cross_thread_state = Arc::new(RwLock::new(Model::new())); let (mic_sender, mic_receiver) = channel(); @@ -160,28 +148,17 @@ fn start_listening_current_dropdown_value(dropdown: >k::ComboBoxText, mic_send state.borrow_mut().pa_stream = stream; } -fn start_processing_audio(mic_receiver: Receiver>, cross_thread_state: Arc>) { +fn start_processing_audio(mic_receiver: Receiver>, cross_thread_state: Arc>) { thread::spawn(move || { while let Ok(samples) = mic_receiver.recv() { //just in case we hit performance difficulties, clear out the channel while mic_receiver.try_recv().ok() != None {} - let signal = ::transforms::align_to_rising_edge(&samples); - let correlation = ::transforms::correlation(&samples); - let fundamental = ::transforms::find_fundamental_frequency(&samples, ::audio::SAMPLE_RATE); - let pitch = match fundamental { - Some(fundamental) => ::transforms::hz_to_pitch(fundamental), - None => String::new() - }; - let error = fundamental.map(::transforms::hz_to_cents_error); + let new_model = Model::from_signal(&samples, ::audio::SAMPLE_RATE); match cross_thread_state.write() { - Ok(mut state) => { - state.fundamental_frequency = fundamental; - state.pitch = pitch; - state.signal = signal; - state.correlation = correlation; - state.error = error; + Ok(mut model) => { + *model = new_model }, Err(err) => { println!("Error updating cross thread state: {}", err); @@ -191,7 +168,7 @@ fn start_processing_audio(mic_receiver: Receiver>, cross_thread_state: }); } -fn setup_pitch_label_callbacks(state: Rc>, cross_thread_state: Arc>) { +fn setup_pitch_label_callbacks(state: Rc>, cross_thread_state: Arc>) { gtk::timeout_add(1000/FPS, move || { let ui = &state.borrow().ui; if let Ok(cross_thread_state) = cross_thread_state.read() { @@ -206,7 +183,7 @@ fn setup_pitch_label_callbacks(state: Rc>, cross_threa }); } -fn setup_pitch_error_indicator_callbacks(state: Rc>, cross_thread_state: Arc>) { +fn setup_pitch_error_indicator_callbacks(state: Rc>, cross_thread_state: Arc>) { let outer_state = state.clone(); let canvas = &outer_state.borrow().ui.pitch_error_indicator; canvas.connect_draw(move |canvas, context| { @@ -246,7 +223,7 @@ fn setup_pitch_error_indicator_callbacks(state: Rc>, c }); } -fn setup_oscilloscope_drawing_area_callbacks(state: Rc>, cross_thread_state: Arc>) { +fn setup_oscilloscope_drawing_area_callbacks(state: Rc>, cross_thread_state: Arc>) { let outer_state = state.clone(); let canvas = &outer_state.borrow().ui.oscilloscope_chart; canvas.connect_draw(move |canvas, context| { @@ -279,7 +256,7 @@ fn setup_oscilloscope_drawing_area_callbacks(state: Rc }); } -fn setup_correlation_drawing_area_callbacks(state: Rc>, cross_thread_state: Arc>) { +fn setup_correlation_drawing_area_callbacks(state: Rc>, cross_thread_state: Arc>) { let outer_state = state.clone(); let canvas = &outer_state.borrow().ui.correlation_chart; canvas.connect_draw(move |canvas, context| { diff --git a/src/lib.rs b/src/lib.rs index b053e0c..74aeaf1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,3 +14,5 @@ pub mod audio; #[cfg(target_os = "emscripten")] pub mod emscripten_api; + +pub mod model; diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..7fad354 --- /dev/null +++ b/src/model.rs @@ -0,0 +1,35 @@ +use transforms; + +#[derive(Default)] +pub struct Model { + pub fundamental_frequency: Option, + pub pitch: String, + pub error: Option, + pub signal: Vec, + pub correlation: Vec +} + +impl Model { + pub fn new() -> Model { + Model::default() + } + + pub fn from_signal(signal: &[f32], sample_rate: f32) -> Model { + let correlation = transforms::correlation(signal); + let fundamental = transforms::find_fundamental_frequency(signal, sample_rate); + let pitch = fundamental.map_or( + String::new(), + transforms::hz_to_pitch + ); + + let error = fundamental.map(transforms::hz_to_cents_error); + + Model { + fundamental_frequency: fundamental, + pitch: pitch, + error: error, + signal: transforms::align_to_rising_edge(signal), + correlation: correlation + } + } +} -- cgit v1.2.3