music_math/
scaling.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
use crate::binaryops::clip;
use num_traits::Float;

/// Map an input value from one range to another.
/// It performs clipping on the input value in the range of `from_min` and `from_max`.
/// Using generics, this function can map any type that implements the `Into` trait.
///
/// Note: If the denominator is zero, the function will return `to_min` to avoid division by zero.
#[inline]
#[must_use]
pub fn linlin<T: Float>(value: T, from_min: T, from_max: T, to_min: T, to_max: T) -> T {
    let denominator = from_max.sub(from_min);

    if denominator == T::zero() {
        return to_min;
    }

    let value = clip(value, from_min, from_max);
    let nominator = value.sub(from_min);

    nominator
        .mul(to_max.sub(to_min))
        .div(denominator)
        .add(to_min)
}

/// Map an input value from one range to another with an exponential curve. It performs clipping on
/// the input value in the range of `from_min` and `from_max`.
/// Using generics, this function can map any type that implements the `Float` trait.
///
/// Note: If the denominator is zero, the function will return `to_min` to avoid division by zero.
#[inline]
#[must_use]
pub fn linexp<T: Float>(
    value: T,
    from_min: T,
    from_max: T,
    to_min: T,
    to_max: T,
    exponent: T,
) -> T {
    let denominator = from_max.sub(from_min);
    if denominator == T::zero() {
        to_min
    } else {
        let value = clip(value, from_min, from_max);
        let value = value.sub(from_min).div(denominator).powf(exponent);

        value.mul(to_max.sub(to_min)).add(to_min)
    }
}

/// Convert a decibel value to amplitude.
/// # Panics
/// If a type conversion fails.
#[inline]
#[must_use]
pub fn dbamp<T: Float>(db: T) -> T {
    let twenty = T::from(20.0).expect("Could not convert 20.0 to T");
    let ten = T::from(10.0).expect("Could not convert 10.0 to T");
    let n = db.div(twenty);

    ten.powf(n)
}

/// Convert an amplitude value to decibels.
/// # Panics
/// If a type conversion fails.
#[inline]
#[must_use]
pub fn ampdb<T: Float>(amp: T) -> T {
    let twenty = T::from(20.0).expect("Could not convert 20.0 to T");
    twenty.mul(amp.log10())
}