Initial release: Orca VM, TUI, MIDI/OSC/UDP output, and release pipeline
This commit is contained in:
+102
@@ -0,0 +1,102 @@
|
||||
use std::process::exit;
|
||||
|
||||
use orca_rs::io::{EventOutputs, MidiOut, OscOut, UdpOut};
|
||||
use orca_rs::tui::{self, TuiConfig};
|
||||
|
||||
const USAGE: &str = "\
|
||||
Usage: orca-rs [options] [file.orca]
|
||||
|
||||
Open the Orca live-coding environment. If the file does not exist it will
|
||||
be created on first save; with no file, edits save to untitled.orca.
|
||||
|
||||
Options:
|
||||
--bpm <n> Tempo (default: 120)
|
||||
--seed <n> Random seed for the R operator (default: 1)
|
||||
--udp-port <n> Send ';' operator strings over UDP to this port
|
||||
--udp-host <h> UDP destination host (default: 127.0.0.1)
|
||||
--osc-port <n> Send '=' operator messages over OSC to this port
|
||||
--osc-host <h> OSC destination host (default: 127.0.0.1)
|
||||
--no-midi Disable MIDI output (on by default: creates a virtual
|
||||
ALSA port named 'orca-rs', visible in PipeWire)
|
||||
--midi-name <s> Connect to the MIDI output port whose name contains <s>
|
||||
instead of creating a virtual port
|
||||
--list-midi List available MIDI output ports and exit
|
||||
-h, --help Print this message and exit
|
||||
";
|
||||
|
||||
fn die(msg: &str) -> ! {
|
||||
eprintln!("{msg}");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fn parse_num(args: &mut impl Iterator<Item = String>, flag: &str) -> usize {
|
||||
args.next()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or_else(|| die(&format!("{flag} requires a number")))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut path: Option<String> = None;
|
||||
let mut bpm: usize = 120;
|
||||
let mut seed: usize = 1;
|
||||
let mut udp_host = "127.0.0.1".to_string();
|
||||
let mut udp_port: Option<u16> = None;
|
||||
let mut osc_host = "127.0.0.1".to_string();
|
||||
let mut osc_port: Option<u16> = None;
|
||||
let mut midi = true;
|
||||
let mut midi_name: Option<String> = None;
|
||||
|
||||
let mut args = std::env::args().skip(1);
|
||||
while let Some(arg) = args.next() {
|
||||
match arg.as_str() {
|
||||
"--bpm" => bpm = parse_num(&mut args, "--bpm").clamp(10, 999),
|
||||
"--seed" => seed = parse_num(&mut args, "--seed"),
|
||||
"--udp-port" => udp_port = Some(parse_num(&mut args, "--udp-port") as u16),
|
||||
"--udp-host" => udp_host = args.next().unwrap_or_else(|| die("--udp-host requires a value")),
|
||||
"--osc-port" => osc_port = Some(parse_num(&mut args, "--osc-port") as u16),
|
||||
"--osc-host" => osc_host = args.next().unwrap_or_else(|| die("--osc-host requires a value")),
|
||||
"--midi" => midi = true,
|
||||
"--no-midi" => midi = false,
|
||||
"--midi-name" => {
|
||||
midi = true;
|
||||
midi_name = Some(args.next().unwrap_or_else(|| die("--midi-name requires a value")));
|
||||
}
|
||||
"--list-midi" => {
|
||||
let ports = MidiOut::list_ports();
|
||||
if ports.is_empty() {
|
||||
println!("no MIDI output ports found");
|
||||
} else {
|
||||
for p in ports {
|
||||
println!("{p}");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
"-h" | "--help" => {
|
||||
print!("{USAGE}");
|
||||
return;
|
||||
}
|
||||
_ if path.is_none() && !arg.starts_with('-') => path = Some(arg),
|
||||
_ => die(USAGE),
|
||||
}
|
||||
}
|
||||
|
||||
let mut outputs = EventOutputs::default();
|
||||
if let Some(port) = udp_port {
|
||||
outputs.udp =
|
||||
Some(UdpOut::new(&udp_host, port).unwrap_or_else(|e| die(&format!("UDP setup failed: {e}"))));
|
||||
}
|
||||
if let Some(port) = osc_port {
|
||||
outputs.osc =
|
||||
Some(OscOut::new(&osc_host, port).unwrap_or_else(|e| die(&format!("OSC setup failed: {e}"))));
|
||||
}
|
||||
if midi {
|
||||
outputs.midi = Some(MidiOut::open(midi_name.as_deref()).unwrap_or_else(|e| {
|
||||
die(&format!("MIDI setup failed: {e} (use --no-midi to run without MIDI)"))
|
||||
}));
|
||||
}
|
||||
|
||||
if let Err(e) = tui::run(TuiConfig { path, bpm, seed, outputs }) {
|
||||
die(&e);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user