summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Worthe <justin.worthe@gmail.com>2016-09-25 16:19:58 +0200
committerJustin Worthe <justin.worthe@gmail.com>2016-09-25 16:19:58 +0200
commit59607a6ba83048d8f92920d974f4ab2c6ecbaae6 (patch)
tree92a92d2e12409700049188414648d796f6757797
GUI, lists PA devices, backend can watch audio stream
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock304
-rw-r--r--Cargo.toml9
-rw-r--r--src/audio.rs74
-rw-r--r--src/gui.rs28
-rw-r--r--src/lib.rs6
-rw-r--r--src/main.rs11
-rw-r--r--src/transforms.rs53
8 files changed, 486 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eb5a316
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..7e12927
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,304 @@
+[root]
+name = "musician_training"
+version = "0.1.0"
+dependencies = [
+ "dft 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gtk 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "portaudio 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "atk-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gobject-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "bitflags"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bitflags"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bitflags"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "c_vec"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "cairo-rs"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "c_vec 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cairo-sys-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "cairo-sys-rs"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "dft"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-complex 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gdk"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cairo-rs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-pixbuf 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pango 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gdk-pixbuf"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gdk-pixbuf-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gdk-pixbuf-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gio-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gobject-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gdk-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cairo-sys-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-pixbuf-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gio-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gobject-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pango-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gio"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gio-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gobject-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gio-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gobject-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "glib"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "gio-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gobject-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gtk"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cairo-rs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cairo-sys-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-pixbuf 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-pixbuf-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gio 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gio-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gobject-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gtk-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pango 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "gtk-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "atk-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cairo-sys-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-pixbuf-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gdk-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gio-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gobject-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pango-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "num"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "pango"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "glib 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pango-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pango-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glib-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gobject-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "portaudio"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "bitflags 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..7b65fb2
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "musician_training"
+version = "0.1.0"
+authors = ["Justin Worthe <justin.worthe@gmail.com>"]
+
+[dependencies]
+portaudio = "0.6.4"
+dft = "0.4.3"
+gtk = "0.1.0"
diff --git a/src/audio.rs b/src/audio.rs
new file mode 100644
index 0000000..526a227
--- /dev/null
+++ b/src/audio.rs
@@ -0,0 +1,74 @@
+extern crate portaudio;
+use portaudio as pa;
+
+const SAMPLE_RATE: f64 = 44100.0;
+const FRAMES: usize = 512;
+
+pub fn get_device_list() -> Result<Vec<(u32, String)>, pa::Error> {
+ let pa = try!(pa::PortAudio::new());
+ let default_host = try!(pa.default_host_api());
+ println!("Using default host: {:#?}", default_host);
+
+ let mut list = Vec::new();
+ let devices = try!(pa.devices());
+ for device in devices {
+ let (pa::DeviceIndex(idx), info) = try!(device);
+ if info.max_input_channels == 0 {
+ continue;
+ }
+ list.push((idx, info.name.to_string()));
+ }
+ Ok(list)
+}
+
+pub fn run(device_index: u32) -> Result<(), pa::Error> {
+ let pa = try!(pa::PortAudio::new());
+
+ let input_info = try!(pa.device_info(pa::DeviceIndex(device_index)));
+ println!("Using {} for input", input_info.name);
+
+ // Construct the input stream parameters.
+ let latency = input_info.default_low_input_latency;
+ let input_params = pa::StreamParameters::<f32>::new(pa::DeviceIndex(device_index), 1, true, latency);
+
+ // Check that the stream format is supported.
+ try!(pa.is_input_format_supported(input_params, SAMPLE_RATE));
+
+ // Construct the settings with which we'll open our duplex stream.
+ let settings = pa::InputStreamSettings::new(input_params, SAMPLE_RATE, FRAMES as u32);
+
+ // We'll use this channel to send the count_down to the main thread for fun.
+ let (sender, receiver) = ::std::sync::mpsc::channel();
+
+ // A callback to pass to the non-blocking stream.
+ let callback = move |pa::InputStreamCallbackArgs { buffer, .. }| {
+ sender.send(buffer.iter().map(|x| *x as f64).collect()).ok();
+ pa::Continue
+ };
+
+ // Construct a stream with input and output sample types of f32.
+ let mut stream = try!(pa.open_non_blocking_stream(settings, callback));
+
+ try!(stream.start());
+
+ let mut samples_index = 0;
+ // Do some stuff!
+ while let Ok(samples) = receiver.recv() {
+ samples_index += 1;
+ if samples_index % 100 != 0 {
+ continue;
+ }
+
+ let frequency_domain = ::transforms::fft(samples, SAMPLE_RATE);
+
+ 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());
+ }
+
+ try!(stream.stop());
+
+ Ok(())
+}
diff --git a/src/gui.rs b/src/gui.rs
new file mode 100644
index 0000000..66ae1f7
--- /dev/null
+++ b/src/gui.rs
@@ -0,0 +1,28 @@
+use gtk;
+use gtk::prelude::*;
+
+pub fn start_gui() -> Result<(), String> {
+ try!(gtk::init().map_err(|_| "Failed to initialize GTK."));
+
+ // Create the main window.
+ let window = gtk::Window::new(gtk::WindowType::Toplevel);
+ window.set_title("Musician Training");
+
+ let audio_devices = try!(::audio::get_device_list().map_err(|e| e.to_string()));
+ let dropdown = gtk::ComboBoxText::new();
+ for (index, name) in audio_devices {
+ dropdown.append(Some(format!("{}", index).as_ref()), name.as_ref());
+ }
+ window.add(&dropdown);
+ window.set_default_size(300, 300);
+
+ window.show_all();
+
+ window.connect_delete_event(|_, _| {
+ gtk::main_quit();
+ gtk::prelude::Inhibit(false)
+ });
+
+ gtk::main();
+ Ok(())
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..81da674
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,6 @@
+extern crate gtk;
+extern crate portaudio;
+
+pub mod transforms;
+pub mod gui;
+pub mod audio;
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..0eea7f3
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,11 @@
+extern crate musician_training;
+
+use musician_training::*;
+
+fn main() {
+ let gui_result = gui::start_gui();
+ if gui_result.is_err() {
+ println!("Failed to initialize");
+ return;
+ }
+}
diff --git a/src/transforms.rs b/src/transforms.rs
new file mode 100644
index 0000000..6b491ac
--- /dev/null
+++ b/src/transforms.rs
@@ -0,0 +1,53 @@
+extern crate dft;
+
+#[derive(Clone, Debug)]
+pub struct FrequencyBucket {
+ pub min_freq: f64,
+ pub max_freq: f64,
+ pub intensity: f64
+}
+
+pub fn fft(input: Vec<f64>, sample_rate: f64) -> Vec<FrequencyBucket> {
+ let frames = input.len();
+ 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;
+
+ 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()
+}
+
+#[test]
+fn fft_on_sine_wave() {
+ use std::f64::consts;
+
+ let sample_rate = 44100.0 as f64;
+ let amplitude = 1.0 as f64;
+ let frames = 16384;
+ let frequency = 10000.0 as f64; //10KHz
+ let samples = (0..frames)
+ .map(|x| {
+ let t = x as f64 / sample_rate;
+ (2.0 as f64 * consts::PI * frequency * t).sin() * amplitude
+ }).collect();
+
+ let result = fft(samples, sample_rate);
+
+ let peak = result.iter()
+ .fold(None as Option<FrequencyBucket>, |max, next|
+ if max.is_none() || max.clone().unwrap().intensity < next.intensity { Some(next.clone()) } else { max }
+ ).unwrap();
+
+ println!("{:?}", peak);
+
+ assert!(peak.min_freq <= frequency);
+ assert!(peak.max_freq >= frequency);
+}