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,
/// Width of phrase pool
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
#[derive(Copy, Clone, PartialEq, Eq)]
@ -121,9 +123,9 @@ impl<E: Engine> Arranger<E> {
phrases: Arc<RwLock<PhrasePool<E>>>,
) -> Self {
let mut app = Self {
jack: None,
focus_cursor: (0, 1),
phrases_split: 20,
jack: None,
focus_cursor: (0, 1),
phrases_split: 20,
arrangement_split: 21,
editor: PhraseEditor::new(),
status: ArrangerStatusBar::ArrangementClip,
@ -135,6 +137,7 @@ impl<E: Engine> Arranger<E> {
transport,
arrangement,
phrases,
size: Measure::new(),
};
app.update_focus();
app

View file

@ -118,7 +118,7 @@ impl Content for Arrangement<Tui> {
ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)),
}?;
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)
})
}
@ -137,7 +137,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let border_hi = Color::Rgb(100, 110, 40);
let border_lo = Color::Rgb(70, 80, 50);
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 tracks_footer = 3u16;
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 - 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
pub struct PhraseLength<E: Engine> {

View file

@ -10,100 +10,137 @@ impl<E: Engine> Audio for Sequencer<E> {
}
impl Audio for PhrasePlayer {
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_inputs = self.has_midi_inputs();
if has_midi_outputs {
self.clear_midi_out_buf(frames);
self.reset_midi_out_buf(false /* FIXME where did force-reset come from? */);
}
let has_midi_inputs = self.has_midi_inputs();
let quant = self.clock.quant().get();
if let Some((start_frame, phrase)) = self.playing_phrase() {
// Clear output buffer(s)
self.clear(scope, false);
// Write chunk of phrase to output
phrase.read().map(|phrase|{
if has_midi_outputs {
let output = &mut self.midi_out_buf;
let notes_on = &mut self.notes_out.write().unwrap();
let frame0 = frame0.saturating_sub(start_frame);
let frame_n = frame0 + frames;
let ticks = self.clock.timebase().pulses_between_samples(frame0, frame_n);
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();
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);
}
}
}
self.play(scope);
}
if has_midi_inputs {
if self.recording || self.monitoring {
// Record and/or monitor input
self.record(scope)
} else if has_midi_outputs && self.monitoring {
// Monitor input to output
self.monitor(scope)
}
} 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();
// Monitor each input
// Record from each input
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 {
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);
}
}
}
}
// 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() {
let writer = &mut port.writer(scope);
let output = &self.midi_out_buf;
for time in 0..frames {
for time in 0..samples {
for event in output[time].iter() {
writer.write(&RawMidi { time: time as u32, bytes: &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.