use audiograph::*;
use std::io::Read;
const APP_NAME: &str = "pass-through-audiograph";
const OUT_L: &str = "out-l";
const OUT_R: &str = "out-r";
const IN_L: &str = "in-l";
const IN_R: &str = "in-r";
type Sample = f32;
struct Context {
in_l_port: *const jack::Port<jack::AudioIn>,
in_r_port: *const jack::Port<jack::AudioIn>,
out_l_port: *mut jack::Port<jack::AudioOut>,
out_r_port: *mut jack::Port<jack::AudioOut>,
ps: *const jack::ProcessScope,
}
impl Context {
fn get_audio_input(&self) -> [&[f32]; 2] {
unsafe {
let ps = &*self.ps;
let left = (&*self.in_l_port).as_slice(ps);
let right = (&*self.in_r_port).as_slice(ps);
[left, right]
}
}
fn get_audio_output(&mut self) -> [&mut [f32]; 2] {
unsafe {
let ps = &*self.ps;
let left = (&mut *self.out_l_port).as_mut_slice(ps);
let right = (&mut *self.out_r_port).as_mut_slice(ps);
[left, right]
}
}
}
struct InputRoute;
impl Route<Sample, Context> for InputRoute {
fn process(
&mut self,
_input: &[BufferPoolReference<Sample>],
output: &mut [BufferPoolReference<Sample>],
_frames: usize,
context: &mut Context,
) {
for (output_stream, input_stream) in output.iter_mut().zip(context.get_audio_input().iter())
{
for (out_sample, in_sample) in
output_stream.as_mut().iter_mut().zip(input_stream.iter())
{
*out_sample = *in_sample;
}
}
}
}
struct OutputRoute;
impl Route<Sample, Context> for OutputRoute {
fn process(
&mut self,
input: &[BufferPoolReference<Sample>],
_output: &mut [BufferPoolReference<Sample>],
_frames: usize,
context: &mut Context,
) {
for (output_stream, input_stream) in
context.get_audio_output().iter_mut().zip(input.as_ref())
{
for (out_sample, in_sample) in output_stream.iter_mut().zip(input_stream.as_ref()) {
*out_sample = *in_sample;
}
}
}
}
enum Routes {
Input(InputRoute),
Output(OutputRoute),
}
impl<'a> Route<Sample, Context> for Routes {
fn process(
&mut self,
input: &[BufferPoolReference<Sample>],
output: &mut [BufferPoolReference<Sample>],
frames: usize,
context: &mut Context,
) {
match self {
Routes::Input(r) => r.process(input, output, frames, context),
Routes::Output(r) => r.process(input, output, frames, context),
}
}
}
fn main() {
let client = jack::Client::new(APP_NAME, jack::ClientOptions::NO_START_SERVER)
.unwrap()
.0;
let channels = 2;
let buffer_size = client.buffer_size();
let mut graph = RouteGraphBuilder::new()
.with_buffer_size(buffer_size as usize)
.build();
let output = graph
.add_node_with_idx(|id| Node::with_id(id, channels, Routes::Output(OutputRoute), vec![]));
graph.add_node_with_idx(|id| {
Node::with_id(
id,
channels,
Routes::Input(InputRoute),
vec![Connection::new(output.clone(), 1.)],
)
});
graph.topographic_sort();
let out_spec = jack::AudioOut::default();
let in_spec = jack::AudioIn::default();
let mut out_l_port = client.register_port(OUT_L, out_spec).unwrap();
let mut out_r_port = client.register_port(OUT_R, out_spec).unwrap();
let in_l_port = client.register_port(IN_L, in_spec).unwrap();
let in_r_port = client.register_port(IN_R, in_spec).unwrap();
let process = jack::ClosureProcessHandler::new(
move |_: &jack::Client, ps: &jack::ProcessScope| -> jack::Control {
let frames = ps.n_frames() as usize;
let mut context = Context {
out_r_port: &mut out_r_port,
out_l_port: &mut out_l_port,
in_l_port: &in_l_port,
in_r_port: &in_r_port,
ps,
};
graph.process(frames, &mut context);
jack::Control::Continue
},
);
let active = client.activate_async((), process).unwrap();
let system_out_1 = active
.as_client()
.port_by_name("system:playback_1")
.unwrap();
let system_out_2 = active
.as_client()
.port_by_name("system:playback_2")
.unwrap();
let out_l_port = active
.as_client()
.port_by_name(format!("{}:{}", APP_NAME, OUT_L).as_str())
.unwrap();
let out_r_port = active
.as_client()
.port_by_name(format!("{}:{}", APP_NAME, OUT_R).as_str())
.unwrap();
active
.as_client()
.connect_ports(&out_l_port, &system_out_1)
.unwrap();
active
.as_client()
.connect_ports(&out_r_port, &system_out_2)
.unwrap();
let mut stdin = std::io::stdin();
let _ = stdin.read(&mut [0u8]).unwrap();
active.deactivate().unwrap();
}