From 3ca0eb1eeaa6dd269cb292bd69210453fd1e35b6 Mon Sep 17 00:00:00 2001 From: Alex Page Date: Sun, 27 Nov 2022 22:36:39 -0500 Subject: [PATCH] Use simpler algorithm for PC speaker tones --- src/resources/sound_effects.rs | 7 ++-- src/resources/sound_output.rs | 67 ++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/resources/sound_effects.rs b/src/resources/sound_effects.rs index 7e512d5..c3ae8c5 100644 --- a/src/resources/sound_effects.rs +++ b/src/resources/sound_effects.rs @@ -46,11 +46,10 @@ impl SoundEffects { pub fn new(sound_output: &SoundOutput) -> Self { Self { startup: sound_output.render_sound_effect(&SoundEffect { - sounds: (30..400) - .step_by(8) + sounds: (1..800) .map(|x| Sound { - sound_type: SoundType::Tone(x), - duration: Duration::from_millis(24), + sound_type: SoundType::Tone(x / 2), + duration: Duration::from_millis(1), }) .collect(), }), diff --git a/src/resources/sound_output.rs b/src/resources/sound_output.rs index cdb96ff..5fb049a 100644 --- a/src/resources/sound_output.rs +++ b/src/resources/sound_output.rs @@ -1,4 +1,4 @@ -use std::{f32::consts::PI, sync::Arc}; +use std::sync::Arc; use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, @@ -61,41 +61,54 @@ impl SoundOutput { } pub fn render_sound_effect(&self, effect: &SoundEffect) -> SoundSamples { + // Keep these outside the loop to remember phase + let mut half_cycle_counter: u32 = 0; + let mut speaker_out: bool = false; + let effect_buffer: Vec = effect .sounds .iter() .flat_map(|sound| match sound.sound_type { - SoundType::Silence => (0 - ..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize) - .map(|_| 0f32) - .collect::>(), + SoundType::Silence => { + // Reset phase on silence + half_cycle_counter = 0; + speaker_out = false; + (0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize) + .map(|_| 0f32) + .collect::>() + } SoundType::Tone(freq) => { + // A frequency of 0 is silence if freq == 0 { + half_cycle_counter = 0; + speaker_out = false; + return (0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize) .map(|_| 0f32) .collect::>(); } - let num_harmonics = self.sample_rate.0 / (freq as u32 * 2); - let coefficients = (0..=num_harmonics) - .map(|i| { - if i == 0 { - return 0.0; - } - (i as f32 * 0.5 * PI).sin() * 2.0 / (i as f32 * PI) - }) - .collect::>(); - let scaler = freq as f32 * PI * 2.0 / self.sample_rate.0 as f32; - (0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize) - .map(|i| { - let temp = scaler * i as f32; - coefficients - .iter() - .enumerate() - .map(|(j, coef)| coef * (j as f32 * temp).cos()) - .sum::() - }) - .collect::>() + + let mut buffer: Vec = vec![ + 0.; + (self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) + as usize + ]; + let half_cycle_counter_upper_bound: u32 = self.sample_rate.0 / freq; + + for sample in &mut buffer { + if speaker_out { + *sample = 0.75; + } + + half_cycle_counter += 2; + if half_cycle_counter >= half_cycle_counter_upper_bound { + half_cycle_counter %= half_cycle_counter_upper_bound; + speaker_out = !speaker_out; + } + } + + buffer } SoundType::Noise(min, max) => { (0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize) @@ -116,9 +129,7 @@ impl SoundOutput { } pub fn play_sound(&mut self, samples: SoundSamples) -> SoundEffectHandle { - let mut gain = oddio::Gain::new( - oddio::FramesSignal::from(samples) - ); + let mut gain = oddio::Gain::new(oddio::FramesSignal::from(samples)); gain.set_gain(-10.0); self.mixer_handle .control::, _>()