Mix all playing sounds and music into the output buffer.
(&mut self, output: &mut [f32])
| 170 | |
| 171 | /// Mix all playing sounds and music into the output buffer. |
| 172 | pub fn mix_output(&mut self, output: &mut [f32]) { |
| 173 | for sample in output.iter_mut() { |
| 174 | *sample = 0.0; |
| 175 | } |
| 176 | |
| 177 | // Spatial audio: compute listener-relative parameters once |
| 178 | let lx = self.listener_x; |
| 179 | let ly = self.listener_y; |
| 180 | let lz = self.listener_z; |
| 181 | let lfx = self.listener_forward_x; |
| 182 | let _lfy = self.listener_forward_y; // unused — listener "right" math projects out the Y component |
| 183 | let lfz = self.listener_forward_z; |
| 184 | // Listener right vector (cross product of forward and up=[0,1,0]) |
| 185 | let lrx = lfz; |
| 186 | let lrz = -lfx; |
| 187 | let lr_len = (lrx * lrx + lrz * lrz).sqrt().max(0.001); |
| 188 | |
| 189 | // Mix sound effects |
| 190 | self.playing.retain_mut(|p| { |
| 191 | if !p.playing { return false; } |
| 192 | let sound = match self.sounds.get(p.data_handle) { |
| 193 | Some(s) => s, |
| 194 | None => return false, |
| 195 | }; |
| 196 | |
| 197 | // Compute spatial gain and pan |
| 198 | let (gain_l, gain_r) = if p.spatial { |
| 199 | let dx = p.source_x - lx; |
| 200 | let dy = p.source_y - ly; |
| 201 | let dz = p.source_z - lz; |
| 202 | let dist = (dx*dx + dy*dy + dz*dz).sqrt().max(0.1); |
| 203 | // Distance attenuation: 1/distance, clamped |
| 204 | let attenuation = (1.0 / dist).min(1.0); |
| 205 | // Pan: dot product of source direction with listener right |
| 206 | let pan = ((dx * lrx + dz * lrz) / (dist * lr_len)).clamp(-1.0, 1.0); |
| 207 | let left = attenuation * (1.0 - pan) * 0.5; |
| 208 | let right = attenuation * (1.0 + pan) * 0.5; |
| 209 | (left, right) |
| 210 | } else { |
| 211 | (1.0, 1.0) |
| 212 | }; |
| 213 | |
| 214 | let base_vol = p.volume * self.master_volume; |
| 215 | let vol_l = base_vol * gain_l; |
| 216 | let vol_r = base_vol * gain_r; |
| 217 | let mut i = 0; |
| 218 | while i < output.len() && p.position < sound.samples.len() { |
| 219 | if sound.channels == 1 { |
| 220 | let sample = sound.samples[p.position]; |
| 221 | output[i] += sample * vol_l; |
| 222 | if i + 1 < output.len() { output[i + 1] += sample * vol_r; } |
| 223 | p.position += 1; |
| 224 | i += 2; |
| 225 | } else { |
| 226 | // For stereo sources, apply gain to each channel |
| 227 | output[i] += sound.samples[p.position] * vol_l; |
| 228 | p.position += 1; |
| 229 | if i + 1 < output.len() && p.position < sound.samples.len() { |
no test coverage detected