1use crate::errors::Error;
2
3const REFERENCE_PITCH: f32 = 440.0;
5
6#[derive(Debug, Clone, Copy)]
8pub enum AudioChannel {
9 A,
10 B,
11 C
12}
13
14impl AudioChannel {
15 pub fn index(&self) -> usize {
16 self.clone() as usize
17 }
18}
19
20#[derive(Debug, Clone, Copy)]
22pub struct AudioChannelData {
23 pub address: u8,
24 pub enabled: bool,
25 pub noise_enabled: bool,
26 pub level: u8,
27 pub pitch_bend: f32,
28 pub last_note: Option<Note>,
29}
30
31impl AudioChannelData {
32 pub fn new(address: u8) -> Self {
34 Self {
35 address: address,
36 enabled: false,
37 noise_enabled: false,
38 level: 0,
39 pitch_bend: 0.0,
40 last_note: None,
41 }
42 }
43
44 #[allow(unused)]
46 pub fn set_pitch_bend(&mut self, byte1: u8, byte2: u8) {
47 let new: f32 = (((byte2 as u16) << 7) + byte1 as u16).into();
48 let as_semitones: f32 = (new - 8192.0) / 1024.0;
49 self.pitch_bend = as_semitones;
50 }
51}
52
53#[repr(i8)]
55#[derive(Debug, Clone, Copy)]
56pub enum Accidental {
57 Natural = 0,
58 Sharp = 2,
59 Flat = -2,
60 MicroSharp = 1,
61 MicroFlat = -1,
62}
63
64impl From<Accidental> for f32 {
65 fn from(acc: Accidental) -> f32 {
66 (acc as i8) as f32 / 2.0
67 }
68}
69
70#[repr(i8)]
72#[derive(Debug, Clone, Copy)]
73pub enum BaseNote {
74 C = -9,
75 D = -7,
76 E = -5,
77 F = -4,
78 G = -2,
79 A = 0,
80 B = 2,
81}
82
83impl From<BaseNote> for f32 {
84 fn from(bn: BaseNote) -> f32 {
85 bn as i8 as f32
86 }
87}
88
89#[derive(Debug, Clone, Copy)]
102pub struct Note {
103 base_note: BaseNote,
104 octave: u8,
105 accidental: Option<Accidental>,
106 offset: f32,
107}
108
109impl Note {
110 pub fn new(base_note: BaseNote, octave: u8, accidental: Option<Accidental>) -> Result<Self, Error> {
112 if octave <= 14 {
113 Ok(Self {
114 base_note: base_note,
115 octave: octave.clamp(0, 14),
116 accidental: accidental,
117 offset: 0.0,
118 })
119 } else {
120 Err(Error::OctaveOutOfRange(octave))
121 }
122
123 }
124
125 pub fn transpose(self, semitones: f32) -> Self {
128 Self {
129 offset: self.offset + semitones,
130 ..self
131 }
132 }
133
134 pub fn as_hz(&self) -> u32 {
136 use libm::{powf, roundf};
138
139 let distance_a4: f32 = f32::from(self.base_note)
140 + f32::from(self.accidental.unwrap_or(Accidental::Natural))
141 + (self.octave.clamp(0, 14) as f32 - 4.0) * 12.0
142 + self.offset;
143
144 roundf(REFERENCE_PITCH * powf(2.0, distance_a4 / 12.0)) as u32
145 }
146}