summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJustin Worthe <justin.worthe@gmail.com>2017-01-17 21:08:36 +0200
committerJustin Worthe <justin.worthe@gmail.com>2017-01-17 21:08:36 +0200
commit12d372a646370d01c7f2e0038d98bf04b45c2bed (patch)
treec8a557187af7ca10407b3c8f5d17efe5aee25b55 /src
parentb4f87e573f2ba4acc01af49e0887779d75bcd08d (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/gui.rs86
-rw-r--r--src/transforms.rs7
2 files changed, 83 insertions, 10 deletions
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<pa::Stream<pa::NonBlocking, pa::Input<f32>>>,
freq_spectrum: Vec<::transforms::FrequencyBucket>,
+ correlation: Vec<f64>,
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<Vec<f64>>, state: Rc<Re
});
}
-fn start_processing_audio(mic_receiver: Receiver<Vec<f64>>, pitch_sender: Sender<String>, freq_sender: Sender<Vec<::transforms::FrequencyBucket>>) {
+fn start_processing_audio(mic_receiver: Receiver<Vec<f64>>, pitch_sender: Sender<String>, freq_sender: Sender<Vec<::transforms::FrequencyBucket>>, correlation_sender: Sender<Vec<f64>>) {
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<String>, state: Rc<RefCe
});
}
-fn setup_drawing_area_callbacks(spectrum_receiver: Receiver<Vec<::transforms::FrequencyBucket>>, state: Rc<RefCell<ApplicationState>>) {
+fn setup_freq_drawing_area_callbacks(spectrum_receiver: Receiver<Vec<::transforms::FrequencyBucket>>, state: Rc<RefCell<ApplicationState>>) {
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<Vec<::transforms::Fr
let len = spectrum.len() as f64;
context.new_path();
- context.move_to(0.0, 0.0);
+ context.move_to(0.0, height);
for (i, bucket) in spectrum.iter().enumerate() {
let x = i as f64 * width / len;
@@ -184,6 +199,59 @@ fn setup_frequency_spectrum_callback(spectrum_receiver: Receiver<Vec<::transform
match spectrum {
Some(spectrum) => {
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<Vec<f64>>, state: Rc<RefCell<ApplicationState>>) {
+ 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<Vec<f64>>, state: Rc<RefCell<ApplicationState>>) {
+ 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<FrequencyBucket>) -> Op
Some(max_bucket.ave_freq())
}
-pub fn find_fundamental_frequency_correlation(input: &Vec<f64>, sample_rate: f64) -> Option<f64> {
+pub fn correlation(input: &Vec<f64>) -> Vec<f64> {
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<f64>, sample_rate: f64
}
correlation.push(c);
}
+ correlation
+}
+
+pub fn find_fundamental_frequency_correlation(input: &Vec<f64>, sample_rate: f64) -> Option<f64> {
+ let mut correlation = correlation(&input);
//at offset = 0, we have union, so we want to remove that peak
for offset in 1..correlation.len() {