dynamic device activation

This commit is contained in:
🪞👃🪞 2024-06-16 19:13:50 +03:00
parent b73aa8a0dc
commit e4f3942757
9 changed files with 484 additions and 587 deletions

View file

@ -3,108 +3,97 @@ use ratatui::style::Stylize;
pub struct Sequencer {
name: String,
mode: SequencerMode,
cursor: (u16, u16, u16),
mode: SequencerView,
note_axis: (u16, u16),
note_cursor: u16,
time_axis: (u16, u16),
time_cursor: u16,
rate: Hz,
tempo: Tempo,
sequences: Vec<Sequence>,
input_port: ::jack::Port<::jack::MidiIn>,
transport: ::jack::Transport,
sequence: std::collections::BTreeMap<u32, Vec<Vec<u8>>>,
ppq: u32,
input_port: Port<MidiIn>,
input_connect: Vec<String>,
output_port: ::jack::Port<::jack::MidiOut>,
output_port: Port<MidiOut>,
output_connect: Vec<String>,
playing: bool,
recording: bool,
overdub: bool,
}
pub enum SequencerMode {
enum SequencerView {
Tiny,
Compact,
Vertical,
Horizontal,
Vertical,
}
impl Sequencer {
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
pub fn new (name: &str) -> Usually<DynamicDevice<Self>> {
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
let mut input = client.register_port("input", ::jack::MidiIn::default())?;
let mut output = client.register_port("output", ::jack::MidiOut::default())?;
let device = DynamicDevice::new(render, handle, process, Self {
name: name.into(),
mode: SequencerMode::Vertical,
cursor: (11, 0, 0),
rate: Hz(client.sample_rate() as u32),
tempo: Tempo(120000),
sequences: vec![
Sequence::new(&client, "Rhythm#000",)?,
Sequence::new(&client, "Rhythm#001",)?,
Sequence::new(&client, "Rhythm#002",)?,
Sequence::new(&client, "Rhythm#003",)?,
],
input_port: client.register_port("in", ::jack::MidiIn::default())?,
input_connect: vec![],
output_port: client.register_port("out", ::jack::MidiOut::default())?,
output_connect: vec![],
});
device.activate(client)?;
Ok(device)
DynamicDevice::new(
render,
handle,
process,
Self {
name: name.into(),
mode: SequencerView::Horizontal,
note_axis: (36, 60),
note_cursor: 0,
time_axis: (0, 64),
time_cursor: 0,
rate: Hz(client.sample_rate() as u32),
tempo: Tempo(120000),
transport: client.transport(),
input_port: client.register_port("in", MidiIn::default())?,
input_connect: vec!["nanoKEY Studio * (capture): *".into()],
output_port: client.register_port("out", MidiOut::default())?,
output_connect: vec![],
ppq: 96,
sequence: std::collections::BTreeMap::new(),
playing: true,
recording: true,
overdub: true,
}
).activate(client)
}
}
pub fn process (state: &mut Sequencer, client: &Client, scope: &ProcessScope) -> Control {
for seq in state.sequences.iter() {
seq.chunk_out(scope, &mut state.output_port);
seq.chunk_in(scope, &state.input_port);
}
process_out(state, scope);
process_in(state, scope);
Control::Continue
}
pub struct Sequence {
is_recording: bool,
overdub: bool,
ppq: u32,
buffer: std::collections::BTreeMap<u32, Option<Vec<Vec<u8>>>>,
loop_points: Option<(u32, u32)>,
is_playing: bool,
}
impl Sequence {
fn new (client: &Client, name: &str) -> Usually<Self> {
Ok(Self {
is_recording: false,
overdub: false,
ppq: 96,
buffer: std::collections::BTreeMap::new(),
loop_points: None,
is_playing: false
})
}
fn chunk_in (
&self,
scope: &ProcessScope,
port: &::jack::Port<::jack::MidiIn>,
) {
if self.is_recording {
for event in port.iter(scope) {
let ::jack::RawMidi { time, bytes } = event;
println!("\n{time} {bytes:?}");
}
}
}
fn chunk_out (
&self,
scope: &ProcessScope,
port: &mut ::jack::Port<::jack::MidiOut>,
) {
if self.is_playing {
let size = scope.n_frames();
let start = scope.last_frame_time();
let end = start + size;
let mut writer = port.writer(scope);
for time in 0..size {
// TODO
}
fn process_in (state: &Sequencer, scope: &ProcessScope) {
for event in state.input_port.iter(scope) {
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
midly::live::LiveEvent::Midi { channel, message } => match message {
midly::MidiMessage::NoteOn { key, vel } => {
panic!("on")
},
midly::MidiMessage::NoteOff { key, vel } => {
panic!("off")
},
_ => {}
},
_ => {}
}
}
}
fn process_out (state: &mut Sequencer, scope: &ProcessScope) {
if state.playing {
let size = scope.n_frames();
let start = scope.last_frame_time();
let end = start + size;
let mut writer = state.output_port.writer(scope);
for time in 0..size {
// TODO
}
}
}
impl NotificationHandler for Sequencer {
fn thread_init (&self, _: &Client) {}
@ -127,112 +116,152 @@ impl NotificationHandler for Sequencer {
}
}
pub const ACTIONS: [(&'static str, &'static str);4] = [
("+/-", "Zoom"),
("A/D", "Add/delete note"),
("]/[", "Duration"),
("CapsLock", "Auto advance"),
pub fn handle (state: &mut Sequencer, event: &AppEvent) -> Result<(), Box<dyn Error>> {
match event {
AppEvent::Input(Event::Key(event)) => {
for (code, _, _, command) in COMMANDS.iter() {
if *code == event.code {
command(state);
break
}
}
},
_ => {}
};
Ok(())
}
const COMMANDS: [(KeyCode, &'static str, &'static str, &'static dyn Fn(&mut Sequencer));18] = [
(KeyCode::Up, "cursor_up", "move cursor up", &cursor_up),
(KeyCode::Down, "cursor_down", "move cursor down", &cursor_down),
(KeyCode::Left, "cursor_left", "move cursor left", &cursor_left),
(KeyCode::Right, "cursor_right", "move cursor right", &cursor_right),
(KeyCode::Char(']'), "cursor_inc", "increase note duration", &cursor_inc),
(KeyCode::Char('['), "cursor_dec", "decrease note duration", &cursor_dec),
(KeyCode::Char('`'), "mode_next", "next view mode", &mode_next),
(KeyCode::Tab, "mode_prev", "previous view mode", &mode_prev),
(KeyCode::Char('+'), "zoom_in", "Zoom in", &nop),
(KeyCode::Char('-'), "zoom_out", "Zoom out", &nop),
(KeyCode::Char('a'), "note_add", "Add note", &note_add),
(KeyCode::Char('d'), "note_del", "Delete note", &note_del),
(KeyCode::CapsLock, "advance", "Toggle auto advance", &nop),
(KeyCode::Char('w'), "rest", "Advance by note duration", &nop),
(KeyCode::Char('r'), "record", "Toggle recodring", &toggle_record),
(KeyCode::Char('o'), "overdub", "Toggle overdub", &toggle_overdub),
(KeyCode::Char('p'), "play", "Toggle play/pause", &toggle_play),
(KeyCode::Char('s'), "stop", "Stop and rewind", &nop),
];
pub fn handle (state: &mut Sequencer, event: &AppEvent) -> Result<(), Box<dyn Error>> {
if let AppEvent::Focus = event {
fn nop (_: &mut Sequencer) {
}
fn note_add (s: &mut Sequencer) {
let time = (s.time_axis.0 + s.time_cursor) as u32;
let time_start = time * s.ppq;
let time_end = (time + 1) * s.ppq;
let note_on = vec![0, 0, 0];
let note_off = vec![0, 0, 0];
if s.sequence.contains_key(&time_start) {
s.sequence.get_mut(&time_start).unwrap().push(note_off.clone());
} else {
s.sequence.insert(time_start, vec![note_on]);
}
if let AppEvent::Input(Event::Key(event)) = event {
match event.code {
KeyCode::Down => {
state.cursor.0 = if state.cursor.0 >= 23 {
0
} else {
state.cursor.0 + 1
}
},
KeyCode::Up => {
state.cursor.0 = if state.cursor.0 == 0 {
23
} else {
state.cursor.0 - 1
}
},
KeyCode::Left => {
state.cursor.1 = if state.cursor.1 == 0 {
63
} else {
state.cursor.1 - 1
}
},
KeyCode::Right => {
state.cursor.1 = if state.cursor.1 == 63 {
0
} else {
state.cursor.1 + 1
}
},
KeyCode::Char('[') => {
if state.cursor.2 > 0 {
state.cursor.2 = state.cursor.2 - 1
}
},
KeyCode::Char(']') => {
state.cursor.2 = state.cursor.2 + 1
},
KeyCode::Char('i') => {
//state.inputs_open.fetch_xor(true, Ordering::Relaxed);
},
KeyCode::Char('o') => {
//state.outputs_open.fetch_xor(true, Ordering::Relaxed);
},
KeyCode::Char('a') => {
let row = state.cursor.0 as usize;
let step = state.cursor.1 as usize;
let duration = state.cursor.2 as usize;
//let mut sequence = state.sequence.lock().unwrap();
//sequence[row][step] = Some(SequencerEvent::NoteOn(48 - row as u8, 128));
//if state.cursor.2 > 0 {
//sequence[row][step + duration] = Some(SequencerEvent::NoteOff(35));
//}
},
_ => {
println!("{event:?}");
}
}
if s.sequence.contains_key(&time_end) {
s.sequence.get_mut(&time_end).unwrap().push(note_off.clone());
} else {
s.sequence.insert(time_end, vec![note_off]);
}
Ok(())
}
fn note_del (_: &mut Sequencer) {
}
fn cursor_up (s: &mut Sequencer) {
let time = s.time_axis.1 - s.time_axis.0;
let note = s.note_axis.1 - s.note_axis.0;
match s.mode {
SequencerView::Vertical => { s.time_cursor = ((time + s.time_cursor) - 1) % time },
SequencerView::Horizontal => { s.note_cursor = ((note + s.note_cursor) - 1) % note },
_ => unimplemented!()
}
}
fn cursor_down (s: &mut Sequencer) {
let time = s.time_axis.1 - s.time_axis.0;
let note = s.note_axis.1 - s.note_axis.0;
match s.mode {
SequencerView::Vertical => { s.time_cursor = ((time + s.time_cursor) + 1) % time },
SequencerView::Horizontal => { s.note_cursor = ((note + s.note_cursor) + 1) % note },
_ => unimplemented!()
}
}
fn cursor_left (s: &mut Sequencer) {
let time = s.time_axis.1 - s.time_axis.0;
let note = s.note_axis.1 - s.note_axis.0;
match s.mode {
SequencerView::Vertical => { s.note_cursor = ((note + s.note_cursor) - 1) % note },
SequencerView::Horizontal => { s.time_cursor = ((time + s.time_cursor) - 1) % time },
_ => unimplemented!()
}
}
fn cursor_right (s: &mut Sequencer) {
let time = s.time_axis.1 - s.time_axis.0;
let note = s.note_axis.1 - s.note_axis.0;
match s.mode {
SequencerView::Vertical => { s.note_cursor = ((note + s.note_cursor) + 1) % note },
SequencerView::Horizontal => { s.time_cursor = ((time + s.time_cursor) + 1) % time },
_ => unimplemented!()
}
}
fn cursor_inc (s: &mut Sequencer) {
//s.cursor.2 = s.cursor.2 + 1
}
fn cursor_dec (s: &mut Sequencer) {
//if s.cursor.2 > 0 { s.cursor.2 = s.cursor.2 - 1 }
}
fn mode_next (s: &mut Sequencer) {
s.mode = SequencerView::Horizontal
}
fn mode_prev (s: &mut Sequencer) {
s.mode = SequencerView::Vertical
}
fn toggle_play (s: &mut Sequencer) {
s.playing = !s.playing
}
fn toggle_record (s: &mut Sequencer) {
s.recording = !s.recording
}
fn toggle_overdub (s: &mut Sequencer) {
s.overdub = !s.overdub
}
fn render (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect)
-> Usually<Rect>
{
let Rect { x, y, width, height } = area;
let (time0, time1) = sequencer.time_axis;
let (note0, note1) = sequencer.note_axis;
let header = draw_sequencer_header(sequencer, buf, area)?;
let piano = match sequencer.mode {
SequencerMode::Tiny => Rect {
x: area.x,
y: area.y,
width: area.width,
height: 0
},
SequencerMode::Compact => Rect {
x: area.x,
y: area.y,
width: area.width,
height: 0
},
SequencerMode::Vertical => draw_sequencer_vertical(sequencer, buf, Rect {
x: area.x,
y: area.y + header.height,
width: area.width,
height: area.height - header.height
SequencerView::Tiny =>
Rect { x, y, width, height: 0 },
SequencerView::Compact =>
Rect { x, y, width, height: 0 },
SequencerView::Vertical => draw_sequencer_vertical(sequencer, buf, Rect {
x,
y: y + header.height,
width: 3 + note1 - note0,
height: 3 + time1 - time0,
})?,
SequencerView::Horizontal => draw_sequencer_horizontal(sequencer, buf, Rect {
x,
y: y + header.height,
width: 3 + time1 - time0,
height: 3 + note1 - note0,
})?,
_ => unimplemented!()
};
//draw_box_styled(buf, area, Some(Style::default().red()));
Ok(Rect {
x: area.x,
y: area.y,
width: header.width.max(piano.width),
height: header.height + piano.height
})
Ok(draw_box(buf, Rect {
x,
y,
width: header.width.max(piano.width),
height: header.height + piano.height + 1
}))
}
fn draw_sequencer_header (sequencer: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
@ -240,8 +269,23 @@ fn draw_sequencer_header (sequencer: &Sequencer, buf: &mut Buffer, area: Rect) -
let style = Style::default().gray();
buf.set_string(x + 1, y + 1, &format!(" │ 00:00.00 / 00:00.00"), style);
buf.set_string(x + 2, y + 1, &format!("{}", &sequencer.name), style.white().bold());
buf.set_string(x + 1, y + 2, &format!(" ▶ PLAY │ ⏹ STOP │ ⏺ REC │ ⏺ DUB "), style);
Ok(Rect { x, y, width, height: 4 })
buf.set_string(x + 1, y + 2, &format!(" ▶ PLAY │ ⏹ STOP │ │"), style);
buf.set_string(x + 2, y + 2, &format!("▶ PLAY"), if sequencer.playing {
Style::default().green()
} else {
Style::default().dim()
});
buf.set_string(x + 24, y + 2, &format!("⏺ REC"), if sequencer.recording {
Style::default().red()
} else {
Style::default().dim()
});
buf.set_string(x + 32, y + 2, &format!("⏺ DUB"), if sequencer.overdub {
Style::default().yellow()
} else {
Style::default().dim()
});
Ok(Rect { x, y, width: 39, height: 4 })
}
const KEY_WHITE: Style = Style {
@ -267,33 +311,134 @@ const KEY_HORIZONTAL_STYLE: [Style;12] = [
fn draw_sequencer_vertical (sequencer: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, .. } = area;
for i in 0..area.width-7 {
let color = KEY_HORIZONTAL_STYLE[i as usize % 12];
buf.set_string(x + 5 + i, y - 1, &format!(""), color);
buf.set_string(x + 5 + i, y, &format!(""), color);
}
let (time0, time1) = sequencer.time_axis;
let (note0, note1) = sequencer.note_axis;
let bg = Style::default().on_black();
for i in 0..area.height - 8 {
buf.set_string(x + 5, y + 1 + i, &" ".repeat((area.width-7)as usize), bg);
if i % 4 == 0 {
buf.set_string(x + 2, y + 1 + i, &format!("{:2}", i / 4 + 1), Style::default());
for step in 0..(time1-time0)/2 {
let y = y + step - time0;
buf.set_string(x + 5, y, &" ".repeat(32.max(note1-note0)as usize), bg);
if step % 2 == 0 {
buf.set_string(x + 2, y, &format!("{:2} ", step / 2 + 1), Style::default());
}
}
for octave in -2i16..=8i16 {
let x = x + 5 + (24i16 + octave * 12) as u16;
if x >= area.x + area.width - 6 {
break
for key in note0..note1.max(note0+32) {
let x = x + 5 + key - note0;
let color = KEY_HORIZONTAL_STYLE[key as usize % 12];
buf.set_string(x, y - 1, &format!(""), color);
if key % 12 == 0 {
let octave = format!("C{}", (key / 12) as i8 - 4);
buf.set_string(x, y + 1, &octave, Style::default());
}
buf.set_string(
x,
y + 1,
&format!("C{octave}"),
Style::default()
);
}
Ok(Rect { x, y, width: area.width, height: area.height - 6 })
buf.set_string(
x + 5 + sequencer.note_cursor,
y + sequencer.time_cursor / 2,
if sequencer.time_cursor % 2 == 0 { "" } else { "" },
Style::default()
);
Ok(Rect { x, y, width: area.width, height: (time1-time0)/2 })
}
const KEYS_VERTICAL: [&'static str; 6] = [
"", "", "", "", "", "",
];
fn draw_sequencer_horizontal (sequencer: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, .. } = area;
let (time0, time1) = sequencer.time_axis;
let (note0, note1) = sequencer.note_axis;
let bw = Style::default().dim();
let bg = Style::default().on_black();
for i in 0..32.max(note1-note0)/2 {
let y = y + i;
buf.set_string(x + 1, y, KEYS_VERTICAL[(i % 6) as usize], bw);
buf.set_string(x + 2, y, "", bw);
buf.set_string(x + 5, y, &" ".repeat((time1 - time0) as usize), bg);
if i % 6 == 0 {
let octave = format!("C{}", ((note1 - i) / 6) as i8 - 4);
buf.set_string(x+5, y, &octave, Style::default());
}
}
for step in time0..time1 {
let time_start = step as u32 * sequencer.ppq;
let time_end = (step + 1) as u32 * sequencer.ppq;
for (i, (t, events)) in sequencer.sequence.range(time_start..time_end).enumerate() {
if events.len() > 0 {
buf.set_string(x + 5 + step as u16, y, "", bw);
}
}
}
buf.set_string(
x + 5 + sequencer.time_cursor,
y + sequencer.note_cursor / 2,
if sequencer.note_cursor % 2 == 0 { "" } else { "" },
Style::default()
);
Ok(Rect { x, y, width: time1 - time0 + 6, height: 32.max(note1 - note0) / 2 })
}
//buf.set_string(x + 3, y, "╭1.1.", Style::default().dim());
//buf.set_string(x + 3 + 16, y, "╭1.2.", Style::default().dim());
//buf.set_string(x + 3 + 32, y, "╭1.3.", Style::default().dim());
//buf.set_string(x + 3 + 48, y, "╭1.4.", Style::default().dim());
////buf.set_string(x + 2, y, "╭", Style::default().dim());
////buf.set_string(x + 2, y + 13, "╰", Style::default().dim());
//buf.set_string(x + 2 + 65, y, "╮", Style::default().dim());
//buf.set_string(x + 2 + 65, y + 13, "╯", Style::default().dim());
//let transport = sequencer.transport.query().unwrap();
//let frame = transport.pos.frame();
//let rate = transport.pos.frame_rate().unwrap();
//let second = (frame as f64) / (rate as f64);
//let minute = second / 60f64;
//let bpm = 120f64;
//let div = 4;
//let beats = minute * bpm;
//let bars = beats as u32 / div as u32;
//let beat = beats as u32 % div as u32 + 1;
//let beat_sub = beats % 1.0;
//buf.set_string(
//x - 18, y + height,
//format!("BBT {bars:04}:{beat:02}.{:02}", (beat_sub * 16.0) as u32),
//Style::default()
//);
//let bg = if step as u32 == (beat - 1) * 16 + (beat_sub * 16.0) as u32 {
//ratatui::style::Color::Gray
//} else if step == sequencer.cursor.1 as usize || key == sequencer.cursor.0/2 {
//ratatui::style::Color::Black
//} else {
//ratatui::style::Color::Reset
//};
//let top = sequence[(key * 2) as usize][step].is_some();
//let bottom = sequence[(key * 2 + 1) as usize][step].is_some();
//match (top, bottom) {
//(true, true) => {
//buf.set_string(x + 3 + step as u16, y + 1 + key, "█",
//Style::default().yellow().not_dim().bold().bg(bg));
//},
//(true, false) => {
//buf.set_string(x + 3 + step as u16, y + 1 + key, "▀",
//Style::default().yellow().not_dim().bold().bg(bg));
//},
//(false, true) => {
//buf.set_string(x + 3 + step as u16, y + 1 + key, "▄",
//Style::default().yellow().not_dim().bold().bg(bg));
//},
//(false, false) => if step % 16 == 0 {
//buf.set_string(x + 3 + step as u16, y + 1 + key, "┊",
//Style::default().dim().bg(bg))
//} else {
//buf.set_string(x + 3 + step as u16, y + 1 + key, "·",
//Style::default().dim().bg(bg))
//},
//}
//for step in 0..64 {
//if step % 8 == 0 {
//buf.set_string(x + 3 + step as u16, y + 1 + 12, [
//"|a", "|b", "|c", "|d", "|e", "|f", "|g", "|h"
//][step / 8 as usize], Style::default().dim())
//}
//}
//{
//let mut area = area.clone();
//area.y = area.y + 3;
@ -371,219 +516,32 @@ fn draw_sequencer_vertical (sequencer: &Sequencer, buf: &mut Buffer, area: Rect)
//}
//}
//
fn draw_sequence_button (
buf: &mut Buffer,
x: u16,
y: u16,
name: &str
) {
draw_box(buf, Rect { x, y, width: 18, height: 3 });
buf.set_string(x + 1, y + 1, &format!("{} ", name),
Style::default().white().bold().not_dim());
buf.set_string(x + 4, y + 0, "", Style::default().gray());
buf.set_string(x + 4, y + 1, "", Style::default().gray().dim());
buf.set_string(x + 4, y + 2, "", Style::default().gray());
}
fn draw_rec_dub_button (
buf: &mut Buffer,
x: u16,
y: u16,
) {
draw_box(buf, Rect { x, y, width: 18, height: 3 });
buf.set_string(x + 1, y + 1, " ⏺ REC DUB ",
Style::default().white().bold().not_dim());
buf.set_string(x + 5, y + 0, "", Style::default().gray());
buf.set_string(x + 5, y + 1, "", Style::default().gray().dim());
buf.set_string(x + 5, y + 2, "", Style::default().gray());
buf.set_string(x + 11, y + 0, "", Style::default().gray());
buf.set_string(x + 11, y + 1, "", Style::default().gray().dim());
buf.set_string(x + 11, y + 2, "", Style::default().gray());
}
const NOTE_NAMES: [&'static str;12] = [
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
];
const KEYS_VERTICAL: [&'static str; 6] = [
"", "", "", "", "", "",
];
fn draw_sequence_keys (
area: Rect,
buf: &mut Buffer,
transport: &::jack::TransportStatePosition,
sequence: &Arc<Mutex<Vec<Vec<Option<self::Event>>>>>,
cursor: &(u16, u16, u16)
) {
buf.set_string(area.x + 3, area.y, "╭1.1.", Style::default().dim());
buf.set_string(area.x + 3 + 16, area.y, "╭1.2.", Style::default().dim());
buf.set_string(area.x + 3 + 32, area.y, "╭1.3.", Style::default().dim());
buf.set_string(area.x + 3 + 48, area.y, "╭1.4.", Style::default().dim());
//buf.set_string(area.x + 2, area.y, "╭", Style::default().dim());
//buf.set_string(area.x + 2, area.y + 13, "╰", Style::default().dim());
buf.set_string(area.x + 2 + 65, area.y, "", Style::default().dim());
buf.set_string(area.x + 2 + 65, area.y + 13, "", Style::default().dim());
let frame = transport.pos.frame();
let rate = transport.pos.frame_rate().unwrap();
let second = (frame as f64) / (rate as f64);
let minute = second / 60f64;
let bpm = 120f64;
let div = 4;
let beats = minute * bpm;
let bars = beats as u32 / div as u32;
let beat = beats as u32 % div as u32 + 1;
let beat_sub = beats % 1.0;
//buf.set_string(
//area.x - 18, area.y + area.height,
//format!("BBT {bars:04}:{beat:02}.{:02}", (beat_sub * 16.0) as u32),
//Style::default()
//);
let sequence = sequence.lock().unwrap();
for key in 0..12 {
buf.set_string(area.x, area.y + 1 + key, KEYS_VERTICAL[(key % 6) as usize],
Style::default().dim());
buf.set_string(area.x + 1, area.y + 1 + key, "",
Style::default().dim());
for step in 0..64 {
let bg = if step as u32 == (beat - 1) * 16 + (beat_sub * 16.0) as u32 {
ratatui::style::Color::Gray
} else if step == cursor.1 as usize || key == cursor.0/2 {
ratatui::style::Color::Black
} else {
ratatui::style::Color::Reset
};
let top = sequence[(key * 2) as usize][step].is_some();
let bottom = sequence[(key * 2 + 1) as usize][step].is_some();
match (top, bottom) {
(true, true) => {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "",
Style::default().yellow().not_dim().bold().bg(bg));
},
(true, false) => {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "",
Style::default().yellow().not_dim().bold().bg(bg));
},
(false, true) => {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "",
Style::default().yellow().not_dim().bold().bg(bg));
},
(false, false) => if step % 16 == 0 {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "",
Style::default().dim().bg(bg))
} else {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "·",
Style::default().dim().bg(bg))
},
}
}
}
for step in 0..64 {
if step % 8 == 0 {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + 12, [
"|A", "|B", "|C", "|D", "|E", "|F", "|G", "|H"
][step / 8 as usize], Style::default().dim())
}
}
}
fn draw_sequence_cursor (
area: Rect, buf: &mut Buffer, cursor: &(u16, u16, u16)
) {
let cursor_character = match cursor.0 % 2 {
0 => "",
1 => "",
_ => unreachable!()
};
let cursor_y = cursor.0 / 2;
let cursor_text = if cursor.2 == 0 {
cursor_character.into()
} else {
cursor_character.repeat(cursor.2 as usize)
};
let style = Style::default().yellow().dim();
buf.set_string(area.x + cursor.1 + 3, area.y + 1 + cursor_y, cursor_text, style);
}
//pub type MIDISequenceVoice = std::collections::BTreeMap<u32, NoteEvent>;
//#[derive(Clone)]
//pub enum NoteEvent {
//On(u8),
//Off(u8),
//fn draw_sequence_button (
//buf: &mut Buffer,
//x: u16,
//y: u16,
//name: &str
//) {
//draw_box(buf, Rect { x, y, width: 18, height: 3 });
//buf.set_string(x + 1, y + 1, &format!(" ▶ {} ", name),
//Style::default().white().bold().not_dim());
//buf.set_string(x + 4, y + 0, "┬", Style::default().gray());
//buf.set_string(x + 4, y + 1, "│", Style::default().gray().dim());
//buf.set_string(x + 4, y + 2, "┴", Style::default().gray());
//}
//const VOICE_EMPTY: MIDISequenceVoice = MIDISequenceVoice::new();
//impl MIDISequence {
//fn new () -> Self {
//Self {
//channels: [
//MIDISequenceChannel::new(1),
//MIDISequenceChannel::new(2),
//MIDISequenceChannel::new(3),
//MIDISequenceChannel::new(4),
//MIDISequenceChannel::new(5),
//MIDISequenceChannel::new(6),
//MIDISequenceChannel::new(7),
//MIDISequenceChannel::new(8),
//MIDISequenceChannel::new(9),
//MIDISequenceChannel::new(10),
//MIDISequenceChannel::new(11),
//MIDISequenceChannel::new(12),
//MIDISequenceChannel::new(13),
//MIDISequenceChannel::new(14),
//MIDISequenceChannel::new(15),
//MIDISequenceChannel::new(16),
//]
//}
//}
//fn draw_rec_dub_button (
//buf: &mut Buffer,
//x: u16,
//y: u16,
//) {
//draw_box(buf, Rect { x, y, width: 18, height: 3 });
//buf.set_string(x + 1, y + 1, " ⏺ REC DUB ",
//Style::default().white().bold().not_dim());
//buf.set_string(x + 5, y + 0, "┬", Style::default().gray());
//buf.set_string(x + 5, y + 1, "│", Style::default().gray().dim());
//buf.set_string(x + 5, y + 2, "┴", Style::default().gray());
//buf.set_string(x + 11, y + 0, "┬", Style::default().gray());
//buf.set_string(x + 11, y + 1, "│", Style::default().gray().dim());
//buf.set_string(x + 11, y + 2, "┴", Style::default().gray());
//}
//pub struct MIDISequence {
//channels: [MIDISequenceChannel;16],
//}
//pub struct MIDISequenceChannel {
//number: u8,
//notes: [MIDISequenceVoice;128],
//}
//impl MIDISequenceChannel {
//fn new (number: u8) -> Self {
//Self {
//number,
//notes: [VOICE_EMPTY;128]
//}
//}
//}
//#[derive(Clone)]
//pub enum SequencerEvent {
//NoteOn(u8, u8),
//NoteOff(u8)
//}
//let buffer_index = self.to_buffer_index(chunk_start + frame, scope, bpm);
//let value = frame_steps[(start_looped + frame as usize) % loop_frames];
//if let Some(step) = value {
//for track in sequence.iter() {
//for event in track[step].iter() {
//writer.write(&::jack::RawMidi {
//time: frame as u32,
//bytes: &match event {
//SequencerEvent::NoteOn(pitch, velocity) => [
//0b10010000,
//*pitch,
//*velocity
//],
//SequencerEvent::NoteOff(pitch) => [
//0b10000000,
//*pitch,
//0b00000000
//],
//}
//}).unwrap()
//}
//}
//}

View file

@ -10,12 +10,12 @@ pub struct Transport {
impl Transport {
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
Ok(DynamicDevice::new(render, handle, process, Self {
DynamicDevice::new(render, handle, process, Self {
name: name.into(),
transport: client.transport(),
timesig: (4.0, 4.0),
bpm: 113.0,
}))
}).activate(client)
}
pub fn play_from_start_or_stop_and_rewind (&mut self) {
@ -233,39 +233,22 @@ pub const ACTIONS: [(&'static str, &'static str);4] = [
("(Shift-)Space", "⯈ Play/pause"),
];
struct TransportJack;
impl NotificationHandler for TransportJack {
fn thread_init (&self, _: &Client) {
}
fn shutdown (&mut self, status: ClientStatus, reason: &str) {
}
fn freewheel (&mut self, _: &Client, is_enabled: bool) {
}
impl NotificationHandler for Transport {
fn thread_init (&self, _: &Client) {}
fn shutdown (&mut self, status: ClientStatus, reason: &str) {}
fn freewheel (&mut self, _: &Client, is_enabled: bool) {}
fn sample_rate (&mut self, _: &Client, _: Frames) -> Control {
Control::Quit
}
fn client_registration (&mut self, _: &Client, name: &str, is_reg: bool) {
}
fn port_registration (&mut self, _: &Client, port_id: PortId, is_reg: bool) {
}
fn client_registration (&mut self, _: &Client, name: &str, is_reg: bool) {}
fn port_registration (&mut self, _: &Client, port_id: PortId, is_reg: bool) {}
fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control {
Control::Continue
}
fn ports_connected (&mut self, _: &Client, id_a: PortId, id_b: PortId, are: bool) {
}
fn ports_connected (&mut self, _: &Client, id_a: PortId, id_b: PortId, are: bool) {}
fn graph_reorder (&mut self, _: &Client) -> Control {
Control::Continue
}
fn xrun (&mut self, _: &Client) -> Control {
Control::Continue
}