use std::{f32::consts::PI, sync::Arc}; use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, SampleRate, Stream, }; use oddio::{Frames, Handle, Mixer}; use rand::Rng; use super::sound_effects::{SoundEffect, SoundType}; pub type SoundSamples = Arc>; pub struct SoundOutput { mixer_handle: Handle>, sample_rate: SampleRate, _stream: Stream, } impl Default for SoundOutput { fn default() -> Self { Self::new() } } impl SoundOutput { pub fn new() -> Self { let host = cpal::default_host(); let device = host .default_output_device() .expect("no output device available"); let sample_rate = device.default_output_config().unwrap().sample_rate(); let config = cpal::StreamConfig { channels: 2, sample_rate, buffer_size: cpal::BufferSize::Default, }; let (mixer_handle, mixer) = oddio::split(oddio::Mixer::new()); let stream = device .build_output_stream( &config, move |out_flat: &mut [f32], _: &cpal::OutputCallbackInfo| { let out_stereo: &mut [[f32; 2]] = oddio::frame_stereo(out_flat); oddio::run(&mixer, sample_rate.0, out_stereo); }, move |err| { eprintln!("{}", err); }, ) .unwrap(); stream.play().unwrap(); Self { mixer_handle, sample_rate, _stream: stream, } } pub fn render_sound_effect(&self, effect: &SoundEffect) -> SoundSamples { 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::Tone(freq) => { if freq == 0 { 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::>() } SoundType::Noise(min, max) => { (0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize) .map(|i| { let t = i as f32 / self.sample_rate.0 as f32; (t * (rand::thread_rng().gen_range(min as f32..max as f32)) * 2.0 * std::f32::consts::PI) .sin() .signum() }) .collect::>() } }) .collect(); oddio::Frames::from_iter(self.sample_rate.0, effect_buffer.iter().copied()) } pub fn play_sound(&mut self, samples: SoundSamples) { self.mixer_handle .control::, _>() .play(oddio::MonoToStereo::new(oddio::Gain::new( oddio::FramesSignal::from(samples), 0.20, ))); } }