use anyhow::{ensure, Result};
unsafe extern "C" {
fn aw_silk_encode(
pcm: *const i16,
samples: i32,
out: *mut u8,
capacity: i32,
length: *mut i32,
) -> i32;
fn aw_silk_decode(
input: *const u8,
length: i32,
pcm: *mut i16,
capacity: i32,
samples: *mut i32,
) -> i32;
}
pub const SAMPLE_RATE: u32 = 16000;
pub const BIT_RATE: u32 = 16000;
pub const FRAME_MS: u32 = 20;
pub const MAX_NOTE_MS: u32 = 60_000;
pub const MAX_SAMPLES: usize = (MAX_NOTE_MS / 1000 * SAMPLE_RATE) as usize;
pub const MAX_STREAM_BYTES: usize = 200 * 1024;
pub const MAX_NOTE_BYTES: usize = 10
+ (MAX_NOTE_MS / FRAME_MS) as usize * (2 + (BIT_RATE / 8 * FRAME_MS / 1000) as usize * 3 / 2);
const _: () = assert!(MAX_NOTE_BYTES <= MAX_STREAM_BYTES);
pub fn encode(pcm: &[i16]) -> Result<Vec<u8>> {
ensure!(
!pcm.is_empty() && pcm.len() <= MAX_SAMPLES,
"voice_duration_limit"
);
let mut out = vec![0u8; MAX_STREAM_BYTES];
let mut length = 0i32;
let status = unsafe {
aw_silk_encode(
pcm.as_ptr(),
pcm.len() as i32,
out.as_mut_ptr(),
out.len() as i32,
&mut length,
)
};
ensure!(
status == 0 && length >= 12 && length as usize <= out.len(),
"silk_encode_failed"
);
out.truncate(length as usize);
validate_framing(&out)?;
Ok(out)
}
pub fn validate_framing(input: &[u8]) -> Result<u32> {
ensure!(
input.len() >= 12
&& input.len() <= MAX_STREAM_BYTES
&& input[0] <= 3
&& input[1..10] == *b"#!SILK_V3",
"invalid_silk_stream"
);
let mut p = 10;
let mut packets = 0u32;
while p < input.len() {
ensure!(p + 2 <= input.len(), "invalid_silk_packet");
let n = i16::from_le_bytes(input[p..p + 2].try_into()?) as i32;
p += 2;
ensure!(
n > 0 && n <= 5120 && n as usize <= input.len() - p,
"invalid_silk_packet"
);
p += n as usize;
packets += 1;
ensure!(packets <= MAX_NOTE_MS / FRAME_MS, "voice_duration_limit");
}
ensure!(packets > 0, "empty_silk_stream");
Ok(packets * FRAME_MS)
}
pub fn decode(input: &[u8]) -> Result<Vec<i16>> {
validate_framing(input)?;
let mut pcm = vec![0i16; MAX_SAMPLES];
let mut samples = 0i32;
let status = unsafe {
aw_silk_decode(
input.as_ptr(),
input.len() as i32,
pcm.as_mut_ptr(),
pcm.len() as i32,
&mut samples,
)
};
ensure!(
status == 0 && samples > 0 && samples as usize <= pcm.len(),
"silk_decode_failed"
);
pcm.truncate(samples as usize);
Ok(pcm)
}
pub fn wav(pcm: &[i16]) -> Result<Vec<u8>> {
ensure!(pcm.len() <= MAX_SAMPLES, "voice_duration_limit");
let n = u32::try_from(pcm.len() * 2)?;
let mut out = Vec::with_capacity(44 + n as usize);
out.extend(b"RIFF");
out.extend((36 + n).to_le_bytes());
out.extend(b"WAVEfmt ");
out.extend(16u32.to_le_bytes());
out.extend(1u16.to_le_bytes());
out.extend(1u16.to_le_bytes());
out.extend(SAMPLE_RATE.to_le_bytes());
out.extend((SAMPLE_RATE * 2).to_le_bytes());
out.extend(2u16.to_le_bytes());
out.extend(16u16.to_le_bytes());
out.extend(b"data");
out.extend(n.to_le_bytes());
for s in pcm {
out.extend(s.to_le_bytes());
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn independent_fresh_tones_encode_decode() {
let pcm = (0..16000)
.map(|i| ((i as f64 * 440.0 * std::f64::consts::TAU / 16000.0).sin() * 10000.0) as i16)
.collect::<Vec<_>>();
let encoded = encode(&pcm).unwrap();
assert_eq!(validate_framing(&encoded).unwrap(), 1000);
let decoded = decode(&encoded).unwrap();
assert_eq!(decoded.len(), pcm.len());
assert!(decoded.iter().any(|s| s.abs() > 1000));
assert_eq!(&wav(&decoded).unwrap()[..4], b"RIFF");
}
#[test]
fn maximum_note_stays_within_derived_byte_bound() {
assert_eq!(MAX_SAMPLES, 960_000);
assert_eq!(MAX_NOTE_BYTES, 186_010);
let mut seed = 0x9e37_79b9_7f4a_7c15u64;
let noise = (0..MAX_SAMPLES)
.map(|_| {
seed = seed
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
(seed >> 48) as u16 as i16
})
.collect::<Vec<_>>();
let harmonic = (0..MAX_SAMPLES)
.map(|i| {
let t = i as f64 / SAMPLE_RATE as f64;
let f0 = 120.0 + 40.0 * (t * 3.0).sin();
let v = (1..30)
.map(|h| (t * f0 * h as f64 * std::f64::consts::TAU).sin() / h as f64)
.sum::<f64>();
(v * (t * 4.0).sin().abs() * 8000.0) as i16
})
.collect::<Vec<_>>();
for pcm in [noise, harmonic] {
let encoded = encode(&pcm).unwrap();
assert!(encoded.len() <= MAX_NOTE_BYTES, "{}", encoded.len());
assert_eq!(validate_framing(&encoded).unwrap(), MAX_NOTE_MS);
}
let too_long = vec![0i16; MAX_SAMPLES + 1];
assert!(encode(&too_long).is_err());
}
#[test]
fn malformed_packet_refused_before_ffi() {
for input in [
vec![],
[vec![2], b"#!SILK_V3".to_vec(), vec![0, 0]].concat(),
[vec![2], b"#!SILK_V3".to_vec(), vec![255, 255]].concat(),
] {
assert!(decode(&input).is_err());
}
}
}