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
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
/// Trait used for translating one sample type to another.
///
/// # Examples
///
/// ```rust
/// use rotary::Translate as _;
///
/// assert_eq!(i16::translate(-1.0f32), i16::MIN);
/// assert_eq!(i16::translate(0.0f32), 0);
///
/// assert_eq!(u16::translate(-1.0f32), u16::MIN);
/// assert_eq!(u16::translate(0.0f32), 32768);
/// ```
pub trait Translate<T> {
    /// Translate one kind of buffer to another.
    fn translate(value: T) -> Self;
}

macro_rules! identity {
    ($ty:ty) => {
        impl Translate<$ty> for $ty {
            fn translate(value: $ty) -> Self {
                value
            }
        }
    };
}

macro_rules! int_to_float {
    ($signed:ident, $unsigned:ident, $float:ident) => {
        impl Translate<$signed> for $float {
            #[inline]
            fn translate(value: $signed) -> Self {
                if value < 0 {
                    (value as $float / -($signed::MIN as $float))
                } else {
                    (value as $float / $signed::MAX as $float)
                }
            }
        }

        impl Translate<$float> for $signed {
            #[inline]
            fn translate(value: $float) -> Self {
                if value >= 0.0 {
                    (value * $signed::MAX as $float) as $signed
                } else {
                    (-value * $signed::MIN as $float) as $signed
                }
            }
        }

        impl Translate<$float> for $unsigned {
            #[inline]
            fn translate(value: $float) -> Self {
                let value = value.clamp(-1.0, 1.0);

                (((value + 1.0) * 0.5) * $unsigned::MAX as $float).round() as $unsigned
            }
        }

        impl Translate<$unsigned> for $float {
            #[inline]
            fn translate(value: $unsigned) -> Self {
                // Note: less conversion loss the closer we stay to 0.
                $float::translate(i16::translate(value))
            }
        }
    };
}

identity!(f32);
identity!(f64);
identity!(i16);
identity!(u16);

int_to_float!(i16, u16, f32);
int_to_float!(i16, u16, f64);

impl Translate<u16> for i16 {
    #[inline]
    fn translate(value: u16) -> Self {
        (value as i16).wrapping_sub(i16::MIN)
    }
}

impl Translate<i16> for u16 {
    #[inline]
    fn translate(value: i16) -> Self {
        value.wrapping_add(i16::MIN) as u16
    }
}

impl Translate<f32> for f64 {
    #[inline]
    fn translate(value: f32) -> Self {
        value as f64
    }
}

impl Translate<f64> for f32 {
    #[inline]
    fn translate(value: f64) -> Self {
        value as f32
    }
}