diff --git a/crates/tek_sequencer/src/sequencer_snd.rs b/crates/tek_sequencer/src/sequencer_snd.rs index 002bddd6..bfbc55d5 100644 --- a/crates/tek_sequencer/src/sequencer_snd.rs +++ b/crates/tek_sequencer/src/sequencer_snd.rs @@ -14,12 +14,10 @@ impl Audio for PhrasePlayer { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { let has_midi_outputs = self.has_midi_outputs(); let has_midi_inputs = self.has_midi_inputs(); - //if has_midi_outputs { - // Clear output buffer(s) - self.clear(scope, false); - // Write chunk of phrase to output - self.play(scope); - //} + // Clear output buffer(s) + self.clear(scope, false); + // Write chunk of phrase to output, handle switchover + self.play(scope); if has_midi_inputs { if self.recording || self.monitoring { // Record and/or monitor input @@ -51,31 +49,16 @@ impl PhrasePlayer { fn is_rolling (&self) -> bool { *self.clock.playing.read().unwrap() == Some(TransportState::Rolling) } - /// Return playing phrase with starting point - fn playing (&self) -> Option<(usize, Arc>)> { - if let (true, Some((started, Some(ref phrase)))) = (self.is_rolling(), &self.phrase) { - Some((started.sample().get() as usize, phrase.clone())) - } else { - None - } - } - /// Return next phrase with starting point - fn enqueued (&self) -> Option<(usize, Option>>)> { - if let (true, Some((start_at, phrase))) = (self.is_rolling(), &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; - let mut next = false; + // If no phrase is playing, prepare for switchover immediately + let mut next = self.phrase.is_none(); // Write MIDI events from currently playing phrase (if any) to MIDI output buffer - if let Some((start, ref phrase)) = self.playing() { + if let (true, Some((started, phrase))) = (self.is_rolling(), &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. - let sample = sample0.saturating_sub(start); + let sample = sample0.saturating_sub(started.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 // sample of the output buffer that corresponds to a MIDI pulse. @@ -84,43 +67,52 @@ impl PhrasePlayer { let output = &mut self.midi_out_buf; // Buffer for bytes of a MIDI event. let mut mm = Vec::with_capacity(8); - // Source phrase from which the MIDI events will be taken. - let phrase = phrase.read().unwrap(); // Notes active during current chunk. let notes = &mut self.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) - if self.next_phrase.is_some() && pulse >= phrase.length { - next = true; + next = self.next_phrase.is_some() && if let Some(ref phrase) = phrase { + pulse >= phrase.read().unwrap().length + } else { + true + }; + if next { break } - let pulse = pulse % phrase.length; - // 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. - mm.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 mm).unwrap(); - // Append serialized message to output buffer. - output[sample].push(mm.clone()); - // Update the list of currently held notes. - update_keys(notes, &message); + // If there's a currently playing phrase, output notes from it to buffer: + if let Some(ref phrase) = phrase { + // Source phrase from which the MIDI events will be taken. + let phrase = phrase.read().unwrap(); + // 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() { + // Clear output buffer for this MIDI event. + mm.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 mm).unwrap(); + // Append serialized message to output buffer. + output[sample].push(mm.clone()); + // Update the list of currently held notes. + update_keys(notes, &message); + } } } } // If it's time for the next enqueued phrase, handle it here: if next { - if let Some((start, phrase)) = self.enqueued() { + if let (true, Some((start_at, phrase))) = (self.is_rolling(), &self.next_phrase) { + let start = start_at.sample().get() as usize; // If it's time to switch to the next phrase: if start <= sample0 { // 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)); + self.phrase = Some((started, phrase.clone())); // Unset enqueuement (TODO: where to implement looping?) self.next_phrase = None } @@ -131,10 +123,9 @@ impl PhrasePlayer { } 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; + if let (true, Some((started, phrase))) = (self.is_rolling(), &self.phrase) { + let start = started.sample.get() as usize; + let quant = self.clock.quant().get(); // For highlighting keys and note repeat let mut notes_in = self.notes_in.write().unwrap(); // Record from each input @@ -145,20 +136,24 @@ impl PhrasePlayer { self.midi_out_buf[sample].push(bytes.to_vec()) } if self.recording { - phrase.record_event({ - let sample = (sample0 + sample - start) as f64; - let pulse = self.clock.timebase().samples_to_pulse(sample); - let quantized = (pulse / quant).round() * quant; - let looped = quantized as usize % length; - looped - }, message); + if let Some(phrase) = phrase { + let mut phrase = phrase.write().unwrap(); + let length = phrase.length; + phrase.record_event({ + let sample = (sample0 + sample - start) as f64; + let pulse = self.clock.timebase().samples_to_pulse(sample); + let quantized = (pulse / quant).round() * quant; + let looped = quantized as usize % length; + looped + }, message); + } } update_keys(&mut notes_in, &message); } } } } - if let Some((start, ref phrase)) = self.enqueued() { + if let (true, Some((start_at, phrase))) = (self.is_rolling(), &self.next_phrase) { // TODO switch to next phrase and record into it } }