Skip to content

Commit

Permalink
Implement buffer-less true-peak scanning
Browse files Browse the repository at this point in the history
- Split interp::Frame into utils::FrameAcc based on dasp::Frame and
  utils::Samples::foreach_frame
- Push incoming frame:s directly onto the interpolator, one at a time, and
  check sample-max on resulting frames immediately. This removes the need
  for input and output-buffering.
  • Loading branch information
rawler committed Nov 4, 2020
1 parent 7969ce4 commit 4de11d5
Show file tree
Hide file tree
Showing 3 changed files with 221 additions and 314 deletions.
250 changes: 18 additions & 232 deletions src/interp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

trait Interpolator: std::fmt::Debug {
pub(crate) trait Interpolator: std::fmt::Debug {
fn process(&mut self, src: &[f32], dst: &mut [f32]);
fn reset(&mut self);
fn get_factor(&self) -> usize;
Expand All @@ -29,21 +29,8 @@ trait Interpolator: std::fmt::Debug {
pub struct Interp(Box<dyn Interpolator>);

impl Interp {
pub fn new(taps: usize, factor: usize, channels: u32) -> Self {
let imp: Box<dyn Interpolator> = match (taps, factor, channels) {
(49, 2, 1) => Box::new(specialized::Interp2F::<[f32; 1]>::new()),
(49, 2, 2) => Box::new(specialized::Interp2F::<[f32; 2]>::new()),
(49, 2, 4) => Box::new(specialized::Interp2F::<[f32; 4]>::new()),
(49, 2, 6) => Box::new(specialized::Interp2F::<[f32; 6]>::new()),
(49, 2, 8) => Box::new(specialized::Interp2F::<[f32; 8]>::new()),
(49, 4, 1) => Box::new(specialized::Interp4F::<[f32; 1]>::new()),
(49, 4, 2) => Box::new(specialized::Interp4F::<[f32; 2]>::new()),
(49, 4, 4) => Box::new(specialized::Interp4F::<[f32; 4]>::new()),
(49, 4, 6) => Box::new(specialized::Interp4F::<[f32; 6]>::new()),
(49, 4, 8) => Box::new(specialized::Interp4F::<[f32; 8]>::new()),
(taps, factor, channels) => Box::new(generic::Interp::new(taps, factor, channels)),
};
Self(imp)
pub fn new(_taps: usize, _factor: usize, _channels: u32) -> Self {
unimplemented!()
}

pub fn process(&mut self, src: &[f32], dst: &mut [f32]) {
Expand All @@ -59,7 +46,7 @@ impl Interp {
}
}

mod generic {
pub mod generic {
use smallvec::SmallVec;
use std::f64;
/// Data structure for polyphase FIR interpolator
Expand Down Expand Up @@ -209,161 +196,8 @@ mod generic {
}
}

/// A trait to be generic over number of channels in a of frame
///
/// TODO: Might want to use dasp-frame instead here, but needs
/// coordination with `Samples` trait
trait Frame: Sized + Copy {
const CHANNELS: usize;

fn scale_add(&mut self, other: &Self, coeff: f32);
fn from_planar(slice: &[f32], stride: usize) -> Self;
}

type MonoFrame32 = [f32; 1];
type StereoFrame32 = [f32; 2];
type QuadFrame32 = [f32; 4];
type SurroundFrame32 = [f32; 6];
type Surround8Frame32 = [f32; 8];

impl Frame for MonoFrame32 {
const CHANNELS: usize = 1;

#[inline(always)]
fn scale_add(&mut self, other: &Self, coeff: f32) {
for i in 0..Self::CHANNELS {
#[cfg(feature = "precision-true-peak")]
{
self[i] = other[i].mul_add(coeff, self[i]);
}
#[cfg(not(feature = "precision-true-peak"))]
{
self[i] += other[i] * coeff;
}
}
}

#[inline(always)]
fn from_planar(slice: &[f32], _stride: usize) -> Self {
[slice[0]]
}
}

impl Frame for StereoFrame32 {
const CHANNELS: usize = 2;

#[inline(always)]
fn scale_add(&mut self, other: &Self, coeff: f32) {
for i in 0..Self::CHANNELS {
#[cfg(feature = "precision-true-peak")]
{
self[i] = other[i].mul_add(coeff, self[i]);
}
#[cfg(not(feature = "precision-true-peak"))]
{
self[i] += other[i] * coeff;
}
}
}

#[inline(always)]
fn from_planar(slice: &[f32], stride: usize) -> Self {
[slice[0], slice[stride]]
}
}

impl Frame for QuadFrame32 {
const CHANNELS: usize = 4;

#[inline(always)]
fn scale_add(&mut self, other: &Self, coeff: f32) {
for i in 0..Self::CHANNELS {
#[cfg(feature = "precision-true-peak")]
{
self[i] = other[i].mul_add(coeff, self[i]);
}
#[cfg(not(feature = "precision-true-peak"))]
{
self[i] += other[i] * coeff;
}
}
}

#[inline(always)]
fn from_planar(slice: &[f32], stride: usize) -> Self {
[
slice[0],
slice[stride],
slice[2 * stride],
slice[3 * stride],
]
}
}

impl Frame for SurroundFrame32 {
const CHANNELS: usize = 6;

#[inline(always)]
fn scale_add(&mut self, other: &Self, coeff: f32) {
for i in 0..Self::CHANNELS {
#[cfg(feature = "precision-true-peak")]
{
self[i] = other[i].mul_add(coeff, self[i]);
}
#[cfg(not(feature = "precision-true-peak"))]
{
self[i] += other[i] * coeff;
}
}
}

#[inline(always)]
fn from_planar(slice: &[f32], stride: usize) -> Self {
[
slice[0],
slice[stride],
slice[2 * stride],
slice[3 * stride],
slice[4 * stride],
slice[5 * stride],
]
}
}

impl Frame for Surround8Frame32 {
const CHANNELS: usize = 8;

#[inline(always)]
fn scale_add(&mut self, other: &Self, coeff: f32) {
for i in 0..Self::CHANNELS {
#[cfg(feature = "precision-true-peak")]
{
self[i] = other[i].mul_add(coeff, self[i]);
}
#[cfg(not(feature = "precision-true-peak"))]
{
self[i] += other[i] * coeff;
}
}
}

#[inline(always)]
fn from_planar(slice: &[f32], stride: usize) -> Self {
[
slice[0],
slice[stride],
slice[2 * stride],
slice[3 * stride],
slice[4 * stride],
slice[5 * stride],
slice[6 * stride],
slice[7 * stride],
]
}
}

mod specialized {
use super::Frame;
pub mod specialized {
use crate::utils::FrameAccumulator;
use std::f64::consts::PI;

const ALMOST_ZERO: f64 = 0.000001;
Expand All @@ -374,18 +208,18 @@ mod specialized {
const FACTOR2: usize = 2;
const FACTOR2_INPUT_LENGTH: usize = TAPS / FACTOR2;

#[derive(Debug)]
pub(super) struct Interp4F<F: Frame> {
#[derive(Debug, Clone)]
pub(crate) struct Interp4F<F: FrameAccumulator> {
filter: [[f32; FACTOR4]; FACTOR4_INPUT_LENGTH],
buffer: [F; FACTOR4_INPUT_LENGTH],
buffer_pos: usize,
}

impl<F> Interp4F<F>
where
F: Frame + Default,
F: FrameAccumulator + Default,
{
pub(super) fn new() -> Self {
pub(crate) fn new() -> Self {
let mut filter: [[_; FACTOR4]; FACTOR4_INPUT_LENGTH] = Default::default();
for (j, coeff) in filter
.iter_mut()
Expand Down Expand Up @@ -415,7 +249,7 @@ mod specialized {
}
}

pub(super) fn push(&mut self, frame: &F) -> [F; FACTOR4] {
pub(crate) fn push(&mut self, frame: &F) -> [F; FACTOR4] {
// Write in Frames in reverse, to enable forward-scanning with filter
self.buffer_pos = (self.buffer_pos + self.buffer.len() - 1) % self.buffer.len();
self.buffer[self.buffer_pos] = *frame;
Expand All @@ -441,48 +275,24 @@ mod specialized {

output
}
}

impl<F> super::Interpolator for Interp4F<F>
where
F: Frame + std::fmt::Debug + Default + AsRef<[f32]>,
{
fn process(&mut self, src: &[f32], dst: &mut [f32]) {
assert_eq!(0, src.len() % F::CHANNELS);
assert_eq!(src.len() * FACTOR4, dst.len());
let frames = src.len() / F::CHANNELS;

for i in 0..frames {
let res = self.push(&F::from_planar(&src[i..], frames));
for c in 0..F::CHANNELS {
for (f, frame) in res.iter().enumerate() {
dst[c * frames * FACTOR4 + i * FACTOR4 + f] = frame.as_ref()[c];
}
}
}
}

fn reset(&mut self) {
pub(crate) fn reset(&mut self) {
self.buffer = Default::default();
}

fn get_factor(&self) -> usize {
4
}
}

#[derive(Debug)]
pub(super) struct Interp2F<F: Frame> {
#[derive(Debug, Clone)]
pub(crate) struct Interp2F<F: FrameAccumulator> {
filter: [[f32; FACTOR2]; FACTOR2_INPUT_LENGTH],
buffer: [F; FACTOR2_INPUT_LENGTH],
buffer_pos: usize,
}

impl<F> Interp2F<F>
where
F: Frame + Default,
F: FrameAccumulator + Default,
{
pub(super) fn new() -> Self {
pub(crate) fn new() -> Self {
let mut filter: [[_; FACTOR2]; FACTOR2_INPUT_LENGTH] = Default::default();
for (j, coeff) in filter
.iter_mut()
Expand Down Expand Up @@ -512,7 +322,7 @@ mod specialized {
}
}

pub(super) fn push(&mut self, frame: &F) -> [F; FACTOR2] {
pub(crate) fn push(&mut self, frame: &F) -> [F; FACTOR2] {
// Write in Frames in reverse, to enable forward-scanning with filter
self.buffer_pos = (self.buffer_pos + self.buffer.len() - 1) % self.buffer.len();
self.buffer[self.buffer_pos] = *frame;
Expand All @@ -538,34 +348,10 @@ mod specialized {

output
}
}

impl<F> super::Interpolator for Interp2F<F>
where
F: Frame + std::fmt::Debug + Default + AsRef<[f32]>,
{
fn process(&mut self, src: &[f32], dst: &mut [f32]) {
assert_eq!(0, src.len() % F::CHANNELS);
assert_eq!(src.len() * FACTOR2, dst.len());
let frames = src.len() / F::CHANNELS;

for i in 0..frames {
let res = self.push(&F::from_planar(&src[i..], frames));
for c in 0..F::CHANNELS {
for (f, frame) in res.iter().enumerate() {
dst[c * frames * FACTOR2 + i * FACTOR2 + f] = frame.as_ref()[c];
}
}
}
}

fn reset(&mut self) {
pub(crate) fn reset(&mut self) {
self.buffer = Default::default();
}

fn get_factor(&self) -> usize {
2
}
}
}

Expand Down
Loading

0 comments on commit 4de11d5

Please sign in to comment.