simple_client/
simple_client.rs

1// use the jack bindings
2extern crate easyjack as jack;
3
4// use nix for signal handling
5// we need to use low level signal handlers to clean up in real time (the other crates don't behave
6// in real time very well)
7// This is a pain, but rust doesn't currently have a better way to handle signals without causing
8// stuttering (xruns) as the client is shutting down
9extern crate nix;
10
11use nix::sys::signal;
12use std::sync::atomic;
13use std::sync::mpsc::{SyncSender, Receiver};
14use std::sync::mpsc;
15use std::thread;
16use std::time::Duration;
17
18// the size of a list of samples
19const N: usize = 200;
20
21// signals are literally a giant steaming mess
22// create an atomic bool which will be save to change the value of inside of a signal handler
23static RUNNING: atomic::AtomicBool = atomic::ATOMIC_BOOL_INIT;
24
25/// This struct handles the process callback
26/// It holds a list of samples and continues to play them back until it receives a new set of
27/// samples over the `incoming` channel. When it receives new samples, it moves them (memcpy) into
28/// its own buffer.
29/// The samples are played back at different rates so that we can hear the difference in the right
30/// and left channel
31struct AudioHandler {
32    /// current list of samples
33    samples: [jack::DefaultAudioSample; N],
34
35    /// where we currently are in our list of samples
36    right_phase: usize,
37    left_phase: usize,
38
39    /// handles for the ports we are reading and writing to.
40    /// Remember, port handles can become invalid if you are careless with them
41    right_output: jack::OutputPortHandle<jack::DefaultAudioSample>,
42    left_output: jack::OutputPortHandle<jack::DefaultAudioSample>,
43
44    /// incoming changes
45    /// these are copied into the channel, then copied out of the channel.
46    /// Copies are pretty fast, but some applications may need to be more clever
47    incoming: Receiver<[jack::DefaultAudioSample; N]>,
48}
49
50impl AudioHandler {
51    /// constructs a new audio handler
52    fn new(
53        init_samples: [jack::DefaultAudioSample; N],
54        right: jack::OutputPortHandle<jack::DefaultAudioSample>,
55        left: jack::OutputPortHandle<jack::DefaultAudioSample>,
56        incoming: Receiver<[jack::DefaultAudioSample; N]>) -> Self
57    {
58        AudioHandler {
59            samples: init_samples,
60            right_phase: 0,
61            left_phase: 0,
62            right_output: right,
63            left_output: left,
64            incoming: incoming
65        }
66    }
67}
68
69/// implement the `ProcessHandler` for the `AudioHandler`
70impl jack::ProcessHandler for AudioHandler {
71    fn process(&mut self, ctx: &jack::CallbackContext, nframes: jack::NumFrames) -> i32 {
72        // get the ports
73        let right = self.right_output.get_write_buffer(nframes, ctx);
74        let left  = self.left_output.get_write_buffer(nframes, ctx);
75
76        // for every frame, write our current progress
77        for i in 0..(nframes as usize) {
78            right[i] = self.samples[self.right_phase];
79            left[i]  = self.samples[self.left_phase];
80
81            // adjust the phases, keep left and right out of sync
82            self.right_phase = self.right_phase + 1;
83            self.left_phase  = self.left_phase + 3;
84
85            if self.right_phase >= self.samples.len() {
86                self.right_phase = 0
87            }
88
89            if self.left_phase >= self.samples.len() {
90                self.left_phase = 0
91            }
92        }
93
94        // try to update the samples, if we need to
95        match self.incoming.try_recv() {
96            Ok(samples) => self.samples = samples,
97            Err(_) => (),
98        };
99
100        0
101    }
102}
103
104/// A simple wrapper around a jack client
105/// Creates a handler and sets up channels to communicate with the handler
106struct SimpleClient<'a> {
107    client: jack::Client<'a>,
108    sender: SyncSender<[jack::DefaultAudioSample; N]>,
109}
110
111impl<'a> SimpleClient<'a> {
112    fn new() -> Result<Self, jack::status::Status> {
113        let client = jack::Client::open("simple", jack::options::NO_START_SERVER);
114        let mut client = match client {
115            Ok((client, _)) => client,
116            Err(code)       => return Err(code),
117        };
118
119        // get some ports
120        let right = client.register_output_audio_port("output1").unwrap();
121        let left  = client.register_output_audio_port("output2").unwrap();
122
123        // create a channel pair we can use to communicate with
124        let (tx, rx) = mpsc::sync_channel(1);
125
126        // create a client, set it up as an audio processing handler
127        let handler = AudioHandler::new(SimpleClient::compute_sine(0.2), right, left, rx);
128        client.set_process_handler(handler).unwrap();
129
130        Ok(SimpleClient {
131            client: client,
132            sender: tx,
133        })
134    }
135
136    fn activate(&mut self) -> Result<(), jack::status::Status> {
137        self.client.activate()
138    }
139
140    fn run(mut self) {
141        let mut i = 0;
142        while RUNNING.load(atomic::Ordering::SeqCst) {
143            let newsine = SimpleClient::compute_sine(i as f32 / 10.0);
144
145            match self.sender.send(newsine) {
146                Ok(_)  => (),
147                Err(_) => (),
148            };
149
150            i += 1;
151            if i > 10 {
152                i = 0
153            }
154
155            thread::sleep(Duration::from_millis(1000));
156        }
157
158        println!("tearing down");
159        self.client.close().unwrap();
160    }
161
162    fn compute_sine(constant: f32) -> [jack::DefaultAudioSample; N] {
163        let mut sine = [0.0; N];
164        for i in 0..N {
165            let inner = ((i as f32) / (N as f32)) * 3.14159265 * 2.0;
166            sine[i] = constant * inner.sin();
167        }
168
169        sine
170    }
171}
172
173extern "C" fn handle_sigint(_: i32) {
174    RUNNING.store(false, atomic::Ordering::SeqCst);
175}
176
177fn main() {
178    // register a signal handler (see comments at top of file)
179    let action = signal::SigAction::new(
180        signal::SigHandler::Handler(handle_sigint),
181        signal::SaFlags::empty(),
182        signal::SigSet::empty());
183
184    unsafe { signal::sigaction(signal::Signal::SIGINT, &action) }.unwrap();
185
186    // set our global atomic to true
187    RUNNING.store(true, atomic::Ordering::SeqCst);
188
189    let mut c = SimpleClient::new().unwrap();
190    c.activate().unwrap();
191    c.run()
192}