mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-04-30 22:30:13 +02:00
finish applying port autoconnect refactor, move entry point to top level, update usage
This commit is contained in:
parent
fe70b57dc1
commit
c3de403645
|
@ -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"
|
||||
|
|
41
Justfile
41
Justfile
|
@ -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" \
|
||||
|
|
26
README.md
26
README.md
|
@ -15,11 +15,13 @@ or [**matrix** `@unspeaker:matrix.org`](https://matrix.to/#/@unspeaker:matrix.or
|
|||
|
||||

|
||||
|
||||
## 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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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,
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue