usfx/
lib.rs

1//! Generate sound effects for your game in realtime.
2//!
3//! ## Example
4//!
5//! ```rust
6//! // Create a simple blip sound
7//! let mut sample = usfx::Sample::default();
8//! sample.volume(0.5);
9//!
10//! // Use a sine wave oscillator at 500 hz
11//! sample.osc_type(usfx::OscillatorType::Sine);
12//! sample.osc_frequency(500);
13//!
14//! // Set the envelope
15//! sample.env_attack(0.02);
16//! sample.env_decay(0.05);
17//! sample.env_sustain(0.2);
18//! sample.env_release(0.5);
19//!
20//! // Add some distortion
21//! sample.dis_crunch(0.5);
22//! sample.dis_drive(0.9);
23//!
24//! // Create a mixer so we can play the sound
25//! let mut mixer = usfx::Mixer::default();
26//!
27//! // Play our sample
28//! mixer.play(sample);
29//!
30//! // Plug our mixer into the audio device loop
31//! // ...
32//! # let mut audio_device_buffer = [0.0; 2000];
33//! mixer.generate(&mut audio_device_buffer);
34//! ```
35
36mod effects;
37mod envelope;
38mod oscillator;
39
40use effects::{distortion::Distortion, Effect};
41use envelope::{Envelope, State};
42use oscillator::Oscillator;
43pub use oscillator::{DutyCycle, OscillatorType};
44#[cfg(feature = "serde")]
45use serde::{Deserialize, Serialize};
46use std::{cell::RefCell, collections::HashMap};
47
48/// Audio sample that procedurally generates it's sound.
49///
50/// Plug this into the [`Mixer`] object to play the sound.
51///
52/// ```rust
53/// // Generate a sine wave at 2khz
54/// let mut sine_wave = usfx::Sample::default();
55/// sine_wave.osc_frequency(2000);
56/// sine_wave.osc_type(usfx::OscillatorType::Sine);
57///
58/// // Add it to the mixer
59/// let mut mixer = usfx::Mixer::default();
60/// mixer.play(sine_wave);
61///
62/// // Plug it into a audio library, see the examples for a cpal & SDL2 implementation
63/// // ...
64/// // Call the generator to get a buffer for the audio library
65/// # let mut buffer = [0.0];
66/// mixer.generate(&mut buffer);
67/// ```
68///
69/// [`Generator`]: struct.Generator.html
70#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
71#[derive(Debug, Copy, Clone)]
72pub struct Sample {
73    volume: Option<f32>,
74    osc_frequency: usize,
75    osc_type: OscillatorType,
76    osc_duty_cycle: DutyCycle,
77    env_attack: f32,
78    env_decay: f32,
79    env_release: f32,
80    env_sustain: f32,
81    dis_crunch: Option<f32>,
82    dis_drive: Option<f32>,
83}
84
85impl Default for Sample {
86    /// The default is a sinewave of 441 hz.
87    fn default() -> Self {
88        Self {
89            volume: None,
90            osc_frequency: 441,
91            osc_type: OscillatorType::Sine,
92            osc_duty_cycle: DutyCycle::default(),
93            env_attack: 0.01,
94            env_decay: 0.1,
95            env_sustain: 0.5,
96            env_release: 0.5,
97            dis_crunch: None,
98            dis_drive: None,
99        }
100    }
101}
102
103impl Sample {
104    /// Set the volume which is a multiplier of the result.
105    ///
106    /// A range from 0.0-1.0 will result in proper behavior, but you can experiment with other
107    /// values.
108    pub fn volume(&mut self, volume: f32) -> &mut Self {
109        self.volume = Some(volume);
110
111        self
112    }
113
114    /// Set the frequency of the oscillator in hertz.
115    ///
116    /// When using the noise oscillator type this will be the seed.
117    /// A range from 1-20000 is allowed.
118    pub fn osc_frequency(&mut self, frequency: usize) -> &mut Self {
119        self.osc_frequency = frequency;
120
121        self
122    }
123
124    /// Set the type of the oscillator.
125    ///
126    /// See the [`OscillatorType`] enum for supported wave types.
127    ///
128    /// [`OscillatorType`]: enum.OscillatorType.html
129    pub fn osc_type(&mut self, oscillator: OscillatorType) -> &mut Self {
130        self.osc_type = oscillator;
131
132        self
133    }
134
135    /// Set the length of the pulse, this only applies when you use a square wave.
136    ///
137    /// Changing of the duty cycle mainly results in a different timbre.
138    /// A range from 0.0-1.0 will have results, other values won't do anything.
139    pub fn osc_duty_cycle(&mut self, duty_cycle: DutyCycle) -> &mut Self {
140        self.osc_duty_cycle = duty_cycle;
141
142        self
143    }
144
145    /// Set the time until the first envelope slope reaches it's maximum height.
146    ///
147    /// A range from 0.0-1.0 will result in proper behavior, but you can experiment with other
148    /// values.
149    pub fn env_attack(&mut self, attack: f32) -> &mut Self {
150        self.env_attack = attack;
151
152        self
153    }
154
155    /// Set the time it takes from the maximum height to go into the main plateau.
156    ///
157    /// A range from 0.0-1.0 will result in proper behavior, but you can experiment with other
158    /// values.
159    pub fn env_decay(&mut self, decay: f32) -> &mut Self {
160        self.env_decay = decay;
161
162        self
163    }
164
165    /// Set the height of the main plateau.
166    ///
167    /// A range from 0.0-1.0 will result in proper behavior, but you can experiment with other
168    /// values.
169    pub fn env_sustain(&mut self, sustain: f32) -> &mut Self {
170        self.env_sustain = sustain;
171
172        self
173    }
174
175    /// Set the time it takes to go from the end of the plateau to zero.
176    ///
177    /// A range from 0.0-1.0 will result in proper behavior, but you can experiment with other
178    /// values.
179    pub fn env_release(&mut self, release: f32) -> &mut Self {
180        self.env_release = release;
181
182        self
183    }
184
185    /// Overdrive that adds hard clipping.
186    ///
187    /// A range from 0.0-1.0 will result in proper behavior, but you can experiment with other
188    /// values.
189    pub fn dis_crunch(&mut self, crunch: f32) -> &mut Self {
190        self.dis_crunch = Some(crunch);
191
192        self
193    }
194
195    /// Overdrive with soft clipping.
196    ///
197    /// A range from 0.0-1.0 will result in proper behavior, but you can experiment with other
198    /// values.
199    pub fn dis_drive(&mut self, drive: f32) -> &mut Self {
200        self.dis_drive = Some(drive);
201
202        self
203    }
204}
205
206/// Convert samples with PCM.
207///
208/// This struct is created by [`Sample`].
209/// You can use this generator directly or plug it into a [`Mixer`] object.
210///
211/// [`Sample`]: struct.Sample.html
212/// [`Mixer`]: struct.Mixer.html
213#[derive(Debug)]
214struct Generator {
215    /// Whether we are finished running the sample.
216    pub(crate) finished: bool,
217    /// The total offset.
218    offset: usize,
219    /// Multiplier of the result.
220    volume: Option<f32>,
221
222    /// The oscillator, because it's a trait it has to be boxed.
223    oscillator: Oscillator,
224    /// The ADSR envelope.
225    envelope: Envelope,
226
227    /// Distortion effect.
228    distortion: Option<Distortion>,
229}
230
231impl Generator {
232    /// Generate the sound for the sample.
233    fn run(&mut self, output: &mut [f32]) {
234        // Run the oscillator
235        self.oscillator.generate(output, self.offset);
236
237        // Apply the ADSR and set the state if we're finished or not
238        if self.envelope.apply(output, self.offset) == State::Done {
239            self.finished = true;
240        }
241
242        // Apply the distortion
243        if let Some(distortion) = &mut self.distortion {
244            distortion.apply(output, self.offset);
245        }
246
247        // Apply the volume
248        if let Some(volume) = self.volume {
249            output.iter_mut().for_each(|tone| *tone *= volume);
250        }
251
252        self.offset += output.len();
253    }
254}
255
256/// Manage samples and mix the volume output of each.
257///
258/// ```rust
259/// // Instantiate a new mixer with a sample rate of 44100
260/// let mut mixer = usfx::Mixer::new(44_100);
261///
262/// // Create a default sample as the sinewave
263/// let sample = usfx::Sample::default();
264/// // Create another sample with a trianglewave
265/// let mut other_sample = usfx::Sample::default();
266/// other_sample.osc_type(usfx::OscillatorType::Triangle);
267///
268/// // Play two oscillators at the same time
269/// mixer.play(sample);
270/// mixer.play(other_sample);
271///
272/// // This buffer should be passed by the audio library.
273/// let mut buffer = [0.0; 44_100];
274/// // Fill the buffer with procedurally generated sound.
275/// mixer.generate(&mut buffer);
276/// ```
277#[derive(Debug)]
278pub struct Mixer {
279    /// List of generators.
280    generators: Vec<Generator>,
281    /// Store the sample rate so we can keep oscillator buffers.
282    sample_rate: usize,
283    /// A lookup table of oscillator buffers.
284    oscillator_lookup: HashMap<(usize, DutyCycle, OscillatorType), RefCell<Vec<f32>>>,
285}
286
287impl Mixer {
288    /// Create a new mixer object.
289    pub fn new(sample_rate: usize) -> Self {
290        Self {
291            sample_rate,
292            ..Self::default()
293        }
294    }
295
296    /// Play a sample.
297    pub fn play(&mut self, sample: Sample) {
298        // Create the ADSR envelope generator
299        let envelope = Envelope::new(
300            self.sample_rate as f32,
301            sample.env_attack,
302            sample.env_decay,
303            sample.env_sustain,
304            sample.env_release,
305        );
306
307        // Get the cached buffer (or automatically create a new one)
308        let buffer =
309            self.oscillator_buffer(sample.osc_frequency, sample.osc_duty_cycle, sample.osc_type);
310
311        // Create the oscillator
312        let oscillator = Oscillator::new(buffer, self.sample_rate);
313
314        // Create the distortion if applicable
315        let distortion = match (sample.dis_crunch, sample.dis_drive) {
316            (Some(crunch), Some(drive)) => Some(Distortion::new(crunch, drive)),
317            (Some(crunch), None) => Some(Distortion::new(crunch, 1.0)),
318            (None, Some(drive)) => Some(Distortion::new(0.0, drive)),
319            (None, None) => None,
320        };
321
322        // Combine them in a generator
323        let generator = Generator {
324            finished: false,
325            offset: 0,
326            volume: sample.volume,
327
328            oscillator,
329            envelope,
330
331            distortion,
332        };
333
334        // Use the generator
335        self.generators.push(generator);
336    }
337
338    /// Generate a frame for the sample.
339    ///
340    /// The output buffer can be smaller but not bigger than the sample size.
341    ///
342    /// ```rust
343    /// // Instantiate a new mixer
344    /// let mut mixer = usfx::Mixer::default();
345    ///
346    /// // Create a default sample as the sinewave
347    /// mixer.play(usfx::Sample::default());
348    ///
349    /// // This buffer should be passed by the audio library
350    /// let mut buffer = [0.0; 44_100];
351    /// // Fill the buffer with procedurally generated sound
352    /// mixer.generate(&mut buffer);
353    /// ```
354    pub fn generate(&mut self, output: &mut [f32]) {
355        // Set the buffer to zero
356        output.iter_mut().for_each(|tone| *tone = 0.0);
357
358        // If there are no generators just return the empty buffer
359        let generators_len = self.generators.len();
360        if generators_len == 0 {
361            return;
362        }
363
364        // Run the generators
365        self.generators
366            .iter_mut()
367            .for_each(|generator| generator.run(output));
368
369        // Remove the ones that are finished
370        self.generators.retain(|generator| !generator.finished);
371
372        // Calculate the inverse so we can multiply instead of divide which is more efficient
373        let buffer_len_inv = 1.0 / generators_len as f32;
374
375        // Divide the generators by the current samples
376        output.iter_mut().for_each(|tone| *tone *= buffer_len_inv);
377    }
378
379    /// Retrieve an oscillator buffer or create it when it doesn't exist yet.
380    fn oscillator_buffer(
381        &mut self,
382        frequency: usize,
383        duty_cycle: DutyCycle,
384        oscillator_type: OscillatorType,
385    ) -> RefCell<Vec<f32>> {
386        match self
387            .oscillator_lookup
388            .get(&(frequency, duty_cycle, oscillator_type))
389        {
390            // A buffer was already cached, return it
391            Some(buffer) => RefCell::clone(buffer),
392            // Nothing is found, cache a new buffer of frequencies
393            None => {
394                // Build a lookup table and wrap it in a refcell so there can be multiple immutable
395                // references to it
396                let lut = RefCell::new(oscillator_type.build_lut(
397                    frequency,
398                    duty_cycle,
399                    self.sample_rate,
400                ));
401
402                // Clone it so it can be returned after the original object is inserted
403                let cloned_ref = RefCell::clone(&lut);
404
405                // Add the new lookup table to the cache
406                self.oscillator_lookup
407                    .insert((frequency, duty_cycle, oscillator_type), lut);
408
409                cloned_ref
410            }
411        }
412    }
413}
414
415impl Default for Mixer {
416    /// The default sample rate is 44100.
417    fn default() -> Self {
418        Self {
419            sample_rate: 44100,
420            generators: vec![],
421            oscillator_lookup: HashMap::new(),
422        }
423    }
424}