launch countdown/switchover, pt.2

This commit is contained in:
🪞👃🪞 2024-11-01 20:52:14 +02:00
parent 3df8e87840
commit 97a7bf5b1d
3 changed files with 31 additions and 12 deletions

View file

@ -236,12 +236,20 @@ impl<E: Engine> Arrangement<E> {
size: Measure::new(), size: Measure::new(),
} }
} }
fn is_stopped (&self) -> bool {
*self.clock.playing.read().unwrap() == Some(TransportState::Stopped)
}
pub fn activate (&mut self) { pub fn activate (&mut self) {
match self.selected { match self.selected {
ArrangementFocus::Scene(s) => { ArrangementFocus::Scene(s) => {
for (t, track) in self.tracks.iter_mut().enumerate() { for (t, track) in self.tracks.iter_mut().enumerate() {
track.player.enqueue_next(self.scenes[s].clips[t].as_ref()); track.player.enqueue_next(self.scenes[s].clips[t].as_ref());
} }
// TODO make transport available here, so that
// activating a scene when stopped starts playback
//if self.is_stopped() {
//self.transport.toggle_play()
//}
}, },
ArrangementFocus::Clip(t, s) => { ArrangementFocus::Clip(t, s) => {
self.tracks[t].player.enqueue_next(self.scenes[s].clips[t].as_ref()); self.tracks[t].player.enqueue_next(self.scenes[s].clips[t].as_ref());

View file

@ -11,7 +11,7 @@ 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 has_midi_outputs = self.has_midi_outputs(); let has_midi_outputs = self.has_midi_outputs();
let has_midi_inputs = self.has_midi_inputs(); let has_midi_inputs = self.has_midi_inputs();
if has_midi_outputs { if has_midi_outputs {
// Clear output buffer(s) // Clear output buffer(s)
self.clear(scope, false); self.clear(scope, false);
@ -45,11 +45,12 @@ impl PhrasePlayer {
all_notes_off(&mut self.midi_out_buf); self.reset = false; all_notes_off(&mut self.midi_out_buf); self.reset = false;
} }
} }
fn is_rolling (&self) -> bool {
*self.clock.playing.read().unwrap() == Some(TransportState::Rolling)
}
/// Return playing phrase with starting point /// Return playing phrase with starting point
fn playing (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> { fn playing (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> {
if let ( if let (true, Some((started, Some(ref phrase)))) = (self.is_rolling(), &self.phrase) {
Some(TransportState::Rolling), Some((started, Some(ref phrase)))
) = (*self.clock.playing.read().unwrap(), &self.phrase) {
Some((started.sample().get() as usize, phrase.clone())) Some((started.sample().get() as usize, phrase.clone()))
} else { } else {
None None
@ -57,9 +58,7 @@ impl PhrasePlayer {
} }
/// Return next phrase with starting point /// Return next phrase with starting point
fn enqueued (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> { fn enqueued (&self) -> Option<(usize, Arc<RwLock<Phrase>>)> {
if let ( if let (true, Some((start_at, Some(ref phrase)))) = (self.is_rolling(), &self.next_phrase) {
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())) Some((start_at.sample().get() as usize, phrase.clone()))
} else { } else {
None None
@ -76,6 +75,10 @@ impl PhrasePlayer {
let notes_on = &mut self.notes_out.write().unwrap(); let notes_on = &mut self.notes_out.write().unwrap();
let mut buf = Vec::with_capacity(8); let mut buf = Vec::with_capacity(8);
for (sample, tick) in ticks { for (sample, tick) in ticks {
// If a next phrase is enqueued, and we're past the end of the current one,
// break the loop here (FIXME count tick correctly)
if self.next_phrase.is_some() && tick >= phrase.length { break }
// Output events from phrase at appropriate frames of output buffer
let tick = tick % phrase.length; let tick = tick % phrase.length;
for message in phrase.notes[tick].iter() { for message in phrase.notes[tick].iter() {
buf.clear(); buf.clear();
@ -88,6 +91,9 @@ impl PhrasePlayer {
} }
}).unwrap() }).unwrap()
} }
if let Some((start, ref phrase)) = self.enqueued() {
// TODO switch to next phrase and fill in remaining ticks from it
}
} }
fn record (&mut self, scope: &ProcessScope) { fn record (&mut self, scope: &ProcessScope) {
let sample0 = scope.last_frame_time() as usize; let sample0 = scope.last_frame_time() as usize;
@ -101,7 +107,9 @@ impl PhrasePlayer {
for input in self.midi_inputs.iter() { for input in self.midi_inputs.iter() {
for (sample, 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 {
if self.monitoring { self.midi_out_buf[sample].push(bytes.to_vec()) } if self.monitoring {
self.midi_out_buf[sample].push(bytes.to_vec())
}
if self.recording { if self.recording {
phrase.record_event({ phrase.record_event({
let pulse = self.clock.timebase().samples_to_pulse( let pulse = self.clock.timebase().samples_to_pulse(
@ -117,6 +125,9 @@ impl PhrasePlayer {
} }
} }
} }
if let Some((start, ref phrase)) = self.enqueued() {
// TODO switch to next phrase and record into it
}
} }
fn monitor (&mut self, scope: &ProcessScope) { fn monitor (&mut self, scope: &ProcessScope) {
let mut notes_in = self.notes_in.write().unwrap(); let mut notes_in = self.notes_in.write().unwrap();

View file

@ -2,13 +2,13 @@ use crate::*;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TransportTime { pub struct TransportTime {
/// Current moment in time /// Current moment in time
pub instant: Instant, pub instant: Instant,
/// Note quantization factor /// Note quantization factor
pub quant: TimeUnit, pub quant: TimeUnit,
/// Launch quantization factor /// Launch quantization factor
pub sync: TimeUnit, pub sync: TimeUnit,
/// Playback state /// Playback state
pub playing: RwLock<Option<TransportState>>, pub playing: RwLock<Option<TransportState>>,
} }
impl TransportTime { impl TransportTime {
pub fn timebase (&self) -> &Arc<Timebase> { &self.instant.timebase } pub fn timebase (&self) -> &Arc<Timebase> { &self.instant.timebase }