#![forbid(unsafe_code)]
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use anyhow::{Result, anyhow};
use kira::sound::static_sound::{StaticSoundData, StaticSoundHandle, StaticSoundSettings};
use kira::{AudioManager, AudioManagerSettings, Decibels, DefaultBackend, Frame, Tween};
#[cfg(feature = "synth")]
mod synth;
#[cfg(feature = "synth")]
pub use synth::Synth;
#[cfg(feature = "synth")]
pub use synthie;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct SoundId(pub u32);
pub struct Audio {
manager: AudioManager<DefaultBackend>,
sounds: HashMap<SoundId, StaticSoundData>,
next_id: u32,
}
impl Audio {
pub fn new() -> Result<Self> {
let manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())
.map_err(|e| anyhow!("kira AudioManager init: {e}"))?;
Ok(Self {
manager,
sounds: HashMap::new(),
next_id: 1,
})
}
pub fn load_sound(&mut self, path: impl AsRef<Path>) -> Result<SoundId> {
let data = StaticSoundData::from_file(path.as_ref())
.map_err(|e| anyhow!("kira load {}: {e}", path.as_ref().display()))?;
let id = SoundId(self.next_id);
self.next_id += 1;
self.sounds.insert(id, data);
Ok(id)
}
pub fn play(&mut self, id: SoundId) -> Result<StaticSoundHandle> {
let data = self
.sounds
.get(&id)
.ok_or_else(|| anyhow!("unknown SoundId {id:?}"))?
.clone();
self.manager
.play(data)
.map_err(|e| anyhow!("kira play: {e}"))
}
pub fn play_samples(&mut self, samples: &[f32], sample_rate: u32) -> Result<StaticSoundHandle> {
let frames: Arc<[Frame]> = samples
.iter()
.map(|&s| Frame { left: s, right: s })
.collect();
let data = StaticSoundData {
sample_rate,
frames,
settings: StaticSoundSettings::default(),
slice: None,
};
self.manager
.play(data)
.map_err(|e| anyhow!("kira play: {e}"))
}
pub fn set_master_volume(&mut self, volume: f32) {
let gain = if volume <= 0.0 {
Decibels::SILENCE
} else {
Decibels::from(20.0 * volume.log10())
};
self.manager.main_track().set_volume(gain, Tween::default());
}
}