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
107
108
109
110
111
112
113
114
115
116
117
use crate::prelude::*;
/// The value of a pitch bend, represented as 14 bits.
///
/// A value of `0x0000` indicates full bend downwards.
/// A value of `0x2000` indicates no bend.
/// A value of `0x3FFF` indicates full bend upwards.
///
/// This value is available via [`PitchBend::value`]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub struct PitchBend {
lsb: DataByte,
msb: DataByte,
}
impl PitchBend {
/// Creates a new pitch bend given the
/// least significant and most significant bytes.
///
/// Checks for byte correctness (leading 0 bit)
pub fn new(lsb: u8, msb: u8) -> Result<Self, std::io::Error> {
let lsb = DataByte::new(lsb)?;
let msb = DataByte::new(msb)?;
Ok(Self { lsb, msb })
}
/// Creates a new pitch bend given the
/// least significant and most significant bytes.
///
/// Does not check for correctness
pub const fn new_unchecked(lsb: u8, msb: u8) -> Self {
Self {
lsb: DataByte::new_unchecked(lsb),
msb: DataByte::new_unchecked(msb),
}
}
/// Returns a reference to the pitch bend's least significant byte.
pub fn lsb(&self) -> u8 {
self.lsb.0
}
/// Returns a reference to the pitch bend's most significant byte.
pub fn msb(&self) -> u8 {
self.msb.0
}
/// Represents a pitch bend
pub fn value(&self) -> u16 {
let lsb = self.lsb.value();
let msb = self.msb.value();
let combined: u16 = ((msb as u16) << 8) | (lsb as u16);
combined
}
/// Represents a u16, lsb then msb, as a pitch bend
pub fn from_bits(rep: u16) -> Result<Self, std::io::Error> {
let lsb = (rep >> 8) as u8;
let msb = (rep & 0x00FF) as u8;
Self::new(lsb, msb)
}
}
impl PitchBend {
/// The minimum value of `0x0000`, indicating full bend downwards.
pub const MIN_BYTES: u16 = 0x0000;
/// The middle value of `0x2000`, indicating no bend.
pub const MID_BYTES: u16 = 0x2000;
/// The maximum value of `0x3FFF`, indicating full bend upwards.
pub const MAX_VALUE: u16 = 0x3FFF;
/// Create a `PitchBend` value from an int in the range `[-0x2000, 0x1FFF]`.
///
/// Integers outside this range will be clamped.
#[inline]
pub fn from_int(int: i16) -> Self {
PitchBend::from_bits((int.clamp(-0x2000, 0x1FFF) + 0x2000) as u16).unwrap()
}
/// Create a `PitchBend` value from a number in the range `[-1.0, 1.0)`.
///
/// Floats outside this range will be clamped.
#[inline]
pub fn from_f32(float: f32) -> Self {
PitchBend::from_int((float.clamp(-1.0, 1.0) * 0x2000 as f32) as i16)
}
/// Create a `PitchBend` value from a number in the range `[-1.0, 1.0)`.
///
/// Floats outside this range will be clamped.
#[inline]
pub fn from_f64(float: f64) -> Self {
PitchBend::from_int((float.clamp(-1.0, 1.0) * 0x2000 as f64) as i16)
}
/// Returns an int in the range `[-0x2000, 0x1FFF]`.
///
/// Do not use this when writing to a midi file.
#[inline]
pub fn as_int(self) -> i16 {
self.value() as i16 - 0x2000
}
/// Returns an `f32` in the range `[-1.0, 1.0)`.
#[inline]
pub fn as_f32(self) -> f32 {
self.as_int() as f32 * (1.0 / 0x2000 as f32)
}
/// Returns an `f64` in the range `[-1.0, 1.0)`.
#[inline]
pub fn as_f64(self) -> f64 {
self.as_int() as f64 * (1.0 / 0x2000 as f64)
}
}