feat: WarzonePhone lossy VoIP protocol — Phase 1 complete

Rust workspace with 7 crates implementing a custom VoIP protocol
designed for extremely lossy connections (5-70% loss, 100-500kbps,
300-800ms RTT). 89 tests passing across all crates.

Crates:
- wzp-proto: Wire format, traits, adaptive quality controller, jitter buffer, session FSM
- wzp-codec: Opus encoder/decoder (audiopus), Codec2 stubs, adaptive switching, resampling
- wzp-fec: RaptorQ fountain codes, interleaving, block management (proven 30-70% loss recovery)
- wzp-crypto: X25519+ChaCha20-Poly1305, Warzone identity compatible, anti-replay, rekeying
- wzp-transport: QUIC via quinn with DATAGRAM frames, path monitoring, signaling streams
- wzp-relay: Integration stub (Phase 2)
- wzp-client: Integration stub (Phase 2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-03-27 12:45:07 +04:00
commit 51e893590c
47 changed files with 7097 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
//! Simple linear resampler for 48 kHz <-> 8 kHz conversion.
//!
//! These are basic implementations suitable for voice. For higher quality,
//! replace with the `rubato` crate later.
/// Downsample from 48 kHz to 8 kHz (6:1 decimation with averaging).
///
/// Each output sample is the average of 6 consecutive input samples,
/// providing basic anti-aliasing via a box filter.
pub fn resample_48k_to_8k(input: &[i16]) -> Vec<i16> {
const RATIO: usize = 6;
let out_len = input.len() / RATIO;
let mut output = Vec::with_capacity(out_len);
for chunk in input.chunks_exact(RATIO) {
let sum: i32 = chunk.iter().map(|&s| s as i32).sum();
output.push((sum / RATIO as i32) as i16);
}
output
}
/// Upsample from 8 kHz to 48 kHz (1:6 interpolation with linear interp).
///
/// Linearly interpolates between each pair of input samples to produce
/// 6 output samples per input sample.
pub fn resample_8k_to_48k(input: &[i16]) -> Vec<i16> {
const RATIO: usize = 6;
if input.is_empty() {
return Vec::new();
}
let out_len = input.len() * RATIO;
let mut output = Vec::with_capacity(out_len);
for i in 0..input.len() {
let current = input[i] as i32;
let next = if i + 1 < input.len() {
input[i + 1] as i32
} else {
current // hold last sample
};
for j in 0..RATIO {
let interp = current + (next - current) * j as i32 / RATIO as i32;
output.push(interp as i16);
}
}
output
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_length() {
// 960 samples at 48kHz (20ms) -> 160 samples at 8kHz -> 960 samples at 48kHz
let input_48k = vec![0i16; 960];
let down = resample_48k_to_8k(&input_48k);
assert_eq!(down.len(), 160);
let up = resample_8k_to_48k(&down);
assert_eq!(up.len(), 960);
}
#[test]
fn dc_signal_preserved() {
// A constant signal should survive resampling
let input = vec![1000i16; 960];
let down = resample_48k_to_8k(&input);
assert!(down.iter().all(|&s| s == 1000));
let up = resample_8k_to_48k(&down);
assert!(up.iter().all(|&s| s == 1000));
}
#[test]
fn empty_input() {
assert!(resample_48k_to_8k(&[]).is_empty());
assert!(resample_8k_to_48k(&[]).is_empty());
}
}