// use the jack bindings extern crate easyjack as jack; // use nix for signal handling // see simple_client example for some description of how this works extern crate nix; use nix::sys::signal; use std::f32::consts; use std::f32; use std::sync::atomic; use std::sync::mpsc::{SyncSender, Receiver}; use std::sync::mpsc; use std::thread; use std::time::Duration; static RUNNING: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT; type IPort = jack::InputPortHandle; type OPort = jack::OutputPortHandle; /// struct to handle metadata operations struct MetadataHandler { outgoing: SyncSender<[jack::DefaultAudioSample; 128]>, } impl MetadataHandler { pub fn new(outgoing: SyncSender<[jack::DefaultAudioSample; 128]>) -> Self { MetadataHandler { outgoing: outgoing } } } impl jack::MetadataHandler for MetadataHandler { fn sample_rate_changed(&mut self, srate: jack::NumFrames) -> i32 { println!("updating sample rate: {}", srate); let f = AudioHandler::calc_note_freqs(srate); match self.outgoing.send(f) { Ok(_) => 0, Err(_) => 0 } } fn callbacks_of_interest(&self) -> Vec { vec![jack::MetadataHandlers::SampleRate] } } /// struct to handle audio event loop struct AudioHandler { input: jack::InputPortHandle, output: jack::OutputPortHandle, // TODO explain what this is all about ramp: jack::DefaultAudioSample, note_on: jack::DefaultAudioSample, note: u8, note_freqs: [jack::DefaultAudioSample; 128], incoming: Receiver<[jack::DefaultAudioSample; 128]>, } impl AudioHandler { pub fn new(input: IPort, output: OPort, incoming: Receiver<[jack::DefaultAudioSample; 128]>) -> AudioHandler { let freqs = [0.0; 128]; AudioHandler { input: input, output: output, ramp: 0.0, note_on: 0.0, note: 0, note_freqs: freqs, incoming: incoming, } } pub fn calc_note_freqs(srate: jack::NumFrames) -> [jack::DefaultAudioSample; 128] { println!("recalculating note frequencies"); let mut freqs = [0.0; 128]; print!("new_freqs: "); for i in 0..128 { let a = 2.0 * (440.0 / 32.0); let b = 2.0_f32.powf( (i as f32 - 9.0) / 12.0 ); freqs[i] = a * b / srate as f32; print!("{},", freqs[i]); } println!(""); freqs } } impl jack::ProcessHandler for AudioHandler { fn process(&mut self, ctx: &jack::CallbackContext, nframes: jack::NumFrames) -> i32 { let output_buffer = self.output.get_write_buffer(nframes, &ctx); let input_buffer = self.input.get_read_buffer(nframes, &ctx); let mut event_index = 0; let event_count = input_buffer.len(); for i in 0..(nframes as usize) { if event_index < event_count { let event = input_buffer.get(event_index); println!("evi={} evt={}, i={}", event_index, event.get_jack_time(), i); if event.get_jack_time() == i as jack::NumFrames { let buf = event.raw_midi_bytes(); if buf[0] & 0x90 == 0x90 { println!("note on!"); self.note = buf[1]; self.note_on = 1.0; } else if buf[0] & 0x90 == 0x80 { println!("note off!"); self.note = buf[1]; self.note_on = 0.0; } event_index += 1; if event_index < event_count { event_index += 1; } } } self.ramp += self.note_freqs[self.note as usize]; self.ramp = if self.ramp > 1.0 { self.ramp - 2.0 } else { self.ramp }; let s = (2.0 * (consts::PI) * self.ramp).sin(); output_buffer[i] = self.note_on*s; // println!("output_buffer[{}] = {}", i, output_buffer[i]); } match self.incoming.try_recv() { Ok(freqs) => self.note_freqs = freqs, Err(_) => (), }; 0 } } extern "C" fn handle_sigint(_: i32) { RUNNING.store(false, atomic::Ordering::SeqCst); } fn main() { // register a signal handler (see comments at top of file) let action = signal::SigAction::new( signal::SigHandler::Handler(handle_sigint), signal::SaFlags::empty(), signal::SigSet::empty()); unsafe { signal::sigaction(signal::Signal::SIGINT, &action) }.unwrap(); // set our global atomic to true RUNNING.store(true, atomic::Ordering::SeqCst); let mut c = jack::Client::open("midi_sine", jack::options::NO_START_SERVER).unwrap().0; let i = c.register_input_midi_port("midi_in").unwrap(); let o = c.register_output_audio_port("audio_out").unwrap(); let (tx, rx) = mpsc::sync_channel(1); let handler = AudioHandler::new(i, o, rx); c.set_process_handler(handler).unwrap(); let handler = MetadataHandler::new(tx); c.set_metadata_handler(handler).unwrap(); c.activate().unwrap(); while RUNNING.load(atomic::Ordering::SeqCst) { thread::sleep(Duration::from_millis(1000)); } println!("tearing down"); c.close().unwrap(); }