pub const FREQUENCY: i32 = 22050;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use sdl2::{
audio::{AudioCallback, AudioDevice, AudioSpecDesired},
AudioSubsystem,
};
pub enum SoundType {
None,
Normal,
}
impl SoundType {
pub fn create(self) -> Rc<RefCell<dyn SoundDriver>> {
match self {
SoundType::None => Rc::new(RefCell::new(NullSound::new())),
SoundType::Normal => Rc::new(RefCell::new(Sound::new())),
}
}
}
pub trait SoundDriver {
fn add_channel(&mut self, channel: usize) -> Result<String, String>;
fn feed_samples(&mut self, channel: usize, freq: f32, volume: f32) -> Result<String, String>;
fn pause(&mut self, channel: usize) -> Result<String, String>;
fn play(&mut self, channel: usize) -> Result<String, String>;
}
pub struct NullSound {}
impl Default for NullSound {
fn default() -> Self {
Self::new()
}
}
impl NullSound {
pub fn new() -> Self {
Self {}
}
}
impl SoundDriver for NullSound {
fn add_channel(&mut self, _channel: usize) -> Result<String, String> {
Ok("null sound driver".to_string())
}
fn feed_samples(
&mut self,
_channel: usize,
_freq: f32,
_volume: f32,
) -> Result<String, String> {
Ok("null sound driver".to_string())
}
fn pause(&mut self, _channel: usize) -> Result<String, String> {
Ok("null sound driver".to_string())
}
fn play(&mut self, _channel: usize) -> Result<String, String> {
Ok("null sound driver".to_string())
}
}
#[allow(dead_code)]
enum Tone {
Square,
Sin,
}
struct Oscillator {
current_step: f32,
step_size: f32,
volume: f32,
mode: Tone,
}
impl Oscillator {
pub fn new(rate: f32, volume: f32, mode: Tone) -> Self {
Self {
current_step: 0.0,
volume,
step_size: 2.0 * std::f32::consts::PI / rate,
mode,
}
}
pub fn next(&mut self) -> f32 {
self.current_step += self.step_size;
match self.mode {
Tone::Square => {
if f32::sin(self.current_step) > 0.0 {
self.volume
} else {
-self.volume
}
}
Tone::Sin => f32::sin(self.current_step) * self.volume,
}
}
pub fn modify(&mut self, rate: f32, volume: f32) {
self.step_size = 2.0 * std::f32::consts::PI / rate;
self.volume = volume;
}
}
impl AudioCallback for Oscillator {
type Channel = f32;
fn callback(&mut self, out: &mut [Self::Channel]) {
for x in out.iter_mut() {
*x = self.next();
}
}
}
pub struct Sound {
audio_subsystem: AudioSubsystem,
devices: HashMap<usize, AudioDevice<Oscillator>>,
}
impl Default for Sound {
fn default() -> Self {
Self::new()
}
}
impl Sound {
pub fn new() -> Self {
let sdl_context = sdl2::init().unwrap();
let audio_subsystem = sdl_context.audio().unwrap();
Self {
audio_subsystem,
devices: HashMap::new(),
}
}
}
impl SoundDriver for Sound {
fn add_channel(&mut self, channel: usize) -> Result<String, String> {
if self.devices.contains_key(&channel) {
return Err(format!("cannot add same channel {}", channel));
}
let desired_spec = AudioSpecDesired {
freq: Some(FREQUENCY),
channels: Some(1), samples: Some(512),
};
let device = self
.audio_subsystem
.open_playback(None, &desired_spec, |_spec| {
Oscillator::new(1.0, 0.0, Tone::Sin)
})?;
self.devices.insert(channel, device);
Ok("channel added".to_string())
}
fn feed_samples(&mut self, channel: usize, freq: f32, volume: f32) -> Result<String, String> {
let audio_queue = self
.devices
.get_mut(&channel)
.ok_or(format!("no such channel {}", channel))?;
{
audio_queue
.lock()
.modify(FREQUENCY as f32 / freq, volume / 15.0);
}
Ok("sound queue".to_string())
}
fn pause(&mut self, channel: usize) -> Result<String, String> {
let audio_queue = self
.devices
.get(&channel)
.ok_or(format!("no such channel {}", channel))?;
audio_queue.pause();
Ok("sound pause".to_string())
}
fn play(&mut self, channel: usize) -> Result<String, String> {
let audio_queue = self
.devices
.get_mut(&channel)
.ok_or(format!("no such channel {}", channel))?;
audio_queue.resume();
Ok("sound play".to_string())
}
}