refactor: device abstraction, layout components

This commit is contained in:
🪞👃🪞 2024-06-12 16:55:57 +03:00
parent d330d31ce4
commit 788dc1ccde
21 changed files with 1144 additions and 1166 deletions

View file

@ -1,44 +1,23 @@
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"),
("]/[", "Duration"),
("CapsLock", "Auto advance"),
];
pub struct Sequencer {
name: Arc<str>,
exited: Arc<AtomicBool>,
playing: Arc<AtomicBool>,
recording: Arc<AtomicBool>,
overdub: Arc<AtomicBool>,
inputs_open: Arc<AtomicBool>,
outputs_open: Arc<AtomicBool>,
cursor: (u16, u16, u16),
timesig: (f32, f32),
pub jack_client: Jack<Notifications>,
sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>>,
sequences: Arc<Mutex<Vec<MIDISequence>>>,
}
#[derive(Clone)]
pub enum Event {
NoteOn(u8, u8),
NoteOff(u8)
name: String,
playing: Arc<AtomicBool>,
recording: Arc<AtomicBool>,
overdub: Arc<AtomicBool>,
inputs_open: Arc<AtomicBool>,
outputs_open: Arc<AtomicBool>,
cursor: (u16, u16, u16),
timesig: (f32, f32),
pub jack_client: Jack<self::Notifications>,
sequence: Arc<Mutex<Vec<Vec<Option<SequencerEvent>>>>>,
sequences: Arc<Mutex<Vec<MIDISequence>>>,
}
impl Sequencer {
pub fn new (
name: Option<&str>,
connect_inputs: Option<&[String]>,
connect_outputs: Option<&[String]>,
) -> Result<Self, Box<dyn Error>> {
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
let beats = 4;
let steps = 16;
let bpm = 120.0;
@ -52,7 +31,6 @@ impl Sequencer {
let playing = Arc::new(AtomicBool::new(true));
let recording = Arc::new(AtomicBool::new(true));
let overdub = Arc::new(AtomicBool::new(false));
let name = name.unwrap_or("sequencer");
let (client, _status) = Client::new(
name, ClientOptions::NO_START_SERVER
)?;
@ -62,7 +40,7 @@ impl Sequencer {
let mut output = client.register_port(
"output", ::jack::MidiOut::default()
)?;
let sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>> = Arc::new(
let sequence: Arc<Mutex<Vec<Vec<Option<SequencerEvent>>>>> = Arc::new(
Mutex::new(vec![vec![None;64];128])
);
let mut step_frames = vec![];
@ -75,9 +53,8 @@ impl Sequencer {
for (index, frame) in step_frames.iter().enumerate() {
frame_steps[*frame] = Some(index);
}
Ok(Self {
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
name: name.into(),
exited: exited.clone(),
playing: playing.clone(),
recording: recording.clone(),
overdub: overdub.clone(),
@ -88,7 +65,7 @@ impl Sequencer {
])),
cursor: (11, 0, 0),
timesig: (4.0, 4.0),
jack_client: crate::engine::activate_jack_client(
jack_client: crate::device::activate_jack_client(
client,
Notifications,
Box::new(move |client: &Client, scope: &ProcessScope| -> Control {
@ -123,12 +100,12 @@ impl Sequencer {
writer.write(&::jack::RawMidi {
time: frame as u32,
bytes: &match event {
Event::NoteOn(pitch, velocity) => [
SequencerEvent::NoteOn(pitch, velocity) => [
0b10010000,
*pitch,
*velocity
],
Event::NoteOff(pitch) => [
SequencerEvent::NoteOff(pitch) => [
0b10000000,
*pitch,
0b00000000
@ -144,29 +121,21 @@ impl Sequencer {
Control::Continue
})
)?
})
}))
}
}
impl Exitable for Sequencer {
fn exit (&mut self) {
self.exited.store(true, Ordering::Relaxed)
}
fn exited (&self) -> bool {
self.exited.fetch_and(true, Ordering::Relaxed)
}
}
pub const ACTIONS: [(&'static str, &'static str);4] = [
("+/-", "Zoom"),
("A/D", "Add/delete note"),
("]/[", "Duration"),
("CapsLock", "Auto advance"),
];
impl HandleInput for Sequencer {
fn handle (&mut self, event: &crate::engine::Event) -> Result<(), Box<dyn Error>> {
handle(self, event)
}
}
pub fn handle (state: &mut Sequencer, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
fn handle (state: &mut Sequencer, event: &crate::engine::Event) -> Result<(), Box<dyn Error>> {
if let crate::engine::Event::Input(crossterm::event::Event::Key(event)) = event {
if let EngineEvent::Input(Event::Key(event)) = event {
match event.code {
KeyCode::Down => {
state.cursor.0 = if state.cursor.0 >= 23 {
@ -215,9 +184,9 @@ fn handle (state: &mut Sequencer, event: &crate::engine::Event) -> Result<(), Bo
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(self::Event::NoteOn(48 - row as u8, 128));
sequence[row][step] = Some(SequencerEvent::NoteOn(48 - row as u8, 128));
if state.cursor.2 > 0 {
sequence[row][step + duration] = Some(self::Event::NoteOff(35));
sequence[row][step + duration] = Some(SequencerEvent::NoteOff(35));
}
},
_ => {
@ -236,13 +205,7 @@ const KEYS_VERTICAL: [&'static str; 6] = [
"", "", "", "", "", "",
];
impl WidgetRef for Sequencer {
fn render_ref (&self, mut area: Rect, buf: &mut Buffer) {
draw_sequencer(self, buf, area)
}
}
fn draw_sequencer (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect) {
fn render (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect) {
area.height = 18;
//draw_box(buf, area);
let Rect { x, y, width, height } = area;
@ -289,12 +252,12 @@ fn draw_sequencer (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect) {
//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());
}
//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());
@ -542,6 +505,65 @@ impl NotificationHandler for Notifications {
}
}
pub struct MIDISequence {
channels: [MIDISequenceChannel;16],
}
pub struct MIDISequenceChannel {
number: u8,
notes: [MIDISequenceVoice;128],
}
pub type MIDISequenceVoice = std::collections::BTreeMap<u32, NoteEvent>;
#[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]
}
}
}
#[derive(Clone)]
pub enum SequencerEvent {
NoteOn(u8, u8),
NoteOff(u8)
}
#[cfg(test)]
mod test {
#[test]