Last active
August 29, 2015 14:23
-
-
Save HybridEidolon/8aae90086a65a03d9d7b to your computer and use it in GitHub Desktop.
sine keyboard
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate sdl2; | |
use sdl2::audio; | |
use std::f32::consts::PI; | |
use std::sync::Arc; | |
use std::sync::Mutex; | |
fn note_frequency(tone: i32) -> f32 { | |
440.0 * (1.059463 as f32).powi(tone) | |
} | |
fn scancode_to_halfstep(code: sdl2::keyboard::Scancode) -> Option<i32> { | |
use sdl2::keyboard::Scancode::*; | |
match code { | |
Z => Some(3), | |
S => Some(4), | |
X => Some(5), | |
D => Some(6), | |
C => Some(7), | |
V => Some(8), | |
G => Some(9), | |
B => Some(10), | |
H => Some(11), | |
N => Some(12), | |
J => Some(13), | |
M => Some(14), | |
Comma => Some(15), | |
L => Some(16), | |
Period => Some(17), | |
_ => None | |
} | |
} | |
struct Generator { | |
freq: i32, | |
phase: u32, | |
volume: f32, | |
note: Vec<i32>, | |
spectro_buffer: Arc<Mutex<Vec<i16>>> | |
} | |
impl audio::AudioCallback for Generator { | |
type Channel = i16; | |
fn callback(&mut self, out: &mut [i16]) { | |
// Clear the buffer. | |
for x in out.iter_mut() { | |
*x = 0; | |
} | |
for note in self.note.iter() { | |
let note_freq = note_frequency(*note); | |
let mut phase_counter = self.phase; | |
for x in out.iter_mut() { | |
let phase_f = (phase_counter as f32 / self.freq as f32) * note_freq * PI; | |
let sine_sample = (phase_f).sin() * self.volume; | |
let sample = (sine_sample * 32768.0).round() as i16; | |
*x = x.saturating_add(sample); | |
phase_counter = phase_counter.wrapping_add(1); | |
} | |
} | |
self.phase = self.phase.wrapping_add(out.len() as u32); | |
// replace the spectro buffer | |
{ | |
let mut specbuf = self.spectro_buffer.lock().unwrap(); | |
specbuf.clear(); | |
for x in out.iter() { | |
specbuf.push(*x); | |
} | |
} | |
} | |
} | |
fn main() { | |
let mut sdl_context = sdl2::init().video().audio().unwrap(); | |
let display = sdl_context.window("Hello", 800, 600).build().unwrap(); | |
let mut running = true; | |
let desired_spec = audio::AudioSpecDesired { | |
freq: Some(44100), | |
channels: Some(1), | |
samples: Some(512) | |
}; | |
let spectrobuf = Arc::new(Mutex::new(Vec::with_capacity(512))); | |
let mut device = audio::AudioDevice::open_playback(None, desired_spec, |spec| { | |
// Initialize audio callback | |
Generator { | |
freq: spec.freq, | |
phase: 0, | |
volume: 0.30, | |
note: Vec::with_capacity(8), | |
spectro_buffer: spectrobuf.clone() | |
} | |
}).unwrap(); | |
device.resume(); | |
while running { | |
for event in sdl_context.event_pump().poll_iter() { | |
use sdl2::event::Event; | |
match event { | |
Event::Quit { .. } => { | |
running = false; | |
}, | |
Event::KeyDown { scancode, repeat, .. } => { | |
if !repeat { | |
let mut d = device.lock(); | |
let halfstep = scancode.map(scancode_to_halfstep); | |
halfstep.map(|n| n.map(|nn| d.note.push(nn))); | |
} | |
}, | |
Event::KeyUp { scancode, .. } => { | |
let mut d = device.lock(); | |
scancode | |
.map(scancode_to_halfstep) | |
.map(|n| n.map(|nn| { | |
let loc = d.note.iter().position(|&v| v == nn); | |
loc.map(|l| d.note.remove(l)); | |
})); | |
} | |
_ => () | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment