1use embassy_nrf::pwm;
3use embassy_time::{Delay, Timer};
4use embedded_hal::delay::DelayNs;
5
6#[derive(Copy, Clone, PartialEq)]
8#[allow(missing_docs)]
9pub enum Pitch {
10 Silent,
11 Named(NamedPitch),
12 Frequency(u32),
14}
15
16#[allow(dead_code, missing_docs)]
18#[derive(Copy, Clone, PartialEq)]
19#[repr(u32)]
20pub enum NamedPitch {
21 C0 = 16,
22 CS0 = 17,
23 D0 = 18,
24 DS0 = 19,
25 E0 = 20,
26 F0 = 21,
27 FS0 = 23,
28 G0 = 24,
29 GS0 = 25,
30 A0 = 27,
31 AS0 = 29,
32 B0 = 30,
33 C1 = 32,
34 CS1 = 34,
35 D1 = 36,
36 DS1 = 38,
37 E1 = 41,
38 F1 = 43,
39 FS1 = 46,
40 G1 = 49,
41 GS1 = 51,
42 A1 = 55,
43 AS1 = 58,
44 B1 = 61,
45 C2 = 65,
46 CS2 = 69,
47 D2 = 73,
48 DS2 = 77,
49 E2 = 82,
50 F2 = 87,
51 FS2 = 92,
52 G2 = 98,
53 GS2 = 103,
54 A2 = 110,
55 AS2 = 116,
56 B2 = 123,
57 C3 = 130,
58 CS3 = 138,
59 D3 = 146,
60 DS3 = 155,
61 E3 = 164,
62 F3 = 174,
63 FS3 = 185,
64 G3 = 196,
65 GS3 = 207,
66 A3 = 220,
67 AS3 = 233,
68 B3 = 246,
69 C4 = 261,
70 CS4 = 277,
71 D4 = 293,
72 DS4 = 311,
73 E4 = 329,
74 F4 = 349,
75 FS4 = 369,
76 G4 = 392,
77 GS4 = 415,
78 A4 = 440,
79 AS4 = 466,
80 B4 = 493,
81 C5 = 523,
82 CS5 = 554,
83 D5 = 587,
84 DS5 = 622,
85 E5 = 659,
86 F5 = 698,
87 FS5 = 739,
88 G5 = 783,
89 GS5 = 830,
90 A5 = 880,
91 AS5 = 932,
92 B5 = 987,
93 C6 = 1046,
94 CS6 = 1108,
95 D6 = 1174,
96 DS6 = 1244,
97 E6 = 1318,
98 F6 = 1396,
99 FS6 = 1479,
100 G6 = 1567,
101 GS6 = 1661,
102 A6 = 1760,
103 AS6 = 1864,
104 B6 = 1975,
105 C7 = 2093,
106 CS7 = 2217,
107 D7 = 2349,
108 DS7 = 2489,
109 E7 = 2637,
110 F7 = 2793,
111 FS7 = 2959,
112 G7 = 3135,
113 GS7 = 3322,
114 A7 = 3520,
115 AS7 = 3729,
116 B7 = 3951,
117 C8 = 4186,
118 CS8 = 4434,
119 D8 = 4698,
120 DS8 = 4978,
121 E8 = 5274,
122 F8 = 5587,
123 FS8 = 5919,
124 G8 = 6271,
125 GS8 = 6644,
126 A8 = 7040,
127 AS8 = 7458,
128 B8 = 7902,
129}
130
131impl NamedPitch {
132 #[must_use]
134 pub fn into_frequency(self) -> u32 {
135 self as u32
136 }
137}
138
139impl From<NamedPitch> for Pitch {
140 fn from(value: NamedPitch) -> Self {
141 Self::Named(value)
142 }
143}
144
145#[derive(Clone, Copy)]
147pub struct Note(pub Pitch, pub u32);
148
149pub struct PwmSpeaker<'a, T: pwm::Instance> {
151 pwm: pwm::SimplePwm<'a, T>,
152}
153
154impl<'a, T: pwm::Instance> PwmSpeaker<'a, T> {
155 pub fn new(pwm: pwm::SimplePwm<'a, T>) -> Self {
157 Self { pwm }
158 }
159
160 fn start_play(&mut self, frequency: u32) {
161 self.pwm.set_prescaler(pwm::Prescaler::Div4);
162 self.pwm.set_period(frequency);
163 self.pwm.enable();
164 self.pwm.set_duty(0, self.pwm.max_duty() / 2);
165 }
166
167 fn stop_play(&mut self) {
168 self.pwm.disable();
169 }
170
171 pub async fn play(&mut self, note: &Note) {
173 let Note(pitch, duration) = note;
174
175 let frequency = match pitch {
176 Pitch::Silent => {
177 Timer::after_millis(u64::from(*duration)).await;
178 return;
179 }
180 Pitch::Named(n) => n.into_frequency(),
181 Pitch::Frequency(f) => *f,
182 };
183
184 self.start_play(frequency);
185 Timer::after_millis(u64::from(*duration)).await;
186 self.stop_play();
187 }
188
189 pub fn play_blocking(&mut self, note: &Note) {
191 let Note(pitch, duration) = note;
192 let mut delay = Delay;
193
194 let frequency = match pitch {
195 Pitch::Silent => {
196 delay.delay_ms(*duration);
197 return;
198 }
199 Pitch::Named(n) => n.into_frequency(),
200 Pitch::Frequency(f) => *f,
201 };
202
203 self.start_play(frequency);
204 delay.delay_ms(*duration);
205 self.stop_play();
206 }
207
208 pub fn start_note(&mut self, pitch: Pitch) {
210 let frequency = match pitch {
211 Pitch::Silent => return,
212 Pitch::Named(n) => n.into_frequency(),
213 Pitch::Frequency(f) => f,
214 };
215 self.start_play(frequency);
216 }
217
218 pub fn stop(&mut self) {
220 self.stop_play();
221 }
222}