diff --git a/crates/tek_api/src/api_clock.rs b/crates/tek_api/src/api_clock.rs index 09ace6ae..d66de4b9 100644 --- a/crates/tek_api/src/api_clock.rs +++ b/crates/tek_api/src/api_clock.rs @@ -26,7 +26,7 @@ pub trait ClockApi { /// Launch quantization factor fn sync (&self) -> &LaunchSync; - fn timebase (&self) -> &Timebase { + fn timebase (&self) -> &Arc { &self.current().timebase } fn sr (&self) -> &SampleRate { diff --git a/crates/tek_api/src/api_player.rs b/crates/tek_api/src/api_player.rs index 4df0773c..12cbb2e3 100644 --- a/crates/tek_api/src/api_player.rs +++ b/crates/tek_api/src/api_player.rs @@ -11,6 +11,7 @@ pub trait HasMidiBuffer { fn midi_buffer (&self) -> &mut Vec>>; fn reset (&self) -> bool; + fn reset_mut (&mut self) -> &mut bool; /// Clear the section of the output buffer that we will be using, @@ -28,44 +29,18 @@ pub trait HasMidiBuffer { pub trait HasPhrase: ClockApi + PlayheadApi + HasMidiBuffer { fn phrase (&self) - -> &Option<(Instant, Arc>)>; + -> &Option<(Instant, Option>>)>; fn phrase_mut (&self) - -> &mut Option<(Instant, Arc>)>; + -> &mut Option<(Instant, Option>>)>; fn next_phrase (&self) - -> &Option<(Instant, Arc>)>; + -> &Option<(Instant, Option>>)>; fn next_phrase_mut (&mut self) - -> &mut Option<(Instant, Arc>)>; - - fn switchover (&mut self, scope: &ProcessScope) { - if self.is_rolling() { - let sample0 = scope.last_frame_time() as usize; - //let samples = scope.n_frames() as usize; - if let Some((start_at, phrase)) = &self.next_phrase() { - let start = start_at.sample.get() as usize; - let sample = self.started().read().unwrap().unwrap().0; - // If it's time to switch to the next phrase: - if start <= sample0.saturating_sub(sample) { - // Samples elapsed since phrase was supposed to start - let skipped = sample0 - start; - // Switch over to enqueued phrase - let started = Instant::from_sample(&self.timebase(), start as f64); - *self.phrase_mut() = Some((started, phrase.clone())); - // Unset enqueuement (TODO: where to implement looping?) - *self.next_phrase_mut() = None - } - // TODO fill in remaining ticks of chunk from next phrase. - // ?? just call self.play(scope) again, since enqueuement is off ??? - self.play(scope); - // ?? or must it be with modified scope ?? - // likely not because start time etc - } - } - } + -> &mut Option<(Instant, Option>>)>; fn enqueue_next (&mut self, phrase: Option<&Arc>>) { let start = self.next_launch_pulse(); *self.next_phrase_mut() = Some(( - Instant::from_pulse(&self.timebase(), start as f64), + Instant::from_pulse(self.timebase(), start as f64), phrase.map(|p|p.clone()) )); *self.reset_mut() = true; @@ -159,11 +134,11 @@ pub trait MidiInputApi: PlayheadApi + HasMidiBuffer + HasPhrase { pub trait MidiOutputApi: PlayheadApi + HasMidiBuffer + HasPhrase { fn midi_outs (&self) -> &Vec>; - fn midi_outs_mut (&self) -> &mut Vec>; + fn midi_outs_mut (&mut self) -> &mut Vec>; fn midi_note (&mut self) -> &mut Vec; - fn notes_out (&mut self) -> &Arc>>; + fn notes_out (&self) -> &Arc>; fn has_midi_outs (&self) -> bool { self.midi_outs().len() > 0 @@ -177,22 +152,29 @@ pub trait MidiOutputApi: PlayheadApi + HasMidiBuffer + HasPhrase { let samples = scope.n_frames() as usize; // If no phrase is playing, prepare for switchover immediately next = self.phrase().is_none(); - if let Some((started, phrase)) = &self.phrase() { + let mut midi_note = self.midi_note(); + let ref phrase = self.phrase(); + let ref started0 = self.started(); + let ref timebase = self.timebase(); + let ref notes_out = self.notes_out(); + let ref next_phrase = self.next_phrase(); + let ref midi_buffer = self.midi_buffer(); + if let Some((started, phrase)) = phrase { // First sample to populate. Greater than 0 means that the first // pulse of the phrase falls somewhere in the middle of the chunk. let sample = started.sample.get() as usize; - let sample = sample + self.started().read().unwrap().unwrap().0; + let sample = sample + started0.read().unwrap().unwrap().0; let sample = sample0.saturating_sub(sample); // 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 // sample of the output buffer that corresponds to a MIDI pulse. - let pulses = self.timebase().pulses_between_samples(sample, sample + samples); + let pulses = timebase.pulses_between_samples(sample, sample + samples); // Notes active during current chunk. - let notes = &mut self.notes_out().write().unwrap(); + let notes = &mut notes_out.write().unwrap(); for (sample, pulse) in pulses { // If a next phrase is enqueued, and we're past the end of the current one, // break the loop here (FIXME count pulse correctly) - next = self.next_phrase().is_some() && if let Some(ref phrase) = phrase { + next = next_phrase.is_some() && if let Some(ref phrase) = phrase { pulse >= phrase.read().unwrap().length } else { true @@ -209,15 +191,15 @@ pub trait MidiOutputApi: PlayheadApi + HasMidiBuffer + HasPhrase { // Output each MIDI event from phrase at appropriate frames of output buffer: for message in phrase.notes[pulse].iter() { // Clear output buffer for this MIDI event. - self.midi_note().clear(); + midi_note.clear(); // TODO: support MIDI channels other than CH1. let channel = 0.into(); // Serialize MIDI event into message buffer. LiveEvent::Midi { channel, message: *message } - .write(&mut self.midi_note()) + .write(&mut midi_note) .unwrap(); // Append serialized message to output buffer. - self.midi_buffer()[sample].push(self.midi_note().clone()); + midi_buffer[sample].push(midi_note.clone()); // Update the list of currently held notes. update_keys(&mut*notes, &message); } @@ -228,6 +210,32 @@ pub trait MidiOutputApi: PlayheadApi + HasMidiBuffer + HasPhrase { next } + fn switchover (&mut self, scope: &ProcessScope) { + if self.is_rolling() { + let sample0 = scope.last_frame_time() as usize; + //let samples = scope.n_frames() as usize; + if let Some((start_at, phrase)) = &self.next_phrase() { + let start = start_at.sample.get() as usize; + let sample = self.started().read().unwrap().unwrap().0; + // If it's time to switch to the next phrase: + if start <= sample0.saturating_sub(sample) { + // Samples elapsed since phrase was supposed to start + let skipped = sample0 - start; + // Switch over to enqueued phrase + let started = Instant::from_sample(&self.timebase(), start as f64); + *self.phrase_mut() = Some((started, phrase.clone())); + // Unset enqueuement (TODO: where to implement looping?) + *self.next_phrase_mut() = None + } + // TODO fill in remaining ticks of chunk from next phrase. + // ?? just call self.play(scope) again, since enqueuement is off ??? + self.play(scope); + // ?? or must it be with modified scope ?? + // likely not because start time etc + } + } + } + fn write (&mut self, scope: &ProcessScope) { let samples = scope.n_frames() as usize; for port in self.midi_outs_mut().iter_mut() { diff --git a/crates/tek_api/src/api_scene.rs b/crates/tek_api/src/api_scene.rs index 8d15c6f3..838cb3d8 100644 --- a/crates/tek_api/src/api_scene.rs +++ b/crates/tek_api/src/api_scene.rs @@ -71,10 +71,12 @@ pub trait ArrangerSceneApi: Sized { .all(|(track_index, clip)|match clip { Some(clip) => tracks .get(track_index) - .map(|track|if let Some((_, Some(phrase))) = track.player().phrase() { - *phrase.read().unwrap() == *clip.read().unwrap() - } else { - false + .map(|track|{ + if let Some((_, Some(phrase))) = track.player().phrase() { + *phrase.read().unwrap() == *clip.read().unwrap() + } else { + false + } }) .unwrap_or(false), None => true