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
36
Justfile
36
Justfile
|
|
@ -42,12 +42,15 @@ fpush:
|
||||||
ftpush:
|
ftpush:
|
||||||
git push --tags -fu codeberg && git push --tags -fu origin
|
git push --tags -fu codeberg && git push --tags -fu origin
|
||||||
|
|
||||||
debug := "reset && cargo run --"
|
debug := "reset && cargo run --"
|
||||||
release := "reset && cargo run --release --"
|
release := "reset && cargo run --release --"
|
||||||
name := "-n tek"
|
name := "-n tek"
|
||||||
bpm := "-b 174"
|
bpm := "-b 174"
|
||||||
midi-in := "-i 'Midi-Bridge:.*nanoKEY.*:.*capture.*'"
|
midi-in := "-i 'Midi-Bridge:.*nanoKEY.*:.*capture.*'"
|
||||||
midi-out := "-o 'Midi-Bridge:.*playback.*'"
|
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
|
# TODO: arranger track mappings
|
||||||
#-i "1=Midi-Bridge:nanoKEY Studio 2:(capture_0) nanoKEY Studio nanoKEY Studio _"
|
#-i "1=Midi-Bridge:nanoKEY Studio 2:(capture_0) nanoKEY Studio nanoKEY Studio _"
|
||||||
|
|
@ -82,28 +85,13 @@ groovebox:
|
||||||
{{debug}} {{name}} {{bpm}} groovebox
|
{{debug}} {{name}} {{bpm}} groovebox
|
||||||
groovebox-ext:
|
groovebox-ext:
|
||||||
reset
|
reset
|
||||||
{{debug}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} groovebox \
|
{{debug}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} {{audio-in}} {{audio-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"
|
|
||||||
groovebox-release:
|
groovebox-release:
|
||||||
{{release}} {{name}} {{bpm}} groovebox
|
{{release}} {{name}} {{bpm}} groovebox
|
||||||
groovebox-release-ext:
|
groovebox-release-ext:
|
||||||
{{release}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} groovebox \
|
{{release}} {{name}} {{bpm}} {{midi-in}} {{midi-out}} {{audio-in}} {{audio-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"
|
|
||||||
groovebox-release-ext-browser:
|
groovebox-release-ext-browser:
|
||||||
reset
|
{{release}} {{name}} {{bpm}} {{midi-in}} {{firefox-in}} {{audio-out}} groovebox
|
||||||
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"
|
|
||||||
|
|
||||||
sequencer:
|
sequencer:
|
||||||
{{debug}} {{name}} {{bpm}} sequencer
|
{{debug}} {{name}} {{bpm}} sequencer
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,22 @@ impl Tek {
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
color: ItemPalette::random(),
|
color: ItemPalette::random(),
|
||||||
clock: Clock::new(jack, bpm)?,
|
clock: Clock::new(jack, bpm)?,
|
||||||
midi_ins: vec![],
|
midi_ins: {
|
||||||
midi_outs: vec![],
|
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: SourceIter(KEYS_APP),
|
||||||
keys_clip: SourceIter(KEYS_CLIP),
|
keys_clip: SourceIter(KEYS_CLIP),
|
||||||
keys_track: SourceIter(KEYS_TRACK),
|
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)
|
self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max)
|
||||||
}
|
}
|
||||||
const WIDTH_OFFSET: usize = 1;
|
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> {
|
fn track_next_name (&self) -> Arc<str> {
|
||||||
format!("Track{:02}", self.tracks().len() + 1).into()
|
format!("Track{:02}", self.tracks().len() + 1).into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
126
tek/src/view.rs
126
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(),
|
":scene-add" => self.view_scene_add().boxed(),
|
||||||
});
|
});
|
||||||
provide_num!(u16: |self: Tek| {
|
provide_num!(u16: |self: Tek| {
|
||||||
":sidebar-w" => self.w_sidebar(),
|
":w-sidebar-w" => self.w_sidebar(),
|
||||||
":sample-h" => if self.is_editing() { 0 } else { 5 },
|
":h-sample-h" => if self.is_editing() { 0 } else { 5 },
|
||||||
":samples-w" => if self.is_editing() { 4 } else { 11 },
|
":w-samples-w" => if self.is_editing() { 4 } else { 11 },
|
||||||
":samples-y" => if self.is_editing() { 1 } else { 0 },
|
":y-samples-y" => if self.is_editing() { 1 } else { 0 },
|
||||||
":outs-y" => self.size.h().saturating_sub(self.midi_outs.len() + 2) as u16,
|
":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 {
|
macro_rules! per_track {
|
||||||
($area:expr;|$self:ident,$track:ident,$index:ident|$content:expr) => {{
|
($area:expr;|$self:ident,$track:ident,$index:ident|$content:expr) => {{
|
||||||
|
|
@ -187,18 +190,14 @@ impl Tek {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
fn view_inputs (&self) -> impl Content<TuiOut> + use<'_> {
|
fn view_inputs (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let w = self.w();
|
|
||||||
let fg = Tui::g(224);
|
let fg = Tui::g(224);
|
||||||
let bg = Tui::g(64);
|
let bg = Tui::g(64);
|
||||||
let mut h = 2.max(1 + self.midi_ins.len());
|
let conn = move|conn: &PortConnect|Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, conn.info()))));
|
||||||
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 header: ThunkBox<_> = io_header!(self, " I ", " midi ins", self.midi_ins.len(),
|
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())))),
|
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 cells: ThunkBox<_> = per_track!(self.size.w();|self, track, t|{
|
||||||
let rec = track.player.recording;
|
let rec = track.player.recording;
|
||||||
let mon = track.player.monitoring;
|
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 rec { White } else { track.color.darkest.rgb }, bg, "Recrd"),
|
||||||
Tui::fg_bg(if mon { White } else { track.color.darkest.rgb }, bg, "Monit"),
|
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,
|
||||||
Tui::fg_bg(if rec { White } else { track.color.darkest.rgb }, bg, "R▞▞▞▞"),
|
Self::wrap(bg, fg, Bsp::e(
|
||||||
Tui::fg_bg(if mon { White } else { track.color.darkest.rgb }, bg, "M▞▞▞▞"),
|
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<'_> {
|
fn view_outputs (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let fg = Tui::g(224);
|
let fg = Tui::g(224);
|
||||||
let bg = Tui::g(64);
|
let bg = Tui::g(64);
|
||||||
let mut h = 2.max(1 + self.midi_outs.len());
|
let conn = move|conn: &PortConnect|Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, conn.info()))));
|
||||||
for midi_out in self.midi_outs.iter() { h += midi_out.conn().len() }
|
|
||||||
let header: ThunkBox<_> = io_header!(self, " O ", " midi outs", self.midi_outs.len(),
|
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())))),
|
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,
|
Map::new(||output.conn().iter(), move|connect, index|map_south(index as u16, 0,
|
||||||
Tui::fg_bg(fg, bg, connect.info())))))))));
|
Fill::x(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg, connect.info()))))))))));
|
||||||
let mute = false;
|
let mute = false;
|
||||||
let solo = false;
|
let solo = false;
|
||||||
let cells: ThunkBox<_> = per_track!(self.size.w();|self, track, t|{
|
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(
|
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 mute { White } else { track.color.darkest.rgb }, bg, "Play "),
|
||||||
Tui::fg_bg(if solo { White } else { track.color.darkest.rgb }, bg, "Solo "),))),
|
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,
|
||||||
Tui::fg_bg(if mute { White } else { track.color.darkest.rgb }, bg, "P▞▞▞▞"),
|
Self::wrap(bg, fg, Bsp::e(
|
||||||
Tui::fg_bg(if solo { White } else { track.color.darkest.rgb }, bg, "S▞▞▞▞"))))))});
|
Tui::fg_bg(if mute { White } else { track.color.darkest.rgb }, bg, "P▞▞▞▞"),
|
||||||
self.view_row(self.w(), h as u16, header, cells)
|
Tui::fg_bg(if solo { White } else { track.color.darkest.rgb }, bg, "S▞▞▞▞"))))))});
|
||||||
|
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> {
|
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)))
|
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<'_> {
|
fn view_tracks (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let height = 1;
|
let height = 1;
|
||||||
let header: ThunkBox<_> =
|
let header: ThunkBox<_> =
|
||||||
|
|
@ -258,19 +287,24 @@ impl Tek {
|
||||||
});
|
});
|
||||||
self.view_row(self.w(), height, header, content)
|
self.view_row(self.w(), height, header, content)
|
||||||
}
|
}
|
||||||
fn view_track_add (&self) -> impl Content<TuiOut> + use<'_> {
|
fn scenes_sizes (&self, editing: bool, height: usize, larger: usize,)
|
||||||
let data = (self.selected.track().unwrap_or(0), self.tracks().len());
|
-> impl Iterator<Item = (usize, &Scene, usize, usize)> + Send + Sync
|
||||||
self.fmtd.write().unwrap().trks.update(Some(data),
|
{
|
||||||
rewrite!(buf, "({}/{})", data.0, data.1));
|
let mut y = 0;
|
||||||
button(" T ", Bsp::e(" track ",
|
let (selected_track, selected_scene) = match self.selected() {
|
||||||
self.fmtd.read().unwrap().trks.view.clone()))
|
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<'_> {
|
fn h_scenes (&self, editing: bool, height: usize, larger: usize) -> u16 {
|
||||||
let data = (self.selected().scene().unwrap_or(0), self.scenes().len());
|
self.scenes_sizes(editing, height, larger).last().map(|(_, _, _, y)|y as u16).unwrap_or(0)
|
||||||
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 view_scenes (&self) -> impl Content<TuiOut> + use<'_> {
|
fn view_scenes (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
let bstyle = Style::default().fg(Tui::g(0));
|
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))),
|
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> (
|
fn button <'a> (
|
||||||
key: impl Content<TuiOut> + 'a,
|
key: impl Content<TuiOut> + 'a,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(bsp/s (max/y 1 :toolbar)
|
(bsp/s (max/y 1 :toolbar)
|
||||||
(fill/x (align/c (bsp/a (fill/xy (align/e :pool))
|
(fill/x (align/c (bsp/a (fill/xy (align/e :pool))
|
||||||
(bsp/a (fill/xy (align/n (bsp/s :tracks :outputs)))
|
(bsp/a (fill/xy (align/n (bsp/s :tracks :inputs)))
|
||||||
(bsp/a (fill/x (fixed/y :outs-y (align/s :inputs)))
|
(bsp/a (fill/x (fixed/y :y-outs (align/s :outputs)))
|
||||||
(bsp/s :scenes :scene-add)))))))
|
(bsp/s :scenes :scene-add)))))))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue