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}