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::*;
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 {
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 {
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 {
Control::Continue
}
pub fn render (_: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
for i in 1..=8 {
buf.set_string(area.x + 2 + (i-1) * 10, area.y, format!("Track#{i} | "), Style::default())
macro_rules! set {
($buf:expr, $style:expr, $x: expr, $y: expr, $fmt: literal $(, $val:expr)*) => {
$buf.set_string($x, $y, &format!($fmt $(, $val)*), $style)
}
for i in 0..=7 {
for j in 0..=7 {
buf.set_string(area.x + 2 + i * 10, area.y + 2 + j, format!("······· | "), Style::default().dim())
}
trait Blit {
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,
y: area.y - 1,
width: area.width,
height: 12
}))
width
}
pub fn handle (_: &mut Launcher, _: &AppEvent) -> Usually<bool> {
Ok(false)
fn draw_crossings (state: &Launcher, buf: &mut Buffer, x: u16, y: u16) {
"".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 ratatui::style::Stylize;
type Sequence = std::collections::BTreeMap<u32, Vec<::midly::MidiMessage>>;
mod keys;
use keys::*;
mod horizontal;
use horizontal::*;
pub mod horizontal;
mod vertical;
use vertical::*;
pub mod 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 {
name: String,
pub name: String,
/// JACK transport handle.
transport: ::jack::Transport,
/// JACK MIDI input port that will be created.
@ -32,7 +39,7 @@ pub struct Sequencer {
/// Sequence selector
sequence: usize,
/// Map: tick -> MIDI events at tick
sequences: Vec<Sequence>,
pub sequences: Vec<Sequence>,
/// Red keys on piano roll.
notes_on: Vec<bool>,
@ -71,35 +78,44 @@ impl Sequencer {
let transport = client.transport();
let state = transport.query_state()?;
DynamicDevice::new(render, handle, Self::process, Self {
name: name.into(),
input_port: client.register_port("in", MidiIn::default())?,
output_port: client.register_port("out", MidiOut::default())?,
name: name.into(),
input_port: client.register_port("in", MidiIn::default())?,
output_port: client.register_port("out", MidiOut::default())?,
timebase: timebase.clone(),
steps: 64,
resolution: 8,
sequence: 0,
sequences: vec![std::collections::BTreeMap::new();8],
notes_on: vec![false;128],
timebase: timebase.clone(),
steps: 64,
resolution: 4,
sequence: 0,
sequences: vec![
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,
monitoring: true,
recording: true,
overdub: true,
playing: TransportState::Starting,
monitoring: true,
recording: true,
overdub: true,
transport,
mode: SequencerView::Horizontal,
note_axis: (36, 68),
note_cursor: 0,
time_axis: (0, 64),
time_cursor: 0,
mode: SequencerView::Horizontal,
note_axis: (36, 68),
note_cursor: 0,
time_axis: (0, 64),
time_cursor: 0,
}).activate(client)
}
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
// 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();
self.playing = transport.state;
let pos = &transport.pos;
@ -112,7 +128,33 @@ impl Sequencer {
let frames = scope.n_frames() as usize;
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) {
let tick = tick as u32;
let msg = midly::live::LiveEvent::parse(event.bytes).unwrap();
@ -164,40 +206,14 @@ impl Sequencer {
}
}
// Read from sequence
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![];
::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
// Write to port from output buffer
// (containing notes from sequence and/or monitor)
let mut writer = self.output_port.writer(scope);
for t in 0..scope.n_frames() {
if let Some(Some(frame)) = output.get_mut(t as usize) {
for time in 0..scope.n_frames() {
if let Some(Some(frame)) = output.get_mut(time as usize) {
for event in frame.iter() {
writer.write(&::jack::RawMidi {
time: t,
bytes: &event
}).expect(&format!("{event:?}"));
writer.write(&::jack::RawMidi { time, 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 },
SequencerView::Compact =>
Rect { x, y, width, height: 0 },
SequencerView::Vertical => draw_vertical(s, buf, Rect {
SequencerView::Vertical => self::vertical::draw(s, buf, Rect {
x,
y: y + header.height,
width: 3 + note1 - note0,
height: 3 + time1 - time0,
}, steps)?,
SequencerView::Horizontal => draw_horizontal(s, buf, Rect {
SequencerView::Horizontal => self::horizontal::draw(s, buf, Rect {
x,
y: y + header.height,
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::Stopped => format!("⏹ STOPPED")
}, match s.playing {
TransportState::Rolling => style.dim().bold(),
TransportState::Stopped => style.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 + 2, y + 2,
@ -267,24 +283,21 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu
//} else {
//Style::default().dim()
//});
buf.set_string(x + 13, y + 1,
&format!("⏺ REC"), if s.recording {
Style::default().bold().red()
} else {
Style::default().bold().dim()
});
buf.set_string(x + 20, y + 1,
&format!("⏺ DUB"), if s.overdub {
Style::default().bold().yellow()
} else {
Style::default().bold().dim()
});
buf.set_string(x + 27, y + 1,
&format!("⏺ MON"), if s.monitoring {
Style::default().bold().green()
} else {
Style::default().bold().dim()
});
buf.set_string(x + 13, y + 1, &format!("⏺ REC"), if s.recording {
Style::default().bold().red()
} else {
Style::default().bold().dim()
});
buf.set_string(x + 20, y + 1, &format!("⏺ DUB"), if s.overdub {
Style::default().bold().yellow()
} else {
Style::default().bold().dim()
});
buf.set_string(x + 27, y + 1, &format!("⏺ MON"), if s.monitoring {
Style::default().bold().green()
} else {
Style::default().bold().dim()
});
let clips = draw_clips(s, buf, area)?;
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> {
let Rect { x, y, .. } = area;
let style = Style::default().gray();
for i in 0..8 {
buf.set_string(x + 2, y + 3 + i*2, &format!("{}", &s.name), if i as usize == s.sequence {
for (i, sequence) in s.sequences.iter().enumerate() {
let label = format!("{}", &sequence.name);
buf.set_string(x + 2, y + 3 + (i as u16)*2, &label, if i == s.sequence {
match s.playing {
TransportState::Rolling => style.white().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 {
for (_, (_, events)) in sequence.range(start..end).enumerate() {
for (_, (_, events)) in sequence.notes.range(start..end).enumerate() {
for event in events.iter() {
match event {
::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],
[CapsLock, NONE, "advance", "Toggle auto advance", 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('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('q'), NONE, "quantize_next", "Next quantize value", quantize_next],
[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 note_on = ::midly::MidiMessage::NoteOn { 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) {
sequence.get_mut(&start).unwrap().push(note_on.clone());
} else {
@ -496,6 +511,10 @@ fn toggle_overdub (s: &mut Sequencer) -> Usually<bool> {
s.overdub = !s.overdub;
Ok(true)
}
fn toggle_monitor (s: &mut Sequencer) -> Usually<bool> {
s.monitoring = !s.monitoring;
Ok(true)
}
fn quantize_next (s: &mut Sequencer) -> Usually<bool> {
if s.resolution < 64 {
s.resolution = s.resolution * 2;

View file

@ -1,7 +1,7 @@
use crate::prelude::*;
use super::*;
pub fn draw_horizontal (
pub fn draw (
s: &Sequencer,
buf: &mut Buffer,
mut area: Rect,
@ -14,7 +14,7 @@ pub fn draw_horizontal (
let height = 32.max(s.note_axis.1 - s.note_axis.0) / 2;
lanes(s, buf, x, y, width);
cursor(s, buf, x, y);
footer(s, buf, x, y, width, height);
footer(s, buf, x - 13, y, width, height);
Ok(Rect {
x: x - 13,
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 bg = Style::default().on_black();
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 bg = Style::default().on_black();
let ppq = s.timebase.ppq() as u32;
let (time0, time1) = s.time_axis;
let notes = &s.sequences[s.sequence].notes;
for step in time0..time1 {
let time_start = step as u32 * ppq;
let time_end = (step + 1) as u32 * ppq;
if step % s.resolution as u16 == 0 {
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 {
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 bg = Style::default().on_black();
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());
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 [
["S", &format!("ync"), &format!("<4/4>")],
["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 bg = Style::default().on_black();
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 bg = Style::default().on_black();
let Rect { x, y, .. } = area;

View file

@ -1,7 +1,7 @@
use crate::prelude::*;
use super::*;
pub fn draw_vertical (
pub fn draw (
s: &Sequencer,
buf: &mut Buffer,
mut area: Rect,
@ -17,7 +17,7 @@ pub fn draw_vertical (
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 bw = Style::default().dim().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!(
"Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
4 * s.resolution,
@ -77,7 +77,7 @@ fn footer (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16, height: u16) {
), 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(
x + 5 + s.note_cursor,
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 Rect { x, y, .. } = area;
let (note0, note1) = s.note_axis;

View file

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