#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
use crate::Error;
use super::{ChannelMappingTable, ChannelSource, Interleaved, Sample};
use super::layout::{ChannelLayout, MappingType};
use super::rate_alloc::{
MAX_BITRATE_PER_CHANNEL, MS_FRAME_TMP, OPUS_AUTO, OPUS_BITRATE_MAX, cbr_last_stream_bitrate,
cbr_max_data_bytes, clamp_user_bitrate, rate_allocation, smallest_packet, stream_max_bytes,
};
use super::surround::{MASK_BANDS, SurroundAnalysis};
use crate::analysis::{AnalysisPcm, SecondChannel};
use crate::packet;
use crate::{
Application, Bandwidth, Bitrate, Channels, ForcedMode, FrameDuration, InbandFec, OpusEncoder,
SampleRate, Signal,
};
#[inline]
fn stream_bitrate(value: i32, stream_channels: usize) -> Bitrate {
Bitrate::within_limits(value.clamp(500, MAX_BITRATE_PER_CHANNEL * stream_channels as i32))
}
fn stream_input_channels(
layout: &ChannelLayout,
s: usize,
) -> Result<(usize, Option<usize>), Error> {
if s < layout.coupled_streams() {
let missing = Error::bad_arg("multistream encoder: a coupled stream has no input channel");
let left = layout.left_channel(s, None).ok_or(missing)?;
let right = layout.right_channel(s, None).ok_or(missing)?;
Ok((left, Some(right)))
} else {
let chan = layout
.mono_channel(s, None)
.ok_or(Error::bad_arg("multistream encoder: a mono stream has no input channel"))?;
Ok((chan, None))
}
}
#[inline]
fn stream_mask<'a>(
dst: &'a mut [f32; 2 * MASK_BANDS],
band_smr: &[f32],
c1: usize,
c2: Option<usize>,
) -> &'a [f32] {
dst[..MASK_BANDS].copy_from_slice(&band_smr[MASK_BANDS * c1..][..MASK_BANDS]);
match c2 {
Some(c2) => {
dst[MASK_BANDS..].copy_from_slice(&band_smr[MASK_BANDS * c2..][..MASK_BANDS]);
&dst[..]
}
None => &dst[..MASK_BANDS],
}
}
pub struct MultistreamEncoder {
layout: ChannelLayout,
mapping_type: MappingType,
lfe_stream: Option<usize>,
application: Application,
sampling_rate: i32,
bitrate_bps: i32,
user_bandwidth: Option<Bandwidth>,
max_bandwidth: Bandwidth,
user_force_mode: Option<ForcedMode>,
encoders: Vec<OpusEncoder>,
stream_inputs: Vec<(usize, Option<usize>)>,
rates: Vec<i32>,
variable_duration: FrameDuration,
buf: Vec<f32>,
tmp: Vec<u8>,
surround: Option<SurroundAnalysis>,
band_smr: Vec<f32>,
}
impl core::fmt::Debug for MultistreamEncoder {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("MultistreamEncoder")
.field("sampling_rate", &self.sampling_rate())
.field("layout", &self.layout)
.field("mapping_type", &self.mapping_type)
.field("application", &self.application)
.field("bitrate_setting", &self.bitrate_setting())
.field("complexity", &self.complexity())
.field("vbr", &self.vbr())
.field("bandwidth_setting", &self.user_bandwidth)
.field("max_bandwidth", &self.max_bandwidth)
.field("force_mode", &self.user_force_mode)
.field("expert_frame_duration", &self.variable_duration)
.finish_non_exhaustive()
}
}
impl MultistreamEncoder {
pub fn new(
sampling_rate: SampleRate,
channels: usize,
streams: usize,
coupled_streams: usize,
mapping: &[u8],
application: Application,
) -> Result<Self, Error> {
let layout = ChannelLayout::new(channels, streams, coupled_streams, mapping)?;
Self::init(sampling_rate, layout, MappingType::None, None, application)
}
pub fn from_mapping_table(
sampling_rate: SampleRate,
table: &ChannelMappingTable,
application: Application,
) -> Result<Self, Error> {
if table.mapping.len() != table.channels as usize {
return Err(Error::bad_arg(
"multistream layout: the mapping table length does not match the channel count",
));
}
Self::new(
sampling_rate,
table.channels as usize,
table.stream_count as usize,
table.coupled_count as usize,
&table.mapping,
application,
)
}
pub fn from_layout(
sampling_rate: SampleRate,
layout: &ChannelLayout,
application: Application,
) -> Result<Self, Error> {
Self::init(sampling_rate, layout.clone(), MappingType::None, None, application)
}
pub fn new_surround(
sampling_rate: SampleRate,
channels: usize,
mapping_family: u8,
application: Application,
) -> Result<Self, Error> {
let (layout, mapping_type, lfe_stream) = ChannelLayout::surround(channels, mapping_family)?;
Self::init(sampling_rate, layout, mapping_type, lfe_stream, application)
}
fn init(
sampling_rate: SampleRate,
layout: ChannelLayout,
mapping_type: MappingType,
lfe_stream: Option<usize>,
application: Application,
) -> Result<Self, Error> {
layout.validate_for_encoder()?;
let lfe_stream = if mapping_type == MappingType::Surround {
lfe_stream
} else {
None
};
let streams = layout.streams();
let coupled = layout.coupled_streams();
let channels = layout.channels();
let stream_inputs = (0..streams)
.map(|s| stream_input_channels(&layout, s))
.collect::<Result<Vec<_>, _>>()?;
let mut encoders: Vec<OpusEncoder> = Vec::with_capacity(streams);
for s in 0..streams {
let stream_channels = if s < coupled { Channels::Stereo } else { Channels::Mono };
let slot = &mut encoders.spare_capacity_mut()[0];
let enc = OpusEncoder::init(slot, sampling_rate, stream_channels, application)?;
if Some(s) == lfe_stream {
enc.set_lfe(true);
}
unsafe { encoders.set_len(s + 1) };
}
let sampling_rate = sampling_rate.hz() as i32;
let max_frame = 6 * sampling_rate as usize / 50;
let (surround, band_smr) = if mapping_type == MappingType::Surround {
(
Some(SurroundAnalysis::new(channels, sampling_rate)),
vec![0.0; MASK_BANDS * channels],
)
} else {
(None, Vec::new())
};
Ok(Self {
layout,
mapping_type,
lfe_stream,
application,
sampling_rate,
bitrate_bps: OPUS_AUTO,
user_bandwidth: None,
max_bandwidth: Bandwidth::Fullband,
user_force_mode: None,
encoders,
stream_inputs,
rates: vec![0; streams],
variable_duration: FrameDuration::Argument,
buf: vec![0.0; 2 * max_frame],
tmp: vec![0; MS_FRAME_TMP],
surround,
band_smr,
})
}
pub fn layout(&self) -> &ChannelLayout {
&self.layout
}
pub fn channels(&self) -> usize {
self.layout.channels()
}
pub fn mapping_type(&self) -> MappingType {
self.mapping_type
}
pub fn lfe_stream(&self) -> Option<usize> {
self.lfe_stream
}
pub fn application(&self) -> Application {
self.encoders.first().map_or(self.application, OpusEncoder::application)
}
pub fn set_application(&mut self, application: Application) -> Result<(), Error> {
for enc in &mut self.encoders {
enc.set_application(application)?;
}
self.application = application;
Ok(())
}
pub fn sampling_rate(&self) -> SampleRate {
SampleRate::from_valid_hz(self.sampling_rate)
}
pub fn lookahead(&self) -> usize {
self.encoders.first().map_or(0, |enc| enc.lookahead())
}
pub fn encode(
&mut self,
pcm: &[f32],
frame_size: usize,
output: &mut [u8],
) -> Result<usize, Error> {
let selected = self.check_input(pcm.len(), frame_size)?;
let src = Interleaved {
pcm,
channels: self.layout.channels(),
};
self.encode_native(&src, selected, frame_size, output)
}
pub fn encode_i16(
&mut self,
pcm: &[i16],
frame_size: usize,
output: &mut [u8],
) -> Result<usize, Error> {
let selected = self.check_input(pcm.len(), frame_size)?;
let src = Interleaved {
pcm,
channels: self.layout.channels(),
};
self.encode_native(&src, selected, frame_size, output)
}
pub fn encode24(
&mut self,
pcm: &[i32],
frame_size: usize,
output: &mut [u8],
) -> Result<usize, Error> {
let selected = self.check_input(pcm.len(), frame_size)?;
let src = Interleaved {
pcm,
channels: self.layout.channels(),
};
self.encode_native(&src, selected, frame_size, output)
}
fn check_input(&self, pcm_len: usize, frame_size: usize) -> Result<usize, Error> {
let selected = crate::frame_size_select(
self.application,
frame_size,
self.variable_duration,
self.sampling_rate,
)
.ok_or(Error::INVALID_FRAME_SIZE)?;
if pcm_len < frame_size.saturating_mul(self.layout.channels()) {
return Err(Error::INPUT_TOO_SHORT);
}
Ok(selected)
}
fn encode_native<T: Sample>(
&mut self,
src: &Interleaved<'_, T>,
frame_size: usize,
analysis_frame_size: usize,
output: &mut [u8],
) -> Result<usize, Error> {
let fs = self.sampling_rate;
let streams = self.layout.streams();
let coupled = self.layout.coupled_streams();
let surround_mode = self.mapping_type == MappingType::Surround;
let masking = surround_mode && self.application != Application::RestrictedSilk;
let vbr = self.vbr();
let smallest = smallest_packet(streams, frame_size, fs)?;
if output.len() < smallest {
return Err(Error::BUFFER_TOO_SMALL);
}
let have_mask = match self.surround.as_mut() {
Some(analysis) if masking => analysis.run(src, frame_size, &mut self.band_smr),
_ => false,
};
let rate_sum = rate_allocation(
&self.layout,
self.mapping_type,
self.lfe_stream,
self.bitrate_bps,
frame_size,
fs,
&mut self.rates,
)?;
let mut max_data_bytes = output.len();
if !vbr {
max_data_bytes = cbr_max_data_bytes(
self.bitrate_bps,
rate_sum,
streams,
frame_size,
fs,
max_data_bytes,
)?;
}
let surround_bandwidth = if surround_mode {
let total_bitrate =
if self.bitrate_bps == OPUS_AUTO || self.bitrate_bps == OPUS_BITRATE_MAX {
rate_sum
} else {
self.bitrate_bps
};
let nb_channels = self.layout.channels() as i64;
let (frame_size, fs) = (frame_size as i64, fs as i64);
let mut equiv_rate = total_bitrate as i64;
if frame_size * 50 < fs {
equiv_rate -= 60 * (fs / frame_size - 50) * nb_channels;
}
Some(if equiv_rate > 10000 * nb_channels {
Bandwidth::Fullband
} else if equiv_rate > 7000 * nb_channels {
Bandwidth::Superwideband
} else if equiv_rate > 5000 * nb_channels {
Bandwidth::Wideband
} else {
Bandwidth::Narrowband
})
} else {
None
};
for (s, enc) in self.encoders.iter_mut().enumerate() {
enc.set_bitrate(stream_bitrate(self.rates[s], enc.channels().count()));
if let Some(bandwidth) = surround_bandwidth {
enc.set_bandwidth(Some(bandwidth));
if s < coupled {
enc.set_force_mode(Some(ForcedMode::CeltOnly));
enc.set_force_channels(Some(Channels::Stereo));
}
} else if self.mapping_type == MappingType::Ambisonics {
enc.set_force_mode(Some(ForcedMode::CeltOnly));
}
}
let mut band_log_e = [0.0f32; 2 * MASK_BANDS];
let mut tot_size = 0usize;
let streams_and_inputs = self.encoders.iter_mut().zip(&self.stream_inputs);
for (s, (enc, &(c1, c2))) in streams_and_inputs.enumerate() {
let last = s + 1 == streams;
let stream_channels = enc.channels().count();
match c2 {
Some(c2) => src.copy_pair(&mut self.buf, c1, c2, frame_size),
None => src.copy(&mut self.buf, c1, frame_size),
}
if masking {
let _ = enc.set_energy_mask(if have_mask {
Some(stream_mask(&mut band_log_e, &self.band_smr, c1, c2))
} else {
None
});
}
let curr_max = stream_max_bytes(max_data_bytes, tot_size, streams, s, frame_size, fs)?;
if !vbr && last {
enc.set_bitrate(stream_bitrate(
cbr_last_stream_bitrate(curr_max, frame_size, fs)?,
stream_channels,
));
}
let analysis_pcm = AnalysisPcm {
pcm: T::analysis_pcm(src.pcm),
channels: src.channels,
c1,
c2: match c2 {
Some(c2) => SecondChannel::Channel(c2),
None => SecondChannel::None,
},
frame_size: analysis_frame_size,
};
let len = enc.encode_impl(
&self.buf[..frame_size * stream_channels],
frame_size,
&mut self.tmp[..curr_max],
analysis_pcm,
T::LSB_DEPTH,
T::FLOAT_API,
)?;
let mut rp = packet::Repacketizer::new();
rp.cat(&self.tmp[..len]).map_err(|_| Error::INTERNAL_ERROR)?;
let room = output
.get_mut(tot_size..max_data_bytes)
.ok_or(Error::BUFFER_TOO_SMALL)?;
let framing = packet::Framing::from_flag(!last);
let padding = packet::Padding::from_flag(!vbr && last);
let written = rp.out_range_ext(0, rp.nb_frames(), room, framing, padding, &[])?;
tot_size += written;
}
Ok(tot_size)
}
pub fn set_bitrate(&mut self, bitrate: Bitrate) {
self.bitrate_bps = match bitrate {
Bitrate::Auto => OPUS_AUTO,
Bitrate::Max => OPUS_BITRATE_MAX,
Bitrate::BitsPerSecond(bps) => {
clamp_user_bitrate(Bitrate::bps_to_i32(bps), self.layout.channels())
.unwrap_or(self.bitrate_bps)
}
};
}
pub fn bitrate_setting(&self) -> Bitrate {
match self.bitrate_bps {
OPUS_AUTO => Bitrate::Auto,
OPUS_BITRATE_MAX => Bitrate::Max,
bps => Bitrate::within_limits(bps),
}
}
pub fn bitrate(&self) -> i32 {
let sum: i64 = self.encoders.iter().map(|enc| enc.bitrate() as i64).sum();
sum.clamp(0, i32::MAX as i64) as i32
}
pub fn set_complexity(&mut self, complexity: u8) -> Result<(), Error> {
if complexity > 10 {
return Err(Error::INVALID_COMPLEXITY);
}
for enc in &mut self.encoders {
enc.set_complexity(complexity)?;
}
Ok(())
}
pub fn complexity(&self) -> u8 {
self.encoders.first().map_or(0, |enc| enc.complexity())
}
pub fn set_vbr(&mut self, vbr: bool) {
for enc in &mut self.encoders {
enc.set_vbr(vbr);
}
}
pub fn vbr(&self) -> bool {
self.encoders.first().is_none_or(|enc| enc.vbr())
}
pub fn set_vbr_constraint(&mut self, constrained: bool) {
for enc in &mut self.encoders {
enc.set_vbr_constraint(constrained);
}
}
pub fn vbr_constraint(&self) -> bool {
self.encoders.first().is_none_or(|enc| enc.vbr_constraint())
}
pub fn set_inband_fec(&mut self, fec: InbandFec) {
for enc in &mut self.encoders {
enc.set_inband_fec(fec);
}
}
pub fn inband_fec(&self) -> InbandFec {
self.encoders.first().map_or(InbandFec::Off, |enc| enc.inband_fec())
}
pub fn set_signal(&mut self, signal: Signal) {
for enc in &mut self.encoders {
enc.set_signal(signal);
}
}
pub fn signal(&self) -> Signal {
self.encoders.first().map_or(Signal::Auto, |enc| enc.signal())
}
pub fn set_lsb_depth(&mut self, depth: u8) -> Result<(), Error> {
if !(8..=24).contains(&depth) {
return Err(Error::INVALID_LSB_DEPTH);
}
for enc in &mut self.encoders {
enc.set_lsb_depth(depth)?;
}
Ok(())
}
pub fn lsb_depth(&self) -> u8 {
self.encoders.first().map_or(24, |enc| enc.lsb_depth())
}
pub fn set_dtx(&mut self, enabled: bool) {
for enc in &mut self.encoders {
enc.set_dtx(enabled);
}
}
pub fn dtx(&self) -> bool {
self.encoders.first().is_some_and(|enc| enc.dtx())
}
pub fn set_packet_loss_perc(&mut self, percent: u8) -> Result<(), Error> {
if percent > 100 {
return Err(Error::INVALID_PACKET_LOSS);
}
for enc in &mut self.encoders {
enc.set_packet_loss_perc(percent)?;
}
Ok(())
}
pub fn packet_loss_perc(&self) -> u8 {
self.encoders.first().map_or(0, |enc| enc.packet_loss_perc())
}
pub fn set_bandwidth(&mut self, bandwidth: Option<Bandwidth>) {
self.user_bandwidth = bandwidth;
for enc in &mut self.encoders {
enc.set_bandwidth(bandwidth);
}
}
pub fn bandwidth_setting(&self) -> Option<Bandwidth> {
self.user_bandwidth
}
pub fn bandwidth(&self) -> Bandwidth {
self.encoders.first().map_or(Bandwidth::Fullband, OpusEncoder::bandwidth)
}
pub fn set_max_bandwidth(&mut self, bandwidth: Bandwidth) {
for enc in &mut self.encoders {
enc.set_max_bandwidth(bandwidth);
}
self.max_bandwidth = bandwidth;
}
pub fn max_bandwidth(&self) -> Bandwidth {
self.max_bandwidth
}
pub fn set_expert_frame_duration(&mut self, duration: FrameDuration) {
self.variable_duration = duration;
}
pub fn expert_frame_duration(&self) -> FrameDuration {
self.variable_duration
}
pub fn set_force_mode(&mut self, mode: Option<ForcedMode>) {
self.user_force_mode = mode;
for enc in &mut self.encoders {
enc.set_force_mode(mode);
}
}
pub fn force_mode(&self) -> Option<ForcedMode> {
self.user_force_mode
}
pub fn set_force_channels(&mut self, channels: Option<Channels>) {
for enc in &mut self.encoders {
enc.set_force_channels(channels);
}
}
pub fn force_channels(&self) -> Option<Channels> {
self.encoders.first().and_then(|enc| enc.force_channels())
}
pub fn set_prediction_disabled(&mut self, disabled: bool) {
for enc in &mut self.encoders {
enc.set_prediction_disabled(disabled);
}
}
pub fn prediction_disabled(&self) -> bool {
self.encoders.first().is_some_and(|enc| enc.prediction_disabled())
}
pub fn set_phase_inversion_disabled(&mut self, disabled: bool) {
for enc in &mut self.encoders {
enc.set_phase_inversion_disabled(disabled);
}
}
pub fn phase_inversion_disabled(&self) -> bool {
self.encoders.first().is_some_and(|enc| enc.phase_inversion_disabled())
}
pub fn voice_ratio(&self) -> Option<u8> {
self.encoders.first().and_then(|enc| enc.voice_ratio())
}
pub fn final_range(&self) -> u32 {
self.encoders
.iter()
.fold(0, |acc, enc| acc ^ enc.final_range())
}
pub fn reset_state(&mut self) {
if let Some(analysis) = self.surround.as_mut() {
analysis.reset();
}
for enc in &mut self.encoders {
enc.reset_state();
}
}
pub fn stream_encoder(&self, stream: usize) -> Option<&OpusEncoder> {
self.encoders.get(stream)
}
pub fn stream_encoder_mut(&mut self, stream: usize) -> Option<&mut OpusEncoder> {
self.encoders.get_mut(stream)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_three_constructors_build_the_same_encoder() {
let fs = SampleRate::Hz48000;
let app = Application::Audio;
let mapping = [0u8, 4, 1, 2, 3, 5];
let table = ChannelMappingTable::new(6, 4, 2, &mapping);
let layout = ChannelLayout::new(6, 4, 2, &mapping).unwrap();
let mut encoders = [
MultistreamEncoder::new(fs, 6, 4, 2, &mapping, app).unwrap(),
MultistreamEncoder::from_mapping_table(fs, &table, app).unwrap(),
MultistreamEncoder::from_layout(fs, &layout, app).unwrap(),
];
let pcm: Vec<f32> = (0..960 * 6)
.map(|k| 0.2 * ((k / 6) as f32 * 0.013 * (1 + k % 6) as f32).sin())
.collect();
let mut packets = Vec::new();
for enc in &mut encoders {
assert_eq!(enc.layout(), &layout);
assert_eq!(enc.channels(), 6);
assert_eq!(enc.mapping_type(), MappingType::None);
assert_eq!(enc.lfe_stream(), None);
let mut out = vec![0u8; 4000];
let mut frames = Vec::new();
for _ in 0..3 {
let n = enc.encode(&pcm, 960, &mut out).unwrap();
frames.push(out[..n].to_vec());
}
packets.push(frames);
}
assert_eq!(packets[0], packets[1]);
assert_eq!(packets[0], packets[2]);
for len in [5, 7] {
let bad = ChannelMappingTable::new(6, 4, 2, &[0, 4, 1, 2, 3, 5, 5][..len]);
let err = MultistreamEncoder::from_mapping_table(fs, &bad, app).unwrap_err();
assert_eq!(err.kind(), crate::ErrorKind::BadArg);
}
let layout = ChannelLayout::new(3, 2, 1, &[0, 1, 0]).unwrap();
let err = MultistreamEncoder::from_layout(fs, &layout, app).unwrap_err();
assert_eq!(err.kind(), crate::ErrorKind::BadArg);
}
#[test]
fn bandwidth_and_force_mode_getters() {
let mut enc =
MultistreamEncoder::new_surround(SampleRate::Hz48000, 6, 1, Application::Audio).unwrap();
assert_eq!(enc.bandwidth(), Bandwidth::Fullband);
assert_eq!(enc.force_mode(), None);
enc.set_force_mode(Some(ForcedMode::SilkOnly));
assert_eq!(enc.force_mode(), Some(ForcedMode::SilkOnly));
enc.set_bitrate(Bitrate::BitsPerSecond(core::num::NonZeroU32::new(24_000).unwrap()));
let pcm = vec![0.1f32; 960 * 6];
let mut out = vec![0u8; 4000];
enc.encode(&pcm, 960, &mut out).unwrap();
assert_eq!(enc.bandwidth(), Bandwidth::Narrowband);
assert_eq!(enc.bandwidth_setting(), None);
assert_eq!(enc.force_mode(), Some(ForcedMode::SilkOnly));
assert_eq!(enc.stream_encoder(0).unwrap().force_mode(), Some(ForcedMode::CeltOnly));
enc.set_force_mode(None);
assert_eq!(enc.force_mode(), None);
}
#[test]
fn stream_mask_holds_the_left_channel_then_the_right() {
let band_smr: Vec<f32> = (0..4 * MASK_BANDS)
.map(|k| (100 * (k / MASK_BANDS) + k % MASK_BANDS) as f32)
.collect();
let mut dst = [-1.0f32; 2 * MASK_BANDS];
let mask = stream_mask(&mut dst, &band_smr, 3, Some(1));
assert_eq!(mask.len(), 2 * MASK_BANDS);
for band in 0..MASK_BANDS {
assert_eq!(mask[band], (300 + band) as f32);
assert_eq!(mask[MASK_BANDS + band], (100 + band) as f32);
}
let mut dst = [-1.0f32; 2 * MASK_BANDS];
let mask = stream_mask(&mut dst, &band_smr, 2, None);
assert_eq!(mask.len(), MASK_BANDS);
assert_eq!(mask[0], 200.0);
assert_eq!(mask[MASK_BANDS - 1], 220.0);
assert!(dst[MASK_BANDS..].iter().all(|&v| v == -1.0));
}
#[test]
fn no_mask_is_set_when_the_analysis_gives_none() {
let mut enc = MultistreamEncoder::new_surround(
SampleRate::Hz48000,
6,
1,
Application::Audio,
)
.unwrap();
let pcm: Vec<f32> = (0..960 * 6)
.map(|k| ((k * 7 % 97) as f32 - 48.0) / 200.0)
.collect();
let mut out = vec![0u8; 6000];
enc.encode(&pcm, 960, &mut out).unwrap();
for stream in &enc.encoders {
let mask = stream.energy_mask().expect("a surround stream has a mask");
assert_eq!(mask.len(), MASK_BANDS * stream.channels().count());
}
enc.surround = Some(SurroundAnalysis::new(2, 48000));
enc.encode(&pcm, 960, &mut out).unwrap();
for stream in &enc.encoders {
assert_eq!(stream.energy_mask(), None);
}
}
#[test]
fn only_a_surround_layout_has_an_analysis() {
for channels in 1..=8 {
let enc =
MultistreamEncoder::new_surround(SampleRate::Hz48000, channels, 1, Application::Audio).unwrap();
assert_eq!(enc.surround.is_some(), channels > 2, "{channels} channels");
assert_eq!(enc.band_smr.len(), if channels > 2 { MASK_BANDS * channels } else { 0 });
}
for (channels, family) in [(3, 255), (6, 255), (4, 2), (6, 2), (9, 2)] {
let enc = MultistreamEncoder::new_surround(
SampleRate::Hz48000,
channels,
family,
Application::Audio,
)
.unwrap();
assert!(enc.surround.is_none(), "family {family}, {channels} channels");
}
let enc =
MultistreamEncoder::new(SampleRate::Hz48000, 6, 4, 2, &[0, 4, 1, 2, 3, 5], Application::Audio)
.unwrap();
assert!(enc.surround.is_none(), "the generic constructor");
assert_eq!(enc.lfe_stream(), None);
}
#[test]
fn stream_input_channels_follow_the_layout() {
let enc = MultistreamEncoder::new_surround(
SampleRate::Hz48000,
6,
1,
Application::Audio,
)
.unwrap();
assert_eq!(enc.layout().mapping(), &[0, 4, 1, 2, 3, 5]);
let channels = |s| stream_input_channels(enc.layout(), s).unwrap();
assert_eq!(channels(0), (0, Some(2)));
assert_eq!(channels(1), (3, Some(4)));
assert_eq!(channels(2), (1, None));
assert_eq!(channels(3), (5, None));
let enc = MultistreamEncoder::new_surround(
SampleRate::Hz48000,
8,
1,
Application::Audio,
)
.unwrap();
let channels = |s| stream_input_channels(enc.layout(), s).unwrap();
assert_eq!(channels(0), (0, Some(2)));
assert_eq!(channels(1), (3, Some(4)));
assert_eq!(channels(2), (5, Some(6)));
assert_eq!(channels(3), (1, None));
assert_eq!(channels(4), (7, None));
let enc = MultistreamEncoder::new(
SampleRate::Hz48000,
5,
2,
1,
&[1, 0, 255, 2, 2],
Application::Audio,
)
.unwrap();
let channels = |s| stream_input_channels(enc.layout(), s).unwrap();
assert_eq!(channels(0), (1, Some(0)));
assert_eq!(channels(1), (3, None));
}
#[test]
fn stream_bitrate_follows_the_stream_encoder_control() {
let bps = |value| Bitrate::BitsPerSecond(core::num::NonZeroU32::new(value).unwrap());
assert_eq!(stream_bitrate(1, 1), bps(500));
assert_eq!(stream_bitrate(500, 2), bps(500));
assert_eq!(stream_bitrate(64_000, 1), bps(64_000));
assert_eq!(stream_bitrate(750_001, 1), bps(750_000));
assert_eq!(stream_bitrate(2_000_000, 2), bps(1_500_000));
assert_eq!(stream_bitrate(i32::MAX, 2), bps(1_500_000));
let mut enc =
OpusEncoder::new(SampleRate::Hz48000, Channels::Stereo, Application::Audio).unwrap();
for value in [i32::MIN, 0, 1, 500, 64_000, 2_000_000, i32::MAX] {
enc.set_bitrate(stream_bitrate(value, 2));
assert_eq!(enc.bitrate_setting(), stream_bitrate(value, 2), "{value}");
}
}
#[test]
fn bitrate_setting_round_trips() {
let mut enc = MultistreamEncoder::new_surround(
SampleRate::Hz48000,
6,
1,
Application::Audio,
)
.unwrap();
let bps = |value| Bitrate::BitsPerSecond(core::num::NonZeroU32::new(value).unwrap());
assert_eq!(enc.bitrate_setting(), Bitrate::Auto);
enc.set_bitrate(Bitrate::Max);
assert_eq!(enc.bitrate_setting(), Bitrate::Max);
enc.set_bitrate(bps(256_000));
assert_eq!(enc.bitrate_setting(), bps(256_000));
enc.set_bitrate(bps(1));
assert_eq!(enc.bitrate_setting(), bps(3000));
enc.set_bitrate(bps(i32::MAX as u32));
assert_eq!(enc.bitrate_setting(), bps(4_500_000));
enc.set_bitrate(bps(u32::MAX));
assert_eq!(enc.bitrate_setting(), bps(4_500_000));
}
#[test]
fn frame_sizes_are_those_of_the_stream_encoder() {
for &rate in SampleRate::ALL {
let enc = MultistreamEncoder::new_surround(rate, 2, 1, Application::Audio).unwrap();
let fs = rate.hz();
let ms = |tenths: usize| fs as usize * tenths / 10_000;
for tenths in [25, 50, 100, 200, 400, 600, 800, 1000, 1200] {
assert_eq!(
enc.check_input(usize::MAX, ms(tenths)),
Ok(ms(tenths)),
"{fs} Hz, {tenths}"
);
}
for tenths in [150, 300, 1400] {
assert!(
enc.check_input(usize::MAX, ms(tenths)).is_err(),
"{fs} Hz, {tenths}"
);
}
for n in [0, 1, ms(25) - 1, ms(200) + 1, fs as usize + 1, usize::MAX] {
assert!(
enc.check_input(usize::MAX, n).is_err(),
"{fs} Hz, {n} samples"
);
}
assert!(enc.check_input(2 * ms(200) - 1, ms(200)).is_err());
assert!(enc.check_input(2 * ms(200), ms(200)).is_ok());
}
}
#[test]
fn the_expert_frame_duration_picks_the_frame() {
let mut enc =
MultistreamEncoder::new_surround(SampleRate::Hz48000, 2, 1, Application::Audio).unwrap();
assert_eq!(enc.expert_frame_duration(), FrameDuration::Argument);
enc.set_expert_frame_duration(FrameDuration::Ms20);
assert_eq!(enc.check_input(2 * 2880, 2880), Ok(960));
assert!(enc.check_input(2 * 2880 - 1, 2880).is_err());
assert!(enc.check_input(usize::MAX, 480).is_err());
assert_eq!(enc.check_input(usize::MAX, 1000), Ok(960));
enc.reset_state();
assert_eq!(enc.expert_frame_duration(), FrameDuration::Ms20);
}
}