Skip to content

Commit 4de11d5

Browse files
committed
Implement buffer-less true-peak scanning
- 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.
1 parent 7969ce4 commit 4de11d5

File tree

3 files changed

+221
-314
lines changed

3 files changed

+221
-314
lines changed

src/interp.rs

Lines changed: 18 additions & 232 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2020
// THE SOFTWARE.
2121

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

3131
impl Interp {
32-
pub fn new(taps: usize, factor: usize, channels: u32) -> Self {
33-
let imp: Box<dyn Interpolator> = match (taps, factor, channels) {
34-
(49, 2, 1) => Box::new(specialized::Interp2F::<[f32; 1]>::new()),
35-
(49, 2, 2) => Box::new(specialized::Interp2F::<[f32; 2]>::new()),
36-
(49, 2, 4) => Box::new(specialized::Interp2F::<[f32; 4]>::new()),
37-
(49, 2, 6) => Box::new(specialized::Interp2F::<[f32; 6]>::new()),
38-
(49, 2, 8) => Box::new(specialized::Interp2F::<[f32; 8]>::new()),
39-
(49, 4, 1) => Box::new(specialized::Interp4F::<[f32; 1]>::new()),
40-
(49, 4, 2) => Box::new(specialized::Interp4F::<[f32; 2]>::new()),
41-
(49, 4, 4) => Box::new(specialized::Interp4F::<[f32; 4]>::new()),
42-
(49, 4, 6) => Box::new(specialized::Interp4F::<[f32; 6]>::new()),
43-
(49, 4, 8) => Box::new(specialized::Interp4F::<[f32; 8]>::new()),
44-
(taps, factor, channels) => Box::new(generic::Interp::new(taps, factor, channels)),
45-
};
46-
Self(imp)
32+
pub fn new(_taps: usize, _factor: usize, _channels: u32) -> Self {
33+
unimplemented!()
4734
}
4835

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

62-
mod generic {
49+
pub mod generic {
6350
use smallvec::SmallVec;
6451
use std::f64;
6552
/// Data structure for polyphase FIR interpolator
@@ -209,161 +196,8 @@ mod generic {
209196
}
210197
}
211198

212-
/// A trait to be generic over number of channels in a of frame
213-
///
214-
/// TODO: Might want to use dasp-frame instead here, but needs
215-
/// coordination with `Samples` trait
216-
trait Frame: Sized + Copy {
217-
const CHANNELS: usize;
218-
219-
fn scale_add(&mut self, other: &Self, coeff: f32);
220-
fn from_planar(slice: &[f32], stride: usize) -> Self;
221-
}
222-
223-
type MonoFrame32 = [f32; 1];
224-
type StereoFrame32 = [f32; 2];
225-
type QuadFrame32 = [f32; 4];
226-
type SurroundFrame32 = [f32; 6];
227-
type Surround8Frame32 = [f32; 8];
228-
229-
impl Frame for MonoFrame32 {
230-
const CHANNELS: usize = 1;
231-
232-
#[inline(always)]
233-
fn scale_add(&mut self, other: &Self, coeff: f32) {
234-
for i in 0..Self::CHANNELS {
235-
#[cfg(feature = "precision-true-peak")]
236-
{
237-
self[i] = other[i].mul_add(coeff, self[i]);
238-
}
239-
#[cfg(not(feature = "precision-true-peak"))]
240-
{
241-
self[i] += other[i] * coeff;
242-
}
243-
}
244-
}
245-
246-
#[inline(always)]
247-
fn from_planar(slice: &[f32], _stride: usize) -> Self {
248-
[slice[0]]
249-
}
250-
}
251-
252-
impl Frame for StereoFrame32 {
253-
const CHANNELS: usize = 2;
254-
255-
#[inline(always)]
256-
fn scale_add(&mut self, other: &Self, coeff: f32) {
257-
for i in 0..Self::CHANNELS {
258-
#[cfg(feature = "precision-true-peak")]
259-
{
260-
self[i] = other[i].mul_add(coeff, self[i]);
261-
}
262-
#[cfg(not(feature = "precision-true-peak"))]
263-
{
264-
self[i] += other[i] * coeff;
265-
}
266-
}
267-
}
268-
269-
#[inline(always)]
270-
fn from_planar(slice: &[f32], stride: usize) -> Self {
271-
[slice[0], slice[stride]]
272-
}
273-
}
274-
275-
impl Frame for QuadFrame32 {
276-
const CHANNELS: usize = 4;
277-
278-
#[inline(always)]
279-
fn scale_add(&mut self, other: &Self, coeff: f32) {
280-
for i in 0..Self::CHANNELS {
281-
#[cfg(feature = "precision-true-peak")]
282-
{
283-
self[i] = other[i].mul_add(coeff, self[i]);
284-
}
285-
#[cfg(not(feature = "precision-true-peak"))]
286-
{
287-
self[i] += other[i] * coeff;
288-
}
289-
}
290-
}
291-
292-
#[inline(always)]
293-
fn from_planar(slice: &[f32], stride: usize) -> Self {
294-
[
295-
slice[0],
296-
slice[stride],
297-
slice[2 * stride],
298-
slice[3 * stride],
299-
]
300-
}
301-
}
302-
303-
impl Frame for SurroundFrame32 {
304-
const CHANNELS: usize = 6;
305-
306-
#[inline(always)]
307-
fn scale_add(&mut self, other: &Self, coeff: f32) {
308-
for i in 0..Self::CHANNELS {
309-
#[cfg(feature = "precision-true-peak")]
310-
{
311-
self[i] = other[i].mul_add(coeff, self[i]);
312-
}
313-
#[cfg(not(feature = "precision-true-peak"))]
314-
{
315-
self[i] += other[i] * coeff;
316-
}
317-
}
318-
}
319-
320-
#[inline(always)]
321-
fn from_planar(slice: &[f32], stride: usize) -> Self {
322-
[
323-
slice[0],
324-
slice[stride],
325-
slice[2 * stride],
326-
slice[3 * stride],
327-
slice[4 * stride],
328-
slice[5 * stride],
329-
]
330-
}
331-
}
332-
333-
impl Frame for Surround8Frame32 {
334-
const CHANNELS: usize = 8;
335-
336-
#[inline(always)]
337-
fn scale_add(&mut self, other: &Self, coeff: f32) {
338-
for i in 0..Self::CHANNELS {
339-
#[cfg(feature = "precision-true-peak")]
340-
{
341-
self[i] = other[i].mul_add(coeff, self[i]);
342-
}
343-
#[cfg(not(feature = "precision-true-peak"))]
344-
{
345-
self[i] += other[i] * coeff;
346-
}
347-
}
348-
}
349-
350-
#[inline(always)]
351-
fn from_planar(slice: &[f32], stride: usize) -> Self {
352-
[
353-
slice[0],
354-
slice[stride],
355-
slice[2 * stride],
356-
slice[3 * stride],
357-
slice[4 * stride],
358-
slice[5 * stride],
359-
slice[6 * stride],
360-
slice[7 * stride],
361-
]
362-
}
363-
}
364-
365-
mod specialized {
366-
use super::Frame;
199+
pub mod specialized {
200+
use crate::utils::FrameAccumulator;
367201
use std::f64::consts::PI;
368202

369203
const ALMOST_ZERO: f64 = 0.000001;
@@ -374,18 +208,18 @@ mod specialized {
374208
const FACTOR2: usize = 2;
375209
const FACTOR2_INPUT_LENGTH: usize = TAPS / FACTOR2;
376210

377-
#[derive(Debug)]
378-
pub(super) struct Interp4F<F: Frame> {
211+
#[derive(Debug, Clone)]
212+
pub(crate) struct Interp4F<F: FrameAccumulator> {
379213
filter: [[f32; FACTOR4]; FACTOR4_INPUT_LENGTH],
380214
buffer: [F; FACTOR4_INPUT_LENGTH],
381215
buffer_pos: usize,
382216
}
383217

384218
impl<F> Interp4F<F>
385219
where
386-
F: Frame + Default,
220+
F: FrameAccumulator + Default,
387221
{
388-
pub(super) fn new() -> Self {
222+
pub(crate) fn new() -> Self {
389223
let mut filter: [[_; FACTOR4]; FACTOR4_INPUT_LENGTH] = Default::default();
390224
for (j, coeff) in filter
391225
.iter_mut()
@@ -415,7 +249,7 @@ mod specialized {
415249
}
416250
}
417251

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

442276
output
443277
}
444-
}
445278

446-
impl<F> super::Interpolator for Interp4F<F>
447-
where
448-
F: Frame + std::fmt::Debug + Default + AsRef<[f32]>,
449-
{
450-
fn process(&mut self, src: &[f32], dst: &mut [f32]) {
451-
assert_eq!(0, src.len() % F::CHANNELS);
452-
assert_eq!(src.len() * FACTOR4, dst.len());
453-
let frames = src.len() / F::CHANNELS;
454-
455-
for i in 0..frames {
456-
let res = self.push(&F::from_planar(&src[i..], frames));
457-
for c in 0..F::CHANNELS {
458-
for (f, frame) in res.iter().enumerate() {
459-
dst[c * frames * FACTOR4 + i * FACTOR4 + f] = frame.as_ref()[c];
460-
}
461-
}
462-
}
463-
}
464-
465-
fn reset(&mut self) {
279+
pub(crate) fn reset(&mut self) {
466280
self.buffer = Default::default();
467281
}
468-
469-
fn get_factor(&self) -> usize {
470-
4
471-
}
472282
}
473283

474-
#[derive(Debug)]
475-
pub(super) struct Interp2F<F: Frame> {
284+
#[derive(Debug, Clone)]
285+
pub(crate) struct Interp2F<F: FrameAccumulator> {
476286
filter: [[f32; FACTOR2]; FACTOR2_INPUT_LENGTH],
477287
buffer: [F; FACTOR2_INPUT_LENGTH],
478288
buffer_pos: usize,
479289
}
480290

481291
impl<F> Interp2F<F>
482292
where
483-
F: Frame + Default,
293+
F: FrameAccumulator + Default,
484294
{
485-
pub(super) fn new() -> Self {
295+
pub(crate) fn new() -> Self {
486296
let mut filter: [[_; FACTOR2]; FACTOR2_INPUT_LENGTH] = Default::default();
487297
for (j, coeff) in filter
488298
.iter_mut()
@@ -512,7 +322,7 @@ mod specialized {
512322
}
513323
}
514324

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

539349
output
540350
}
541-
}
542-
543-
impl<F> super::Interpolator for Interp2F<F>
544-
where
545-
F: Frame + std::fmt::Debug + Default + AsRef<[f32]>,
546-
{
547-
fn process(&mut self, src: &[f32], dst: &mut [f32]) {
548-
assert_eq!(0, src.len() % F::CHANNELS);
549-
assert_eq!(src.len() * FACTOR2, dst.len());
550-
let frames = src.len() / F::CHANNELS;
551-
552-
for i in 0..frames {
553-
let res = self.push(&F::from_planar(&src[i..], frames));
554-
for c in 0..F::CHANNELS {
555-
for (f, frame) in res.iter().enumerate() {
556-
dst[c * frames * FACTOR2 + i * FACTOR2 + f] = frame.as_ref()[c];
557-
}
558-
}
559-
}
560-
}
561351

562-
fn reset(&mut self) {
352+
pub(crate) fn reset(&mut self) {
563353
self.buffer = Default::default();
564354
}
565-
566-
fn get_factor(&self) -> usize {
567-
2
568-
}
569355
}
570356
}
571357

0 commit comments

Comments
 (0)