refactor PhrasePlayer::process callback

This commit is contained in:
🪞👃🪞 2024-11-01 20:06:42 +02:00
parent 0820c10f8b
commit 3df8e87840
4 changed files with 118 additions and 87 deletions

View file

@ -22,6 +22,8 @@ pub struct Arranger<E: Engine> {
pub arrangement_split: u16, pub arrangement_split: u16,
/// Width of phrase pool /// Width of phrase pool
pub phrases_split: u16, pub phrases_split: u16,
/// Width and height of app at last render
pub size: Measure<E>,
} }
/// Sections in the arranger app that may be focused /// Sections in the arranger app that may be focused
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
@ -121,9 +123,9 @@ impl<E: Engine> Arranger<E> {
phrases: Arc<RwLock<PhrasePool<E>>>, phrases: Arc<RwLock<PhrasePool<E>>>,
) -> Self { ) -> Self {
let mut app = Self { let mut app = Self {
jack: None, jack: None,
focus_cursor: (0, 1), focus_cursor: (0, 1),
phrases_split: 20, phrases_split: 20,
arrangement_split: 21, arrangement_split: 21,
editor: PhraseEditor::new(), editor: PhraseEditor::new(),
status: ArrangerStatusBar::ArrangementClip, status: ArrangerStatusBar::ArrangementClip,
@ -135,6 +137,7 @@ impl<E: Engine> Arranger<E> {
transport, transport,
arrangement, arrangement,
phrases, phrases,
size: Measure::new(),
}; };
app.update_focus(); app.update_focus();
app app

View file

@ -118,7 +118,7 @@ impl Content for Arrangement<Tui> {
ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)), ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)),
}?; }?;
let color = if self.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; let color = if self.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
add(&TuiStyle::fg("Session", color).push_x(1))?; add(&TuiStyle::fg("Session", color).push_x(2))?;
add(&self.size) add(&self.size)
}) })
} }
@ -137,7 +137,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let border_hi = Color::Rgb(100, 110, 40); let border_hi = Color::Rgb(100, 110, 40);
let border_lo = Color::Rgb(70, 80, 50); let border_lo = Color::Rgb(70, 80, 50);
let border_fg = if self.0.focused { border_hi } else { border_lo }; let border_fg = if self.0.focused { border_hi } else { border_lo };
let border = Lozenge(Style::default().bg(border_bg).fg(border_fg)); //let border = Lozenge(Style::default().bg(border_bg).fg(border_fg));
let track_title_h = 2u16; let track_title_h = 2u16;
let tracks_footer = 3u16; let tracks_footer = 3u16;
let scene_title_w = 3 + Scene::longest_name(scenes) as u16; // x of 1st track let scene_title_w = 3 + Scene::longest_name(scenes) as u16; // x of 1st track

View file

@ -403,15 +403,6 @@ impl PhrasePlayer {
.map(|(started,_)|started.sample().get()) .map(|(started,_)|started.sample().get())
.map(|started|(started - self.clock.instant.sample().get()) as usize) .map(|started|(started - self.clock.instant.sample().get()) as usize)
} }
pub fn playing_phrase (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> {
if let (
Some(TransportState::Rolling), Some((started, Some(ref phrase)))
) = (*self.clock.playing.read().unwrap(), &self.phrase) {
Some((started.sample().get() as usize, phrase.clone()))
} else {
None
}
}
} }
/// Displays and edits phrase length /// Displays and edits phrase length
pub struct PhraseLength<E: Engine> { pub struct PhraseLength<E: Engine> {

View file

@ -10,100 +10,137 @@ impl<E: Engine> Audio for Sequencer<E> {
} }
impl Audio for PhrasePlayer { impl Audio for PhrasePlayer {
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
let frame0 = scope.last_frame_time() as usize;
let frames = scope.n_frames() as usize;
let has_midi_outputs = self.has_midi_outputs(); let has_midi_outputs = self.has_midi_outputs();
let has_midi_inputs = self.has_midi_inputs();
if has_midi_outputs { if has_midi_outputs {
self.clear_midi_out_buf(frames); // Clear output buffer(s)
self.reset_midi_out_buf(false /* FIXME where did force-reset come from? */); self.clear(scope, false);
}
let has_midi_inputs = self.has_midi_inputs();
let quant = self.clock.quant().get();
if let Some((start_frame, phrase)) = self.playing_phrase() {
// Write chunk of phrase to output // Write chunk of phrase to output
phrase.read().map(|phrase|{ self.play(scope);
if has_midi_outputs { }
let output = &mut self.midi_out_buf; if has_midi_inputs {
let notes_on = &mut self.notes_out.write().unwrap(); if self.recording || self.monitoring {
let frame0 = frame0.saturating_sub(start_frame); // Record and/or monitor input
let frame_n = frame0 + frames; self.record(scope)
let ticks = self.clock.timebase().pulses_between_samples(frame0, frame_n); } else if has_midi_outputs && self.monitoring {
let mut buf = Vec::with_capacity(8); // Monitor input to output
for (sample, tick) in ticks { self.monitor(scope)
let tick = tick % phrase.length;
for message in phrase.notes[tick].iter() {
buf.clear();
let channel = 0.into();
let message = *message;
LiveEvent::Midi { channel, message }.write(&mut buf).unwrap();
output[sample].push(buf.clone());
update_keys(notes_on, &message);
}
}
}
}).unwrap();
let mut phrase = phrase.write().unwrap();
let length = phrase.length;
// Monitor and record input
if has_midi_inputs && (self.recording || self.monitoring) {
// For highlighting keys and note repeat
let mut notes_in = self.notes_in.write().unwrap();
// Record from each input
for input in self.midi_inputs.iter() {
for (frame, event, bytes) in parse_midi_input(input.iter(scope)) {
if let LiveEvent::Midi { message, .. } = event {
if self.monitoring { self.midi_out_buf[frame].push(bytes.to_vec()) }
if self.recording {
phrase.record_event({
let pulse = self.clock.timebase().samples_to_pulse(
(frame0 + frame - start_frame) as f64
);
let quantized = (pulse / quant).round() * quant;
let looped = quantized as usize % length;
looped
}, message);
}
update_keys(&mut notes_in, &message);
}
}
}
} }
} else if has_midi_inputs && has_midi_outputs && self.monitoring { }
// Write to output port(s)
self.write(scope);
Control::Continue
}
}
impl PhrasePlayer {
fn has_midi_inputs (&self) -> bool { self.midi_inputs.len() > 0 }
fn has_midi_outputs (&self) -> bool { self.midi_outputs.len() > 0 }
/// Clear the section of the output buffer that we will be using,
/// emitting "all notes off" at start of buffer if requested.
fn clear (&mut self, scope: &ProcessScope, force_reset: bool) {
for frame in &mut self.midi_out_buf[0..scope.n_frames() as usize] {
frame.clear();
}
if self.reset || force_reset {
all_notes_off(&mut self.midi_out_buf); self.reset = false;
}
}
/// Return playing phrase with starting point
fn playing (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> {
if let (
Some(TransportState::Rolling), Some((started, Some(ref phrase)))
) = (*self.clock.playing.read().unwrap(), &self.phrase) {
Some((started.sample().get() as usize, phrase.clone()))
} else {
None
}
}
/// Return next phrase with starting point
fn enqueued (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> {
if let (
Some(TransportState::Rolling), Some((start_at, Some(ref phrase)))
) = (*self.clock.playing.read().unwrap(), &self.next_phrase) {
Some((start_at.sample().get() as usize, phrase.clone()))
} else {
None
}
}
fn play (&mut self, scope: &ProcessScope) {
let sample0 = scope.last_frame_time() as usize;
let samples = scope.n_frames() as usize;
if let Some((start, ref phrase)) = self.playing() {
let sample = sample0.saturating_sub(start);
let ticks = self.clock.timebase().pulses_between_samples(sample, sample + samples);
phrase.read().map(|phrase|{
let output = &mut self.midi_out_buf;
let notes_on = &mut self.notes_out.write().unwrap();
let mut buf = Vec::with_capacity(8);
for (sample, tick) in ticks {
let tick = tick % phrase.length;
for message in phrase.notes[tick].iter() {
buf.clear();
let channel = 0.into();
let message = *message;
LiveEvent::Midi { channel, message }.write(&mut buf).unwrap();
output[sample].push(buf.clone());
update_keys(notes_on, &message);
}
}
}).unwrap()
}
}
fn record (&mut self, scope: &ProcessScope) {
let sample0 = scope.last_frame_time() as usize;
if let Some((start, ref phrase)) = self.playing() {
let quant = self.clock.quant().get();
let mut phrase = phrase.write().unwrap();
let length = phrase.length;
// For highlighting keys and note repeat
let mut notes_in = self.notes_in.write().unwrap(); let mut notes_in = self.notes_in.write().unwrap();
// Monitor each input // Record from each input
for input in self.midi_inputs.iter() { for input in self.midi_inputs.iter() {
for (frame, event, bytes) in parse_midi_input(input.iter(scope)) { for (sample, event, bytes) in parse_midi_input(input.iter(scope)) {
if let LiveEvent::Midi { message, .. } = event { if let LiveEvent::Midi { message, .. } = event {
self.midi_out_buf[frame].push(bytes.to_vec()); if self.monitoring { self.midi_out_buf[sample].push(bytes.to_vec()) }
if self.recording {
phrase.record_event({
let pulse = self.clock.timebase().samples_to_pulse(
(sample0 + sample - start) as f64
);
let quantized = (pulse / quant).round() * quant;
let looped = quantized as usize % length;
looped
}, message);
}
update_keys(&mut notes_in, &message); update_keys(&mut notes_in, &message);
} }
} }
} }
} }
// Write to midi output }
fn monitor (&mut self, scope: &ProcessScope) {
let mut notes_in = self.notes_in.write().unwrap();
for input in self.midi_inputs.iter() {
for (sample, event, bytes) in parse_midi_input(input.iter(scope)) {
if let LiveEvent::Midi { message, .. } = event {
self.midi_out_buf[sample].push(bytes.to_vec());
update_keys(&mut notes_in, &message);
}
}
}
}
fn write (&mut self, scope: &ProcessScope) {
let samples = scope.n_frames() as usize;
for port in self.midi_outputs.iter_mut() { for port in self.midi_outputs.iter_mut() {
let writer = &mut port.writer(scope); let writer = &mut port.writer(scope);
let output = &self.midi_out_buf; let output = &self.midi_out_buf;
for time in 0..frames { for time in 0..samples {
for event in output[time].iter() { for event in output[time].iter() {
writer.write(&RawMidi { time: time as u32, bytes: &event }) writer.write(&RawMidi { time: time as u32, bytes: &event })
.expect(&format!("{event:?}")); .expect(&format!("{event:?}"));
} }
} }
} }
Control::Continue
}
}
impl PhrasePlayer {
pub fn has_midi_inputs (&self) -> bool { self.midi_inputs.len() > 0 }
pub fn has_midi_outputs (&self) -> bool { self.midi_outputs.len() > 0 }
/// Clear the section of the output buffer that we will be using
pub fn clear_midi_out_buf (&mut self, frames: usize) {
for frame in &mut self.midi_out_buf[0..frames] { frame.clear(); }
}
/// Emit "all notes off" at start of buffer if requested
pub fn reset_midi_out_buf (&mut self, force_reset: bool) {
if self.reset || force_reset { all_notes_off(&mut self.midi_out_buf); self.reset = false; }
} }
} }
/// Add "all notes off" to the start of a buffer. /// Add "all notes off" to the start of a buffer.