use crate::error;
use crate::layout;
use audio_core_bsd::AudioFrame;
const MAX_DECODE_FRAMES: usize = 2880;
pub trait AudioDecoder: Send {
fn decode(&mut self, packet: &[u8]) -> error::Result<Vec<f32>>;
}
#[derive(Debug)]
pub struct OpusDecoder {
decoder: opus::Decoder,
sample_rate: u32,
channels: u16,
out_buf: Vec<f32>,
}
impl OpusDecoder {
pub fn new(sample_rate: u32, channels: u16) -> error::Result<Self> {
if sample_rate == 0 {
return Err(error::OpusError::InvalidSampleRate(sample_rate));
}
let opus_channels = match channels {
1 => opus::Channels::Mono,
2 => opus::Channels::Stereo,
other => return Err(error::OpusError::UnsupportedChannels(other)),
};
let decoder = opus::Decoder::new(sample_rate, opus_channels)
.map_err(|e| error::OpusError::Decode(e.to_string()))?;
Ok(Self {
decoder,
sample_rate,
channels,
out_buf: vec![0.0f32; MAX_DECODE_FRAMES * usize::from(channels)],
})
}
#[must_use]
pub fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[must_use]
pub fn channels(&self) -> u16 {
self.channels
}
fn decode_interleaved(&mut self, packet: &[u8]) -> error::Result<usize> {
let decoded = self
.decoder
.decode_float(packet, &mut self.out_buf, false)
.map_err(|e| error::OpusError::Decode(e.to_string()))?;
Ok(decoded)
}
pub fn decode_interleaved_vec(&mut self, packet: &[u8]) -> error::Result<Vec<f32>> {
let frames = self.decode_interleaved(packet)?;
let total = frames * usize::from(self.channels);
Ok(self.out_buf[..total].to_vec())
}
pub fn decode_frame(&mut self, packet: &[u8]) -> error::Result<AudioFrame> {
let frames = self.decode_interleaved(packet)?;
let ch = self.channels;
let planar = layout::interleaved_to_planar(ch, &self.out_buf[..frames * usize::from(ch)])?;
Ok(AudioFrame::from_planar(ch, self.sample_rate, planar))
}
}
impl AudioDecoder for OpusDecoder {
fn decode(&mut self, packet: &[u8]) -> error::Result<Vec<f32>> {
self.decode_interleaved_vec(packet)
}
}
#[cfg(test)]
#[allow(clippy::cast_precision_loss)] mod tests {
use super::super::encoder::{AudioEncoder, OpusEncoder};
use super::*;
const FRAME_SIZE: usize = 960;
const SAMPLE_RATE: u32 = 48_000;
#[test]
fn mono_decoder_constructs() {
let dec = OpusDecoder::new(SAMPLE_RATE, 1);
assert!(dec.is_ok());
let dec = dec.unwrap();
assert_eq!(dec.sample_rate(), SAMPLE_RATE);
assert_eq!(dec.channels(), 1);
}
#[test]
fn stereo_decoder_constructs() {
let dec = OpusDecoder::new(SAMPLE_RATE, 2);
assert!(dec.is_ok());
}
#[test]
fn zero_sample_rate_is_rejected() {
let err = OpusDecoder::new(0, 1).unwrap_err();
assert_eq!(err, error::OpusError::InvalidSampleRate(0));
}
#[test]
fn unsupported_channels_are_rejected() {
for bad in [0u16, 3, 6] {
let err = OpusDecoder::new(SAMPLE_RATE, bad).unwrap_err();
assert_eq!(err, error::OpusError::UnsupportedChannels(bad));
}
}
#[test]
fn decode_a_known_good_mono_packet() {
let mut enc = OpusEncoder::new(SAMPLE_RATE, 1, opus::Application::Audio).unwrap();
let pcm: Vec<f32> = (0..FRAME_SIZE)
.map(|i| {
0.5 * (2.0 * std::f32::consts::PI * 440.0 * i as f32 / SAMPLE_RATE as f32).sin()
})
.collect();
let packet = enc.encode(&pcm).unwrap();
assert!(!packet.is_empty());
let mut dec = OpusDecoder::new(SAMPLE_RATE, 1).unwrap();
let out = dec.decode(&packet).unwrap();
assert_eq!(out.len(), FRAME_SIZE);
}
#[test]
fn decode_a_known_good_stereo_packet() {
let mut enc = OpusEncoder::new(SAMPLE_RATE, 2, opus::Application::Audio).unwrap();
let pcm: Vec<f32> = (0..FRAME_SIZE * 2)
.map(|i| 0.3 * (i as f32 * 0.01).sin())
.collect();
let packet = enc.encode(&pcm).unwrap();
assert!(!packet.is_empty());
let mut dec = OpusDecoder::new(SAMPLE_RATE, 2).unwrap();
let out = dec.decode(&packet).unwrap();
assert_eq!(out.len(), FRAME_SIZE * 2);
}
#[test]
fn decode_frame_produces_correct_channels_and_rate() {
let mut enc = OpusEncoder::new(SAMPLE_RATE, 2, opus::Application::Audio).unwrap();
let pcm = vec![0.0f32; FRAME_SIZE * 2];
let packet = enc.encode(&pcm).unwrap();
let mut dec = OpusDecoder::new(SAMPLE_RATE, 2).unwrap();
let frame = dec.decode_frame(&packet).unwrap();
assert_eq!(frame.channels, 2);
assert_eq!(frame.sample_rate, SAMPLE_RATE);
assert_eq!(frame.num_frames(), FRAME_SIZE);
assert_eq!(frame.samples.len(), FRAME_SIZE * 2);
}
#[test]
fn decode_frame_mono_matches_canonical_block_size() {
let mut enc = OpusEncoder::new(SAMPLE_RATE, 1, opus::Application::Audio).unwrap();
let pcm = vec![0.0f32; FRAME_SIZE];
let packet = enc.encode(&pcm).unwrap();
let mut dec = OpusDecoder::new(SAMPLE_RATE, 1).unwrap();
let frame = dec.decode_frame(&packet).unwrap();
assert_eq!(frame.num_frames(), FRAME_SIZE);
}
#[test]
fn decode_corrupt_packet_returns_decode_error() {
let mut dec = OpusDecoder::new(SAMPLE_RATE, 1).unwrap();
let res = dec.decode(&[0u8]);
if res.is_ok() {
let res2 = dec.decode(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
assert!(res2.is_err(), "a corrupt packet must yield a Decode error");
}
}
#[test]
fn out_buffer_is_reused_across_calls() {
let mut enc = OpusEncoder::new(SAMPLE_RATE, 1, opus::Application::Audio).unwrap();
let mut dec = OpusDecoder::new(SAMPLE_RATE, 1).unwrap();
let pcm = vec![0.0f32; FRAME_SIZE];
for _ in 0..3 {
let packet = enc.encode(&pcm).unwrap();
let out = dec.decode(&packet).unwrap();
assert_eq!(out.len(), FRAME_SIZE);
}
}
#[test]
fn interleaved_to_planar_mono_is_identity() {
let inter = vec![0.1, 0.2, 0.3, 0.4];
let out = layout::interleaved_to_planar(1, &inter).unwrap();
assert_eq!(out, inter);
}
#[test]
fn interleaved_to_planar_stereo_stride_is_correct() {
let inter = vec![10.0, 20.0, 11.0, 21.0, 12.0, 22.0];
let out = layout::interleaved_to_planar(2, &inter).unwrap();
assert_eq!(out, vec![10.0, 11.0, 12.0, 20.0, 21.0, 22.0]);
}
#[test]
fn interleaved_to_planar_preserves_all_samples() {
for channels in 1u16..=2 {
let nf = 960;
let inter: Vec<f32> = (0..nf * usize::from(channels)).map(|i| i as f32).collect();
let out = layout::interleaved_to_planar(channels, &inter).unwrap();
assert_eq!(out.len(), inter.len());
let mut a = out.clone();
a.sort_by(|x, y| x.partial_cmp(y).unwrap());
let mut b = inter.clone();
b.sort_by(|x, y| x.partial_cmp(y).unwrap());
assert_eq!(a, b);
}
}
}