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 Tempo (default: 120) --seed Random seed for the R operator (default: 1) --udp-port Send ';' operator strings over UDP to this port --udp-host UDP destination host (default: 127.0.0.1) --osc-port Send '=' operator messages over OSC to this port --osc-host 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 Connect to the MIDI output port whose name contains 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, 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 = 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 = None; let mut osc_host = "127.0.0.1".to_string(); let mut osc_port: Option = None; let mut midi = true; let mut midi_name: Option = 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); } }