#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![allow(unsafe_op_in_unsafe_fn)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::needless_range_loop)]
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
struct Readme;
mod config;
mod decoder;
mod encoder;
mod error;
pub mod multistream;
pub mod ogg;
pub mod packet;
pub mod parallel;
pub mod repacketizer;
mod soft_clip;
mod analysis;
mod analysis_data;
mod celt;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod cpu_features;
mod hp_cutoff;
mod range_coder;
mod silk;
mod toc;
#[cfg(feature = "probe")]
pub mod probe {
pub const CELT_BAND_EDGES_200HZ: [i16; 22] = crate::celt::modes::EBAND_5MS;
}
pub use config::{Application, Bandwidth, OpusMode, RateControl, Signal};
pub use decoder::OpusDecoder;
pub use encoder::{MAX_PACKET_BYTES, OpusEncoder};
pub use error::{Error, Result};
pub use multistream::{ChannelLayout, OpusMSDecoder, OpusMSEncoder};
pub use ogg::{OggOpusReader, OggOpusWriter, OggPacket, OpusHead, OpusTags, Trim};
pub use packet::MAX_PACKET_SAMPLES;
pub use parallel::{DEFAULT_WARMUP_MS, ParallelConfig, ParallelPlan, encode_parallel};
pub use repacketizer::Repacketizer;
pub use soft_clip::SoftClip;
#[cfg(test)]
mod integration_tests {
use crate::config::OpusMode;
use crate::toc::{
channels_from_toc, frame_duration_ms_from_toc, frame_rate_from_params, gen_toc,
mode_from_toc,
};
use crate::{Application, Bandwidth, OpusDecoder, OpusEncoder, RateControl};
fn frame_size_from_toc(toc: u8, sampling_rate: i32) -> Option<usize> {
let mode = mode_from_toc(toc);
match mode {
OpusMode::CeltOnly => {
let period = ((toc >> 3) & 0x03) as i32;
let frame_rate = 400 >> period;
if frame_rate == 0 || sampling_rate % frame_rate != 0 {
return None;
}
Some((sampling_rate / frame_rate) as usize)
}
OpusMode::SilkOnly => {
let duration_ms = frame_duration_ms_from_toc(toc);
Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
}
OpusMode::Hybrid => {
let duration_ms = frame_duration_ms_from_toc(toc);
Some((sampling_rate as i64 * duration_ms as i64 / 1000) as usize)
}
}
}
#[test]
fn gen_toc_matches_celt_reference_values() {
let sampling_rate = 48_000;
let cases = [
(120usize, 0xE0u8),
(240usize, 0xE8u8),
(480usize, 0xF0u8),
(960usize, 0xF8u8),
];
for (frame_size, expected_toc) in cases {
let frame_rate = frame_rate_from_params(sampling_rate, frame_size).unwrap();
let toc = gen_toc(OpusMode::CeltOnly, frame_rate, Bandwidth::Fullband, 1);
assert_eq!(
toc, expected_toc,
"frame_size {} expected TOC {:02X} got {:02X}",
frame_size, expected_toc, toc
);
let decoded_size = frame_size_from_toc(toc, sampling_rate).unwrap();
assert_eq!(decoded_size, frame_size);
}
let stereo_toc = gen_toc(
OpusMode::CeltOnly,
frame_rate_from_params(sampling_rate, 960).unwrap(),
Bandwidth::Fullband,
2,
);
assert_eq!(channels_from_toc(stereo_toc), 2);
}
#[test]
fn gen_toc_covers_every_silk_duration() {
for &(rate, ms, frame_size) in &[
(48_000i32, 10i32, 480usize),
(48_000, 20, 960),
(48_000, 40, 1920),
(48_000, 60, 2880),
(8_000, 10, 80),
(8_000, 20, 160),
(8_000, 40, 320),
(8_000, 60, 480),
] {
let frame_rate = frame_rate_from_params(rate, frame_size)
.unwrap_or_else(|| panic!("{rate} Hz / {ms} ms rejected"));
let toc = gen_toc(OpusMode::SilkOnly, frame_rate, Bandwidth::Wideband, 1);
assert_eq!(
frame_duration_ms_from_toc(toc),
ms,
"{rate} Hz / {ms} ms produced TOC {toc:02X}"
);
assert_eq!(mode_from_toc(toc), OpusMode::SilkOnly);
assert_eq!(frame_size_from_toc(toc, rate).unwrap(), frame_size);
}
}
#[test]
fn frame_rate_rejects_durations_that_need_multi_frame_packets() {
for &(rate, frame_size) in &[
(48_000i32, 3840usize), (48_000, 4800), (48_000, 5760), (16_000, 1280), (8_000, 640), ] {
assert!(
frame_rate_from_params(rate, frame_size).is_none(),
"{rate} Hz / {frame_size} samples was accepted"
);
}
assert!(frame_rate_from_params(48_000, 0).is_none());
assert!(frame_rate_from_params(48_000, 333).is_none());
assert!(frame_rate_from_params(48_000, usize::MAX).is_none());
}
#[test]
fn test_celt_decoder_large_frame_sizes() {
let sampling_rate = 48000;
let channels = 1;
let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
let frame_sizes = [120, 240, 480, 960];
for frame_size in frame_sizes {
let toc = gen_toc(
OpusMode::CeltOnly,
frame_rate_from_params(sampling_rate, frame_size).unwrap(),
Bandwidth::Fullband,
channels,
);
let packet = [toc, 0, 0, 0, 0];
let mut output = vec![0.0f32; frame_size * channels];
let _ = decoder.decode(&packet, frame_size, &mut output);
}
let channels = 2;
let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
for frame_size in frame_sizes {
let toc = gen_toc(
OpusMode::CeltOnly,
frame_rate_from_params(sampling_rate, frame_size).unwrap(),
Bandwidth::Fullband,
channels,
);
let packet = [toc, 0, 0, 0, 0];
let mut output = vec![0.0f32; frame_size * channels];
let _ = decoder.decode(&packet, frame_size, &mut output);
}
}
#[test]
fn test_celt_decoder_edge_case_frame_sizes() {
let sampling_rate = 48000;
let channels = 1;
let mut decoder = OpusDecoder::new(sampling_rate, channels).unwrap();
let edge_sizes = [2048, 2167, 2168, 2169, 2880, 3072];
for frame_size in edge_sizes {
let mut output = vec![0.0f32; frame_size * channels];
let _ = decoder.decode(&[0x80, 0, 0, 0], frame_size, &mut output);
}
}
#[test]
fn test_invalid_small_frame_size_returns_error_not_panic() {
let mut enc = OpusEncoder::new(48000, 2, Application::Voip).unwrap();
enc.bitrate_bps = 64000;
enc.complexity = 5;
enc.rate_control = RateControl::Cbr;
let input = vec![0.0f32; 48 * 2]; let mut output = vec![0u8; 256];
let result = enc.encode(&input, 48, &mut output);
assert!(
result.is_err(),
"encode with invalid frame_size=48 should return Err, not panic"
);
}
#[test]
fn test_invalid_small_frame_size_audio_application_returns_error() {
let mut enc = OpusEncoder::new(48000, 1, Application::Audio).unwrap();
let input = vec![0.0f32; 48];
let mut output = vec![0u8; 256];
let result = enc.encode(&input, 48, &mut output);
assert!(
result.is_err(),
"Audio/48kHz encoder with frame_size=48 should return Err"
);
}
}