systematizing jack handlers

This commit is contained in:
🪞👃🪞 2024-06-14 16:54:28 +03:00
parent d627d257ad
commit 4ae62c5bc2
10 changed files with 342 additions and 208 deletions

View file

@ -3,6 +3,7 @@ use ratatui::style::Stylize;
pub struct Sequencer {
name: String,
jack: Jack<SequencerJack>,
playing: Arc<AtomicBool>,
recording: Arc<AtomicBool>,
overdub: Arc<AtomicBool>,
@ -10,9 +11,16 @@ pub struct Sequencer {
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>>>,
mode: SequencerMode,
}
pub enum SequencerMode {
Tiny,
Compact,
Vertical,
Horizontal,
}
impl Sequencer {
@ -31,15 +39,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 (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 sequence: Arc<Mutex<Vec<Vec<Option<SequencerEvent>>>>> = Arc::new(
Mutex::new(vec![vec![None;64];128])
);
@ -53,7 +52,17 @@ impl Sequencer {
for (index, frame) in step_frames.iter().enumerate() {
frame_steps[*frame] = Some(index);
}
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()
)?;
Ok(DynamicDevice::new(render, handle, |_|{}, Self {
mode: SequencerMode::Vertical,
name: name.into(),
playing: playing.clone(),
recording: recording.clone(),
@ -65,67 +74,70 @@ impl Sequencer {
])),
cursor: (11, 0, 0),
timesig: (4.0, 4.0),
jack_client: crate::device::activate_jack_client(
client,
Notifications,
Box::new(move |client: &Client, scope: &ProcessScope| -> Control {
if exited.fetch_and(true, Ordering::Relaxed) {
return Control::Quit
}
if client.transport().query_state().unwrap() == TransportState::Rolling {
let chunk_start = scope.last_frame_time();
let chunk_size = scope.n_frames();
let chunk_end = chunk_start + chunk_size;
let start_looped = chunk_start as usize % loop_frames;
let end_looped = chunk_end as usize % loop_frames;
// Write MIDI notes from input to sequence
if recording.fetch_and(true, Ordering::Relaxed) {
//let overdub = overdub.fetch_and(true, Ordering::Relaxed);
for event in input.iter(scope) {
let ::jack::RawMidi { time, bytes } = event;
println!("\n{time} {bytes:?}");
}
}
// Write MIDI notes from sequence to output
if playing.fetch_and(true, Ordering::Relaxed) {
let mut writer = output.writer(scope);
let sequence = sequence.lock().unwrap();
for frame in 0..chunk_size {
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()
}
}
}
}
}
}
Control::Continue
})
)?
jack: SequencerJack.activate(client, ClosureProcessHandler::new(
Box::new(move |client: &Client, scope: &ProcessScope| process(client, scope)) as BoxedProcessHandler
))?
}))
}
}
pub fn process (
client: &Client,
scope: &ProcessScope
) -> Control {
//if exited.fetch_and(true, Ordering::Relaxed) {
//return Control::Quit
//}
//if client.transport().query_state().unwrap() == TransportState::Rolling {
//let chunk_start = scope.last_frame_time();
//let chunk_size = scope.n_frames();
//let chunk_end = chunk_start + chunk_size;
//let start_looped = chunk_start as usize % loop_frames;
//let end_looped = chunk_end as usize % loop_frames;
//// Write MIDI notes from input to sequence
//if recording.fetch_and(true, Ordering::Relaxed) {
////let overdub = overdub.fetch_and(true, Ordering::Relaxed);
//for event in input.iter(scope) {
//let ::jack::RawMidi { time, bytes } = event;
//println!("\n{time} {bytes:?}");
//}
//}
//// Write MIDI notes from sequence to output
//if playing.fetch_and(true, Ordering::Relaxed) {
//let mut writer = output.writer(scope);
//let sequence = sequence.lock().unwrap();
//for frame in 0..chunk_size {
//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()
//}
//}
//}
//}
//}
//}
Control::Continue
}
pub const ACTIONS: [(&'static str, &'static str);4] = [
("+/-", "Zoom"),
("A/D", "Add/delete note"),
@ -135,6 +147,9 @@ pub const ACTIONS: [(&'static str, &'static str);4] = [
pub fn handle (state: &mut Sequencer, event: &EngineEvent) -> Result<(), Box<dyn Error>> {
if let EngineEvent::Focus = event {
}
if let EngineEvent::Input(Event::Key(event)) = event {
match event.code {
KeyCode::Down => {
@ -197,33 +212,38 @@ pub fn handle (state: &mut Sequencer, event: &EngineEvent) -> Result<(), Box<dyn
Ok(())
}
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 render (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect)
-> Usually<Rect>
{
let style = Style::default().gray();
let header = draw_sequencer_header(sequencer, buf, area)?;
let piano = draw_sequencer_vertical(sequencer, buf, Rect {
x: area.x,
y: area.y + header.height,
width: area.width,
height: area.height - header.height
})?;
let area = Rect {
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
})?,
_ => 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
};
//draw_box_styled(buf, area, Some(Style::default().red()));
Ok(area)
})
}
fn draw_sequencer_header (sequencer: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
@ -235,32 +255,52 @@ fn draw_sequencer_header (sequencer: &Sequencer, buf: &mut Buffer, area: Rect) -
Ok(Rect { x, y, width, height: 4 })
}
const KEY_WHITE: Style = Style {
fg: Some(Color::Gray),
bg: None,
underline_color: None,
add_modifier: ::ratatui::style::Modifier::empty(),
sub_modifier: ::ratatui::style::Modifier::empty(),
};
const KEY_BLACK: Style = Style {
fg: Some(Color::Black),
bg: None,
underline_color: None,
add_modifier: ::ratatui::style::Modifier::empty(),
sub_modifier: ::ratatui::style::Modifier::empty(),
};
const KEY_HORIZONTAL_STYLE: [Style;12] = [
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
];
fn draw_sequencer_vertical (sequencer: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, .. } = area;
let white = Style::default().gray();
let black = Style::default().black();
for i in 0..area.width-4 {
let color = match i % 12 {
0 => white,
1 => black,
2 => white,
3 => black,
4 => white,
5 => white,
6 => black,
7 => white,
8 => black,
9 => white,
10 => black,
11 => white,
_ => unreachable!()
};
buf.set_string(x + 2 + i, y - 1, &format!(""), color);
buf.set_string(x + 2 + i, y, &format!(""), color);
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 bg = Style::default().on_black();
for i in 0..area.height - 8 {
buf.set_string(x + 2, y + 1 + i, &" ".repeat((area.width-4)as usize), bg);
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 octave in -2i16..=8i16 {
let x = x + 5 + (24i16 + octave * 12) as u16;
if x >= area.x + area.width - 6 {
break
}
buf.set_string(
x,
y + 1,
&format!("C{octave}"),
Style::default()
);
}
Ok(Rect { x, y, width: area.width, height: area.height - 6 })
}
@ -373,6 +413,14 @@ fn draw_rec_dub_button (
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,
@ -470,9 +518,17 @@ fn draw_sequence_cursor (
buf.set_string(area.x + cursor.1 + 3, area.y + 1 + cursor_y, cursor_text, style);
}
pub struct Notifications;
pub struct SequencerJack;
impl NotificationHandler for Notifications {
impl SequencerJack {
fn activate (self, client: Client, handler: ClosureProcessHandler<BoxedProcessHandler>)
-> Usually<AsyncClient<Self, ClosureProcessHandler<BoxedProcessHandler>>>
{
Ok(client.activate_async(self, handler)?)
}
}
impl NotificationHandler for SequencerJack {
fn thread_init (&self, _: &Client) {
}