From 4ebecc24279d331e766532bdd44d50fff4caecba Mon Sep 17 00:00:00 2001 From: unspeaker Date: Sat, 29 Jun 2024 14:12:21 +0300 Subject: [PATCH] wip --- src/device/{chain.rs => chain/mod.rs} | 0 src/device/launcher/grid.rs | 22 ++++- src/device/launcher/handle.rs | 2 +- src/device/{launcher.rs => launcher/mod.rs} | 31 ++++--- src/device/sequencer/horizontal.rs | 85 ++++++++++--------- src/device/{sequencer.rs => sequencer/mod.rs} | 20 +++-- src/main.rs | 18 ++-- 7 files changed, 107 insertions(+), 71 deletions(-) rename src/device/{chain.rs => chain/mod.rs} (100%) rename src/device/{launcher.rs => launcher/mod.rs} (91%) rename src/device/{sequencer.rs => sequencer/mod.rs} (98%) diff --git a/src/device/chain.rs b/src/device/chain/mod.rs similarity index 100% rename from src/device/chain.rs rename to src/device/chain/mod.rs diff --git a/src/device/launcher/grid.rs b/src/device/launcher/grid.rs index cb6b15bc..65ba4a02 100644 --- a/src/device/launcher/grid.rs +++ b/src/device/launcher/grid.rs @@ -99,15 +99,16 @@ impl<'a> LauncherGridView<'a> { if let Some(scene) = self.state.scenes.get(index) { let hi = (track + 1 == self.state.cursor.0) && (index + 1 == self.state.cursor.1); - let style = Some(self.highlight(hi)); - if let Some(Some(clip)) = scene.clips.get(track) { + let style = Some(self.highlight(hi)); + let clip = scene.clips.get(track); + if let Some(Some(clip)) = clip { if let Some(phrase) = self.state.tracks[track].sequencer.state().sequences.get(*clip) { format!("⯈ {}", phrase.name).blit(self.buf, x, y + y2, style); } else { "????".blit(self.buf, x, y + y2, Some(Style::default().dim())) } } else { - "....".blit(self.buf, x, y + y2, Some(Style::default().dim())) + " ·········".blit(self.buf, x, y + y2, Some(Style::default().dim())) } if hi { draw_box_styled(self.buf, Rect { @@ -116,12 +117,25 @@ impl<'a> LauncherGridView<'a> { width: 16, height: 3 }, style); + if self.focused { + let style = Some(self.highlight(hi).bold().yellow()); + if let Some(Some(_)) = clip { } else { + "+ Add clip".blit(self.buf, x + 1, y + y2, Some(Style::default().dim())); + "+".blit(self.buf, x + 1, y + y2, style); + } + "↑".blit(self.buf, x + 6, y + y2 - 1, style); + "↓".blit(self.buf, x + 6, y + y2 + 1, style); + ",".blit(self.buf, x - 1, y + y2, style); + "←".blit(self.buf, x - 2, y + y2, style); + ".".blit(self.buf, x + 12, y + y2, style); + "→".blit(self.buf, x + 13, y + y2, style); + } } } } y2 = y2 + 1; } - "+Add clip…".blit(self.buf, x, y + y2, Some(Style::default().dim())); + " + Add clip".blit(self.buf, x, y + y2, Some(Style::default().dim())); } fn highlight (&self, highlight: bool) -> Style { diff --git a/src/device/launcher/handle.rs b/src/device/launcher/handle.rs index 65b32f58..82e4a04b 100644 --- a/src/device/launcher/handle.rs +++ b/src/device/launcher/handle.rs @@ -78,7 +78,7 @@ fn add_track (state: &mut Launcher) -> Usually { Ok(true) } fn delete_track (state: &mut Launcher) -> Usually { - if state.cursor.0 >= 1 { + if state.tracks.len() > 0 && state.cursor.0 >= 1 { state.tracks.remove(state.cursor.0 - 1); state.cursor.0 = state.cursor.0.min(state.tracks.len()); } diff --git a/src/device/launcher.rs b/src/device/launcher/mod.rs similarity index 91% rename from src/device/launcher.rs rename to src/device/launcher/mod.rs index 0490315c..25ea143b 100644 --- a/src/device/launcher.rs +++ b/src/device/launcher/mod.rs @@ -59,7 +59,7 @@ impl Launcher { recording: false, overdub: true, transport, - cursor: (1, 1), + cursor: (0, 0), position: 0, scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]), tracks: if let Some(tracks) = tracks { tracks } else { vec![ @@ -229,14 +229,26 @@ fn draw_section_sequencer (state: &Launcher, buf: &mut Buffer, area: Rect) -> Us }; if let Some(track) = state.tracks.get(state.col().saturating_sub(1)) { let state = track.sequencer.state(); - crate::device::sequencer::horizontal::keys(&state, buf, Rect { x, y: y + 1, width, height })?; - crate::device::sequencer::horizontal::lanes(&state, buf, x, y + 1, width); - crate::device::sequencer::horizontal::cursor( - &state, buf, x, y + 1, match view { - LauncherView::Sequencer => Style::default().green().not_dim(), - _ => Style::default().green().dim(), - } - ); + let keys_area = Rect { x, y: y + 1, width, height }; + crate::device::sequencer::horizontal::keys(buf, keys_area, state.note_axis.1)?; + if let Some(phrase) = state.phrase() { + crate::device::sequencer::horizontal::lanes(buf, x, y + 1, + &phrase, + state.timebase.ppq() as u32, + state.resolution as u32, + state.time_axis.0 as u32, + state.time_axis.1 as u32, + state.note_axis.0 as u32, + state.note_axis.1 as u32, + ); + } + let cursor_style = match view { + LauncherView::Sequencer => Style::default().green().not_dim(), + _ => Style::default().green().dim(), + }; + crate::device::sequencer::horizontal::cursor(buf, x, y + 1, cursor_style, + state.time_cursor, + state.note_cursor); } Ok(area) } @@ -246,7 +258,6 @@ fn draw_highlight (buf: &mut Buffer, highlight: &Option, style: Style) { } } fn draw_section_chains (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually { - let Rect { x, y, width, height } = area; let style = Some(Style::default().green().dim()); let chain = state.active_chain(); let plugins = if let Some(chain) = &chain { diff --git a/src/device/sequencer/horizontal.rs b/src/device/sequencer/horizontal.rs index 6faea181..470c3d88 100644 --- a/src/device/sequencer/horizontal.rs +++ b/src/device/sequencer/horizontal.rs @@ -9,11 +9,23 @@ pub fn draw ( ) -> Usually { area.x = area.x + 13; let Rect { x, y, width, .. } = area; - keys(s, buf, area)?; + keys(buf, area, s.note_axis.1)?; timer(s, buf, x, y, beat); let height = 32.max(s.note_axis.1 - s.note_axis.0) / 2; - lanes(s, buf, x, y, width); - cursor(s, buf, x, y, Style::default().green().not_dim()); + if let Some(phrase) = s.phrase() { + lanes(buf, x, y, + phrase, + s.timebase.ppq() as u32, + s.resolution as u32, + s.time_axis.0 as u32, + s.time_axis.1 as u32, + s.note_axis.0 as u32, + s.note_axis.1 as u32, + ); + } + cursor(buf, x, y, Style::default().green().not_dim(), + s.time_cursor, + s.note_cursor); footer(s, buf, x, y, width, height); Ok(Rect { x: x - 13, @@ -24,7 +36,6 @@ pub fn draw ( } pub fn timer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, beat: usize) { - let bw = Style::default().dim(); let (time0, time1) = s.time_axis; for step in time0..time1 { buf.set_string(x + 6 + step, y - 1, &"-", if beat % s.steps == step as usize { @@ -35,11 +46,9 @@ pub fn timer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, beat: usize) { } } -pub fn keys (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually { +pub fn keys (buf: &mut Buffer, area: Rect, note1: u16) -> Usually { let bw = Style::default().dim(); let Rect { x, y, width, height } = area; - let (note0, note1) = s.note_axis; - let (time0, time1) = s.time_axis; let h = 32.max(height.saturating_sub(2)*2)/2; for i in 0..h { let y = y + i; @@ -55,68 +64,64 @@ pub fn keys (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually { Ok(area) } -pub fn lanes (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16) { +pub fn lanes ( + buf: &mut Buffer, + x: u16, + y: u16, + phrase: &Phrase, + ppq: u32, + time_zoom: u32, + time0: u32, + time1: u32, + note0: u32, + note1: u32, +) { let bg = Style::default(); let bw = bg.dim(); let wh = bg.white(); - let ppq = s.timebase.ppq() as u32; - let (time0, time1) = s.time_axis; - let (note0, note1) = s.note_axis; - let notes = &s.sequences[s.sequence].notes; + //let (time0, time1) = s.time_axis; + //let (note0, note1) = s.note_axis; + //let resolution = s.resolution; for step in time0..time1 { - let (a, b) = ( - (step + 0) as u32 * ppq / s.resolution as u32, - (step + 1) as u32 * ppq / s.resolution as u32, - ); - if step % s.resolution as u16 == 0 { - buf.set_string(x + 5 + step, y - 1, &format!("{}", step + 1), Style::default()); + let x = x as u32 + 5 + step; + let (a, b) = ((step + 0) * ppq / time_zoom, (step + 1) * ppq / time_zoom,); + if step % time_zoom == 0 { + format!("{}", step + 1).blit(buf, x as u16, y - 1, None); } - let h = (note1 - note0)/2; + let h = (note1-note0)/2; for k in 0..h { let (character, style) = match ( - contains_note_on(&s.sequences[s.sequence], - ::midly::num::u7::from_int_lossy((note0 + k * 2 + 0) as u8), - a, b), - contains_note_on(&s.sequences[s.sequence], - ::midly::num::u7::from_int_lossy((note0 + k * 2 + 1) as u8), - a, b), + contains_note_on(phrase, u7::from_int_lossy((note0 + k * 2 + 0) as u8), a, b), + contains_note_on(phrase, u7::from_int_lossy((note0 + k * 2 + 1) as u8), a, b), ) { (true, true) => ("█", wh), (true, false) => ("▀", wh), (false, true) => ("▄", wh), (false, false) => ("·", bw), }; - //let (character, style) = ("▄", bg); - buf.set_string(x + 5 + step, y + h - k, character, style); + let y = y as u32 + h + k; + character.blit(buf, x as u16, y as u16, Some(style)); } - //for (_, (_, events)) in notes.range(time_start..time_end).enumerate() { - //if events.len() > 0 { - //buf.set_string(x + 6 + step as u16, y, "█", wh); - //} - //} } } -pub fn cursor (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, style: Style) { +pub fn cursor (buf: &mut Buffer, x: u16, y: u16, style: Style, time_cursor: u16, note_cursor: u16) { buf.set_string( - x + 5 + s.time_cursor, - y + s.note_cursor / 2, - if s.note_cursor % 2 == 0 { "▀" } else { "▄" }, + x + 5 + time_cursor, + y + note_cursor / 2, + if note_cursor % 2 == 0 { "▀" } else { "▄" }, style ); } pub fn footer (s: &Sequencer, buf: &mut Buffer, mut x: u16, y: u16, width: u16, height: u16) { - let bw = Style::default().dim(); - let bg = Style::default(); - let (note0, note1) = s.note_axis; buf.set_string(x, y + height, format!("├{}┤", "-".repeat((width - 2).into())), Style::default().dim()); buf.set_string(x, y + height + 2, format!("├{}┤", "-".repeat((width - 2).into())), Style::default().dim()); x = x + 2; { - for (i, [letter, title, value]) in [ + for (_, [letter, title, value]) in [ ["S", &format!("ync"), &format!("<4/4>")], ["Q", &format!("uant"), &format!("<1/{}>", 4 * s.resolution)], ["N", &format!("ote"), &format!("{} ({}-{})", diff --git a/src/device/sequencer.rs b/src/device/sequencer/mod.rs similarity index 98% rename from src/device/sequencer.rs rename to src/device/sequencer/mod.rs index 97ab0dcf..c882e9dc 100644 --- a/src/device/sequencer.rs +++ b/src/device/sequencer/mod.rs @@ -33,10 +33,7 @@ pub struct Sequencer { pub midi_out: Port, /// Holds info about tempo - timebase: Arc, - /// Sequencer resolution, e.g. 16 steps per beat. - /// FIXME: grid in ppm will simplify calculations - resolution: usize, + pub timebase: Arc, /// Steps in sequence, e.g. 64 16ths = 4 beat loop. /// FIXME: play start / end / loop in ppm steps: usize, @@ -59,13 +56,16 @@ pub struct Sequencer { /// Display mode mode: SequencerView, /// Range of notes to display - note_axis: (u16, u16), + pub note_axis: (u16, u16), /// Position of cursor within note range - note_cursor: u16, + pub note_cursor: u16, + /// Sequencer resolution, e.g. 16 steps per beat. + /// FIXME: grid in ppm will simplify calculations + pub resolution: usize, /// Range of time steps to display - time_axis: (u16, u16), + pub time_axis: (u16, u16), /// Position of cursor within time range - time_cursor: u16, + pub time_cursor: u16, } #[derive(Debug, Clone)] @@ -111,6 +111,10 @@ impl Sequencer { }).activate(client) } + pub fn phrase <'a> (&'a self) -> Option<&'a Phrase> { + self.sequences.get(self.sequence) + } + pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { // Prepare output buffer diff --git a/src/main.rs b/src/main.rs index 430077a5..f7398f99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,11 +29,11 @@ fn main () -> Result<(), Box> { ppq: AtomicUsize::new(96), }); let ppq = timebase.ppq() as u32; - run(Launcher::new("Launcher#0", &timebase, + let app = Launcher::new("Launcher#0", &timebase, Some(vec![ + Track::new("Kick", &timebase, Some(vec![ - //Sampler::new("Sampler")?.boxed(), - Plugin::lv2("Kick/ChowKick", "file:///home/user/.lv2/ChowKick.lv2", &[1, 1, 0, 2])?.boxed(), + //Plugin::lv2("Kick/ChowKick", "file:///home/user/.lv2/ChowKick.lv2", &[1, 1, 0, 2])?.boxed(), ]), Some(vec![ Phrase::new("HelloKick", ppq * 4, Some(BTreeMap::from([ ( ppq * 0, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ), @@ -58,17 +58,19 @@ fn main () -> Result<(), Box> { ]))) ]))?, + //Sampler::new("Sampler")?.boxed(), //Plugin::lv2("Kick/ChowKick", "file:///home/user/.lv2/ChowKick.lv2", &[1, 1, 0, 2])?.boxed(), //Plugin::lv2("Bass/Helm", "file:///home/user/.lv2/Helm.lv2", &[1, 0, 0, 2])?.boxed(), //Plugin::lv2("Pads/Odin2", "file:///home/user/.lv2/Odin2.lv2", &[1, 0, 0, 2])?.boxed(), ]), Some(vec![ Scene::new(&"Scene#01", &[Some(0), None, None, None]), - Scene::new(&"Scene#02", &[Some(0), Some(0), None, None]), - Scene::new(&"Scene#03", &[None, Some(0), None, None]), - Scene::new(&"Scene#04", &[None, None, None, None]), - Scene::new(&"Scene#05", &[None, None, None, None]), + //Scene::new(&"Scene#02", &[Some(0), Some(0), None, None]), + //Scene::new(&"Scene#03", &[None, Some(0), None, None]), + //Scene::new(&"Scene#04", &[None, None, None, None]), + //Scene::new(&"Scene#05", &[None, None, None, None]), ]) - )?.connect(input, &output)?) + )?.connect(input, &output)?; + run(app) }