finish applying port autoconnect refactor, move entry point to top level, update usage

This commit is contained in:
🪞👃🪞 2025-01-09 21:56:47 +01:00
parent fe70b57dc1
commit c3de403645
9 changed files with 126 additions and 102 deletions

View file

@ -6,3 +6,7 @@ version = "0.2.0"
[dependencies]
tek = { path = "./tek" }
clap = { version = "4.5.4", features = [ "derive" ] }
[[bin]]
name = "tek"
path = "./tek.rs"

View file

@ -6,34 +6,31 @@ status:
cloc --by-file src/
git status
upstreams := "codeberg origin" # TODO!
amend:
git commit --amend
push:
git push -u codeberg main
git push -u origin main
git push -u codeberg main && git push -u origin main
tpush:
git push --tags -u codeberg
git push --tags -u origin
git push --tags -u codeberg && git push --tags -u origin
fpush:
git push -fu codeberg main
git push -fu origin main
git push -fu codeberg main && git push -fu origin main
ftpush:
git push --tags -fu codeberg
git push --tags -fu origin
git push --tags -fu codeberg && git push --tags -fu origin
run:
reset && cargo run
clock:
reset
cargo run --bin tek -- clock
reset && cargo run -- clock
clock-release:
reset
cargo run --release -- clock
reset && cargo run --release -- clock
arranger:
reset
cargo run --bin tek -- arranger
reset && cargo run --bin tek -- arranger
arranger-ext:
reset
cargo run --bin tek -- arranger -n tek \
reset && cargo run --bin tek -- arranger -n tek \
-i "1=Midi-Bridge:nanoKEY Studio 2:(capture_0) nanoKEY Studio nanoKEY Studio _" \
-o "1=Midi-Bridge:Komplete Audio 6 1:(playback_0) Komplete Audio 6 MIDI 1" \
-i "2=Midi-Bridge:nanoKEY Studio 2:(capture_0) nanoKEY Studio nanoKEY Studio _" \
@ -59,22 +56,22 @@ arranger-release-ext:
groovebox:
reset
cargo run -- groovebox -b 174
cargo run -- -n tek -b 174 groovebox
groovebox-ext:
reset
cargo run -- groovebox -n tek \
-i "Midi-Bridge:nanoKEY Studio 1:(capture_0) nanoKEY Studio nanoKEY Studio _" \
cargo run -- -n tek -b 174 groovebox \
-i "Midi-Bridge:nanoKEY Studio.*capture.*" \
-l "Komplete Audio 6 Pro:capture_AUX1" \
-r "Komplete Audio 6 Pro:capture_AUX1" \
-L "Komplete Audio 6 Pro:playback_AUX1" \
-R "Komplete Audio 6 Pro:playback_AUX1"
groovebox-release:
reset
cargo run --release -- groovebox
cargo run --release -- -n tek -b 174 groovebox
groovebox-release-ext:
reset
cargo run --release -- groovebox -n tek \
-i "Midi-Bridge:nanoKEY Studio 1:(capture_0) nanoKEY Studio nanoKEY Studio _" \
cargo run --release -- -n tek -b 174 groovebox \
-i "Midi-Bridge:nanoKEY Studio.*capture.*" \
-l "Komplete Audio 6 Pro:capture_AUX1" \
-r "Komplete Audio 6 Pro:capture_AUX1" \
-L "Komplete Audio 6 Pro:playback_AUX1" \

View file

@ -15,11 +15,13 @@ or [**matrix** `@unspeaker:matrix.org`](https://matrix.to/#/@unspeaker:matrix.or
![Screenshot](https://codeberg.org/unspeaker/tek/releases/download/0.2.0-rc.7/Screenshot%20From%202025-01-02%2023-18-05.png)
## usage
```
Usage: tek [OPTIONS] <COMMAND>
Commands:
clock A standalone transport view
clock A standalone transport clock
sequencer A MIDI sequencer
sampler A MIDI-controlled audio sampler
groovebox Sequencer and sampler together
@ -30,13 +32,21 @@ Commands:
help Print this message or the help of the given subcommand(s)
Options:
-t, --name <NAME> Name of JACK client
-S, --sync-lead Whether to attempt to become transport master
-s, --sync-follow Whether to sync to external transport master
-b, --bpm <BPM> Initial tempo in beats per minute
-h, --help Print help
-V, --version Print version
-n, --name <NAME> Name of JACK client
-S, --sync-lead Whether to attempt to become transport master
-s, --sync-follow Whether to sync to external transport master
-b, --bpm <BPM> Initial tempo in beats per minute
-t, --show-clock Whether to include a transport toolbar (default: true)
-I, --midi-from <MIDI_FROM> MIDI outs to connect to (multiple instances accepted)
-i, --midi-from-re <MIDI_FROM_RE> MIDI outs to connect to (multiple instances accepted)
-O, --midi-to <MIDI_TO> MIDI ins to connect to (multiple instances accepted)
-o, --midi-to-re <MIDI_TO_RE> MIDI ins to connect to (multiple instances accepted)
-l, --left-from <LEFT_FROM> Audio outs to connect to left input
-r, --right-from <RIGHT_FROM> Audio outs to connect to right input
-L, --left-to <LEFT_TO> Audio ins to connect from left output
-R, --right-to <RIGHT_TO> Audio ins to connect from right output
-h, --help Print help
-V, --version Print version
```
the project roadmap is at https://codeberg.org/unspeaker/tek/milestones

View file

@ -62,6 +62,15 @@ pub struct PortConnection {
pub status: Vec<(Port<Unowned>, PortConnectionStatus)>,
}
impl PortConnection {
pub fn collect (exact: &[impl AsRef<str>], re: &[impl AsRef<str>], re_all: &[impl AsRef<str>])
-> Vec<Self>
{
let mut connections = vec![];
for port in exact.iter() { connections.push(Self::exact(port)) }
for port in re.iter() { connections.push(Self::regexp(port)) }
for port in re_all.iter() { connections.push(Self::regexp_all(port)) }
connections
}
/// Connect to this exact port
pub fn exact (name: impl AsRef<str>) -> Self {
let name = PortConnectionName::Exact(name.as_ref().into());

View file

@ -8,7 +8,7 @@ pub struct TekCli {
/// Which app to initialize
#[command(subcommand)] mode: TekMode,
/// Name of JACK client
#[arg(short='t', long)] name: Option<String>,
#[arg(short='n', long)] name: Option<String>,
/// Whether to attempt to become transport master
#[arg(short='S', long, default_value_t = false)] sync_lead: bool,
/// Whether to sync to external transport master
@ -26,13 +26,13 @@ pub struct TekCli {
/// MIDI ins to connect to (multiple instances accepted)
#[arg(short='o', long)] midi_to_re: Vec<String>,
/// Audio outs to connect to left input
#[arg(short='l', long)] l_from: Vec<String>,
#[arg(short='l', long)] left_from: Vec<String>,
/// Audio outs to connect to right input
#[arg(short='r', long)] r_from: Vec<String>,
#[arg(short='r', long)] right_from: Vec<String>,
/// Audio ins to connect from left output
#[arg(short='L', long)] l_to: Vec<String>,
#[arg(short='L', long)] left_to: Vec<String>,
/// Audio ins to connect from right output
#[arg(short='R', long)] r_to: Vec<String>,
#[arg(short='R', long)] right_to: Vec<String>,
}
#[derive(Debug, Clone, Subcommand)]
@ -48,11 +48,11 @@ pub enum TekMode {
/// Multi-track MIDI sequencer.
Arranger {
/// Number of tracks
#[arg(short = 'x', long, default_value_t = 16)] tracks: usize,
#[arg(short = 'x', long, default_value_t = 16)] tracks: usize,
/// Width of tracks
#[arg(short = 'w', long, default_value_t = 6)] track_width: usize,
#[arg(short = 'w', long, default_value_t = 6)] track_width: usize,
/// Number of scenes
#[arg(short = 'y', long, default_value_t = 8)] scenes: usize,
#[arg(short = 'y', long, default_value_t = 8)] scenes: usize,
},
/// TODO: A MIDI-controlled audio mixer
Mixer,
@ -64,16 +64,17 @@ pub enum TekMode {
/// Application entrypoint.
pub fn main () -> Usually<()> {
let cli = TekCli::parse();
let name = cli.name.as_ref().map_or("tek", |x|x.as_str());
let jack = JackConnection::new(name)?;
let engine = Tui::new()?;
let mut midi_froms = vec![];
let mut midi_tos = vec![];
for port in cli.midi_from.iter() { midi_froms.push(PortConnection::exact(port.into())) }
for port in cli.midi_from_re.iter() { midi_froms.push(PortConnection::regexp(port.into())) }
for port in cli.midi_to.iter() { midi_tos.push(PortConnection::exact(port.into())) }
for port in cli.midi_to_re.iter() { midi_tos.push(PortConnection::regexp(port.into())) }
let cli = TekCli::parse();
let name = cli.name.as_ref().map_or("tek", |x|x.as_str());
let jack = JackConnection::new(name)?;
let engine = Tui::new()?;
let empty = &[] as &[&str];
let midi_froms = PortConnection::collect(&cli.midi_from, &cli.midi_from_re, empty);
let midi_tos = PortConnection::collect(&cli.midi_to, &cli.midi_to_re, empty);
let left_froms = PortConnection::collect(&cli.left_from, empty, empty);
let left_tos = PortConnection::collect(&cli.left_to, empty, empty);
let right_froms = PortConnection::collect(&cli.right_from, empty, empty);
let right_tos = PortConnection::collect(&cli.right_to, empty, empty);
Ok(match cli.mode {
TekMode::Clock => engine.run(&jack.activate_with(|jack|Ok(TransportTui {
@ -112,7 +113,8 @@ pub fn main () -> Usually<()> {
note_lo: 36.into(),
note_pt: 36.into(),
color: ItemPalette::from(Color::Rgb(64, 128, 32)),
state: Sampler::new(jack, &"sampler", &midi_froms, &[&l_from, &r_from], &[&l_to, &r_to])?,
state: Sampler::new(jack, &"sampler", &midi_froms,
&[&left_froms, &right_froms], &[&left_tos, &right_tos])?,
}
))?)?,
@ -121,7 +123,8 @@ pub fn main () -> Usually<()> {
let color = Some(ItemColor::random().into());
let clip = Arc::new(RwLock::new(MidiClip::new("Clip", true, length, None, color)));
let mut player = MidiPlayer::new(jack, &"sequencer", Some(&clip), &midi_froms, &midi_tos)?;
let sampler = Sampler::new(jack, &"sampler", &midi_froms, &[&l_from, &r_from], &[&l_to, &r_to])?;
let sampler = Sampler::new(jack, &"sampler", &midi_froms,
&[&left_froms, &right_froms], &[&left_tos, &right_tos])?;
jack.read().unwrap().client().connect_ports(&player.midi_outs[0].port, &sampler.midi_in.port)?;
let app = Groovebox {
player,

View file

@ -27,67 +27,68 @@ impl Arranger {
&mut self,
count: usize,
width: usize,
midi_from: &[impl AsRef<str>],
midi_to: &[impl AsRef<str>],
midi_from: &[PortConnection],
midi_to: &[PortConnection],
) -> Usually<()> {
let jack = self.jack.clone();
let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random();
for i in 0..count {
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
let mut track = self.track_add(None, Some(color))?;
track.width = width;
let port = JackPort::<MidiIn>::new(&jack, &format!("{}I", &track.name), &[])?;
track.width = width;
let port = JackPort::<MidiIn>::new(&jack, &format!("{}I", &track.name), midi_from)?;
track.player.midi_ins.push(port);
let port = JackPort::<MidiOut>::new(&jack, &format!("{}O", &track.name), &[])?;
let port = JackPort::<MidiOut>::new(&jack, &format!("{}O", &track.name), midi_to)?;
track.player.midi_outs.push(port);
}
for connection in midi_from.iter() {
let mut split = connection.as_ref().split("=");
let number = split.next().unwrap().trim();
if let Ok(track) = number.parse::<usize>() {
if track < 1 {
panic!("Tracks start from 1")
}
if track > count {
panic!("Tried to connect track {track} or {count}. Pass -t {track} to increase number of tracks.")
}
if let Some(port) = split.next() {
if let Some(port) = jack.read().unwrap().client().port_by_name(port).as_ref() {
//jack.read().unwrap().client().connect_ports(port, &self.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 midi_to.iter() {
let mut split = connection.as_ref().split("=");
let number = split.next().unwrap().trim();
if let Ok(track) = number.parse::<usize>() {
if track < 1 {
panic!("Tracks start from 1")
}
if track > count {
panic!("Tried to connect track {track} or {count}. Pass -t {track} to increase number of tracks.")
}
if let Some(port) = split.next() {
if let Some(port) = jack.read().unwrap().client().port_by_name(port).as_ref() {
//jack.read().unwrap().client().connect_ports(&self.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}")
}
}
// TODO: port per track:
//for connection in midi_from.iter() {
//let mut split = connection.as_ref().split("=");
//let number = split.next().unwrap().trim();
//if let Ok(track) = number.parse::<usize>() {
//if track < 1 {
//panic!("Tracks start from 1")
//}
//if track > count {
//panic!("Tried to connect track {track} or {count}. Pass -t {track} to increase number of tracks.")
//}
//if let Some(port) = split.next() {
//if let Some(port) = jack.read().unwrap().client().port_by_name(port).as_ref() {
////jack.read().unwrap().client().connect_ports(port, &self.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 midi_to.iter() {
//let mut split = connection.as_ref().split("=");
//let number = split.next().unwrap().trim();
//if let Ok(track) = number.parse::<usize>() {
//if track < 1 {
//panic!("Tracks start from 1")
//}
//if track > count {
//panic!("Tried to connect track {track} or {count}. Pass -t {track} to increase number of tracks.")
//}
//if let Some(port) = split.next() {
//if let Some(port) = jack.read().unwrap().client().port_by_name(port).as_ref() {
////jack.read().unwrap().client().connect_ports(&self.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(())
}
}