meadow-dsp-essentials 0.1.4

Liberally-licensed essential audio DSP library used in the Meadowlark DAW project
Documentation
#[cfg(not(feature = "std"))]
use num_traits::Float;

pub const DEFAULT_AMP_EPSILON: f32 = 0.00001;
pub const DEFAULT_DB_EPSILON: f32 = -100.0;

/// Returns the raw amplitude from the given decibel value.
///
/// This does not handle the case where `db = f32::NEG_INFINITY`.
#[inline]
pub fn db_to_amp(db: f32) -> f32 {
    10.0f32.powf(0.05 * db)
}

/// Returns the decibel value from the given raw amplitude.
///
/// This does not handle the case where `amp <= 0.0`.
#[inline]
pub fn amp_to_db(amp: f32) -> f32 {
    20.0 * amp.log10()
}

/// Returns the raw amplitude from the given decibel value.
///
/// If `db == f32::NEG_INFINITY`, then the returned value will be `0.0`.
#[inline]
pub fn db_to_amp_neg_inf(db: f32) -> f32 {
    if db == f32::NEG_INFINITY {
        0.0
    } else {
        10.0f32.powf(0.05 * db)
    }
}

/// Returns the decibel value from the given raw amplitude.
///
/// If `amp <= 0.0`, then the returned value will be `f32::NEG_INFINITY`.
#[inline]
pub fn amp_to_db_neg_inf(amp: f32) -> f32 {
    if amp <= 0.0 {
        f32::NEG_INFINITY
    } else {
        20.0 * amp.log10()
    }
}

/// Returns the raw amplitude from the given decibel value.
///
/// If `db == f32::NEG_INFINITY || db <= db_epsilon`, then `0.0` (silence) will be
/// returned.
#[inline]
pub fn db_to_amp_clamped(db: f32, db_epsilon: f32) -> f32 {
    if db == f32::NEG_INFINITY || db <= db_epsilon {
        0.0
    } else {
        db_to_amp_neg_inf(db)
    }
}

/// Returns the decibel value from the given raw amplitude.
///
/// If `amp <= amp_epsilon`, then `f32::NEG_INFINITY` (silence) will be returned.
#[inline]
pub fn amp_to_db_clamped(amp: f32, amp_epsilon: f32) -> f32 {
    if amp <= amp_epsilon {
        f32::NEG_INFINITY
    } else {
        amp_to_db_neg_inf(amp)
    }
}

/// Map the linear volume (where `0.0` means mute and `1.0` means unity
/// gain) to the corresponding raw amplitude value (not decibels) for use in
/// DSP. Values above `1.0` are allowed.
///
/// If the resulting amplitude is `<= amp_epsilon`, then `0.0` (silence) will be
/// returned.
///
/// This mapping is useful for volume sliders.
#[inline]
pub fn linear_volume_to_amp_clamped(linear_volume: f32, amp_epsilon: f32) -> f32 {
    let v = linear_volume * linear_volume;
    if v <= amp_epsilon { 0.0 } else { v }
}

/// Map the raw amplitude (where `0.0` means mute and `1.0` means unity
/// gain) to the corresponding linear volume.
///
/// If the amplitude is `<= amp_epsilon`, then `0.0` (silence) will be
/// returned.
///
/// This mapping is useful for volume sliders.
#[inline]
pub fn amp_to_linear_volume_clamped(amp: f32, amp_epsilon: f32) -> f32 {
    if amp <= amp_epsilon { 0.0 } else { amp.sqrt() }
}

/// Thoroughly checks if the given buffer contains silence (as in all samples
/// have an absolute amplitude less than or equal to `amp_epsilon`)
pub fn is_buffer_silent(buffer: &[f32], amp_epsilon: f32) -> bool {
    let mut silent = true;
    for &s in buffer.iter() {
        if s.abs() > amp_epsilon {
            silent = false;
            break;
        }
    }
    silent
}

/// Efficiently detects the maximum absolute peak value in a buffer of samples.
pub fn max_peak(data: &[f32]) -> f32 {
    const CHUNK_SIZE: usize = 8;

    // Processing in chunks like this breaks the dependency chain which allows
    // the compiler to properly autovectorize this loop.
    let mut tmp = [0.0; CHUNK_SIZE];
    let mut iter = data.chunks_exact(CHUNK_SIZE);
    for chunk in iter.by_ref() {
        for i in 0..CHUNK_SIZE {
            let abs = chunk[i].abs();
            if abs > tmp[i] {
                tmp[i] = abs;
            }
        }
    }

    let mut res = 0.0;
    for s in tmp {
        if s > res {
            res = s;
        }
    }

    for &s in iter.remainder() {
        let abs = s.abs();
        if abs > res {
            res = abs;
        }
    }

    res
}