use prometeu_core::hardware::{AudioCommand, Channel, LoopMode, MAX_CHANNELS, OUTPUT_SAMPLE_RATE}; use std::time::Duration; pub struct AudioMixer { voices: [Channel; MAX_CHANNELS], pub last_processing_time: Duration, } impl AudioMixer { pub fn new() -> Self { Self { voices: Default::default(), last_processing_time: Duration::ZERO, } } pub fn process_command(&mut self, cmd: AudioCommand) { match cmd { AudioCommand::Play { sample, voice_id, volume, pan, pitch, priority, loop_mode, } => { if voice_id < MAX_CHANNELS { self.voices[voice_id] = Channel { sample: Some(sample), pos: 0.0, pitch, volume, pan, loop_mode, priority, }; } } AudioCommand::Stop { voice_id } => { if voice_id < MAX_CHANNELS { self.voices[voice_id].sample = None; } } AudioCommand::SetVolume { voice_id, volume } => { if voice_id < MAX_CHANNELS { self.voices[voice_id].volume = volume; } } AudioCommand::SetPan { voice_id, pan } => { if voice_id < MAX_CHANNELS { self.voices[voice_id].pan = pan; } } AudioCommand::SetPitch { voice_id, pitch } => { if voice_id < MAX_CHANNELS { self.voices[voice_id].pitch = pitch; } } } } pub fn fill_buffer(&mut self, buffer: &mut [f32]) { let start = std::time::Instant::now(); // Zera o buffer (estéreo) for sample in buffer.iter_mut() { *sample = 0.0; } for voice in self.voices.iter_mut() { let sample_data = match &voice.sample { Some(s) => s, None => continue, }; let pitch_ratio = sample_data.sample_rate as f64 / OUTPUT_SAMPLE_RATE as f64; let step = voice.pitch * pitch_ratio; let vol_f = voice.volume as f32 / 255.0; let pan_f = voice.pan as f32 / 255.0; let vol_l = vol_f * (1.0 - pan_f).sqrt(); let vol_r = vol_f * pan_f.sqrt(); for frame in buffer.chunks_exact_mut(2) { let pos_int = voice.pos as usize; let pos_fract = voice.pos - pos_int as f64; if pos_int >= sample_data.data.len() { voice.sample = None; break; } // Interpolação Linear let s1 = sample_data.data[pos_int] as f32 / 32768.0; let s2 = if pos_int + 1 < sample_data.data.len() { sample_data.data[pos_int + 1] as f32 / 32768.0 } else if voice.loop_mode == LoopMode::On { let loop_start = sample_data.loop_start.unwrap_or(0) as usize; sample_data.data[loop_start] as f32 / 32768.0 } else { 0.0 }; let sample_val = s1 + (s2 - s1) * pos_fract as f32; frame[0] += sample_val * vol_l; frame[1] += sample_val * vol_r; voice.pos += step; let end_pos = sample_data.loop_end.map(|e| e as f64).unwrap_or(sample_data.data.len() as f64); if voice.pos >= end_pos { if voice.loop_mode == LoopMode::On { let loop_start = sample_data.loop_start.unwrap_or(0) as f64; voice.pos = loop_start + (voice.pos - end_pos); } else { voice.sample = None; break; } } } } // Clamp final para evitar clipping (opcional se usarmos f32, mas bom para fidelidade) for sample in buffer.iter_mut() { *sample = sample.clamp(-1.0, 1.0); } self.last_processing_time = start.elapsed(); } }