diff --git a/src/core/mod.rs b/src/core/mod.rs index 0d254f8..58fa7e4 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -294,6 +294,17 @@ fn apply_volume(frame: &mut [i16], vol: f32) { } } +/// Apply the listener's per-peer volume for the audio sender id currently being +/// mixed. The map key must be the same `EndpointId` used for the jitter buffer. +fn apply_peer_volume( + frame: &mut [i16], + peer_id: EndpointId, + volumes: &HashMap, +) { + let vol = volumes.get(&peer_id).copied().unwrap_or(1.0); + apply_volume(frame, vol); +} + /// Normalized RMS level of a frame in `[0.0, 1.0]` (32768 = full scale), for the /// UI level meter. An empty frame reads as 0.0. fn frame_level(frame: &[i16]) -> f32 { @@ -1707,8 +1718,7 @@ async fn run_core_loop( peer_noise_gates.remove(&peer_id); } - let vol = current_volumes.get(&peer_id).copied().unwrap_or(1.0); - apply_volume(&mut frame, vol); + apply_peer_volume(&mut frame, peer_id, ¤t_volumes); let eq_settings = current_eq .get(&peer_id) @@ -2776,9 +2786,9 @@ async fn run_core_loop( #[cfg(test)] mod tests { use super::{ - admit_retained, apply_volume, audio_datagram_len_ok, frame_level, mix_frames, - mix_stereo_frames, next_game_change, should_auto_fetch, stereo_to_mono, KnownPeers, - MicLevelMeter, PeerSpeakTicket, MAX_OPUS_PAYLOAD, MAX_RETAINED_PEERS, + admit_retained, apply_peer_volume, apply_volume, audio_datagram_len_ok, frame_level, + mix_frames, mix_stereo_frames, next_game_change, should_auto_fetch, stereo_to_mono, + KnownPeers, MicLevelMeter, PeerSpeakTicket, MAX_OPUS_PAYLOAD, MAX_RETAINED_PEERS, MIC_LEVEL_REPORT_SAMPLES, }; @@ -3038,6 +3048,30 @@ mod tests { assert_eq!(frame, vec![2000, -2000]); } + #[test] + fn peer_volume_map_scales_the_matching_audio_peer_frame() { + let peer = iroh::SecretKey::generate().public(); + let other_peer = iroh::SecretKey::generate().public(); + let volumes = std::collections::HashMap::from([(peer, 0.5), (other_peer, 2.0)]); + let mut frame = vec![100, -200, 300, -400]; + + apply_peer_volume(&mut frame, peer, &volumes); + + assert_eq!(frame, vec![50, -100, 150, -200]); + } + + #[test] + fn peer_volume_map_defaults_to_unity_when_audio_peer_key_is_unmatched() { + let ui_peer = iroh::SecretKey::generate().public(); + let audio_peer = iroh::SecretKey::generate().public(); + let volumes = std::collections::HashMap::from([(ui_peer, 0.5)]); + let mut frame = vec![100, -200, 300, -400]; + + apply_peer_volume(&mut frame, audio_peer, &volumes); + + assert_eq!(frame, vec![100, -200, 300, -400]); + } + #[test] fn three_peers_sum_without_saturation() { let a = vec![10, 20];