kroz-rs/src/resources/sound_output.rs
2022-01-27 17:22:14 -05:00

125 lines
4.3 KiB
Rust

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<Frames<f32>>;
pub struct SoundOutput {
mixer_handle: Handle<Mixer<[f32; 2]>>,
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<f32> = 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::<Vec<f32>>(),
SoundType::Tone(freq) => {
if freq == 0 {
return (0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32())
as usize)
.map(|_| 0f32)
.collect::<Vec<f32>>();
}
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::<Vec<f32>>();
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::<f32>()
})
.collect::<Vec<f32>>()
}
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::<Vec<f32>>()
}
})
.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::<oddio::Mixer<_>, _>()
.play(oddio::MonoToStereo::new(oddio::Gain::new(
oddio::FramesSignal::from(samples),
0.20,
)));
}
}