microbit_bsp/
speaker.rs

1//! Simple speaker utilities for PWM-based synth
2use embassy_nrf::pwm;
3use embassy_time::{Delay, Timer};
4use embedded_hal::delay::DelayNs;
5
6/// Represents any pitch
7#[derive(Copy, Clone, PartialEq)]
8#[allow(missing_docs)]
9pub enum Pitch {
10    Silent,
11    Named(NamedPitch),
12    /// Hz
13    Frequency(u32),
14}
15
16/// Pitch for standard scale
17#[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    /// Turn into Hz
133    #[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/// A note is a pitch + a duration (ms)
146#[derive(Clone, Copy)]
147pub struct Note(pub Pitch, pub u32);
148
149/// PWM based speaker capable of playing notes with a given pitch
150pub struct PwmSpeaker<'a, T: pwm::Instance> {
151    pwm: pwm::SimplePwm<'a, T>,
152}
153
154impl<'a, T: pwm::Instance> PwmSpeaker<'a, T> {
155    /// Create a new speaker instance
156    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    /// Play a note
172    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    /// Play a note, blocking variant.
190    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    /// Start playing a note in a non-blocking way
209    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    /// Stop playing a note
219    pub fn stop(&mut self) {
220        self.stop_play();
221    }
222}