mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
append tracks/scenes + move cursor
This commit is contained in:
parent
5bf1bad7be
commit
a670320533
2 changed files with 86 additions and 64 deletions
|
|
@ -60,20 +60,25 @@ impl PianoHorizontal {
|
||||||
for (x, time) in (0..buf.width).map(|x|(x, x*zoom)) {
|
for (x, time) in (0..buf.width).map(|x|(x, x*zoom)) {
|
||||||
let cell = buf.get_mut(x, y).unwrap();
|
let cell = buf.get_mut(x, y).unwrap();
|
||||||
cell.set_bg(clip.color.darkest.rgb);
|
cell.set_bg(clip.color.darkest.rgb);
|
||||||
cell.set_fg(clip.color.darker.rgb);
|
if time % 384 == 0 {
|
||||||
cell.set_char(if time % 384 == 0 {
|
cell.set_fg(clip.color.darker.rgb);
|
||||||
'│'
|
cell.set_char('│');
|
||||||
} else if time % 96 == 0 {
|
} else if time % 96 == 0 {
|
||||||
'╎'
|
cell.set_fg(clip.color.dark.rgb);
|
||||||
|
cell.set_char('╎');
|
||||||
} else if time % note_len == 0 {
|
} else if time % note_len == 0 {
|
||||||
'┊'
|
cell.set_fg(clip.color.darker.rgb);
|
||||||
|
cell.set_char('┊');
|
||||||
} else if (127 - note) % 12 == 0 {
|
} else if (127 - note) % 12 == 0 {
|
||||||
'='
|
cell.set_fg(clip.color.darker.rgb);
|
||||||
|
cell.set_char('=');
|
||||||
} else if (127 - note) % 6 == 0 {
|
} else if (127 - note) % 6 == 0 {
|
||||||
'—'
|
cell.set_fg(clip.color.darker.rgb);
|
||||||
|
cell.set_char('—');
|
||||||
} else {
|
} else {
|
||||||
'·'
|
cell.set_fg(clip.color.darker.rgb);
|
||||||
});
|
cell.set_char('·');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
127
tek/src/lib.rs
127
tek/src/lib.rs
|
|
@ -225,9 +225,41 @@ impl Tek {
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
arranger.scenes_add(scenes);
|
arranger.scenes_add(scenes);
|
||||||
arranger.tracks_add(tracks, track_width, &[], &[]);
|
arranger.tracks_add(tracks, track_width, midi_froms, midi_tos);
|
||||||
|
arranger.selected = Selection::Clip(1, 1);
|
||||||
Ok(arranger)
|
Ok(arranger)
|
||||||
}
|
}
|
||||||
|
fn scenes_add (&mut self, n: usize) -> Usually<()> {
|
||||||
|
let scene_color_1 = ItemColor::random();
|
||||||
|
let scene_color_2 = ItemColor::random();
|
||||||
|
for i in 0..n {
|
||||||
|
let _ = self.scene_add(None, Some(
|
||||||
|
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn tracks_add (
|
||||||
|
&mut self,
|
||||||
|
count: usize,
|
||||||
|
width: usize,
|
||||||
|
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 mut track = self.track_add(None, Some(color))?.1;
|
||||||
|
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), midi_to)?;
|
||||||
|
track.player.midi_outs.push(port);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
fn new_groovebox (
|
fn new_groovebox (
|
||||||
jack: &Arc<RwLock<JackConnection>>,
|
jack: &Arc<RwLock<JackConnection>>,
|
||||||
bpm: Option<f64>,
|
bpm: Option<f64>,
|
||||||
|
|
@ -300,7 +332,6 @@ impl Tek {
|
||||||
// TODO: sync follow
|
// TODO: sync follow
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn editor (&self) -> impl Content<TuiOut> + '_ { &self.editor }
|
|
||||||
fn view_clock (&self) -> impl Content<TuiOut> + use<'_> {
|
fn view_clock (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
Outer(Style::default().fg(TuiTheme::g(0))).enclose(row!(
|
Outer(Style::default().fg(TuiTheme::g(0))).enclose(row!(
|
||||||
self.view_engine_stats(), " ",
|
self.view_engine_stats(), " ",
|
||||||
|
|
@ -422,7 +453,7 @@ impl Tek {
|
||||||
&mut self,
|
&mut self,
|
||||||
name: Option<&str>,
|
name: Option<&str>,
|
||||||
color: Option<ItemPalette>
|
color: Option<ItemPalette>
|
||||||
) -> Usually<&mut Track> {
|
) -> Usually<(usize, &mut Track)> {
|
||||||
let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into());
|
let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into());
|
||||||
let track = Track {
|
let track = Track {
|
||||||
width: (name.len() + 2).max(9),
|
width: (name.len() + 2).max(9),
|
||||||
|
|
@ -439,28 +470,7 @@ impl Tek {
|
||||||
scene.clips.push(None);
|
scene.clips.push(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(&mut self.tracks_mut()[index])
|
Ok((index, &mut self.tracks_mut()[index]))
|
||||||
}
|
|
||||||
fn tracks_add (
|
|
||||||
&mut self,
|
|
||||||
count: usize,
|
|
||||||
width: usize,
|
|
||||||
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 mut track = self.track_add(None, Some(color))?;
|
|
||||||
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), midi_to)?;
|
|
||||||
track.player.midi_outs.push(port);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
fn track_del (&mut self, index: usize) {
|
fn track_del (&mut self, index: usize) {
|
||||||
self.tracks_mut().remove(index);
|
self.tracks_mut().remove(index);
|
||||||
|
|
@ -469,7 +479,7 @@ impl Tek {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
||||||
-> Usually<&mut Scene>
|
-> Usually<(usize, &mut Scene)>
|
||||||
{
|
{
|
||||||
let scene = Scene {
|
let scene = Scene {
|
||||||
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
|
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
|
||||||
|
|
@ -478,17 +488,7 @@ impl Tek {
|
||||||
};
|
};
|
||||||
self.scenes_mut().push(scene);
|
self.scenes_mut().push(scene);
|
||||||
let index = self.scenes().len() - 1;
|
let index = self.scenes().len() - 1;
|
||||||
Ok(&mut self.scenes_mut()[index])
|
Ok((index, &mut self.scenes_mut()[index]))
|
||||||
}
|
|
||||||
fn scenes_add (&mut self, n: usize) -> Usually<()> {
|
|
||||||
let scene_color_1 = ItemColor::random();
|
|
||||||
let scene_color_2 = ItemColor::random();
|
|
||||||
for i in 0..n {
|
|
||||||
let _scene = self.scene_add(None, Some(
|
|
||||||
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
|
|
||||||
))?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
fn clip_columns <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
fn clip_columns <'a> (&'a self) -> BoxThunk<'a, TuiOut> {
|
||||||
let editing = self.is_editing();
|
let editing = self.is_editing();
|
||||||
|
|
@ -511,15 +511,14 @@ impl Tek {
|
||||||
("⏹ ".to_string(), TuiTheme::g(64), TuiTheme::g(32))
|
("⏹ ".to_string(), TuiTheme::g(64), TuiTheme::g(32))
|
||||||
};
|
};
|
||||||
let same_track = selected_track == Some(t+1);
|
let same_track = selected_track == Some(t+1);
|
||||||
let selected = same_track && Some(s+1) == selected_scene;
|
let selected = same_track && Some(s+1) == selected_scene;
|
||||||
let neighbor = same_track && Some(s) == selected_scene;
|
let neighbor = same_track && Some(s) == selected_scene;
|
||||||
let active = editing && selected;
|
let active = editing && selected;
|
||||||
let label = move||Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string())));
|
let label = move||Tui::fg(fg, Push::x(1, Tui::bold(true, name.to_string())));
|
||||||
map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(
|
map_south(y1 as u16, h, Push::y(1, Fixed::y(h, Either::new(active,
|
||||||
active,
|
|
||||||
Thunk::new(||Bsp::a(
|
Thunk::new(||Bsp::a(
|
||||||
Fill::xy(Align::nw(button(" Tab ".into(), "".into()))),
|
Fill::xy(Align::nw(button(" Tab ".into(), "".into()))),
|
||||||
self.editor())),
|
&self.editor)),
|
||||||
Thunk::new(move||Bsp::a(
|
Thunk::new(move||Bsp::a(
|
||||||
When::new(selected, Fill::y(Align::n(button(" Tab ".into(), "edit".into())))),
|
When::new(selected, Fill::y(Align::n(button(" Tab ".into(), "edit".into())))),
|
||||||
phat_sel_3(
|
phat_sel_3(
|
||||||
|
|
@ -704,13 +703,13 @@ command!(|self: TekCommand, app: Tek|match self {
|
||||||
if let Some(ref pool) = app.pool {
|
if let Some(ref pool) = app.pool {
|
||||||
if app.is_editing() {
|
if app.is_editing() {
|
||||||
if let Selection::Clip(t, s) = app.selected {
|
if let Selection::Clip(t, s) = app.selected {
|
||||||
if let Some(scene) = app.scenes.get_mut(s) {
|
if let Some(scene) = app.scenes.get_mut(s.saturating_sub(1)) {
|
||||||
if let Some(slot) = scene.clips.get_mut(t) {
|
if let Some(slot) = scene.clips.get_mut(t.saturating_sub(1)) {
|
||||||
if slot.is_none() {
|
if slot.is_none() {
|
||||||
let (index, mut clip) = pool.add_new_clip();
|
let (index, mut clip) = pool.add_new_clip();
|
||||||
// autocolor: new clip colors from scene and track color
|
// autocolor: new clip colors from scene and track color
|
||||||
clip.write().unwrap().color = ItemColor::random_near(
|
clip.write().unwrap().color = ItemColor::random_near(
|
||||||
app.tracks[t].color.base.mix(scene.color.base, 0.5),
|
app.tracks[t.saturating_sub(1)].color.base.mix(scene.color.base, 0.5),
|
||||||
0.2
|
0.2
|
||||||
).into();
|
).into();
|
||||||
if let Some(ref mut editor) = app.editor {
|
if let Some(ref mut editor) = app.editor {
|
||||||
|
|
@ -852,8 +851,17 @@ edn_command!(TrackCommand: |app: Tek| {
|
||||||
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
|
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
|
||||||
});
|
});
|
||||||
command!(|self: TrackCommand, app: Tek|match self {
|
command!(|self: TrackCommand, app: Tek|match self {
|
||||||
Self::Add => { app.track_add(None, None)?; None },
|
Self::Add => {
|
||||||
Self::Del(index) => { app.track_del(index); None },
|
use Selection::*;
|
||||||
|
let index = app.track_add(None, None)?.0 + 1;
|
||||||
|
app.selected = match app.selected {
|
||||||
|
Track(t) => Track(index),
|
||||||
|
Clip(t, s) => Clip(index, s),
|
||||||
|
_ => app.selected
|
||||||
|
};
|
||||||
|
Some(Self::Del(index))
|
||||||
|
},
|
||||||
|
Self::Del(index) => { app.track_del(index); None },
|
||||||
Self::Stop(track) => { app.tracks[track].player.enqueue_next(None); None },
|
Self::Stop(track) => { app.tracks[track].player.enqueue_next(None); None },
|
||||||
Self::SetColor(index, color) => {
|
Self::SetColor(index, color) => {
|
||||||
let old = app.tracks[index].color;
|
let old = app.tracks[index].color;
|
||||||
|
|
@ -881,12 +889,12 @@ trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
|
||||||
{
|
{
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
let active = match self.selected() {
|
let active = match self.selected() {
|
||||||
Selection::Track(t) if editing => Some(t),
|
Selection::Track(t) if editing => Some(t.saturating_sub(1)),
|
||||||
Selection::Clip(t, _) if editing => Some(t),
|
Selection::Clip(t, _) if editing => Some(t.saturating_sub(1)),
|
||||||
_ => None
|
_ => None
|
||||||
};
|
};
|
||||||
self.tracks().iter().enumerate().map(move |(index, track)|{
|
self.tracks().iter().enumerate().map(move |(index, track)|{
|
||||||
let width = if Some(&index) == active { bigger } else { track.width.max(8) };
|
let width = if Some(index) == active { bigger } else { track.width.max(8) };
|
||||||
let data = (index, track, x, x + width);
|
let data = (index, track, x, x + width);
|
||||||
x += width;
|
x += width;
|
||||||
data
|
data
|
||||||
|
|
@ -1014,7 +1022,16 @@ edn_command!(SceneCommand: |app: Tek| {
|
||||||
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
|
("swap" [a: usize, b: usize] Self::Swap(a.unwrap(), b.unwrap()))
|
||||||
});
|
});
|
||||||
command!(|self: SceneCommand, app: Tek|match self {
|
command!(|self: SceneCommand, app: Tek|match self {
|
||||||
Self::Add => { app.scene_add(None, None)?; None }
|
Self::Add => {
|
||||||
|
use Selection::*;
|
||||||
|
let index = app.scene_add(None, None)?.0 + 1;
|
||||||
|
app.selected = match app.selected {
|
||||||
|
Scene(s) => Scene(index),
|
||||||
|
Clip(t, s) => Clip(t, index),
|
||||||
|
_ => app.selected
|
||||||
|
};
|
||||||
|
Some(Self::Del(index))
|
||||||
|
},
|
||||||
Self::Del(index) => { app.scene_del(index); None },
|
Self::Del(index) => { app.scene_del(index); None },
|
||||||
Self::SetColor(index, color) => {
|
Self::SetColor(index, color) => {
|
||||||
let old = app.scenes[index].color;
|
let old = app.scenes[index].color;
|
||||||
|
|
@ -1044,11 +1061,11 @@ trait HasScenes: HasSelection + HasEditor + Send + Sync {
|
||||||
{
|
{
|
||||||
let mut y = 0;
|
let mut y = 0;
|
||||||
let (selected_track, selected_scene) = match self.selected() {
|
let (selected_track, selected_scene) = match self.selected() {
|
||||||
Selection::Clip(t, s) => (Some(t), Some(s)),
|
Selection::Clip(t, s) => (Some(t.saturating_sub(1)), Some(s.saturating_sub(1))),
|
||||||
_ => (None, None)
|
_ => (None, None)
|
||||||
};
|
};
|
||||||
self.scenes().iter().enumerate().map(move|(s, scene)|{
|
self.scenes().iter().enumerate().map(move|(s, scene)|{
|
||||||
let active = editing && selected_track.is_some() && selected_scene == Some(&s);
|
let active = editing && selected_track.is_some() && selected_scene == Some(s);
|
||||||
let height = if active { larger } else { height };
|
let height = if active { larger } else { height };
|
||||||
let data = (s, scene, y, y + height);
|
let data = (s, scene, y, y + height);
|
||||||
y += height;
|
y += height;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue