diff --git a/benches/ebur128.rs b/benches/ebur128.rs index 2205c98..832fbd1 100644 --- a/benches/ebur128.rs +++ b/benches/ebur128.rs @@ -17,7 +17,9 @@ fn get_results_c(ebu: &EbuR128C, mode: ModeC) { if mode.contains(ModeC::S) { black_box(ebu.loudness_shortterm().unwrap()); } - black_box(ebu.loudness_window(1).unwrap()); + if mode.intersects(ModeC::M | ModeC::S | ModeC::I | ModeC::LRA) { + black_box(ebu.loudness_window(1).unwrap()); + } if mode.contains(ModeC::LRA) { black_box(ebu.loudness_range().unwrap()); @@ -52,7 +54,9 @@ fn get_results(ebu: &EbuR128, mode: Mode) { if mode.contains(Mode::S) { black_box(ebu.loudness_shortterm().unwrap()); } - black_box(ebu.loudness_window(1).unwrap()); + if mode.intersects(Mode::M | Mode::S | Mode::I | Mode::LRA) { + black_box(ebu.loudness_window(1).unwrap()); + } if mode.contains(Mode::LRA) { black_box(ebu.loudness_range().unwrap()); diff --git a/benches/filter.rs b/benches/filter.rs index d6bb78e..ee120a5 100644 --- a/benches/filter.rs +++ b/benches/filter.rs @@ -30,6 +30,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { black_box(2), black_box(false), black_box(false), + black_box(true), ); drop(black_box(f)); }) @@ -79,7 +80,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { } { - let mut f = filter::Filter::new(48_000, 2, *sample_peak, false); + let mut f = filter::Filter::new(48_000, 2, *sample_peak, false, true); group.bench_function("Rust/Interleaved", |b| { b.iter(|| { f.process( @@ -91,7 +92,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { }) }); - let mut f = filter::Filter::new(48_000, 2, *sample_peak, false); + let mut f = filter::Filter::new(48_000, 2, *sample_peak, false, true); group.bench_function("Rust/Planar", |b| { b.iter(|| { f.process( @@ -143,7 +144,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { } { - let mut f = filter::Filter::new(48_000, 2, *sample_peak, false); + let mut f = filter::Filter::new(48_000, 2, *sample_peak, false, true); group.bench_function("Rust/Interleaved", |b| { b.iter(|| { f.process( @@ -155,7 +156,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { }) }); - let mut f = filter::Filter::new(48_000, 2, *sample_peak, false); + let mut f = filter::Filter::new(48_000, 2, *sample_peak, false, true); group.bench_function("Rust/Planar", |b| { b.iter(|| { f.process( @@ -207,7 +208,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { } { - let mut f = filter::Filter::new(48_000, 2, *sample_peak, false); + let mut f = filter::Filter::new(48_000, 2, *sample_peak, false, true); group.bench_function("Rust/Interleaved", |b| { b.iter(|| { f.process( @@ -219,7 +220,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { }) }); - let mut f = filter::Filter::new(48_000, 2, *sample_peak, false); + let mut f = filter::Filter::new(48_000, 2, *sample_peak, false, true); group.bench_function("Rust/Planar", |b| { b.iter(|| { f.process( @@ -271,7 +272,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { } { - let mut f = filter::Filter::new(48_000, 2, *sample_peak, false); + let mut f = filter::Filter::new(48_000, 2, *sample_peak, false, true); group.bench_function("Rust/Interleaved", |b| { b.iter(|| { f.process( @@ -283,7 +284,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { }) }); - let mut f = filter::Filter::new(48_000, 2, *sample_peak, false); + let mut f = filter::Filter::new(48_000, 2, *sample_peak, false, true); group.bench_function("Rust/Planar", |b| { b.iter(|| { f.process( diff --git a/src/ebur128.rs b/src/ebur128.rs index ff7b88c..fd6fcd6 100644 --- a/src/ebur128.rs +++ b/src/ebur128.rs @@ -66,9 +66,9 @@ bitflags! { /// can call [`EbuR128::loudness_range`](struct.EbuR128.html#method.loudness_range) const LRA = 0b00001000 | Mode::S.bits; /// can call [`EbuR128::sample_peak`](struct.EbuR128.html#method.sample_peak) - const SAMPLE_PEAK = 0b00010000 | Mode::M.bits; + const SAMPLE_PEAK = 0b00010000; /// can call [`EbuR128::true_peak`](struct.EbuR128.html#method.true_peak) - const TRUE_PEAK = 0b00110001; + const TRUE_PEAK = 0b00110000; /// uses histogram algorithm to calculate loudness const HISTOGRAM = 0b01000000; } @@ -300,15 +300,22 @@ impl EbuR128 { let history = usize::MAX; let samples_in_100ms = (rate as usize + 5) / 10; + let calculate_loudness = mode.intersects(Mode::M | Mode::S | Mode::I | Mode::LRA); let window = if mode.contains(Mode::S) { 3000 } else if mode.contains(Mode::M) { 400 + } else if mode.intersects(Mode::SAMPLE_PEAK | Mode::TRUE_PEAK) { + 0 } else { return Err(Error::InvalidMode); }; - let audio_data = Self::allocate_audio_data(channels, rate, window)?; + let audio_data = if window > 0 { + Self::allocate_audio_data(channels, rate, window)? + } else { + Vec::new().into_boxed_slice() + }; // start at the beginning of the buffer let audio_data_index = 0; @@ -324,6 +331,7 @@ impl EbuR128 { channels, mode.contains(Mode::SAMPLE_PEAK), mode.contains(Mode::TRUE_PEAK), + calculate_loudness, ); let channel_map = default_channel_map(channels); @@ -448,7 +456,11 @@ impl EbuR128 { return Ok(()); } - self.audio_data = Self::allocate_audio_data(channels, rate, self.window)?; + self.audio_data = if self.window > 0 { + Self::allocate_audio_data(channels, rate, self.window)? + } else { + Vec::new().into_boxed_slice() + }; if self.channels != channels { self.channels = channels; @@ -462,11 +474,15 @@ impl EbuR128 { self.samples_in_100ms = (rate as usize + 5) / 10; } + let calculate_loudness = self + .mode + .intersects(Mode::M | Mode::S | Mode::I | Mode::LRA); self.filter = crate::filter::Filter::new( rate, channels, self.mode.contains(Mode::SAMPLE_PEAK), self.mode.contains(Mode::TRUE_PEAK), + calculate_loudness, ); // the first block needs 400ms of audio data diff --git a/src/filter.rs b/src/filter.rs index 86b9270..ecca4d9 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -34,6 +34,9 @@ pub struct Filter { /// One filter state per channel. filter_state: Box<[[f64; 5]]>, + /// Whether to run the K-weighting biquad and write filtered samples to `dest`. + /// Set false for peak-only modes where no loudness energy is ever read. + calculate_loudness: bool, /// Whether to measure sample peak. calculate_sample_peak: bool, /// Previously measured sample peak. @@ -115,6 +118,7 @@ impl Filter { channels: u32, calculate_sample_peak: bool, calculate_true_peak: bool, + calculate_loudness: bool, ) -> Self { assert!(rate > 0); assert!(channels > 0); @@ -132,6 +136,7 @@ impl Filter { b, a, filter_state: vec![[0.0; 5]; channels as usize].into_boxed_slice(), + calculate_loudness, calculate_sample_peak, sample_peak: vec![0.0; channels as usize].into_boxed_slice(), tp, @@ -169,7 +174,6 @@ impl Filter { dest_index: usize, channel_map: &[crate::ebur128::Channel], ) { - assert!(dest.len() % self.channels as usize == 0); assert!(channel_map.len() == self.channels as usize); assert!(src.channels() == self.channels as usize); assert!(self.filter_state.len() == self.channels as usize); @@ -197,48 +201,52 @@ impl Filter { } } - let dest_stride = dest.len() / self.channels as usize; - assert!(dest_index + src.frames() <= dest_stride); + if self.calculate_loudness { + assert!(dest.len() % self.channels as usize == 0); + let dest_stride = dest.len() / self.channels as usize; + assert!(dest_index + src.frames() <= dest_stride); - for (c, (channel_map, dest)) in - Iterator::zip(channel_map.iter(), dest.chunks_exact_mut(dest_stride)).enumerate() - { - if *channel_map == crate::ebur128::Channel::Unused { - continue; - } + for (c, (channel_map, dest)) in + Iterator::zip(channel_map.iter(), dest.chunks_exact_mut(dest_stride)) + .enumerate() + { + if *channel_map == crate::ebur128::Channel::Unused { + continue; + } + + assert!(c < src.channels()); + + let Filter { + ref mut filter_state, + ref a, + ref b, + .. + } = *self; + let filter_state = &mut filter_state[c]; + + src.foreach_sample_zipped(c, dest[dest_index..].iter_mut(), |src, dest| { + filter_state[0] = (*src).to_sample::() + - a[1] * filter_state[1] + - a[2] * filter_state[2] + - a[3] * filter_state[3] + - a[4] * filter_state[4]; + *dest = b[0] * filter_state[0] + + b[1] * filter_state[1] + + b[2] * filter_state[2] + + b[3] * filter_state[3] + + b[4] * filter_state[4]; + + filter_state[4] = filter_state[3]; + filter_state[3] = filter_state[2]; + filter_state[2] = filter_state[1]; + filter_state[1] = filter_state[0]; + }); - assert!(c < src.channels()); - - let Filter { - ref mut filter_state, - ref a, - ref b, - .. - } = *self; - let filter_state = &mut filter_state[c]; - - src.foreach_sample_zipped(c, dest[dest_index..].iter_mut(), |src, dest| { - filter_state[0] = (*src).to_sample::() - - a[1] * filter_state[1] - - a[2] * filter_state[2] - - a[3] * filter_state[3] - - a[4] * filter_state[4]; - *dest = b[0] * filter_state[0] - + b[1] * filter_state[1] - + b[2] * filter_state[2] - + b[3] * filter_state[3] - + b[4] * filter_state[4]; - - filter_state[4] = filter_state[3]; - filter_state[3] = filter_state[2]; - filter_state[2] = filter_state[1]; - filter_state[1] = filter_state[0]; - }); - - if ftz.is_none() { - for v in filter_state { - if v.abs() < f64::EPSILON { - *v = 0.0; + if ftz.is_none() { + for v in filter_state { + if v.abs() < f64::EPSILON { + *v = 0.0; + } } } } @@ -261,37 +269,39 @@ impl Filter { assert!(self.filter_state.len() == self.channels as usize); ftz::with_ftz(|ftz| { - for (c, channel_map) in channel_map.iter().enumerate() { - if *channel_map == crate::ebur128::Channel::Unused { - continue; - } + if self.calculate_loudness { + for (c, channel_map) in channel_map.iter().enumerate() { + if *channel_map == crate::ebur128::Channel::Unused { + continue; + } + + assert!(c < src.channels()); + + let Filter { + ref mut filter_state, + ref a, + .. + } = *self; + let filter_state = &mut filter_state[c]; + + src.foreach_sample(c, |src| { + filter_state[0] = (*src).to_sample::() + - a[1] * filter_state[1] + - a[2] * filter_state[2] + - a[3] * filter_state[3] + - a[4] * filter_state[4]; + + filter_state[4] = filter_state[3]; + filter_state[3] = filter_state[2]; + filter_state[2] = filter_state[1]; + filter_state[1] = filter_state[0]; + }); - assert!(c < src.channels()); - - let Filter { - ref mut filter_state, - ref a, - .. - } = *self; - let filter_state = &mut filter_state[c]; - - src.foreach_sample(c, |src| { - filter_state[0] = (*src).to_sample::() - - a[1] * filter_state[1] - - a[2] * filter_state[2] - - a[3] * filter_state[3] - - a[4] * filter_state[4]; - - filter_state[4] = filter_state[3]; - filter_state[3] = filter_state[2]; - filter_state[2] = filter_state[1]; - filter_state[1] = filter_state[0]; - }); - - if ftz.is_none() { - for v in filter_state { - if v.abs() < f64::EPSILON { - *v = 0.0; + if ftz.is_none() { + for v in filter_state { + if v.abs() < f64::EPSILON { + *v = 0.0; + } } } } @@ -559,6 +569,7 @@ mod tests { signal.channels, calculate_sample_peak, calculate_true_peak, + true, ); let mut data_out_tmp = vec![0.0f64; frames * signal.channels as usize]; @@ -647,6 +658,7 @@ mod tests { signal.channels, calculate_sample_peak, calculate_true_peak, + true, ); let mut data_out_tmp = vec![0.0f64; frames * signal.channels as usize]; @@ -735,6 +747,7 @@ mod tests { signal.channels, calculate_sample_peak, calculate_true_peak, + true, ); let mut data_out_tmp = vec![0.0f64; frames * signal.channels as usize]; @@ -823,6 +836,7 @@ mod tests { signal.channels, calculate_sample_peak, calculate_true_peak, + true, ); let mut data_out_tmp = vec![0.0f64; frames * signal.channels as usize];