diff --git a/crates/tek_sequencer/src/sequencer_snd.rs b/crates/tek_sequencer/src/sequencer_snd.rs index b0adab07..d566a885 100644 --- a/crates/tek_sequencer/src/sequencer_snd.rs +++ b/crates/tek_sequencer/src/sequencer_snd.rs @@ -17,7 +17,9 @@ impl Audio for PhrasePlayer { // Clear output buffer(s) self.clear(scope, false); // Write chunk of phrase to output, handle switchover - self.play(scope); + if self.play(scope) { + self.switchover(scope); + } if has_midi_inputs { if self.recording || self.monitoring { // Record and/or monitor input @@ -34,8 +36,15 @@ impl Audio for PhrasePlayer { } /// Methods used primarily by the process callback impl PhrasePlayer { - fn has_midi_inputs (&self) -> bool { self.midi_inputs.len() > 0 } - fn has_midi_outputs (&self) -> bool { self.midi_outputs.len() > 0 } + fn is_rolling (&self) -> bool { + *self.clock.playing.read().unwrap() == Some(TransportState::Rolling) + } + 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) { @@ -46,16 +55,14 @@ impl PhrasePlayer { all_notes_off(&mut self.midi_out_buf); self.reset = false; } } - fn is_rolling (&self) -> bool { - *self.clock.playing.read().unwrap() == Some(TransportState::Rolling) - } - fn play (&mut self, scope: &ProcessScope) { + fn play (&mut self, scope: &ProcessScope) -> bool { let sample0 = scope.last_frame_time() as usize; let samples = scope.n_frames() as usize; + let mut next = false; // Write MIDI events from currently playing phrase (if any) to MIDI output buffer if self.is_rolling() { // If no phrase is playing, prepare for switchover immediately - let mut next = self.phrase.is_none(); + next = self.phrase.is_none(); if let Some((started, phrase)) = &self.phrase { // First sample to populate. Greater than 0 means that the first // pulse of the phrase falls somewhere in the middle of the chunk. @@ -105,25 +112,29 @@ impl PhrasePlayer { } } } - // If it's time for the next enqueued phrase, handle it here: - if next { - if let Some((start_at, phrase)) = &self.next_phrase { - let start = start_at.sample.get() as usize; - // If it's time to switch to the next phrase: - if start <= sample0.saturating_sub( - self.clock.started.read().unwrap().unwrap().0 - ) { - // Samples elapsed since phrase was supposed to start - let skipped = sample0 - start; - // Switch over to enqueued phrase - let started = Instant::from_sample(&self.clock.timebase(), start as f64); - self.phrase = Some((started, phrase.clone())); - // Unset enqueuement (TODO: where to implement looping?) - self.next_phrase = None - } - // TODO fill in remaining ticks of chunk from next phrase. - // ?? just call self.play(scope) again, since enqueuement is off ??? + } + next + } + fn switchover (&mut self, scope: &ProcessScope) { + let sample0 = scope.last_frame_time() as usize; + let samples = scope.n_frames() as usize; + if self.is_rolling() { + if let Some((start_at, phrase)) = &self.next_phrase { + let start = start_at.sample.get() as usize; + // If it's time to switch to the next phrase: + if start <= sample0.saturating_sub( + self.clock.started.read().unwrap().unwrap().0 + ) { + // Samples elapsed since phrase was supposed to start + let skipped = sample0 - start; + // Switch over to enqueued phrase + let started = Instant::from_sample(&self.clock.timebase(), start as f64); + self.phrase = Some((started, phrase.clone())); + // Unset enqueuement (TODO: where to implement looping?) + self.next_phrase = None } + // TODO fill in remaining ticks of chunk from next phrase. + // ?? just call self.play(scope) again, since enqueuement is off ??? } } }