Created
August 22, 2018 08:53
-
-
Save drozdziak1/fc9ecac2b9b5404e36887ed584957432 to your computer and use it in GitHub Desktop.
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
#[macro_use] | |
extern crate failure; | |
#[macro_use] | |
extern crate log; | |
extern crate env_logger; | |
extern crate rosc; | |
extern crate rustyline; | |
use failure::Error; | |
use log::LevelFilter; | |
use rosc::{decoder, encoder, OscMessage, OscPacket, OscType}; | |
use std::{env, net::UdpSocket, process, process::Command, thread::sleep, time::Duration}; | |
static DEFAULT_JMM_BOOTSTRAP_DELAY: Duration = Duration::from_millis(300); | |
static DEFAULT_JMM_PORT: u16 = 61234; | |
fn make_jmm_osc_request( | |
sock: &mut UdpSocket, | |
path: &str, | |
args: Option<Vec<OscType>>, | |
) -> Result<OscPacket, Error> { | |
let packet = OscPacket::Message(OscMessage { | |
addr: path.to_owned(), | |
args: args, | |
}); | |
sock.send(match &encoder::encode(&packet) { | |
Ok(buf) => buf, | |
Err(e) => bail!("{:?}", e), // TODO: Flimsy, A more robust error would be better | |
})?; | |
sock.set_read_timeout(Some(Duration::from_secs(1)))?; | |
let mut buf = vec![0u8; 200]; | |
sock.recv(&mut buf)?; | |
let recv_packet = match decoder::decode(&buf) { | |
Ok(p) => p, | |
Err(e) => bail!("{:?}", e), // TODO: same here | |
}; | |
debug!( | |
"Successfuly received and parsed an OSC packet: {:?}", | |
recv_packet | |
); | |
Ok(recv_packet) | |
} | |
fn main() { | |
match env::var("RUST_LOG") { | |
Ok(_) => env_logger::init(), | |
Err(_) => env_logger::Builder::new() | |
.filter_level(LevelFilter::Info) | |
.init(), | |
} | |
let jmm_bootstrap_delay = match env::var("JMM_BOOTSTRAP_DELAY_MS") { | |
Ok(n) => Duration::from_millis(n.parse::<u64>().unwrap()), | |
Err(_) => DEFAULT_JMM_BOOTSTRAP_DELAY, | |
}; | |
let jmm_port = match env::var("JMM_PORT") { | |
Ok(n) => n.parse::<u16>().unwrap(), | |
Err(_) => DEFAULT_JMM_PORT, | |
}; | |
info!("Starting..."); | |
// The jackminimix instance we wrap | |
let mut jmm = match Command::new("jackminimix") | |
.args(&["-p", &format!("{}", jmm_port)]) | |
//.stdout(Stdio::null()) | |
.spawn() | |
{ | |
Ok(chld) => { | |
info!( | |
"Spawned a jackminimix instance with process id {}", | |
chld.id() | |
); | |
chld | |
} | |
Err(e) => { | |
error!("Could not spawn jackminimix: {:?}", e); | |
process::exit(1); | |
} | |
}; | |
sleep(Duration::from_millis(500)); // Sometimes jackminimix wouldn't open its socket in time | |
let mut sock = UdpSocket::bind("0.0.0.0:0").unwrap(); | |
info!("Socket bound to {:?}", sock.local_addr().unwrap()); | |
sock.connect(format!("0.0.0.0:{}", jmm_port)).unwrap(); | |
info!("Socket connected to 0.0.0.0:{}", jmm_port); | |
match make_jmm_osc_request(&mut sock, "/ping", None).unwrap() { | |
OscPacket::Message(OscMessage { | |
addr: "/pong".to_owned(), | |
args: None, | |
}) => info!("JMM ping OK"), | |
_ => { | |
error!("Failed to ping the jackminimix instance. Try setting JMM_BOOTSTRAP_DELAY above {:?}?", jmm_bootstrap_delay); | |
process::exit(1); | |
} | |
}; | |
let channel_count = | |
match make_jmm_osc_request(&mut sock, "/mixer/get_channel_count", None).unwrap() { | |
OscPacket::Message(msg) => match msg.args { | |
Some(list) => match list.as_slice() { | |
[OscType::Int(count)] => count.clone(), | |
_ => { | |
error!("Received an unrecognized argument list from channel output query"); | |
process::exit(1); | |
} | |
}, | |
None => { | |
error!("Received an empty argument list from channel count query"); | |
process::exit(1); | |
} | |
}, | |
OscPacket::Bundle(_) => { | |
error!("Received a bundle message from channel count query"); | |
process::exit(1); | |
} | |
}; | |
info!("JackMiniMix has {:?} channels", channel_count); | |
let mut rl = rustyline::Editor::<()>::new(); | |
loop { | |
if let Ok(line) = rl.readline("(jmm) ") { | |
debug!("Line {:?} received", line); | |
let mut split = line.split_whitespace(); | |
match split.nth(0) { | |
Some("ping") => { | |
let packet = make_jmm_osc_request(&mut sock, "/ping", None).unwrap(); | |
info!("Ping response: {:?}", packet); | |
} | |
// Get volume for all channels | |
Some("get-all") => for ch_id in 1..=channel_count {}, | |
// Set volume for all channels | |
Some("set-all") => { | |
let gain: f32 = match split.nth(0) { | |
Some(g) => match g.parse() { | |
Ok(val) => val, | |
Err(e) => { | |
error!("Could not parse {:?} as float: {:?}", g, e); | |
continue; | |
} | |
}, | |
None => { | |
error!("Missing gain to set. Example: \"set-all -3.5\""); | |
continue; | |
} | |
}; | |
debug!( | |
"Attempting to set gain {:.1}db on all {:?} channels", | |
gain, channel_count | |
); | |
} | |
Some(other) => error!("Unknown command {:?}", other), | |
None => {} | |
} | |
} else { | |
jmm.kill().unwrap(); | |
debug!("Killed jackminimix with process id {}", jmm.id()); | |
println!("Bye!"); | |
process::exit(0); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment