use crate::energy_to_loudness;
use bitflags::bitflags;
use std::error;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
NoMem,
InvalidMode,
InvalidChannelIndex,
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::NoMem => write!(f, "NoMem"),
Error::InvalidMode => write!(f, "Invalid Mode"),
Error::InvalidChannelIndex => write!(f, "Invalid Channel Index"),
}
}
}
bitflags! {
pub struct Mode: u8 {
const M = 0b00000001;
const S = 0b00000010 | Mode::M.bits;
const I = 0b00000100 | Mode::M.bits;
const LRA = 0b00001000 | Mode::S.bits;
const SAMPLE_PEAK = 0b00010000 | Mode::M.bits;
const TRUE_PEAK = 0b00110001;
const HISTOGRAM = 0b01000000;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Channel {
Unused,
Left,
Right,
Center,
LeftSurround,
RightSurround,
DualMono,
MpSC,
MmSC,
Mp060,
Mm060,
Mp090,
Mm090,
Mp135,
Mm135,
Mp180,
Up000,
Up030,
Um030,
Up045,
Um045,
Up090,
Um090,
Up110,
Um110,
Up135,
Um135,
Up180,
Tp000,
Bp000,
Bp045,
Bm045,
}
pub struct EbuR128 {
mode: Mode,
rate: u32,
channels: u32,
audio_data: Box<[f64]>,
audio_data_index: usize,
needed_frames: usize,
channel_map: Box<[Channel]>,
samples_in_100ms: usize,
filter: crate::filter::Filter,
block_energy_history: crate::history::History,
short_term_block_energy_history: crate::history::History,
short_term_frame_counter: usize,
sample_peak: Box<[f64]>,
true_peak: Box<[f64]>,
window: usize,
history: usize,
}
impl fmt::Debug for EbuR128 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EbuR128")
.field("mode", &self.mode)
.field("rate", &self.rate)
.field("channels", &self.channels)
.field("audio_data_index", &self.audio_data_index)
.field("needed_frames", &self.needed_frames)
.field("channel_map", &self.channel_map)
.field("samples_in_100ms", &self.samples_in_100ms)
.field("filter", &self.filter)
.field("block_energy_history", &self.block_energy_history)
.field(
"short_term_block_energy_history",
&self.short_term_block_energy_history,
)
.field("short_term_frame_counter", &self.short_term_frame_counter)
.field("sample_peak", &self.sample_peak)
.field("true_peak", &self.true_peak)
.field("window", &self.window)
.field("history", &self.history)
.finish()
}
}
pub(crate) fn default_channel_map(channels: u32) -> Vec<Channel> {
match channels {
4 => vec![
Channel::Left,
Channel::Right,
Channel::LeftSurround,
Channel::RightSurround,
],
5 => vec![
Channel::Left,
Channel::Right,
Channel::Center,
Channel::LeftSurround,
Channel::RightSurround,
],
_ => {
let mut v = vec![Channel::Unused; channels as usize];
let set_channels = std::cmp::min(channels as usize, 6);
v[0..set_channels].copy_from_slice(
&[
Channel::Left,
Channel::Right,
Channel::Center,
Channel::Unused,
Channel::LeftSurround,
Channel::RightSurround,
][..set_channels],
);
v
}
}
}
const MAX_RATE: u32 = 2822400;
const MAX_CHANNELS: u32 = 64;
impl EbuR128 {
pub fn new(channels: u32, rate: u32, mode: Mode) -> Result<Self, Error> {
if channels == 0 || channels > MAX_CHANNELS {
return Err(Error::NoMem);
}
if rate < 16 || rate > MAX_RATE {
return Err(Error::NoMem);
}
let sample_peak = vec![0.0; channels as usize];
let true_peak = vec![0.0; channels as usize];
let history = std::usize::MAX;
let samples_in_100ms = (rate as usize + 5) / 10;
let window = if mode.contains(Mode::S) {
3000
} else if mode.contains(Mode::M) {
400
} else {
return Err(Error::InvalidMode);
};
let mut audio_data_frames = (rate as usize).checked_mul(window).ok_or(Error::NoMem)? / 1000;
if audio_data_frames % samples_in_100ms != 0 {
audio_data_frames = audio_data_frames
.checked_add(samples_in_100ms)
.ok_or(Error::NoMem)?
- (audio_data_frames % samples_in_100ms);
}
let audio_data = vec![
0.0;
audio_data_frames
.checked_mul(channels as usize)
.ok_or(Error::NoMem)?
];
let audio_data_index = 0;
let block_energy_history =
crate::history::History::new(mode.contains(Mode::HISTOGRAM), history / 100);
let short_term_block_energy_history =
crate::history::History::new(mode.contains(Mode::HISTOGRAM), history / 3000);
let short_term_frame_counter = 0;
let filter = crate::filter::Filter::new(
rate,
channels,
mode.contains(Mode::SAMPLE_PEAK),
mode.contains(Mode::TRUE_PEAK),
);
let channel_map = default_channel_map(channels);
let needed_frames = samples_in_100ms * 4;
Ok(Self {
mode,
rate,
channels,
audio_data: audio_data.into_boxed_slice(),
audio_data_index,
needed_frames,
channel_map: channel_map.into_boxed_slice(),
samples_in_100ms,
filter,
block_energy_history,
short_term_block_energy_history,
short_term_frame_counter,
sample_peak: sample_peak.into_boxed_slice(),
true_peak: true_peak.into_boxed_slice(),
window,
history,
})
}
pub fn mode(&self) -> Mode {
self.mode
}
pub fn channels(&self) -> u32 {
self.channels
}
pub fn rate(&self) -> u32 {
self.rate
}
pub fn channel_map(&self) -> &[Channel] {
&*self.channel_map
}
pub fn max_window(&self) -> usize {
self.window
}
pub fn max_history(&self) -> usize {
self.history
}
pub fn set_channel(&mut self, channel_number: u32, value: Channel) -> Result<(), Error> {
if channel_number >= self.channels {
return Err(Error::InvalidChannelIndex);
}
if value == Channel::DualMono && (self.channels != 1 || channel_number != 0) {
return Err(Error::InvalidChannelIndex);
}
self.channel_map[channel_number as usize] = value;
Ok(())
}
pub fn set_channel_map(&mut self, channel_map: &[Channel]) -> Result<(), Error> {
if channel_map.len() != self.channels as usize {
return Err(Error::InvalidChannelIndex);
}
for (channel_number, value) in channel_map.iter().enumerate() {
if *value == Channel::DualMono && (self.channels != 1 || channel_number != 0) {
return Err(Error::InvalidChannelIndex);
}
}
self.channel_map.copy_from_slice(channel_map);
Ok(())
}
pub fn change_parameters(&mut self, channels: u32, rate: u32) -> Result<(), Error> {
if channels == 0 || channels > MAX_CHANNELS {
return Err(Error::NoMem);
}
if rate < 16 || rate > MAX_RATE {
return Err(Error::NoMem);
}
if self.rate == rate && self.channels == channels {
return Ok(());
}
let samples_in_100ms = (rate as usize + 5) / 10;
let mut audio_data_frames = (rate as usize)
.checked_mul(self.window)
.ok_or(Error::NoMem)?
/ 1000;
if audio_data_frames % samples_in_100ms != 0 {
audio_data_frames = audio_data_frames
.checked_add(samples_in_100ms)
.ok_or(Error::NoMem)?
- (audio_data_frames % samples_in_100ms);
}
self.audio_data = vec![
0.0;
audio_data_frames
.checked_mul(channels as usize)
.ok_or(Error::NoMem)?
]
.into_boxed_slice();
if self.channels != channels {
self.channels = channels;
self.channel_map = default_channel_map(channels).into_boxed_slice();
self.sample_peak = vec![0.0; channels as usize].into_boxed_slice();
self.true_peak = vec![0.0; channels as usize].into_boxed_slice();
}
if self.rate != rate {
self.rate = rate;
self.samples_in_100ms = samples_in_100ms;
}
self.filter = crate::filter::Filter::new(
rate,
channels,
self.mode.contains(Mode::SAMPLE_PEAK),
self.mode.contains(Mode::TRUE_PEAK),
);
self.needed_frames = self.samples_in_100ms * 4;
self.audio_data_index = 0;
self.short_term_frame_counter = 0;
Ok(())
}
pub fn set_max_window(&mut self, window: u32) -> Result<(), Error> {
let window = if self.mode.contains(Mode::S) {
std::cmp::max(window, 3000)
} else if self.mode.contains(Mode::M) {
std::cmp::max(window, 400)
} else {
window
};
if window as usize == self.window {
return Ok(());
}
let mut audio_data_frames = (self.rate as usize)
.checked_mul(window as usize)
.ok_or(Error::NoMem)?
/ 1000;
if audio_data_frames % self.samples_in_100ms != 0 {
audio_data_frames = audio_data_frames
.checked_add(self.samples_in_100ms)
.ok_or(Error::NoMem)?
- (audio_data_frames % self.samples_in_100ms);
}
self.audio_data = vec![
0.0;
audio_data_frames
.checked_mul(self.channels as usize)
.ok_or(Error::NoMem)?
]
.into_boxed_slice();
self.window = window as usize;
self.needed_frames = self.samples_in_100ms * 4;
self.audio_data_index = 0;
self.short_term_frame_counter = 0;
Ok(())
}
pub fn set_max_history(&mut self, history: u32) -> Result<(), Error> {
let history = if self.mode.contains(Mode::S) {
std::cmp::max(history, 3000)
} else if self.mode.contains(Mode::M) {
std::cmp::max(history, 400)
} else {
history
};
if self.history == history as usize {
return Ok(());
}
self.history = history as usize;
self.block_energy_history.set_max_size(self.history / 100);
self.short_term_block_energy_history
.set_max_size(self.history / 3000);
Ok(())
}
pub fn reset(&mut self) {
for v in &mut *self.audio_data {
*v = 0.0;
}
self.needed_frames = self.samples_in_100ms * 4;
self.audio_data_index = 0;
self.short_term_frame_counter = 0;
for v in &mut *self.true_peak {
*v = 0.0;
}
for v in &mut *self.sample_peak {
*v = 0.0;
}
self.filter.reset();
self.block_energy_history.reset();
self.short_term_block_energy_history.reset();
}
fn add_frames<'a, T: crate::AsF64 + 'a, S: crate::Samples<'a, T>>(
&mut self,
mut src: S,
) -> Result<(), Error> {
if src.frames() == 0 {
return Ok(());
}
if self.channels == 0 {
return Err(Error::NoMem);
}
self.filter.reset_peaks();
while src.frames() > 0 {
let num_frames = src.frames();
if num_frames >= self.needed_frames {
let (current, next) = src.split_at(self.needed_frames);
self.filter.process(
¤t,
&mut *self.audio_data,
self.audio_data_index,
&self.channel_map,
);
src = next;
self.audio_data_index += self.needed_frames;
if self.mode.contains(Mode::I) {
let energy = crate::filter::Filter::calc_gating_block(
self.samples_in_100ms * 4,
&self.audio_data,
self.audio_data_index,
&self.channel_map,
);
self.block_energy_history.add(energy);
}
if self.mode.contains(Mode::LRA) {
self.short_term_frame_counter += self.needed_frames;
if self.short_term_frame_counter == self.samples_in_100ms * 30 {
let energy = self.energy_shortterm()?;
self.short_term_block_energy_history.add(energy);
self.short_term_frame_counter = self.samples_in_100ms * 20;
}
}
if self.audio_data_index == self.audio_data.len() / self.channels as usize {
self.audio_data_index = 0;
}
self.needed_frames = self.samples_in_100ms;
} else {
let (current, next) = src.split_at(num_frames);
self.filter.process(
¤t,
&mut *self.audio_data,
self.audio_data_index,
&self.channel_map,
);
self.audio_data_index += num_frames;
if self.mode.contains(Mode::LRA) {
self.short_term_frame_counter += num_frames;
}
src = next;
self.needed_frames -= num_frames;
}
}
let prev_sample_peak = self.filter.sample_peak();
for (sample_peak, prev_sample_peak) in
self.sample_peak.iter_mut().zip(prev_sample_peak.iter())
{
if *prev_sample_peak > *sample_peak {
*sample_peak = *prev_sample_peak;
}
}
let prev_true_peak = self.filter.true_peak();
for (true_peak, prev_true_peak) in self.true_peak.iter_mut().zip(prev_true_peak.iter()) {
if *prev_true_peak > *true_peak {
*true_peak = *prev_true_peak;
}
}
Ok(())
}
pub fn add_frames_i16(&mut self, frames: &[i16]) -> Result<(), Error> {
self.add_frames(crate::Interleaved::new(frames, self.channels as usize)?)
}
pub fn add_frames_i32(&mut self, frames: &[i32]) -> Result<(), Error> {
self.add_frames(crate::Interleaved::new(frames, self.channels as usize)?)
}
pub fn add_frames_f32(&mut self, frames: &[f32]) -> Result<(), Error> {
self.add_frames(crate::Interleaved::new(frames, self.channels as usize)?)
}
pub fn add_frames_f64(&mut self, frames: &[f64]) -> Result<(), Error> {
self.add_frames(crate::Interleaved::new(frames, self.channels as usize)?)
}
pub fn add_frames_planar_i16(&mut self, frames: &[&[i16]]) -> Result<(), Error> {
self.add_frames(crate::Planar::new(frames)?)
}
pub fn add_frames_planar_i32(&mut self, frames: &[&[i32]]) -> Result<(), Error> {
self.add_frames(crate::Planar::new(frames)?)
}
pub fn add_frames_planar_f32(&mut self, frames: &[&[f32]]) -> Result<(), Error> {
self.add_frames(crate::Planar::new(frames)?)
}
pub fn add_frames_planar_f64(&mut self, frames: &[&[f64]]) -> Result<(), Error> {
self.add_frames(crate::Planar::new(frames)?)
}
pub fn loudness_global(&self) -> Result<f64, Error> {
if !self.mode.contains(Mode::I) {
return Err(Error::InvalidMode);
}
Ok(self.block_energy_history.gated_loudness())
}
pub fn loudness_global_multiple<'a>(
iter: impl Iterator<Item = &'a Self>,
) -> Result<f64, Error> {
use smallvec::SmallVec;
let h = iter
.map(|e| {
if !e.mode.contains(Mode::I) {
Err(Error::InvalidMode)
} else {
Ok(&e.block_energy_history)
}
})
.collect::<Result<SmallVec<[_; 16]>, _>>()?;
Ok(crate::history::History::gated_loudness_multiple(&*h))
}
fn energy_in_interval(&self, interval_frames: usize) -> Result<f64, Error> {
if interval_frames > self.audio_data.len() / self.channels as usize {
return Err(Error::InvalidMode);
}
Ok(crate::filter::Filter::calc_gating_block(
interval_frames,
&self.audio_data,
self.audio_data_index,
&self.channel_map,
))
}
pub fn loudness_momentary(&self) -> Result<f64, Error> {
let energy = self.energy_in_interval(self.samples_in_100ms * 4)?;
if energy <= 0.0 {
return Ok(-std::f64::INFINITY);
}
Ok(energy_to_loudness(energy))
}
fn energy_shortterm(&self) -> Result<f64, Error> {
self.energy_in_interval(self.samples_in_100ms * 30)
}
pub fn loudness_shortterm(&self) -> Result<f64, Error> {
let energy = self.energy_shortterm()?;
if energy <= 0.0 {
return Ok(-std::f64::INFINITY);
}
Ok(energy_to_loudness(energy))
}
pub fn loudness_window(&self, window: u32) -> Result<f64, Error> {
let interval_frames = (self.rate as usize)
.checked_mul(window as usize)
.ok_or(Error::InvalidMode)?
/ 1000;
let energy = self.energy_in_interval(interval_frames)?;
if energy <= 0.0 {
return Ok(-std::f64::INFINITY);
}
Ok(energy_to_loudness(energy))
}
pub fn loudness_range(&self) -> Result<f64, Error> {
if !self.mode.contains(Mode::LRA) {
return Err(Error::InvalidMode);
}
Ok(self.short_term_block_energy_history.loudness_range())
}
pub fn loudness_range_multiple<'a>(
iter: impl IntoIterator<Item = &'a Self>,
) -> Result<f64, Error> {
use smallvec::SmallVec;
let h = iter
.into_iter()
.map(|e| {
if !e.mode.contains(Mode::LRA) {
Err(Error::InvalidMode)
} else {
Ok(&e.short_term_block_energy_history)
}
})
.collect::<Result<SmallVec<[_; 16]>, _>>()?;
crate::history::History::loudness_range_multiple(&*h).map_err(|_| Error::InvalidMode)
}
pub fn sample_peak(&self, channel_number: u32) -> Result<f64, Error> {
if !self.mode.contains(Mode::SAMPLE_PEAK) {
return Err(Error::InvalidMode);
}
if channel_number >= self.channels {
return Err(Error::InvalidChannelIndex);
}
Ok(self.sample_peak[channel_number as usize])
}
pub fn prev_sample_peak(&self, channel_number: u32) -> Result<f64, Error> {
if !self.mode.contains(Mode::SAMPLE_PEAK) {
return Err(Error::InvalidMode);
}
if channel_number >= self.channels {
return Err(Error::InvalidChannelIndex);
}
Ok(self.filter.sample_peak()[channel_number as usize])
}
pub fn true_peak(&self, channel_number: u32) -> Result<f64, Error> {
if !self.mode.contains(Mode::TRUE_PEAK) {
return Err(Error::InvalidMode);
}
if channel_number >= self.channels {
return Err(Error::InvalidChannelIndex);
}
if self.sample_peak[channel_number as usize] > self.true_peak[channel_number as usize] {
Ok(self.sample_peak[channel_number as usize])
} else {
Ok(self.true_peak[channel_number as usize])
}
}
pub fn prev_true_peak(&self, channel_number: u32) -> Result<f64, Error> {
if !self.mode.contains(Mode::TRUE_PEAK) {
return Err(Error::InvalidMode);
}
if channel_number >= self.channels {
return Err(Error::InvalidChannelIndex);
}
let sample_peak = self.filter.sample_peak();
let true_peak = self.filter.true_peak();
if sample_peak[channel_number as usize] > true_peak[channel_number as usize] {
Ok(sample_peak[channel_number as usize])
} else {
Ok(true_peak[channel_number as usize])
}
}
pub fn relative_threshold(&self) -> Result<f64, Error> {
if !self.mode.contains(Mode::I) {
return Err(Error::InvalidMode);
}
Ok(self.block_energy_history.relative_threshold())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "c-tests")]
use crate::tests::Signal;
use float_eq::assert_float_eq;
#[cfg(feature = "c-tests")]
use quickcheck_macros::quickcheck;
#[test]
fn sine_stereo_i16() {
let mut data = vec![0i16; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator) * (std::i16::MAX - 1) as f32;
out[0] = val as i16;
out[1] = val as i16;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all()).unwrap();
ebu.add_frames_i16(&data).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6500000000000054,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6820309226891973,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6834583474398446,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.875007988101488,
abs <= 0.000001
);
assert_float_eq!(ebu.loudness_range().unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(
ebu.sample_peak(0).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.sample_peak(1).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_sample_peak(0).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_sample_peak(1).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.650000000000006,
abs <= 0.000001
);
ebu.reset();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-std::f64::INFINITY,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-std::f64::INFINITY,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-std::f64::INFINITY,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-std::f64::INFINITY,
abs <= 0.000001
);
assert_float_eq!(ebu.loudness_range().unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.true_peak(0).unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.true_peak(1).unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_true_peak(0).unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_true_peak(1).unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.relative_threshold().unwrap(), -70.0, abs <= 0.000001);
}
#[test]
fn sine_stereo_i32() {
let mut data = vec![0i32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator) * (std::i32::MAX - 1) as f32;
out[0] = val as i32;
out[1] = val as i32;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all()).unwrap();
ebu.add_frames_i32(&data).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6500000000000054,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6813325598274425,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6827591715105212,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.8742956620040943,
abs <= 0.000001
);
assert_float_eq!(ebu.loudness_range().unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.650000000000006,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_f32() {
let mut data = vec![0.0f32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator);
out[0] = val;
out[1] = val;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all()).unwrap();
ebu.add_frames_f32(&data).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6500000000000054,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6813325598268921,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6827591715100236,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.8742956620008693,
abs <= 0.000001
);
assert_float_eq!(ebu.loudness_range().unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.650000000000006,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_f64() {
let mut data = vec![0.0f64; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator);
out[0] = val as f64;
out[1] = val as f64;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all()).unwrap();
ebu.add_frames_f64(&data).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6500000000000054,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6813325598268921,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6827591715100236,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.8742956620008693,
abs <= 0.000001
);
assert_float_eq!(ebu.loudness_range().unwrap(), 0.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.650000000000006,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_i16_no_histogram() {
let mut data = vec![0i16; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator) * (std::i16::MAX - 1) as f32;
out[0] = val as i16;
out[1] = val as i16;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_i16(&data).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.683303243667768,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6820309226891973,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6834583474398446,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.875007988101488,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_range().unwrap(),
0.00006950793233284625,
abs <= 0.000001
);
assert_float_eq!(
ebu.sample_peak(0).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.sample_peak(1).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_sample_peak(0).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_sample_peak(1).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.683303243667767,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_i32_no_histogram() {
let mut data = vec![0i32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator) * (std::i32::MAX - 1) as f32;
out[0] = val as i32;
out[1] = val as i32;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_i32(&data).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6826039914171368,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6813325598274425,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6827591715105212,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.8742956620040943,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_range().unwrap(),
0.00006921150165073442,
abs <= 0.000001
);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.682603991417135,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_f32_no_histogram() {
let mut data = vec![0.0f32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator);
out[0] = val;
out[1] = val;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_f32(&data).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6826039914165554,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6813325598268921,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6827591715100236,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.8742956620008693,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_range().unwrap(),
0.00006921150169403312,
abs <= 0.000001
);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.682603991416554,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_f64_no_histogram() {
let mut data = vec![0.0f64; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator);
out[0] = val as f64;
out[1] = val as f64;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_f64(&data).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6826039914165554,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6813325598268921,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6827591715100236,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.8742956620008693,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_range().unwrap(),
0.00006921150169403312,
abs <= 0.000001
);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.682603991416554,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_i16_planar_no_histogram() {
let mut data = vec![0i16; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
let (fst, snd) = data.split_at_mut(48_000 * 5);
for (fst, snd) in fst.iter_mut().zip(snd.iter_mut()) {
let val = f32::sin(accumulator) * (std::i16::MAX - 1) as f32;
*fst = val as i16;
*snd = val as i16;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_planar_i16(&[fst, snd]).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.683303243667768,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6820309226891973,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6834583474398446,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.875007988101488,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_range().unwrap(),
0.00006950793233284625,
abs <= 0.000001
);
assert_float_eq!(
ebu.sample_peak(0).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.sample_peak(1).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_sample_peak(0).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_sample_peak(1).unwrap(),
0.99993896484375,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0007814168930054,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.683303243667767,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_i32_planar_no_histogram() {
let mut data = vec![0i32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
let (fst, snd) = data.split_at_mut(48_000 * 5);
for (fst, snd) in fst.iter_mut().zip(snd.iter_mut()) {
let val = f32::sin(accumulator) * (std::i32::MAX - 1) as f32;
*fst = val as i32;
*snd = val as i32;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_planar_i32(&[fst, snd]).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6826039914171368,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6813325598274425,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6827591715105212,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.8742956620040943,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_range().unwrap(),
0.00006921150165073442,
abs <= 0.000001
);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.682603991417135,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_f32_planar_no_histogram() {
let mut data = vec![0.0f32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
let (fst, snd) = data.split_at_mut(48_000 * 5);
for (fst, snd) in fst.iter_mut().zip(snd.iter_mut()) {
let val = f32::sin(accumulator);
*fst = val;
*snd = val;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_planar_f32(&[fst, snd]).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6826039914165554,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6813325598268921,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6827591715100236,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.8742956620008693,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_range().unwrap(),
0.00006921150169403312,
abs <= 0.000001
);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.682603991416554,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_f64_planar_no_histogram() {
let mut data = vec![0.0f64; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
let (fst, snd) = data.split_at_mut(48_000 * 5);
for (fst, snd) in fst.iter_mut().zip(snd.iter_mut()) {
let val = f32::sin(accumulator);
*fst = val as f64;
*snd = val as f64;
accumulator += step;
}
let mut ebu = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_planar_f64(&[fst, snd]).unwrap();
assert_float_eq!(
ebu.loudness_global().unwrap(),
-0.6826039914165554,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
-0.6813325598268921,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
-0.6827591715100236,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
-0.8742956620008693,
abs <= 0.000001
);
assert_float_eq!(
ebu.loudness_range().unwrap(),
0.00006921150169403312,
abs <= 0.000001
);
assert_float_eq!(ebu.sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(0).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(ebu.prev_sample_peak(1).unwrap(), 1.0, abs <= 0.000001);
assert_float_eq!(
ebu.true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(0).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.prev_true_peak(1).unwrap(),
1.0008491277694702,
abs <= 0.000001
);
assert_float_eq!(
ebu.relative_threshold().unwrap(),
-10.682603991416554,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_f32_multiple() {
let mut data = vec![0.0f32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator);
out[0] = val;
out[1] = val;
accumulator += step;
}
let mut ebu1 = EbuR128::new(2, 48_000, Mode::all()).unwrap();
ebu1.add_frames_f32(&data).unwrap();
let mut data = vec![0.0f32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 880.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator);
out[0] = 0.5 * val;
out[1] = 0.5 * val;
accumulator += step;
}
let mut ebu2 = EbuR128::new(2, 48_000, Mode::all()).unwrap();
ebu2.add_frames_f32(&data).unwrap();
assert_float_eq!(
EbuR128::loudness_global_multiple([&ebu1, &ebu2].iter().copied()).unwrap(),
-2.603757953612454,
abs <= 0.000001
);
assert_float_eq!(
EbuR128::loudness_range_multiple([&ebu1, &ebu2].iter().copied()).unwrap(),
5.599999999999995,
abs <= 0.000001
);
}
#[test]
fn sine_stereo_f32_no_histogram_multiple() {
let mut data = vec![0.0f32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 440.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator);
out[0] = val;
out[1] = val;
accumulator += step;
}
let mut ebu1 = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu1.add_frames_f32(&data).unwrap();
let mut data = vec![0.0f32; 48_000 * 5 * 2];
let mut accumulator = 0.0;
let step = 2.0 * std::f32::consts::PI * 880.0 / 48_000.0;
for out in data.chunks_exact_mut(2) {
let val = f32::sin(accumulator);
out[0] = 0.5 * val;
out[1] = 0.5 * val;
accumulator += step;
}
let mut ebu2 = EbuR128::new(2, 48_000, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu2.add_frames_f32(&data).unwrap();
assert_float_eq!(
EbuR128::loudness_global_multiple([&ebu1, &ebu2].iter().copied()).unwrap(),
-2.6302830567858275,
abs <= 0.000001
);
assert_float_eq!(
EbuR128::loudness_range_multiple([&ebu1, &ebu2].iter().copied()).unwrap(),
5.571749801957784,
abs <= 0.000001
);
}
#[cfg(feature = "c-tests")]
fn compare_results(ebu: &EbuR128, ebu_c: &ebur128_c::EbuR128, channels: u32) {
assert_float_eq!(
ebu.loudness_global().unwrap(),
ebu_c.loudness_global().unwrap(),
ulps <= 2
);
assert_float_eq!(
ebu.loudness_momentary().unwrap(),
ebu_c.loudness_momentary().unwrap(),
ulps <= 2
);
assert_float_eq!(
ebu.loudness_shortterm().unwrap(),
ebu_c.loudness_shortterm().unwrap(),
ulps <= 2
);
assert_float_eq!(
ebu.loudness_window(1).unwrap(),
ebu_c.loudness_window(1).unwrap(),
ulps <= 2
);
assert_float_eq!(
ebu.loudness_range().unwrap(),
ebu_c.loudness_range().unwrap(),
ulps <= 2
);
for c in 0..channels {
assert_float_eq!(
ebu.sample_peak(c).unwrap(),
ebu_c.sample_peak(c).unwrap(),
ulps <= 2
);
assert_float_eq!(
ebu.prev_sample_peak(c).unwrap(),
ebu_c.prev_sample_peak(c).unwrap(),
ulps <= 2
);
assert_float_eq!(
ebu.true_peak(c).unwrap(),
ebu_c.true_peak(c).unwrap(),
ulps <= 2
);
assert_float_eq!(
ebu.prev_true_peak(c).unwrap(),
ebu_c.prev_true_peak(c).unwrap(),
ulps <= 2
);
}
assert_float_eq!(
ebu.relative_threshold().unwrap(),
ebu_c.relative_threshold().unwrap(),
ulps <= 2
);
}
#[cfg(feature = "c-tests")]
#[quickcheck]
fn compare_c_impl_i16(signal: Signal<i16>) {
let mut ebu = EbuR128::new(signal.channels, signal.rate, Mode::all()).unwrap();
ebu.add_frames_i16(&signal.data).unwrap();
let mut ebu_c =
ebur128_c::EbuR128::new(signal.channels, signal.rate, ebur128_c::Mode::all()).unwrap();
ebu_c.add_frames_i16(&signal.data).unwrap();
compare_results(&ebu, &ebu_c, signal.channels);
}
#[cfg(feature = "c-tests")]
#[quickcheck]
fn compare_c_impl_i32(signal: Signal<i32>) {
let mut ebu = EbuR128::new(signal.channels, signal.rate, Mode::all()).unwrap();
ebu.add_frames_i32(&signal.data).unwrap();
let mut ebu_c =
ebur128_c::EbuR128::new(signal.channels, signal.rate, ebur128_c::Mode::all()).unwrap();
ebu_c.add_frames_i32(&signal.data).unwrap();
compare_results(&ebu, &ebu_c, signal.channels);
}
#[cfg(feature = "c-tests")]
#[quickcheck]
fn compare_c_impl_f32(signal: Signal<f32>) {
let mut ebu = EbuR128::new(signal.channels, signal.rate, Mode::all()).unwrap();
ebu.add_frames_f32(&signal.data).unwrap();
let mut ebu_c =
ebur128_c::EbuR128::new(signal.channels, signal.rate, ebur128_c::Mode::all()).unwrap();
ebu_c.add_frames_f32(&signal.data).unwrap();
compare_results(&ebu, &ebu_c, signal.channels);
}
#[cfg(feature = "c-tests")]
#[quickcheck]
fn compare_c_impl_f64(signal: Signal<f64>) {
let mut ebu = EbuR128::new(signal.channels, signal.rate, Mode::all()).unwrap();
ebu.add_frames_f64(&signal.data).unwrap();
let mut ebu_c =
ebur128_c::EbuR128::new(signal.channels, signal.rate, ebur128_c::Mode::all()).unwrap();
ebu_c.add_frames_f64(&signal.data).unwrap();
compare_results(&ebu, &ebu_c, signal.channels);
}
#[cfg(feature = "c-tests")]
#[quickcheck]
fn compare_c_impl_i16_no_histogram(signal: Signal<i16>) {
let mut ebu =
EbuR128::new(signal.channels, signal.rate, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_i16(&signal.data).unwrap();
let mut ebu_c = ebur128_c::EbuR128::new(
signal.channels,
signal.rate,
ebur128_c::Mode::all() & !ebur128_c::Mode::HISTOGRAM,
)
.unwrap();
ebu_c.add_frames_i16(&signal.data).unwrap();
compare_results(&ebu, &ebu_c, signal.channels);
}
#[cfg(feature = "c-tests")]
#[quickcheck]
fn compare_c_impl_i32_no_histogram(signal: Signal<i32>) {
let mut ebu =
EbuR128::new(signal.channels, signal.rate, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_i32(&signal.data).unwrap();
let mut ebu_c = ebur128_c::EbuR128::new(
signal.channels,
signal.rate,
ebur128_c::Mode::all() & !ebur128_c::Mode::HISTOGRAM,
)
.unwrap();
ebu_c.add_frames_i32(&signal.data).unwrap();
compare_results(&ebu, &ebu_c, signal.channels);
}
#[cfg(feature = "c-tests")]
#[quickcheck]
fn compare_c_impl_f32_no_histogram(signal: Signal<f32>) {
let mut ebu =
EbuR128::new(signal.channels, signal.rate, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_f32(&signal.data).unwrap();
let mut ebu_c = ebur128_c::EbuR128::new(
signal.channels,
signal.rate,
ebur128_c::Mode::all() & !ebur128_c::Mode::HISTOGRAM,
)
.unwrap();
ebu_c.add_frames_f32(&signal.data).unwrap();
compare_results(&ebu, &ebu_c, signal.channels);
}
#[cfg(feature = "c-tests")]
#[quickcheck]
fn compare_c_impl_f64_no_histogram(signal: Signal<f64>) {
let mut ebu =
EbuR128::new(signal.channels, signal.rate, Mode::all() & !Mode::HISTOGRAM).unwrap();
ebu.add_frames_f64(&signal.data).unwrap();
let mut ebu_c = ebur128_c::EbuR128::new(
signal.channels,
signal.rate,
ebur128_c::Mode::all() & !ebur128_c::Mode::HISTOGRAM,
)
.unwrap();
ebu_c.add_frames_f64(&signal.data).unwrap();
compare_results(&ebu, &ebu_c, signal.channels);
}
}