mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
implement midi autoconnect for arranger
This commit is contained in:
parent
f1847b62b8
commit
512e466af1
3 changed files with 107 additions and 13 deletions
22
Justfile
22
Justfile
|
|
@ -31,9 +31,31 @@ transport-release:
|
||||||
arranger:
|
arranger:
|
||||||
reset
|
reset
|
||||||
cargo run --bin tek_arranger
|
cargo run --bin tek_arranger
|
||||||
|
arranger-ext:
|
||||||
|
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 _" \
|
||||||
|
-o "2=Midi-Bridge:Komplete Audio 6 1:(playback_0) Komplete Audio 6 MIDI 1" \
|
||||||
|
-i "3=Midi-Bridge:nanoKEY Studio 2:(capture_0) nanoKEY Studio nanoKEY Studio _" \
|
||||||
|
-o "3=Midi-Bridge:Komplete Audio 6 1:(playback_0) Komplete Audio 6 MIDI 1" \
|
||||||
|
-i "4=Midi-Bridge:nanoKEY Studio 2:(capture_0) nanoKEY Studio nanoKEY Studio _" \
|
||||||
|
-o "4=Midi-Bridge:Komplete Audio 6 1:(playback_0) Komplete Audio 6 MIDI 1"
|
||||||
arranger-release:
|
arranger-release:
|
||||||
reset
|
reset
|
||||||
cargo run --release --bin tek_arranger
|
cargo run --release --bin tek_arranger
|
||||||
|
arranger-release-ext:
|
||||||
|
reset
|
||||||
|
cargo run --release --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 _" \
|
||||||
|
-o "2=Midi-Bridge:Komplete Audio 6 1:(playback_0) Komplete Audio 6 MIDI 1" \
|
||||||
|
-i "3=Midi-Bridge:nanoKEY Studio 2:(capture_0) nanoKEY Studio nanoKEY Studio _" \
|
||||||
|
-o "3=Midi-Bridge:Komplete Audio 6 1:(playback_0) Komplete Audio 6 MIDI 1" \
|
||||||
|
-i "4=Midi-Bridge:nanoKEY Studio 2:(capture_0) nanoKEY Studio nanoKEY Studio _" \
|
||||||
|
-o "4=Midi-Bridge:Komplete Audio 6 1:(playback_0) Komplete Audio 6 MIDI 1"
|
||||||
|
|
||||||
groovebox:
|
groovebox:
|
||||||
reset
|
reset
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,33 @@ pub fn main () -> Usually<()> {
|
||||||
ArrangerCli::parse().run()
|
ArrangerCli::parse().run()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses CLI arguments to the `tek_arranger` invocation.
|
/// Launches an interactive MIDI arranger.
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub struct ArrangerCli {
|
pub struct ArrangerCli {
|
||||||
/// Name of JACK client
|
/// Name of JACK client
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
|
||||||
/// Whether to include a transport toolbar (default: true)
|
/// Whether to include a transport toolbar (default: true)
|
||||||
#[arg(short, long, default_value_t = true)]
|
#[arg(short, long, default_value_t = true)]
|
||||||
transport: bool,
|
transport: bool,
|
||||||
|
|
||||||
/// Number of tracks
|
/// Number of tracks
|
||||||
#[arg(short = 'x', long, default_value_t = 4)]
|
#[arg(short = 'x', long, default_value_t = 4)]
|
||||||
tracks: usize,
|
tracks: usize,
|
||||||
|
|
||||||
/// Number of scenes
|
/// Number of scenes
|
||||||
#[arg(short, long, default_value_t = 8)]
|
#[arg(short, long, default_value_t = 8)]
|
||||||
scenes: usize,
|
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 {
|
impl ArrangerCli {
|
||||||
|
|
@ -31,8 +42,9 @@ impl ArrangerCli {
|
||||||
}
|
}
|
||||||
Tui::run(JackClient::new(client_name.as_str())?.activate_with(|jack|{
|
Tui::run(JackClient::new(client_name.as_str())?.activate_with(|jack|{
|
||||||
let mut app = ArrangerTui::try_from(jack)?;
|
let mut app = ArrangerTui::try_from(jack)?;
|
||||||
|
let jack = jack.read().unwrap();
|
||||||
app.color = ItemPalette::random();
|
app.color = ItemPalette::random();
|
||||||
add_tracks(&mut app, self.tracks)?;
|
add_tracks(&jack, &mut app, &self)?;
|
||||||
add_scenes(&mut app, self.scenes)?;
|
add_scenes(&mut app, self.scenes)?;
|
||||||
Ok(app)
|
Ok(app)
|
||||||
})?)?;
|
})?)?;
|
||||||
|
|
@ -40,7 +52,8 @@ impl ArrangerCli {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_tracks (app: &mut ArrangerTui, n: usize) -> Usually<()> {
|
fn add_tracks (jack: &JackClient, app: &mut ArrangerTui, cli: &ArrangerCli) -> Usually<()> {
|
||||||
|
let n = cli.tracks;
|
||||||
let track_color_1 = ItemColor::random();
|
let track_color_1 = ItemColor::random();
|
||||||
let track_color_2 = ItemColor::random();
|
let track_color_2 = ItemColor::random();
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
|
|
@ -48,6 +61,60 @@ fn add_tracks (app: &mut ArrangerTui, n: usize) -> Usually<()> {
|
||||||
track_color_1.mix(track_color_2, i as f32 / n as f32).into()
|
track_color_1.mix(track_color_2, i as f32 / n as f32).into()
|
||||||
))?;
|
))?;
|
||||||
track.width = 8;
|
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) {
|
||||||
|
jack.client().connect_ports(&port, &app.tracks[track-1].player.midi_ins[0])?;
|
||||||
|
//jack.client().connect_ports(&port, &app.tracks[track].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) {
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,29 @@ pub fn main () -> Usually<()> {
|
||||||
SequencerCli::parse().run()
|
SequencerCli::parse().run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Launches a single interactive MIDI sequencer.
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub struct SequencerCli {
|
pub struct SequencerCli {
|
||||||
/// Name of JACK client
|
/// Name of JACK client
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
|
||||||
|
/// Whether to include a transport toolbar (default: true)
|
||||||
|
#[arg(short, long, default_value_t = true)]
|
||||||
|
transport: bool,
|
||||||
|
|
||||||
/// MIDI outs to connect to (multiple instances accepted)
|
/// MIDI outs to connect to (multiple instances accepted)
|
||||||
#[arg(short='i', long)]
|
#[arg(short='i', long)]
|
||||||
midi_from: Vec<String>,
|
midi_from: Vec<String>,
|
||||||
|
|
||||||
/// MIDI ins to connect to (multiple instances accepted)
|
/// MIDI ins to connect to (multiple instances accepted)
|
||||||
#[arg(short='o', long)]
|
#[arg(short='o', long)]
|
||||||
midi_to: Vec<String>,
|
midi_to: Vec<String>,
|
||||||
|
|
||||||
/// Default phrase duration (in pulses; default: 4 * PPQ = 1 bar)
|
/// Default phrase duration (in pulses; default: 4 * PPQ = 1 bar)
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
length: Option<usize>,
|
length: Option<usize>,
|
||||||
/// Whether to include a transport toolbar (default: true)
|
|
||||||
#[arg(short, long, default_value_t = true)]
|
|
||||||
transport: bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SequencerCli {
|
impl SequencerCli {
|
||||||
|
|
@ -30,8 +35,8 @@ impl SequencerCli {
|
||||||
Tui::run(JackClient::new(name)?.activate_with(|jack|{
|
Tui::run(JackClient::new(name)?.activate_with(|jack|{
|
||||||
let mut app = SequencerTui::try_from(jack)?;
|
let mut app = SequencerTui::try_from(jack)?;
|
||||||
let jack = jack.read().unwrap();
|
let jack = jack.read().unwrap();
|
||||||
let midi_in = jack.register_port("in", MidiIn::default())?;
|
let midi_in = jack.register_port("i", MidiIn::default())?;
|
||||||
let midi_out = jack.register_port("out", MidiOut::default())?;
|
let midi_out = jack.register_port("o", MidiOut::default())?;
|
||||||
connect_from(&jack, &midi_in, &self.midi_from)?;
|
connect_from(&jack, &midi_in, &self.midi_from)?;
|
||||||
connect_to(&jack, &midi_out, &self.midi_to)?;
|
connect_to(&jack, &midi_out, &self.midi_to)?;
|
||||||
app.player.midi_ins.push(midi_in);
|
app.player.midi_ins.push(midi_in);
|
||||||
|
|
@ -52,7 +57,7 @@ fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> U
|
||||||
if let Some(port) = jack.port_by_name(&port) {
|
if let Some(port) = jack.port_by_name(&port) {
|
||||||
jack.client().connect_ports(&port, &input)?;
|
jack.client().connect_ports(&port, &input)?;
|
||||||
} else {
|
} else {
|
||||||
panic!("Missing MIDI output: {port}");
|
panic!("Missing MIDI output: {port}. Use jack_lsp to list all port names.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -63,7 +68,7 @@ fn connect_to (jack: &JackClient, output: &Port<MidiOut>, ports: &[String]) -> U
|
||||||
if let Some(port) = jack.port_by_name(&port) {
|
if let Some(port) = jack.port_by_name(&port) {
|
||||||
jack.client().connect_ports(&output, &port)?;
|
jack.client().connect_ports(&output, &port)?;
|
||||||
} else {
|
} else {
|
||||||
panic!("Missing MIDI output: {port}");
|
panic!("Missing MIDI input: {port}. Use jack_lsp to list all port names.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue