Skip to main content

rustysynth_ext/
synthesizer.rs

1use std::cmp;
2use std::collections::HashMap;
3use std::sync::Arc;
4
5use crate::array_math::ArrayMath;
6use crate::channel::Channel;
7use crate::chorus::Chorus;
8use crate::error::SynthesizerError;
9use crate::region_pair::RegionPair;
10use crate::reverb::Reverb;
11use crate::soundfont::SoundFont;
12use crate::soundfont_math::SoundFontMath;
13use crate::synthesizer_settings::SynthesizerSettings;
14use crate::voice_collection::VoiceCollection;
15
16/// An instance of the SoundFont synthesizer.
17#[derive(Debug)]
18#[non_exhaustive]
19pub struct Synthesizer {
20    pub(crate) sound_font: Arc<SoundFont>,
21    pub(crate) sample_rate: i32,
22    pub(crate) block_size: usize,
23    pub(crate) maximum_polyphony: usize,
24
25    preset_lookup: HashMap<i32, usize>,
26    default_preset: usize,
27
28    channels: Vec<Channel>,
29
30    voices: VoiceCollection,
31
32    block_left: Vec<f32>,
33    block_right: Vec<f32>,
34
35    inverse_block_size: f32,
36
37    block_read: usize,
38
39    master_volume: f32,
40
41    effects: Option<Effects>,
42}
43
44impl Synthesizer {
45    /// The number of channels.
46    pub const CHANNEL_COUNT: usize = 16;
47    /// The percussion channel.
48    pub const PERCUSSION_CHANNEL: usize = 9;
49
50    /// Initializes a new synthesizer using a specified SoundFont and settings.
51    ///
52    /// # Arguments
53    ///
54    /// * `sound_font` - The SoundFont instance.
55    /// * `settings` - The settings for synthesis.
56    pub fn new(
57        sound_font: &Arc<SoundFont>,
58        settings: &SynthesizerSettings,
59    ) -> Result<Self, SynthesizerError> {
60        settings.validate()?;
61
62        let mut preset_lookup: HashMap<i32, usize> = HashMap::new();
63
64        let mut min_preset_id = i32::MAX;
65        let mut default_preset: usize = 0;
66        for i in 0..sound_font.presets.len() {
67            let preset = &sound_font.presets[i];
68
69            // The preset ID is Int32, where the upper 16 bits represent the bank number
70            // and the lower 16 bits represent the patch number.
71            // This ID is used to search for presets by the combination of bank number
72            // and patch number.
73            let preset_id = (preset.bank_number << 16) | preset.patch_number;
74            preset_lookup.insert(preset_id, i);
75
76            // The preset with the minimum ID number will be default.
77            // If the SoundFont is GM compatible, the piano will be chosen.
78            if preset_id < min_preset_id {
79                default_preset = i;
80                min_preset_id = preset_id;
81            }
82        }
83
84        let mut channels: Vec<Channel> = Vec::with_capacity(Synthesizer::CHANNEL_COUNT);
85        for i in 0..Synthesizer::CHANNEL_COUNT {
86            channels.push(Channel::new(i == Synthesizer::PERCUSSION_CHANNEL));
87        }
88
89        let voices = VoiceCollection::new(settings);
90
91        let block_left: Vec<f32> = vec![0_f32; settings.block_size];
92        let block_right: Vec<f32> = vec![0_f32; settings.block_size];
93
94        let inverse_block_size = 1_f32 / settings.block_size as f32;
95
96        let block_read = settings.block_size;
97
98        let master_volume = 0.5_f32;
99
100        let effects = if settings.enable_reverb_and_chorus {
101            Some(Effects::new(settings))
102        } else {
103            None
104        };
105
106        Ok(Self {
107            sound_font: Arc::clone(sound_font),
108            sample_rate: settings.sample_rate,
109            block_size: settings.block_size,
110            maximum_polyphony: settings.maximum_polyphony,
111            preset_lookup,
112            default_preset,
113            channels,
114            voices,
115            block_left,
116            block_right,
117            inverse_block_size,
118            block_read,
119            master_volume,
120            effects,
121        })
122    }
123
124    /// Processes a MIDI message.
125    ///
126    /// # Arguments
127    ///
128    /// * `channel` - The channel to which the message will be sent.
129    /// * `command` - The type of the message.
130    /// * `data1` - The first data part of the message.
131    /// * `data2` - The second data part of the message.
132    pub fn process_midi_message(&mut self, channel: i32, command: i32, data1: i32, data2: i32) {
133        if !(0 <= channel && channel < self.channels.len() as i32) {
134            return;
135        }
136
137        let channel_info = &mut self.channels[channel as usize];
138
139        match command {
140            0x80 => self.note_off(channel, data1),       // Note Off
141            0x90 => self.note_on(channel, data1, data2), // Note On
142            0xB0 => match data1 // Controller
143            {
144                0x00 => channel_info.set_bank(data2), // Bank Selection
145                0x01 => channel_info.set_modulation_coarse(data2), // Modulation Coarse
146                0x21 => channel_info.set_modulation_fine(data2), // Modulation Fine
147                0x06 => channel_info.data_entry_coarse(data2), // Data Entry Coarse
148                0x26 => channel_info.data_entry_fine(data2), // Data Entry Fine
149                0x07 => channel_info.set_volume_coarse(data2), // Channel Volume Coarse
150                0x27 => channel_info.set_volume_fine(data2), // Channel Volume Fine
151                0x0A => channel_info.set_pan_coarse(data2), // Pan Coarse
152                0x2A => channel_info.set_pan_fine(data2), // Pan Fine
153                0x0B => channel_info.set_expression_coarse(data2), // Expression Coarse
154                0x2B => channel_info.set_expression_fine(data2), // Expression Fine
155                0x40 => channel_info.set_hold_pedal(data2), // Hold Pedal
156                0x5B => channel_info.set_reverb_send(data2), // Reverb Send
157                0x5D => channel_info.set_chorus_send(data2), // Chorus Send
158                0x63 => channel_info.set_nrpn_coarse(data2), // NRPN Coarse
159                0x62 => channel_info.set_nrpn_fine(data2), // NRPN Fine
160                0x65 => channel_info.set_rpn_coarse(data2), // RPN Coarse
161                0x64 => channel_info.set_rpn_fine(data2), // RPN Fine
162                0x78 => self.note_off_all_channel(channel, true), // All Sound Off
163                0x79 => self.reset_all_controllers_channel(channel), // Reset All Controllers
164                0x7B => self.note_off_all_channel(channel, false), // All Note Off
165                _ => (),
166            },
167            0xC0 => channel_info.set_patch(data1), // Program Change
168            0xE0 => channel_info.set_pitch_bend(data1, data2), // Pitch Bend
169            _ => (),
170        }
171    }
172
173    /// Stops a note.
174    ///
175    /// # Arguments
176    ///
177    /// * `channel` - The channel of the note.
178    /// * `key` - The key of the note.
179    pub fn note_off(&mut self, channel: i32, key: i32) {
180        if !(0 <= channel && channel < self.channels.len() as i32) {
181            return;
182        }
183
184        for voice in self.voices.get_active_voices().iter_mut() {
185            if voice.channel() == channel && voice.key() == key {
186                voice.end();
187            }
188        }
189    }
190
191    /// Starts a note.
192    ///
193    /// # Arguments
194    ///
195    /// * `channel` - The channel of the note.
196    /// * `key` - The key of the note.
197    /// * `velocity` - The velocity of the note.
198    pub fn note_on(&mut self, channel: i32, key: i32, velocity: i32) {
199        if velocity == 0 {
200            self.note_off(channel, key);
201            return;
202        }
203
204        if !(0 <= channel && channel < self.channels.len() as i32) {
205            return;
206        }
207
208        let channel_info = &self.channels[channel as usize];
209
210        let preset_id = (channel_info.get_bank_number() << 16) | channel_info.get_patch_number();
211
212        let mut preset = self.default_preset;
213        match self.preset_lookup.get(&preset_id) {
214            Some(value) => preset = *value,
215            None => {
216                // Try fallback to the GM sound set.
217                // Normally, the given patch number + the bank number 0 will work.
218                // For drums (bank number >= 128), it seems to be better to select the standard set (128:0).
219                let gm_preset_id = if channel_info.get_bank_number() < 128 {
220                    channel_info.get_patch_number()
221                } else {
222                    128 << 16
223                };
224
225                // If no corresponding preset was found. Use the default one...
226                if let Some(value) = self.preset_lookup.get(&gm_preset_id) {
227                    preset = *value
228                }
229            }
230        }
231
232        let preset = &self.sound_font.presets[preset];
233        for preset_region in preset.regions.iter() {
234            if preset_region.contains(key, velocity) {
235                let instrument = &self.sound_font.instruments[preset_region.instrument];
236                for instrument_region in instrument.regions.iter() {
237                    if instrument_region.contains(key, velocity) {
238                        let region_pair = RegionPair::new(preset_region, instrument_region);
239
240                        if let Some(value) = self.voices.request_new(instrument_region, channel) {
241                            value.start(&region_pair, channel, key, velocity)
242                        }
243                    }
244                }
245            }
246        }
247    }
248
249    /// Stops all the notes in the specified channel.
250    ///
251    /// # Arguments
252    ///
253    /// * `immediate` - If `true`, notes will stop immediately without the release sound.
254    pub fn note_off_all(&mut self, immediate: bool) {
255        if immediate {
256            self.voices.clear();
257        } else {
258            for voice in self.voices.get_active_voices().iter_mut() {
259                voice.end();
260            }
261        }
262    }
263
264    /// Stops all the notes in the specified channel.
265    ///
266    /// # Arguments
267    ///
268    /// * `channel` - The channel in which the notes will be stopped.
269    /// * `immediate` - If `true`, notes will stop immediately without the release sound.
270    pub fn note_off_all_channel(&mut self, channel: i32, immediate: bool) {
271        if immediate {
272            for voice in self.voices.get_active_voices().iter_mut() {
273                if voice.channel() == channel {
274                    voice.kill();
275                }
276            }
277        } else {
278            for voice in self.voices.get_active_voices().iter_mut() {
279                if voice.channel() == channel {
280                    voice.end();
281                }
282            }
283        }
284    }
285
286    /// Resets all the controllers.
287    pub fn reset_all_controllers(&mut self) {
288        for channel in &mut self.channels {
289            channel.reset_all_controllers();
290        }
291    }
292
293    /// Resets all the controllers of the specified channel.
294    ///
295    /// # Arguments
296    ///
297    /// * `channel` - The channel to be reset.
298    pub fn reset_all_controllers_channel(&mut self, channel: i32) {
299        if !(0 <= channel && channel < self.channels.len() as i32) {
300            return;
301        }
302
303        self.channels[channel as usize].reset_all_controllers();
304    }
305
306    /// Resets the synthesizer.
307    pub fn reset(&mut self) {
308        self.voices.clear();
309
310        for channel in &mut self.channels {
311            channel.reset();
312        }
313
314        if let Some(effects) = self.effects.as_mut() {
315            effects.reverb.mute();
316            effects.chorus.mute();
317        }
318
319        self.block_read = self.block_size;
320    }
321
322    /// Renders the waveform.
323    ///
324    /// # Arguments
325    ///
326    /// * `left` - The buffer of the left channel to store the rendered waveform.
327    /// * `right` - The buffer of the right channel to store the rendered waveform.
328    ///
329    /// # Remarks
330    ///
331    /// The output buffers for the left and right must be the same length.
332    pub fn render(&mut self, left: &mut [f32], right: &mut [f32]) {
333        if left.len() != right.len() {
334            panic!("The output buffers for the left and right must be the same length.");
335        }
336
337        let left_length = left.len();
338
339        let mut wrote = 0;
340        while wrote < left_length {
341            if self.block_read == self.block_size {
342                self.render_block();
343                self.block_read = 0;
344            }
345
346            let src_rem = self.block_size - self.block_read;
347            let dst_rem = left_length - wrote;
348            let rem = cmp::min(src_rem, dst_rem);
349
350            left[wrote..wrote + rem]
351                .copy_from_slice(&self.block_left[self.block_read..self.block_read + rem]);
352            right[wrote..wrote + rem]
353                .copy_from_slice(&self.block_right[self.block_read..self.block_read + rem]);
354
355            self.block_read += rem;
356            wrote += rem;
357        }
358    }
359
360    fn render_block(&mut self) {
361        self.voices
362            .process(&self.sound_font.wave_data, &self.channels);
363
364        self.block_left.fill(0_f32);
365        self.block_right.fill(0_f32);
366        for voice in self.voices.get_active_voices().iter_mut() {
367            Synthesizer::write_block_stereo(
368                self.master_volume * voice.previous_mix_gain_left,
369                self.master_volume * voice.current_mix_gain_left,
370                self.master_volume * voice.previous_mix_gain_right,
371                self.master_volume * voice.current_mix_gain_right,
372                voice.block(),
373                &mut self.block_left[..],
374                &mut self.block_right[..],
375                self.inverse_block_size,
376            );
377        }
378
379        if let Some(effects) = self.effects.as_mut() {
380            let chorus = &mut effects.chorus;
381            let chorus_input_left = &mut effects.chorus_input_left[..];
382            let chorus_input_right = &mut effects.chorus_input_right[..];
383            let chorus_output_left = &mut effects.chorus_output_left[..];
384            let chorus_output_right = &mut effects.chorus_output_right[..];
385            chorus_input_left.fill(0_f32);
386            chorus_input_right.fill(0_f32);
387            for voice in self.voices.get_active_voices().iter_mut() {
388                Synthesizer::write_block_stereo(
389                    voice.previous_chorus_send * voice.previous_mix_gain_left,
390                    voice.current_chorus_send * voice.current_mix_gain_left,
391                    voice.previous_chorus_send * voice.previous_mix_gain_right,
392                    voice.current_chorus_send * voice.current_mix_gain_right,
393                    voice.block(),
394                    chorus_input_left,
395                    chorus_input_right,
396                    self.inverse_block_size,
397                );
398            }
399            chorus.process(
400                chorus_input_left,
401                chorus_input_right,
402                chorus_output_left,
403                chorus_output_right,
404            );
405            ArrayMath::multiply_add(
406                self.master_volume,
407                chorus_output_left,
408                &mut self.block_left[..],
409            );
410            ArrayMath::multiply_add(
411                self.master_volume,
412                chorus_output_right,
413                &mut self.block_right[..],
414            );
415
416            let reverb = &mut effects.reverb;
417            let reverb_input = &mut effects.reverb_input[..];
418            let reverb_output_left = &mut effects.reverb_output_left[..];
419            let reverb_output_right = &mut effects.reverb_output_right[..];
420            reverb_input.fill(0_f32);
421            for voice in self.voices.get_active_voices().iter_mut() {
422                let previous_gain = reverb.get_input_gain()
423                    * voice.previous_reverb_send
424                    * (voice.previous_mix_gain_left + voice.previous_mix_gain_right);
425                let current_gain = reverb.get_input_gain()
426                    * voice.current_reverb_send
427                    * (voice.current_mix_gain_left + voice.current_mix_gain_right);
428                Synthesizer::write_block(
429                    previous_gain,
430                    current_gain,
431                    voice.block(),
432                    &mut reverb_input[..],
433                    self.inverse_block_size,
434                );
435            }
436
437            reverb.process(reverb_input, reverb_output_left, reverb_output_right);
438            ArrayMath::multiply_add(
439                self.master_volume,
440                reverb_output_left,
441                &mut self.block_left[..],
442            );
443            ArrayMath::multiply_add(
444                self.master_volume,
445                reverb_output_right,
446                &mut self.block_right[..],
447            );
448        }
449    }
450
451    fn write_block(
452        previous_gain: f32,
453        current_gain: f32,
454        source: &[f32],
455        destination: &mut [f32],
456        inverse_block_size: f32,
457    ) {
458        if SoundFontMath::max(previous_gain, current_gain) < SoundFontMath::NON_AUDIBLE {
459            return;
460        }
461
462        if (current_gain - previous_gain).abs() < 1.0E-3_f32 {
463            ArrayMath::multiply_add(current_gain, source, destination);
464        } else {
465            let step = inverse_block_size * (current_gain - previous_gain);
466            ArrayMath::multiply_add_slope(previous_gain, step, source, destination);
467        }
468    }
469
470    /// Mixes the source into both channels in a single pass.
471    ///
472    /// Each channel is mixed exactly as [Synthesizer::write_block] would do on
473    /// its own, including the ramp when its gain changes over the block.
474    #[expect(
475        clippy::too_many_arguments,
476        reason = "the two channels read naturally side by side"
477    )]
478    fn write_block_stereo(
479        previous_gain_left: f32,
480        current_gain_left: f32,
481        previous_gain_right: f32,
482        current_gain_right: f32,
483        source: &[f32],
484        destination_left: &mut [f32],
485        destination_right: &mut [f32],
486        inverse_block_size: f32,
487    ) {
488        let left_is_audible =
489            SoundFontMath::max(previous_gain_left, current_gain_left) >= SoundFontMath::NON_AUDIBLE;
490        let right_is_audible = SoundFontMath::max(previous_gain_right, current_gain_right)
491            >= SoundFontMath::NON_AUDIBLE;
492
493        match (left_is_audible, right_is_audible) {
494            (false, false) => {}
495            (true, false) => Synthesizer::write_block(
496                previous_gain_left,
497                current_gain_left,
498                source,
499                destination_left,
500                inverse_block_size,
501            ),
502            (false, true) => Synthesizer::write_block(
503                previous_gain_right,
504                current_gain_right,
505                source,
506                destination_right,
507                inverse_block_size,
508            ),
509            (true, true) => {
510                let left_step = Synthesizer::gain_step(
511                    previous_gain_left,
512                    current_gain_left,
513                    inverse_block_size,
514                );
515                let right_step = Synthesizer::gain_step(
516                    previous_gain_right,
517                    current_gain_right,
518                    inverse_block_size,
519                );
520
521                match (left_step, right_step) {
522                    (Some(left_step), Some(right_step)) => ArrayMath::multiply_add_slope_stereo(
523                        previous_gain_left,
524                        left_step,
525                        previous_gain_right,
526                        right_step,
527                        source,
528                        destination_left,
529                        destination_right,
530                    ),
531                    (None, None) => ArrayMath::multiply_add_stereo(
532                        current_gain_left,
533                        current_gain_right,
534                        source,
535                        destination_left,
536                        destination_right,
537                    ),
538                    (Some(left_step), None) => ArrayMath::multiply_add_slope_stereo(
539                        previous_gain_left,
540                        left_step,
541                        current_gain_right,
542                        0_f32,
543                        source,
544                        destination_left,
545                        destination_right,
546                    ),
547                    (None, Some(right_step)) => ArrayMath::multiply_add_slope_stereo(
548                        current_gain_left,
549                        0_f32,
550                        previous_gain_right,
551                        right_step,
552                        source,
553                        destination_left,
554                        destination_right,
555                    ),
556                }
557            }
558        }
559    }
560
561    /// Returns the per-sample gain increment, or None when the gain is constant.
562    fn gain_step(previous_gain: f32, current_gain: f32, inverse_block_size: f32) -> Option<f32> {
563        if (current_gain - previous_gain).abs() < 1.0E-3_f32 {
564            None
565        } else {
566            Some(inverse_block_size * (current_gain - previous_gain))
567        }
568    }
569
570    /// Gets the SoundFont used as the audio source.
571    pub fn get_sound_font(&self) -> &SoundFont {
572        &self.sound_font
573    }
574
575    /// Gets the sample rate for synthesis.
576    pub fn get_sample_rate(&self) -> i32 {
577        self.sample_rate
578    }
579
580    /// Gets the block size for rendering waveform.
581    pub fn get_block_size(&self) -> usize {
582        self.block_size
583    }
584
585    /// Gets the number of maximum polyphony.
586    pub fn get_maximum_polyphony(&self) -> usize {
587        self.maximum_polyphony
588    }
589
590    /// Gets the value indicating whether reverb and chorus are enabled.
591    pub fn get_enable_reverb_and_chorus(&self) -> bool {
592        self.effects.is_some()
593    }
594
595    /// Gets the master volume.
596    pub fn get_master_volume(&self) -> f32 {
597        self.master_volume
598    }
599
600    /// Sets the master volume.
601    ///
602    /// # Arguments
603    ///
604    /// * `value` - The new value of the master volume.
605    pub fn set_master_volume(&mut self, value: f32) {
606        self.master_volume = value;
607    }
608}
609
610#[derive(Debug)]
611struct Effects {
612    reverb: Reverb,
613    reverb_input: Vec<f32>,
614    reverb_output_left: Vec<f32>,
615    reverb_output_right: Vec<f32>,
616
617    chorus: Chorus,
618    chorus_input_left: Vec<f32>,
619    chorus_input_right: Vec<f32>,
620    chorus_output_left: Vec<f32>,
621    chorus_output_right: Vec<f32>,
622}
623
624impl Effects {
625    fn new(settings: &SynthesizerSettings) -> Effects {
626        Self {
627            reverb: Reverb::new(settings.sample_rate),
628            reverb_input: vec![0_f32; settings.block_size],
629            reverb_output_left: vec![0_f32; settings.block_size],
630            reverb_output_right: vec![0_f32; settings.block_size],
631            chorus: Chorus::new(settings.sample_rate, 0.002, 0.0019, 0.4),
632            chorus_input_left: vec![0_f32; settings.block_size],
633            chorus_input_right: vec![0_f32; settings.block_size],
634            chorus_output_left: vec![0_f32; settings.block_size],
635            chorus_output_right: vec![0_f32; settings.block_size],
636        }
637    }
638}