#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(missing_docs)]
#![warn(clippy::all, clippy::pedantic)]
pub mod decoder;
pub mod encoder;
pub mod error;
pub mod layout;
pub mod worker;
pub use decoder::{AudioDecoder, OpusDecoder};
pub use encoder::{AudioEncoder, OpusEncoder};
pub use error::{OpusError, Result};
pub use worker::{OpusDecodeWorker, OpusEncodeWorker};
#[cfg(test)]
#[allow(clippy::cast_precision_loss)] mod tests {
use super::*;
use audio_core_bsd::AudioFrame;
const FRAME_SIZE: usize = 960;
const SAMPLE_RATE: u32 = 48_000;
fn sine_440() -> Vec<f32> {
let phase_step = 2.0 * std::f32::consts::PI * 440.0 / SAMPLE_RATE as f32;
(0..FRAME_SIZE)
.map(|i| 0.5 * (i as f32 * phase_step).sin())
.collect()
}
#[test]
fn encode_then_decode_round_trips_canonical_block() {
let pcm = sine_440();
let mut enc = OpusEncoder::new(SAMPLE_RATE, 1, opus::Application::Audio).unwrap();
let mut dec = OpusDecoder::new(SAMPLE_RATE, 1).unwrap();
let packet = enc.encode(&pcm).unwrap();
assert!(!packet.is_empty(), "Opus packet must not be empty");
let out = dec.decode(&packet).unwrap();
assert_eq!(
out.len(),
FRAME_SIZE,
"decoded mono frame count must equal the canonical 960-sample block",
);
}
#[test]
fn encode_frame_then_decode_frame_round_trips_shape() {
let mut enc = OpusEncoder::new(SAMPLE_RATE, 2, opus::Application::Audio).unwrap();
let mut dec = OpusDecoder::new(SAMPLE_RATE, 2).unwrap();
let frame = AudioFrame::from_planar(2, SAMPLE_RATE, sine_440_for_stereo());
let packet = enc.encode_frame(&frame).unwrap();
assert!(!packet.is_empty());
let decoded = dec.decode_frame(&packet).unwrap();
assert_eq!(decoded.channels, 2);
assert_eq!(decoded.sample_rate, SAMPLE_RATE);
assert_eq!(decoded.num_frames(), FRAME_SIZE);
}
fn sine_440_for_stereo() -> Vec<f32> {
let mono = sine_440();
let mut planar = Vec::with_capacity(FRAME_SIZE * 2);
planar.extend_from_slice(&mono);
planar.extend(std::iter::repeat_n(0.0f32, FRAME_SIZE));
planar
}
#[test]
fn planar_interleaved_round_trip_is_bit_exact_stereo() {
let original: Vec<f32> = (0..1920).map(|i| (i as f32) * 0.001).collect();
let inter = layout::planar_to_interleaved(2, &original).unwrap();
assert_eq!(inter.len(), original.len());
let back = layout::interleaved_to_planar(2, &inter).unwrap();
assert_eq!(back, original);
}
#[test]
fn layout_round_trip_preserves_audioframe_samples() {
use layout::{interleaved_to_planar as i2p, planar_to_interleaved as p2i};
let frame = AudioFrame::silence(2, 480, SAMPLE_RATE);
let inter = p2i(2, &frame.samples).unwrap();
let back = i2p(2, &inter).unwrap();
assert_eq!(back, frame.samples);
let mut src = AudioFrame::silence(2, 480, SAMPLE_RATE);
for (i, s) in src.channel_slice_mut(0).iter_mut().enumerate() {
*s = (i as f32) * 0.001;
}
let inter = p2i(2, &src.samples).unwrap();
assert_eq!(inter.len(), src.samples.len());
let back = i2p(2, &inter).unwrap();
assert_eq!(back, src.samples);
}
}