use std::
{
io::Cursor,
sync::
{
Arc,
Mutex,
LazyLock,
},
};
use lewton::inside_ogg::OggStreamReader;
use crate::network::voice::consts;
struct ActiveEffect
{
buffer: Arc<Vec<f32>>,
position: usize,
}
pub enum SoundEffect
{
Join,
Leave,
}
static ACTIVE_EFFECTS: Mutex<Vec<ActiveEffect>> = Mutex::new(Vec::new());
pub static JOIN_SOUND: LazyLock<Arc<Vec<f32>>> = LazyLock::new(|| {
let bytes = include_bytes!("./join.ogg");
Arc::new(decode_ogg_from_bytes(bytes))
});
pub static LEAVE_SOUND: LazyLock<Arc<Vec<f32>>> = LazyLock::new(|| {
let bytes = include_bytes!("./leave.ogg");
Arc::new(decode_ogg_from_bytes(bytes))
});
fn decode_ogg_from_bytes(bytes: &[u8]) -> Vec<f32> {
let cursor = Cursor::new(bytes);
let mut reader = OggStreamReader::new(cursor).expect("Failed to read ogg");
let mut samples = Vec::new();
let channels = reader.ident_hdr.audio_channels as usize;
while let Ok(Some(packet)) = reader.read_dec_packet_itl()
{
for chunk in packet.chunks(channels)
{
let mut sum = 0.0;
for sample in chunk
{
sum += *sample as f32;
}
samples.push((sum / channels as f32) / 32768.0);
}
}
samples
}
pub fn queue_effect(effect: SoundEffect) {
let sound = match effect
{
SoundEffect::Join => JOIN_SOUND.clone(),
SoundEffect::Leave => LEAVE_SOUND.clone(),
};
if let Ok(mut effects) = ACTIVE_EFFECTS.lock()
{
effects.push(ActiveEffect
{
buffer: sound,
position: 0,
});
}
}
pub fn clear_effects() {
if let Ok(mut effects) = ACTIVE_EFFECTS.lock()
{
effects.clear();
}
}
pub fn play_effects(mixed_sample: &mut f32) {
if let Ok(mut effects) = ACTIVE_EFFECTS.try_lock()
{
effects.retain_mut(|effect|
{
if effect.position < effect.buffer.len() {
*mixed_sample += effect.buffer[effect.position] * consts::SOUND_EFFECT_VOLUME;
effect.position += 1;
true
} else { false } });
}
}
pub fn is_playing() -> bool {
ACTIVE_EFFECTS.lock().map(|e| !e.is_empty()).unwrap_or(false)
}