From d330d31ce448efefe8a7cc525b68990b0d14ed3a Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 12 Jun 2024 12:43:17 +0300 Subject: [PATCH] wip: prototype chains/stacks --- src/device/sequencer.rs | 191 ++++++++++++++++++++++++++++------- src/device/sequencer/midi.rs | 52 ++++++++++ src/main.rs | 62 +++++++----- 3 files changed, 239 insertions(+), 66 deletions(-) create mode 100644 src/device/sequencer/midi.rs diff --git a/src/device/sequencer.rs b/src/device/sequencer.rs index 1315b267..8f77f6eb 100644 --- a/src/device/sequencer.rs +++ b/src/device/sequencer.rs @@ -1,6 +1,9 @@ use crate::prelude::*; use ratatui::style::Stylize; +mod midi; +pub use midi::*; + pub const ACTIONS: [(&'static str, &'static str);4] = [ ("+/-", "Zoom"), ("A/D", "Add/delete note"), @@ -14,10 +17,13 @@ pub struct Sequencer { playing: Arc, recording: Arc, overdub: Arc, - sequence: Arc>>>>, + inputs_open: Arc, + outputs_open: Arc, cursor: (u16, u16, u16), timesig: (f32, f32), pub jack_client: Jack, + sequence: Arc>>>>, + sequences: Arc>>, } #[derive(Clone)] @@ -76,6 +82,10 @@ impl Sequencer { recording: recording.clone(), overdub: overdub.clone(), sequence: sequence.clone(), + inputs_open: Arc::new(AtomicBool::new(false)), + outputs_open: Arc::new(AtomicBool::new(false)), + sequences: Arc::new(Mutex::new(vec![ + ])), cursor: (11, 0, 0), timesig: (4.0, 4.0), jack_client: crate::engine::activate_jack_client( @@ -194,6 +204,12 @@ fn handle (state: &mut Sequencer, event: &crate::engine::Event) -> Result<(), Bo 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; @@ -213,10 +229,10 @@ fn handle (state: &mut Sequencer, event: &crate::engine::Event) -> Result<(), Bo } const NOTE_NAMES: [&'static str;12] = [ - "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B ", + "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", ]; -const KEYS: [&'static str; 6] = [ +const KEYS_VERTICAL: [&'static str; 6] = [ "▀", "▀", "▀", "█", "▄", "▄", ]; @@ -227,42 +243,138 @@ impl WidgetRef for Sequencer { } fn draw_sequencer (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect) { - area.height = 15; - draw_box(buf, area); + area.height = 18; + //draw_box(buf, area); let Rect { x, y, width, height } = area; + //{ + //let mut area = area.clone(); + //area.y = area.y + 3; + //area.x = area.x + 1; + //area.height = area.height - 2; + //draw_sequence_keys(area, buf, + //&sequencer.jack_client.as_client().transport().query().unwrap(), + //&sequencer.sequence, + //&sequencer.cursor); + //draw_sequence_cursor(area, buf, + //&sequencer.cursor); + //} + //draw_box(buf, Rect { x, y, width: 18, height }); + + draw_box(buf, Rect { x, y, width: 40, height: 8 }); + draw_box(buf, Rect { x, y, width: 40, height: 6 }); + 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.bold()); + buf.set_string(x + 1, y + 2, &format!(" ▶ PLAY │ ⏹ STOP │ ⏺ REC │ ⏺ DUB "), style); + buf.set_string(x + 0, y + 3, &format!("├--------------------------------------┤"), style.dim()); + buf.set_string(x + 1, y + 6, &format!(" Inputs... │ Outputs... "), style); + buf.set_string(x + 0, y + 5, &format!("├--------------------------------------┤"), style.dim()); + for i in 0..3 { + buf.set_string(1 + i * 12 + x + 1, y + 4, &format!(" "), Style::default().on_white()); + buf.set_string(1 + i * 12 + x + 2, y + 4, &format!(" "), Style::default().on_black()); + buf.set_string(1 + i * 12 + x + 3, y + 4, &format!(" "), Style::default().on_white()); + buf.set_string(1 + i * 12 + x + 4, y + 4, &format!(" "), Style::default().on_black()); + buf.set_string(1 + i * 12 + x + 5, y + 4, &format!(" "), Style::default().on_white()); + buf.set_string(1 + i * 12 + x + 6, y + 4, &format!(" "), Style::default().on_white()); + buf.set_string(1 + i * 12 + x + 7, y + 4, &format!(" "), Style::default().on_black()); + buf.set_string(1 + i * 12 + x + 8, y + 4, &format!(" "), Style::default().on_white()); + buf.set_string(1 + i * 12 + x + 9, y + 4, &format!(" "), Style::default().on_black()); + buf.set_string(1 + i * 12 + x + 10, y + 4, &format!(" "), Style::default().on_white()); + buf.set_string(1 + i * 12 + x + 11, y + 4, &format!(" "), Style::default().on_black()); + buf.set_string(1 + i * 12 + x + 12, y + 4, &format!(" "), Style::default().on_white()); + } + //buf.set_string(x + 1, y + 3, + //&format!(" │ INPUT │ OUTPUT │"), + //Style::default().gray()); + //buf.set_string(x + 1, y + 5, + //&format!(" {} │ IN │ ⏺ REC │ ⏺ DUB │ ▶ PLAY │ ⏹ STOP │ OUT │ 00:00.00 / 00:00.00", &sequencer.name), + //Style::default().gray()); + if sequencer.inputs_open.fetch_and(true, Ordering::Relaxed) { + buf.set_string(x + 15, y + 1, "IN", Style::default().green().bold()); + } + if sequencer.outputs_open.fetch_and(true, Ordering::Relaxed) { + buf.set_string(x + 54, y + 1, "OUT", Style::default().green().bold()); + } + let mut command = |y2: u16, c: &str, ommand: &str, value: &str| { buf.set_string(x + 1, y + y2, c, Style::default().bold()); buf.set_string(x + 2, y + y2, ommand, Style::default().dim()); buf.set_string(x + 4 + ommand.len() as u16, y + y2, value, Style::default().bold()); }; - for (y, c, ommand, value) in [ - (5, "I", "nputs", "[+]"), - (6, "O", "utputs", "[+]"), - (7, "C", "hannel", "01"), - (8, "G", "rid", "1/16"), - (9, "Z", "oom", "1/64"), - (10, "R", "ate", "1/1"), - (11, "S", "ync", "1 bar"), - (12, "A", "dd note", ""), - (13, "D", "elete note", ""), - ] { - command(y, c, ommand, value) - } - { - let mut area = area.clone(); - //area.y = area.y + 1; - area.x = area.x + 19; - area.height = area.height - 2; - draw_sequence_keys(area, buf, - &sequencer.jack_client.as_client().transport().query().unwrap(), - &sequencer.sequence, - &sequencer.cursor); - draw_sequence_cursor(area, buf, - &sequencer.cursor); - } - draw_box(buf, Rect { x, y, width: 18, height }); - draw_rec_dub_button(buf, x, y + 2); - draw_sequence_button(buf, x, y, &sequencer.name); + //for (y, c, ommand, value) in [ + ////(5, "I", "nputs", "[+]"), + ////(6, "O", "utputs", "[+]"), + ////(7, "C", "hannel", "01"), + //(8, "G", "rid", "1/16"), + //(9, "Z", "oom", "1/64"), + //(10, "R", "ate", "1/1"), + //(11, "S", "ync", "1 bar"), + //(12, "A", "dd note", ""), + //(13, "D", "elete note", ""), + //] { + //command(y, c, ommand, value) + //} + //draw_box(buf, Rect { x, y: y + height - 1, width, height: 3 }); + //buf.set_string(x + 1, y + height, + //&format!(" Grid 1/16 │ Zoom 1/64 │ Rate 1/1 │ Sync 1 bar │ Add note │ Delete note "), + //Style::default().gray()); + //buf.set_string(x + 1, y + 1, &format!(" ▶ {} ", &sequencer.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()); + //buf.set_string(x + 21, y + 1, " ⏺ REC DUB ", + //Style::default().white().bold().not_dim()); + //buf.set_string(x + 18, y + 0, "┬", Style::default().gray()); + //buf.set_string(x + 18, y + 1, "│", Style::default().gray().dim()); + //buf.set_string(x + 18, y + 2, "┴", Style::default().gray()); + + //draw_rec_dub_button(buf, x + 17, y); + //draw_sequence_button(buf, x, y, &sequencer.name); + + //draw_leaf(buf, Rect { x, y: y + height, width, height: 2 }, 0, 0, "Inputs "); + //draw_leaf(buf, Rect { x, y: y + height, width, height: 2 }, 0, 8, "Outputs"); + //for x2 in 0..4 { + //for y2 in 0..4 { + //buf.set_string( + //x + 2 + x2 * 3, + //y + 5 + y2, + //format!("{:>02} ", 1 + x2 + y2 * 4), + //Style::default().green().bold().not_dim() + //) + //} + //} + // + let y = y + 8; + draw_box(buf, Rect { x, y: y, width: 40, height: 10 }); + buf.set_string(x + 1, y + 1, &format!(" LV2Host#00"), style.bold()); + buf.set_string(x + 13, y + 1, &format!("│ Plugin Name"), style.not_dim()); + buf.set_string(x + 0, y + 2, &format!("├--------------------------------------┤"), style.dim()); + buf.set_string(x + 1, y + 3, &format!(" Parameter 1 0.0"), style); + buf.set_string(x + 1, y + 4, &format!(" Parameter 2 0.0"), style); + buf.set_string(x + 1, y + 5, &format!(" Parameter 3 0.0"), style); + buf.set_string(x + 1, y + 6, &format!(" Parameter 4 0.0"), style); + buf.set_string(x + 0, y + 7, &format!("├--------------------------------------┤"), style.dim()); + buf.set_string(x + 1, y + 8, &format!(" Inputs... │ Outputs... "), style); + + let y = y + 10; + draw_box(buf, Rect { x, y: y, width: 40, height: 14 }); + buf.set_string(x + 1, y + 1, &format!(" Sampler#00 │"), style.bold()); + buf.set_string(x + 0, y + 2, &format!("├--------------------------------------┤"), style.dim()); + buf.set_string(x + 2, y + 3, &format!("C0 Sample#000"), style.bold()); + buf.set_string(x + 2, y + 4, &format!(" {:.03}s", + 100000.0/44100.0), style); + buf.set_string(x + 2, y + 5, &format!("C#0 Sample#001"), style.bold()); + buf.set_string(x + 2, y + 6, &format!(" {:.03}-{:.03}s", + 50000.0/44100.0, 100000.0/44100.0), style); + buf.set_string(x + 2, y + 7, &format!("D0 Sample#002"), style.bold()); + buf.set_string(x + 2, y + 8, &format!(" {:.03}-{:.03}/{:.03}s", + 0.0, 50000.0/44100.0, 100000.0/44100.0), style); + buf.set_string(x + 2, y + 9, &format!("D#0 Sample#003"), style.bold()); + buf.set_string(x + 2, y + 10, &format!(" {:.03}-[{:.03}-{:.03}]/{:.03}s ", + 10000.0/44100.0, 25000.0/44100.0, 50000.0/44100.0, 100000.0/44100.0), style); + buf.set_string(x + 0, y + 7 + 4, &format!("├--------------------------------------┤"), style.dim()); + buf.set_string(x + 1, y + 7 + 5, &format!(" Inputs... │ Outputs... "), style); } fn draw_sequence_button ( @@ -285,11 +397,14 @@ fn draw_rec_dub_button ( y: u16, ) { draw_box(buf, Rect { x, y, width: 18, height: 3 }); - buf.set_string(x + 1, y + 1, " ⏺ REC DUB ", + buf.set_string(x + 1, y + 1, " ⏺ REC DUB ", 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()); + 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()); } fn draw_sequence_keys ( @@ -324,7 +439,7 @@ fn draw_sequence_keys ( //); let sequence = sequence.lock().unwrap(); for key in 0..12 { - buf.set_string(area.x, area.y + 1 + key, KEYS[(key % 6) as usize], + 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()); diff --git a/src/device/sequencer/midi.rs b/src/device/sequencer/midi.rs new file mode 100644 index 00000000..58b44b7a --- /dev/null +++ b/src/device/sequencer/midi.rs @@ -0,0 +1,52 @@ +pub struct MIDISequence { + channels: [MIDISequenceChannel;16], +} + +pub struct MIDISequenceChannel { + number: u8, + notes: [MIDISequenceVoice;128], +} + +pub type MIDISequenceVoice = std::collections::BTreeMap; + +#[derive(Clone)] +pub enum NoteEvent { + On(u8), + Off(u8), +} + +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), + ] + } + } +} + +impl MIDISequenceChannel { + fn new (number: u8) -> Self { + Self { + number, + notes: [VOICE_EMPTY;128] + } + } +} diff --git a/src/main.rs b/src/main.rs index c38c1276..1ec717ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,9 +48,6 @@ fn main () -> Result<(), Box> { )?, focus: 0, devices: vec![ - crate::device::Device::Mixer( - crate::device::mixer::Mixer::new()? - ), crate::device::Device::Sequencer( crate::device::sequencer::Sequencer::new( Some("Melody#000"), @@ -58,19 +55,22 @@ fn main () -> Result<(), Box> { None )? ), - crate::device::Device::Sequencer( - crate::device::sequencer::Sequencer::new( - Some("Rhythm#000"), - None, - None, - )? - ), - crate::device::Device::Sampler( - crate::device::sampler::Sampler::new()? - ), - crate::device::Device::Looper( - crate::device::looper::Looper::new()? - ), + //crate::device::Device::Sequencer( + //crate::device::sequencer::Sequencer::new( + //Some("Rhythm#000"), + //None, + //None, + //)? + //), + //crate::device::Device::Mixer( + //crate::device::mixer::Mixer::new()? + //), + //crate::device::Device::Sampler( + //crate::device::sampler::Sampler::new()? + //), + //crate::device::Device::Looper( + //crate::device::looper::Looper::new()? + //), ] }) } @@ -106,24 +106,30 @@ impl WidgetRef for App { fn render_ref (&self, area: Rect, buffer: &mut Buffer) { use ratatui::style::Stylize; let mut constraints = vec![ - Constraint::Length(4), - Constraint::Length(10), - Constraint::Max(18), - Constraint::Max(18), - Constraint::Max(0), - Constraint::Max(0), - Constraint::Max(0), + Constraint::Max(40), + Constraint::Max(40), ]; let areas = Layout::default() - .direction(Direction::Vertical) + .direction(Direction::Horizontal) .constraints(&constraints) - .split(area); + .split(Rect { + x: area.x, + y: area.y, + width: area.width, + height: area.height - 4 + }); + + self.transport.render(Rect { + x: area.width.saturating_sub(80u16) / 2, + y: area.y + area.height - 4, + width: area.width, + height: 4 + }, buffer); - self.transport.render(areas[0], buffer); for (index, device) in self.devices.iter().enumerate() { - device.render(areas[index + 1], buffer); + device.render(areas[index], buffer); if index == self.focus { - draw_focus_corners(buffer, areas[index+1]); + draw_focus_corners(buffer, areas[index]); } } }