Use simpler algorithm for PC speaker tones
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Alex Page 2022-11-27 22:36:39 -05:00
parent 4b0b67c69a
commit 3ca0eb1eea
2 changed files with 42 additions and 32 deletions

View file

@ -46,11 +46,10 @@ impl SoundEffects {
pub fn new(sound_output: &SoundOutput) -> Self { pub fn new(sound_output: &SoundOutput) -> Self {
Self { Self {
startup: sound_output.render_sound_effect(&SoundEffect { startup: sound_output.render_sound_effect(&SoundEffect {
sounds: (30..400) sounds: (1..800)
.step_by(8)
.map(|x| Sound { .map(|x| Sound {
sound_type: SoundType::Tone(x), sound_type: SoundType::Tone(x / 2),
duration: Duration::from_millis(24), duration: Duration::from_millis(1),
}) })
.collect(), .collect(),
}), }),

View file

@ -1,4 +1,4 @@
use std::{f32::consts::PI, sync::Arc}; use std::sync::Arc;
use cpal::{ use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait}, traits::{DeviceTrait, HostTrait, StreamTrait},
@ -61,41 +61,54 @@ impl SoundOutput {
} }
pub fn render_sound_effect(&self, effect: &SoundEffect) -> SoundSamples { 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<f32> = effect let effect_buffer: Vec<f32> = effect
.sounds .sounds
.iter() .iter()
.flat_map(|sound| match sound.sound_type { .flat_map(|sound| match sound.sound_type {
SoundType::Silence => (0 SoundType::Silence => {
..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize) // Reset phase on silence
.map(|_| 0f32) half_cycle_counter = 0;
.collect::<Vec<f32>>(), speaker_out = false;
(0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize)
.map(|_| 0f32)
.collect::<Vec<f32>>()
}
SoundType::Tone(freq) => { SoundType::Tone(freq) => {
// A frequency of 0 is silence
if freq == 0 { if freq == 0 {
half_cycle_counter = 0;
speaker_out = false;
return (0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) return (0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32())
as usize) as usize)
.map(|_| 0f32) .map(|_| 0f32)
.collect::<Vec<f32>>(); .collect::<Vec<f32>>();
} }
let num_harmonics = self.sample_rate.0 / (freq as u32 * 2);
let coefficients = (0..=num_harmonics) let mut buffer: Vec<f32> = vec![
.map(|i| { 0.;
if i == 0 { (self.sample_rate.0 as f32 * sound.duration.as_secs_f32())
return 0.0; as usize
} ];
(i as f32 * 0.5 * PI).sin() * 2.0 / (i as f32 * PI) let half_cycle_counter_upper_bound: u32 = self.sample_rate.0 / freq;
})
.collect::<Vec<f32>>(); for sample in &mut buffer {
let scaler = freq as f32 * PI * 2.0 / self.sample_rate.0 as f32; if speaker_out {
(0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize) *sample = 0.75;
.map(|i| { }
let temp = scaler * i as f32;
coefficients half_cycle_counter += 2;
.iter() if half_cycle_counter >= half_cycle_counter_upper_bound {
.enumerate() half_cycle_counter %= half_cycle_counter_upper_bound;
.map(|(j, coef)| coef * (j as f32 * temp).cos()) speaker_out = !speaker_out;
.sum::<f32>() }
}) }
.collect::<Vec<f32>>()
buffer
} }
SoundType::Noise(min, max) => { SoundType::Noise(min, max) => {
(0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize) (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 { pub fn play_sound(&mut self, samples: SoundSamples) -> SoundEffectHandle {
let mut gain = oddio::Gain::new( let mut gain = oddio::Gain::new(oddio::FramesSignal::from(samples));
oddio::FramesSignal::from(samples)
);
gain.set_gain(-10.0); gain.set_gain(-10.0);
self.mixer_handle self.mixer_handle
.control::<oddio::Mixer<_>, _>() .control::<oddio::Mixer<_>, _>()