1#![allow(non_camel_case_types)]
2use std::{
3 collections::{HashMap, HashSet},
4 io,
5 path::PathBuf,
6 sync::Arc,
7};
8
9use biquad::Q_BUTTERWORTH_F32;
10use rayon::iter::{IntoParallelIterator, ParallelIterator};
11use thiserror::Error;
12use xsynth_soundfonts::{convert_sample_index, FilterType, LoopMode};
13
14use self::audio::load_audio_file;
15pub use self::audio::AudioLoadError;
16
17use super::{
18 voice::VoiceControlData,
19 voice::{EnvelopeParameters, Voice},
20};
21use crate::{helpers::db_to_amp, voice::EnvelopeDescriptor, AudioStreamParams, ChannelCount};
22
23pub use xsynth_soundfonts::{sf2::Sf2ParseError, sfz::SfzParseError};
24
25mod audio;
26mod config;
27mod utils;
28mod voice_spawners;
29use utils::*;
30use voice_spawners::*;
31
32pub use config::*;
33
34pub trait VoiceSpawner: Sync + Send {
35 fn spawn_voice(&self, control: &VoiceControlData) -> Box<dyn Voice>;
36 fn exclusive_class(&self) -> Option<u8> {
37 None
38 }
39}
40
41pub trait SoundfontBase: Sync + Send + std::fmt::Debug {
42 fn stream_params(&self) -> &'_ AudioStreamParams;
43
44 fn get_attack_voice_spawners_at(
45 &self,
46 bank: u8,
47 preset: u8,
48 key: u8,
49 vel: u8,
50 ) -> Vec<Box<dyn VoiceSpawner>>;
51 fn get_release_voice_spawners_at(
52 &self,
53 bank: u8,
54 preset: u8,
55 key: u8,
56 vel: u8,
57 ) -> Vec<Box<dyn VoiceSpawner>>;
58}
59
60#[derive(Clone)]
61pub(super) struct LoopParams {
62 pub mode: LoopMode,
63 pub offset: u32,
64 pub start: u32,
65 pub end: u32,
66 pub stop: Option<u32>,
67}
68
69struct SampleVoiceSpawnerParams {
70 volume: f32,
71 pan: f32,
72 speed_mult: f32,
73 cutoff: Option<f32>,
74 resonance: f32,
75 filter_type: FilterType,
76 loop_params: LoopParams,
77 envelope: Arc<EnvelopeParameters>,
78 sample: Arc<[Arc<[f32]>]>,
79 interpolator: Interpolator,
80 exclusive_class: Option<u8>,
81}
82
83pub(super) struct SoundfontInstrument {
84 bank: u8,
85 preset: u8,
86 spawner_params_list: Vec<Vec<Arc<SampleVoiceSpawnerParams>>>,
87}
88
89pub struct SampleSoundfont {
172 instruments: Vec<SoundfontInstrument>,
173 stream_params: AudioStreamParams,
174}
175
176#[derive(Debug, Error)]
178pub enum LoadSfzError {
179 #[error("IO Error")]
180 IOError(#[from] io::Error),
181
182 #[error("Error loading samples")]
183 AudioLoadError(#[from] AudioLoadError),
184
185 #[error("Error parsing the SFZ: {0}")]
186 SfzParseError(#[from] SfzParseError),
187}
188
189#[derive(Debug, Error)]
192pub enum LoadSfError {
193 #[error("Error loading the SFZ: {0}")]
194 LoadSfzError(#[from] LoadSfzError),
195
196 #[error("Error loading the SF2: {0}")]
197 LoadSf2Error(#[from] Sf2ParseError),
198
199 #[error("Unsupported format")]
200 Unsupported,
201}
202
203impl SampleSoundfont {
204 pub fn new(
214 path: impl Into<PathBuf>,
215 stream_params: AudioStreamParams,
216 options: SoundfontInitOptions,
217 ) -> Result<Self, LoadSfError> {
218 let path: PathBuf = path.into();
219 if let Some(ext) = path.extension() {
220 match ext.to_str().unwrap_or("").to_lowercase().as_str() {
221 "sfz" => {
222 Self::new_sfz(path, stream_params, options).map_err(LoadSfError::LoadSfzError)
223 }
224 "sf2" => {
225 Self::new_sf2(path, stream_params, options).map_err(LoadSfError::LoadSf2Error)
226 }
227 _ => Err(LoadSfError::Unsupported),
228 }
229 } else {
230 Err(LoadSfError::Unsupported)
231 }
232 }
233
234 pub fn new_sfz(
243 sfz_path: impl Into<PathBuf>,
244 stream_params: AudioStreamParams,
245 options: SoundfontInitOptions,
246 ) -> Result<Self, LoadSfzError> {
247 let regions = xsynth_soundfonts::sfz::parse_soundfont(sfz_path.into())?;
248
249 let unique_sample_params: HashSet<_> = regions
251 .iter()
252 .map(sample_cache_from_region_params)
253 .collect();
254
255 let samples: Result<HashMap<_, _>, _> = unique_sample_params
257 .into_par_iter()
258 .map(|params| -> Result<(_, _), LoadSfzError> {
259 let sample = load_audio_file(¶ms.path, stream_params)?;
260 Ok((params, sample))
261 })
262 .collect();
263 let samples = samples?;
264
265 let mut spawner_params_list = Vec::<Vec<Arc<SampleVoiceSpawnerParams>>>::new();
267 for _ in 0..(128 * 128) {
268 spawner_params_list.push(Vec::new());
269 }
270
271 for region in regions {
273 let params = sample_cache_from_region_params(®ion);
274
275 if region.keyrange.contains(&-1) {
277 continue;
278 }
279
280 for key in region.keyrange.clone() {
281 for vel in region.velrange.clone() {
282 let index = key_vel_to_index(key as u8, vel);
283 let speed_mult =
284 get_speed_mult_from_keys(key as u8, region.pitch_keycenter as u8)
285 * cents_factor(region.tune as f32);
286
287 let mut envelope = region.ampeg_envelope.clone();
288 envelope.ampeg_release +=
289 (vel as f32 / 127.0) * region.ampeg_envelope.ampeg_vel2release;
290 let envelope_params = Arc::new(
291 envelope_descriptor_from_region_params(&envelope).to_envelope_params(
292 stream_params.sample_rate,
293 options.vol_envelope_options,
294 ),
295 );
296
297 let mut cutoff = None;
298 if options.use_effects {
299 if let Some(mut cutoff_t) = region.cutoff {
300 if cutoff_t >= 1.0 {
301 let cents = vel as f32 / 127.0 * region.fil_veltrack as f32
302 + (key as f32 - region.fil_keycenter as f32)
303 * region.fil_keytrack as f32;
304 cutoff_t *= cents_factor(cents);
305 cutoff = Some(
306 cutoff_t
307 .clamp(1.0, stream_params.sample_rate as f32 / 2.0 - 100.0),
308 );
309 }
310 }
311 }
312
313 let pan_mult = vel as f32 / 127.0 * region.pan_veltrack
314 + (key as f32 - region.pan_keycenter as f32) * region.pan_keytrack;
315 let pan = (region.pan as f32 + pan_mult).clamp(-100.0, 100.0) / 100.0;
316 let pan = (pan + 1.0) / 2.0;
317
318 let vol_vel = {
319 let a = region.amp_veltrack / 100.0;
320 let aabs = a.abs();
321 let vel = vel as f32;
322
323 127.0 * (1.0 - aabs)
324 + vel * (a + aabs) / 2.0
325 + (127.0 - vel) * (aabs - a) / 2.0
326 };
327 let vol_mult = (vol_vel / 127.0).powi(2);
328 let vol_db_add =
329 (key as f32 - region.amp_keycenter as f32) * region.amp_keytrack;
330 let vol_db = (region.volume as f32 + vol_db_add).clamp(-96.0, 12.0);
331 let volume = vol_mult * db_to_amp(vol_db);
332
333 let sample_rate = samples[¶ms].1;
334
335 let loop_params = LoopParams {
336 mode: if region.loop_start == region.loop_end {
337 LoopMode::NoLoop
338 } else {
339 region.loop_mode
340 },
341 offset: convert_sample_index(
342 region.offset,
343 sample_rate,
344 stream_params.sample_rate,
345 ),
346 start: convert_sample_index(
347 region.loop_start,
348 sample_rate,
349 stream_params.sample_rate,
350 ),
351 end: convert_sample_index(
352 region.loop_end,
353 sample_rate,
354 stream_params.sample_rate,
355 ),
356 stop: None,
357 };
358
359 let mut region_samples = samples[¶ms].0.clone();
360 if stream_params.channels == ChannelCount::Stereo && region_samples.len() == 1 {
361 region_samples =
362 Arc::new([region_samples[0].clone(), region_samples[0].clone()]);
363 }
364
365 let spawner_params = Arc::new(SampleVoiceSpawnerParams {
366 pan,
367 volume,
368 envelope: envelope_params,
369 speed_mult,
370 cutoff,
371 resonance: db_to_amp(region.resonance) * Q_BUTTERWORTH_F32,
372 filter_type: region.filter_type,
373 interpolator: options.interpolator,
374 loop_params,
375 sample: region_samples,
376 exclusive_class: None,
377 });
378
379 spawner_params_list[index].push(spawner_params.clone());
380 }
381 }
382 }
383
384 Ok(SampleSoundfont {
385 instruments: vec![SoundfontInstrument {
386 bank: options.bank.unwrap_or(0),
387 preset: options.preset.unwrap_or(0),
388 spawner_params_list,
389 }],
390 stream_params,
391 })
392 }
393
394 pub fn new_sf2(
403 sf2_path: impl Into<PathBuf>,
404 stream_params: AudioStreamParams,
405 options: SoundfontInitOptions,
406 ) -> Result<Self, Sf2ParseError> {
407 let presets =
408 xsynth_soundfonts::sf2::load_soundfont(sf2_path.into(), stream_params.sample_rate)?;
409
410 let mut instruments = Vec::new();
411
412 for preset in presets {
413 if let Some(bank) = options.bank {
414 if bank != preset.bank as u8 {
415 continue;
416 }
417 }
418 if let Some(presetn) = options.preset {
419 if presetn != preset.preset as u8 {
420 continue;
421 }
422 }
423
424 let mut spawner_params_list = Vec::<Vec<Arc<SampleVoiceSpawnerParams>>>::new();
425 for _ in 0..(128 * 128) {
426 spawner_params_list.push(Vec::new());
427 }
428
429 let mut unique_envelope_params =
430 Vec::<(EnvelopeDescriptor, Arc<EnvelopeParameters>)>::new();
431
432 for region in preset.regions {
433 for key in region.keyrange.clone() {
434 for vel in region.velrange.clone() {
435 let index = key_vel_to_index(key, vel);
436 let note_params = region.note_params(key, vel);
437 let envelope =
438 envelope_descriptor_from_region_params(¬e_params.ampeg_envelope);
439 let envelope_params = if let Some((_, params)) = unique_envelope_params
440 .iter()
441 .find(|(descriptor, _)| *descriptor == envelope)
442 {
443 params.clone()
444 } else {
445 let params = Arc::new(envelope.to_envelope_params(
446 stream_params.sample_rate,
447 options.vol_envelope_options,
448 ));
449 unique_envelope_params.push((envelope, params.clone()));
450 params
451 };
452 let tuned_key_cents =
453 (key as f32 - region.root_key as f32) * region.scale_tuning as f32;
454 let speed_mult = cents_factor(
455 tuned_key_cents
456 + region.fine_tune as f32
457 + region.coarse_tune as f32 * 100.0
458 + note_params.tune_cents,
459 );
460
461 let mut cutoff = None;
462 if options.use_effects {
463 if let Some(cutoff_t) = note_params.cutoff {
464 if cutoff_t >= 1.0 {
465 cutoff = Some(cutoff_t.clamp(
466 1.0,
467 stream_params.sample_rate as f32 / 2.0 - 100.0,
468 ));
469 }
470 }
471 }
472
473 let pan = ((note_params.pan as f32 / 500.0) + 1.0) / 2.0;
474
475 let loop_params = LoopParams {
476 mode: if region.loop_start == region.loop_end {
477 LoopMode::NoLoop
478 } else {
479 region.loop_mode
480 },
481 offset: region.offset,
482 start: region.loop_start,
483 end: region.loop_end,
484 stop: Some(region.sample_end),
485 };
486
487 let mut region_samples = region.sample.clone();
488 if stream_params.channels == ChannelCount::Stereo
489 && region_samples.len() == 1
490 {
491 region_samples =
492 Arc::new([region_samples[0].clone(), region_samples[0].clone()]);
493 }
494
495 let spawner_params = Arc::new(SampleVoiceSpawnerParams {
496 pan,
497 volume: note_params.volume,
498 envelope: envelope_params,
499 speed_mult,
500 cutoff,
501 resonance: db_to_amp(note_params.resonance) * Q_BUTTERWORTH_F32,
502 filter_type: FilterType::LowPass,
503 interpolator: options.interpolator,
504 loop_params,
505 sample: region_samples,
506 exclusive_class: region.exclusive_class,
507 });
508
509 spawner_params_list[index].push(spawner_params.clone());
510 }
511 }
512 }
513
514 let new = SoundfontInstrument {
515 bank: preset.bank as u8,
516 preset: preset.preset as u8,
517 spawner_params_list,
518 };
519 instruments.push(new);
520 }
521
522 Ok(SampleSoundfont {
523 instruments,
524 stream_params,
525 })
526 }
527}
528
529impl std::fmt::Debug for SampleSoundfont {
530 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
531 write!(f, "SampleSoundfont")
532 }
533}
534
535impl SoundfontBase for SampleSoundfont {
536 fn stream_params(&self) -> &'_ AudioStreamParams {
537 &self.stream_params
538 }
539
540 fn get_attack_voice_spawners_at(
541 &self,
542 bank: u8,
543 preset: u8,
544 key: u8,
545 vel: u8,
546 ) -> Vec<Box<dyn VoiceSpawner>> {
547 use simdeez::*; use simdeez::prelude::*;
550
551 simd_runtime_generate!(
552 fn get(
553 key: u8,
554 vel: u8,
555 sf: &SoundfontInstrument,
556 stream_params: &AudioStreamParams,
557 ) -> Vec<Box<dyn VoiceSpawner>> {
558 if sf.spawner_params_list.is_empty() {
559 return Vec::new();
560 }
561
562 let index = key_vel_to_index(key, vel);
563 let mut vec = Vec::<Box<dyn VoiceSpawner>>::new();
564 for spawner in &sf.spawner_params_list[index] {
565 match stream_params.channels {
566 ChannelCount::Stereo => vec.push(Box::new(
567 StereoSampledVoiceSpawner::<S>::new(spawner, vel, *stream_params),
568 )),
569 ChannelCount::Mono => vec.push(Box::new(
570 MonoSampledVoiceSpawner::<S>::new(spawner, vel, *stream_params),
571 )),
572 }
573 }
574 vec
575 }
576 );
577
578 let empty = SoundfontInstrument {
579 bank: 0,
580 preset: 0,
581 spawner_params_list: Vec::new(),
582 };
583
584 let instrument = self
585 .instruments
586 .iter()
587 .find(|i| i.bank == bank && i.preset == preset)
588 .unwrap_or(&empty);
589
590 get(key, vel, instrument, self.stream_params())
591 }
592
593 fn get_release_voice_spawners_at(
594 &self,
595 _bank: u8,
596 _preset: u8,
597 _key: u8,
598 _vel: u8,
599 ) -> Vec<Box<dyn VoiceSpawner>> {
600 vec![]
601 }
602}