audio: extract apply_peer_volume seam + A24 regression tests
A24 (per-peer volume slider has no effect) does not reproduce on current main from a code trace: the UI slider's EndpointId is the same key the mixer uses for the incoming jitter frame, and the gain is applied before EQ/pan/output. Extract the inline per-peer lookup into a pure apply_peer_volume() seam and add two regression tests: - matching key scales the frame (0.5 halves it) - mismatched key defaults to unity (guards the key-identity failure mode) No wire/gossip/identity/PeerState change. The field-reported A24 was most likely a stale listener build (volume is listener-side); needs a 2-machine audible re-verify to close. Co-Authored-By: Codex (gpt-5.5) <noreply@openai.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+39
-5
@@ -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<EndpointId, f32>,
|
||||||
|
) {
|
||||||
|
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
|
/// 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.
|
/// UI level meter. An empty frame reads as 0.0.
|
||||||
fn frame_level(frame: &[i16]) -> f32 {
|
fn frame_level(frame: &[i16]) -> f32 {
|
||||||
@@ -1707,8 +1718,7 @@ async fn run_core_loop(
|
|||||||
peer_noise_gates.remove(&peer_id);
|
peer_noise_gates.remove(&peer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let vol = current_volumes.get(&peer_id).copied().unwrap_or(1.0);
|
apply_peer_volume(&mut frame, peer_id, ¤t_volumes);
|
||||||
apply_volume(&mut frame, vol);
|
|
||||||
|
|
||||||
let eq_settings = current_eq
|
let eq_settings = current_eq
|
||||||
.get(&peer_id)
|
.get(&peer_id)
|
||||||
@@ -2776,9 +2786,9 @@ async fn run_core_loop(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
admit_retained, apply_volume, audio_datagram_len_ok, frame_level, mix_frames,
|
admit_retained, apply_peer_volume, apply_volume, audio_datagram_len_ok, frame_level,
|
||||||
mix_stereo_frames, next_game_change, should_auto_fetch, stereo_to_mono, KnownPeers,
|
mix_frames, mix_stereo_frames, next_game_change, should_auto_fetch, stereo_to_mono,
|
||||||
MicLevelMeter, PeerSpeakTicket, MAX_OPUS_PAYLOAD, MAX_RETAINED_PEERS,
|
KnownPeers, MicLevelMeter, PeerSpeakTicket, MAX_OPUS_PAYLOAD, MAX_RETAINED_PEERS,
|
||||||
MIC_LEVEL_REPORT_SAMPLES,
|
MIC_LEVEL_REPORT_SAMPLES,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3038,6 +3048,30 @@ mod tests {
|
|||||||
assert_eq!(frame, vec![2000, -2000]);
|
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]
|
#[test]
|
||||||
fn three_peers_sum_without_saturation() {
|
fn three_peers_sum_without_saturation() {
|
||||||
let a = vec![10, 20];
|
let a = vec![10, 20];
|
||||||
|
|||||||
Reference in New Issue
Block a user