mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: figuring out sane layout for midi routings
This commit is contained in:
parent
923568d7f9
commit
4eff4316c6
5 changed files with 117 additions and 83 deletions
24
Justfile
24
Justfile
|
|
@ -48,6 +48,9 @@ name := "-n tek"
|
|||
bpm := "-b 174"
|
||||
midi-in := "-i 'Midi-Bridge:.*nanoKEY.*:.*capture.*'"
|
||||
midi-out := "-o 'Midi-Bridge:.*playback.*'"
|
||||
audio-in := "-l 'Komplete Audio 6 Pro:capture_AUX1' -r 'Komplete Audio 6 Pro:capture_AUX1'"
|
||||
audio-out := "-L 'Komplete Audio 6 Pro:playback_AUX1' -R 'Komplete Audio 6 Pro:playback_AUX1'"
|
||||
firefox-in := "-l 'Firefox:output_FL' -r 'Firefox:output_FR'"
|
||||
|
||||
# TODO: arranger track mappings
|
||||
#-i "1=Midi-Bridge:nanoKEY Studio 2:(capture_0) nanoKEY Studio nanoKEY Studio _"
|
||||
|
|
@ -82,28 +85,13 @@ groovebox:
|
|||
{{debug}} {{name}} {{bpm}} groovebox
|
||||
groovebox-ext:
|
||||
reset
|
||||
{{debug}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} groovebox \
|
||||
-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"
|
||||
{{debug}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} {{audio-in}} {{audio-out}} groovebox
|
||||
groovebox-release:
|
||||
{{release}} {{name}} {{bpm}} groovebox
|
||||
groovebox-release-ext:
|
||||
{{release}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} groovebox \
|
||||
-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_AUX2"
|
||||
{{release}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} {{audio-in}} {{audio-out}} groovebox
|
||||
groovebox-release-ext-browser:
|
||||
reset
|
||||
cargo run --release -- groovebox -n tek \
|
||||
-b 112 \
|
||||
-i "Midi-Bridge:nanoKEY Studio 1:(capture_0) nanoKEY Studio nanoKEY Studio _" \
|
||||
-l "Firefox:output_FL" \
|
||||
-r "Firefox:output_FR" \
|
||||
-L "Komplete Audio 6 Pro:playback_AUX1" \
|
||||
-R "Komplete Audio 6 Pro:playback_AUX2"
|
||||
{{release}} {{name}} {{bpm}} {{midi-in}} {{firefox-in}} {{audio-out}} groovebox
|
||||
|
||||
sequencer:
|
||||
{{debug}} {{name}} {{bpm}} sequencer
|
||||
|
|
|
|||
|
|
@ -104,8 +104,22 @@ impl Tek {
|
|||
jack: jack.clone(),
|
||||
color: ItemPalette::random(),
|
||||
clock: Clock::new(jack, bpm)?,
|
||||
midi_ins: vec![],
|
||||
midi_outs: vec![],
|
||||
midi_ins: {
|
||||
let mut midi_ins = vec![];
|
||||
for (index, connect) in midi_froms.iter().enumerate() {
|
||||
let port = JackMidiIn::new(jack, &format!("m/{index}"), &[connect.clone()])?;
|
||||
midi_ins.push(port);
|
||||
}
|
||||
midi_ins
|
||||
},
|
||||
midi_outs: {
|
||||
let mut midi_outs = vec![];
|
||||
for (index, connect) in midi_tos.iter().enumerate() {
|
||||
let port = JackMidiOut::new(jack, &format!("{index}/m"), &[connect.clone()])?;
|
||||
midi_outs.push(port);
|
||||
}
|
||||
midi_outs
|
||||
},
|
||||
keys: SourceIter(KEYS_APP),
|
||||
keys_clip: SourceIter(KEYS_CLIP),
|
||||
keys_track: SourceIter(KEYS_TRACK),
|
||||
|
|
|
|||
|
|
@ -269,22 +269,6 @@ pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync
|
|||
self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max)
|
||||
}
|
||||
const WIDTH_OFFSET: usize = 1;
|
||||
fn tracks_sizes <'a> (&'a self, editing: bool, bigger: usize)
|
||||
-> impl Iterator<Item=(usize, &'a Track, usize, usize)> + Send + Sync + 'a
|
||||
{
|
||||
let mut x = 0;
|
||||
let active = match self.selected() {
|
||||
Selection::Track(t) if editing => Some(t.saturating_sub(1)),
|
||||
Selection::Clip(t, _) if editing => Some(t.saturating_sub(1)),
|
||||
_ => None
|
||||
};
|
||||
self.tracks().iter().enumerate().map(move |(index, track)|{
|
||||
let width = if Some(index) == active { bigger } else { track.width.max(8) };
|
||||
let data = (index, track, x, x + width);
|
||||
x += width + Self::WIDTH_OFFSET;
|
||||
data
|
||||
})
|
||||
}
|
||||
fn track_next_name (&self) -> Arc<str> {
|
||||
format!("Track{:02}", self.tracks().len() + 1).into()
|
||||
}
|
||||
|
|
|
|||
116
tek/src/view.rs
116
tek/src/view.rs
|
|
@ -63,11 +63,14 @@ view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); {
|
|||
":scene-add" => self.view_scene_add().boxed(),
|
||||
});
|
||||
provide_num!(u16: |self: Tek| {
|
||||
":sidebar-w" => self.w_sidebar(),
|
||||
":sample-h" => if self.is_editing() { 0 } else { 5 },
|
||||
":samples-w" => if self.is_editing() { 4 } else { 11 },
|
||||
":samples-y" => if self.is_editing() { 1 } else { 0 },
|
||||
":outs-y" => self.size.h().saturating_sub(self.midi_outs.len() + 2) as u16,
|
||||
":w-sidebar-w" => self.w_sidebar(),
|
||||
":h-sample-h" => if self.is_editing() { 0 } else { 5 },
|
||||
":w-samples-w" => if self.is_editing() { 4 } else { 11 },
|
||||
":y-samples-y" => if self.is_editing() { 1 } else { 0 },
|
||||
":h-ins" => self.h_inputs(),
|
||||
":h-outs" => self.h_outputs(),
|
||||
":y-ins" => (self.size.h() as u16).saturating_sub(self.h_inputs()),
|
||||
":y-outs" => (self.size.h() as u16).saturating_sub(self.h_outputs()),
|
||||
});
|
||||
macro_rules! per_track {
|
||||
($area:expr;|$self:ident,$track:ident,$index:ident|$content:expr) => {{
|
||||
|
|
@ -187,18 +190,14 @@ impl Tek {
|
|||
))
|
||||
}
|
||||
fn view_inputs (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let w = self.w();
|
||||
let fg = Tui::g(224);
|
||||
let bg = Tui::g(64);
|
||||
let mut h = 2.max(1 + self.midi_ins.len());
|
||||
for midi_in in self.midi_ins.iter() { h += midi_in.conn().len() }
|
||||
let conn = move|conn: &PortConnect|{
|
||||
Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, conn.info()))))
|
||||
};
|
||||
let conn = move|conn: &PortConnect|Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, conn.info()))));
|
||||
let header: ThunkBox<_> = io_header!(self, " I ", " midi ins", self.midi_ins.len(),
|
||||
Map::new(||self.midi_ins.iter(), move|input, index|map_south(index as u16, 1u16, Bsp::s(
|
||||
Map::new(||self.midi_ins.iter(), move|input, index|map_south(index as u16, 1, Bsp::s(
|
||||
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(input.name())))),
|
||||
input.conn().get(0).map(conn)))));
|
||||
Map::new(||input.conn().iter(), move|connect, index|map_south(index as u16, 0,
|
||||
Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, connect.info()))))))))));
|
||||
let cells: ThunkBox<_> = per_track!(self.size.w();|self, track, t|{
|
||||
let rec = track.player.recording;
|
||||
let mon = track.player.monitoring;
|
||||
|
|
@ -209,22 +208,27 @@ impl Tek {
|
|||
Tui::fg_bg(if rec { White } else { track.color.darkest.rgb }, bg, "Recrd"),
|
||||
Tui::fg_bg(if mon { White } else { track.color.darkest.rgb }, bg, "Monit"),
|
||||
))),
|
||||
Map::new(||self.midi_outs.iter(), move|output, index|map_south(index as u16, 1u16, Self::wrap(bg, fg, Bsp::e(
|
||||
Map::new(||self.midi_ins.iter(), move|input, index|map_south(index as u16, 1,
|
||||
Self::wrap(bg, fg, Bsp::e(
|
||||
Tui::fg_bg(if rec { White } else { track.color.darkest.rgb }, bg, "R▞▞▞▞"),
|
||||
Tui::fg_bg(if mon { White } else { track.color.darkest.rgb }, bg, "M▞▞▞▞"),
|
||||
)))))});
|
||||
self.view_row(w, h as u16, header, cells)
|
||||
Tui::bg(Black, self.view_row(self.w(), self.h_inputs(), header, cells))
|
||||
}
|
||||
fn h_inputs (&self) -> u16 {
|
||||
let mut h = 1;
|
||||
for midi_in in self.midi_ins.iter() { h += 1 + midi_in.conn().len() as u16 }
|
||||
h
|
||||
}
|
||||
fn view_outputs (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let fg = Tui::g(224);
|
||||
let bg = Tui::g(64);
|
||||
let mut h = 2.max(1 + self.midi_outs.len());
|
||||
for midi_out in self.midi_outs.iter() { h += midi_out.conn().len() }
|
||||
let conn = move|conn: &PortConnect|Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, conn.info()))));
|
||||
let header: ThunkBox<_> = io_header!(self, " O ", " midi outs", self.midi_outs.len(),
|
||||
Map::new(||self.midi_outs.iter(), move|output, index|map_south(index as u16, 1u16, Bsp::s(
|
||||
Map::new(||self.midi_outs.iter(), move|output, index|map_south(index as u16, 0, Bsp::s(
|
||||
Fill::x(Tui::bold(true, Tui::fg_bg(fg, bg, Align::w(output.name())))),
|
||||
output.conn().get(0).map(|connect|Fill::x(Align::w(Tui::bold(false,
|
||||
Tui::fg_bg(fg, bg, connect.info())))))))));
|
||||
Map::new(||output.conn().iter(), move|connect, index|map_south(index as u16, 0,
|
||||
Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, connect.info()))))))))));
|
||||
let mute = false;
|
||||
let solo = false;
|
||||
let cells: ThunkBox<_> = per_track!(self.size.w();|self, track, t|{
|
||||
|
|
@ -234,14 +238,39 @@ impl Tek {
|
|||
Self::wrap(bg, fg, Tui::bold(true, Bsp::e(
|
||||
Tui::fg_bg(if mute { White } else { track.color.darkest.rgb }, bg, "Play "),
|
||||
Tui::fg_bg(if solo { White } else { track.color.darkest.rgb }, bg, "Solo "),))),
|
||||
Map::new(||self.midi_outs.iter(), move|output, index|map_south(index as u16, 1u16, Self::wrap(bg, fg, Bsp::e(
|
||||
Map::new(||self.midi_outs.iter(), move|output, index|map_south(index as u16, 1u16,
|
||||
Self::wrap(bg, fg, Bsp::e(
|
||||
Tui::fg_bg(if mute { White } else { track.color.darkest.rgb }, bg, "P▞▞▞▞"),
|
||||
Tui::fg_bg(if solo { White } else { track.color.darkest.rgb }, bg, "S▞▞▞▞"))))))});
|
||||
self.view_row(self.w(), h as u16, header, cells)
|
||||
Tui::bg(Black, self.view_row(self.w(), self.h_outputs(), header, cells))
|
||||
}
|
||||
fn h_outputs (&self) -> u16 {
|
||||
let mut h = 1;
|
||||
for midi_out in self.midi_outs.iter() { h += 1 + midi_out.conn().len() as u16 }
|
||||
h
|
||||
}
|
||||
fn wrap (bg: Color, fg: Color, content: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
||||
Bsp::e(Tui::fg_bg(bg, Reset, "▐"), Bsp::w(Tui::fg_bg(bg, Reset, "▌"), Tui::fg_bg(fg, bg, content)))
|
||||
}
|
||||
fn tracks_sizes <'a> (&'a self, editing: bool, bigger: usize)
|
||||
-> impl Iterator<Item=(usize, &'a Track, usize, usize)> + Send + Sync + 'a
|
||||
{
|
||||
let mut x = 0;
|
||||
let active = match self.selected() {
|
||||
Selection::Track(t) if editing => Some(t.saturating_sub(1)),
|
||||
Selection::Clip(t, _) if editing => Some(t.saturating_sub(1)),
|
||||
_ => None
|
||||
};
|
||||
self.tracks().iter().enumerate().map(move |(index, track)|{
|
||||
let width = if Some(index) == active { bigger } else { track.width.max(8) };
|
||||
let data = (index, track, x, x + width);
|
||||
x += width + Self::WIDTH_OFFSET;
|
||||
data
|
||||
})
|
||||
}
|
||||
fn w_tracks (&self, editing: bool, bigger: usize) -> u16 {
|
||||
self.tracks_sizes(editing, bigger).last().map(|(_, _, _, x)|x as u16).unwrap_or(0)
|
||||
}
|
||||
fn view_tracks (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let height = 1;
|
||||
let header: ThunkBox<_> =
|
||||
|
|
@ -258,19 +287,24 @@ impl Tek {
|
|||
});
|
||||
self.view_row(self.w(), height, header, content)
|
||||
}
|
||||
fn view_track_add (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let data = (self.selected.track().unwrap_or(0), self.tracks().len());
|
||||
self.fmtd.write().unwrap().trks.update(Some(data),
|
||||
rewrite!(buf, "({}/{})", data.0, data.1));
|
||||
button(" T ", Bsp::e(" track ",
|
||||
self.fmtd.read().unwrap().trks.view.clone()))
|
||||
fn scenes_sizes (&self, editing: bool, height: usize, larger: usize,)
|
||||
-> impl Iterator<Item = (usize, &Scene, usize, usize)> + Send + Sync
|
||||
{
|
||||
let mut y = 0;
|
||||
let (selected_track, selected_scene) = match self.selected() {
|
||||
Selection::Clip(t, s) => (Some(t.saturating_sub(1)), Some(s.saturating_sub(1))),
|
||||
_ => (None, None)
|
||||
};
|
||||
self.scenes().iter().enumerate().map(move|(s, scene)|{
|
||||
let active = editing && selected_track.is_some() && selected_scene == Some(s);
|
||||
let height = if active { larger } else { height };
|
||||
let data = (s, scene, y, y + height);
|
||||
y += height;
|
||||
data
|
||||
})
|
||||
}
|
||||
fn view_scene_add (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let data = (self.selected().scene().unwrap_or(0), self.scenes().len());
|
||||
self.fmtd.write().unwrap().scns.update(Some(data),
|
||||
rewrite!(buf, "({}/{})", data.0, data.1));
|
||||
button(" C-a ", Bsp::e(" add scene ",
|
||||
self.fmtd.read().unwrap().scns.view.clone()))
|
||||
fn h_scenes (&self, editing: bool, height: usize, larger: usize) -> u16 {
|
||||
self.scenes_sizes(editing, height, larger).last().map(|(_, _, _, y)|y as u16).unwrap_or(0)
|
||||
}
|
||||
fn view_scenes (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let bstyle = Style::default().fg(Tui::g(0));
|
||||
|
|
@ -377,6 +411,20 @@ impl Tek {
|
|||
When::new(compact, Margin::x(1, Tui::fg_bg(Tui::g(255), Tui::g(96), label))),
|
||||
))
|
||||
}
|
||||
fn view_track_add (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let data = (self.selected.track().unwrap_or(0), self.tracks().len());
|
||||
self.fmtd.write().unwrap().trks.update(Some(data),
|
||||
rewrite!(buf, "({}/{})", data.0, data.1));
|
||||
button(" T ", Bsp::e(" track ",
|
||||
self.fmtd.read().unwrap().trks.view.clone()))
|
||||
}
|
||||
fn view_scene_add (&self) -> impl Content<TuiOut> + use<'_> {
|
||||
let data = (self.selected().scene().unwrap_or(0), self.scenes().len());
|
||||
self.fmtd.write().unwrap().scns.update(Some(data),
|
||||
rewrite!(buf, "({}/{})", data.0, data.1));
|
||||
button(" C-a ", Bsp::e(" add scene ",
|
||||
self.fmtd.read().unwrap().scns.view.clone()))
|
||||
}
|
||||
}
|
||||
fn button <'a> (
|
||||
key: impl Content<TuiOut> + 'a,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
(bsp/s (max/y 1 :toolbar)
|
||||
(fill/x (align/c (bsp/a (fill/xy (align/e :pool))
|
||||
(bsp/a (fill/xy (align/n (bsp/s :tracks :outputs)))
|
||||
(bsp/a (fill/x (fixed/y :outs-y (align/s :inputs)))
|
||||
(bsp/a (fill/xy (align/n (bsp/s :tracks :inputs)))
|
||||
(bsp/a (fill/x (fixed/y :y-outs (align/s :outputs)))
|
||||
(bsp/s :scenes :scene-add)))))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue