mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-04-28 13:30:14 +02:00
134 lines
4.6 KiB
Rust
134 lines
4.6 KiB
Rust
include!("./lib.rs");
|
|
use tek::ArrangerTui;
|
|
pub fn main () -> Usually<()> { ArrangerCli::parse().run() }
|
|
|
|
/// Launches an interactive MIDI arranger.
|
|
#[derive(Debug, Parser)]
|
|
#[command(version, about, long_about = None)]
|
|
pub struct ArrangerCli {
|
|
/// Name of JACK client
|
|
#[arg(short, long)]
|
|
name: Option<String>,
|
|
|
|
/// Whether to include a transport toolbar (default: true)
|
|
#[arg(short, long, default_value_t = true)]
|
|
transport: bool,
|
|
|
|
/// Number of tracks
|
|
#[arg(short = 'x', long, default_value_t = 4)]
|
|
tracks: usize,
|
|
|
|
/// Number of scenes
|
|
#[arg(short, long, default_value_t = 8)]
|
|
scenes: usize,
|
|
|
|
/// MIDI outs to connect each track to.
|
|
#[arg(short='i', long)]
|
|
midi_from: Vec<String>,
|
|
|
|
/// MIDI ins to connect each track to.
|
|
#[arg(short='o', long)]
|
|
midi_to: Vec<String>,
|
|
}
|
|
|
|
impl ArrangerCli {
|
|
/// Run the arranger TUI from CLI arguments.
|
|
fn run (&self) -> Usually<()> {
|
|
let mut client_name = String::from("tek_arranger");
|
|
if let Some(name) = self.name.as_ref() {
|
|
client_name = name.clone();
|
|
}
|
|
Tui::run(JackClient::new(client_name.as_str())?.activate_with(|jack|{
|
|
let mut app = ArrangerTui::try_from(jack)?;
|
|
let jack = jack.read().unwrap();
|
|
app.color = ItemPalette::random();
|
|
add_tracks(&jack, &mut app, self)?;
|
|
add_scenes(&mut app, self.scenes)?;
|
|
Ok(app)
|
|
})?)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
fn add_tracks (jack: &JackClient, app: &mut ArrangerTui, cli: &ArrangerCli) -> Usually<()> {
|
|
let n = cli.tracks;
|
|
let track_color_1 = ItemColor::random();
|
|
let track_color_2 = ItemColor::random();
|
|
for i in 0..n {
|
|
let track = app.track_add(None, Some(
|
|
track_color_1.mix(track_color_2, i as f32 / n as f32).into()
|
|
))?;
|
|
track.width = 8;
|
|
let name = track.name.read().unwrap();
|
|
track.player.midi_ins.push(
|
|
jack.register_port(&format!("{}I", &name), MidiIn::default())?
|
|
);
|
|
track.player.midi_outs.push(
|
|
jack.register_port(&format!("{}O", &name), MidiOut::default())?
|
|
);
|
|
}
|
|
for connection in cli.midi_from.iter() {
|
|
let mut split = connection.split("=");
|
|
let number = split.next().unwrap().trim();
|
|
if let Ok(track) = number.parse::<usize>() {
|
|
if track < 1 {
|
|
panic!("Tracks are zero-indexed")
|
|
}
|
|
if track > n {
|
|
panic!("Tried to connect track {track} or {n}. Pass -t {track} to increase number of tracks.")
|
|
}
|
|
if let Some(port) = split.next() {
|
|
if let Some(port) = jack.port_by_name(port).as_ref() {
|
|
jack.client().connect_ports(port, &app.tracks[track-1].player.midi_ins[0])?;
|
|
} else {
|
|
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
|
|
}
|
|
} else {
|
|
panic!("No port specified for track {track}. Format is TRACK_NUMBER=PORT_NAME")
|
|
}
|
|
} else {
|
|
panic!("Failed to parse track number: {number}")
|
|
}
|
|
}
|
|
for connection in cli.midi_to.iter() {
|
|
let mut split = connection.split("=");
|
|
let number = split.next().unwrap().trim();
|
|
if let Ok(track) = number.parse::<usize>() {
|
|
if track < 1 {
|
|
panic!("Tracks are zero-indexed")
|
|
}
|
|
if track > n {
|
|
panic!("Tried to connect track {track} or {n}. Pass -t {track} to increase number of tracks.")
|
|
}
|
|
if let Some(port) = split.next() {
|
|
if let Some(port) = jack.port_by_name(port).as_ref() {
|
|
jack.client().connect_ports(&app.tracks[track-1].player.midi_outs[0], port)?;
|
|
} else {
|
|
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
|
|
}
|
|
} else {
|
|
panic!("No port specified for track {track}. Format is TRACK_NUMBER=PORT_NAME")
|
|
}
|
|
} else {
|
|
panic!("Failed to parse track number: {number}")
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn add_scenes (app: &mut ArrangerTui, n: usize) -> Usually<()> {
|
|
let scene_color_1 = ItemColor::random();
|
|
let scene_color_2 = ItemColor::random();
|
|
for i in 0..n {
|
|
let _scene = app.scene_add(None, Some(
|
|
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
|
|
))?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[test] fn verify_arranger_cli () {
|
|
use clap::CommandFactory;
|
|
ArrangerCli::command().debug_assert();
|
|
}
|