forked from hartmann1301/Arduboy2
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Arduboy2Beep.h
362 lines (342 loc) · 11.7 KB
/
Arduboy2Beep.h
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/**
* @file Arduboy2Beep.h
* \brief
* Classes to generate simple square wave tones on the Arduboy speaker pins.
*/
#ifndef ARDUBOY2_BEEP_H
#define ARDUBOY2_BEEP_H
/** \brief
* Play simple square wave tones using speaker pin 1.
*
* \note
* Class `BeepPin2` provides identical functions for playing tones on speaker
* pin 2. Both classes can be used in the same sketch to allow playing
* two tones at the same time. To do this, the `begin()` and `timer()`
* functions of both classes must be used.
*
* \details
* This class can be used to play square wave tones on speaker pin 1.
* The functions are designed to produce very small and efficient code.
*
* A tone can be set to play for a given duration, or continuously until
* stopped or replaced by a new tone. No interrupts are used. A tone is
* generated by a hardware timer/counter directly toggling the pin,
* so once started, no CPU cycles are used to actually play the tone.
* The program continues to run while a tone is playing. However, a small
* amount of code is required to time and stop a tone after a given duration.
*
* Tone frequencies can range from 15.26Hz to 1000000Hz.
*
* Although there's no specific code to handle mute control, the
* `Arduboy2Audio` class will work since it has code to mute sound by setting
* the speaker pins to input mode and unmute by setting the pins as outputs.
* The `BeepPin1` class doesn't interfere with this operation.
*
* In order to avoid needing to use interrupts, the duration of tones is timed
* by calling the `timer()` function continuously at a fixed interval.
* The duration of a tone is given by specifying the number of times `timer()`
* will be called before stopping the tone.
*
* For sketches that use `Arduboy2::nextFrame()`, or some other method to
* generate frames at a fixed rate, `timer()` can be called once per frame.
* Tone durations will then be given as the number of frames to play the tone
* for. For example, with a rate of 60 frames per second a duration of 30
* would be used to play a tone for half a second.
*
* The variable named `#duration` is the counter that times the duration of a
* tone. A sketch can determine if a tone is currently playing by testing if
* the `#duration` variable is non-zero (assuming it's a timed tone, not a
* continuous tone).
*
* To keep the code small and efficient, the frequency of a tone is specified
* by the actual count value to be loaded into to timer/counter peripheral.
* The frequency will be determined by the count provided and the clock rate
* of the timer/counter. In order to allow a tone's frequency to be specified
* in hertz (cycles per second) the `freq()` helper function is provided,
* which converts a given frequency to the required count value.
*
* NOTE that it is intended that `freq()` only be called with constant values.
* If `freq()` is called with a variable, code to perform floating point math
* will be included in the sketch, which will likely greatly increase the
* sketch's code size unless the sketch also uses floating point math for
* other purposes.
*
* The formulas for frequency/count conversion are:
*
* count=(1000000/frequency)-1
* frequency=1000000/(count+1)
*
* Counts must be between 0 and 65535.
*
* All members of the class are static, so it's not necessary to create an
* instance of the class in order to use it. However, creating an instance
* doesn't produce any more code and it may make the source code smaller and
* make it easier to switch to the `BeepPin2` class if it becomes necessary.
*
* The following is a basic example sketch, which will generate a tone when
* a button is pressed.
*
* \code{.cpp}
* #include <Arduboy2.h>
* // There's no need to #include <Arduboy2Beep.h>
* // It will be included in Arduboy2.h
*
* Arduboy2 arduboy;
* BeepPin1 beep; // class instance for speaker pin 1
*
* void setup() {
* arduboy.begin();
* arduboy.setFrameRate(50);
* beep.begin(); // set up the hardware for playing tones
* }
*
* void loop() {
* if (!arduboy.nextFrame()) {
* return;
* }
*
* beep.timer(); // handle tone duration
*
* arduboy.pollButtons();
*
* if (arduboy.justPressed(A_BUTTON)) {
* // play a 1000Hz tone for 100 frames (2 seconds at 50 FPS)
* // beep.freq(1000) is used to convert 1000Hz to the required count
* beep.tone(beep.freq(1000), 100);
* }
* }
* \endcode
*
* \note
* These functions, and the equivalents in class `BeepPin2`, will not work with
* a DevKit Arduboy because the speaker pins used cannot be directly controlled
* by a timer/counter. "Dummy" functions are provided so a sketch will compile
* and work properly but no sound will be produced.
*
* \see BeepPin2
*/
class BeepPin1
{
public:
/** \brief
* The counter used by the `timer()` function to time the duration of a tone.
*
* \details
* This variable is set by the `dur` parameter of the `tone()` function.
* It is then decremented each time the `timer()` function is called, if its
* value isn't 0. When `timer()` decrements it to 0, a tone that is playing
* will be stopped.
*
* A sketch can determine if a tone is currently playing by testing if
* this variable is non-zero (assuming it's a timed tone, not a continuous
* tone).
*
* Example:
* \code{.cpp}
* beep.tone(beep.freq(1000), 15);
* while (beep.duration != 0) { } // wait for the tone to stop playing
* \endcode
*
* It can also be manipulated directly by the sketch, although this should
* seldom be necessary.
*/
static uint8_t duration;
/** \brief
* Set up the hardware.
*
* \details
* Prepare the hardware for playing tones.
* This function must be called (usually in `setup()`) before using any of
* the other functions in this class.
*/
static void begin();
/** \brief
* Play a tone continually, until replaced by a new tone or stopped.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
*
* \details
* A tone is played indefinitely, until replaced by another tone or stopped
* using `noTone()`.
*
* The tone's frequency is determined by the specified count, which is loaded
* into the timer/counter that generates the tone. A desired frequency can be
* converted into the required count value using the `freq()` function.
*
* \see freq() timer() noTone()
*/
static void tone(uint16_t count);
/** \brief
* Play a tone for a given duration.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
* \param dur The duration of the tone, used by `timer()`.
*
* \details
* A tone is played for the specified duration, or until replaced by another
* tone or stopped using `noTone()`.
*
* The tone's frequency is determined by the specified count, which is loaded
* into the timer/counter that generates the tone. A desired frequency can be
* converted into the required count value using the `freq()` function.
*
* The duration value is the number of times the `timer()` function must be
* called before the tone is stopped.
*
* \see freq() timer() noTone()
*/
static void tone(uint16_t count, uint8_t dur);
/** \brief
* Handle the duration that a tone plays for.
*
* \details
* This function must be called at a constant interval, which would normally
* be once per frame, in order to stop a tone after the desired tone duration
* has elapsed.
*
* If the value of the `duration` variable is not 0, it will be decremented.
* When the `duration` variable is decremented to 0, a playing tone will be
* stopped.
*/
static void timer();
/** \brief
* Stop a tone that is playing.
*
* \details
* If a tone is playing it will be stopped. It's safe to call this function
* even if a tone isn't currently playing.
*
* \see tone()
*/
static void noTone();
/** \brief
* Convert a frequency to the required count.
*
* \param hz The frequency, in hertz (cycles per second), to be converted
* to a count.
*
* \return The required count to be loaded into the timer/counter for the
* given frequency.
*
* \details
* This helper function will convert a desired tone frequency to the closest
* value required by the `tone()` function's `count` parameter.
* The calculated count is rounded up or down to the nearest integer,
* if necessary.
*
* Example:
* \code{.cpp}
* beep.tone(beep.freq(440)); // play a 440Hz tone until stopped or replaced
* \endcode
*
* \note
* It is intended that `freq()` only be called with constant values.
* If `freq()` is called with a variable, code to perform floating point math
* will be included in the sketch, which will likely greatly increase the
* sketch's code size unless the sketch also uses floating point math for
* other purposes.
*/
static constexpr uint16_t freq(const float hz)
{
return (uint16_t) (((F_CPU / 8 / 2) + (hz / 2)) / hz) - 1;
}
};
/** \brief
* Play simple square wave tones using speaker pin 2.
*
* \details
* This class contains the same functions as class `BeepPin1` except they use
* speaker pin 2 instead of speaker pin 1.
*
* Using `BeepPin1` is more desirable, as it uses a 16 bit Timer, which can
* produce a greater frequency range and resolution than the 10 bit Timer
* used by `BeepPin2`. However, if the sketch also includes other sound
* generating code that uses speaker pin 1, `BeepPin2` can be used to avoid
* conflict.
*
* Tone frequencies on speaker pin 2 can range from 61.04Hz to 15625Hz using
* allowed counts from 3 to 1023.
*
* The formulas for frequency/count conversion are:
*
* count=(62500/frequency)-1
* frequency=62500/(count+1)
*
* See the documentation for `BeepPin1` for more details.
*
* \see BeepPin1
*/
class BeepPin2
{
public:
/** \brief
* The counter used by the `timer()` function to time the duration of a tone
* played on speaker pin 2.
*
* \details
* For details see `BeepPin1::duration`.
*/
static uint8_t duration;
/** \brief
* Set up the hardware for playing tones using speaker pin 2.
*
* \details
* For details see `BeepPin1::begin()`.
*/
static void begin();
/** \brief
* Play a tone on speaker pin 2 continually, until replaced by a new tone
* or stopped.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
*
* \details
* For details see `BeepPin1::tone(uint16_t)`.
*/
static void tone(uint16_t count);
/** \brief
* Play a tone on speaker pin 2 for a given duration.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
* \param dur The duration of the tone, used by `timer()`.
*
* \details
* For details see `BeepPin1::tone(uint16_t, uint8_t)`.
*/
static void tone(uint16_t count, uint8_t dur);
/** \brief
* Handle the duration that a tone on speaker pin 2 plays for.
*
* \details
* For details see `BeepPin1::timer()`.
*/
static void timer();
/** \brief
* Stop a tone that is playing on speaker pin 2.
*
* \details
* For details see `BeepPin1::noTone()`.
*/
static void noTone();
/** \brief
* Convert a frequency to the required count for speaker pin 2.
*
* \param hz The frequency, in hertz (cycles per second), to be converted
* to a count.
*
* \return The required count to be loaded into the timer/counter for the
* given frequency.
*
* \details
* For details see `BeepPin1::freq()`.
*/
static constexpr uint16_t freq(const float hz)
{
return (uint16_t) (((F_CPU / 128 / 2) + (hz / 2)) / hz) - 1;
}
};
#endif