This commit is contained in:
🪞👃🪞 2024-06-29 14:12:21 +03:00
parent f48f17e9a4
commit 4ebecc2427
7 changed files with 107 additions and 71 deletions

View file

@ -99,15 +99,16 @@ impl<'a> LauncherGridView<'a> {
if let Some(scene) = self.state.scenes.get(index) { if let Some(scene) = self.state.scenes.get(index) {
let hi = (track + 1 == self.state.cursor.0) && let hi = (track + 1 == self.state.cursor.0) &&
(index + 1 == self.state.cursor.1); (index + 1 == self.state.cursor.1);
let style = Some(self.highlight(hi)); let style = Some(self.highlight(hi));
if let Some(Some(clip)) = scene.clips.get(track) { 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) { if let Some(phrase) = self.state.tracks[track].sequencer.state().sequences.get(*clip) {
format!("{}", phrase.name).blit(self.buf, x, y + y2, style); format!("{}", phrase.name).blit(self.buf, x, y + y2, style);
} else { } else {
"????".blit(self.buf, x, y + y2, Some(Style::default().dim())) "????".blit(self.buf, x, y + y2, Some(Style::default().dim()))
} }
} else { } else {
"....".blit(self.buf, x, y + y2, Some(Style::default().dim())) " ·········".blit(self.buf, x, y + y2, Some(Style::default().dim()))
} }
if hi { if hi {
draw_box_styled(self.buf, Rect { draw_box_styled(self.buf, Rect {
@ -116,12 +117,25 @@ impl<'a> LauncherGridView<'a> {
width: 16, width: 16,
height: 3 height: 3
}, style); }, 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; 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 { fn highlight (&self, highlight: bool) -> Style {

View file

@ -78,7 +78,7 @@ fn add_track (state: &mut Launcher) -> Usually<bool> {
Ok(true) Ok(true)
} }
fn delete_track (state: &mut Launcher) -> Usually<bool> { fn delete_track (state: &mut Launcher) -> Usually<bool> {
if state.cursor.0 >= 1 { if state.tracks.len() > 0 && state.cursor.0 >= 1 {
state.tracks.remove(state.cursor.0 - 1); state.tracks.remove(state.cursor.0 - 1);
state.cursor.0 = state.cursor.0.min(state.tracks.len()); state.cursor.0 = state.cursor.0.min(state.tracks.len());
} }

View file

@ -59,7 +59,7 @@ impl Launcher {
recording: false, recording: false,
overdub: true, overdub: true,
transport, transport,
cursor: (1, 1), cursor: (0, 0),
position: 0, position: 0,
scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]), scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]),
tracks: if let Some(tracks) = tracks { tracks } else { vec![ 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)) { if let Some(track) = state.tracks.get(state.col().saturating_sub(1)) {
let state = track.sequencer.state(); let state = track.sequencer.state();
crate::device::sequencer::horizontal::keys(&state, buf, Rect { x, y: y + 1, width, height })?; let keys_area = Rect { x, y: y + 1, width, height };
crate::device::sequencer::horizontal::lanes(&state, buf, x, y + 1, width); crate::device::sequencer::horizontal::keys(buf, keys_area, state.note_axis.1)?;
crate::device::sequencer::horizontal::cursor( if let Some(phrase) = state.phrase() {
&state, buf, x, y + 1, match view { crate::device::sequencer::horizontal::lanes(buf, x, y + 1,
LauncherView::Sequencer => Style::default().green().not_dim(), &phrase,
_ => Style::default().green().dim(), 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) Ok(area)
} }
@ -246,7 +258,6 @@ fn draw_highlight (buf: &mut Buffer, highlight: &Option<Rect>, style: Style) {
} }
} }
fn draw_section_chains (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> { fn draw_section_chains (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, width, height } = area;
let style = Some(Style::default().green().dim()); let style = Some(Style::default().green().dim());
let chain = state.active_chain(); let chain = state.active_chain();
let plugins = if let Some(chain) = &chain { let plugins = if let Some(chain) = &chain {

View file

@ -9,11 +9,23 @@ pub fn draw (
) -> Usually<Rect> { ) -> Usually<Rect> {
area.x = area.x + 13; area.x = area.x + 13;
let Rect { x, y, width, .. } = area; let Rect { x, y, width, .. } = area;
keys(s, buf, area)?; keys(buf, area, s.note_axis.1)?;
timer(s, buf, x, y, beat); timer(s, buf, x, y, beat);
let height = 32.max(s.note_axis.1 - s.note_axis.0) / 2; let height = 32.max(s.note_axis.1 - s.note_axis.0) / 2;
lanes(s, buf, x, y, width); if let Some(phrase) = s.phrase() {
cursor(s, buf, x, y, Style::default().green().not_dim()); 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); footer(s, buf, x, y, width, height);
Ok(Rect { Ok(Rect {
x: x - 13, x: x - 13,
@ -24,7 +36,6 @@ pub fn draw (
} }
pub fn timer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, beat: usize) { 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; let (time0, time1) = s.time_axis;
for step in time0..time1 { for step in time0..time1 {
buf.set_string(x + 6 + step, y - 1, &"-", if beat % s.steps == step as usize { 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<Rect> { pub fn keys (buf: &mut Buffer, area: Rect, note1: u16) -> Usually<Rect> {
let bw = Style::default().dim(); let bw = Style::default().dim();
let Rect { x, y, width, height } = area; 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; let h = 32.max(height.saturating_sub(2)*2)/2;
for i in 0..h { for i in 0..h {
let y = y + i; let y = y + i;
@ -55,68 +64,64 @@ pub fn keys (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
Ok(area) 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 bg = Style::default();
let bw = bg.dim(); let bw = bg.dim();
let wh = bg.white(); let wh = bg.white();
let ppq = s.timebase.ppq() as u32; //let (time0, time1) = s.time_axis;
let (time0, time1) = s.time_axis; //let (note0, note1) = s.note_axis;
let (note0, note1) = s.note_axis; //let resolution = s.resolution;
let notes = &s.sequences[s.sequence].notes;
for step in time0..time1 { for step in time0..time1 {
let (a, b) = ( let x = x as u32 + 5 + step;
(step + 0) as u32 * ppq / s.resolution as u32, let (a, b) = ((step + 0) * ppq / time_zoom, (step + 1) * ppq / time_zoom,);
(step + 1) as u32 * ppq / s.resolution as u32, if step % time_zoom == 0 {
); format!("{}", step + 1).blit(buf, x as u16, y - 1, None);
if step % s.resolution as u16 == 0 {
buf.set_string(x + 5 + step, y - 1, &format!("{}", step + 1), Style::default());
} }
let h = (note1 - note0)/2; let h = (note1-note0)/2;
for k in 0..h { for k in 0..h {
let (character, style) = match ( let (character, style) = match (
contains_note_on(&s.sequences[s.sequence], contains_note_on(phrase, u7::from_int_lossy((note0 + k * 2 + 0) as u8), a, b),
::midly::num::u7::from_int_lossy((note0 + k * 2 + 0) as u8), contains_note_on(phrase, u7::from_int_lossy((note0 + k * 2 + 1) as u8), a, b),
a, b),
contains_note_on(&s.sequences[s.sequence],
::midly::num::u7::from_int_lossy((note0 + k * 2 + 1) as u8),
a, b),
) { ) {
(true, true) => ("", wh), (true, true) => ("", wh),
(true, false) => ("", wh), (true, false) => ("", wh),
(false, true) => ("", wh), (false, true) => ("", wh),
(false, false) => ("·", bw), (false, false) => ("·", bw),
}; };
//let (character, style) = ("▄", bg); let y = y as u32 + h + k;
buf.set_string(x + 5 + step, y + h - k, character, style); 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( buf.set_string(
x + 5 + s.time_cursor, x + 5 + time_cursor,
y + s.note_cursor / 2, y + note_cursor / 2,
if s.note_cursor % 2 == 0 { "" } else { "" }, if note_cursor % 2 == 0 { "" } else { "" },
style style
); );
} }
pub fn footer (s: &Sequencer, buf: &mut Buffer, mut x: u16, y: u16, width: u16, height: u16) { 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())), buf.set_string(x, y + height, format!("{}", "-".repeat((width - 2).into())),
Style::default().dim()); Style::default().dim());
buf.set_string(x, y + height + 2, format!("{}", "-".repeat((width - 2).into())), buf.set_string(x, y + height + 2, format!("{}", "-".repeat((width - 2).into())),
Style::default().dim()); Style::default().dim());
x = x + 2; x = x + 2;
{ {
for (i, [letter, title, value]) in [ for (_, [letter, title, value]) in [
["S", &format!("ync"), &format!("<4/4>")], ["S", &format!("ync"), &format!("<4/4>")],
["Q", &format!("uant"), &format!("<1/{}>", 4 * s.resolution)], ["Q", &format!("uant"), &format!("<1/{}>", 4 * s.resolution)],
["N", &format!("ote"), &format!("{} ({}-{})", ["N", &format!("ote"), &format!("{} ({}-{})",

View file

@ -33,10 +33,7 @@ pub struct Sequencer {
pub midi_out: Port<MidiOut>, pub midi_out: Port<MidiOut>,
/// Holds info about tempo /// Holds info about tempo
timebase: Arc<Timebase>, pub timebase: Arc<Timebase>,
/// Sequencer resolution, e.g. 16 steps per beat.
/// FIXME: grid in ppm will simplify calculations
resolution: usize,
/// Steps in sequence, e.g. 64 16ths = 4 beat loop. /// Steps in sequence, e.g. 64 16ths = 4 beat loop.
/// FIXME: play start / end / loop in ppm /// FIXME: play start / end / loop in ppm
steps: usize, steps: usize,
@ -59,13 +56,16 @@ pub struct Sequencer {
/// Display mode /// Display mode
mode: SequencerView, mode: SequencerView,
/// Range of notes to display /// Range of notes to display
note_axis: (u16, u16), pub note_axis: (u16, u16),
/// Position of cursor within note range /// 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 /// Range of time steps to display
time_axis: (u16, u16), pub time_axis: (u16, u16),
/// Position of cursor within time range /// Position of cursor within time range
time_cursor: u16, pub time_cursor: u16,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -111,6 +111,10 @@ impl Sequencer {
}).activate(client) }).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 { pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
// Prepare output buffer // Prepare output buffer

View file

@ -29,11 +29,11 @@ fn main () -> Result<(), Box<dyn Error>> {
ppq: AtomicUsize::new(96), ppq: AtomicUsize::new(96),
}); });
let ppq = timebase.ppq() as u32; let ppq = timebase.ppq() as u32;
run(Launcher::new("Launcher#0", &timebase, let app = Launcher::new("Launcher#0", &timebase,
Some(vec![ Some(vec![
Track::new("Kick", &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![ ]), Some(vec![
Phrase::new("HelloKick", ppq * 4, Some(BTreeMap::from([ Phrase::new("HelloKick", ppq * 4, Some(BTreeMap::from([
( ppq * 0, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ), ( ppq * 0, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ),
@ -58,17 +58,19 @@ fn main () -> Result<(), Box<dyn Error>> {
]))) ])))
]))?, ]))?,
//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(),
//Plugin::lv2("Bass/Helm", "file:///home/user/.lv2/Helm.lv2", &[1, 0, 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(), //Plugin::lv2("Pads/Odin2", "file:///home/user/.lv2/Odin2.lv2", &[1, 0, 0, 2])?.boxed(),
]), ]),
Some(vec![ Some(vec![
Scene::new(&"Scene#01", &[Some(0), None, None, None]), Scene::new(&"Scene#01", &[Some(0), None, None, None]),
Scene::new(&"Scene#02", &[Some(0), Some(0), None, None]), //Scene::new(&"Scene#02", &[Some(0), Some(0), None, None]),
Scene::new(&"Scene#03", &[None, Some(0), None, None]), //Scene::new(&"Scene#03", &[None, Some(0), None, None]),
Scene::new(&"Scene#04", &[None, None, None, None]), //Scene::new(&"Scene#04", &[None, None, None, None]),
Scene::new(&"Scene#05", &[None, None, None, None]), //Scene::new(&"Scene#05", &[None, None, None, None]),
]) ])
)?.connect(input, &output)?) )?.connect(input, &output)?;
run(app)
} }