summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin.worthe@gmail.com>2016-11-26 09:42:25 +0200
committerJustin Worthe <justin.worthe@gmail.com>2016-11-26 09:42:25 +0200
commitfba482b5545a6c02bf32d9dc4f261f6fc5556064 (patch)
treed5c54382fbcd82515d41dc899391b47b2aaa1a54
parent14d8545f0c905ea94556919693c08fb887a3ce59 (diff)
Updated name of crate everywhere
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml2
-rw-r--r--README.org19
-rw-r--r--src/audio.rs6
-rw-r--r--src/main.rs4
-rw-r--r--src/transforms.rs86
6 files changed, 80 insertions, 39 deletions
diff --git a/Cargo.lock b/Cargo.lock
index bbe1b8e..c105d18 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,5 +1,5 @@
[root]
-name = "musician_training"
+name = "rusty_microphone"
version = "0.1.0"
dependencies = [
"dft 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index ec26c94..d580786 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "musician_training"
+name = "rusty_microphone"
version = "0.1.0"
authors = ["Justin Worthe <justin.worthe@gmail.com>"]
diff --git a/README.org b/README.org
index b4f17e7..4bf820b 100644
--- a/README.org
+++ b/README.org
@@ -11,6 +11,9 @@ The other goal of this project is to give me a platform for learning
Rust, and various other technologies that I do not get to touch in my
day to day work.
+NB this is a work in progress, and does not provide any actual value
+yet.
+
* Getting started
This is a Rust project. The latest version of the Rust compiler and
@@ -64,20 +67,12 @@ src_sh{cargo test}
Cargo test', but again, formalize and in the docs above.
** Audio interfaces
*** DONE Read the list of microphones.
-*** TODO Start listening on a selected microphone, and let other plugins process the signal.
-- There needs to be able to be multiple of these, and those listening
- need to be able to be turned on and off independently.
-*** TODO Plugin for finding frequency spectrum
-*** TODO Plugin for finding fundamental frequencies, expressed as a note and amount sharp/flat
-*** TODO Plugin for recording and playback
-- During playback, it would be cool if the other plugins can be
- pointed at the sound being played back.
+*** DONE Start listening on a selected microphone, and let other plugins process the signal.
+*** TODO Function for finding frequency spectrum
+*** TODO Function for finding fundamental frequencies, expressed as a note and amount sharp/flat
+*** TODO Function for recording and playback
** GUI
*** DONE Opens a window with a drop down of available microphones
*** TODO Real-time updating graph of frequency spectrum
*** TODO Real-time updating graphical representation of fundamental frequency
*** TODO Recording and playback controls
-*** TODO Enabling or disabling of plugins and their GUI elements on the fly
-- This is necessary because as the project scales, you might want to
- be able to focus on certain areas and hide others, or some things
- may be processor-intensive.
diff --git a/src/audio.rs b/src/audio.rs
index e418e94..bcee400 100644
--- a/src/audio.rs
+++ b/src/audio.rs
@@ -24,7 +24,8 @@ pub fn get_device_list(pa: &pa::PortAudio) -> Result<Vec<(u32, String)>, pa::Err
Ok(list)
}
-//#[test]
+#[test]
+#[ignore]
fn get_device_list_returns_devices() {
let pa = init().expect("Could not init portaudio");
let devices = get_device_list(&pa).expect("Getting devices had an error");
@@ -61,7 +62,8 @@ pub fn start_listening(pa: &pa::PortAudio, device_index: u32,
Ok(stream)
}
-//#[test]
+#[test]
+#[ignore]
fn start_listening_returns_successfully() {
let pa = init().expect("Could not init portaudio");
let devices = get_device_list(&pa).expect("Getting devices had an error");
diff --git a/src/main.rs b/src/main.rs
index 0eea7f3..91befca 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
-extern crate musician_training;
+extern crate rusty_microphone;
-use musician_training::*;
+use rusty_microphone::*;
fn main() {
let gui_result = gui::start_gui();
diff --git a/src/transforms.rs b/src/transforms.rs
index 371379d..4d0e224 100644
--- a/src/transforms.rs
+++ b/src/transforms.rs
@@ -7,10 +7,18 @@ pub struct FrequencyBucket {
pub intensity: f64
}
+impl FrequencyBucket {
+ pub fn ave_freq(&self) -> f64 {
+ (self.min_freq + self.max_freq) / 2.0
+ }
+}
+
pub fn fft(input: Vec<f64>, sample_rate: f64) -> Vec<FrequencyBucket> {
let frames = input.len();
+ let mean_input = input.iter().sum::<f64>()/input.len() as f64;
+
+ let mut intensities = input.iter().map(|x|x-mean_input).collect::<Vec<_>>();
let plan = dft::Plan::new(dft::Operation::Forward, frames);
- let mut intensities = input.clone();
dft::transform(&mut intensities, &plan);
let frequency_resolution = sample_rate / 2.0 / frames as f64;
@@ -28,35 +36,71 @@ pub fn fft(input: Vec<f64>, sample_rate: f64) -> Vec<FrequencyBucket> {
pub fn find_fundamental_frequency(frequency_domain: &Vec<FrequencyBucket>) -> Option<f64> {
//TODO look at all significant frequencies, find fundamental
//TODO return None is none of them are significant
+ let positive_buckets = frequency_domain.iter().filter(|x| x.intensity > 0.0).cloned().collect::<Vec<_>>();
+ let average_intensity = positive_buckets.iter().map(|x| x.intensity).sum::<f64>() / frequency_domain.len() as f64;
+ let significant_buckets = positive_buckets.iter().filter(|x| x.intensity > average_intensity).cloned().collect::<Vec<_>>();
+ for bucket in significant_buckets.iter() {
+ //println!("{:?}", bucket);
+ }
- let max_frequency = frequency_domain.iter()
+ let max_bucket = significant_buckets.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;
-
- Some(max_frequency)
+ ).unwrap();
+
+ Some(max_bucket.ave_freq())
}
-#[test]
-fn fft_on_sine_wave() {
- use std::f64::consts;
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::f64::consts::PI;
- let sample_rate = 44100.0 as f64;
- let amplitude = 1.0 as f64;
- let frames = 16384;
- let frequency = 10000.0 as f64; //10KHz
- let frequency_resolution = sample_rate / 2.0 / frames as f64;
+ const SAMPLE_RATE: f64 = 44100.0;
+ const FRAMES: usize = 512;
+
+ fn frequency_resolution() -> f64 {
+ SAMPLE_RATE / 2.0 / FRAMES as f64
+ }
+
+ fn sin_arg(f: f64, t: f64, phase: f64) -> f64 {
+ 2.0 as f64 * PI * f * t + phase
+ }
+
+ fn sample_sinusoud(amplitude: f64, frequency: f64, phase: f64) -> Vec<f64> {
+ (0..FRAMES)
+ .map(|x| {
+ let t = x as f64 / SAMPLE_RATE;
+ sin_arg(frequency, t, phase).sin() * amplitude
+ }).collect()
+ }
- let samples = (0..frames)
- .map(|x| {
- let t = x as f64 / sample_rate;
- (2.0 as f64 * consts::PI * frequency * t).sin() * amplitude
- }).collect();
+ #[test]
+ fn fft_on_sine_wave() {
+ let frequency = 10000.0 as f64; //10KHz
+
+ 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);
+ }
- let result = fft(samples, sample_rate);
- let fundamental = find_fundamental_frequency(&result);
+ #[test]
+ fn fft_on_two_sine_waves() {
+ let samples1k = sample_sinusoud(0.5, 1000.0, 0.0);
+ let samples2k = sample_sinusoud(1.0, 10000.0, 0.0);
+ let expected_fundamental = 1000.0;
+
+ let samples = samples1k.iter().zip(samples2k.iter())
+ .map(|(a, b)| a+b)
+ .collect();
+ 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);
+ assert!((fundamental-expected_fundamental).abs() < frequency_resolution(), "expected_fundamental={}, actual={}", expected_fundamental, fundamental);
+ }
}
pub fn hz_to_pitch(hz: f64) -> String {