1use 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#[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 pub channels: NonZeroChannelCount,
54 pub num_declickers: u32,
61 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#[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 LinearFast,
89 }
91
92#[derive(Clone, Diff, Patch, PartialEq)]
96#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
97pub struct SamplerNode {
98 pub sample: Option<ArcGc<dyn SampleResource>>,
100
101 pub volume: Volume,
109
110 pub play: Notify<bool>,
113
114 pub play_from: PlayFrom,
117
118 pub repeat_mode: RepeatMode,
120
121 pub speed: f64,
126
127 pub mono_to_stereo: bool,
131 pub crossfade_on_seek: bool,
136 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 pub fn set_sample(&mut self, sample: ArcGc<dyn SampleResource>) {
179 self.sample = Some(sample);
180 }
181
182 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 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 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 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 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 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 pub fn start_or_restart(&mut self) {
234 self.play_from = PlayFrom::BEGINNING;
235 *self.play = true;
236 }
237
238 pub fn start_from(&mut self, from: PlayFrom) {
240 self.play_from = from;
241 *self.play = true;
242 }
243
244 pub fn pause(&mut self) {
246 self.play_from = PlayFrom::Resume;
247 *self.play = false;
248 }
249
250 pub fn resume(&mut self) {
252 *self.play = true;
253 }
254
255 pub fn stop(&mut self) {
260 self.play_from = PlayFrom::BEGINNING;
261 *self.play = false;
262 }
263
264 pub fn start_or_restart_requested(&self) -> bool {
266 *self.play && self.play_from == PlayFrom::BEGINNING
267 }
268
269 pub fn resume_requested(&self) -> bool {
271 *self.play && self.play_from == PlayFrom::Resume
272 }
273
274 pub fn pause_requested(&self) -> bool {
276 !*self.play && self.play_from == PlayFrom::Resume
277 }
278
279 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 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 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 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 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 pub fn playing(&self) -> bool {
364 SharedPlaybackState::from_u32(self.shared_state.playback_state.load(Ordering::Relaxed))
365 == SharedPlaybackState::Playing
366 }
367
368 pub fn paused(&self) -> bool {
370 SharedPlaybackState::from_u32(self.shared_state.playback_state.load(Ordering::Relaxed))
371 == SharedPlaybackState::Paused
372 }
373
374 pub fn stopped(&self) -> bool {
377 SharedPlaybackState::from_u32(self.shared_state.playback_state.load(Ordering::Relaxed))
378 == SharedPlaybackState::Stopped
379 }
380
381 pub fn mark_playing(&self) {
384 self.shared_state
385 .playback_state
386 .store(SharedPlaybackState::Playing as u32, Ordering::Relaxed);
387 }
388
389 pub fn mark_paused(&self) {
392 self.shared_state
393 .playback_state
394 .store(SharedPlaybackState::Paused as u32, Ordering::Relaxed);
395 }
396
397 pub fn mark_stopped(&self) {
400 self.shared_state
401 .playback_state
402 .store(SharedPlaybackState::Stopped as u32, Ordering::Relaxed);
403 }
404
405 pub fn finished(&self) -> u64 {
407 self.shared_state.finished.load(Ordering::Relaxed)
408 }
409
410 pub fn clear_finished(&self) {
412 self.shared_state.finished.store(0, Ordering::Relaxed);
413 }
414
415 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 u64::MAX - 4
430 } else {
431 u64::MAX - 5
433 }
434 } else {
435 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#[derive(Debug, Clone, Copy, PartialEq, RealtimeClone)]
455#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
456pub enum PlayFrom {
457 Resume,
460 Seconds(f64),
463 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#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Diff, Patch)]
527#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
528pub enum RepeatMode {
529 #[default]
531 PlayOnce,
532 RepeatMultiple { num_times_to_repeat: u32 },
534 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 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 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 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 self.declicker.fade_to_0(&extra.declick_values);
779
780 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 if self.playing && !self.is_first_process {
957 #[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 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 self.declicker.fade_to_0(&extra.declick_values);
1068 self.paused = true;
1069 } else {
1070 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 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 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 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 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}