wip: launcher grid

This commit is contained in:
🪞👃🪞 2024-06-24 21:35:45 +03:00
parent 55e6c19c92
commit 1f194dafd8
5 changed files with 371 additions and 129 deletions

View file

@ -1,38 +1,256 @@
use crate::prelude::*; use crate::prelude::*;
pub struct Launcher { pub struct Launcher {
name: String name: String,
timebase: Arc<Timebase>,
cursor: (usize, usize),
tracks: Vec<DynamicDevice<Sequencer>>,
chains: Vec<DynamicDevice<Chain>>,
rows: usize,
show_help: bool,
} }
impl Launcher { impl Launcher {
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> { pub fn new (name: &str, timebase: &Arc<Timebase>) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
Ok(DynamicDevice::new(render, handle, process, Self { Ok(DynamicDevice::new(render, handle, process, Self {
name: name.into(), name: name.into(),
timebase: timebase.clone(),
cursor: (0, 0),
rows: 8,
tracks: vec![
Sequencer::new("Drum", timebase)?,
Sequencer::new("Bass", timebase)?,
Sequencer::new("Pads", timebase)?,
Sequencer::new("Lead", timebase)?,
],
chains: vec![
// TODO
],
show_help: true
})) }))
} }
fn cols (&self) -> usize {
(self.tracks.len() + 1) as usize
}
fn col (&self) -> usize {
self.cursor.0 as usize
}
fn dec_col (&mut self) {
self.cursor.0 = if self.cursor.0 > 0 {
self.cursor.0 - 1
} else {
(self.cols() - 1) as usize
}
}
fn inc_col (&mut self) {
self.cursor.0 = if self.cursor.0 >= self.cols() - 1 {
0
} else {
self.cursor.0 + 1
}
}
fn rows (&self) -> usize {
(self.rows + 2) as usize
}
fn row (&self) -> usize {
self.cursor.1 as usize
}
fn dec_row (&mut self) {
self.cursor.1 = if self.cursor.1 > 0 {
self.cursor.1 - 1
} else {
self.rows() - 1
}
}
fn inc_row (&mut self) {
self.cursor.1 = if self.cursor.1 >= self.rows() - 1 {
0
} else {
self.cursor.1 + 1
}
}
} }
impl DevicePorts for Launcher {}
pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control { pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
Control::Continue Control::Continue
} }
macro_rules! set {
pub fn render (_: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> { ($buf:expr, $style:expr, $x: expr, $y: expr, $fmt: literal $(, $val:expr)*) => {
for i in 1..=8 { $buf.set_string($x, $y, &format!($fmt $(, $val)*), $style)
buf.set_string(area.x + 2 + (i-1) * 10, area.y, format!("Track#{i} | "), Style::default())
} }
for i in 0..=7 { }
for j in 0..=7 { trait Blit {
buf.set_string(area.x + 2 + i * 10, area.y + 2 + j, format!("······· | "), Style::default().dim()) fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>);
}
impl<T: AsRef<str>> Blit for T {
fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>) {
buf.set_string(x, y, self.as_ref(), style.unwrap_or(Style::default()))
}
}
pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, width, height } = area;
let separator = format!("{}", "-".repeat((width - 2).into()));
let scenes = draw_launcher_scenes(state, buf, x, y);
separator.blit(buf, x, y + 2, Some(Style::default().dim()));
separator.blit(buf, x, y + 4, Some(Style::default().dim()));
separator.blit(buf, x, y + 40, Some(Style::default().dim()));
let (w, mut highlight) = draw_launcher_tracks(state, buf, x, y);
if state.col() == 0 {
highlight = Some(scenes);
}
draw_crossings(state, buf, x + w - 2, y);
draw_sequencer(state, buf, x, y + 21, width, height - 22)?;
draw_box(buf, area);
draw_launcher_highlight(state, buf, &highlight);
Ok(area)
}
fn draw_launcher_scenes (
state: &Launcher, buf: &mut Buffer, x: u16, y: u16,
) -> Rect {
let style = Style::default().not_dim().bold();
let row = state.row() as u16;
let col = state.col() as u16;
let mut width = 8u16;
let mut height = 6u16;
format!("{} | ", state.name).blit(buf, x+2, y+1, Some(
if row == 0 && col == 0 { Style::default().green() } else { Style::default() }
));
format!("Sync 1/1").blit(buf, x+2, y+3, Some(
if row == 1 && col == 0 { Style::default().green() } else { Style::default() }
));
for j in 0..=7 {
let y = y + 5 + j as u16 * 2;
let label = format!("▶ Scene {j}");
width = width.max(label.len() as u16 + 3);
label.blit(buf, x + 2, y, Some(
if row == j + 2 && col == 0 { Style::default().green() } else { Style::default() }
));
height = height + 2;
}
Rect { x, y, width, height }
}
fn draw_launcher_tracks (
state: &Launcher, buf: &mut Buffer, x: u16, y: u16
) -> (u16, Option<Rect>) {
let mut w = 15;
let mut highlight = None;
for (i, track) in state.tracks.iter().enumerate() {
let track = track.state();
draw_crossings(state, buf, x + w - 2, y);
let width = draw_launcher_track(state, buf, x + w, y, i as u16, &track);
if i + 1 == state.col() {
highlight = Some(Rect { x: x + w - 2, y, width: width + 1, height: 22 });
}
w = w + width;
}
(w, highlight)
}
fn draw_launcher_track (
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, i: u16, track: &Sequencer
) -> u16 {
let mut width = track.name.len() as u16 + 3;
let row = state.row() as u16;
let col = state.col() as u16;
track.name.blit(buf, x, y + 1, Some(
if row == 0 && col == i + 1 { Style::default().green() } else { Style::default() }
));
"(global)".blit(buf, x, y + 3, Some(
if row == 1 && col == i + 1 { Style::default().green() } else { Style::default().dim() }
));
"".blit(buf, x - 2, y + 3, Some(
Style::default().dim()
));
for j in 0..=7 {
let y = y + 5 + j as u16 * 2;
if let Some(sequence) = track.sequences.get(j) {
let label = format!("{}", &sequence.name);
width = width.max(label.len() as u16 + 1);
let style = if j + 2 == row as usize && i + 1 == col {
Style::default().green().bold()
} else {
Style::default()
};
buf.set_string(x - 0, y + 0, &label, style);
buf.set_string(x - 2, y + 0, &"", style.dim());
buf.set_string(x - 2, y + 1, &"", style.dim());
buf.set_string(x + width - 2, y + 0, &"", style.dim());
buf.set_string(x + width - 2, y + 1, &"", style.dim());
} else {
buf.set_string(x - 2, y as u16,
format!("{}", &"·".repeat(track.name.len())),
Style::default().dim()
);
} }
} }
Ok(draw_box(buf, Rect {
x: area.x, width
y: area.y - 1,
width: area.width,
height: 12
}))
} }
pub fn handle (_: &mut Launcher, _: &AppEvent) -> Usually<bool> { fn draw_crossings (state: &Launcher, buf: &mut Buffer, x: u16, y: u16) {
Ok(false) "".blit(buf, x, y + 0, None);
"".blit(buf, x, y + 1, Some(Style::default().dim()));
"".blit(buf, x, y + 2, Some(Style::default().dim()));
"".blit(buf, x, y + 3, Some(Style::default().dim()));
"".blit(buf, x, y + 4, Some(Style::default().dim()));
}
fn draw_sequencer (
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16
) -> Usually<()> {
if let Some(sequencer) = state.tracks.get(state.col().saturating_sub(1)) {
crate::device::sequencer::horizontal::footer(
&sequencer.state(), buf, 0, y, width, 0
);
crate::device::sequencer::horizontal::keys(
&sequencer.state(), buf, Rect { x, y: y + 3, width, height }
)?;
}
Ok(())
}
fn draw_launcher_highlight (
state: &Launcher, buf: &mut Buffer, highlight: &Option<Rect>) {
if let Some(area) = highlight {
draw_box_styled(buf, *area, Some(Style::default().green().dim()));
}
}
pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually<bool> {
handle_keymap(state, event, KEYMAP)
}
pub const KEYMAP: &'static [KeyBinding<Launcher>] = keymap!(Launcher {
[Up, NONE, "cursor_up", "move cursor up", cursor_up],
[Down, NONE, "cursor_down", "move cursor down", cursor_down],
[Left, NONE, "cursor_left", "move cursor left", cursor_left],
[Right, NONE, "cursor_right", "move cursor right", cursor_right],
[Char('r'), NONE, "rename", "rename current element", rename],
[F(1), NONE, "toggle_help", "toggle help", toggle_help],
});
fn rename (_: &mut Launcher) -> Usually<bool> {
Ok(true)
}
fn cursor_up (state: &mut Launcher) -> Usually<bool> {
state.dec_row();
Ok(true)
}
fn cursor_down (state: &mut Launcher) -> Usually<bool> {
state.inc_row();
Ok(true)
}
fn cursor_left (state: &mut Launcher) -> Usually<bool> {
state.dec_col();
Ok(true)
}
fn cursor_right (state: &mut Launcher) -> Usually<bool> {
state.inc_col();
Ok(true)
}
fn toggle_help (state: &mut Launcher) -> Usually<bool> {
state.show_help = !state.show_help;
Ok(true)
} }

View file

@ -1,19 +1,26 @@
use crate::prelude::*; use crate::prelude::*;
use ratatui::style::Stylize; use ratatui::style::Stylize;
type Sequence = std::collections::BTreeMap<u32, Vec<::midly::MidiMessage>>;
mod keys; mod keys;
use keys::*; use keys::*;
mod horizontal; pub mod horizontal;
use horizontal::*;
mod vertical; pub mod vertical;
use vertical::*;
pub struct Sequence {
pub name: String,
pub notes: std::collections::BTreeMap<u32, Vec<::midly::MidiMessage>>
}
impl Sequence {
pub fn new (name: &str) -> Self {
Self { name: name.to_string(), notes: std::collections::BTreeMap::new() }
}
}
pub struct Sequencer { pub struct Sequencer {
name: String, pub name: String,
/// JACK transport handle. /// JACK transport handle.
transport: ::jack::Transport, transport: ::jack::Transport,
/// JACK MIDI input port that will be created. /// JACK MIDI input port that will be created.
@ -32,7 +39,7 @@ pub struct Sequencer {
/// Sequence selector /// Sequence selector
sequence: usize, sequence: usize,
/// Map: tick -> MIDI events at tick /// Map: tick -> MIDI events at tick
sequences: Vec<Sequence>, pub sequences: Vec<Sequence>,
/// Red keys on piano roll. /// Red keys on piano roll.
notes_on: Vec<bool>, notes_on: Vec<bool>,
@ -71,35 +78,44 @@ impl Sequencer {
let transport = client.transport(); let transport = client.transport();
let state = transport.query_state()?; let state = transport.query_state()?;
DynamicDevice::new(render, handle, Self::process, Self { DynamicDevice::new(render, handle, Self::process, Self {
name: name.into(), name: name.into(),
input_port: client.register_port("in", MidiIn::default())?, input_port: client.register_port("in", MidiIn::default())?,
output_port: client.register_port("out", MidiOut::default())?, output_port: client.register_port("out", MidiOut::default())?,
timebase: timebase.clone(), timebase: timebase.clone(),
steps: 64, steps: 64,
resolution: 8, resolution: 4,
sequence: 0, sequence: 0,
sequences: vec![std::collections::BTreeMap::new();8], sequences: vec![
notes_on: vec![false;128], Sequence::new(&"Phrase#01"),
Sequence::new(&"Phrase#02"),
Sequence::new(&"Phrase#03"),
Sequence::new(&"Phrase#04"),
Sequence::new(&"Phrase#05"),
Sequence::new(&"Phrase#06"),
Sequence::new(&"Phrase#07"),
Sequence::new(&"Phrase#08"),
],
notes_on: vec![false;128],
playing: TransportState::Starting, playing: TransportState::Starting,
monitoring: true, monitoring: true,
recording: true, recording: true,
overdub: true, overdub: true,
transport, transport,
mode: SequencerView::Horizontal, mode: SequencerView::Horizontal,
note_axis: (36, 68), note_axis: (36, 68),
note_cursor: 0, note_cursor: 0,
time_axis: (0, 64), time_axis: (0, 64),
time_cursor: 0, time_cursor: 0,
}).activate(client) }).activate(client)
} }
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
// Update time // Update time
let mut sequence = &mut self.sequences[self.sequence]; let mut sequence = &mut self.sequences[self.sequence].notes;
let transport = self.transport.query().unwrap(); let transport = self.transport.query().unwrap();
self.playing = transport.state; self.playing = transport.state;
let pos = &transport.pos; let pos = &transport.pos;
@ -112,7 +128,33 @@ impl Sequencer {
let frames = scope.n_frames() as usize; let frames = scope.n_frames() as usize;
let mut output: Vec<Option<Vec<Vec<u8>>>> = vec![None;frames]; let mut output: Vec<Option<Vec<Vec<u8>>>> = vec![None;frames];
// Read from input, write inputs to sequence and/or output // Read from sequence into output buffer
if self.playing == TransportState::Rolling {
let frame = transport.pos.frame() as usize;
let quant = self.timebase.fpb() as usize * self.steps / self.resolution;
let ticks = self.timebase.frames_to_ticks(frame, frame + frames, quant);
for (time, tick) in ticks.iter() {
if let Some(events) = sequence.get(&(*tick as u32)) {
for message in events.iter() {
let mut buf = vec![];
let channel = 0.into();
let message = *message;
::midly::live::LiveEvent::Midi { channel, message }
.write(&mut buf)
.unwrap();
let t = *time as usize;
if output[t].is_none() {
output[t] = Some(vec![]);
}
if let Some(Some(frame)) = output.get_mut(t) {
frame.push(buf);
}
}
}
}
}
// Read from input, write inputs to sequence and/or output buffer
for event in self.input_port.iter(scope) { for event in self.input_port.iter(scope) {
let tick = tick as u32; let tick = tick as u32;
let msg = midly::live::LiveEvent::parse(event.bytes).unwrap(); let msg = midly::live::LiveEvent::parse(event.bytes).unwrap();
@ -164,40 +206,14 @@ impl Sequencer {
} }
} }
// Read from sequence // Write to port from output buffer
if self.playing == TransportState::Rolling { // (containing notes from sequence and/or monitor)
let frame = transport.pos.frame() as usize;
let quant = self.timebase.fpb() as usize * self.steps / self.resolution;
let ticks = self.timebase.frames_to_ticks(frame, frame + frames, quant);
for (time, tick) in ticks.iter() {
if let Some(events) = sequence.get(&(*tick as u32)) {
for message in events.iter() {
let mut buf = vec![];
::midly::live::LiveEvent::Midi {
channel: 0.into(),
message: *message,
}.write(&mut buf).unwrap();
let t = *time as usize;
if output[t].is_none() {
output[t] = Some(vec![]);
}
if let Some(Some(frame)) = output.get_mut(t) {
frame.push(buf);
}
}
}
}
}
// Write from monitor and/or sequence
let mut writer = self.output_port.writer(scope); let mut writer = self.output_port.writer(scope);
for t in 0..scope.n_frames() { for time in 0..scope.n_frames() {
if let Some(Some(frame)) = output.get_mut(t as usize) { if let Some(Some(frame)) = output.get_mut(time as usize) {
for event in frame.iter() { for event in frame.iter() {
writer.write(&::jack::RawMidi { writer.write(&::jack::RawMidi { time, bytes: &event })
time: t, .expect(&format!("{event:?}"));
bytes: &event
}).expect(&format!("{event:?}"));
} }
} }
} }
@ -221,13 +237,13 @@ fn render (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
Rect { x, y, width, height: 0 }, Rect { x, y, width, height: 0 },
SequencerView::Compact => SequencerView::Compact =>
Rect { x, y, width, height: 0 }, Rect { x, y, width, height: 0 },
SequencerView::Vertical => draw_vertical(s, buf, Rect { SequencerView::Vertical => self::vertical::draw(s, buf, Rect {
x, x,
y: y + header.height, y: y + header.height,
width: 3 + note1 - note0, width: 3 + note1 - note0,
height: 3 + time1 - time0, height: 3 + time1 - time0,
}, steps)?, }, steps)?,
SequencerView::Horizontal => draw_horizontal(s, buf, Rect { SequencerView::Horizontal => self::horizontal::draw(s, buf, Rect {
x, x,
y: y + header.height, y: y + header.height,
width: area.width.max(3 + time1 - time0), width: area.width.max(3 + time1 - time0),
@ -256,9 +272,9 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu
TransportState::Starting => format!("READY ..."), TransportState::Starting => format!("READY ..."),
TransportState::Stopped => format!("⏹ STOPPED") TransportState::Stopped => format!("⏹ STOPPED")
}, match s.playing { }, match s.playing {
TransportState::Rolling => style.dim().bold(), TransportState::Stopped => style.dim().bold(),
TransportState::Starting => style.not_dim().bold(), TransportState::Starting => style.not_dim().bold(),
TransportState::Stopped => style.not_dim().white().bold() TransportState::Rolling => style.not_dim().white().bold()
}); });
buf.set_string(x, y + 2, format!("{}", "-".repeat((area.width - 2).into())), style.dim()); buf.set_string(x, y + 2, format!("{}", "-".repeat((area.width - 2).into())), style.dim());
//buf.set_string(x + 2, y + 2, //buf.set_string(x + 2, y + 2,
@ -267,24 +283,21 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu
//} else { //} else {
//Style::default().dim() //Style::default().dim()
//}); //});
buf.set_string(x + 13, y + 1, buf.set_string(x + 13, y + 1, &format!("⏺ REC"), if s.recording {
&format!("⏺ REC"), if s.recording { Style::default().bold().red()
Style::default().bold().red() } else {
} else { Style::default().bold().dim()
Style::default().bold().dim() });
}); buf.set_string(x + 20, y + 1, &format!("⏺ DUB"), if s.overdub {
buf.set_string(x + 20, y + 1, Style::default().bold().yellow()
&format!("⏺ DUB"), if s.overdub { } else {
Style::default().bold().yellow() Style::default().bold().dim()
} else { });
Style::default().bold().dim() buf.set_string(x + 27, y + 1, &format!("⏺ MON"), if s.monitoring {
}); Style::default().bold().green()
buf.set_string(x + 27, y + 1, } else {
&format!("⏺ MON"), if s.monitoring { Style::default().bold().dim()
Style::default().bold().green() });
} else {
Style::default().bold().dim()
});
let clips = draw_clips(s, buf, area)?; let clips = draw_clips(s, buf, area)?;
Ok(Rect { x, y, width: area.width, height: 3 }) Ok(Rect { x, y, width: area.width, height: 3 })
} }
@ -292,8 +305,9 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu
fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> { fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, .. } = area; let Rect { x, y, .. } = area;
let style = Style::default().gray(); let style = Style::default().gray();
for i in 0..8 { for (i, sequence) in s.sequences.iter().enumerate() {
buf.set_string(x + 2, y + 3 + i*2, &format!("{}", &s.name), if i as usize == s.sequence { let label = format!("{}", &sequence.name);
buf.set_string(x + 2, y + 3 + (i as u16)*2, &label, if i == s.sequence {
match s.playing { match s.playing {
TransportState::Rolling => style.white().bold(), TransportState::Rolling => style.white().bold(),
_ => style.not_dim().bold() _ => style.not_dim().bold()
@ -306,7 +320,7 @@ fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
} }
pub fn contains_note_on (sequence: &Sequence, k: ::midly::num::u7, start: u32, end: u32) -> bool { pub fn contains_note_on (sequence: &Sequence, k: ::midly::num::u7, start: u32, end: u32) -> bool {
for (_, (_, events)) in sequence.range(start..end).enumerate() { for (_, (_, events)) in sequence.notes.range(start..end).enumerate() {
for event in events.iter() { for event in events.iter() {
match event { match event {
::midly::MidiMessage::NoteOn {key,..} => { ::midly::MidiMessage::NoteOn {key,..} => {
@ -339,9 +353,10 @@ pub const KEYMAP: &'static [KeyBinding<Sequencer>] = keymap!(Sequencer {
[Char('z'), NONE, "note_del", "Delete note", note_del], [Char('z'), NONE, "note_del", "Delete note", note_del],
[CapsLock, NONE, "advance", "Toggle auto advance", nop], [CapsLock, NONE, "advance", "Toggle auto advance", nop],
[Char('w'), NONE, "rest", "Advance by note duration", nop], [Char('w'), NONE, "rest", "Advance by note duration", nop],
[Char(' '), NONE, "toggle_play", "Toggle play/pause", toggle_play],
[Char('r'), NONE, "toggle_record", "Toggle recording", toggle_record], [Char('r'), NONE, "toggle_record", "Toggle recording", toggle_record],
[Char('d'), NONE, "toggle_overdub", "Toggle overdub", toggle_overdub], [Char('d'), NONE, "toggle_overdub", "Toggle overdub", toggle_overdub],
[Char(' '), NONE, "toggle_play", "Toggle play/pause", toggle_play], [Char('m'), NONE, "toggle_monitor", "Toggle input monitoring", toggle_monitor],
[Char('s'), NONE, "stop_and_rewind", "Stop and rewind", stop_and_rewind], [Char('s'), NONE, "stop_and_rewind", "Stop and rewind", stop_and_rewind],
[Char('q'), NONE, "quantize_next", "Next quantize value", quantize_next], [Char('q'), NONE, "quantize_next", "Next quantize value", quantize_next],
[Char('Q'), SHIFT, "quantize_prev", "Previous quantize value", quantize_prev], [Char('Q'), SHIFT, "quantize_prev", "Previous quantize value", quantize_prev],
@ -378,7 +393,7 @@ fn note_add (s: &mut Sequencer) -> Usually<bool> {
let key = ::midly::num::u7::from_int_lossy((s.note_cursor + s.note_axis.0) as u8); let key = ::midly::num::u7::from_int_lossy((s.note_cursor + s.note_axis.0) as u8);
let note_on = ::midly::MidiMessage::NoteOn { key, vel: 100.into() }; let note_on = ::midly::MidiMessage::NoteOn { key, vel: 100.into() };
let note_off = ::midly::MidiMessage::NoteOff { key, vel: 100.into() }; let note_off = ::midly::MidiMessage::NoteOff { key, vel: 100.into() };
let mut sequence = &mut s.sequences[s.sequence]; let mut sequence = &mut s.sequences[s.sequence].notes;
if sequence.contains_key(&start) { if sequence.contains_key(&start) {
sequence.get_mut(&start).unwrap().push(note_on.clone()); sequence.get_mut(&start).unwrap().push(note_on.clone());
} else { } else {
@ -496,6 +511,10 @@ fn toggle_overdub (s: &mut Sequencer) -> Usually<bool> {
s.overdub = !s.overdub; s.overdub = !s.overdub;
Ok(true) Ok(true)
} }
fn toggle_monitor (s: &mut Sequencer) -> Usually<bool> {
s.monitoring = !s.monitoring;
Ok(true)
}
fn quantize_next (s: &mut Sequencer) -> Usually<bool> { fn quantize_next (s: &mut Sequencer) -> Usually<bool> {
if s.resolution < 64 { if s.resolution < 64 {
s.resolution = s.resolution * 2; s.resolution = s.resolution * 2;

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use super::*; use super::*;
pub fn draw_horizontal ( pub fn draw (
s: &Sequencer, s: &Sequencer,
buf: &mut Buffer, buf: &mut Buffer,
mut area: Rect, mut area: Rect,
@ -14,7 +14,7 @@ pub fn draw_horizontal (
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); lanes(s, buf, x, y, width);
cursor(s, buf, x, y); cursor(s, buf, x, y);
footer(s, buf, x, y, width, height); footer(s, buf, x - 13, y, width, height);
Ok(Rect { Ok(Rect {
x: x - 13, x: x - 13,
y: y, y: y,
@ -23,7 +23,7 @@ pub fn draw_horizontal (
}) })
} }
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 bw = Style::default().dim();
let bg = Style::default().on_black(); let bg = Style::default().on_black();
let (time0, time1) = s.time_axis; let (time0, time1) = s.time_axis;
@ -36,18 +36,19 @@ fn timer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, beat: usize) {
} }
} }
fn lanes (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16) { pub fn lanes (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16) {
let bw = Style::default().dim(); let bw = Style::default().dim();
let bg = Style::default().on_black(); let bg = Style::default().on_black();
let ppq = s.timebase.ppq() as u32; let ppq = s.timebase.ppq() as u32;
let (time0, time1) = s.time_axis; let (time0, time1) = s.time_axis;
let notes = &s.sequences[s.sequence].notes;
for step in time0..time1 { for step in time0..time1 {
let time_start = step as u32 * ppq; let time_start = step as u32 * ppq;
let time_end = (step + 1) as u32 * ppq; let time_end = (step + 1) as u32 * ppq;
if step % s.resolution as u16 == 0 { if step % s.resolution as u16 == 0 {
buf.set_string(x + 6 + step, y - 1, &format!("{}", step + 1), Style::default()); buf.set_string(x + 6 + step, y - 1, &format!("{}", step + 1), Style::default());
} }
for (_, (_, events)) in s.sequences[s.sequence].range(time_start..time_end).enumerate() { for (_, (_, events)) in notes.range(time_start..time_end).enumerate() {
if events.len() > 0 { if events.len() > 0 {
buf.set_string(x + 6 + step as u16, y, "", bw); buf.set_string(x + 6 + step as u16, y, "", bw);
} }
@ -55,14 +56,16 @@ fn lanes (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16) {
} }
} }
fn footer (s: &Sequencer, buf: &mut Buffer, 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 bw = Style::default().dim();
let bg = Style::default().on_black(); let bg = Style::default().on_black();
let (note0, note1) = s.note_axis; let (note0, note1) = s.note_axis;
buf.set_string(x - 13, 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())),
Style::default().dim());
x = x + 2;
{ {
let mut x = x - 11;
for (i, [letter, title, value]) in [ for (i, [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)],
@ -87,7 +90,7 @@ fn footer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, width: u16, height:
} }
} }
fn cursor (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) { pub fn cursor (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
let bw = Style::default().dim(); let bw = Style::default().dim();
let bg = Style::default().on_black(); let bg = Style::default().on_black();
buf.set_string( buf.set_string(
@ -98,7 +101,7 @@ fn cursor (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
); );
} }
fn keys (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> { pub fn keys (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
let bw = Style::default().dim(); let bw = Style::default().dim();
let bg = Style::default().on_black(); let bg = Style::default().on_black();
let Rect { x, y, .. } = area; let Rect { x, y, .. } = area;

View file

@ -1,7 +1,7 @@
use crate::prelude::*; use crate::prelude::*;
use super::*; use super::*;
pub fn draw_vertical ( pub fn draw (
s: &Sequencer, s: &Sequencer,
buf: &mut Buffer, buf: &mut Buffer,
mut area: Rect, mut area: Rect,
@ -17,7 +17,7 @@ pub fn draw_vertical (
Ok(Rect { x, y, width: area.width, height: height + 3 }) Ok(Rect { x, y, width: area.width, height: height + 3 })
} }
fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) { pub fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
let ppq = s.timebase.ppq() as u32; let ppq = s.timebase.ppq() as u32;
let bw = Style::default().dim().on_black(); let bw = Style::default().dim().on_black();
let bg = Style::default().on_black(); let bg = Style::default().on_black();
@ -64,7 +64,7 @@ fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
} }
} }
fn footer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, height: u16) { pub fn footer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, height: u16) {
buf.set_string(x + 2, y + height + 1, format!( buf.set_string(x + 2, y + height + 1, format!(
"Q 1/{} | N {} ({}-{}) | T {} ({}-{})", "Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
4 * s.resolution, 4 * s.resolution,
@ -77,7 +77,7 @@ fn footer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, height: u16) {
), Style::default().dim()); ), Style::default().dim());
} }
fn playhead (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) { pub fn playhead (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
buf.set_string( buf.set_string(
x + 5 + s.note_cursor, x + 5 + s.note_cursor,
y + s.time_cursor / 2, y + s.time_cursor / 2,
@ -86,7 +86,7 @@ fn playhead (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
); );
} }
fn keys (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize) { pub fn keys (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize) {
let ppq = s.timebase.ppq() as u32; let ppq = s.timebase.ppq() as u32;
let Rect { x, y, .. } = area; let Rect { x, y, .. } = area;
let (note0, note1) = s.note_axis; let (note0, note1) = s.note_axis;

View file

@ -21,10 +21,12 @@ fn main () -> Result<(), Box<dyn Error>> {
crate::config::create_dirs(&xdg)?; crate::config::create_dirs(&xdg)?;
let transport = crate::device::Transport::new("Transport")?; let transport = crate::device::Transport::new("Transport")?;
let timebase = transport.state.lock().unwrap().timebase(); let timebase = transport.state.lock().unwrap().timebase();
crate::device::run(Chain::new("Chain#0000", vec![ //crate::device::run(Sequencer::new("Sequencer#0", &timebase)?)
Box::new(Sequencer::new("Phrase#000", &timebase)?), crate::device::run(Launcher::new("Launcher#0", &timebase)?)
Box::new(Sequencer::new("Phrase#001", &timebase)?), //crate::device::run(Chain::new("Chain#0000", vec![
//Box::new(Sequencer::new("Phrase#002", &timebase)?), //Box::new(Sequencer::new("Phrase#000", &timebase)?),
//Box::new(Plugin::new("Plugin#000")?), //Box::new(Sequencer::new("Phrase#001", &timebase)?),
])?) ////Box::new(Sequencer::new("Phrase#002", &timebase)?),
////Box::new(Plugin::new("Plugin#000")?),
//])?)
} }