use std::fmt;
use std::sync::{atomic, mpsc, Arc};
use all_is_cubes::fluff::Fluff;
use all_is_cubes::listen::Listener;
use all_is_cubes_ui::apps::Session;
use kira::manager::error::PlaySoundError;
use kira::manager::AudioManager;
use kira::sound::static_sound::StaticSoundData;
pub(crate) struct AudioOut {
#[allow(dead_code)] sender: mpsc::SyncSender<AudioCommand>,
}
impl fmt::Debug for AudioOut {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AudioOut").finish_non_exhaustive()
}
}
#[derive(Debug)]
enum AudioCommand {
Fluff(Fluff),
}
pub(crate) fn init_sound(session: &Session) -> Result<AudioOut, anyhow::Error> {
let manager = AudioManager::<kira::manager::backend::cpal::CpalBackend>::new(
kira::manager::AudioManagerSettings::default(),
)?;
let (sender, receiver) = mpsc::sync_channel(256);
std::thread::Builder::new()
.name("all_is_cubes_audio".to_owned())
.spawn(move || audio_command_thread(receiver, manager))
.unwrap();
session.listen_fluff(FluffListener::new(sender.clone()));
Ok(AudioOut { sender })
}
fn audio_command_thread(receiver: mpsc::Receiver<AudioCommand>, mut manager: AudioManager) {
let beep = StaticSoundData {
sample_rate: 44100,
frames: Arc::new(
(0..2205)
.map(|i| {
let wave = (i as f32 / 44.1 * 4.0).sin() * 0.1;
kira::dsp::Frame {
left: wave,
right: wave,
}
})
.collect(),
),
settings: kira::sound::static_sound::StaticSoundSettings::default(),
};
let happened = StaticSoundData {
sample_rate: 44100,
frames: Arc::new(
(0..220)
.map(|i| {
let wave = (i as f32 / 44.1 * 2.0).sin() * 0.1;
kira::dsp::Frame {
left: wave,
right: wave,
}
})
.collect(),
),
settings: kira::sound::static_sound::StaticSoundSettings::default(),
};
while let Ok(message) = receiver.recv() {
match message {
AudioCommand::Fluff(Fluff::Beep) => play_fluff(&mut manager, &beep),
AudioCommand::Fluff(Fluff::Happened) => play_fluff(&mut manager, &happened),
AudioCommand::Fluff(f) => log::debug!("No known sound for Fluff value: {f:?}"),
}
}
}
fn play_fluff(manager: &mut AudioManager, sound: &StaticSoundData) {
match manager.play(sound.clone()) {
Ok(_handle) => {}
Err(PlaySoundError::SoundLimitReached) => {
}
Err(error) => log::error!(
"Playback error: {error}",
error = all_is_cubes::util::ErrorChain(&error)
),
}
}
struct FluffListener {
sender: mpsc::SyncSender<AudioCommand>,
alive: atomic::AtomicBool,
}
impl FluffListener {
fn new(sender: mpsc::SyncSender<AudioCommand>) -> Self {
Self {
sender,
alive: atomic::AtomicBool::new(true),
}
}
}
impl Listener<Fluff> for FluffListener {
fn receive(&self, fluff: Fluff) {
match self.sender.try_send(AudioCommand::Fluff(fluff)) {
Ok(()) => {}
Err(mpsc::TrySendError::Full(_)) => {}
Err(mpsc::TrySendError::Disconnected(_)) => {
self.alive.store(false, atomic::Ordering::Relaxed);
}
}
}
fn alive(&self) -> bool {
self.alive.load(atomic::Ordering::Relaxed)
}
}