summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJustin Worthe <justin@worthe-it.co.za>2017-07-03 21:03:17 +0200
committerJustin Worthe <justin@worthe-it.co.za>2017-07-03 21:03:17 +0200
commita215f3c16192e8fd4b74bd2a53927763f27d9b8e (patch)
tree0a8efcdf74fcab7f82f5cb85369822f52fd6f7eb /src
parentf7b208ff5c3c9d77465e745bc9ded1d87ed32335 (diff)
Removed FFT chart
I was only using the correlation anyway, so this simplifies a bit
Diffstat (limited to 'src')
-rw-r--r--src/gui.rs49
-rw-r--r--src/transforms.rs72
2 files changed, 2 insertions, 119 deletions
diff --git a/src/gui.rs b/src/gui.rs
index 41668a3..595872d 100644
--- a/src/gui.rs
+++ b/src/gui.rs
@@ -17,10 +17,8 @@ struct RustyUi {
pitch_label: gtk::Label,
pitch_error_indicator: gtk::DrawingArea,
oscilloscope_chart: gtk::DrawingArea,
- freq_chart: gtk::DrawingArea,
correlation_chart: gtk::DrawingArea,
oscilloscope_toggle_button: gtk::Button,
- freq_toggle_button: gtk::Button,
correlation_toggle_button: gtk::Button
}
@@ -35,7 +33,6 @@ struct CrossThreadState {
pitch: String,
error: f64,
signal: Vec<f64>,
- freq_spectrum: Vec<::transforms::FrequencyBucket>,
correlation: Vec<f64>
}
@@ -56,7 +53,6 @@ pub fn start_gui() -> Result<(), String> {
pitch: String::new(),
error: 0.0,
signal: Vec::new(),
- freq_spectrum: Vec::new(),
correlation: Vec::new()
}));
@@ -68,7 +64,6 @@ pub fn start_gui() -> Result<(), String> {
setup_pitch_label_callbacks(state.clone(), cross_thread_state.clone());
setup_pitch_error_indicator_callbacks(state.clone(), cross_thread_state.clone());
setup_oscilloscope_drawing_area_callbacks(state.clone(), cross_thread_state.clone());
- setup_freq_drawing_area_callbacks(state.clone(), cross_thread_state.clone());
setup_correlation_drawing_area_callbacks(state.clone(), cross_thread_state.clone());
setup_chart_visibility_callbacks(state.clone());
@@ -97,8 +92,6 @@ fn create_window(microphones: Vec<(u32, String)>) -> RustyUi {
let oscilloscope_toggle_button = gtk::Button::new_with_label("Osc");
hbox.add(&oscilloscope_toggle_button);
- let freq_toggle_button = gtk::Button::new_with_label("FFT");
- hbox.add(&freq_toggle_button);
let correlation_toggle_button = gtk::Button::new_with_label("Corr");
hbox.add(&correlation_toggle_button);
@@ -114,11 +107,6 @@ fn create_window(microphones: Vec<(u32, String)>) -> RustyUi {
oscilloscope_chart.set_vexpand(true);
vbox.add(&oscilloscope_chart);
- let freq_chart = gtk::DrawingArea::new();
- freq_chart.set_size_request(600, 250);
- freq_chart.set_vexpand(true);
- vbox.add(&freq_chart);
-
let correlation_chart = gtk::DrawingArea::new();
correlation_chart.set_size_request(600, 250);
correlation_chart.set_vexpand(true);
@@ -131,10 +119,8 @@ fn create_window(microphones: Vec<(u32, String)>) -> RustyUi {
pitch_label: pitch_label,
pitch_error_indicator: pitch_error_indicator,
oscilloscope_chart: oscilloscope_chart,
- freq_chart: freq_chart,
correlation_chart: correlation_chart,
oscilloscope_toggle_button: oscilloscope_toggle_button,
- freq_toggle_button: freq_toggle_button,
correlation_toggle_button: correlation_toggle_button
}
}
@@ -182,7 +168,6 @@ fn start_processing_audio(mic_receiver: Receiver<Vec<f64>>, cross_thread_state:
};
let signal = ::transforms::align_to_rising_edge(&samples);
- let frequency_domain = ::transforms::fft(&samples, ::audio::SAMPLE_RATE);
let correlation = ::transforms::correlation(&samples);
let fundamental = ::transforms::find_fundamental_frequency_correlation(&samples, ::audio::SAMPLE_RATE);
let (pitch, error) = match fundamental {
@@ -195,7 +180,6 @@ fn start_processing_audio(mic_receiver: Receiver<Vec<f64>>, cross_thread_state:
state.fundamental_frequency = fundamental.unwrap_or(1.0);
state.pitch = pitch;
state.signal = signal;
- state.freq_spectrum = frequency_domain;
state.correlation = correlation;
state.error = error
},
@@ -213,7 +197,6 @@ fn setup_pitch_label_callbacks(state: Rc<RefCell<ApplicationState>>, cross_threa
ui.pitch_error_indicator.queue_draw();
ui.oscilloscope_chart.queue_draw();
ui.correlation_chart.queue_draw();
- ui.freq_chart.queue_draw();
gtk::Continue(true)
});
@@ -278,31 +261,6 @@ fn setup_oscilloscope_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>
});
}
-fn setup_freq_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
- let outer_state = state.clone();
- let ref canvas = outer_state.borrow().ui.freq_chart;
- canvas.connect_draw(move |ref canvas, ref context| {
- let ref spectrum = cross_thread_state.read().unwrap().freq_spectrum;
- let width = canvas.get_allocated_width() as f64;
- let height = canvas.get_allocated_height() as f64;
- let max = spectrum.iter().map(|x| x.intensity).fold(0.0, |max, x| if max > x { max } else { x });
- let len = spectrum.len() as f64;
-
- context.new_path();
- context.move_to(0.0, height);
-
- for (i, bucket) in spectrum.iter().enumerate() {
- let x = i as f64 * width / len;
- let y = height - (bucket.intensity * height / max);
- context.line_to(x, y);
- }
-
- context.stroke();
-
- gtk::Inhibit(false)
- });
-}
-
fn setup_correlation_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>>, cross_thread_state: Arc<RwLock<CrossThreadState>>) {
let outer_state = state.clone();
let ref canvas = outer_state.borrow().ui.correlation_chart;
@@ -349,7 +307,6 @@ fn setup_correlation_drawing_area_callbacks(state: Rc<RefCell<ApplicationState>>
fn setup_chart_visibility_callbacks(state: Rc<RefCell<ApplicationState>>) {
let outer_state = state.clone();
let ref oscilloscope_toggle_button = outer_state.borrow().ui.oscilloscope_toggle_button;
- let ref freq_toggle_button = outer_state.borrow().ui.freq_toggle_button;
let ref correlation_toggle_button = outer_state.borrow().ui.correlation_toggle_button;
let oscilloscope_state = state.clone();
@@ -357,12 +314,6 @@ fn setup_chart_visibility_callbacks(state: Rc<RefCell<ApplicationState>>) {
let ref chart = oscilloscope_state.borrow().ui.oscilloscope_chart;
chart.set_visible(!chart.get_visible());
});
-
- let freq_state = state.clone();
- freq_toggle_button.connect_clicked(move |_| {
- let ref chart = freq_state.borrow().ui.freq_chart;
- chart.set_visible(!chart.get_visible());
- });
let correlation_state = state.clone();
correlation_toggle_button.connect_clicked(move |_| {
diff --git a/src/transforms.rs b/src/transforms.rs
index eeb175e..229af23 100644
--- a/src/transforms.rs
+++ b/src/transforms.rs
@@ -1,42 +1,8 @@
-extern crate dft;
-
-#[derive(Clone, Debug)]
-pub struct FrequencyBucket {
- pub min_freq: f64,
- pub max_freq: f64,
- pub intensity: f64
-}
-
-impl FrequencyBucket {
- pub fn ave_freq(&self) -> f64 {
- (self.min_freq + self.max_freq) / 2.0
- }
-}
-
pub fn remove_mean_offset(input: &Vec<f64>) -> Vec<f64> {
let mean_input = input.iter().sum::<f64>()/input.len() as f64;
input.iter().map(|x|x-mean_input).collect()
}
-pub fn fft(input: &Vec<f64>, sample_rate: f64) -> Vec<FrequencyBucket> {
- let mut intensities = remove_mean_offset(&input);
-
- let frames = intensities.len();
- let plan = dft::Plan::new(dft::Operation::Forward, frames);
- let frequency_resolution = sample_rate / 2.0 / frames as f64;
-
- dft::transform(&mut intensities, &plan);
-
- intensities.iter().enumerate().map(|(index, &value)| {
- let index = index as f64;
- FrequencyBucket {
- min_freq: index * frequency_resolution,
- max_freq: (index+1.0) * frequency_resolution,
- intensity: value
- }
- }).collect()
-}
-
pub fn correlation(input: &Vec<f64>) -> Vec<f64> {
let mut correlation = Vec::with_capacity(input.len());
for offset in 0..input.len() {
@@ -173,34 +139,6 @@ mod tests {
}
#[test]
- fn fft_on_sine_wave() {
- let frequency = 440.0 as f64; //concert A
-
- let samples = sample_sinusoud(1.0, frequency, 0.0);
- let frequency_domain = fft(&samples, SAMPLE_RATE);
- let fundamental = find_fundamental_frequency(&frequency_domain).unwrap();
-
- assert!((fundamental-frequency).abs() < frequency_resolution(), "expected={}, actual={}", frequency, fundamental);
- }
-
- #[test]
- fn fft_on_two_sine_waves() {
- //Unfortunately, real signals won't be this neat
- let samples1a = sample_sinusoud(2.0, 440.0, 0.0);
- let samples2a = sample_sinusoud(1.0, 880.0, 0.0);
- let expected_fundamental = 440.0;
-
- let samples = samples1a.iter().zip(samples2a.iter())
- .map(|(a, b)| a+b)
- .collect();
- let frequency_domain = fft(&samples, SAMPLE_RATE);
-
- let fundamental = find_fundamental_frequency(&frequency_domain).unwrap();
-
- assert!((fundamental-expected_fundamental).abs() < frequency_resolution(), "expected_fundamental={}, actual={}", expected_fundamental, fundamental);
- }
-
- #[test]
fn correlation_on_sine_wave() {
let frequency = 440.0 as f64; //concert A
@@ -272,14 +210,7 @@ pub fn hz_to_pitch(hz: f64) -> String {
return "< C1".to_string();
}
- let mut cents = ((midi_number * 100.0).round() % 100.0) as i32;
- if cents >= 50 {
- //don't need to adjust pitch here, that's already been done by the round above
- cents -= 100;
- }
-
format!("{}{}", name, octave)
-// format!("{}{} {:+}", name, octave, cents)
}
#[test]
@@ -304,7 +235,8 @@ fn f5_is_correct() {
pub fn align_to_rising_edge(samples: &Vec<f64>) -> Vec<f64> {
- samples.iter()
+ remove_mean_offset(&samples)
+ .iter()
.skip_while(|x| !x.is_sign_negative())
.skip_while(|x| x.is_sign_negative())
.cloned()