kroz-rs/src/resources/sound_output.rs

113 lines
3.9 KiB
Rust

use std::sync::Arc;
use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait},
SampleRate, Stream,
};
use fundsp::prelude::*;
use oddio::{Frames, FramesSignal, Gain, Handle, Mixer, MonoToStereo, Stop};
use super::sound_effects::{SoundEffect, SoundType};
pub type SoundSamples = Arc<Frames<f32>>;
pub type SoundEffectHandle = Handle<Stop<MonoToStereo<Gain<FramesSignal<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) => {
let mut c = square_hz(freq as f32);
c.reset(Some(self.sample_rate.0 as f64));
(0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize)
.map(|_| c.get_mono())
.collect::<Vec<f32>>()
}
SoundType::Sweep(start_freq, end_freq) => {
let mut c = lfo(|t| {
lerp(
start_freq as f32,
end_freq as f32,
t * sound.duration.as_secs_f32(),
)
}) >> square();
c.reset(Some(self.sample_rate.0 as f64));
(0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize)
.map(|_| c.get_mono())
.collect::<Vec<f32>>()
}
SoundType::Noise(min, max) => {
let mut c =
(((white() + dc(1.0)) * dc(max as f32 / 2.0)) + dc(min as f32)) >> square();
(0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize)
.map(|_| c.get_mono())
.collect::<Vec<f32>>()
}
})
.collect();
oddio::Frames::from_iter(self.sample_rate.0, effect_buffer.iter().copied())
}
pub fn play_sound(&mut self, samples: SoundSamples) -> SoundEffectHandle {
self.mixer_handle
.control::<oddio::Mixer<_>, _>()
.play(oddio::MonoToStereo::new(oddio::Gain::new(
oddio::FramesSignal::from(samples),
0.50,
)))
}
}