use std::collections::HashMap;
use std::path::Path;
use std::sync::{Arc, Mutex};
use crate::{math::Position, EngineResult};
#[derive(Debug)]
pub struct AudioManager {
sources: HashMap<AudioId, AudioSource>,
listeners: Vec<AudioListener>,
next_id: AudioId,
master_volume: f32,
music_volume: f32,
sfx_volume: f32,
backend: AudioBackend,
mixer: AudioMixer,
effects: Vec<AudioEffect>,
}
impl AudioManager {
pub fn new() -> EngineResult<Self> {
Ok(Self {
sources: HashMap::new(),
listeners: Vec::new(),
next_id: AudioId(1),
master_volume: 1.0,
music_volume: 0.7,
sfx_volume: 1.0,
backend: AudioBackend::new()?,
mixer: AudioMixer::new(),
effects: Vec::new(),
})
}
pub fn load_audio<P: AsRef<Path>>(&mut self, path: P) -> EngineResult<AudioId> {
let id = self.next_id;
self.next_id.0 += 1;
let source = AudioSource::from_file(path.as_ref())?;
self.sources.insert(id, source);
Ok(id)
}
pub fn load_audio_from_memory(&mut self, data: &[u8], format: AudioFormat) -> EngineResult<AudioId> {
let id = self.next_id;
self.next_id.0 += 1;
let source = AudioSource::from_memory(data, format)?;
self.sources.insert(id, source);
Ok(id)
}
pub fn play(&mut self, id: AudioId) -> EngineResult<PlaybackId> {
self.play_with_settings(id, PlaybackSettings::default())
}
pub fn play_with_settings(&mut self, id: AudioId, settings: PlaybackSettings) -> EngineResult<PlaybackId> {
if let Some(source) = self.sources.get(&id) {
let playback_id = self.backend.play(source, &settings)?;
Ok(playback_id)
} else {
Err("Audio source not found".into())
}
}
pub fn play_at_position(&mut self, id: AudioId, position: Position, settings: PlaybackSettings) -> EngineResult<PlaybackId> {
if let Some(source) = self.sources.get(&id) {
let mut spatial_settings = settings;
spatial_settings.position = Some(SpatialPosition {
position,
velocity: Position::new(0.0, 0.0),
attenuation: AttenuationModel::Linear { max_distance: 1000.0 },
});
let playback_id = self.backend.play(source, &spatial_settings)?;
Ok(playback_id)
} else {
Err("Audio source not found".into())
}
}
pub fn stop_playback(&mut self, playback_id: PlaybackId) {
self.backend.stop(playback_id);
}
pub fn stop_all(&mut self) {
self.backend.stop_all();
}
pub fn pause_playback(&mut self, playback_id: PlaybackId) {
self.backend.pause(playback_id);
}
pub fn resume_playback(&mut self, playback_id: PlaybackId) {
self.backend.resume(playback_id);
}
pub fn set_master_volume(&mut self, volume: f32) {
self.master_volume = volume.clamp(0.0, 1.0);
self.backend.set_master_volume(self.master_volume);
}
pub fn set_music_volume(&mut self, volume: f32) {
self.music_volume = volume.clamp(0.0, 1.0);
}
pub fn set_sfx_volume(&mut self, volume: f32) {
self.sfx_volume = volume.clamp(0.0, 1.0);
}
pub fn add_listener(&mut self, listener: AudioListener) {
self.listeners.push(listener);
self.backend.update_listeners(&self.listeners);
}
pub fn update_listener(&mut self, index: usize, listener: AudioListener) {
if index < self.listeners.len() {
self.listeners[index] = listener;
self.backend.update_listeners(&self.listeners);
}
}
pub fn add_effect(&mut self, effect: AudioEffect) {
self.effects.push(effect);
self.mixer.set_effects(&self.effects);
}
pub fn clear_effects(&mut self) {
self.effects.clear();
self.mixer.clear_effects();
}
pub fn update(&mut self, delta_time: f32) {
self.backend.update(delta_time);
self.mixer.update(delta_time);
for listener in &self.listeners {
self.backend.update_spatial_audio(listener);
}
}
pub fn get_playback_info(&self, playback_id: PlaybackId) -> Option<PlaybackInfo> {
self.backend.get_playback_info(playback_id)
}
pub fn is_playing(&self, playback_id: PlaybackId) -> bool {
self.backend.is_playing(playback_id)
}
pub fn get_duration(&self, id: AudioId) -> Option<f32> {
self.sources.get(&id).map(|source| source.duration)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AudioId(u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PlaybackId(u32);
#[derive(Debug, Clone)]
pub struct AudioSource {
pub data: Arc<[u8]>,
pub format: AudioFormat,
pub sample_rate: u32,
pub channels: u16,
pub duration: f32,
pub metadata: AudioMetadata,
}
impl AudioSource {
pub fn from_file<P: AsRef<Path>>(path: P) -> EngineResult<Self> {
let path = path.as_ref();
let extension = path.extension().and_then(|ext| ext.to_str()).unwrap_or("");
match extension.to_lowercase().as_str() {
"wav" => Self::load_wav(path),
"mp3" => Self::load_mp3(path),
"ogg" => Self::load_ogg(path),
"flac" => Self::load_flac(path),
_ => Err(format!("Unsupported audio format: {}", extension).into()),
}
}
pub fn from_memory(data: &[u8], format: AudioFormat) -> EngineResult<Self> {
match format {
AudioFormat::Wav => Self::decode_wav(data),
AudioFormat::Mp3 => Self::decode_mp3(data),
AudioFormat::Ogg => Self::decode_ogg(data),
AudioFormat::Flac => Self::decode_flac(data),
AudioFormat::Raw { sample_rate, channels } => {
Ok(Self {
data: Arc::from(data),
format,
sample_rate,
channels,
duration: data.len() as f32 / (sample_rate as f32 * channels as f32 * 2.0),
metadata: AudioMetadata::default(),
})
}
}
}
fn load_wav<P: AsRef<Path>>(path: P) -> EngineResult<Self> {
let data = std::fs::read(path)?;
Self::decode_wav(&data)
}
fn load_mp3<P: AsRef<Path>>(path: P) -> EngineResult<Self> {
let data = std::fs::read(path)?;
Self::decode_mp3(&data)
}
fn load_ogg<P: AsRef<Path>>(path: P) -> EngineResult<Self> {
let data = std::fs::read(path)?;
Self::decode_ogg(&data)
}
fn load_flac<P: AsRef<Path>>(path: P) -> EngineResult<Self> {
let data = std::fs::read(path)?;
Self::decode_flac(&data)
}
fn decode_wav(data: &[u8]) -> EngineResult<Self> {
if data.len() < 44 {
return Err("Invalid WAV file: too short".into());
}
if &data[0..4] != b"RIFF" {
return Err("Invalid WAV file: missing RIFF header".into());
}
if &data[8..12] != b"WAVE" {
return Err("Invalid WAV file: not WAVE format".into());
}
let mut offset = 12;
let mut sample_rate = 0;
let mut channels = 0;
let mut bits_per_sample = 0;
let mut data_offset = 0;
let mut data_size = 0;
while offset < data.len() - 8 {
let chunk_id = &data[offset..offset + 4];
let chunk_size = u32::from_le_bytes([
data[offset + 4], data[offset + 5], data[offset + 6], data[offset + 7]
]) as usize;
if chunk_id == b"fmt " {
if chunk_size < 16 {
return Err("Invalid WAV file: format chunk too small".into());
}
let format_tag = u16::from_le_bytes([data[offset + 8], data[offset + 9]]);
if format_tag != 1 {
return Err("Unsupported WAV format: only PCM supported".into());
}
channels = u16::from_le_bytes([data[offset + 10], data[offset + 11]]);
sample_rate = u32::from_le_bytes([
data[offset + 12], data[offset + 13], data[offset + 14], data[offset + 15]
]);
bits_per_sample = u16::from_le_bytes([data[offset + 22], data[offset + 23]]);
} else if chunk_id == b"data" {
data_offset = offset + 8;
data_size = chunk_size;
break;
}
offset += 8 + chunk_size;
if chunk_size % 2 == 1 {
offset += 1;
}
}
if sample_rate == 0 || channels == 0 || data_offset == 0 {
return Err("Invalid WAV file: missing required chunks".into());
}
let audio_data = &data[data_offset..data_offset + data_size];
let duration = data_size as f32 / (sample_rate as f32 * channels as f32 * (bits_per_sample as f32 / 8.0));
Ok(Self {
data: Arc::from(audio_data),
format: AudioFormat::Wav,
sample_rate,
channels,
duration,
metadata: AudioMetadata::default(),
})
}
fn decode_mp3(data: &[u8]) -> EngineResult<Self> {
Ok(Self {
data: Arc::from(data),
format: AudioFormat::Mp3,
sample_rate: 44100,
channels: 2,
duration: 0.0,
metadata: AudioMetadata::default(),
})
}
fn decode_ogg(data: &[u8]) -> EngineResult<Self> {
Ok(Self {
data: Arc::from(data),
format: AudioFormat::Ogg,
sample_rate: 44100,
channels: 2,
duration: 0.0,
metadata: AudioMetadata::default(),
})
}
fn decode_flac(data: &[u8]) -> EngineResult<Self> {
Ok(Self {
data: Arc::from(data),
format: AudioFormat::Flac,
sample_rate: 44100,
channels: 2,
duration: 0.0,
metadata: AudioMetadata::default(),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AudioFormat {
Wav,
Mp3,
Ogg,
Flac,
Raw { sample_rate: u32, channels: u16 },
}
#[derive(Debug, Clone, Default)]
pub struct AudioMetadata {
pub title: Option<String>,
pub artist: Option<String>,
pub album: Option<String>,
pub year: Option<u32>,
pub genre: Option<String>,
pub duration: Option<f32>,
}
#[derive(Debug, Clone)]
pub struct PlaybackSettings {
pub volume: f32,
pub pitch: f32,
pub looping: bool,
pub position: Option<SpatialPosition>,
pub fade_in: Option<f32>,
pub fade_out: Option<f32>,
pub category: AudioCategory,
pub priority: AudioPriority,
}
impl Default for PlaybackSettings {
fn default() -> Self {
Self {
volume: 1.0,
pitch: 1.0,
looping: false,
position: None,
fade_in: None,
fade_out: None,
category: AudioCategory::SFX,
priority: AudioPriority::Normal,
}
}
}
#[derive(Debug, Clone)]
pub struct SpatialPosition {
pub position: Position,
pub velocity: Position,
pub attenuation: AttenuationModel,
}
#[derive(Debug, Clone)]
pub enum AttenuationModel {
None,
Linear { max_distance: f32 },
Exponential { rolloff_factor: f32 },
InverseSquare { reference_distance: f32 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AudioCategory {
Music,
SFX,
Voice,
Ambient,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AudioPriority {
Low,
Normal,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct AudioListener {
pub position: Position,
pub velocity: Position,
pub forward: Position,
pub up: Position,
pub gain: f32,
}
impl Default for AudioListener {
fn default() -> Self {
Self {
position: Position::new(0.0, 0.0),
velocity: Position::new(0.0, 0.0),
forward: Position::new(0.0, -1.0),
up: Position::new(0.0, 1.0),
gain: 1.0,
}
}
}
#[derive(Debug, Clone)]
pub struct PlaybackInfo {
pub position: f32,
pub duration: f32,
pub volume: f32,
pub pitch: f32,
pub is_playing: bool,
pub is_paused: bool,
pub is_looping: bool,
}
#[derive(Debug, Clone)]
pub enum AudioEffect {
Reverb {
decay_time: f32,
density: f32,
diffusion: f32,
gain: f32,
gain_hf: f32,
decay_hf_ratio: f32,
},
Echo {
delay: f32,
decay: f32,
feedback: f32,
dry_wet: f32,
},
Chorus {
rate: f32,
depth: f32,
feedback: f32,
delay: f32,
},
Distortion {
gain: f32,
edge: f32,
low_eq_gain: f32,
eq_center: f32,
eq_bandwidth: f32,
},
Equalizer {
low_gain: f32,
mid1_gain: f32,
mid1_center: f32,
mid1_width: f32,
mid2_gain: f32,
mid2_center: f32,
mid2_width: f32,
high_gain: f32,
},
Compressor {
threshold: f32,
ratio: f32,
attack: f32,
release: f32,
makeup_gain: f32,
},
LowPassFilter {
cutoff: f32,
resonance: f32,
},
HighPassFilter {
cutoff: f32,
resonance: f32,
},
}
#[derive(Debug)]
pub struct AudioBackend {
device: Arc<Mutex<AudioDevice>>,
context: Arc<Mutex<AudioContext>>,
playback_instances: HashMap<PlaybackId, PlaybackInstance>,
next_playback_id: PlaybackId,
}
impl AudioBackend {
pub fn new() -> EngineResult<Self> {
let device = Arc::new(Mutex::new(AudioDevice::new()?));
let context = Arc::new(Mutex::new(AudioContext::new(&device)?));
Ok(Self {
device,
context,
playback_instances: HashMap::new(),
next_playback_id: PlaybackId(1),
})
}
pub fn play(&mut self, source: &AudioSource, settings: &PlaybackSettings) -> EngineResult<PlaybackId> {
let id = self.next_playback_id;
self.next_playback_id.0 += 1;
let instance = PlaybackInstance::new(source, settings)?;
self.playback_instances.insert(id, instance);
if let Ok(mut context) = self.context.lock() {
context.start_playback(id, source, settings)?;
}
Ok(id)
}
pub fn stop(&mut self, playback_id: PlaybackId) {
if let Some(instance) = self.playback_instances.get_mut(&playback_id) {
instance.stop();
}
if let Ok(mut context) = self.context.lock() {
context.stop_playback(playback_id);
}
}
pub fn stop_all(&mut self) {
for instance in self.playback_instances.values_mut() {
instance.stop();
}
if let Ok(mut context) = self.context.lock() {
context.stop_all();
}
self.playback_instances.clear();
}
pub fn pause(&mut self, playback_id: PlaybackId) {
if let Some(instance) = self.playback_instances.get_mut(&playback_id) {
instance.pause();
}
}
pub fn resume(&mut self, playback_id: PlaybackId) {
if let Some(instance) = self.playback_instances.get_mut(&playback_id) {
instance.resume();
}
}
pub fn set_master_volume(&mut self, volume: f32) {
if let Ok(mut context) = self.context.lock() {
context.set_master_volume(volume);
}
}
pub fn update_listeners(&mut self, listeners: &[AudioListener]) {
if let Ok(mut context) = self.context.lock() {
context.update_listeners(listeners);
}
}
pub fn update(&mut self, delta_time: f32) {
self.playback_instances.retain(|_, instance| !instance.is_finished());
for instance in self.playback_instances.values_mut() {
instance.update(delta_time);
}
}
pub fn update_spatial_audio(&mut self, listener: &AudioListener) {
if let Ok(mut context) = self.context.lock() {
context.update_spatial_audio(listener);
}
}
pub fn get_playback_info(&self, playback_id: PlaybackId) -> Option<PlaybackInfo> {
self.playback_instances.get(&playback_id).map(|instance| instance.get_info())
}
pub fn is_playing(&self, playback_id: PlaybackId) -> bool {
self.playback_instances.get(&playback_id)
.map(|instance| instance.is_playing())
.unwrap_or(false)
}
}
#[derive(Debug)]
pub struct AudioDevice {
sample_rate: u32,
channels: u16,
buffer_size: usize,
latency: f32,
}
impl AudioDevice {
pub fn new() -> EngineResult<Self> {
Ok(Self {
sample_rate: 44100,
channels: 2,
buffer_size: 1024,
latency: 0.023,
})
}
pub fn get_sample_rate(&self) -> u32 {
self.sample_rate
}
pub fn get_channels(&self) -> u16 {
self.channels
}
pub fn get_buffer_size(&self) -> usize {
self.buffer_size
}
pub fn get_latency(&self) -> f32 {
self.latency
}
}
#[derive(Debug)]
pub struct AudioContext {
master_volume: f32,
listeners: Vec<AudioListener>,
active_sources: HashMap<PlaybackId, ContextSource>,
}
impl AudioContext {
pub fn new(_device: &Arc<Mutex<AudioDevice>>) -> EngineResult<Self> {
Ok(Self {
master_volume: 1.0,
listeners: vec![AudioListener::default()],
active_sources: HashMap::new(),
})
}
pub fn start_playback(&mut self, id: PlaybackId, source: &AudioSource, settings: &PlaybackSettings) -> EngineResult<()> {
let context_source = ContextSource::new(source, settings)?;
self.active_sources.insert(id, context_source);
Ok(())
}
pub fn stop_playback(&mut self, id: PlaybackId) {
self.active_sources.remove(&id);
}
pub fn stop_all(&mut self) {
self.active_sources.clear();
}
pub fn set_master_volume(&mut self, volume: f32) {
self.master_volume = volume;
}
pub fn update_listeners(&mut self, listeners: &[AudioListener]) {
self.listeners = listeners.to_vec();
}
pub fn update_spatial_audio(&mut self, _listener: &AudioListener) {
for source in self.active_sources.values_mut() {
source.update_spatial_audio(_listener);
}
}
}
#[derive(Debug)]
pub struct ContextSource {
position: f32,
volume: f32,
pitch: f32,
spatial_position: Option<SpatialPosition>,
}
impl ContextSource {
pub fn new(_source: &AudioSource, settings: &PlaybackSettings) -> EngineResult<Self> {
Ok(Self {
position: 0.0,
volume: settings.volume,
pitch: settings.pitch,
spatial_position: settings.position.clone(),
})
}
pub fn update_spatial_audio(&mut self, _listener: &AudioListener) {
if let Some(spatial) = &self.spatial_position {
let distance = spatial.position.distance(_listener.position);
match spatial.attenuation {
AttenuationModel::Linear { max_distance } => {
self.volume = (1.0 - (distance / max_distance).min(1.0)).max(0.0);
}
AttenuationModel::Exponential { rolloff_factor } => {
self.volume = 1.0 / (1.0 + rolloff_factor * distance);
}
AttenuationModel::InverseSquare { reference_distance } => {
self.volume = reference_distance / (reference_distance + distance);
}
AttenuationModel::None => {}
}
}
}
}
#[derive(Debug)]
pub struct PlaybackInstance {
position: f32,
volume: f32,
pitch: f32,
is_playing: bool,
is_paused: bool,
is_looping: bool,
duration: f32,
fade_in_time: Option<f32>,
fade_out_time: Option<f32>,
current_fade: f32,
}
impl PlaybackInstance {
pub fn new(source: &AudioSource, settings: &PlaybackSettings) -> EngineResult<Self> {
Ok(Self {
position: 0.0,
volume: settings.volume,
pitch: settings.pitch,
is_playing: true,
is_paused: false,
is_looping: settings.looping,
duration: source.duration,
fade_in_time: settings.fade_in,
fade_out_time: settings.fade_out,
current_fade: 1.0,
})
}
pub fn update(&mut self, delta_time: f32) {
if self.is_playing && !self.is_paused {
self.position += delta_time * self.pitch;
if let Some(fade_in) = self.fade_in_time {
if self.position < fade_in {
self.current_fade = self.position / fade_in;
}
}
if let Some(fade_out) = self.fade_out_time {
let fade_start = self.duration - fade_out;
if self.position > fade_start {
self.current_fade = (self.duration - self.position) / fade_out;
}
}
if self.position >= self.duration {
if self.is_looping {
self.position = 0.0;
} else {
self.is_playing = false;
}
}
}
}
pub fn stop(&mut self) {
self.is_playing = false;
}
pub fn pause(&mut self) {
self.is_paused = true;
}
pub fn resume(&mut self) {
self.is_paused = false;
}
pub fn is_finished(&self) -> bool {
!self.is_playing
}
pub fn is_playing(&self) -> bool {
self.is_playing && !self.is_paused
}
pub fn get_info(&self) -> PlaybackInfo {
PlaybackInfo {
position: self.position,
duration: self.duration,
volume: self.volume * self.current_fade,
pitch: self.pitch,
is_playing: self.is_playing,
is_paused: self.is_paused,
is_looping: self.is_looping,
}
}
}
#[derive(Debug)]
pub struct AudioMixer {
effects: Vec<AudioEffect>,
input_buffer: Vec<f32>,
output_buffer: Vec<f32>,
temp_buffer: Vec<f32>,
}
impl AudioMixer {
pub fn new() -> Self {
Self {
effects: Vec::new(),
input_buffer: vec![0.0; 1024],
output_buffer: vec![0.0; 1024],
temp_buffer: vec![0.0; 1024],
}
}
pub fn set_effects(&mut self, effects: &[AudioEffect]) {
self.effects = effects.to_vec();
}
pub fn clear_effects(&mut self) {
self.effects.clear();
}
pub fn update(&mut self, _delta_time: f32) {
self.process_effects();
}
fn process_effects(&mut self) {
if self.effects.is_empty() {
return;
}
self.temp_buffer.copy_from_slice(&self.input_buffer);
let effects = self.effects.clone();
for effect in &effects {
self.apply_effect(effect);
}
self.output_buffer.copy_from_slice(&self.temp_buffer);
}
fn apply_effect(&mut self, effect: &AudioEffect) {
match effect {
AudioEffect::Reverb { decay_time, density, diffusion, gain, gain_hf, decay_hf_ratio } => {
self.apply_reverb(*decay_time, *density, *diffusion, *gain, *gain_hf, *decay_hf_ratio);
}
AudioEffect::Echo { delay, decay, feedback, dry_wet } => {
self.apply_echo(*delay, *decay, *feedback, *dry_wet);
}
AudioEffect::Chorus { rate, depth, feedback, delay } => {
self.apply_chorus(*rate, *depth, *feedback, *delay);
}
AudioEffect::Distortion { gain, edge, low_eq_gain, eq_center, eq_bandwidth } => {
self.apply_distortion(*gain, *edge, *low_eq_gain, *eq_center, *eq_bandwidth);
}
AudioEffect::Equalizer { low_gain, mid1_gain, mid1_center, mid1_width, mid2_gain, mid2_center, mid2_width, high_gain } => {
self.apply_equalizer(*low_gain, *mid1_gain, *mid1_center, *mid1_width, *mid2_gain, *mid2_center, *mid2_width, *high_gain);
}
AudioEffect::Compressor { threshold, ratio, attack, release, makeup_gain } => {
self.apply_compressor(*threshold, *ratio, *attack, *release, *makeup_gain);
}
AudioEffect::LowPassFilter { cutoff, resonance } => {
self.apply_low_pass_filter(*cutoff, *resonance);
}
AudioEffect::HighPassFilter { cutoff, resonance } => {
self.apply_high_pass_filter(*cutoff, *resonance);
}
}
}
fn apply_reverb(&mut self, _decay_time: f32, _density: f32, _diffusion: f32, _gain: f32, _gain_hf: f32, _decay_hf_ratio: f32) {
}
fn apply_echo(&mut self, _delay: f32, _decay: f32, _feedback: f32, _dry_wet: f32) {
}
fn apply_chorus(&mut self, _rate: f32, _depth: f32, _feedback: f32, _delay: f32) {
}
fn apply_distortion(&mut self, gain: f32, _edge: f32, _low_eq_gain: f32, _eq_center: f32, _eq_bandwidth: f32) {
for sample in &mut self.temp_buffer {
*sample = (*sample * gain).tanh();
}
}
fn apply_equalizer(&mut self, _low_gain: f32, _mid1_gain: f32, _mid1_center: f32, _mid1_width: f32, _mid2_gain: f32, _mid2_center: f32, _mid2_width: f32, _high_gain: f32) {
}
fn apply_compressor(&mut self, _threshold: f32, _ratio: f32, _attack: f32, _release: f32, _makeup_gain: f32) {
}
fn apply_low_pass_filter(&mut self, _cutoff: f32, _resonance: f32) {
}
fn apply_high_pass_filter(&mut self, _cutoff: f32, _resonance: f32) {
}
}
impl Default for AudioManager {
fn default() -> Self {
Self::new().unwrap_or_else(|_| Self {
sources: HashMap::new(),
listeners: Vec::new(),
next_id: AudioId(1),
master_volume: 1.0,
music_volume: 0.7,
sfx_volume: 1.0,
backend: AudioBackend {
device: Arc::new(Mutex::new(AudioDevice {
sample_rate: 44100,
channels: 2,
buffer_size: 1024,
latency: 0.023,
})),
context: Arc::new(Mutex::new(AudioContext {
master_volume: 1.0,
listeners: vec![AudioListener::default()],
active_sources: HashMap::new(),
})),
playback_instances: HashMap::new(),
next_playback_id: PlaybackId(1),
},
mixer: AudioMixer::new(),
effects: Vec::new(),
})
}
}