Reorganize sound code
This commit is contained in:
parent
6ce6470236
commit
b48684201b
5 changed files with 43 additions and 43 deletions
125
src/resources/sound_output.rs
Normal file
125
src/resources/sound_output.rs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
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,
|
||||
)));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue