use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
use crate::drc_huffman::drc_huff_decode_diff;
use crate::toc::variable_bits;
pub const DRC_MAX_DECODER_MODES: usize = 8;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct DrcCompressionCurve {
pub drc_lev_nullband_low: u8,
pub drc_lev_nullband_high: u8,
pub drc_gain_max_boost: u8,
pub drc_lev_max_boost: Option<u8>,
pub drc_nr_boost_sections: Option<u8>,
pub drc_gain_section_boost: Option<u8>,
pub drc_lev_section_boost: Option<u8>,
pub drc_gain_max_cut: u8,
pub drc_lev_max_cut: Option<u8>,
pub drc_nr_cut_sections: Option<u8>,
pub drc_gain_section_cut: Option<u8>,
pub drc_lev_section_cut: Option<u8>,
pub drc_tc_default_flag: bool,
pub time_constants: Option<DrcTimeConstants>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct DrcTimeConstants {
pub drc_tc_attack: u8,
pub drc_tc_release: u8,
pub drc_tc_attack_fast: u8,
pub drc_tc_release_fast: u8,
pub drc_adaptive_smoothing_flag: bool,
pub drc_attack_threshold: Option<u8>,
pub drc_release_threshold: Option<u8>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DrcDecoderMode {
pub drc_decoder_mode_id: u8,
pub drc_output_level_from: Option<u8>,
pub drc_output_level_to: Option<u8>,
pub drc_repeat_profile_flag: bool,
pub drc_repeat_id: Option<u8>,
pub drc_default_profile_flag: Option<bool>,
pub drc_compression_curve_flag: bool,
pub compression_curve: Option<DrcCompressionCurve>,
pub drc_gains_config: Option<u8>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DrcConfig {
pub drc_decoder_nr_modes: u8,
pub drc_eac3_profile: u8,
pub modes: Vec<DrcDecoderMode>,
}
impl DrcConfig {
pub fn nr_modes(&self) -> usize {
self.drc_decoder_nr_modes as usize + 1
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DrcGains {
pub mode_id: u8,
pub nr_drc_channels: u8,
pub nr_drc_subframes: u8,
pub nr_drc_bands: u8,
pub drc_gain_val: u8,
pub drc_gain: Vec<u8>,
}
impl DrcGains {
pub fn idx(&self, ch: usize, sf: usize, band: usize) -> usize {
let bands = self.nr_drc_bands as usize;
let sfs = self.nr_drc_subframes as usize;
ch * bands * sfs + band * sfs + sf
}
pub fn gain_db(&self, ch: usize, sf: usize, band: usize) -> i32 {
self.drc_gain[self.idx(ch, sf, band)] as i32 - 64
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DrcData {
pub gainsets: Vec<DrcGains>,
pub curve_present: bool,
pub drc_reset_flag: bool,
pub drc_reserved: u8,
}
#[derive(Debug, Clone, PartialEq)]
pub struct DrcFrame {
pub b_drc_present: bool,
pub config: Option<DrcConfig>,
pub data: Option<DrcData>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DrcChannelInfo {
pub nr_drc_channels: u8,
pub nr_drc_subframes: u8,
}
impl DrcChannelInfo {
pub fn new(nr_drc_channels: u8, nr_drc_subframes: u8) -> Self {
Self {
nr_drc_channels,
nr_drc_subframes,
}
}
}
pub fn nr_drc_subframes(frame_length: u32) -> Option<u8> {
match frame_length {
384 => Some(1),
512 => Some(2),
768 | 960 => Some(3),
1024 => Some(4),
1536 | 1920 => Some(6),
2048 => Some(8),
_ => None,
}
}
pub fn nr_drc_channels(channel_mode: u32) -> u8 {
match channel_mode {
0 | 1 => 1, _ => 3, }
}
pub fn nr_drc_bands(drc_gains_config: u8) -> u8 {
match drc_gains_config {
0 | 1 => 1,
2 => 2,
3 => 4,
_ => 1, }
}
pub fn parse_drc_frame(
br: &mut BitReader<'_>,
b_iframe: bool,
chan_info: DrcChannelInfo,
prev_config: Option<&DrcConfig>,
) -> Result<DrcFrame> {
let b_drc_present = br.read_bit()?;
if !b_drc_present {
return Ok(DrcFrame {
b_drc_present: false,
config: None,
data: None,
});
}
let config = if b_iframe {
Some(parse_drc_config(br)?)
} else {
None
};
let active = config.as_ref().or(prev_config).ok_or_else(|| {
Error::invalid(
"ac4: drc_frame on non-I-frame with no prior drc_config — \
cannot decode drc_data() without per-mode flags",
)
})?;
let data = parse_drc_data(br, active, chan_info)?;
Ok(DrcFrame {
b_drc_present: true,
config,
data: Some(data),
})
}
pub fn parse_drc_config(br: &mut BitReader<'_>) -> Result<DrcConfig> {
let drc_decoder_nr_modes = br.read_u32(3)? as u8;
let mode_count = drc_decoder_nr_modes as usize + 1;
let mut modes = Vec::with_capacity(mode_count);
for _ in 0..mode_count {
modes.push(parse_drc_decoder_mode_config(br, &modes)?);
}
let drc_eac3_profile = br.read_u32(3)? as u8;
Ok(DrcConfig {
drc_decoder_nr_modes,
drc_eac3_profile,
modes,
})
}
fn parse_drc_decoder_mode_config(
br: &mut BitReader<'_>,
prior_modes: &[DrcDecoderMode],
) -> Result<DrcDecoderMode> {
let drc_decoder_mode_id = br.read_u32(3)? as u8;
let (drc_output_level_from, drc_output_level_to) = if drc_decoder_mode_id > 3 {
(Some(br.read_u32(5)? as u8), Some(br.read_u32(5)? as u8))
} else {
(None, None)
};
let drc_repeat_profile_flag = br.read_bit()?;
if drc_repeat_profile_flag {
let drc_repeat_id = br.read_u32(3)? as u8;
let referenced = prior_modes
.iter()
.find(|m| m.drc_decoder_mode_id == drc_repeat_id)
.ok_or_else(|| Error::invalid("ac4: drc_repeat_id refers to undeclared mode"))?;
Ok(DrcDecoderMode {
drc_decoder_mode_id,
drc_output_level_from,
drc_output_level_to,
drc_repeat_profile_flag: true,
drc_repeat_id: Some(drc_repeat_id),
drc_default_profile_flag: None,
drc_compression_curve_flag: referenced.drc_compression_curve_flag,
compression_curve: None, drc_gains_config: referenced.drc_gains_config,
})
} else {
let drc_default_profile_flag = br.read_bit()?;
if drc_default_profile_flag {
Ok(DrcDecoderMode {
drc_decoder_mode_id,
drc_output_level_from,
drc_output_level_to,
drc_repeat_profile_flag: false,
drc_repeat_id: None,
drc_default_profile_flag: Some(true),
drc_compression_curve_flag: true,
compression_curve: None,
drc_gains_config: None,
})
} else {
let drc_compression_curve_flag = br.read_bit()?;
if drc_compression_curve_flag {
let curve = parse_drc_compression_curve(br)?;
Ok(DrcDecoderMode {
drc_decoder_mode_id,
drc_output_level_from,
drc_output_level_to,
drc_repeat_profile_flag: false,
drc_repeat_id: None,
drc_default_profile_flag: Some(false),
drc_compression_curve_flag: true,
compression_curve: Some(curve),
drc_gains_config: None,
})
} else {
let drc_gains_config = br.read_u32(2)? as u8;
Ok(DrcDecoderMode {
drc_decoder_mode_id,
drc_output_level_from,
drc_output_level_to,
drc_repeat_profile_flag: false,
drc_repeat_id: None,
drc_default_profile_flag: Some(false),
drc_compression_curve_flag: false,
compression_curve: None,
drc_gains_config: Some(drc_gains_config),
})
}
}
}
}
pub fn parse_drc_compression_curve(br: &mut BitReader<'_>) -> Result<DrcCompressionCurve> {
let drc_lev_nullband_low = br.read_u32(4)? as u8;
let drc_lev_nullband_high = br.read_u32(4)? as u8;
let drc_gain_max_boost = br.read_u32(4)? as u8;
let (drc_lev_max_boost, drc_nr_boost_sections, drc_gain_section_boost, drc_lev_section_boost) =
if drc_gain_max_boost > 0 {
let lmb = br.read_u32(5)? as u8;
let nbs = br.read_u32(1)? as u8;
if nbs > 0 {
(
Some(lmb),
Some(nbs),
Some(br.read_u32(4)? as u8),
Some(br.read_u32(5)? as u8),
)
} else {
(Some(lmb), Some(nbs), None, None)
}
} else {
(None, None, None, None)
};
let drc_gain_max_cut = br.read_u32(5)? as u8;
let (drc_lev_max_cut, drc_nr_cut_sections, drc_gain_section_cut, drc_lev_section_cut) =
if drc_gain_max_cut > 0 {
let lmc = br.read_u32(6)? as u8;
let ncs = br.read_u32(1)? as u8;
if ncs > 0 {
(
Some(lmc),
Some(ncs),
Some(br.read_u32(5)? as u8),
Some(br.read_u32(5)? as u8),
)
} else {
(Some(lmc), Some(ncs), None, None)
}
} else {
(None, None, None, None)
};
let drc_tc_default_flag = br.read_bit()?;
let time_constants = if !drc_tc_default_flag {
let drc_tc_attack = br.read_u32(8)? as u8;
let drc_tc_release = br.read_u32(8)? as u8;
let drc_tc_attack_fast = br.read_u32(8)? as u8;
let drc_tc_release_fast = br.read_u32(8)? as u8;
let drc_adaptive_smoothing_flag = br.read_bit()?;
let (drc_attack_threshold, drc_release_threshold) = if drc_adaptive_smoothing_flag {
(Some(br.read_u32(5)? as u8), Some(br.read_u32(5)? as u8))
} else {
(None, None)
};
Some(DrcTimeConstants {
drc_tc_attack,
drc_tc_release,
drc_tc_attack_fast,
drc_tc_release_fast,
drc_adaptive_smoothing_flag,
drc_attack_threshold,
drc_release_threshold,
})
} else {
None
};
Ok(DrcCompressionCurve {
drc_lev_nullband_low,
drc_lev_nullband_high,
drc_gain_max_boost,
drc_lev_max_boost,
drc_nr_boost_sections,
drc_gain_section_boost,
drc_lev_section_boost,
drc_gain_max_cut,
drc_lev_max_cut,
drc_nr_cut_sections,
drc_gain_section_cut,
drc_lev_section_cut,
drc_tc_default_flag,
time_constants,
})
}
pub fn parse_drc_data(
br: &mut BitReader<'_>,
config: &DrcConfig,
chan_info: DrcChannelInfo,
) -> Result<DrcData> {
let mut curve_present = false;
let mut gainsets = Vec::new();
for mode in &config.modes {
let mode_id = mode.drc_decoder_mode_id;
if !mode.drc_compression_curve_flag {
let mut drc_gainset_size = br.read_u32(6)?;
let b_more_bits = br.read_bit()?;
if b_more_bits {
drc_gainset_size += variable_bits(br, 2)? << 6;
}
let drc_version = br.read_u32(2)?;
let bit_pos_before = br.bit_position();
let gainset = if drc_version <= 1 {
let gains =
parse_drc_gains(br, mode_id, mode.drc_gains_config.unwrap_or(0), chan_info)?;
Some(gains)
} else {
None
};
let used_bits = (br.bit_position() - bit_pos_before) as u32;
if drc_version >= 1 {
let bits_left = drc_gainset_size
.checked_sub(2)
.and_then(|v| v.checked_sub(used_bits))
.ok_or_else(|| {
Error::invalid(
"ac4: drc_gainset_size accounting underflow \
(drc_gains used more bits than declared)",
)
})?;
if bits_left > 0 {
br.skip(bits_left)?;
}
}
if let Some(g) = gainset {
gainsets.push(g);
}
} else {
curve_present = true;
}
}
let (drc_reset_flag, drc_reserved) = if curve_present {
let reset = br.read_bit()?;
let reserved = br.read_u32(2)? as u8;
(reset, reserved)
} else {
(false, 0)
};
Ok(DrcData {
gainsets,
curve_present,
drc_reset_flag,
drc_reserved,
})
}
pub fn parse_drc_gains(
br: &mut BitReader<'_>,
mode_id: u8,
drc_gains_config_value: u8,
chan_info: DrcChannelInfo,
) -> Result<DrcGains> {
let drc_gain_val = br.read_u32(7)? as u8;
let nr_drc_bands = nr_drc_bands(drc_gains_config_value);
if drc_gains_config_value == 0 {
return Ok(DrcGains {
mode_id,
nr_drc_channels: 1,
nr_drc_subframes: 1,
nr_drc_bands: 1,
drc_gain_val,
drc_gain: vec![drc_gain_val],
});
}
let nr_ch = chan_info.nr_drc_channels as usize;
let nr_sf = chan_info.nr_drc_subframes as usize;
let nr_bd = nr_drc_bands as usize;
if nr_ch == 0 || nr_sf == 0 || nr_bd == 0 {
return Err(Error::invalid(
"ac4: drc_gains() invalid (ch, sf, band) dimensions",
));
}
let mut gains = vec![0u8; nr_ch * nr_bd * nr_sf];
let mut ref_gain: i32 = drc_gain_val as i32;
let mut first = true;
let idx =
|ch: usize, sf: usize, band: usize| -> usize { ch * nr_bd * nr_sf + band * nr_sf + sf };
for ch in 0..nr_ch {
for band in 0..nr_bd {
for sf in 0..nr_sf {
if first {
gains[idx(ch, sf, band)] = drc_gain_val;
first = false;
} else {
let diff = drc_huff_decode_diff(br)?;
let g = ref_gain + diff;
if !(0..=127).contains(&g) {
return Err(Error::invalid(
"ac4: DRC gain out of 7-bit range after diff",
));
}
gains[idx(ch, sf, band)] = g as u8;
}
ref_gain = gains[idx(ch, sf, band)] as i32;
}
ref_gain = gains[idx(ch, 0, band)] as i32;
}
ref_gain = gains[idx(ch, 0, 0)] as i32;
}
Ok(DrcGains {
mode_id,
nr_drc_channels: chan_info.nr_drc_channels,
nr_drc_subframes: chan_info.nr_drc_subframes,
nr_drc_bands,
drc_gain_val,
drc_gain: gains,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::drc_huffman::{DRC_HCB_CW, DRC_HCB_LEN};
use oxideav_core::bits::BitWriter;
fn write_zero_diff(bw: &mut BitWriter) {
bw.write_u32(DRC_HCB_CW[127], DRC_HCB_LEN[127] as u32);
}
fn write_plus_one_diff(bw: &mut BitWriter) {
bw.write_u32(DRC_HCB_CW[128], DRC_HCB_LEN[128] as u32);
}
fn write_minus_one_diff(bw: &mut BitWriter) {
bw.write_u32(DRC_HCB_CW[126], DRC_HCB_LEN[126] as u32);
}
#[test]
fn nr_drc_subframes_table_169() {
assert_eq!(nr_drc_subframes(384), Some(1));
assert_eq!(nr_drc_subframes(512), Some(2));
assert_eq!(nr_drc_subframes(768), Some(3));
assert_eq!(nr_drc_subframes(960), Some(3));
assert_eq!(nr_drc_subframes(1024), Some(4));
assert_eq!(nr_drc_subframes(1536), Some(6));
assert_eq!(nr_drc_subframes(1920), Some(6));
assert_eq!(nr_drc_subframes(2048), Some(8));
assert_eq!(nr_drc_subframes(999), None);
}
#[test]
fn nr_drc_bands_table_163() {
assert_eq!(nr_drc_bands(0), 1);
assert_eq!(nr_drc_bands(1), 1);
assert_eq!(nr_drc_bands(2), 2);
assert_eq!(nr_drc_bands(3), 4);
}
#[test]
fn parse_drc_frame_absent() {
let mut bw = BitWriter::new();
bw.write_bit(false);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let info = DrcChannelInfo::new(1, 1);
let frame = parse_drc_frame(&mut br, true, info, None).unwrap();
assert!(!frame.b_drc_present);
assert!(frame.config.is_none());
assert!(frame.data.is_none());
}
#[test]
fn parse_drc_frame_one_mode_wideband() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(0, 3); bw.write_u32(2, 3); bw.write_bit(false); bw.write_bit(true); bw.write_u32(1, 3); bw.write_bit(false); bw.write_u32(0, 2); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let info = DrcChannelInfo::new(1, 1);
let frame = parse_drc_frame(&mut br, true, info, None).unwrap();
assert!(frame.b_drc_present);
let cfg = frame.config.as_ref().expect("config present");
assert_eq!(cfg.nr_modes(), 1);
assert_eq!(cfg.modes[0].drc_decoder_mode_id, 2);
assert_eq!(
cfg.modes[0].drc_default_profile_flag,
Some(true),
"default profile"
);
assert!(cfg.modes[0].drc_compression_curve_flag);
assert_eq!(cfg.drc_eac3_profile, 1);
let data = frame.data.as_ref().expect("data present");
assert!(data.curve_present);
assert!(data.gainsets.is_empty());
assert!(!data.drc_reset_flag);
}
#[test]
fn parse_drc_frame_with_gainset_wideband() {
let drc_gain_val: u8 = 64;
let drc_gainset_size: u32 = 2 + 7 + 3 ;
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(0, 3); bw.write_u32(0, 3); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(1, 2); bw.write_u32(0, 3); bw.write_u32(drc_gainset_size, 6); bw.write_bit(false); bw.write_u32(0, 2); bw.write_u32(drc_gain_val as u32, 7);
write_plus_one_diff(&mut bw);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let info = DrcChannelInfo::new(1, 2);
let frame = parse_drc_frame(&mut br, true, info, None).unwrap();
let data = frame.data.as_ref().expect("data");
assert!(!data.curve_present);
assert_eq!(data.gainsets.len(), 1);
let g = &data.gainsets[0];
assert_eq!(g.nr_drc_channels, 1);
assert_eq!(g.nr_drc_subframes, 2);
assert_eq!(g.nr_drc_bands, 1);
assert_eq!(g.drc_gain_val, 64);
assert_eq!(g.drc_gain.len(), 2);
assert_eq!(g.gain_db(0, 0, 0), 0);
assert_eq!(g.gain_db(0, 1, 0), 1);
}
#[test]
fn parse_drc_frame_gainset_2band_2ch_2sf() {
let drc_gain_val: u8 = 64;
let total_diff_bits = 3 + 3 + 2 + 3 + 3 + 2 + 3;
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(0, 3); bw.write_u32(0, 3); bw.write_bit(false); bw.write_bit(false); bw.write_bit(false); bw.write_u32(2, 2); bw.write_u32(0, 3);
let drc_gainset_size: u32 = 2 + 7 + total_diff_bits;
bw.write_u32(drc_gainset_size, 6);
bw.write_bit(false); bw.write_u32(0, 2); bw.write_u32(drc_gain_val as u32, 7);
write_plus_one_diff(&mut bw);
write_minus_one_diff(&mut bw);
write_zero_diff(&mut bw);
write_plus_one_diff(&mut bw);
write_minus_one_diff(&mut bw);
write_zero_diff(&mut bw);
write_plus_one_diff(&mut bw);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let info = DrcChannelInfo::new(2, 2);
let frame = parse_drc_frame(&mut br, true, info, None).unwrap();
let g = &frame.data.as_ref().unwrap().gainsets[0];
assert_eq!(g.nr_drc_bands, 2);
assert_eq!(g.drc_gain.len(), 8);
assert_eq!(g.gain_db(0, 0, 0), 0); assert_eq!(g.gain_db(0, 1, 0), 1); assert_eq!(g.gain_db(0, 0, 1), -1); assert_eq!(g.gain_db(0, 1, 1), -1); assert_eq!(g.gain_db(1, 0, 0), 1); assert_eq!(g.gain_db(1, 1, 0), 0); assert_eq!(g.gain_db(1, 0, 1), 1); assert_eq!(g.gain_db(1, 1, 1), 2); }
#[test]
fn parse_drc_compression_curve_minimal() {
let mut bw = BitWriter::new();
bw.write_u32(0, 4); bw.write_u32(0, 4); bw.write_u32(0, 4); bw.write_u32(0, 5); bw.write_bit(true); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let curve = parse_drc_compression_curve(&mut br).unwrap();
assert_eq!(curve.drc_lev_nullband_low, 0);
assert_eq!(curve.drc_gain_max_boost, 0);
assert!(curve.drc_lev_max_boost.is_none());
assert!(curve.time_constants.is_none());
assert!(curve.drc_tc_default_flag);
}
#[test]
fn parse_drc_compression_curve_full_with_boost_and_cut() {
let mut bw = BitWriter::new();
bw.write_u32(3, 4); bw.write_u32(5, 4); bw.write_u32(7, 4); bw.write_u32(15, 5); bw.write_u32(1, 1); bw.write_u32(9, 4); bw.write_u32(20, 5); bw.write_u32(11, 5); bw.write_u32(33, 6); bw.write_u32(1, 1); bw.write_u32(13, 5); bw.write_u32(17, 5); bw.write_bit(false); bw.write_u32(20, 8); bw.write_u32(75, 8); bw.write_u32(2, 8); bw.write_u32(50, 8); bw.write_bit(true); bw.write_u32(15, 5); bw.write_u32(20, 5); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let curve = parse_drc_compression_curve(&mut br).unwrap();
assert_eq!(curve.drc_lev_nullband_low, 3);
assert_eq!(curve.drc_gain_max_boost, 7);
assert_eq!(curve.drc_lev_max_boost, Some(15));
assert_eq!(curve.drc_nr_boost_sections, Some(1));
assert_eq!(curve.drc_gain_section_boost, Some(9));
assert_eq!(curve.drc_lev_section_boost, Some(20));
assert_eq!(curve.drc_gain_max_cut, 11);
assert_eq!(curve.drc_lev_max_cut, Some(33));
let tc = curve.time_constants.as_ref().unwrap();
assert_eq!(tc.drc_tc_attack, 20);
assert!(tc.drc_adaptive_smoothing_flag);
assert_eq!(tc.drc_attack_threshold, Some(15));
assert_eq!(tc.drc_release_threshold, Some(20));
}
#[test]
fn parse_drc_decoder_mode_repeat_profile() {
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(1, 3);
bw.write_u32(0, 3); bw.write_bit(false); bw.write_bit(true);
bw.write_u32(1, 3); bw.write_bit(true); bw.write_u32(0, 3);
bw.write_u32(0, 3); bw.write_bit(true); bw.write_u32(2, 2); bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let info = DrcChannelInfo::new(1, 1);
let frame = parse_drc_frame(&mut br, true, info, None).unwrap();
let cfg = frame.config.unwrap();
assert_eq!(cfg.nr_modes(), 2);
assert!(cfg.modes[0].drc_compression_curve_flag);
assert!(cfg.modes[1].drc_repeat_profile_flag);
assert_eq!(cfg.modes[1].drc_repeat_id, Some(0));
assert!(cfg.modes[1].drc_compression_curve_flag); let data = frame.data.unwrap();
assert!(data.curve_present);
assert!(data.drc_reset_flag);
assert_eq!(data.drc_reserved, 2);
}
#[test]
fn parse_drc_frame_non_iframe_uses_prev_config() {
let drc_gain_val: u8 = 50;
let drc_gainset_size: u32 = 2 + 7;
let mut bw = BitWriter::new();
bw.write_bit(true); bw.write_u32(drc_gainset_size, 6); bw.write_bit(false); bw.write_u32(0, 2); bw.write_u32(drc_gain_val as u32, 7); bw.align_to_byte();
let bytes = bw.finish();
let prev = DrcConfig {
drc_decoder_nr_modes: 0,
drc_eac3_profile: 0,
modes: vec![DrcDecoderMode {
drc_decoder_mode_id: 0,
drc_output_level_from: None,
drc_output_level_to: None,
drc_repeat_profile_flag: false,
drc_repeat_id: None,
drc_default_profile_flag: Some(false),
drc_compression_curve_flag: false,
compression_curve: None,
drc_gains_config: Some(0),
}],
};
let mut br = BitReader::new(&bytes);
let info = DrcChannelInfo::new(1, 1);
let frame = parse_drc_frame(&mut br, false, info, Some(&prev)).unwrap();
assert!(frame.config.is_none(), "no config on non-I-frame");
let data = frame.data.unwrap();
assert_eq!(data.gainsets.len(), 1);
let g = &data.gainsets[0];
assert_eq!(g.drc_gain_val, 50);
assert_eq!(g.gain_db(0, 0, 0), -14);
}
#[test]
fn parse_drc_frame_non_iframe_without_prev_config_errors() {
let mut bw = BitWriter::new();
bw.write_bit(true);
bw.align_to_byte();
let bytes = bw.finish();
let mut br = BitReader::new(&bytes);
let info = DrcChannelInfo::new(1, 1);
assert!(parse_drc_frame(&mut br, false, info, None).is_err());
}
}