113 lines
3.9 KiB
Rust
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,
|
|
)))
|
|
}
|
|
}
|