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}