1use std::{
2 ops::Range,
3 path::Path,
4 sync::{mpsc::SyncSender, Arc},
5 time::Duration,
6};
7
8use crossbeam_queue::ArrayQueue;
9use four_cc::FourCC;
10use strum::VariantNames;
11
12use crate::{
13 generator::{
14 Generator, GeneratorMessage, GeneratorMessagePayload, GeneratorPlaybackEvent,
15 GeneratorPlaybackMessage, GeneratorPlaybackOptions,
16 },
17 modulation::{ModulationConfig, ModulationSource, ModulationTarget},
18 parameter::{
19 formatters, EnumParameter, EnumParameterValue, FloatParameter, IntegerParameter, Parameter,
20 ParameterScaling, ParameterValueUpdate,
21 },
22 source::{
23 file::preloaded::PreloadedFileSource, mixed::MixedSource, unique_source_id, Source,
24 SourceTime,
25 },
26 utils::{
27 ahdsr::AhdsrParameters,
28 buffer::{add_buffers, clear_buffer},
29 dsp::lfo::LfoWaveform,
30 },
31 AudioFileBuffer, Error, FilePlaybackOptions, FileSource, NotePlaybackId, PlaybackId,
32 PlaybackStatusContext, PlaybackStatusEvent, ResamplingQuality,
33};
34
35mod granular;
38mod modulation;
39mod voice;
40
41use modulation::SamplerModulationState;
42use voice::SamplerVoice;
43
44pub use granular::{GrainOverlapMode, GrainPlaybackDirection, GrainWindowMode, GranularParameters};
45
46pub enum SamplerMessage {
52 SetLoopRange(Option<Range<u64>>),
55}
56
57impl GeneratorMessage for SamplerMessage {
58 fn generator_name(&self) -> &'static str {
59 "Sampler"
60 }
61 fn payload(&self) -> &dyn std::any::Any {
62 self
63 }
64}
65
66pub struct Sampler {
73 playback_id: PlaybackId,
74 playback_message_queue: Arc<ArrayQueue<GeneratorPlaybackMessage>>,
75 file_path: Arc<String>,
76 active_voices: usize,
77 voices: Vec<SamplerVoice>,
78 base_transpose: i32,
79 base_finetune: i32,
80 base_volume: f32,
81 base_panning: f32,
82 envelope_parameters: Option<AhdsrParameters>,
83 granular_parameters: Option<GranularParameters>,
84 modulation_state: Option<SamplerModulationState>,
85 active_parameters: Vec<Box<dyn Parameter>>,
86 playback_status_send: Option<SyncSender<PlaybackStatusEvent>>,
87 transient: bool, stopping: bool, stopped: bool, options: GeneratorPlaybackOptions,
91 output_sample_rate: u32,
92 output_channel_count: usize,
93 temp_buffer: Vec<f32>,
94}
95
96impl Sampler {
99 pub const TRANSPOSE: IntegerParameter =
101 IntegerParameter::new(FourCC(*b"STRN"), "Transpose", -48..=48, 0).with_unit("st");
102
103 pub const FINETUNE: IntegerParameter =
104 IntegerParameter::new(FourCC(*b"SFTN"), "Finetune", -100..=100, 0).with_unit("ct");
105
106 pub const VOLUME: FloatParameter = FloatParameter::new(
107 FourCC(*b"SVOL"),
108 "Volume",
109 0.000001..=15.848932, 1.0, )
112 .with_scaling(ParameterScaling::Decibel(-60.0, 24.0))
113 .with_formatter(formatters::GAIN);
114
115 pub const PANNING: FloatParameter =
116 FloatParameter::new(FourCC(*b"SPAN"), "Panning", -1.0..=1.0, 0.0)
117 .with_formatter(formatters::PAN);
118
119 pub fn base_parameters() -> Vec<Box<dyn Parameter>> {
121 vec![
122 Self::TRANSPOSE.into_box(),
123 Self::FINETUNE.into_box(),
124 Self::VOLUME.into_box(),
125 Self::PANNING.into_box(),
126 ]
127 }
128
129 const MIN_TIME_SEC: f32 = 0.0;
131 const MAX_TIME_SEC: f32 = 10.0;
132
133 pub const AMP_ATTACK: FloatParameter = FloatParameter::new(
134 FourCC(*b"AATK"),
135 "Attack",
136 Self::MIN_TIME_SEC..=Self::MAX_TIME_SEC,
137 0.001,
138 )
139 .with_scaling(ParameterScaling::Exponential(2.0))
140 .with_unit("s");
141 pub const AMP_HOLD: FloatParameter = FloatParameter::new(
142 FourCC(*b"AHLD"),
143 "Hold",
144 Self::MIN_TIME_SEC..=Self::MAX_TIME_SEC,
145 0.75,
146 )
147 .with_scaling(ParameterScaling::Exponential(2.0))
148 .with_unit("s");
149 pub const AMP_DECAY: FloatParameter = FloatParameter::new(
150 FourCC(*b"ADCY"),
151 "Decay",
152 Self::MIN_TIME_SEC..=Self::MAX_TIME_SEC,
153 0.5,
154 )
155 .with_scaling(ParameterScaling::Exponential(2.0))
156 .with_unit("s");
157 pub const AMP_SUSTAIN: FloatParameter = FloatParameter::new(
158 FourCC(*b"ASTN"), "Sustain",
160 0.0..=1.0,
161 0.75,
162 );
163 pub const AMP_RELEASE: FloatParameter = FloatParameter::new(
164 FourCC(*b"AREL"),
165 "Release",
166 Self::MIN_TIME_SEC..=Self::MAX_TIME_SEC,
167 1.0,
168 )
169 .with_scaling(ParameterScaling::Exponential(2.0))
170 .with_unit("s");
171
172 pub fn envelope_parameters() -> Vec<Box<dyn Parameter>> {
174 vec![
175 Self::AMP_ATTACK.into_box(),
176 Self::AMP_HOLD.into_box(),
177 Self::AMP_DECAY.into_box(),
178 Self::AMP_SUSTAIN.into_box(),
179 Self::AMP_RELEASE.into_box(),
180 ]
181 }
182
183 pub fn set_envelope_parameter(
185 params: &mut AhdsrParameters,
186 id: FourCC,
187 value: &ParameterValueUpdate,
188 ) -> Result<(), Error> {
189 match id {
190 _ if id == Self::AMP_ATTACK.id() => {
191 let seconds = Sampler::parameter_update_value(value, &Self::AMP_ATTACK)?;
192 params.set_attack_time(Duration::from_secs_f32(seconds.max(0.0)))?;
193 }
194 _ if id == Self::AMP_HOLD.id() => {
195 let seconds = Sampler::parameter_update_value(value, &Self::AMP_HOLD)?;
196 params.set_hold_time(Duration::from_secs_f32(seconds.max(0.0)))?;
197 }
198 _ if id == Self::AMP_DECAY.id() => {
199 let seconds = Sampler::parameter_update_value(value, &Self::AMP_DECAY)?;
200 params.set_decay_time(Duration::from_secs_f32(seconds.max(0.0)))?;
201 }
202 _ if id == Self::AMP_SUSTAIN.id() => {
203 let sustain = Sampler::parameter_update_value(value, &Self::AMP_SUSTAIN)?;
204 params.set_sustain_level(sustain)?;
205 }
206 _ if id == Self::AMP_RELEASE.id() => {
207 let seconds = Sampler::parameter_update_value(value, &Self::AMP_RELEASE)?;
208 params.set_release_time(Duration::from_secs_f32(seconds.max(0.0)))?;
209 }
210 _ => {
211 return Err(Error::ParameterError(format!(
212 "Invalid/unknown envelope parameter '{id}'"
213 )))
214 }
215 }
216 Ok(())
217 }
218
219 const MIN_GRAIN_SIZE_MS: f32 = 1.0;
221 const MAX_GRAIN_SIZE_MS: f32 = 1000.0;
222 const MIN_GRAIN_DENSITY_HZ: f32 = 1.0;
223 const MAX_GRAIN_DENSITY_HZ: f32 = 100.0;
224
225 pub const GRAIN_OVERLAP_MODE: EnumParameter = EnumParameter::new(
226 FourCC(*b"GOVM"),
227 "Overlap Mode",
228 GrainOverlapMode::VARIANTS,
229 GrainOverlapMode::Cloud as usize,
230 );
231
232 pub const GRAIN_WINDOW: EnumParameter = EnumParameter::new(
233 FourCC(*b"GWND"),
234 "Window",
235 GrainWindowMode::VARIANTS,
236 GrainWindowMode::Hann as usize,
237 );
238
239 pub const GRAIN_SIZE: FloatParameter = FloatParameter::new(
240 FourCC(*b"GSIZ"),
241 "Grain Size",
242 Self::MIN_GRAIN_SIZE_MS..=Self::MAX_GRAIN_SIZE_MS,
243 100.0,
244 )
245 .with_scaling(ParameterScaling::Exponential(2.0))
246 .with_unit("ms");
247
248 pub const GRAIN_DENSITY: FloatParameter = FloatParameter::new(
249 FourCC(*b"GDEN"),
250 "Density",
251 Self::MIN_GRAIN_DENSITY_HZ..=Self::MAX_GRAIN_DENSITY_HZ,
252 10.0,
253 )
254 .with_scaling(ParameterScaling::Exponential(2.0))
255 .with_unit("Hz");
256
257 pub const GRAIN_VARIATION: FloatParameter =
258 FloatParameter::new(FourCC(*b"GVAR"), "Variation", 0.0..=1.0, 0.0)
259 .with_formatter(formatters::PERCENT);
260
261 pub const GRAIN_SPRAY: FloatParameter =
262 FloatParameter::new(FourCC(*b"GSPY"), "Spray", 0.0..=1.0, 0.0)
263 .with_formatter(formatters::PERCENT);
264
265 pub const GRAIN_PAN_SPREAD: FloatParameter =
266 FloatParameter::new(FourCC(*b"GPAN"), "Pan Spread", 0.0..=1.0, 0.0)
267 .with_formatter(formatters::PERCENT);
268
269 pub const GRAIN_PLAYBACK_DIR: EnumParameter = EnumParameter::new(
270 FourCC(*b"GDIR"),
271 "Direction",
272 GrainPlaybackDirection::VARIANTS,
273 GrainPlaybackDirection::Forward as usize,
274 );
275 pub const GRAIN_POSITION: FloatParameter =
276 FloatParameter::new(FourCC(*b"GPOS"), "Position", 0.0..=1.0, 0.5)
277 .with_formatter(formatters::PERCENT);
278
279 pub const GRAIN_STEP: FloatParameter =
280 FloatParameter::new(FourCC(*b"GSTP"), "Step", -4.0..=4.0, 0.0).with_unit("x");
281
282 pub fn granular_parameters() -> Vec<Box<dyn Parameter>> {
284 vec![
285 Self::GRAIN_OVERLAP_MODE.into_box(),
286 Self::GRAIN_WINDOW.into_box(),
287 Self::GRAIN_SIZE.into_box(),
288 Self::GRAIN_DENSITY.into_box(),
289 Self::GRAIN_VARIATION.into_box(),
290 Self::GRAIN_SPRAY.into_box(),
291 Self::GRAIN_PAN_SPREAD.into_box(),
292 Self::GRAIN_PLAYBACK_DIR.into_box(),
293 Self::GRAIN_POSITION.into_box(),
294 Self::GRAIN_STEP.into_box(),
295 ]
296 }
297
298 pub fn set_granular_parameter(
300 params: &mut GranularParameters,
301 id: FourCC,
302 value: &ParameterValueUpdate,
303 ) -> Result<(), Error> {
304 match id {
305 _ if id == Self::GRAIN_OVERLAP_MODE.id() => {
306 let mut enum_value = EnumParameterValue::<GrainOverlapMode>::from_description(
307 Self::GRAIN_OVERLAP_MODE,
308 );
309 enum_value.apply_update(value);
310 params.overlap_mode = enum_value.value();
311 }
312 _ if id == Self::GRAIN_WINDOW.id() => {
313 let mut enum_value =
314 EnumParameterValue::<GrainWindowMode>::from_description(Self::GRAIN_WINDOW);
315 enum_value.apply_update(value);
316 params.window = enum_value.value();
317 }
318 _ if id == Self::GRAIN_SIZE.id() => {
319 let ms = Sampler::parameter_update_value(value, &Self::GRAIN_SIZE)?;
320 params.size = ms;
321 }
322 _ if id == Self::GRAIN_DENSITY.id() => {
323 let hz = Sampler::parameter_update_value(value, &Self::GRAIN_DENSITY)?;
324 params.density = hz;
325 }
326 _ if id == Self::GRAIN_VARIATION.id() => {
327 let variation = Sampler::parameter_update_value(value, &Self::GRAIN_VARIATION)?;
328 params.variation = variation;
329 }
330 _ if id == Self::GRAIN_SPRAY.id() => {
331 let spray = Sampler::parameter_update_value(value, &Self::GRAIN_SPRAY)?;
332 params.spray = spray;
333 }
334 _ if id == Self::GRAIN_PAN_SPREAD.id() => {
335 let spread = Sampler::parameter_update_value(value, &Self::GRAIN_PAN_SPREAD)?;
336 params.pan_spread = spread;
337 }
338 _ if id == Self::GRAIN_PLAYBACK_DIR.id() => {
339 let mut enum_value = EnumParameterValue::<GrainPlaybackDirection>::from_description(
340 Self::GRAIN_PLAYBACK_DIR,
341 );
342 enum_value.apply_update(value);
343 params.playback_direction = enum_value.value();
344 }
345 _ if id == Self::GRAIN_POSITION.id() => {
346 let position = Sampler::parameter_update_value(value, &Self::GRAIN_POSITION)?;
347 params.position = position;
348 }
349 _ if id == Self::GRAIN_STEP.id() => {
350 let step = Sampler::parameter_update_value(value, &Self::GRAIN_STEP)?;
351 params.step = step;
352 }
353 _ => {
354 return Err(Error::ParameterError(format!(
355 "Invalid/unknown granular playback parameter '{id}'"
356 )))
357 }
358 }
359 Ok(())
360 }
361
362 pub const MOD_SOURCE_LFO1: FourCC = FourCC(*b"LFO1");
364 pub const MOD_SOURCE_LFO2: FourCC = FourCC(*b"LFO2");
365 pub const MOD_SOURCE_VELOCITY: FourCC = FourCC(*b"VELM");
366 pub const MOD_SOURCE_KEYTRACK: FourCC = FourCC(*b"KEYM");
367
368 pub const MOD_LFO1_RATE: FloatParameter =
370 FloatParameter::new(FourCC(*b"ML1R"), "LFO 1 Rate", 0.01..=20.0, 1.0)
371 .with_scaling(ParameterScaling::Exponential(2.0))
372 .with_unit("Hz");
373 pub const MOD_LFO1_WAVEFORM: EnumParameter = EnumParameter::new(
374 FourCC(*b"ML1W"),
375 "LFO 1 Waveform",
376 LfoWaveform::VARIANTS,
377 LfoWaveform::Sine as usize,
378 );
379
380 pub const MOD_LFO2_RATE: FloatParameter =
382 FloatParameter::new(FourCC(*b"ML2R"), "LFO 2 Rate", 0.01..=20.0, 2.0)
383 .with_scaling(ParameterScaling::Exponential(2.0))
384 .with_unit("Hz");
385 pub const MOD_LFO2_WAVEFORM: EnumParameter = EnumParameter::new(
386 FourCC(*b"ML2W"),
387 "LFO 2 Waveform",
388 LfoWaveform::VARIANTS,
389 LfoWaveform::Triangle as usize,
390 );
391
392 pub fn modulation_config() -> ModulationConfig {
394 ModulationConfig {
395 sources: vec![
396 ModulationSource::Lfo {
397 id: Self::MOD_SOURCE_LFO1,
398 name: "LFO 1",
399 rate_param: Self::MOD_LFO1_RATE,
400 waveform_param: Self::MOD_LFO1_WAVEFORM,
401 },
402 ModulationSource::Lfo {
403 id: Self::MOD_SOURCE_LFO2,
404 name: "LFO 2",
405 rate_param: Self::MOD_LFO2_RATE,
406 waveform_param: Self::MOD_LFO2_WAVEFORM,
407 },
408 ModulationSource::Velocity {
409 id: Self::MOD_SOURCE_VELOCITY,
410 name: "Velocity",
411 },
412 ModulationSource::Keytracking {
413 id: Self::MOD_SOURCE_KEYTRACK,
414 name: "Keytracking",
415 },
416 ],
417 targets: vec![
418 ModulationTarget::new(Self::GRAIN_SIZE.id(), Self::GRAIN_SIZE.name()),
419 ModulationTarget::new(Self::GRAIN_DENSITY.id(), Self::GRAIN_DENSITY.name()),
420 ModulationTarget::new(Self::GRAIN_VARIATION.id(), Self::GRAIN_VARIATION.name()),
421 ModulationTarget::new(Self::GRAIN_SPRAY.id(), Self::GRAIN_SPRAY.name()),
422 ModulationTarget::new(Self::GRAIN_PAN_SPREAD.id(), Self::GRAIN_PAN_SPREAD.name()),
423 ModulationTarget::new(Self::GRAIN_POSITION.id(), Self::GRAIN_POSITION.name()),
424 ModulationTarget::new(Self::GRAIN_STEP.id(), Self::GRAIN_STEP.name()),
425 ],
426 }
427 }
428
429 pub fn from_file<P: AsRef<Path>>(
441 file_path: P,
442 options: GeneratorPlaybackOptions,
443 output_channel_count: usize,
444 output_sample_rate: u32,
445 ) -> Result<Self, Error> {
446 let file_source = PreloadedFileSource::from_file(
447 &file_path,
448 FilePlaybackOptions::default(),
449 output_sample_rate,
450 )?;
451
452 Self::from_file_source(
453 file_source,
454 options,
455 output_channel_count,
456 output_sample_rate,
457 )
458 }
459
460 pub fn from_file_buffer<P: AsRef<Path>>(
463 file_buffer: Vec<u8>,
464 file_path: P,
465 options: GeneratorPlaybackOptions,
466 output_channel_count: usize,
467 output_sample_rate: u32,
468 ) -> Result<Self, Error> {
469 let file_path = file_path.as_ref().to_string_lossy().to_string();
470 let file_source = PreloadedFileSource::from_file_buffer(
471 file_buffer,
472 &file_path,
473 FilePlaybackOptions::default(),
474 output_sample_rate,
475 )?;
476
477 Self::from_file_source(
478 file_source,
479 options,
480 output_channel_count,
481 output_sample_rate,
482 )
483 }
484
485 pub fn from_file_source(
488 file_source: PreloadedFileSource,
489 options: GeneratorPlaybackOptions,
490 output_channel_count: usize,
491 output_sample_rate: u32,
492 ) -> Result<Self, Error> {
493 let file_path = Arc::new(file_source.file_name());
495
496 let playback_message_queue_size: usize = (Self::base_parameters().len()
498 + Self::envelope_parameters().len()
499 + Self::granular_parameters().len())
500 * 2
501 + 16;
502 let playback_message_queue = Arc::new(ArrayQueue::new(playback_message_queue_size));
503
504 let playback_id = unique_source_id();
506 let playback_status_send = None;
507
508 let mut voice_playback_options = FilePlaybackOptions::default();
510 if let Some(duration) = options.playback_pos_emit_rate {
511 voice_playback_options = voice_playback_options.playback_pos_emit_rate(duration);
512 }
513 voice_playback_options.fade_out_duration = Some(Duration::from_millis(50));
515
516 let mut voices = Vec::with_capacity(options.voices);
518 for _ in 0..options.voices {
519 let file_source = file_source
520 .clone(voice_playback_options, output_sample_rate)
521 .map_err(|err| {
522 Error::ParameterError(format!("Failed to create sampler voice: {err}"))
523 })?;
524 voices.push(SamplerVoice::new(
525 file_source,
526 output_channel_count,
527 output_sample_rate,
528 ));
529 }
530
531 let base_transpose = 0;
533 let base_finetune = 0;
534 let base_volume = 1.0;
535 let base_panning = 0.0;
536
537 let envelope_parameters = None;
539 let granular_parameters = None;
540
541 let modulation_state = None;
543
544 let active_voices = 0;
545
546 let active_parameters = Self::base_parameters();
548
549 let transient = false;
551 let stopping = false;
552 let stopped = false;
553
554 let temp_buffer = vec![0.0; MixedSource::MAX_MIX_BUFFER_SAMPLES];
556
557 Ok(Self {
558 playback_id,
559 playback_message_queue,
560 playback_status_send,
561 file_path,
562 active_voices,
563 voices,
564 base_transpose,
565 base_finetune,
566 base_volume,
567 base_panning,
568 envelope_parameters,
569 granular_parameters,
570 modulation_state,
571 active_parameters,
572 transient,
573 stopping,
574 stopped,
575 options,
576 output_sample_rate,
577 output_channel_count,
578 temp_buffer,
579 })
580 }
581
582 pub fn with_ahdsr(mut self, mut parameters: AhdsrParameters) -> Result<Self, Error> {
584 parameters
586 .set_sample_rate(self.output_sample_rate)
587 .map_err(|err| {
588 Error::ParameterError(format!("Failed to initialize AHDSR parameters: {err}"))
589 })?;
590
591 self.active_parameters.extend(Self::envelope_parameters());
593
594 self.envelope_parameters = Some(parameters);
595 Ok(self)
596 }
597
598 pub fn with_granular_playback(mut self, parameters: GranularParameters) -> Result<Self, Error> {
600 parameters
602 .validate()
603 .map_err(|err| Error::ParameterError(format!("Invalid granular parameters: {err}")))?;
604
605 self.active_parameters.extend(Self::granular_parameters());
607
608 let modulation_config = Self::modulation_config();
610
611 self.active_parameters
613 .extend(modulation_config.source_parameters());
614
615 let sample_buffer = Self::create_granular_sample_buffer(
617 self.voices.first().unwrap().file_source().file_buffer(),
618 self.output_sample_rate,
619 )?;
620
621 let modulation_state = SamplerModulationState::new(modulation_config);
623
624 for voice in &mut self.voices {
626 voice.enable_granular_playback(
627 modulation_state.create_matrix(self.output_sample_rate),
628 self.output_sample_rate,
629 sample_buffer.clone(),
630 );
631 }
632
633 self.modulation_state = Some(modulation_state);
634
635 self.granular_parameters = Some(parameters);
636 Ok(self)
637 }
638
639 pub fn loop_range(&self) -> Option<Range<u64>> {
642 self.voices
643 .first()
644 .and_then(|v| v.file_source().loop_range())
645 }
646
647 pub fn set_loop_range(&mut self, range: Option<Range<u64>>) {
650 for voice in &mut self.voices {
651 voice.set_loop_range(range.clone());
652 }
653 }
654
655 fn process_playback_messages(&mut self, current_sample_frame: u64) {
657 while let Some(message) = self.playback_message_queue.pop() {
658 match message {
659 GeneratorPlaybackMessage::Stop => {
660 self.stop(current_sample_frame);
661 }
662 GeneratorPlaybackMessage::Trigger { event } => {
663 if !self.stopping {
665 match event {
666 GeneratorPlaybackEvent::AllNotesOff => {
667 self.trigger_all_notes_off(current_sample_frame);
668 }
669 GeneratorPlaybackEvent::NoteOn {
670 note_id,
671 note,
672 volume,
673 panning,
674 context,
675 } => {
676 self.trigger_note_on(note_id, note, volume, panning, context);
677 }
678 GeneratorPlaybackEvent::NoteOff { note_id } => {
679 self.trigger_note_off(note_id, current_sample_frame);
680 }
681 GeneratorPlaybackEvent::SetSpeed {
682 note_id,
683 speed,
684 glide,
685 } => {
686 self.trigger_set_speed(note_id, speed, glide);
687 }
688 GeneratorPlaybackEvent::SetVolume { note_id, volume } => {
689 self.trigger_set_volume(note_id, volume);
690 }
691 GeneratorPlaybackEvent::SetPanning { note_id, panning } => {
692 self.trigger_set_panning(note_id, panning);
693 }
694 GeneratorPlaybackEvent::SetParameter { id, value } => {
695 if let Err(err) = self.process_parameter_update(id, &value) {
696 log::warn!("Failed to process parameter '{id}' update: {err}");
697 }
698 }
699 GeneratorPlaybackEvent::SetParameters { values } => {
700 if let Err(err) = self.process_parameter_updates(&values) {
701 log::warn!("Failed to process parameter updates: {err}");
702 }
703 }
704 GeneratorPlaybackEvent::SetModulation {
705 source,
706 target,
707 amount,
708 bipolar,
709 } => {
710 if let Err(err) =
711 self.set_modulation(source, target, amount, bipolar)
712 {
713 log::warn!("Failed to set modulation: {err}");
714 }
715 }
716 GeneratorPlaybackEvent::ClearModulation { source, target } => {
717 if let Err(err) = self.clear_modulation(source, target) {
718 log::warn!("Failed to clear modulation: {err}");
719 }
720 }
721 GeneratorPlaybackEvent::ProcessMessage { message } => {
722 if let Err(err) = self.process_message(message.as_ref()) {
723 log::warn!("Failed to process sampler message: {err}");
724 }
725 }
726 }
727 }
728 }
729 }
730 }
731 }
732
733 fn stop(&mut self, current_sample_frame: u64) {
734 self.stopping = self.transient;
736 self.trigger_all_notes_off(current_sample_frame);
738 }
739
740 fn trigger_note_on(
742 &mut self,
743 note_id: NotePlaybackId,
744 note: u8,
745 volume: Option<f32>,
746 panning: Option<f32>,
747 context: Option<PlaybackStatusContext>,
748 ) {
749 let volume_value = volume.unwrap_or(1.0);
751 let panning_value = panning.unwrap_or(0.0);
752
753 let voice_index = self.next_free_voice_index();
755 let voice = &mut self.voices[voice_index];
756
757 voice.start(
759 note_id,
760 note,
761 volume_value,
762 panning_value,
763 self.base_transpose,
764 self.base_finetune,
765 self.base_volume,
766 self.base_panning,
767 &self.envelope_parameters,
768 &self.granular_parameters,
769 context,
770 );
771
772 self.active_voices += 1;
774 }
775
776 fn trigger_note_off(&mut self, note_id: NotePlaybackId, current_sample_frame: u64) {
777 if let Some(voice) = self
778 .voices
779 .iter_mut()
780 .find(|v| v.note_id() == Some(note_id))
781 {
782 voice.stop(&self.envelope_parameters, current_sample_frame);
783 }
785 }
786
787 fn trigger_all_notes_off(&mut self, current_sample_frame: u64) {
788 for voice in &mut self.voices {
789 voice.stop(&self.envelope_parameters, current_sample_frame);
790 }
792 }
793
794 fn trigger_set_speed(&mut self, note_id: NotePlaybackId, speed: f64, glide: Option<f32>) {
795 if let Some(voice) = self
796 .voices
797 .iter_mut()
798 .find(|v| v.note_id() == Some(note_id))
799 {
800 voice.set_speed(speed, glide, self.base_transpose, self.base_finetune);
801 }
802 }
803
804 fn trigger_set_volume(&mut self, note_id: NotePlaybackId, volume: f32) {
805 if let Some(voice) = self
806 .voices
807 .iter_mut()
808 .find(|v| v.note_id() == Some(note_id))
809 {
810 voice.set_volume(volume, self.base_volume);
811 }
812 }
813
814 fn trigger_set_panning(&mut self, note_id: NotePlaybackId, panning: f32) {
815 if let Some(voice) = self
816 .voices
817 .iter_mut()
818 .find(|v| v.note_id() == Some(note_id))
819 {
820 voice.set_panning(panning, self.base_panning);
821 }
822 }
823
824 fn next_free_voice_index(&self) -> usize {
827 if let Some(index) = self.voices.iter().position(|v| !v.is_active()) {
829 return index;
830 }
831 let mut candidate_index = 0;
836 let mut earliest_release_time: Option<u64> = None;
837 let mut oldest_active_playback_id: Option<NotePlaybackId> = None;
838 for (index, voice) in self.voices.iter().enumerate() {
839 if self.envelope_parameters.is_some() && voice.in_release_stage() {
840 if let Some(release_time) = voice.release_start_frame() {
842 if earliest_release_time.is_none_or(|earliest| release_time < earliest) {
843 earliest_release_time = Some(release_time);
844 oldest_active_playback_id = None; candidate_index = index;
846 }
847 }
848 } else if earliest_release_time.is_none() {
849 if let Some(playback_id) = voice.note_id() {
852 if oldest_active_playback_id.is_none_or(|oldest| playback_id < oldest) {
853 oldest_active_playback_id = Some(playback_id);
854 candidate_index = index;
855 }
856 }
857 }
858 }
859 candidate_index
860 }
861
862 fn parameter_update_value(
863 value: &ParameterValueUpdate,
864 descriptor: &FloatParameter,
865 ) -> Result<f32, Error> {
866 match value {
867 ParameterValueUpdate::Normalized(norm) => {
868 Ok(descriptor.denormalize_value(norm.clamp(0.0, 1.0)))
869 }
870 ParameterValueUpdate::Raw(raw) => {
871 if let Some(v) = raw.downcast_ref::<f32>() {
872 Ok(descriptor.clamp_value(*v))
873 } else if let Some(v) = raw.downcast_ref::<f64>() {
874 Ok(descriptor.clamp_value(*v as f32))
875 } else {
876 Err(Error::ParameterError(format!(
877 "Unsupported payload type for sampler parameter '{}'",
878 descriptor.name()
879 )))
880 }
881 }
882 }
883 }
884
885 fn parameter_update_value_integer(
886 value: &ParameterValueUpdate,
887 descriptor: &IntegerParameter,
888 ) -> Result<i32, Error> {
889 match value {
890 ParameterValueUpdate::Normalized(norm) => {
891 Ok(descriptor.denormalize_value(norm.clamp(0.0, 1.0)))
892 }
893 ParameterValueUpdate::Raw(raw) => {
894 if let Some(v) = raw.downcast_ref::<i32>() {
895 Ok(descriptor.clamp_value(*v))
896 } else if let Some(v) = raw.downcast_ref::<i64>() {
897 Ok(descriptor.clamp_value(*v as i32))
898 } else {
899 Err(Error::ParameterError(format!(
900 "Unsupported payload type for sampler parameter '{}'",
901 descriptor.name()
902 )))
903 }
904 }
905 }
906 }
907
908 fn create_granular_sample_buffer(
909 file_buffer: Arc<AudioFileBuffer>,
910 output_sample_rate: u32,
911 ) -> Result<Arc<Box<[f32]>>, Error> {
912 if file_buffer.channel_count() == 1 && file_buffer.sample_rate() == output_sample_rate {
913 Ok(Arc::new(file_buffer.buffer().to_vec().into_boxed_slice()))
915 } else {
916 let mut source = PreloadedFileSource::from_shared_buffer(
918 file_buffer.clone(),
919 "granular temp sample",
920 FilePlaybackOptions::default()
921 .playback_pos_emit_disabled()
922 .resampling_quality(ResamplingQuality::Default)
923 .repeat(0),
924 output_sample_rate,
925 )?;
926 let mut dest_mono_buffer = Vec::with_capacity(
927 (file_buffer.frame_count() as u64 * output_sample_rate as u64
928 / file_buffer.sample_rate() as u64) as usize
929 + 100,
930 );
931 let source_channel_count = source.channel_count();
932 let mut temp_buffer = vec![0.0; 1024 * source_channel_count];
933 let mut time = SourceTime::default();
934 loop {
935 let read = source.write(&mut temp_buffer, &time);
937 if read == 0 {
938 break;
939 }
940 for frame in temp_buffer[..read].chunks(source_channel_count) {
942 dest_mono_buffer.push(frame.iter().sum::<f32>() / source_channel_count as f32);
943 }
944 time.add_frames(read as u64 / source_channel_count as u64);
945 }
946 if dest_mono_buffer.is_empty() {
948 dest_mono_buffer.push(0.0);
949 }
950 Ok(Arc::new(dest_mono_buffer.into_boxed_slice()))
951 }
952 }
953}
954
955impl Source for Sampler {
958 fn sample_rate(&self) -> u32 {
959 self.output_sample_rate
960 }
961
962 fn channel_count(&self) -> usize {
963 self.output_channel_count
964 }
965
966 fn is_exhausted(&self) -> bool {
967 self.stopped
968 }
969
970 fn weight(&self) -> usize {
971 self.active_voices.max(1)
972 }
973
974 fn write(&mut self, output: &mut [f32], time: &SourceTime) -> usize {
975 self.process_playback_messages(time.pos_in_frames);
977
978 if self.stopped || (self.active_voices == 0 && !self.stopping) {
980 return 0;
981 }
982
983 clear_buffer(output);
985
986 let mut active_voices = 0;
988 assert!(self.temp_buffer.len() >= output.len());
989 for voice in &mut self.voices {
990 if voice.is_active() {
991 let mix_buffer = &mut self.temp_buffer[..output.len()];
992 clear_buffer(mix_buffer);
993 let written = voice.process(
994 mix_buffer,
995 self.output_channel_count,
996 &self.envelope_parameters,
997 &self.granular_parameters,
998 time,
999 );
1000 add_buffers(&mut output[..written], &mix_buffer[..written]);
1001 if voice.is_active() {
1002 active_voices += 1;
1004 }
1005 }
1006 }
1007
1008 self.active_voices = active_voices;
1010
1011 if self.stopping && active_voices == 0 {
1013 self.stopped = true;
1014 if let Some(sender) = &self.playback_status_send {
1015 if let Err(err) = sender.send(PlaybackStatusEvent::Stopped {
1016 id: self.playback_id,
1017 path: self.file_path.clone(),
1018 context: None,
1019 exhausted: true,
1020 }) {
1021 log::warn!("Failed to send sampler playback status event: {err}");
1022 }
1023 }
1024 }
1025
1026 output.len()
1028 }
1029}
1030
1031impl Generator for Sampler {
1032 fn generator_name(&self) -> String {
1033 self.file_path.to_string()
1034 }
1035
1036 fn playback_id(&self) -> PlaybackId {
1037 self.playback_id
1038 }
1039
1040 fn playback_options(&self) -> &GeneratorPlaybackOptions {
1041 &self.options
1042 }
1043
1044 fn playback_message_queue(&self) -> Arc<ArrayQueue<GeneratorPlaybackMessage>> {
1045 self.playback_message_queue.clone()
1046 }
1047
1048 fn playback_status_sender(&self) -> Option<SyncSender<PlaybackStatusEvent>> {
1049 self.playback_status_send.clone()
1050 }
1051 fn set_playback_status_sender(&mut self, sender: Option<SyncSender<PlaybackStatusEvent>>) {
1052 self.playback_status_send = sender.clone();
1053 for voice in &mut self.voices {
1054 voice.set_playback_status_sender(sender.clone());
1055 }
1056 }
1057
1058 fn is_transient(&self) -> bool {
1059 self.transient
1060 }
1061 fn set_is_transient(&mut self, is_transient: bool) {
1062 self.transient = is_transient
1063 }
1064
1065 fn parameters(&self) -> Vec<&dyn Parameter> {
1066 self.active_parameters.iter().map(|p| p.as_ref()).collect()
1067 }
1068
1069 fn process_parameter_update(
1070 &mut self,
1071 id: FourCC,
1072 value: &ParameterValueUpdate,
1073 ) -> Result<(), Error> {
1074 match id {
1075 _ if id == Sampler::TRANSPOSE.id() => {
1077 let semitones =
1078 Sampler::parameter_update_value_integer(value, &Sampler::TRANSPOSE)?;
1079 self.base_transpose = semitones;
1080 for voice in &mut self.voices {
1082 if voice.is_active() {
1083 voice.set_base_pitch(self.base_transpose, self.base_finetune);
1084 }
1085 }
1086 return Ok(());
1087 }
1088 _ if id == Sampler::FINETUNE.id() => {
1089 let cents = Sampler::parameter_update_value_integer(value, &Sampler::FINETUNE)?;
1090 self.base_finetune = cents;
1091 for voice in &mut self.voices {
1093 if voice.is_active() {
1094 voice.set_base_pitch(self.base_transpose, self.base_finetune);
1095 }
1096 }
1097 return Ok(());
1098 }
1099 _ if id == Sampler::VOLUME.id() => {
1100 let volume = Sampler::parameter_update_value(value, &Sampler::VOLUME)?;
1101 self.base_volume = volume;
1102 for voice in &mut self.voices {
1104 if voice.is_active() {
1105 voice.set_base_volume(self.base_volume);
1106 }
1107 }
1108 return Ok(());
1109 }
1110 _ if id == Sampler::PANNING.id() => {
1111 let panning = Sampler::parameter_update_value(value, &Sampler::PANNING)?;
1112 self.base_panning = panning;
1113 for voice in &mut self.voices {
1115 if voice.is_active() {
1116 voice.set_base_panning(self.base_panning);
1117 }
1118 }
1119 return Ok(());
1120 }
1121 _ if id == Sampler::AMP_ATTACK.id()
1123 || id == Sampler::AMP_HOLD.id()
1124 || id == Sampler::AMP_DECAY.id()
1125 || id == Sampler::AMP_SUSTAIN.id()
1126 || id == Sampler::AMP_RELEASE.id() =>
1127 {
1128 if let Some(params) = &mut self.envelope_parameters {
1129 return Self::set_envelope_parameter(params, id, value);
1130 }
1131 }
1132 _ if id == Sampler::GRAIN_OVERLAP_MODE.id()
1134 || id == Sampler::GRAIN_WINDOW.id()
1135 || id == Sampler::GRAIN_SIZE.id()
1136 || id == Sampler::GRAIN_DENSITY.id()
1137 || id == Sampler::GRAIN_VARIATION.id()
1138 || id == Sampler::GRAIN_SPRAY.id()
1139 || id == Sampler::GRAIN_PAN_SPREAD.id()
1140 || id == Sampler::GRAIN_PLAYBACK_DIR.id()
1141 || id == Sampler::GRAIN_POSITION.id()
1142 || id == Sampler::GRAIN_STEP.id() =>
1143 {
1144 if let Some(params) = &mut self.granular_parameters {
1145 return Self::set_granular_parameter(params, id, value);
1146 }
1147 }
1148 _ if self
1150 .modulation_state
1151 .as_ref()
1152 .is_some_and(|state| state.is_source_parameter(id)) =>
1153 {
1154 let modulation_state = self.modulation_state.as_mut().unwrap();
1155
1156 let rate = if id == Self::MOD_LFO1_RATE.id() {
1158 Some(Self::parameter_update_value(value, &Self::MOD_LFO1_RATE)?)
1159 } else if id == Self::MOD_LFO2_RATE.id() {
1160 Some(Self::parameter_update_value(value, &Self::MOD_LFO2_RATE)?)
1161 } else {
1162 None
1163 };
1164 let waveform = if id == Self::MOD_LFO1_WAVEFORM.id() {
1166 let mut waveform_value =
1167 EnumParameterValue::from_description(Self::MOD_LFO1_WAVEFORM);
1168 waveform_value.apply_update(value);
1169 Some(waveform_value.value())
1170 } else if id == Self::MOD_LFO2_WAVEFORM.id() {
1171 let mut waveform_value =
1172 EnumParameterValue::from_description(Self::MOD_LFO2_WAVEFORM);
1173 waveform_value.apply_update(value);
1174 Some(waveform_value.value())
1175 } else {
1176 None
1177 };
1178
1179 return modulation_state.apply_parameter_update(
1181 id,
1182 rate,
1183 waveform,
1184 &mut self.voices,
1185 );
1186 }
1187 _ => {}
1188 }
1189 Err(Error::ParameterError(format!(
1190 "Invalid or unknown sampler parameter: '{id}'"
1191 )))
1192 }
1193
1194 fn modulation_sources(&self) -> Vec<ModulationSource> {
1195 if let Some(modulation_state) = &self.modulation_state {
1196 modulation_state.sources()
1197 } else {
1198 Vec::new()
1199 }
1200 }
1201
1202 fn modulation_targets(&self) -> Vec<ModulationTarget> {
1203 if let Some(modulation_state) = &self.modulation_state {
1204 modulation_state.targets()
1205 } else {
1206 Vec::new()
1207 }
1208 }
1209
1210 fn set_modulation(
1211 &mut self,
1212 source: FourCC,
1213 target: FourCC,
1214 amount: f32,
1215 bipolar: bool,
1216 ) -> Result<(), Error> {
1217 if let Some(modulation_state) = &self.modulation_state {
1218 for voice in &mut self.voices {
1219 if let Some(matrix) = voice.modulation_matrix_mut() {
1220 modulation_state.set_modulation(matrix, source, target, amount, bipolar)?;
1221 }
1222 }
1223 Ok(())
1224 } else {
1225 Err(Error::ParameterError(
1226 "Modulation routing only available when granular playback is enabled".to_string(),
1227 ))
1228 }
1229 }
1230
1231 fn clear_modulation(&mut self, source: FourCC, target: FourCC) -> Result<(), Error> {
1232 if let Some(modulation_state) = &self.modulation_state {
1233 for voice in &mut self.voices {
1234 if let Some(matrix) = voice.modulation_matrix_mut() {
1235 modulation_state.clear_modulation(matrix, source, target)?;
1236 }
1237 }
1238 Ok(())
1239 } else {
1240 Err(Error::ParameterError(
1241 "Modulation routing only available when granular playback is enabled".to_string(),
1242 ))
1243 }
1244 }
1245
1246 fn process_message(&mut self, message: &GeneratorMessagePayload) -> Result<(), Error> {
1247 if let Some(msg) = message.payload().downcast_ref::<SamplerMessage>() {
1248 match msg {
1249 SamplerMessage::SetLoopRange(range) => {
1250 let frame_count = self
1252 .voices
1253 .first()
1254 .map(|v| v.file_source().file_buffer().frame_count() as u64)
1255 .unwrap_or(0);
1256 if range.is_none()
1257 || range
1258 .as_ref()
1259 .is_some_and(|r| r.start < frame_count && r.end <= frame_count)
1260 {
1261 self.set_loop_range(range.clone());
1262 Ok(())
1263 } else {
1264 Err(Error::ParameterError(format!(
1265 "Invalid loop range {:?}. Loop must be in range {:?}",
1266 range,
1267 0..frame_count
1268 )))
1269 }
1270 }
1271 }
1272 } else {
1273 Err(Error::ParameterError(format!(
1274 "Sampler: Received unexpected message payload from '{}'.",
1275 message.generator_name()
1276 )))
1277 }
1278 }
1279}