mod decoder;
mod lexer;
use core::num::NonZeroU32;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::prelude::*;
use bitflags::bitflags;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use rand::{seq::IndexedRandom as _, Rng};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub use decoder::{DecodeError, Decoder};
#[derive(Clone, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Deserialize, Serialize)
)]
#[cfg_attr(all(feature = "bevy_reflect", feature = "debug"), reflect(Debug))]
pub struct Packet {
pub name: String,
pub sfxs: HashMap<SfxId, Sfx>,
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Deserialize, Serialize)
)]
#[cfg_attr(all(feature = "bevy_reflect", feature = "debug"), reflect(Debug))]
pub struct Sfx {
pub id: SfxId,
pub name: String,
pub priority: u8,
pub typ: SfxType,
pub flags: SfxFlags,
pub sounds: Vec<Sound>,
}
impl Sfx {
pub fn random_sound(&self, rng: &mut impl Rng) -> Option<&Sound> {
self.sounds.choose(rng)
}
}
pub type SfxId = u8;
#[repr(u8)]
#[derive(Clone, Default, Deserialize, IntoPrimitive, PartialEq, Serialize, TryFromPrimitive)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Deserialize, PartialEq, Serialize)
)]
#[cfg_attr(all(feature = "bevy_reflect", feature = "debug"), reflect(Debug))]
pub enum SfxType {
#[default]
OneShot = 1,
SimultaneousOneShot = 2,
RandomOneShot = 3,
SequentialNonLooping = 4,
SequentialLooping = 5,
RandomLooping = 6,
}
bitflags! {
#[repr(transparent)]
#[derive(Clone, Copy, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(opaque), reflect(Default, Deserialize, Hash, PartialEq, Serialize))]
#[cfg_attr(all(feature = "bevy_reflect", feature = "debug"), reflect(Debug))]
pub struct SfxFlags: u8 {
const NONE = 0;
const UNKNOWN_FLAG_1 = 1 << 0;
const UNKNOWN_FLAG_2 = 1 << 1;
}
}
#[derive(Clone, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "debug", derive(Debug))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Deserialize, Serialize)
)]
#[cfg_attr(all(feature = "bevy_reflect", feature = "debug"), reflect(Debug))]
pub struct Sound {
pub file_stem: String,
pub frequency: u32,
pub frequency_deviation: u32,
pub volume: u8,
pub looped: bool,
pub attack: i8,
pub release: i8,
}
impl Sound {
pub fn random_playback_rate(&self, rng: &mut impl Rng, sample_rate: NonZeroU32) -> f32 {
let frequency = self.frequency as f32;
let base_playback_rate = frequency / sample_rate.get() as f32;
if self.frequency_deviation == 0 {
return base_playback_rate;
}
let random_frequency_deviation = rng.random_range(0..self.frequency_deviation);
base_playback_rate * (frequency / (frequency + random_frequency_deviation as f32))
}
pub fn linear_volume(&self) -> f32 {
self.volume as f32 / 255.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
fn deterministic_rand() -> ChaCha8Rng {
ChaCha8Rng::seed_from_u64(42)
}
#[test]
fn test_appear01_playback_rate() {
let mut rng = deterministic_rand();
let sound = Sound {
frequency: 44_100, frequency_deviation: 0,
..Default::default()
};
let playback_rate = sound.random_playback_rate(&mut rng, 16_000.try_into().unwrap());
assert_eq!(playback_rate, 2.75625);
}
#[test]
fn test_random_playback_rate() {
let mut rng = deterministic_rand();
let sound = Sound {
frequency: 22_050,
frequency_deviation: 100,
..Default::default()
};
let playback_rate = sound.random_playback_rate(&mut rng, 44_100.try_into().unwrap());
assert!(
(0.0..=1.0).contains(&playback_rate),
"Playback rate out of range"
);
}
}