midix/
pitch_bend.rs

1use crate::prelude::*;
2
3/// The value of a pitch bend, represented as 14 bits.
4///
5/// A value of `0x0000` indicates full bend downwards.
6/// A value of `0x2000` indicates no bend.
7/// A value of `0x3FFF` indicates full bend upwards.
8///
9/// This value is available via [`PitchBend::value`]
10#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
11pub struct PitchBend {
12    lsb: DataByte,
13    msb: DataByte,
14}
15
16impl PitchBend {
17    /// Creates a new pitch bend given the
18    /// least significant and most significant bytes.
19    ///
20    /// Checks for byte correctness (leading 0 bit)
21    pub fn new(lsb: u8, msb: u8) -> Result<Self, std::io::Error> {
22        let lsb = DataByte::new(lsb)?;
23        let msb = DataByte::new(msb)?;
24        Ok(Self { lsb, msb })
25    }
26
27    /// Creates a new pitch bend given the
28    /// least significant and most significant bytes.
29    ///
30    /// Does not check for correctness
31    pub const fn new_unchecked(lsb: u8, msb: u8) -> Self {
32        Self {
33            lsb: DataByte::new_unchecked(lsb),
34            msb: DataByte::new_unchecked(msb),
35        }
36    }
37
38    /// Returns a reference to the pitch bend's least significant byte.
39    pub fn lsb(&self) -> u8 {
40        self.lsb.0
41    }
42
43    /// Returns a reference to the pitch bend's most significant byte.
44    pub fn msb(&self) -> u8 {
45        self.msb.0
46    }
47
48    /// Represents a pitch bend
49    pub fn value(&self) -> u16 {
50        let lsb = self.lsb.value();
51        let msb = self.msb.value();
52        let combined: u16 = ((msb as u16) << 8) | (lsb as u16);
53        combined
54    }
55
56    /// Represents a u16, lsb then msb, as a pitch bend
57    pub fn from_bits(rep: u16) -> Result<Self, std::io::Error> {
58        let lsb = (rep >> 8) as u8;
59        let msb = (rep & 0x00FF) as u8;
60        Self::new(lsb, msb)
61    }
62}
63
64impl PitchBend {
65    /// The minimum value of `0x0000`, indicating full bend downwards.
66    pub const MIN_BYTES: u16 = 0x0000;
67
68    /// The middle value of `0x2000`, indicating no bend.
69    pub const MID_BYTES: u16 = 0x2000;
70
71    /// The maximum value of `0x3FFF`, indicating full bend upwards.
72    pub const MAX_VALUE: u16 = 0x3FFF;
73
74    /// Create a `PitchBend` value from an int in the range `[-0x2000, 0x1FFF]`.
75    ///
76    /// Integers outside this range will be clamped.
77    #[inline]
78    pub fn from_int(int: i16) -> Self {
79        PitchBend::from_bits((int.clamp(-0x2000, 0x1FFF) + 0x2000) as u16).unwrap()
80    }
81
82    /// Create a `PitchBend` value from a number in the range `[-1.0, 1.0)`.
83    ///
84    /// Floats outside this range will be clamped.
85    #[inline]
86    pub fn from_f32(float: f32) -> Self {
87        PitchBend::from_int((float.clamp(-1.0, 1.0) * 0x2000 as f32) as i16)
88    }
89
90    /// Create a `PitchBend` value from a number in the range `[-1.0, 1.0)`.
91    ///
92    /// Floats outside this range will be clamped.
93    #[inline]
94    pub fn from_f64(float: f64) -> Self {
95        PitchBend::from_int((float.clamp(-1.0, 1.0) * 0x2000 as f64) as i16)
96    }
97
98    /// Returns an int in the range `[-0x2000, 0x1FFF]`.
99    ///
100    /// Do not use this when writing to a midi file.
101    #[inline]
102    pub fn as_int(self) -> i16 {
103        self.value() as i16 - 0x2000
104    }
105
106    /// Returns an `f32` in the range `[-1.0, 1.0)`.
107    #[inline]
108    pub fn as_f32(self) -> f32 {
109        self.as_int() as f32 * (1.0 / 0x2000 as f32)
110    }
111
112    /// Returns an `f64` in the range `[-1.0, 1.0)`.
113    #[inline]
114    pub fn as_f64(self) -> f64 {
115        self.as_int() as f64 * (1.0 / 0x2000 as f64)
116    }
117}