summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJustin Worthe <justin.worthe@gmail.com>2016-11-06 20:44:48 +0200
committerJustin Worthe <justin.worthe@gmail.com>2016-11-06 20:44:48 +0200
commitda2a138787797bbae1c1f030a9ddf660d85f08bd (patch)
tree64b4f1654926518f549420e11d2e516e1d33d0d1 /src
parent75c9880d1a7bb7e54996743989374a45d5a26e49 (diff)
Hooked up printing FFT result to screen
It's wrong, but it's showing it on the GUI. I'm calling it a win.
Diffstat (limited to 'src')
-rw-r--r--src/gui.rs88
-rw-r--r--src/transforms.rs5
2 files changed, 64 insertions, 29 deletions
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#"
<object class="GtkWindow" id="window">
<property name="title">Rusty Microphone</property>
<child>
- <object class="GtkComboBoxText" id="dropdown">
+ <object class="GtkVBox">
+ <child>
+ <object class="GtkComboBoxText" id="dropdown">
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="pitch-label">
+ <property name="label">Hello world</property>
+ </object>
+ </child>
</object>
</child>
</object>
@@ -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(&gtk_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: &gtk::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: &gtk::ComboBoxText, microphones: Vec<(u32, Strin
dropdown.append(Some(format!("{}", index).as_ref()), name.as_ref());
}
}
+
+fn connect_dropdown_choose_microphone(gtk_builder: &gtk::Builder, mic_sender: Sender<Vec<f64>>, state: Rc<RefCell<ApplicationState>>) {
+ let dropdown: gtk::ComboBoxText = gtk_builder.get_object("dropdown").expect("GUI does not contain an object with id 'dropdown'");
+ dropdown.connect_changed(move |dropdown: &gtk::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<Vec<f64>>, pitch_sender: Sender<String>) {
+ 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()
+}