#![warn(missing_docs)]
#[macro_use]
extern crate enum_display_derive;
use biquad::{Biquad, Coefficients, DirectForm1};
use std::error::Error;
use std::fmt::Display;
use std::result::Result;
const AUDIO_BLOCK_S: f64 = 0.1;
const MOMENTARY_BLOCK_S: f64 = 0.4;
const SHORT_TERM_BLOCK_S: f64 = 3.;
const GATING_THRESHOLD_ABSOLUTE: f64 = -70.;
#[derive(PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd, Debug, Display)]
pub enum Channel {
Left = 0,
Right = 1,
Centre = 2,
Lfe = 3,
LeftSurround = 4,
RightSurround = 5,
}
impl Default for Channel {
fn default() -> Self {
Channel::Left
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display)]
pub enum GatingType {
None,
Absolute,
Relative,
}
impl Default for GatingType {
fn default() -> Self {
GatingType::None
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Display)]
pub enum Mode {
Normal,
Streaming,
}
impl Default for Mode {
fn default() -> Self {
Mode::Normal
}
}
fn channel_from_index(index: usize) -> Channel {
match index {
0 => Channel::Left,
1 => Channel::Right,
2 => Channel::Centre,
3 => Channel::Lfe,
4 => Channel::LeftSurround,
_ => Channel::RightSurround,
}
}
#[derive(PartialEq, Eq, Hash, Copy, Clone, Ord, PartialOrd, Debug, Display)]
pub enum Ebur128Error {
NotEnoughAudio,
TooQuiet,
NotAvailable,
WrongBlockSize,
}
impl Error for Ebur128Error {}
fn root_mean<I: ExactSizeIterator<Item = f64>>(values: I) -> f64 {
let divisor = values.len() as f64;
values.map(|v| v * v).sum::<f64>() / divisor
}
fn channel_gain(channel: Channel) -> f64 {
match channel {
Channel::Left => 1.,
Channel::Right => 1.,
Channel::Centre => 1.,
Channel::LeftSurround => 1.41,
Channel::RightSurround => 1.41,
_ => 0.,
}
}
fn get_filters() -> (DirectForm1<f64>, DirectForm1<f64>) {
let stage1 = Coefficients {
a1: -1.69065929318241,
a2: 0.73248077421585,
b0: 1.53512485958697,
b1: -2.69169618940638,
b2: 1.19839281085285,
};
let stage2 = Coefficients {
a1: -1.99004745483398,
a2: 0.99007225036621,
b0: 1.,
b1: -2.,
b2: 1.,
};
(
DirectForm1::<f64>::new(stage1),
DirectForm1::<f64>::new(stage2),
)
}
pub fn calculate_loudness<I: ExactSizeIterator<Item = f64>>(samples: I, channel: Channel) -> f64 {
root_mean(samples) * channel_gain(channel)
}
fn deinterleave<I: ExactSizeIterator<Item = f64>>(
samples: I,
num_channels: usize,
) -> Vec<Vec<f64>> {
let mut deinterleaved: Vec<Vec<f64>> =
vec![Vec::with_capacity(samples.len() / num_channels); num_channels];
let index = (0..num_channels).cycle();
for (sample, idx) in samples.zip(index) {
deinterleaved[idx].push(sample);
}
deinterleaved
}
fn deinterleave_and_filter<I: ExactSizeIterator<Item = f64>>(
interleaved_samples: I,
num_channels: usize,
) -> Vec<Vec<f64>> {
let mut audio = deinterleave(interleaved_samples, num_channels);
let mut filters = vec![get_filters(); num_channels];
for (channel, (mut f1, mut f2)) in audio.iter_mut().zip(filters.iter_mut()) {
for sample in channel.iter_mut() {
*sample = f2.run(f1.run(*sample));
}
}
audio
}
pub fn calculate_loudness_interleaved<I: ExactSizeIterator<Item = f64>>(
interleaved_samples: I,
num_channels: usize,
) -> f64 {
let audio = deinterleave_and_filter(interleaved_samples, num_channels);
let mut sum = 0f64;
for (index, channel_audio) in audio.into_iter().enumerate() {
let channel = channel_from_index(index);
if channel == Channel::Lfe {
continue;
}
sum += calculate_loudness(channel_audio.iter().cloned(), channel);
}
-0.691 + (10. * sum.log10())
}
#[derive(Clone, Debug)]
pub struct State {
sample_rate: f64,
channels: usize,
loudness_blocks: Vec<f64>,
running_loudness: f64,
blocks_processed: f64,
mode: Mode,
}
impl Default for State {
fn default() -> Self {
State::new(48000., 2, Mode::Normal)
}
}
impl State {
pub fn new(sample_rate: f64, channels: usize, mode: Mode) -> State {
State {
sample_rate: sample_rate,
channels: channels,
loudness_blocks: vec![],
running_loudness: 0.,
blocks_processed: 0.,
mode: mode,
}
}
fn short_term_loudness_blocks() -> usize {
(SHORT_TERM_BLOCK_S / AUDIO_BLOCK_S) as usize
}
fn momentary_loudness_blocks() -> usize {
(MOMENTARY_BLOCK_S / AUDIO_BLOCK_S) as usize
}
fn samples_per_audio_block(&self) -> usize {
(AUDIO_BLOCK_S * self.sample_rate) as usize * self.channels
}
fn gating_threshold(&self, gating: GatingType) -> f64 {
match gating {
GatingType::None => f64::NEG_INFINITY,
GatingType::Absolute => GATING_THRESHOLD_ABSOLUTE,
GatingType::Relative => self.integrated_loudness(GatingType::Absolute).unwrap() - 10.,
}
}
fn update_running_loudness(&mut self, val: f64) {
self.blocks_processed += 1.;
self.running_loudness += (val - self.running_loudness) / self.blocks_processed;
}
fn store_loudness_block(&mut self, loudness: f64) {
self.loudness_blocks.push(loudness);
if self.mode == Mode::Streaming {
while self.loudness_blocks.len() > Self::short_term_loudness_blocks() {
self.loudness_blocks.remove(0);
}
}
}
pub fn process<I: ExactSizeIterator<Item = f64>>(
&mut self,
interleaved_samples: I,
) -> Result<(), Ebur128Error> {
if interleaved_samples.len() != self.samples_per_audio_block() {
return Err(Ebur128Error::WrongBlockSize);
}
let loudness = calculate_loudness_interleaved(interleaved_samples, self.channels);
self.store_loudness_block(loudness);
if self.mode == Mode::Streaming {
self.update_running_loudness(loudness);
}
Ok(())
}
fn loudness(
&self,
gating: GatingType,
starting_block_index: usize,
) -> Result<f64, Ebur128Error> {
let threshold = self.gating_threshold(gating);
let blocks_above_threshold = self.loudness_blocks[starting_block_index..]
.iter()
.filter(|loudness| **loudness >= threshold)
.count();
if blocks_above_threshold == 0 {
return Err(Ebur128Error::TooQuiet);
}
Ok(self.loudness_blocks[starting_block_index..]
.iter()
.filter(|loudness| **loudness >= threshold)
.sum::<f64>()
/ blocks_above_threshold as f64)
}
pub fn integrated_loudness(&self, gating: GatingType) -> Result<f64, Ebur128Error> {
if self.mode == Mode::Streaming {
if gating == GatingType::Relative {
return Err(Ebur128Error::NotAvailable);
}
if self.blocks_processed == 0. {
return Err(Ebur128Error::NotEnoughAudio);
}
return Ok(self.running_loudness);
}
self.loudness(gating, 0)
}
pub fn short_term_loudness(&self, gating: GatingType) -> Result<f64, Ebur128Error> {
let starting_block_idx = self
.loudness_blocks
.len()
.checked_sub(Self::short_term_loudness_blocks())
.unwrap_or(0);
self.loudness(gating, starting_block_idx)
}
pub fn momentary_loudness(&self, gating: GatingType) -> Result<f64, Ebur128Error> {
let starting_block_idx = self
.loudness_blocks
.len()
.checked_sub(Self::momentary_loudness_blocks())
.unwrap_or(0);
self.loudness(gating, starting_block_idx)
}
}
#[cfg(test)]
mod tests {
use super::*;
use dasp_signal::{self as signal, Signal};
use more_asserts::*;
use rand::{thread_rng, Rng};
macro_rules! assert_close_enough {
($left:expr, $right:expr, $tollerance:expr) => {
let (left, right, tollerance) = (&($left), &($right), &($tollerance));
assert_ge!(*left, *right - *tollerance);
assert_le!(*left, *right + *tollerance);
};
}
fn create_noise(length: usize, scale: f64) -> Vec<f64> {
let mut rng = thread_rng();
(0..length).map(|_| rng.gen::<f64>() * scale).collect()
}
fn tone_1k() -> Vec<f64> {
signal::rate(48000.)
.const_hz(1000.)
.sine()
.take(48000)
.collect::<Vec<f64>>()
}
#[test]
fn new_state() {
let state = State::default();
assert!(state.integrated_loudness(GatingType::None).is_err());
}
#[test]
fn input_too_short() {
let mut state = State::default();
assert!(state.process(vec![0f64; 9599].into_iter()).is_err());
}
#[test]
fn input_too_long() {
let mut state = State::default();
assert!(state.process(vec![0f64; 9601].into_iter()).is_err());
}
#[test]
fn input_correct_length() {
let mut state = State::default();
assert!(state.process(vec![0f64; 9600].into_iter()).is_ok());
}
#[test]
fn integrated_loudness_multiple_frames() {
let mut state = State::default();
assert!(state.process(create_noise(9600, 0.5).into_iter()).is_ok());
let loudness1 = state.integrated_loudness(GatingType::None).unwrap();
assert!(state.process(create_noise(9600, 0.5).into_iter()).is_ok());
let loudness2 = state.integrated_loudness(GatingType::None).unwrap();
assert_close_enough!(loudness1, loudness2, 0.1);
}
#[test]
fn integrated_loudness_ungated() {
let mut state = State::default();
assert!(state.process(create_noise(9600, 0.5).into_iter()).is_ok());
let loudness1 = state.integrated_loudness(GatingType::None).unwrap();
assert!(state.process(create_noise(9600, 0.1).into_iter()).is_ok());
let loudness2 = state.integrated_loudness(GatingType::None).unwrap();
assert!(state.process(create_noise(9600, 0.9).into_iter()).is_ok());
let loudness3 = state.integrated_loudness(GatingType::None).unwrap();
assert_gt!(loudness1, loudness2);
assert_lt!(loudness2, loudness3);
}
#[test]
fn integrated_loudness_gated_absolute() {
let mut state = State::default();
assert!(state.process(create_noise(9600, 0.5).into_iter()).is_ok());
let loudness1 = state.integrated_loudness(GatingType::Absolute).unwrap();
assert!(state
.process(create_noise(9600, 0.0001).into_iter())
.is_ok());
let loudness2 = state.integrated_loudness(GatingType::Absolute).unwrap();
assert!(state.process(create_noise(9600, 0.5).into_iter()).is_ok());
let loudness3 = state.integrated_loudness(GatingType::Absolute).unwrap();
assert_eq!(loudness1, loudness2);
assert_close_enough!(loudness1, loudness3, 0.1);
}
#[test]
fn integrated_loudness_gated_relative() {
let mut state = State::default();
assert!(state.process(create_noise(9600, 0.5).into_iter()).is_ok());
let loudness1 = state.integrated_loudness(GatingType::Relative).unwrap();
assert!(state.process(create_noise(9600, 0.01).into_iter()).is_ok());
let loudness2 = state.integrated_loudness(GatingType::Relative).unwrap();
assert!(state.process(create_noise(9600, 0.5).into_iter()).is_ok());
let loudness3 = state.integrated_loudness(GatingType::Relative).unwrap();
assert_eq!(loudness1, loudness2);
assert_close_enough!(loudness1, loudness3, 0.1);
}
#[test]
fn short_term_loudness_ungated() {
let mut state = State::default();
for _ in 0..30 {
assert!(state.process(create_noise(9600, 0.1).into_iter()).is_ok());
}
for _ in 0..30 {
assert!(state.process(create_noise(9600, 0.9).into_iter()).is_ok());
}
let il = state.integrated_loudness(GatingType::None).unwrap();
let stl = state.short_term_loudness(GatingType::None).unwrap();
assert_gt!(stl, il);
}
#[test]
fn momentary_loudness_ungated() {
let mut state = State::default();
for _ in 0..30 {
assert!(state.process(create_noise(9600, 0.1).into_iter()).is_ok());
}
for _ in 0..30 {
assert!(state.process(create_noise(9600, 0.5).into_iter()).is_ok());
}
for _ in 0..4 {
assert!(state.process(create_noise(9600, 0.9).into_iter()).is_ok());
}
let il = state.integrated_loudness(GatingType::None).unwrap();
let stl = state.short_term_loudness(GatingType::None).unwrap();
let ml = state.momentary_loudness(GatingType::None).unwrap();
assert_lt!(il, stl);
assert_lt!(stl, ml);
}
#[test]
fn everything_below_threshold() {
let mut state = State::default();
for _ in 0..30 {
assert_eq!(
state
.process(create_noise(9600, 0.0001).into_iter())
.is_ok(),
true
);
}
assert_eq!(
state.integrated_loudness(GatingType::Absolute).is_err(),
true
);
}
#[test]
fn streaming_mode_integrated_loudness() {
let mut state = State::new(48000., 1, Mode::Streaming);
for _ in 0..30 {
assert_eq!(
state.process(create_noise(4800, 0.5).into_iter()).is_ok(),
true
);
}
let il = state.integrated_loudness(GatingType::Absolute).unwrap();
assert_close_enough!(il, -13.6, 0.1);
}
#[test]
fn streaming_mode_no_relative_threshold() {
let mut state = State::new(48000., 1, Mode::Streaming);
for _ in 0..30 {
assert_eq!(
state.process(create_noise(4800, 0.5).into_iter()).is_ok(),
true
);
}
assert_eq!(
state.integrated_loudness(GatingType::Relative).is_err(),
true
);
}
#[test]
fn streaming_mode_short_term_loudness_ungated() {
let mut state = State::new(48000., 2, Mode::Streaming);
for _ in 0..30 {
assert!(state.process(create_noise(9600, 0.1).into_iter()).is_ok());
}
for _ in 0..30 {
assert!(state.process(create_noise(9600, 0.9).into_iter()).is_ok());
}
let il = state.integrated_loudness(GatingType::None).unwrap();
let stl = state.short_term_loudness(GatingType::None).unwrap();
assert_gt!(stl, il);
}
#[test]
fn streaming_mode_momentary_loudness_ungated() {
let mut state = State::new(48000., 2, Mode::Streaming);
for _ in 0..30 {
assert!(state.process(create_noise(9600, 0.1).into_iter()).is_ok());
}
for _ in 0..30 {
assert!(state.process(create_noise(9600, 0.5).into_iter()).is_ok());
}
for _ in 0..4 {
assert!(state.process(create_noise(9600, 0.9).into_iter()).is_ok());
}
let il = state.integrated_loudness(GatingType::None).unwrap();
let stl = state.short_term_loudness(GatingType::None).unwrap();
let ml = state.momentary_loudness(GatingType::None).unwrap();
assert_lt!(il, stl);
assert_lt!(stl, ml);
}
#[test]
fn sine() {
let mut state = State::new(48000., 1, Mode::Normal);
assert!(state.process(tone_1k().into_iter().take(4800)).is_ok());
let loudness = state.integrated_loudness(GatingType::Absolute).unwrap();
assert_close_enough!(loudness, -3.01, 0.01);
}
#[test]
fn channel_loudness() {
assert_eq!(
calculate_loudness(vec![1., 1., 1.].into_iter(), Channel::Left),
1.
);
assert_eq!(
calculate_loudness(vec![1., 1., 1.].into_iter(), Channel::Right),
1.
);
assert_eq!(
calculate_loudness(vec![1., 1., 1.].into_iter(), Channel::Centre),
1.
);
assert_eq!(
calculate_loudness(vec![1., 1., 1.].into_iter(), Channel::LeftSurround),
1.41
);
assert_eq!(
calculate_loudness(vec![1., 1., 1.].into_iter(), Channel::RightSurround),
1.41
);
}
#[test]
fn deinterleave_works() {
assert_eq!(
deinterleave(vec![0., 1., 0., 1.].into_iter(), 2),
vec![[0., 0.,], [1., 1.,]]
);
}
#[test]
fn deinterleave_and_filter_works() {
let (mut f1l, mut f2l) = get_filters();
let (mut f1r, mut f2r) = get_filters();
let left_result: Vec<f64> = vec![0., 1., 1., 1., 1., 1.]
.into_iter()
.map(|s| f2l.run(f1l.run(s)))
.collect();
let right_result: Vec<f64> = vec![0., 2., 2., 2., 2., 2.]
.into_iter()
.map(|s| f2r.run(f1r.run(s)))
.collect();
let samples: Vec<f64> = vec![0., 0., 1., 2., 1., 2., 1., 2., 1., 2., 1., 2.];
let result = deinterleave_and_filter(samples.into_iter(), 2);
assert_eq!(result.len(), 2);
assert_eq!(result[0], left_result);
assert_eq!(result[1], right_result);
}
#[test]
fn filters_work() {
let mut samples = vec![0f64];
samples.append(&mut vec![1f64; 19]);
let (mut f1, mut f2) = get_filters();
let result: Vec<f64> = samples.into_iter().map(|s| f2.run(f1.run(s))).collect();
assert_eq!(result[0], 0.);
assert_gt!(result[1], result[0]);
for i in 2..20 {
assert_lt!(result[i], result[i - 1]);
}
}
#[test]
fn loudness_interleaved_mono() {
let result = calculate_loudness_interleaved(create_noise(48000, 0.5).into_iter(), 1);
assert_ne!(result, 0.);
}
#[test]
fn loudness_interleaved_stereo() {
let result = calculate_loudness_interleaved(create_noise(48000 * 2, 0.5).into_iter(), 2);
assert_ne!(result, 0.);
}
#[test]
fn loudness_interleaved_5_1() {
let result = calculate_loudness_interleaved(create_noise(48000 * 6, 0.5).into_iter(), 6);
assert_ne!(result, 0.);
}
}