mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
refactor: device abstraction, layout components
This commit is contained in:
parent
d330d31ce4
commit
788dc1ccde
21 changed files with 1144 additions and 1166 deletions
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue