macro_rules! read_samples {
($reader:expr, 16) => {
$reader
.into_samples::<i16>()
.map(|s| s.expect("Failed to read samples from reference file"))
.collect::<Vec<_>>()
};
($reader:expr, 24) => {
$reader
.into_samples::<i32>()
.map(|s| s.expect("Failed to read samples from reference file") << 8)
.collect::<Vec<_>>()
};
}
macro_rules! prepare_file {
($file_name:expr, $rate:expr, $bpp:tt, $mode:expr, $channel_map:expr) => {{
let input_path = {
let mut r = std::path::PathBuf::new();
r.push(env!("CARGO_MANIFEST_DIR"));
r.push("tests");
r.push("reference_files");
r.push($file_name);
r
};
if !input_path.exists() {
panic!("Reference file {} not found", input_path.display());
}
let mut e = ebur128::EbuR128::new($channel_map.len() as u32, $rate, $mode)
.expect("Can't create EbuR128 instance");
e.set_channel_map(&$channel_map).unwrap();
let reader = hound::WavReader::open(&input_path).expect("Failed to read reference file");
let samples = read_samples!(reader, $bpp);
(e, samples)
}};
}
macro_rules! add_samples {
($e:expr, $samples:expr, 16) => {
$e.add_frames_i16(&*$samples).expect("Failed to analyze samples");
};
($e:expr, $samples:expr, 24) => {
$e.add_frames_i32(&*$samples).expect("Failed to analyze samples");
};
}
macro_rules! test_global_loudness(
($file_name:expr, $rate:expr, $bpp:tt, $channel_map:expr, $expected_loudness:expr) => {
{
let (mut e, samples) = prepare_file!($file_name, $rate, $bpp, ebur128::Mode::I, $channel_map);
add_samples!(e, samples, $bpp);
float_eq::assert_float_eq!(e.loudness_global().expect("Failed to get global loudness"), $expected_loudness, abs <= 0.1);
}
{
let (mut e, samples) = prepare_file!($file_name, $rate, $bpp, ebur128::Mode::I | ebur128::Mode::HISTOGRAM, $channel_map);
add_samples!(e, samples, $bpp);
float_eq::assert_float_eq!(e.loudness_global().expect("Failed to get global loudness"), $expected_loudness, abs <= 0.1);
}
};
);
macro_rules! test_true_peak(
($file_name:expr, $rate:expr, $bpp:tt, $channel_map:expr, $expected_true_peak:expr) => {
let (mut e, samples) = prepare_file!($file_name, $rate, $bpp, ebur128::Mode::TRUE_PEAK, $channel_map);
add_samples!(e, samples, $bpp);
let mut max_true_peak = f64::MIN;
for peak in (0..$channel_map.len())
.map(|c| e.true_peak(c as u32).expect("Failed to get true peak")) {
if peak > max_true_peak {
max_true_peak = peak;
}
}
let max_true_peak = 20.0 * f64::log10(max_true_peak);
assert!(
($expected_true_peak - 0.4..=$expected_true_peak + 0.2).contains(&max_true_peak),
"{} != {}",
max_true_peak,
$expected_true_peak,
);
};
);
macro_rules! test_loudness_range(
($file_name:expr, $rate:expr, $bpp:tt, $channel_map:expr, $expected_loudness_range:expr) => {
{
let (mut e, samples) = prepare_file!($file_name, $rate, $bpp, ebur128::Mode::LRA, $channel_map);
add_samples!(e, samples, $bpp);
let loudness_range = e.loudness_range().expect("Failed to get loudness range");
float_eq::assert_float_eq!(loudness_range, $expected_loudness_range, abs <= 1.0, "queue mode");
}
{
let (mut e, samples) = prepare_file!($file_name, $rate, $bpp, ebur128::Mode::LRA | ebur128::Mode::HISTOGRAM, $channel_map);
add_samples!(e, samples, $bpp);
let loudness_range = e.loudness_range().expect("Failed to get loudness range in histogram mode");
float_eq::assert_float_eq!(loudness_range, $expected_loudness_range, abs <= 1.0, "histogram mode");
}
};
);
#[test]
fn seq_3341_1() {
{
test_global_loudness!(
"seq-3341-1-16bit.wav",
48_000,
16,
[ebur128::Channel::Left, ebur128::Channel::Right],
-23.0
);
}
{
let (mut e, samples) = prepare_file!(
"seq-3341-1-16bit.wav",
48_000,
16,
ebur128::Mode::S,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
for (i, chunk) in samples.chunks(2 * 48_000 / 10).enumerate() {
e.add_frames_i16(chunk).expect("Failed to analyze samples");
if i >= 29 {
float_eq::assert_float_eq!(
e.loudness_shortterm()
.expect("Failed to get shortterm loudness"),
-23.0,
abs <= 0.1
);
}
}
}
{
let (mut e, samples) = prepare_file!(
"seq-3341-1-16bit.wav",
48_000,
16,
ebur128::Mode::M,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
for (i, chunk) in samples.chunks(2 * 48_000 / 10).enumerate() {
e.add_frames_i16(chunk).expect("Failed to analyze samples");
if i >= 10 {
float_eq::assert_float_eq!(
e.loudness_momentary()
.expect("Failed to get momentary loudness"),
-23.0,
abs <= 0.1
);
}
}
}
}
#[test]
fn seq_3341_2() {
test_global_loudness!(
"seq-3341-2-16bit.wav",
48_000,
16,
[ebur128::Channel::Left, ebur128::Channel::Right],
-33.0
);
{
let (mut e, samples) = prepare_file!(
"seq-3341-2-16bit.wav",
48_000,
16,
ebur128::Mode::S,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
for (i, chunk) in samples.chunks(2 * 48_000 / 10).enumerate() {
e.add_frames_i16(chunk).expect("Failed to analyze samples");
if i >= 29 {
float_eq::assert_float_eq!(
e.loudness_shortterm()
.expect("Failed to get shortterm loudness"),
-33.0,
abs <= 0.1
);
}
}
}
{
let (mut e, samples) = prepare_file!(
"seq-3341-2-16bit.wav",
48_000,
16,
ebur128::Mode::M,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
for (i, chunk) in samples.chunks(2 * 48_000 / 10).enumerate() {
e.add_frames_i16(chunk).expect("Failed to analyze samples");
if i >= 10 {
float_eq::assert_float_eq!(
e.loudness_momentary()
.expect("Failed to get momentary loudness"),
-33.0,
abs <= 0.1
);
}
}
}
}
#[test]
fn seq_3341_3() {
test_global_loudness!(
"seq-3341-3-16bit-v02.wav",
48_000,
16,
[ebur128::Channel::Left, ebur128::Channel::Right],
-23.0
);
}
#[test]
fn seq_3341_4() {
test_global_loudness!(
"seq-3341-4-16bit-v02.wav",
48_000,
16,
[ebur128::Channel::Left, ebur128::Channel::Right],
-23.0
);
}
#[test]
fn seq_3341_5() {
test_global_loudness!(
"seq-3341-5-16bit-v02.wav",
48_000,
16,
[ebur128::Channel::Left, ebur128::Channel::Right],
-23.0
);
}
#[test]
fn seq_3341_6() {
test_global_loudness!(
"seq-3341-6-5channels-16bit.wav",
48_000,
16,
[
ebur128::Channel::Left,
ebur128::Channel::Right,
ebur128::Channel::Center,
ebur128::Channel::LeftSurround,
ebur128::Channel::RightSurround
],
-23.0
);
}
#[test]
fn seq_3341_6_1() {
test_global_loudness!(
"seq-3341-6-6channels-WAVEEX-16bit.wav",
48_000,
16,
[
ebur128::Channel::Left,
ebur128::Channel::Right,
ebur128::Channel::Center,
ebur128::Channel::Unused,
ebur128::Channel::LeftSurround,
ebur128::Channel::RightSurround
],
-23.0
);
}
#[test]
fn seq_3341_7() {
test_global_loudness!(
"seq-3341-7_seq-3342-5-24bit.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
-23.0
);
}
#[test]
fn seq_3341_8() {
test_global_loudness!(
"seq-3341-2011-8_seq-3342-6-24bit-v02.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
-23.0
);
}
#[test]
fn seq_3341_9() {
let (mut e, samples) = prepare_file!(
"seq-3341-9-24bit.wav",
48_000,
24,
ebur128::Mode::S,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
for (i, chunk) in samples.chunks(2 * 48_000 / 10).enumerate() {
e.add_frames_i32(chunk).expect("Failed to analyze samples");
if i >= 29 {
float_eq::assert_float_eq!(
e.loudness_shortterm()
.expect("Failed to get shortterm loudness"),
-23.0,
abs <= 0.1
);
}
}
}
#[test]
fn seq_3341_10() {
for i in 1..=20 {
let (mut e, samples) = prepare_file!(
format!("seq-3341-10-{i}-24bit.wav"),
48_000,
24,
ebur128::Mode::S,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
let mut max_loudness = f64::MIN;
for chunk in samples.chunks(2 * 48_000 / 10) {
e.add_frames_i32(chunk).expect("Failed to analyze samples");
let loudness = e
.loudness_shortterm()
.expect("Failed to get shortterm loudness");
if loudness > max_loudness {
max_loudness = loudness;
}
}
float_eq::assert_float_eq!(max_loudness, -23.0, abs <= 0.1, "file {}", i);
}
}
#[test]
fn seq_3341_11() {
let (mut e, samples) = prepare_file!(
"seq-3341-11-24bit.wav",
48_000,
24,
ebur128::Mode::S,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
let mut max_loudness = f64::MIN;
for (i, chunk) in samples.chunks(2 * 48_000 / 10).enumerate() {
e.add_frames_i32(chunk).expect("Failed to analyze samples");
let loudness = e
.loudness_shortterm()
.expect("Failed to get shortterm loudness");
if loudness > max_loudness {
max_loudness = loudness;
}
if (i + 1) % 60 == 0 {
let expected = -38.0 + ((i + 1) / 60 - 1) as f64;
float_eq::assert_float_eq!(
max_loudness,
expected,
abs <= 0.1,
"chunk {}",
(i + 1) / 60 - 1
);
}
}
}
#[test]
fn seq_3341_12() {
let (mut e, samples) = prepare_file!(
"seq-3341-12-24bit.wav",
48_000,
24,
ebur128::Mode::M,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
for (i, chunk) in samples.chunks(2 * 48_000 / 10).enumerate() {
e.add_frames_i32(chunk).expect("Failed to analyze samples");
if i >= 10 {
float_eq::assert_float_eq!(
e.loudness_momentary()
.expect("Failed to get momentary loudness"),
-23.0,
abs <= 0.1
);
}
}
}
#[test]
fn seq_3341_13() {
for i in 1..=20 {
let (mut e, samples) = prepare_file!(
format!(
"seq-3341-13-{}-24bit.wav{}",
i,
if i > 2 { ".wav" } else { "" }
),
48_000,
24,
ebur128::Mode::M,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
let mut max_loudness = f64::MIN;
for chunk in samples.chunks(2 * 48_000 / 100) {
e.add_frames_i32(chunk).expect("Failed to analyze samples");
let loudness = e
.loudness_momentary()
.expect("Failed to get momentary loudness");
if loudness > max_loudness {
max_loudness = loudness;
}
}
float_eq::assert_float_eq!(max_loudness, -23.0, abs <= 0.1, "file {}", i);
}
}
#[test]
fn seq_3341_14() {
let (mut e, samples) = prepare_file!(
"seq-3341-14-24bit.wav.wav",
48_000,
24,
ebur128::Mode::M,
[ebur128::Channel::Left, ebur128::Channel::Right]
);
let mut max_loudness = f64::MIN;
for (i, chunk) in samples.chunks(2 * 48_000 / 100).enumerate() {
e.add_frames_i32(chunk).expect("Failed to analyze samples");
let loudness = e
.loudness_momentary()
.expect("Failed to get momentary loudness");
if loudness > max_loudness {
max_loudness = loudness;
}
if (i + 1) % 80 == 0 {
let expected = -38.0 + ((i + 1) / 80 - 1) as f64;
float_eq::assert_float_eq!(
max_loudness,
expected,
abs <= 0.1,
"chunk {}",
(i + 1) / 80 - 1
);
}
}
}
#[test]
fn seq_3341_15() {
test_true_peak!(
"seq-3341-15-24bit.wav.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
-6.0
);
}
#[test]
fn seq_3341_16() {
test_true_peak!(
"seq-3341-16-24bit.wav.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
-6.0
);
}
#[test]
fn seq_3341_17() {
test_true_peak!(
"seq-3341-17-24bit.wav.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
-6.0
);
}
#[test]
fn seq_3341_18() {
test_true_peak!(
"seq-3341-18-24bit.wav.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
-6.0
);
}
#[test]
fn seq_3341_19() {
test_true_peak!(
"seq-3341-19-24bit.wav.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
3.0
);
}
#[test]
fn seq_3341_20() {
test_true_peak!(
"seq-3341-20-24bit.wav.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
0.0
);
}
#[test]
fn seq_3341_21() {
test_true_peak!(
"seq-3341-21-24bit.wav.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
0.0
);
}
#[test]
fn seq_3341_22() {
test_true_peak!(
"seq-3341-22-24bit.wav.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
0.0
);
}
#[test]
fn seq_3341_23() {
test_true_peak!(
"seq-3341-23-24bit.wav.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
0.0
);
}
#[test]
fn seq_3342_1() {
test_loudness_range!(
"seq-3342-1-16bit.wav",
48_000,
16,
[ebur128::Channel::Left, ebur128::Channel::Right],
10.0
);
}
#[test]
fn seq_3342_2() {
test_loudness_range!(
"seq-3342-2-16bit.wav",
48_000,
16,
[ebur128::Channel::Left, ebur128::Channel::Right],
5.0
);
}
#[test]
fn seq_3342_3() {
test_loudness_range!(
"seq-3342-3-16bit.wav",
48_000,
16,
[ebur128::Channel::Left, ebur128::Channel::Right],
20.0
);
}
#[test]
fn seq_3342_4() {
test_loudness_range!(
"seq-3342-4-16bit.wav",
48_000,
16,
[ebur128::Channel::Left, ebur128::Channel::Right],
15.0
);
}
#[test]
fn seq_3342_5() {
test_loudness_range!(
"seq-3341-7_seq-3342-5-24bit.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
5.0
);
}
#[test]
fn seq_3342_6() {
test_loudness_range!(
"seq-3341-2011-8_seq-3342-6-24bit-v02.wav",
48_000,
24,
[ebur128::Channel::Left, ebur128::Channel::Right],
15.0
);
}