1use firewheel_core::clock::{DurationSamples, DurationSeconds};
12use firewheel_core::collector::{OwnedGc, OwnedGcUnsized};
13use firewheel_core::node::{NodeError, ProcBuffers, ProcExtra, ProcStreamCtx};
14
15use bevy_platform::sync::{Arc, Mutex};
16use bevy_platform::time::Instant;
17use core::{
18 num::{NonZeroU32, NonZeroUsize},
19 ops::Range,
20};
21use firewheel_core::diff::{EventQueue, NotifyID, PatchError, PathBuilder, RealtimeClone};
22use smallvec::SmallVec;
23use triple_buffer::{Input, Output};
24
25#[cfg(not(feature = "std"))]
26use bevy_platform::prelude::Box;
27#[cfg(not(feature = "std"))]
28use num_traits::Float;
29
30use firewheel_core::{
31 StreamInfo,
32 channel_config::{ChannelConfig, ChannelCount, NonZeroChannelCount},
33 clock::InstantSeconds,
34 collector::ArcGc,
35 diff::{Diff, Notify, ParamPath, Patch},
36 dsp::{
37 buffer::InstanceBuffer,
38 declick::{DeclickFadeCurve, Declicker},
39 volume::{DEFAULT_MIN_AMP, Volume},
40 },
41 event::{NodeEventType, ParamData, ProcEvents},
42 mask::{MaskType, SilenceMask},
43 node::{
44 AudioNode, AudioNodeInfo, AudioNodeProcessor, ConstructProcessorContext, ProcInfo,
45 ProcessStatus,
46 },
47 sample_resource::SampleResource,
48};
49
50#[cfg(feature = "scheduled_events")]
51use firewheel_core::clock::EventInstant;
52
53pub const MAX_OUT_CHANNELS: usize = 8;
54pub const DEFAULT_NUM_DECLICKERS: usize = 2;
55pub const MIN_PLAYBACK_SPEED: f64 = 0.0000001;
56
57mod resampler;
58mod resource;
59
60pub use self::resource::{SamplerNodeResource, StreamedSample};
61
62use self::resampler::Resampler;
63
64pub type PlaybackID = NotifyID;
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
69#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71pub struct SamplerConfig {
72 pub channels: NonZeroChannelCount,
76 pub num_declickers: u32,
83 pub speed_quality: PlaybackSpeedQuality,
86}
87
88impl Default for SamplerConfig {
89 fn default() -> Self {
90 Self {
91 channels: NonZeroChannelCount::STEREO,
92 num_declickers: DEFAULT_NUM_DECLICKERS as u32,
93 speed_quality: PlaybackSpeedQuality::default(),
94 }
95 }
96}
97
98#[non_exhaustive]
101#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
102#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
104pub enum PlaybackSpeedQuality {
105 #[default]
106 LinearFast,
111 }
113
114#[derive(Debug, Clone, Copy, Diff, Patch, PartialEq)]
118#[cfg_attr(feature = "bevy", derive(bevy_ecs::prelude::Component))]
119#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
120#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121pub struct SamplerNode {
122 pub volume: Volume,
130
131 #[cfg_attr(feature = "serde", serde(skip))]
134 pub play: Notify<bool>,
135
136 pub play_from: PlayFrom,
139
140 pub repeat_mode: RepeatMode,
142
143 pub speed: f64,
148
149 pub mono_to_stereo: bool,
153 pub crossfade_on_seek: bool,
158 pub min_gain: f32,
164}
165
166impl Default for SamplerNode {
167 fn default() -> Self {
168 Self {
169 volume: Volume::default(),
170 play: Default::default(),
171 play_from: PlayFrom::default(),
172 repeat_mode: RepeatMode::default(),
173 speed: 1.0,
174 mono_to_stereo: true,
175 crossfade_on_seek: true,
176 min_gain: DEFAULT_MIN_AMP,
177 }
178 }
179}
180
181impl SamplerNode {
182 pub fn clear_sample_event() -> NodeEventType {
184 NodeEventType::Custom(OwnedGc::new(Box::<Option<SamplerNodeResource>>::new(None)))
185 }
186
187 pub fn set_sample_event<T: SampleResource + Send + Sync + 'static>(sample: T) -> NodeEventType {
190 Self::set_resource_event(SamplerNodeResource::from_sample(sample))
191 }
192
193 pub fn set_streamed_sample_event<T: StreamedSample>(sample: T) -> NodeEventType {
196 Self::set_resource_event(SamplerNodeResource::from_streamed(sample))
197 }
198
199 pub fn set_dyn_sample_event(
202 sample: ArcGc<dyn SampleResource + Send + Sync + 'static>,
203 ) -> NodeEventType {
204 Self::set_resource_event(sample.into())
205 }
206
207 pub fn set_dyn_streamed_sample_event(
210 sample: OwnedGcUnsized<dyn StreamedSample>,
211 ) -> NodeEventType {
212 Self::set_resource_event(sample.into())
213 }
214
215 pub fn set_resource_event(sample: SamplerNodeResource) -> NodeEventType {
217 NodeEventType::Custom(OwnedGc::new(Box::new(Some(sample))))
218 }
219
220 pub fn sync_volume_event(&self) -> NodeEventType {
222 NodeEventType::Param {
223 data: ParamData::Volume(self.volume),
224 path: ParamPath::Single(0),
225 }
226 }
227
228 pub fn sync_play_event(&self) -> NodeEventType {
230 let mut bytes: [u8; 20] = [0; 20];
233 bytes[0..core::mem::size_of::<u64>()].copy_from_slice(&self.play.id().0.to_ne_bytes());
234 bytes[core::mem::size_of::<u64>()] = if *self.play { 1 } else { 0 };
235
236 NodeEventType::Param {
237 data: ParamData::CustomBytes(bytes),
238 path: ParamPath::Single(1),
239 }
240 }
241
242 pub fn playback_id(&self) -> PlaybackID {
244 self.play.id()
245 }
246
247 pub fn sync_play_from_event(&self) -> NodeEventType {
249 NodeEventType::Param {
250 data: self.play_from.as_param_data(),
251 path: ParamPath::Single(2),
252 }
253 }
254
255 pub fn sync_repeat_mode_event(&self) -> NodeEventType {
257 NodeEventType::Param {
258 data: ParamData::any(self.repeat_mode),
259 path: ParamPath::Single(3),
260 }
261 }
262
263 pub fn sync_speed_event(&self) -> NodeEventType {
265 NodeEventType::Param {
266 data: ParamData::F64(self.speed),
267 path: ParamPath::Single(4),
268 }
269 }
270
271 pub fn sync_mono_to_stereo_event(&self) -> NodeEventType {
273 NodeEventType::Param {
274 data: ParamData::Bool(self.mono_to_stereo),
275 path: ParamPath::Single(5),
276 }
277 }
278
279 pub fn sync_crossfade_on_seek_event(&self) -> NodeEventType {
281 NodeEventType::Param {
282 data: ParamData::Bool(self.crossfade_on_seek),
283 path: ParamPath::Single(6),
284 }
285 }
286
287 pub fn sync_min_gain_event(&self) -> NodeEventType {
289 NodeEventType::Param {
290 data: ParamData::F32(self.min_gain),
291 path: ParamPath::Single(7),
292 }
293 }
294
295 pub fn start_or_restart(&mut self) {
299 self.play_from = PlayFrom::BEGINNING;
300 *self.play = true;
301 }
302
303 pub fn start_from(&mut self, from: PlayFrom) {
305 self.play_from = from;
306 *self.play = true;
307 }
308
309 pub fn pause(&mut self) {
311 self.play_from = PlayFrom::Resume;
312 *self.play = false;
313 }
314
315 pub fn resume(&mut self) {
317 *self.play = true;
318 }
319
320 pub fn stop(&mut self) {
325 self.play_from = PlayFrom::BEGINNING;
326 *self.play = false;
327 }
328
329 pub fn start_or_restart_requested(&self) -> bool {
331 *self.play && self.play_from == PlayFrom::BEGINNING
332 }
333
334 pub fn resume_requested(&self) -> bool {
336 *self.play && self.play_from == PlayFrom::Resume
337 }
338
339 pub fn pause_requested(&self) -> bool {
341 !*self.play && self.play_from == PlayFrom::Resume
342 }
343
344 pub fn stop_requested(&self) -> bool {
346 !*self.play && self.play_from != PlayFrom::Resume
347 }
348}
349
350#[derive(Clone)]
351pub struct SamplerState {
352 channel: Arc<Mutex<SharedChannel>>,
353}
354
355impl SamplerState {
356 fn new() -> Self {
357 Self {
358 channel: Arc::new(Mutex::new(SharedChannel::new())),
359 }
360 }
361
362 pub fn current_processor_state(&self) -> CurrentProcessorState {
365 *self.channel.lock().unwrap().proc_state_output.read()
366 }
367
368 pub fn playhead_frames(&self) -> DurationSamples {
371 DurationSamples(
372 self.channel
373 .lock()
374 .unwrap()
375 .proc_state_output
376 .read()
377 .playhead_frames as i64,
378 )
379 }
380
381 pub fn playhead_seconds(&self, sample_rate: NonZeroU32) -> DurationSeconds {
385 DurationSeconds(self.playhead_frames().0 as f64 / sample_rate.get() as f64)
386 }
387
388 pub fn playback_state(&self) -> PlaybackState {
390 self.channel
391 .lock()
392 .unwrap()
393 .proc_state_output
394 .read()
395 .playback_state
396 }
397
398 pub fn playback_state_and_id(&self) -> (PlaybackState, PlaybackID) {
404 let mut ch_guard = self.channel.lock().unwrap();
405 let state = ch_guard.proc_state_output.read();
406 (state.playback_state, state.playback_id)
407 }
408
409 pub fn playback_finished(&self, playback_id: PlaybackID) -> bool {
412 let (playback_state, id) = self.playback_state_and_id();
413 id == playback_id && playback_state == PlaybackState::Stopped
414 }
415
416 pub fn last_finished_playback_id(&self) -> PlaybackID {
418 self.channel
419 .lock()
420 .unwrap()
421 .proc_state_output
422 .read()
423 .last_finished_playback_id
424 }
425
426 pub fn currently_playing(&self) -> bool {
429 self.playback_state() == PlaybackState::Playing
430 }
431
432 pub fn currently_paused(&self) -> bool {
434 self.playback_state() == PlaybackState::Paused
435 }
436
437 pub fn currently_stopped(&self) -> bool {
440 self.playback_state() == PlaybackState::Stopped
441 }
442
443 pub fn playhead_frames_corrected(
450 &self,
451 update_instant: Option<Instant>,
452 sample_rate: NonZeroU32,
453 ) -> DurationSamples {
454 let (playhead_frames, playback_state) = {
455 let mut channel = self.channel.lock().unwrap();
456 let s = channel.proc_state_output.read();
457 (s.playhead_frames, s.playback_state)
458 };
459
460 let Some(update_instant) = update_instant else {
461 return DurationSamples(playhead_frames as i64);
462 };
463
464 if playback_state == PlaybackState::Playing {
465 DurationSamples(
466 playhead_frames as i64
467 + InstantSeconds(update_instant.elapsed().as_secs_f64())
468 .to_samples(sample_rate)
469 .0,
470 )
471 } else {
472 DurationSamples(playhead_frames as i64)
473 }
474 }
475
476 pub fn playhead_seconds_corrected(
482 &self,
483 update_instant: Option<Instant>,
484 sample_rate: NonZeroU32,
485 ) -> DurationSeconds {
486 DurationSeconds(
487 self.playhead_frames_corrected(update_instant, sample_rate)
488 .0 as f64
489 / sample_rate.get() as f64,
490 )
491 }
492}
493
494struct SharedChannel {
495 proc_state_output: Output<CurrentProcessorState>,
496 proc_state_input: Option<Input<CurrentProcessorState>>,
497}
498
499impl SharedChannel {
500 fn new() -> Self {
501 let (proc_state_input, proc_state_output) = triple_buffer::triple_buffer::<
502 CurrentProcessorState,
503 >(&CurrentProcessorState::default());
504
505 Self {
506 proc_state_input: Some(proc_state_input),
507 proc_state_output,
508 }
509 }
510}
511
512#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
514pub struct CurrentProcessorState {
515 pub playhead_frames: u64,
518 pub playback_id: PlaybackID,
521 pub last_finished_playback_id: PlaybackID,
523 pub playback_state: PlaybackState,
525 pub playback_age_frames: u64,
528 pub has_sample_resource: bool,
530}
531
532#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
534pub enum PlaybackState {
535 #[default]
536 Stopped,
539 Paused,
541 Playing,
543}
544
545#[derive(Debug, Clone, Copy, PartialEq, RealtimeClone)]
548#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
549#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
550pub enum PlayFrom {
551 Resume,
554 Seconds(f64),
557 Frames(u64),
561}
562
563impl PlayFrom {
564 pub const BEGINNING: Self = Self::Frames(0);
565
566 pub fn as_frames(&self, sample_rate: NonZeroU32) -> Option<u64> {
567 match *self {
568 Self::Resume => None,
569 Self::Seconds(seconds) => Some(if seconds <= 0.0 {
570 0
571 } else {
572 (seconds.floor() as u64 * sample_rate.get() as u64)
573 + (seconds.fract() * sample_rate.get() as f64).round() as u64
574 }),
575 Self::Frames(frames) => Some(frames),
576 }
577 }
578
579 pub fn as_param_data(&self) -> ParamData {
580 match self {
581 Self::Resume => ParamData::None,
582 Self::Seconds(s) => ParamData::F64(*s),
583 Self::Frames(f) => ParamData::U64(*f),
584 }
585 }
586}
587
588impl Default for PlayFrom {
589 fn default() -> Self {
590 Self::BEGINNING
591 }
592}
593
594impl Diff for PlayFrom {
595 fn diff<E: EventQueue>(&self, baseline: &Self, path: PathBuilder, event_queue: &mut E) {
596 if self != baseline {
597 match self {
598 Self::Resume => event_queue.push_param(ParamData::None, path),
599 Self::Seconds(seconds) => event_queue.push_param(*seconds, path),
600 Self::Frames(frames) => event_queue.push_param(*frames, path),
601 }
602 }
603 }
604}
605
606impl Patch for PlayFrom {
607 type Patch = Self;
608
609 fn patch(data: &ParamData, _path: &[u32]) -> Result<Self::Patch, PatchError> {
610 match data {
611 ParamData::None => Ok(PlayFrom::Resume),
612 ParamData::F64(s) => Ok(PlayFrom::Seconds(*s)),
613 ParamData::U64(f) => Ok(PlayFrom::Frames(*f)),
614 _ => Err(PatchError::InvalidData),
615 }
616 }
617
618 fn apply(&mut self, value: Self::Patch) {
619 *self = value;
620 }
621}
622
623#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Diff, Patch)]
625#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
626#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
627pub enum RepeatMode {
628 #[default]
630 PlayOnce,
631 RepeatMultiple { num_times_to_repeat: u32 },
633 RepeatEndlessly,
635}
636
637impl RepeatMode {
638 pub fn do_loop(&self, num_times_looped_back: u64) -> bool {
639 match self {
640 Self::PlayOnce => false,
641 &Self::RepeatMultiple {
642 num_times_to_repeat,
643 } => num_times_looped_back < num_times_to_repeat as u64,
644 Self::RepeatEndlessly => true,
645 }
646 }
647}
648
649impl AudioNode for SamplerNode {
650 type Configuration = SamplerConfig;
651
652 fn info(&self, config: &Self::Configuration) -> Result<AudioNodeInfo, NodeError> {
653 Ok(AudioNodeInfo::new()
654 .debug_name("sampler")
655 .channel_config(ChannelConfig {
656 num_inputs: ChannelCount::ZERO,
657 num_outputs: config.channels.get(),
658 })
659 .custom_state(SamplerState::new()))
660 }
661
662 fn construct_processor(
663 &self,
664 config: &Self::Configuration,
665 mut cx: ConstructProcessorContext,
666 ) -> Result<impl AudioNodeProcessor, NodeError> {
667 let stop_declicker_buffers = if config.num_declickers == 0 {
668 None
669 } else {
670 Some(InstanceBuffer::<f32>::new(
671 config.num_declickers as usize,
672 NonZeroUsize::new(config.channels.get().get() as usize).unwrap(),
673 cx.stream_info.declick_frames.get() as usize,
674 ))
675 };
676
677 let max_block_frames = cx.stream_info.max_block_frames.get() as usize;
678
679 let playing = *self.play;
680 let paused = !*self.play && self.play_from == PlayFrom::Resume;
681 let playback_state = if playing {
682 PlaybackState::Playing
683 } else if paused {
684 PlaybackState::Paused
685 } else {
686 PlaybackState::Stopped
687 };
688 let playback_id = if playing {
689 self.play.id()
690 } else {
691 PlaybackID::DANGLING
692 };
693
694 let proc_state = CurrentProcessorState {
695 playback_id,
696 playback_state,
697 playhead_frames: self
698 .play_from
699 .as_frames(cx.stream_info.sample_rate)
700 .unwrap_or_default(),
701 ..Default::default()
702 };
703 let mut channel = cx
704 .custom_state_mut::<SamplerState>()
705 .unwrap()
706 .channel
707 .lock()
708 .unwrap();
709 let mut shared_proc_state = if let Some(proc_state_input) = channel.proc_state_input.take()
710 {
711 proc_state_input
712 } else {
713 *channel = SharedChannel::new();
714 channel.proc_state_input.take().unwrap()
715 };
716 shared_proc_state.write(proc_state);
717
718 Ok(SamplerProcessor {
719 config: *config,
720 params: *self,
721 proc_state,
722 shared_proc_state,
723 loaded_sample_state: None,
724 declicker: Declicker::SettledAt1,
725 stop_declicker_buffers,
726 stop_declickers: smallvec::smallvec![StopDeclickerState::default(); config.num_declickers as usize],
727 num_active_stop_declickers: 0,
728 resampler: Some(Resampler::new(config.speed_quality)),
729 speed: self.speed.max(MIN_PLAYBACK_SPEED),
730 playing,
731 paused,
732 #[cfg(feature = "scheduled_events")]
733 queued_playback_instant: None,
734 min_gain: self.min_gain.max(0.0),
735 max_block_frames,
736 num_out_channels: config.channels.get().get() as usize,
737 is_first_process: true,
738 })
739 }
740}
741
742struct SamplerProcessor {
743 config: SamplerConfig,
744 params: SamplerNode,
745 proc_state: CurrentProcessorState,
746 shared_proc_state: Input<CurrentProcessorState>,
747
748 loaded_sample_state: Option<LoadedSampleState>,
749
750 declicker: Declicker,
751
752 playing: bool,
753 paused: bool,
754
755 stop_declicker_buffers: Option<InstanceBuffer<f32>>,
756 stop_declickers: SmallVec<[StopDeclickerState; DEFAULT_NUM_DECLICKERS]>,
757 num_active_stop_declickers: usize,
758
759 resampler: Option<Resampler>,
760 speed: f64,
761
762 #[cfg(feature = "scheduled_events")]
763 queued_playback_instant: Option<EventInstant>,
764
765 min_gain: f32,
766
767 max_block_frames: usize,
768 num_out_channels: usize,
769 is_first_process: bool,
770}
771
772impl SamplerProcessor {
773 fn sync_proc_state(&mut self) {
774 self.shared_proc_state.write(self.proc_state);
775 }
776
777 fn process_internal(
780 &mut self,
781 buffers: &mut [&mut [f32]],
782 frames: usize,
783 looping: bool,
784 extra: &mut ProcExtra,
785 ) -> (bool, usize) {
786 let (finished_playing, mut channels_filled) = if self.speed != 1.0 {
787 let mut resampler = self.resampler.take().unwrap();
789
790 let (finished_playing, channels_filled) =
791 resampler.resample_linear(buffers, 0..frames, extra, self, looping);
792
793 self.resampler = Some(resampler);
794
795 (finished_playing, channels_filled)
796 } else {
797 self.resampler.as_mut().unwrap().reset();
798
799 self.copy_from_sample(buffers, 0..frames, looping)
800 };
801
802 let Some(state) = self.loaded_sample_state.as_ref() else {
803 return (true, 0);
804 };
805
806 if !self.declicker.has_settled() {
807 self.declicker.process(
808 buffers,
809 0..frames,
810 &extra.declick_values,
811 state.gain,
812 DeclickFadeCurve::EqualPower3dB,
813 );
814 } else if state.gain != 1.0 {
815 for b in buffers[..channels_filled].iter_mut() {
816 for s in b[..frames].iter_mut() {
817 *s *= state.gain;
818 }
819 }
820 }
821
822 if state.sample_mono_to_stereo {
823 let (b0, b1) = buffers.split_first_mut().unwrap();
824 b1[0][..frames].copy_from_slice(&b0[..frames]);
825
826 channels_filled = 2;
827 }
828
829 (finished_playing, channels_filled)
830 }
831
832 fn copy_from_sample(
838 &mut self,
839 buffers: &mut [&mut [f32]],
840 range_in_buffer: Range<usize>,
841 looping: bool,
842 ) -> (bool, usize) {
843 let Some(state) = self.loaded_sample_state.as_mut() else {
844 return (true, 0);
845 };
846
847 assert!(state.playhead_frames <= state.sample_len_frames);
848
849 let block_frames = range_in_buffer.end - range_in_buffer.start;
850 let first_copy_frames =
851 if state.playhead_frames + block_frames as u64 > state.sample_len_frames {
852 (state.sample_len_frames - state.playhead_frames) as usize
853 } else {
854 block_frames
855 };
856
857 if first_copy_frames > 0 {
858 match &mut state.sample {
859 SamplerNodeResource::InMemory(sample) => {
860 sample.fill_buffers(
861 buffers,
862 range_in_buffer.start..range_in_buffer.start + first_copy_frames,
863 state.playhead_frames,
864 );
865 }
866 SamplerNodeResource::Streamed(_) => {
867 todo!()
868 }
869 }
870
871 state.playhead_frames += first_copy_frames as u64;
872 }
873
874 if first_copy_frames < block_frames {
875 if looping {
876 let mut frames_copied = first_copy_frames;
877
878 while frames_copied < block_frames {
879 let copy_frames = ((block_frames - frames_copied) as u64)
880 .min(state.sample_len_frames)
881 as usize;
882
883 match &mut state.sample {
884 SamplerNodeResource::InMemory(sample) => {
885 sample.fill_buffers(
886 buffers,
887 range_in_buffer.start + frames_copied
888 ..range_in_buffer.start + frames_copied + copy_frames,
889 0,
890 );
891 }
892 SamplerNodeResource::Streamed(_) => {
893 todo!()
894 }
895 }
896
897 state.playhead_frames = copy_frames as u64;
898 state.num_times_looped_back += 1;
899
900 frames_copied += copy_frames;
901 }
902 } else {
903 let n_channels = buffers.len().min(state.sample_num_channels.get());
904 for b in buffers[..n_channels].iter_mut() {
905 b[range_in_buffer.start + first_copy_frames..range_in_buffer.end].fill(0.0);
906 }
907
908 return (true, n_channels);
909 }
910 }
911
912 (false, buffers.len().min(state.sample_num_channels.get()))
913 }
914
915 fn currently_processing_sample(&self) -> bool {
916 if self.loaded_sample_state.is_none() {
917 false
918 } else {
919 self.playing || (self.paused && !self.declicker.has_settled())
920 }
921 }
922
923 fn num_channels_filled(&self) -> usize {
924 if let Some(state) = &self.loaded_sample_state {
925 if state.sample_mono_to_stereo {
926 2
927 } else {
928 state.sample_num_channels.get().min(self.num_out_channels)
929 }
930 } else {
931 0
932 }
933 }
934
935 fn stop(&mut self, extra: &mut ProcExtra) {
936 if self.currently_processing_sample() {
937 self.declicker.fade_to_0(&extra.declick_values);
941
942 if let Some(mut stop_declicker_buffers) = self.stop_declicker_buffers.take() {
944 if self.num_active_stop_declickers < stop_declicker_buffers.num_instances() {
945 let declicker_i = self
946 .stop_declickers
947 .iter()
948 .enumerate()
949 .find_map(|(i, d)| if d.frames_left == 0 { Some(i) } else { None })
950 .unwrap();
951
952 let n_channels = self.num_channels_filled();
953
954 let fade_out_frames = stop_declicker_buffers.frames();
955
956 self.stop_declickers[declicker_i].frames_left = fade_out_frames;
957 self.stop_declickers[declicker_i].channels = n_channels;
958
959 let mut tmp_buffers = stop_declicker_buffers
960 .instance_mut::<MAX_OUT_CHANNELS>(declicker_i, n_channels, fade_out_frames)
961 .unwrap();
962
963 self.process_internal(&mut tmp_buffers, fade_out_frames, false, extra);
964
965 self.num_active_stop_declickers += 1;
966 }
967
968 self.stop_declicker_buffers = Some(stop_declicker_buffers);
969 }
970 }
971
972 if let Some(state) = &mut self.loaded_sample_state {
973 state.playhead_frames = 0;
974 state.num_times_looped_back = 0;
975 }
976
977 self.declicker.reset_to_1();
978
979 if let Some(resampler) = &mut self.resampler {
980 resampler.reset();
981 }
982 }
983
984 fn load_sample(&mut self, sample: SamplerNodeResource) {
985 let mut gain = self.params.volume.amp_clamped(self.min_gain);
986 if gain > 0.99999 && gain < 1.00001 {
987 gain = 1.0;
988 }
989
990 let (sample_len_frames, sample_num_channels) = match &sample {
991 SamplerNodeResource::InMemory(s) => (s.len_frames(), s.num_channels()),
992 SamplerNodeResource::Streamed(s) => (s.len_frames(), s.num_channels()),
993 };
994
995 let sample_mono_to_stereo = self.params.mono_to_stereo
996 && self.num_out_channels > 1
997 && sample_num_channels.get() == 1;
998
999 self.loaded_sample_state = Some(LoadedSampleState {
1000 sample,
1001 sample_len_frames,
1002 sample_num_channels,
1003 sample_mono_to_stereo,
1004 gain,
1005 playhead_frames: 0,
1006 num_times_looped_back: 0,
1007 });
1008 }
1009}
1010
1011impl AudioNodeProcessor for SamplerProcessor {
1012 fn events(&mut self, info: &ProcInfo, events: &mut ProcEvents, extra: &mut ProcExtra) {
1013 let is_first_process = self.is_first_process;
1014 self.is_first_process = false;
1015
1016 let mut new_playing: Option<bool> = if is_first_process {
1017 Some(self.playing)
1018 } else {
1019 None
1020 };
1021 let mut new_sample = None;
1022 let mut repeat_mode_changed = false;
1023 let mut speed_changed = false;
1024 let mut volume_changed = false;
1025 let mut proc_state_changed = false;
1026
1027 #[cfg(feature = "scheduled_events")]
1028 let mut playback_instant: Option<EventInstant> = None;
1029
1030 #[cfg(feature = "scheduled_events")]
1031 for (mut event, timestamp) in events.drain_with_timestamps() {
1032 let mut s = None;
1033 if event.downcast_swap::<Option<SamplerNodeResource>>(&mut s) {
1034 new_sample = Some(s);
1035 continue;
1036 }
1037
1038 if let Some(patch) = SamplerNode::patch_event(&event) {
1039 match patch {
1040 SamplerNodePatch::Volume(_) => volume_changed = true,
1041 SamplerNodePatch::Play(play) => {
1042 playback_instant = timestamp;
1043 new_playing = Some(*play);
1044
1045 if *play {
1046 self.proc_state.last_finished_playback_id = self.proc_state.playback_id;
1047 self.proc_state.playback_id = play.id();
1048 proc_state_changed = true;
1049 }
1050 }
1051 SamplerNodePatch::RepeatMode(_) => repeat_mode_changed = true,
1052 SamplerNodePatch::Speed(_) => speed_changed = true,
1053 SamplerNodePatch::MinGain(min_gain) => {
1054 self.min_gain = min_gain.max(0.0);
1055 }
1056 _ => {}
1057 }
1058
1059 self.params.apply(patch);
1060 }
1061 }
1062
1063 #[cfg(not(feature = "scheduled_events"))]
1064 for mut event in events.drain() {
1065 let mut s = None;
1066 if event.downcast_swap::<Option<SamplerNodeResource>>(&mut s) {
1067 new_sample = Some(s);
1068 continue;
1069 }
1070
1071 if let Some(patch) = SamplerNode::patch_event(&event) {
1072 match patch {
1073 SamplerNodePatch::Volume(_) => volume_changed = true,
1074 SamplerNodePatch::Play(play) => {
1075 new_playing = Some(*play);
1076
1077 if *play {
1078 self.proc_state.last_finished_playback_id = self.proc_state.playback_id;
1079 self.proc_state.playback_id = play.id();
1080 proc_state_changed = true;
1081 }
1082 }
1083 SamplerNodePatch::RepeatMode(_) => repeat_mode_changed = true,
1084 SamplerNodePatch::Speed(_) => speed_changed = true,
1085 SamplerNodePatch::MinGain(min_gain) => {
1086 self.min_gain = min_gain.max(0.0);
1087 }
1088 _ => {}
1089 }
1090
1091 self.params.apply(patch);
1092 }
1093 }
1094
1095 if speed_changed {
1096 self.speed = self.params.speed.max(MIN_PLAYBACK_SPEED);
1097
1098 if self.speed > 0.99999 && self.speed < 1.00001 {
1099 self.speed = 1.0;
1100 }
1101 }
1102
1103 if volume_changed && let Some(loaded_sample) = &mut self.loaded_sample_state {
1104 loaded_sample.gain = self.params.volume.amp_clamped(self.min_gain);
1105 if loaded_sample.gain > 0.99999 && loaded_sample.gain < 1.00001 {
1106 loaded_sample.gain = 1.0;
1107 }
1108 }
1109
1110 if repeat_mode_changed && let Some(loaded_sample) = &mut self.loaded_sample_state {
1111 loaded_sample.num_times_looped_back = 0;
1112 }
1113
1114 if let Some(maybe_sample) = new_sample {
1115 self.proc_state.has_sample_resource = maybe_sample.is_some();
1116 proc_state_changed = true;
1117
1118 self.stop(extra);
1119
1120 #[cfg(feature = "scheduled_events")]
1121 if new_playing == Some(true)
1122 && playback_instant.is_none()
1123 && let Some(queued_playback_instant) = self.queued_playback_instant.take()
1124 && queued_playback_instant.to_samples(info).is_some()
1125 {
1126 playback_instant = Some(queued_playback_instant);
1127 }
1128
1129 self.loaded_sample_state = None;
1130
1131 if let Some(sample) = maybe_sample {
1132 self.load_sample(sample);
1133 }
1134 }
1135
1136 if let Some(mut new_playing) = new_playing {
1137 self.paused = false;
1138 self.proc_state.playback_age_frames = 0;
1139 proc_state_changed = true;
1140
1141 if new_playing {
1142 let mut playhead_frames_at_play_instant = None;
1143
1144 if self.params.play_from == PlayFrom::Resume {
1145 if self.playing && !is_first_process {
1147 #[cfg(feature = "scheduled_events")]
1149 {
1150 self.queued_playback_instant = None;
1151 }
1152 } else if let Some(loaded_sample_state) = &self.loaded_sample_state {
1153 playhead_frames_at_play_instant = Some(loaded_sample_state.playhead_frames);
1154 }
1155 } else {
1156 if let Some(loaded_sample_state) = &mut self.loaded_sample_state {
1158 loaded_sample_state.num_times_looped_back = 0;
1159 playhead_frames_at_play_instant =
1160 Some(self.params.play_from.as_frames(info.sample_rate).unwrap());
1161 } else {
1162 #[cfg(feature = "scheduled_events")]
1163 {
1164 self.queued_playback_instant = playback_instant;
1165 }
1166 }
1167 }
1168
1169 if let Some(playhead_frames_at_play_instant) = playhead_frames_at_play_instant {
1170 let loaded_sample_state = self.loaded_sample_state.as_mut().unwrap();
1171 let prev_playhead_frames = loaded_sample_state.playhead_frames;
1172
1173 #[cfg(feature = "scheduled_events")]
1174 let mut new_playhead_frames = if let Some(playback_instant) = playback_instant {
1175 let playback_instant_samples = playback_instant
1176 .to_samples(info)
1177 .unwrap_or(info.clock_samples);
1178 let delay = if playback_instant_samples < info.clock_samples {
1179 (info.clock_samples - playback_instant_samples).0 as u64
1180 } else {
1181 0
1182 };
1183
1184 playhead_frames_at_play_instant + delay
1185 } else {
1186 playhead_frames_at_play_instant
1187 };
1188
1189 #[cfg(not(feature = "scheduled_events"))]
1190 let mut new_playhead_frames = playhead_frames_at_play_instant;
1191
1192 if new_playhead_frames >= loaded_sample_state.sample_len_frames {
1193 match self.params.repeat_mode {
1194 RepeatMode::PlayOnce => {
1195 new_playhead_frames = loaded_sample_state.sample_len_frames
1196 }
1197 RepeatMode::RepeatEndlessly => {
1198 while new_playhead_frames >= loaded_sample_state.sample_len_frames {
1199 new_playhead_frames -= loaded_sample_state.sample_len_frames;
1200 loaded_sample_state.num_times_looped_back += 1;
1201 }
1202 }
1203 RepeatMode::RepeatMultiple {
1204 num_times_to_repeat,
1205 } => {
1206 while new_playhead_frames >= loaded_sample_state.sample_len_frames {
1207 if loaded_sample_state.num_times_looped_back
1208 == num_times_to_repeat as u64
1209 {
1210 new_playhead_frames = loaded_sample_state.sample_len_frames;
1211 break;
1212 }
1213
1214 new_playhead_frames -= loaded_sample_state.sample_len_frames;
1215 loaded_sample_state.num_times_looped_back += 1;
1216 }
1217 }
1218 }
1219 }
1220
1221 if prev_playhead_frames != new_playhead_frames {
1222 self.stop(extra);
1223
1224 self.loaded_sample_state.as_mut().unwrap().playhead_frames =
1225 new_playhead_frames;
1226
1227 self.proc_state.playhead_frames = new_playhead_frames;
1228 }
1229
1230 if new_playhead_frames
1231 == self.loaded_sample_state.as_ref().unwrap().sample_len_frames
1232 {
1233 self.proc_state.playhead_frames = new_playhead_frames;
1234
1235 new_playing = false;
1236 } else if new_playhead_frames != 0
1237 || (self.num_active_stop_declickers > 0 && self.params.crossfade_on_seek)
1238 {
1239 self.declicker.reset_to_0();
1240 self.declicker.fade_to_1(&extra.declick_values);
1241 } else {
1242 self.declicker.reset_to_1();
1243 }
1244
1245 #[cfg(feature = "scheduled_events")]
1246 {
1247 self.queued_playback_instant = None;
1248 }
1249 }
1250 } else if self.params.play_from == PlayFrom::Resume {
1251 self.declicker.fade_to_0(&extra.declick_values);
1253 self.paused = true;
1254 } else {
1255 self.stop(extra);
1257 }
1258
1259 self.playing = new_playing;
1260
1261 self.proc_state.playback_state = if self.playing {
1262 PlaybackState::Playing
1263 } else if self.paused {
1264 PlaybackState::Paused
1265 } else {
1266 self.proc_state.last_finished_playback_id = self.proc_state.playback_id;
1267 PlaybackState::Stopped
1268 };
1269 }
1270
1271 if proc_state_changed {
1272 self.sync_proc_state();
1273 }
1274 }
1275
1276 fn bypassed(&mut self, _bypassed: bool) {
1277 self.declicker.reset_to_target();
1278 self.num_active_stop_declickers = 0;
1279 }
1280
1281 fn process(
1282 &mut self,
1283 info: &ProcInfo,
1284 buffers: ProcBuffers,
1285 extra: &mut ProcExtra,
1286 ) -> ProcessStatus {
1287 let currently_processing_sample = self.currently_processing_sample();
1288
1289 if !currently_processing_sample && self.num_active_stop_declickers == 0 {
1290 return ProcessStatus::ClearAllOutputs;
1291 }
1292
1293 let mut num_filled_channels = 0;
1294
1295 if currently_processing_sample {
1296 let sample_state = self.loaded_sample_state.as_ref().unwrap();
1297
1298 let looping = self
1299 .params
1300 .repeat_mode
1301 .do_loop(sample_state.num_times_looped_back);
1302
1303 let (finished, n_channels) =
1304 self.process_internal(buffers.outputs, info.frames, looping, extra);
1305
1306 num_filled_channels = n_channels;
1307
1308 self.proc_state.playhead_frames =
1309 self.loaded_sample_state.as_ref().unwrap().playhead_frames;
1310
1311 if finished {
1312 self.playing = false;
1313 self.proc_state.playback_state = PlaybackState::Stopped;
1314 self.proc_state.last_finished_playback_id = self.proc_state.playback_id;
1315 } else {
1316 self.proc_state.playback_age_frames = self
1317 .proc_state
1318 .playback_age_frames
1319 .saturating_add(info.frames as u64);
1320 }
1321
1322 self.sync_proc_state();
1323 }
1324
1325 for (i, out_buf) in buffers
1326 .outputs
1327 .iter_mut()
1328 .enumerate()
1329 .skip(num_filled_channels)
1330 {
1331 if !info.out_silence_mask.is_channel_silent(i) {
1332 out_buf[..info.frames].fill(0.0);
1333 }
1334 }
1335
1336 if self.num_active_stop_declickers > 0 {
1337 let tmp_buffers = self.stop_declicker_buffers.as_ref().unwrap();
1338 let fade_out_frames = tmp_buffers.frames();
1339
1340 for (declicker_i, declicker) in self.stop_declickers.iter_mut().enumerate() {
1341 if declicker.frames_left == 0 {
1342 continue;
1343 }
1344
1345 let tmp_buffers = tmp_buffers
1346 .instance::<MAX_OUT_CHANNELS>(declicker_i, declicker.channels, fade_out_frames)
1347 .unwrap();
1348
1349 let copy_frames = info.frames.min(declicker.frames_left);
1350 let start_frame = fade_out_frames - declicker.frames_left;
1351
1352 for (out_buf, tmp_buf) in buffers.outputs.iter_mut().zip(tmp_buffers.iter()) {
1353 for (os, &ts) in out_buf[..copy_frames]
1354 .iter_mut()
1355 .zip(tmp_buf[start_frame..start_frame + copy_frames].iter())
1356 {
1357 *os += ts;
1358 }
1359 }
1360
1361 declicker.frames_left -= copy_frames;
1362 if declicker.frames_left == 0 {
1363 self.num_active_stop_declickers -= 1;
1364 }
1365
1366 num_filled_channels = num_filled_channels.max(declicker.channels);
1367 }
1368 }
1369
1370 let out_silence_mask = if num_filled_channels >= self.num_out_channels {
1371 SilenceMask::NONE_SILENT
1372 } else {
1373 let mut mask = SilenceMask::new_all_silent(self.num_out_channels);
1374 for i in 0..num_filled_channels {
1375 mask.set_channel(i, false);
1376 }
1377 mask
1378 };
1379
1380 ProcessStatus::OutputsModifiedWithMask(MaskType::Silence(out_silence_mask))
1381 }
1382
1383 fn new_stream(&mut self, stream_info: &StreamInfo, _context: &mut ProcStreamCtx) {
1384 if stream_info.sample_rate != stream_info.prev_sample_rate {
1385 self.stop_declicker_buffers = if self.config.num_declickers == 0 {
1386 None
1387 } else {
1388 Some(InstanceBuffer::<f32>::new(
1389 self.config.num_declickers as usize,
1390 NonZeroUsize::new(self.config.channels.get().get() as usize).unwrap(),
1391 stream_info.declick_frames.get() as usize,
1392 ))
1393 };
1394
1395 self.loaded_sample_state = None;
1398 self.playing = false;
1399 self.paused = false;
1400 self.proc_state.playback_state = PlaybackState::Stopped;
1401 self.sync_proc_state();
1402 }
1403 }
1404}
1405
1406struct LoadedSampleState {
1407 sample: SamplerNodeResource,
1408 sample_len_frames: u64,
1409 sample_num_channels: NonZeroUsize,
1410 sample_mono_to_stereo: bool,
1411 gain: f32,
1412 playhead_frames: u64,
1413 num_times_looped_back: u64,
1414}
1415
1416#[derive(Default, Clone, Copy)]
1417struct StopDeclickerState {
1418 frames_left: usize,
1419 channels: usize,
1420}