firewheel_nodes/
sampler.rs

1// TODO: The logic in this has become increadibly complex and error-prone. I plan
2// on rewriting the sampler engine using a state machine.
3
4use firewheel_core::clock::{DurationSamples, DurationSeconds};
5use firewheel_core::node::{ProcBuffers, ProcExtra, ProcStreamCtx};
6#[cfg(not(feature = "std"))]
7use num_traits::Float;
8
9use bevy_platform::sync::atomic::{AtomicU64, Ordering};
10use bevy_platform::time::Instant;
11use core::sync::atomic::AtomicU32;
12use core::{
13    num::{NonZeroU32, NonZeroUsize},
14    ops::Range,
15};
16use firewheel_core::diff::{EventQueue, PatchError, PathBuilder, RealtimeClone};
17use smallvec::SmallVec;
18
19use firewheel_core::{
20    channel_config::{ChannelConfig, ChannelCount, NonZeroChannelCount},
21    clock::InstantSeconds,
22    collector::ArcGc,
23    diff::{Diff, Notify, ParamPath, Patch},
24    dsp::{
25        buffer::InstanceBuffer,
26        declick::{DeclickFadeCurve, Declicker},
27        volume::{Volume, DEFAULT_AMP_EPSILON},
28    },
29    event::{NodeEventType, ParamData, ProcEvents},
30    mask::{MaskType, SilenceMask},
31    node::{
32        AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, ProcInfo,
33        ProcessStatus,
34    },
35    sample_resource::SampleResource,
36    StreamInfo,
37};
38
39#[cfg(feature = "scheduled_events")]
40use firewheel_core::clock::EventInstant;
41
42pub const MAX_OUT_CHANNELS: usize = 8;
43pub const DEFAULT_NUM_DECLICKERS: usize = 2;
44pub const MIN_PLAYBACK_SPEED: f64 = 0.0000001;
45
46/// The configuration of a [`SamplerNode`]
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
49#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
50#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
51pub struct SamplerConfig {
52    /// The number of channels in this node.
53    pub channels: NonZeroChannelCount,
54    /// The maximum number of "declickers" present on this node.
55    /// The more declickers there are, the more samples that can be declicked
56    /// when played in rapid succession. (Note more declickers will allocate
57    /// more memory).
58    ///
59    /// By default this is set to `2`.
60    pub num_declickers: u32,
61    /// The quality of the resampling algorithm used when changing the playback
62    /// speed.
63    pub speed_quality: PlaybackSpeedQuality,
64}
65
66impl Default for SamplerConfig {
67    fn default() -> Self {
68        Self {
69            channels: NonZeroChannelCount::STEREO,
70            num_declickers: DEFAULT_NUM_DECLICKERS as u32,
71            speed_quality: PlaybackSpeedQuality::default(),
72        }
73    }
74}
75
76/// The quality of the resampling algorithm used for changing the playback
77/// speed of a sampler node.
78#[non_exhaustive]
79#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
80#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
81#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
82pub enum PlaybackSpeedQuality {
83    #[default]
84    /// Low quality, fast performance. Recommended for most use cases.
85    ///
86    /// More specifically, this uses a linear resampling algorithm with no
87    /// antialiasing filter.
88    LinearFast,
89    // TODO: more quality options
90}
91
92/// A node that plays samples
93///
94/// It supports pausing, resuming, looping, and changing the playback speed.
95#[derive(Clone, Diff, Patch, PartialEq)]
96#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
97pub struct SamplerNode {
98    /// The sample resource to use.
99    pub sample: Option<ArcGc<dyn SampleResource>>,
100
101    /// The volume to play the sample at.
102    ///
103    /// Note, this gain parameter is *NOT* smoothed! If you need the gain to be
104    /// smoothed, please use a [`VolumeNode`] or a [`VolumePanNode`].
105    ///
106    /// [`VolumeNode`]: crate::volume::VolumeNode
107    /// [`VolumePanNode`]: crate::volume_pan::VolumePanNode
108    pub volume: Volume,
109
110    /// Whether or not the current sample should start/restart playing (true), or be
111    /// paused/stopped (false).
112    pub play: Notify<bool>,
113
114    /// Defines where the sampler should start playing from when
115    /// [`SamplerNode::play`] is set to `true`.
116    pub play_from: PlayFrom,
117
118    /// How many times a sample should be repeated.
119    pub repeat_mode: RepeatMode,
120
121    /// The speed at which to play the sample at. `1.0` means to play the sound at
122    /// its original speed, `< 1.0` means to play the sound slower (which will make
123    /// it lower-pitched), and `> 1.0` means to play the sound faster (which will
124    /// make it higher-pitched).
125    pub speed: f64,
126
127    /// If `true`, then mono samples will be converted to stereo during playback.
128    ///
129    /// By default this is set to `true`.
130    pub mono_to_stereo: bool,
131    /// If true, then samples will be crossfaded when the playhead or sample is
132    /// changed (if a sample was currently playing when the event was sent).
133    ///
134    /// By default this is set to `true`.
135    pub crossfade_on_seek: bool,
136    /// If the resutling gain (in raw amplitude, not decibels) is less
137    /// than or equal to this value, then the gain will be clamped to
138    /// `0.0` (silence).
139    ///
140    /// By default this is set to `0.00001` (-100 decibels).
141    pub min_gain: f32,
142}
143
144impl Default for SamplerNode {
145    fn default() -> Self {
146        Self {
147            sample: None,
148            volume: Volume::default(),
149            play: Default::default(),
150            play_from: PlayFrom::default(),
151            repeat_mode: RepeatMode::default(),
152            speed: 1.0,
153            mono_to_stereo: true,
154            crossfade_on_seek: true,
155            min_gain: DEFAULT_AMP_EPSILON,
156        }
157    }
158}
159
160impl core::fmt::Debug for SamplerNode {
161    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
162        let mut f = f.debug_struct("SamplerNode");
163        f.field("has_sample", &self.sample.is_some());
164        f.field("volume", &self.volume);
165        f.field("play", &self.play);
166        f.field("play_from", &self.play_from);
167        f.field("repeat_mode", &self.repeat_mode);
168        f.field("speed", &self.speed);
169        f.field("mono_to_stereo", &self.mono_to_stereo);
170        f.field("crossfade_on_seek", &self.crossfade_on_seek);
171        f.field("min_gain", &self.min_gain);
172        f.finish()
173    }
174}
175
176impl SamplerNode {
177    /// Set the parameters to a play a single sample.
178    pub fn set_sample(&mut self, sample: ArcGc<dyn SampleResource>) {
179        self.sample = Some(sample);
180    }
181
182    /// Returns an event type to sync the `sample` parameter.
183    pub fn sync_sample_event(&self) -> NodeEventType {
184        NodeEventType::Param {
185            data: ParamData::any(self.sample.clone()),
186            path: ParamPath::Single(0),
187        }
188    }
189
190    /// Returns an event type to sync the `volume` parameter.
191    pub fn sync_volume_event(&self) -> NodeEventType {
192        NodeEventType::Param {
193            data: ParamData::Volume(self.volume),
194            path: ParamPath::Single(1),
195        }
196    }
197
198    /// Returns an event type to sync the `play` parameter.
199    pub fn sync_play_event(&self) -> NodeEventType {
200        NodeEventType::Param {
201            data: ParamData::Bool(*self.play),
202            path: ParamPath::Single(2),
203        }
204    }
205
206    /// Returns an event type to sync the `play_from` parameter.
207    pub fn sync_play_from_event(&self) -> NodeEventType {
208        NodeEventType::Param {
209            data: self.play_from.as_param_data(),
210            path: ParamPath::Single(3),
211        }
212    }
213
214    /// Returns an event type to sync the `playhead` parameter.
215    pub fn sync_repeat_mode_event(&self) -> NodeEventType {
216        NodeEventType::Param {
217            data: ParamData::any(self.repeat_mode),
218            path: ParamPath::Single(4),
219        }
220    }
221
222    /// Returns an event type to sync the `speed` parameter.
223    pub fn sync_speed_event(&self) -> NodeEventType {
224        NodeEventType::Param {
225            data: ParamData::F64(self.speed),
226            path: ParamPath::Single(5),
227        }
228    }
229
230    /// Start/restart the sample in this node.
231    ///
232    /// If a sample is already playing, then it will restart from the beginning.
233    pub fn start_or_restart(&mut self) {
234        self.play_from = PlayFrom::BEGINNING;
235        *self.play = true;
236    }
237
238    /// Play the sample in this node from the given playhead.
239    pub fn start_from(&mut self, from: PlayFrom) {
240        self.play_from = from;
241        *self.play = true;
242    }
243
244    /// Pause sample playback.
245    pub fn pause(&mut self) {
246        self.play_from = PlayFrom::Resume;
247        *self.play = false;
248    }
249
250    /// Resume sample playback.
251    pub fn resume(&mut self) {
252        *self.play = true;
253    }
254
255    /// Stop sample playback.
256    ///
257    /// Calling [`SamplerNode::resume`] after this will restart the sample from
258    /// the beginning.
259    pub fn stop(&mut self) {
260        self.play_from = PlayFrom::BEGINNING;
261        *self.play = false;
262    }
263
264    /// Returns `true` if the current state is set to restart the sample.
265    pub fn start_or_restart_requested(&self) -> bool {
266        *self.play && self.play_from == PlayFrom::BEGINNING
267    }
268
269    /// Returns `true` if the current state is set to resume the sample.
270    pub fn resume_requested(&self) -> bool {
271        *self.play && self.play_from == PlayFrom::Resume
272    }
273
274    /// Returns `true` if the current state is set to pause the sample.
275    pub fn pause_requested(&self) -> bool {
276        !*self.play && self.play_from == PlayFrom::Resume
277    }
278
279    /// Returns `true` if the current state is set to stop the sample.
280    pub fn stop_requested(&self) -> bool {
281        !*self.play && self.play_from != PlayFrom::Resume
282    }
283}
284
285#[derive(Clone)]
286pub struct SamplerState {
287    shared_state: ArcGc<SharedState>,
288}
289
290impl SamplerState {
291    fn new() -> Self {
292        Self {
293            shared_state: ArcGc::new(SharedState::default()),
294        }
295    }
296
297    /// Get the current position of the playhead in units of frames (samples of
298    /// a single channel of audio).
299    pub fn playhead_frames(&self) -> DurationSamples {
300        DurationSamples(
301            self.shared_state
302                .sample_playhead_frames
303                .load(Ordering::Relaxed) as i64,
304        )
305    }
306
307    /// Get the current position of the sample playhead in seconds.
308    ///
309    /// * `sample_rate` - The sample rate of the current audio stream.
310    pub fn playhead_seconds(&self, sample_rate: NonZeroU32) -> DurationSeconds {
311        DurationSeconds(self.playhead_frames().0 as f64 / sample_rate.get() as f64)
312    }
313
314    /// Get the current position of the playhead in units of frames (samples of
315    /// a single channel of audio), corrected with the delay between when the audio clock
316    /// was last updated and now.
317    ///
318    /// Call `FirewheelCtx::audio_clock_instant()` right before calling this method to get
319    /// the latest update instant.
320    pub fn playhead_frames_corrected(
321        &self,
322        update_instant: Option<Instant>,
323        sample_rate: NonZeroU32,
324    ) -> DurationSamples {
325        let frames = self.playhead_frames();
326
327        let Some(update_instant) = update_instant else {
328            return frames;
329        };
330
331        if SharedPlaybackState::from_u32(self.shared_state.playback_state.load(Ordering::Relaxed))
332            == SharedPlaybackState::Playing
333        {
334            DurationSamples(
335                frames.0
336                    + InstantSeconds(update_instant.elapsed().as_secs_f64())
337                        .to_samples(sample_rate)
338                        .0 as i64,
339            )
340        } else {
341            frames
342        }
343    }
344
345    /// Get the current position of the playhead in units of seconds, corrected with the
346    /// delay between when the audio clock was last updated and now.
347    ///
348    /// Call `FirewheelCtx::audio_clock_instant()` right before calling this method to get
349    /// the latest update instant.
350    pub fn playhead_seconds_corrected(
351        &self,
352        update_instant: Option<Instant>,
353        sample_rate: NonZeroU32,
354    ) -> DurationSeconds {
355        DurationSeconds(
356            self.playhead_frames_corrected(update_instant, sample_rate)
357                .0 as f64
358                / sample_rate.get() as f64,
359        )
360    }
361
362    /// Returns `true` if the sample is currently playing.
363    pub fn playing(&self) -> bool {
364        SharedPlaybackState::from_u32(self.shared_state.playback_state.load(Ordering::Relaxed))
365            == SharedPlaybackState::Playing
366    }
367
368    /// Returns `true` if the sample is currently paused.
369    pub fn paused(&self) -> bool {
370        SharedPlaybackState::from_u32(self.shared_state.playback_state.load(Ordering::Relaxed))
371            == SharedPlaybackState::Paused
372    }
373
374    /// Returns `true` if the sample has either not started playing yet or has finished
375    /// playing.
376    pub fn stopped(&self) -> bool {
377        SharedPlaybackState::from_u32(self.shared_state.playback_state.load(Ordering::Relaxed))
378            == SharedPlaybackState::Stopped
379    }
380
381    /// Manually set the shared `playing` flag. This can be useful to account for the delay
382    /// between sending a play event and the node's processor receiving that event.
383    pub fn mark_playing(&self) {
384        self.shared_state
385            .playback_state
386            .store(SharedPlaybackState::Playing as u32, Ordering::Relaxed);
387    }
388
389    /// Manually set the shared `paused` flag. This can be useful to account for the delay
390    /// between sending a play event and the node's processor receiving that event.
391    pub fn mark_paused(&self) {
392        self.shared_state
393            .playback_state
394            .store(SharedPlaybackState::Paused as u32, Ordering::Relaxed);
395    }
396
397    /// Manually set the shared `stopped` flag. This can be useful to account for the delay
398    /// between sending a play event and the node's processor receiving that event.
399    pub fn mark_stopped(&self) {
400        self.shared_state
401            .playback_state
402            .store(SharedPlaybackState::Stopped as u32, Ordering::Relaxed);
403    }
404
405    /// Returns the ID stored in the "finished" flag.
406    pub fn finished(&self) -> u64 {
407        self.shared_state.finished.load(Ordering::Relaxed)
408    }
409
410    /// Clears the "finished" flag.
411    pub fn clear_finished(&self) {
412        self.shared_state.finished.store(0, Ordering::Relaxed);
413    }
414
415    /// A score of how suitable this node is to start new work (Play a new sample). The
416    /// higher the score, the better the candidate.
417    pub fn worker_score(&self, params: &SamplerNode) -> u64 {
418        if params.sample.is_some() {
419            let playback_state = SharedPlaybackState::from_u32(
420                self.shared_state.playback_state.load(Ordering::Relaxed),
421            );
422
423            if *params.play {
424                let playhead_frames = self.playhead_frames();
425
426                if playback_state == SharedPlaybackState::Stopped {
427                    if playhead_frames.0 > 0 {
428                        // Sequence has likely finished playing.
429                        u64::MAX - 4
430                    } else {
431                        // Sequence has likely not started playing yet.
432                        u64::MAX - 5
433                    }
434                } else {
435                    // The older the sample is, the better it is as a candidate to steal
436                    // work from.
437                    playhead_frames.0 as u64
438                }
439            } else {
440                match playback_state {
441                    SharedPlaybackState::Stopped => u64::MAX - 1,
442                    SharedPlaybackState::Paused => u64::MAX - 2,
443                    SharedPlaybackState::Playing => u64::MAX - 3,
444                }
445            }
446        } else {
447            u64::MAX
448        }
449    }
450}
451
452/// Defines where the sampler should start playing from when
453/// [`SamplerNode::play`] is set to `true`.
454#[derive(Debug, Clone, Copy, PartialEq, RealtimeClone)]
455#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
456pub enum PlayFrom {
457    /// When [`SamplerNode::play`] is set to `true`, the sampler will resume
458    /// playing from where it last left off.
459    Resume,
460    /// When [`SamplerNode::play`] is set to `true`, the sampler will begin
461    /// playing  from this position in the sample in units of seconds.
462    Seconds(f64),
463    /// When [`SamplerNode::play`] is set to `true`, the sampler will begin
464    /// playing from this position in the sample in units of frames (samples
465    /// in a single channel of audio).
466    Frames(u64),
467}
468
469impl PlayFrom {
470    pub const BEGINNING: Self = Self::Frames(0);
471
472    pub fn as_frames(&self, sample_rate: NonZeroU32) -> Option<u64> {
473        match *self {
474            Self::Resume => None,
475            Self::Seconds(seconds) => Some(if seconds <= 0.0 {
476                0
477            } else {
478                (seconds.floor() as u64 * sample_rate.get() as u64)
479                    + (seconds.fract() * sample_rate.get() as f64).round() as u64
480            }),
481            Self::Frames(frames) => Some(frames),
482        }
483    }
484
485    pub fn as_param_data(&self) -> ParamData {
486        match self {
487            Self::Resume => ParamData::None,
488            Self::Seconds(s) => ParamData::F64(*s),
489            Self::Frames(f) => ParamData::U64(*f),
490        }
491    }
492}
493
494impl Default for PlayFrom {
495    fn default() -> Self {
496        Self::BEGINNING
497    }
498}
499
500impl Diff for PlayFrom {
501    fn diff<E: EventQueue>(&self, baseline: &Self, path: PathBuilder, event_queue: &mut E) {
502        if self != baseline {
503            event_queue.push_param(self.as_param_data(), path);
504        }
505    }
506}
507
508impl Patch for PlayFrom {
509    type Patch = Self;
510
511    fn patch(data: &ParamData, _path: &[u32]) -> Result<Self::Patch, PatchError> {
512        match data {
513            ParamData::None => Ok(PlayFrom::Resume),
514            ParamData::F64(s) => Ok(PlayFrom::Seconds(*s)),
515            ParamData::U64(f) => Ok(PlayFrom::Frames(*f)),
516            _ => Err(PatchError::InvalidData),
517        }
518    }
519
520    fn apply(&mut self, value: Self::Patch) {
521        *self = value;
522    }
523}
524
525/// How many times a sample should be repeated.
526#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Diff, Patch)]
527#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
528pub enum RepeatMode {
529    /// Play the sample once and then stop.
530    #[default]
531    PlayOnce,
532    /// Repeat the sample the given number of times.
533    RepeatMultiple { num_times_to_repeat: u32 },
534    /// Repeat the sample endlessly.
535    RepeatEndlessly,
536}
537
538impl RepeatMode {
539    pub fn do_loop(&self, num_times_looped_back: u64) -> bool {
540        match self {
541            Self::PlayOnce => false,
542            &Self::RepeatMultiple {
543                num_times_to_repeat,
544            } => num_times_looped_back < num_times_to_repeat as u64,
545            Self::RepeatEndlessly => true,
546        }
547    }
548}
549
550impl AudioNode for SamplerNode {
551    type Configuration = SamplerConfig;
552
553    fn info(&self, config: &Self::Configuration) -> AudioNodeInfo {
554        AudioNodeInfo::new()
555            .debug_name("sampler")
556            .channel_config(ChannelConfig {
557                num_inputs: ChannelCount::ZERO,
558                num_outputs: config.channels.get(),
559            })
560            .custom_state(SamplerState::new())
561    }
562
563    fn construct_processor(
564        &self,
565        config: &Self::Configuration,
566        cx: ConstructProcessorContext,
567    ) -> impl AudioNodeProcessor {
568        let stop_declicker_buffers = if config.num_declickers == 0 {
569            None
570        } else {
571            Some(InstanceBuffer::<f32, MAX_OUT_CHANNELS>::new(
572                config.num_declickers as usize,
573                NonZeroUsize::new(config.channels.get().get() as usize).unwrap(),
574                cx.stream_info.declick_frames.get() as usize,
575            ))
576        };
577
578        SamplerProcessor {
579            config: config.clone(),
580            params: self.clone(),
581            shared_state: ArcGc::clone(&cx.custom_state::<SamplerState>().unwrap().shared_state),
582            loaded_sample_state: None,
583            declicker: Declicker::SettledAt1,
584            stop_declicker_buffers,
585            stop_declickers: smallvec::smallvec![StopDeclickerState::default(); config.num_declickers as usize],
586            num_active_stop_declickers: 0,
587            resampler: Some(Resampler::new(config.speed_quality)),
588            speed: self.speed.max(MIN_PLAYBACK_SPEED),
589            playing: *self.play,
590            paused: !*self.play && self.play_from == PlayFrom::Resume,
591            #[cfg(feature = "scheduled_events")]
592            queued_playback_instant: None,
593            min_gain: self.min_gain.max(0.0),
594            is_first_process: true,
595            max_block_frames: cx.stream_info.max_block_frames.get() as usize,
596        }
597    }
598}
599
600struct SamplerProcessor {
601    config: SamplerConfig,
602    params: SamplerNode,
603    shared_state: ArcGc<SharedState>,
604
605    loaded_sample_state: Option<LoadedSampleState>,
606
607    declicker: Declicker,
608
609    playing: bool,
610    paused: bool,
611
612    stop_declicker_buffers: Option<InstanceBuffer<f32, MAX_OUT_CHANNELS>>,
613    stop_declickers: SmallVec<[StopDeclickerState; DEFAULT_NUM_DECLICKERS]>,
614    num_active_stop_declickers: usize,
615
616    resampler: Option<Resampler>,
617    speed: f64,
618
619    #[cfg(feature = "scheduled_events")]
620    queued_playback_instant: Option<EventInstant>,
621
622    min_gain: f32,
623
624    is_first_process: bool,
625    max_block_frames: usize,
626}
627
628impl SamplerProcessor {
629    /// Returns `true` if the sample has finished playing, and also
630    /// returns the number of channels that were filled.
631    fn process_internal(
632        &mut self,
633        buffers: &mut [&mut [f32]],
634        frames: usize,
635        looping: bool,
636        extra: &mut ProcExtra,
637    ) -> (bool, usize) {
638        let (finished_playing, mut channels_filled) = if self.speed != 1.0 {
639            // Get around borrow checker.
640            let mut resampler = self.resampler.take().unwrap();
641
642            let (finished_playing, channels_filled) =
643                resampler.resample_linear(buffers, 0..frames, extra, self, looping);
644
645            self.resampler = Some(resampler);
646
647            (finished_playing, channels_filled)
648        } else {
649            self.resampler.as_mut().unwrap().reset();
650
651            self.copy_from_sample(buffers, 0..frames, looping)
652        };
653
654        let Some(state) = self.loaded_sample_state.as_ref() else {
655            return (true, 0);
656        };
657
658        if !self.declicker.has_settled() {
659            self.declicker.process(
660                buffers,
661                0..frames,
662                &extra.declick_values,
663                state.gain,
664                DeclickFadeCurve::EqualPower3dB,
665            );
666        } else if state.gain != 1.0 {
667            for b in buffers[..channels_filled].iter_mut() {
668                for s in b[..frames].iter_mut() {
669                    *s *= state.gain;
670                }
671            }
672        }
673
674        if state.sample_mono_to_stereo {
675            let (b0, b1) = buffers.split_first_mut().unwrap();
676            b1[0][..frames].copy_from_slice(&b0[..frames]);
677
678            channels_filled = 2;
679        }
680
681        (finished_playing, channels_filled)
682    }
683
684    /// Fill the buffer with raw data from the sample, starting from the
685    /// current playhead. Then increment the playhead.
686    ///
687    /// Returns `true` if the sample has finished playing, and also
688    /// returns the number of channels that were filled.
689    fn copy_from_sample(
690        &mut self,
691        buffers: &mut [&mut [f32]],
692        range_in_buffer: Range<usize>,
693        looping: bool,
694    ) -> (bool, usize) {
695        let Some(state) = self.loaded_sample_state.as_mut() else {
696            return (true, 0);
697        };
698
699        assert!(state.playhead_frames <= state.sample_len_frames);
700
701        let block_frames = range_in_buffer.end - range_in_buffer.start;
702        let first_copy_frames =
703            if state.playhead_frames + block_frames as u64 > state.sample_len_frames {
704                (state.sample_len_frames - state.playhead_frames) as usize
705            } else {
706                block_frames
707            };
708
709        if first_copy_frames > 0 {
710            state.sample.fill_buffers(
711                buffers,
712                range_in_buffer.start..range_in_buffer.start + first_copy_frames,
713                state.playhead_frames,
714            );
715
716            state.playhead_frames += first_copy_frames as u64;
717        }
718
719        if first_copy_frames < block_frames {
720            if looping {
721                let mut frames_copied = first_copy_frames;
722
723                while frames_copied < block_frames {
724                    let copy_frames = ((block_frames - frames_copied) as u64)
725                        .min(state.sample_len_frames)
726                        as usize;
727
728                    state.sample.fill_buffers(
729                        buffers,
730                        range_in_buffer.start + frames_copied
731                            ..range_in_buffer.start + frames_copied + copy_frames,
732                        0,
733                    );
734
735                    state.playhead_frames = copy_frames as u64;
736                    state.num_times_looped_back += 1;
737
738                    frames_copied += copy_frames;
739                }
740            } else {
741                let n_channels = buffers.len().min(state.sample_num_channels.get());
742                for b in buffers[..n_channels].iter_mut() {
743                    b[range_in_buffer.start + first_copy_frames..range_in_buffer.end].fill(0.0);
744                }
745
746                return (true, n_channels);
747            }
748        }
749
750        (false, buffers.len().min(state.sample_num_channels.get()))
751    }
752
753    fn currently_processing_sample(&self) -> bool {
754        if self.params.sample.is_none() {
755            false
756        } else {
757            self.playing || (self.paused && !self.declicker.has_settled())
758        }
759    }
760
761    fn num_channels_filled(&self, num_out_channels: usize) -> usize {
762        if let Some(state) = &self.loaded_sample_state {
763            if state.sample_mono_to_stereo {
764                2
765            } else {
766                state.sample_num_channels.get().min(num_out_channels)
767            }
768        } else {
769            0
770        }
771    }
772
773    fn stop(&mut self, num_out_channels: usize, extra: &mut ProcExtra) {
774        if self.currently_processing_sample() {
775            // Fade out the sample into a temporary look-ahead
776            // buffer to declick.
777
778            self.declicker.fade_to_0(&extra.declick_values);
779
780            // Work around the borrow checker.
781            if let Some(mut stop_declicker_buffers) = self.stop_declicker_buffers.take() {
782                if self.num_active_stop_declickers < stop_declicker_buffers.num_instances() {
783                    let declicker_i = self
784                        .stop_declickers
785                        .iter()
786                        .enumerate()
787                        .find_map(|(i, d)| if d.frames_left == 0 { Some(i) } else { None })
788                        .unwrap();
789
790                    let n_channels = self.num_channels_filled(num_out_channels);
791
792                    let fade_out_frames = stop_declicker_buffers.frames();
793
794                    self.stop_declickers[declicker_i].frames_left = fade_out_frames;
795                    self.stop_declickers[declicker_i].channels = n_channels;
796
797                    let mut tmp_buffers = stop_declicker_buffers
798                        .instance_mut(declicker_i, n_channels, fade_out_frames)
799                        .unwrap();
800
801                    self.process_internal(&mut tmp_buffers, fade_out_frames, false, extra);
802
803                    self.num_active_stop_declickers += 1;
804                }
805
806                self.stop_declicker_buffers = Some(stop_declicker_buffers);
807            }
808        }
809
810        if let Some(state) = &mut self.loaded_sample_state {
811            state.playhead_frames = 0;
812            state.num_times_looped_back = 0;
813        }
814
815        self.declicker.reset_to_1();
816
817        if let Some(resampler) = &mut self.resampler {
818            resampler.reset();
819        }
820    }
821
822    fn load_sample(&mut self, sample: ArcGc<dyn SampleResource>, num_out_channels: usize) {
823        let mut gain = self.params.volume.amp_clamped(self.min_gain);
824        if gain > 0.99999 && gain < 1.00001 {
825            gain = 1.0;
826        }
827
828        let sample_len_frames = sample.len_frames();
829        let sample_num_channels = sample.num_channels();
830
831        let sample_mono_to_stereo =
832            self.params.mono_to_stereo && num_out_channels > 1 && sample_num_channels.get() == 1;
833
834        self.loaded_sample_state = Some(LoadedSampleState {
835            sample,
836            sample_len_frames,
837            sample_num_channels,
838            sample_mono_to_stereo,
839            gain,
840            playhead_frames: 0,
841            num_times_looped_back: 0,
842        });
843    }
844}
845
846impl AudioNodeProcessor for SamplerProcessor {
847    fn process(
848        &mut self,
849        info: &ProcInfo,
850        buffers: ProcBuffers,
851        events: &mut ProcEvents,
852        extra: &mut ProcExtra,
853    ) -> ProcessStatus {
854        let mut sample_changed = self.is_first_process;
855        let mut repeat_mode_changed = false;
856        let mut speed_changed = false;
857        let mut volume_changed = false;
858        let mut new_playing: Option<bool> = if self.is_first_process {
859            Some(self.playing)
860        } else {
861            None
862        };
863
864        #[cfg(feature = "scheduled_events")]
865        let mut playback_instant: Option<EventInstant> = None;
866
867        #[cfg(not(feature = "scheduled_events"))]
868        for patch in events.drain_patches::<SamplerNode>() {
869            match patch {
870                SamplerNodePatch::Sample(_) => sample_changed = true,
871                SamplerNodePatch::Volume(_) => volume_changed = true,
872                SamplerNodePatch::Play(play) => {
873                    new_playing = Some(*play);
874                }
875                SamplerNodePatch::RepeatMode(_) => repeat_mode_changed = true,
876                SamplerNodePatch::Speed(_) => speed_changed = true,
877                SamplerNodePatch::MinGain(min_gain) => {
878                    self.min_gain = min_gain.max(0.0);
879                }
880                _ => {}
881            }
882
883            self.params.apply(patch);
884        }
885
886        #[cfg(feature = "scheduled_events")]
887        for (patch, timestamp) in events.drain_patches_with_timestamps::<SamplerNode>() {
888            match patch {
889                SamplerNodePatch::Sample(_) => sample_changed = true,
890                SamplerNodePatch::Volume(_) => volume_changed = true,
891                SamplerNodePatch::Play(play) => {
892                    playback_instant = timestamp;
893                    new_playing = Some(*play);
894                }
895                SamplerNodePatch::RepeatMode(_) => repeat_mode_changed = true,
896                SamplerNodePatch::Speed(_) => speed_changed = true,
897                SamplerNodePatch::MinGain(min_gain) => {
898                    self.min_gain = min_gain.max(0.0);
899                }
900                _ => {}
901            }
902
903            self.params.apply(patch);
904        }
905
906        if speed_changed {
907            self.speed = self.params.speed.max(MIN_PLAYBACK_SPEED);
908
909            if self.speed > 0.99999 && self.speed < 1.00001 {
910                self.speed = 1.0;
911            }
912        }
913
914        if volume_changed {
915            if let Some(loaded_sample) = &mut self.loaded_sample_state {
916                loaded_sample.gain = self.params.volume.amp_clamped(self.min_gain);
917                if loaded_sample.gain > 0.99999 && loaded_sample.gain < 1.00001 {
918                    loaded_sample.gain = 1.0;
919                }
920            }
921        }
922
923        if repeat_mode_changed {
924            if let Some(loaded_sample) = &mut self.loaded_sample_state {
925                loaded_sample.num_times_looped_back = 0;
926            }
927        }
928
929        if sample_changed {
930            self.stop(buffers.outputs.len(), extra);
931
932            #[cfg(feature = "scheduled_events")]
933            if new_playing == Some(true) && playback_instant.is_none() {
934                if let Some(queued_playback_instant) = self.queued_playback_instant.take() {
935                    if queued_playback_instant.to_samples(info).is_some() {
936                        playback_instant = Some(queued_playback_instant);
937                    }
938                }
939            }
940
941            self.loaded_sample_state = None;
942
943            if let Some(sample) = &self.params.sample {
944                self.load_sample(ArcGc::clone(sample), buffers.outputs.len());
945            }
946        }
947
948        if let Some(mut new_playing) = new_playing {
949            self.paused = false;
950
951            if new_playing {
952                let mut playhead_frames_at_play_instant = None;
953
954                if self.params.play_from == PlayFrom::Resume {
955                    // Resume
956                    if self.playing && !self.is_first_process {
957                        // Sample is already playing, no need to do anything.
958                        #[cfg(feature = "scheduled_events")]
959                        {
960                            self.queued_playback_instant = None;
961                        }
962                    } else if let Some(loaded_sample_state) = &self.loaded_sample_state {
963                        playhead_frames_at_play_instant = Some(loaded_sample_state.playhead_frames);
964                    }
965                } else {
966                    // Play from the given playhead
967                    if let Some(loaded_sample_state) = &mut self.loaded_sample_state {
968                        loaded_sample_state.num_times_looped_back = 0;
969                        playhead_frames_at_play_instant =
970                            Some(self.params.play_from.as_frames(info.sample_rate).unwrap());
971                    } else {
972                        #[cfg(feature = "scheduled_events")]
973                        {
974                            self.queued_playback_instant = playback_instant;
975                        }
976                    }
977                }
978
979                if let Some(playhead_frames_at_play_instant) = playhead_frames_at_play_instant {
980                    let loaded_sample_state = self.loaded_sample_state.as_mut().unwrap();
981                    let prev_playhead_frames = loaded_sample_state.playhead_frames;
982
983                    #[cfg(feature = "scheduled_events")]
984                    let mut new_playhead_frames = if let Some(playback_instant) = playback_instant {
985                        let playback_instant_samples = playback_instant
986                            .to_samples(info)
987                            .unwrap_or(info.clock_samples);
988                        let delay = if playback_instant_samples < info.clock_samples {
989                            (info.clock_samples - playback_instant_samples).0 as u64
990                        } else {
991                            0
992                        };
993
994                        playhead_frames_at_play_instant + delay
995                    } else {
996                        playhead_frames_at_play_instant
997                    };
998
999                    #[cfg(not(feature = "scheduled_events"))]
1000                    let mut new_playhead_frames = playhead_frames_at_play_instant;
1001
1002                    if new_playhead_frames >= loaded_sample_state.sample_len_frames {
1003                        match self.params.repeat_mode {
1004                            RepeatMode::PlayOnce => {
1005                                new_playhead_frames = loaded_sample_state.sample_len_frames
1006                            }
1007                            RepeatMode::RepeatEndlessly => {
1008                                while new_playhead_frames >= loaded_sample_state.sample_len_frames {
1009                                    new_playhead_frames -= loaded_sample_state.sample_len_frames;
1010                                    loaded_sample_state.num_times_looped_back += 1;
1011                                }
1012                            }
1013                            RepeatMode::RepeatMultiple {
1014                                num_times_to_repeat,
1015                            } => {
1016                                while new_playhead_frames >= loaded_sample_state.sample_len_frames {
1017                                    if loaded_sample_state.num_times_looped_back
1018                                        == num_times_to_repeat as u64
1019                                    {
1020                                        new_playhead_frames = loaded_sample_state.sample_len_frames;
1021                                        break;
1022                                    }
1023
1024                                    new_playhead_frames -= loaded_sample_state.sample_len_frames;
1025                                    loaded_sample_state.num_times_looped_back += 1;
1026                                }
1027                            }
1028                        }
1029                    }
1030
1031                    if prev_playhead_frames != new_playhead_frames {
1032                        self.stop(buffers.outputs.len(), extra);
1033
1034                        self.loaded_sample_state.as_mut().unwrap().playhead_frames =
1035                            new_playhead_frames;
1036
1037                        self.shared_state
1038                            .sample_playhead_frames
1039                            .store(new_playhead_frames, Ordering::Relaxed);
1040                    }
1041
1042                    if new_playhead_frames
1043                        == self.loaded_sample_state.as_ref().unwrap().sample_len_frames
1044                    {
1045                        self.shared_state
1046                            .finished
1047                            .store(self.params.play.id(), Ordering::Relaxed);
1048
1049                        new_playing = false;
1050                    } else if new_playhead_frames != 0
1051                        || (self.num_active_stop_declickers > 0 && self.params.crossfade_on_seek)
1052                    {
1053                        self.declicker.reset_to_0();
1054                        self.declicker.fade_to_1(&extra.declick_values);
1055                    } else {
1056                        self.declicker.reset_to_1();
1057                    }
1058
1059                    #[cfg(feature = "scheduled_events")]
1060                    {
1061                        self.queued_playback_instant = None;
1062                    }
1063                }
1064            } else {
1065                if self.params.play_from == PlayFrom::Resume {
1066                    // Pause
1067                    self.declicker.fade_to_0(&extra.declick_values);
1068                    self.paused = true;
1069                } else {
1070                    // Stop
1071                    self.stop(buffers.outputs.len(), extra);
1072                    self.shared_state
1073                        .finished
1074                        .store(self.params.play.id(), Ordering::Relaxed);
1075                }
1076            }
1077
1078            self.playing = new_playing;
1079        }
1080
1081        self.is_first_process = false;
1082
1083        self.shared_state.playback_state.store(
1084            if self.playing {
1085                SharedPlaybackState::Playing
1086            } else if self.paused {
1087                SharedPlaybackState::Paused
1088            } else {
1089                SharedPlaybackState::Stopped
1090            } as u32,
1091            Ordering::Relaxed,
1092        );
1093
1094        let currently_processing_sample = self.currently_processing_sample();
1095
1096        if !currently_processing_sample && self.num_active_stop_declickers == 0 {
1097            return ProcessStatus::ClearAllOutputs;
1098        }
1099
1100        let mut num_filled_channels = 0;
1101
1102        if currently_processing_sample && self.params.sample.is_some() {
1103            let sample_state = self.loaded_sample_state.as_ref().unwrap();
1104
1105            let looping = self
1106                .params
1107                .repeat_mode
1108                .do_loop(sample_state.num_times_looped_back);
1109
1110            let (finished, n_channels) =
1111                self.process_internal(buffers.outputs, info.frames, looping, extra);
1112
1113            num_filled_channels = n_channels;
1114
1115            self.shared_state.sample_playhead_frames.store(
1116                self.loaded_sample_state.as_ref().unwrap().playhead_frames,
1117                Ordering::Relaxed,
1118            );
1119
1120            if finished {
1121                self.playing = false;
1122
1123                self.shared_state
1124                    .playback_state
1125                    .store(SharedPlaybackState::Stopped as u32, Ordering::Relaxed);
1126                self.shared_state
1127                    .finished
1128                    .store(self.params.play.id(), Ordering::Relaxed);
1129            }
1130        }
1131
1132        for (i, out_buf) in buffers
1133            .outputs
1134            .iter_mut()
1135            .enumerate()
1136            .skip(num_filled_channels)
1137        {
1138            if !info.out_silence_mask.is_channel_silent(i) {
1139                out_buf[..info.frames].fill(0.0);
1140            }
1141        }
1142
1143        if self.num_active_stop_declickers > 0 {
1144            let tmp_buffers = self.stop_declicker_buffers.as_ref().unwrap();
1145            let fade_out_frames = tmp_buffers.frames();
1146
1147            for (declicker_i, declicker) in self.stop_declickers.iter_mut().enumerate() {
1148                if declicker.frames_left == 0 {
1149                    continue;
1150                }
1151
1152                let tmp_buffers = tmp_buffers
1153                    .instance(declicker_i, declicker.channels, fade_out_frames)
1154                    .unwrap();
1155
1156                let copy_frames = info.frames.min(declicker.frames_left);
1157                let start_frame = fade_out_frames - declicker.frames_left;
1158
1159                for (out_buf, tmp_buf) in buffers.outputs.iter_mut().zip(tmp_buffers.iter()) {
1160                    for (os, &ts) in out_buf[..copy_frames]
1161                        .iter_mut()
1162                        .zip(tmp_buf[start_frame..start_frame + copy_frames].iter())
1163                    {
1164                        *os += ts;
1165                    }
1166                }
1167
1168                declicker.frames_left -= copy_frames;
1169                if declicker.frames_left == 0 {
1170                    self.num_active_stop_declickers -= 1;
1171                }
1172
1173                num_filled_channels = num_filled_channels.max(declicker.channels);
1174            }
1175        }
1176
1177        let out_silence_mask = if num_filled_channels >= buffers.outputs.len() {
1178            SilenceMask::NONE_SILENT
1179        } else {
1180            let mut mask = SilenceMask::new_all_silent(buffers.outputs.len());
1181            for i in 0..num_filled_channels {
1182                mask.set_channel(i, false);
1183            }
1184            mask
1185        };
1186
1187        ProcessStatus::OutputsModifiedWithMask(MaskType::Silence(out_silence_mask))
1188    }
1189
1190    fn new_stream(&mut self, stream_info: &StreamInfo, _context: &mut ProcStreamCtx) {
1191        if stream_info.sample_rate != stream_info.prev_sample_rate {
1192            self.stop_declicker_buffers = if self.config.num_declickers == 0 {
1193                None
1194            } else {
1195                Some(InstanceBuffer::<f32, MAX_OUT_CHANNELS>::new(
1196                    self.config.num_declickers as usize,
1197                    NonZeroUsize::new(self.config.channels.get().get() as usize).unwrap(),
1198                    stream_info.declick_frames.get() as usize,
1199                ))
1200            };
1201
1202            // The sample rate has changed, meaning that the sample resources now have
1203            // the incorrect sample rate and the user must reload them.
1204            self.params.sample = None;
1205            self.loaded_sample_state = None;
1206            self.playing = false;
1207            self.paused = false;
1208            self.shared_state
1209                .playback_state
1210                .store(SharedPlaybackState::Stopped as u32, Ordering::Relaxed);
1211            self.shared_state.finished.store(0, Ordering::Relaxed);
1212        }
1213    }
1214}
1215
1216struct SharedState {
1217    sample_playhead_frames: AtomicU64,
1218    playback_state: AtomicU32,
1219    finished: AtomicU64,
1220}
1221
1222impl Default for SharedState {
1223    fn default() -> Self {
1224        Self {
1225            sample_playhead_frames: AtomicU64::new(0),
1226            playback_state: AtomicU32::new(SharedPlaybackState::Stopped as u32),
1227            finished: AtomicU64::new(0),
1228        }
1229    }
1230}
1231
1232#[repr(u32)]
1233#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1234enum SharedPlaybackState {
1235    Stopped = 0,
1236    Paused,
1237    Playing,
1238}
1239
1240impl SharedPlaybackState {
1241    fn from_u32(val: u32) -> Self {
1242        match val {
1243            1 => Self::Paused,
1244            2 => Self::Playing,
1245            _ => Self::Stopped,
1246        }
1247    }
1248}
1249
1250struct LoadedSampleState {
1251    sample: ArcGc<dyn SampleResource>,
1252    sample_len_frames: u64,
1253    sample_num_channels: NonZeroUsize,
1254    sample_mono_to_stereo: bool,
1255    gain: f32,
1256    playhead_frames: u64,
1257    num_times_looped_back: u64,
1258}
1259
1260#[derive(Default, Clone, Copy)]
1261struct StopDeclickerState {
1262    frames_left: usize,
1263    channels: usize,
1264}
1265
1266struct Resampler {
1267    fract_in_frame: f64,
1268    is_first_process: bool,
1269    prev_speed: f64,
1270    _quality: PlaybackSpeedQuality,
1271    wraparound_buffer: [[f32; 2]; MAX_OUT_CHANNELS],
1272}
1273
1274impl Resampler {
1275    pub fn new(quality: PlaybackSpeedQuality) -> Self {
1276        Self {
1277            fract_in_frame: 0.0,
1278            is_first_process: true,
1279            prev_speed: 1.0,
1280            _quality: quality,
1281            wraparound_buffer: [[0.0; 2]; MAX_OUT_CHANNELS],
1282        }
1283    }
1284
1285    pub fn resample_linear(
1286        &mut self,
1287        out_buffers: &mut [&mut [f32]],
1288        out_buffer_range: Range<usize>,
1289        extra: &mut ProcExtra,
1290        processor: &mut SamplerProcessor,
1291        looping: bool,
1292    ) -> (bool, usize) {
1293        let total_out_frames = out_buffer_range.end - out_buffer_range.start;
1294
1295        assert_ne!(total_out_frames, 0);
1296
1297        let in_frame_start = if self.is_first_process {
1298            self.prev_speed = processor.speed;
1299            self.fract_in_frame = 0.0;
1300
1301            0.0
1302        } else {
1303            self.fract_in_frame + processor.speed
1304        };
1305
1306        let out_frame_to_in_frame = |out_frame: f64, in_frame_start: f64, speed: f64| -> f64 {
1307            in_frame_start + (out_frame * speed)
1308        };
1309
1310        // The function which maps the output frame to the input frame is given by
1311        // the kinematic equation:
1312        //
1313        // in_frame = in_frame_start + (out_frame * start_speed) + (0.5 * accel * out_frame^2)
1314        //      where: accel = (end_speed - start_speed)
1315        let out_frame_to_in_frame_with_accel =
1316            |out_frame: f64, in_frame_start: f64, start_speed: f64, half_accel: f64| -> f64 {
1317                in_frame_start + (out_frame * start_speed) + (out_frame * out_frame * half_accel)
1318            };
1319
1320        let num_channels = processor.num_channels_filled(out_buffers.len());
1321        let copy_start = if self.is_first_process { 0 } else { 2 };
1322        let mut finished_playing = false;
1323
1324        if self.prev_speed == processor.speed {
1325            self.resample_linear_inner(
1326                out_frame_to_in_frame,
1327                in_frame_start,
1328                self.prev_speed,
1329                out_buffer_range.clone(),
1330                processor,
1331                extra,
1332                looping,
1333                copy_start,
1334                num_channels,
1335                out_buffers,
1336                out_buffer_range.start,
1337                &mut finished_playing,
1338            );
1339        } else {
1340            let half_accel = 0.5 * (processor.speed - self.prev_speed) / total_out_frames as f64;
1341
1342            self.resample_linear_inner(
1343                |out_frame: f64, in_frame_start: f64, speed: f64| {
1344                    out_frame_to_in_frame_with_accel(out_frame, in_frame_start, speed, half_accel)
1345                },
1346                in_frame_start,
1347                self.prev_speed,
1348                out_buffer_range.clone(),
1349                processor,
1350                extra,
1351                looping,
1352                copy_start,
1353                num_channels,
1354                out_buffers,
1355                out_buffer_range.start,
1356                &mut finished_playing,
1357            );
1358        }
1359
1360        self.prev_speed = processor.speed;
1361        self.is_first_process = false;
1362
1363        (finished_playing, num_channels)
1364    }
1365
1366    fn resample_linear_inner<OutToInFrame>(
1367        &mut self,
1368        out_to_in_frame: OutToInFrame,
1369        in_frame_start: f64,
1370        speed: f64,
1371        out_buffer_range: Range<usize>,
1372        processor: &mut SamplerProcessor,
1373        extra: &mut ProcExtra,
1374        looping: bool,
1375        mut copy_start: usize,
1376        num_channels: usize,
1377        out_buffers: &mut [&mut [f32]],
1378        out_buffer_start: usize,
1379        finished_playing: &mut bool,
1380    ) where
1381        OutToInFrame: Fn(f64, f64, f64) -> f64,
1382    {
1383        let mut scratch_buffers = extra.scratch_buffers.all_mut();
1384
1385        let total_out_frames = out_buffer_range.end - out_buffer_range.start;
1386        let output_frame_end = (total_out_frames - 1) as f64;
1387
1388        let input_frame_end = out_to_in_frame(output_frame_end, in_frame_start, speed);
1389        let input_frames_needed = input_frame_end.trunc() as usize + 2;
1390
1391        let mut input_frames_processed = 0;
1392        let mut output_frames_processed = 0;
1393        while output_frames_processed < total_out_frames {
1394            let input_frames =
1395                (input_frames_needed - input_frames_processed).min(processor.max_block_frames);
1396
1397            if input_frames > copy_start {
1398                let (finished, _) = processor.copy_from_sample(
1399                    &mut scratch_buffers[..num_channels],
1400                    copy_start..input_frames,
1401                    looping,
1402                );
1403                if finished {
1404                    *finished_playing = true;
1405                }
1406            }
1407
1408            let max_block_frames_minus_1 = processor.max_block_frames - 1;
1409            let out_ch_start = out_buffer_start + output_frames_processed;
1410
1411            let mut out_frames_count = 0;
1412
1413            // Have an optimized loop for stereo audio.
1414            if num_channels == 2 {
1415                let mut last_in_frame = 0;
1416                let mut last_fract_frame = 0.0;
1417
1418                let (out_ch_0, out_ch_1) = out_buffers.split_first_mut().unwrap();
1419                let (r_ch_0, r_ch_1) = scratch_buffers.split_first_mut().unwrap();
1420
1421                let out_ch_0 = &mut out_ch_0[out_ch_start..out_buffer_range.end];
1422                let out_ch_1 = &mut out_ch_1[0][out_ch_start..out_buffer_range.end];
1423
1424                let r_ch_0 = &mut r_ch_0[..processor.max_block_frames];
1425                let r_ch_1 = &mut r_ch_1[0][..processor.max_block_frames];
1426
1427                if copy_start > 0 {
1428                    r_ch_0[0] = self.wraparound_buffer[0][0];
1429                    r_ch_1[0] = self.wraparound_buffer[1][0];
1430
1431                    r_ch_0[1] = self.wraparound_buffer[0][1];
1432                    r_ch_1[1] = self.wraparound_buffer[1][1];
1433                }
1434
1435                for (i, (out_s_0, out_s_1)) in
1436                    out_ch_0.iter_mut().zip(out_ch_1.iter_mut()).enumerate()
1437                {
1438                    let out_frame = (i + output_frames_processed) as f64;
1439
1440                    let in_frame_f64 = out_to_in_frame(out_frame, in_frame_start, speed);
1441
1442                    let in_frame_usize = in_frame_f64.trunc() as usize - input_frames_processed;
1443                    let fract_frame = in_frame_f64.fract();
1444
1445                    if in_frame_usize >= max_block_frames_minus_1 {
1446                        break;
1447                    }
1448
1449                    let s0_0 = r_ch_0[in_frame_usize];
1450                    let s0_1 = r_ch_1[in_frame_usize];
1451
1452                    let s1_0 = r_ch_0[in_frame_usize + 1];
1453                    let s1_1 = r_ch_1[in_frame_usize + 1];
1454
1455                    *out_s_0 = s0_0 + ((s1_0 - s0_0) * fract_frame as f32);
1456                    *out_s_1 = s0_1 + ((s1_1 - s0_1) * fract_frame as f32);
1457
1458                    last_in_frame = in_frame_usize;
1459                    last_fract_frame = fract_frame;
1460
1461                    out_frames_count += 1;
1462                }
1463
1464                self.wraparound_buffer[0][0] = r_ch_0[last_in_frame];
1465                self.wraparound_buffer[1][0] = r_ch_1[last_in_frame];
1466
1467                self.wraparound_buffer[0][1] = r_ch_0[last_in_frame + 1];
1468                self.wraparound_buffer[1][1] = r_ch_1[last_in_frame + 1];
1469
1470                self.fract_in_frame = last_fract_frame;
1471            } else {
1472                for ((out_ch, r_ch), w_ch) in out_buffers[..num_channels]
1473                    .iter_mut()
1474                    .zip(scratch_buffers[..num_channels].iter_mut())
1475                    .zip(self.wraparound_buffer[..num_channels].iter_mut())
1476                {
1477                    // Hint to compiler to optimize loop.
1478                    assert_eq!(r_ch.len(), processor.max_block_frames);
1479
1480                    if copy_start > 0 {
1481                        r_ch[0] = w_ch[0];
1482                        r_ch[1] = w_ch[1];
1483                    }
1484
1485                    let mut last_in_frame = 0;
1486                    let mut last_fract_frame = 0.0;
1487                    let mut out_frames_ch_count = 0;
1488                    for (i, out_s) in out_ch[out_ch_start..out_buffer_range.end]
1489                        .iter_mut()
1490                        .enumerate()
1491                    {
1492                        let out_frame = (i + output_frames_processed) as f64;
1493
1494                        let in_frame_f64 = out_to_in_frame(out_frame, in_frame_start, speed);
1495
1496                        let in_frame_usize = in_frame_f64.trunc() as usize - input_frames_processed;
1497                        last_fract_frame = in_frame_f64.fract();
1498
1499                        if in_frame_usize >= max_block_frames_minus_1 {
1500                            break;
1501                        }
1502
1503                        let s0 = r_ch[in_frame_usize];
1504                        let s1 = r_ch[in_frame_usize + 1];
1505
1506                        *out_s = s0 + ((s1 - s0) * last_fract_frame as f32);
1507
1508                        last_in_frame = in_frame_usize;
1509                        out_frames_ch_count += 1;
1510                    }
1511
1512                    w_ch[0] = r_ch[last_in_frame];
1513                    w_ch[1] = r_ch[last_in_frame + 1];
1514
1515                    self.fract_in_frame = last_fract_frame;
1516                    out_frames_count = out_frames_ch_count;
1517                }
1518            }
1519
1520            output_frames_processed += out_frames_count;
1521            input_frames_processed += input_frames - 2;
1522
1523            copy_start = 2;
1524        }
1525    }
1526
1527    pub fn reset(&mut self) {
1528        self.fract_in_frame = 0.0;
1529        self.is_first_process = true;
1530    }
1531}