rename phrase -> clip mostly everywhere

This commit is contained in:
🪞👃🪞 2025-01-10 02:12:31 +01:00
parent 709391ff0a
commit 08f7a62692
24 changed files with 426 additions and 423 deletions

View file

@ -28,21 +28,21 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
}
}
/// Output notes from phrase to MIDI output ports.
/// Output notes from clip to MIDI output ports.
fn play (
&mut self, scope: &ProcessScope, note_buf: &mut Vec<u8>, out: &mut [Vec<Vec<u8>>]
) -> bool {
if !self.clock().is_rolling() {
return false
}
// If a phrase is playing, write a chunk of MIDI events from it to the output buffer.
// If no phrase is playing, prepare for switchover immediately.
self.play_phrase().as_ref().map_or(true, |(started, phrase)|{
self.play_chunk(scope, note_buf, out, started, phrase)
// If a clip is playing, write a chunk of MIDI events from it to the output buffer.
// If no clip is playing, prepare for switchover immediately.
self.play_clip().as_ref().map_or(true, |(started, clip)|{
self.play_chunk(scope, note_buf, out, started, clip)
})
}
/// Handle switchover from current to next playing phrase.
/// Handle switchover from current to next playing clip.
fn switchover (
&mut self, scope: &ProcessScope, note_buf: &mut Vec<u8>, out: &mut [Vec<Vec<u8>>]
) {
@ -51,21 +51,21 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
}
let sample0 = scope.last_frame_time() as usize;
//let samples = scope.n_frames() as usize;
if let Some((start_at, phrase)) = &self.next_phrase() {
if let Some((start_at, clip)) = &self.next_clip() {
let start = start_at.sample.get() as usize;
let sample = self.clock().started.read().unwrap()
.as_ref().unwrap().sample.get() as usize;
// If it's time to switch to the next phrase:
// If it's time to switch to the next clip:
if start <= sample0.saturating_sub(sample) {
// Samples elapsed since phrase was supposed to start
// Samples elapsed since clip was supposed to start
let _skipped = sample0 - start;
// Switch over to enqueued phrase
// Switch over to enqueued clip
let started = Moment::from_sample(self.clock().timebase(), start as f64);
// Launch enqueued phrase
*self.play_phrase_mut() = Some((started, phrase.clone()));
// Launch enqueued clip
*self.play_clip_mut() = Some((started, clip.clone()));
// Unset enqueuement (TODO: where to implement looping?)
*self.next_phrase_mut() = None;
// Fill in remaining ticks of chunk from next phrase.
*self.next_clip_mut() = None;
// Fill in remaining ticks of chunk from next clip.
self.play(scope, note_buf, out);
}
}
@ -77,52 +77,52 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts {
note_buf: &mut Vec<u8>,
out: &mut [Vec<Vec<u8>>],
started: &Moment,
phrase: &Option<Arc<RwLock<MidiClip>>>
clip: &Option<Arc<RwLock<MidiClip>>>
) -> bool {
// First sample to populate. Greater than 0 means that the first
// pulse of the phrase falls somewhere in the middle of the chunk.
// pulse of the clip falls somewhere in the middle of the chunk.
let sample = (scope.last_frame_time() as usize).saturating_sub(
started.sample.get() as usize +
self.clock().started.read().unwrap().as_ref().unwrap().sample.get() as usize
);
// Iterator that emits sample (index into output buffer at which to write MIDI event)
// paired with pulse (index into phrase from which to take the MIDI event) for each
// paired with pulse (index into clip from which to take the MIDI event) for each
// sample of the output buffer that corresponds to a MIDI pulse.
let pulses = self.clock().timebase().pulses_between_samples(sample, sample + scope.n_frames() as usize);
// Notes active during current chunk.
let notes = &mut self.notes_out().write().unwrap();
let length = phrase.as_ref().map_or(0, |p|p.read().unwrap().length);
let length = clip.as_ref().map_or(0, |p|p.read().unwrap().length);
for (sample, pulse) in pulses {
// If a next phrase is enqueued, and we're past the end of the current one,
// If a next clip is enqueued, and we're past the end of the current one,
// break the loop here (FIXME count pulse correctly)
let past_end = if phrase.is_some() { pulse >= length } else { true };
if self.next_phrase().is_some() && past_end {
let past_end = if clip.is_some() { pulse >= length } else { true };
if self.next_clip().is_some() && past_end {
return true
}
// If there's a currently playing phrase, output notes from it to buffer:
if let Some(ref phrase) = phrase {
Self::play_pulse(phrase, pulse, sample, note_buf, out, notes)
// If there's a currently playing clip, output notes from it to buffer:
if let Some(ref clip) = clip {
Self::play_pulse(clip, pulse, sample, note_buf, out, notes)
}
}
false
}
fn play_pulse (
phrase: &RwLock<MidiClip>,
clip: &RwLock<MidiClip>,
pulse: usize,
sample: usize,
note_buf: &mut Vec<u8>,
out: &mut [Vec<Vec<u8>>],
notes: &mut [bool;128]
) {
// Source phrase from which the MIDI events will be taken.
let phrase = phrase.read().unwrap();
// Source clip from which the MIDI events will be taken.
let clip = clip.read().unwrap();
// Phrase with zero length is not processed
if phrase.length > 0 {
// Current pulse index in source phrase
let pulse = pulse % phrase.length;
// Output each MIDI event from phrase at appropriate frames of output buffer:
for message in phrase.notes[pulse].iter() {
if clip.length > 0 {
// Current pulse index in source clip
let pulse = pulse % clip.length;
// Output each MIDI event from clip at appropriate frames of output buffer:
for message in clip.notes[pulse].iter() {
// Clear output buffer for this MIDI event.
note_buf.clear();
// TODO: support MIDI channels other than CH1.