1 | #include "ZumoBuzzer.h"
|
---|
2 | #include <Arduino.h>
|
---|
3 |
|
---|
4 | #define BUZZER_PIN 6
|
---|
5 |
|
---|
6 | const char *buzzerSequence = 0;
|
---|
7 |
|
---|
8 | // declaring these globals as static means they won't conflict
|
---|
9 | // with globals in other .cpp files that share the same name
|
---|
10 | static volatile unsigned int buzzerTimeout = 0; // tracks buzzer time limit
|
---|
11 | static char play_mode_setting = PLAY_AUTOMATIC;
|
---|
12 |
|
---|
13 | extern volatile unsigned char buzzerFinished; // flag: 0 while playing
|
---|
14 | extern const char *buzzerSequence;
|
---|
15 |
|
---|
16 |
|
---|
17 | static unsigned char use_program_space; // boolean: true if we should
|
---|
18 | // use program space
|
---|
19 |
|
---|
20 | // music settings and defaults
|
---|
21 | static unsigned char octave = 4; // the current octave
|
---|
22 | static unsigned int whole_note_duration = 2000; // the duration of a whole note
|
---|
23 | static unsigned int note_type = 4; // 4 for quarter, etc
|
---|
24 | static unsigned int duration = 500; // the duration of a note in ms
|
---|
25 | static unsigned int volume = 15; // the note volume
|
---|
26 | static unsigned char staccato = 0; // true if playing staccato
|
---|
27 |
|
---|
28 | // staccato handling
|
---|
29 | static unsigned char staccato_rest_duration; // duration of a staccato
|
---|
30 | // rest, or zero if it is time
|
---|
31 | // to play a note
|
---|
32 |
|
---|
33 | static void nextNote();
|
---|
34 |
|
---|
35 | ZumoBuzzer::ZumoBuzzer()
|
---|
36 | {
|
---|
37 | }
|
---|
38 |
|
---|
39 | inline void ZumoBuzzer::init()
|
---|
40 | {
|
---|
41 | }
|
---|
42 |
|
---|
43 | void ZumoBuzzer::init2()
|
---|
44 | {
|
---|
45 | }
|
---|
46 |
|
---|
47 | void ZumoBuzzer::playFrequency(unsigned int freq, unsigned int dur,
|
---|
48 | unsigned char volume)
|
---|
49 | {
|
---|
50 | tone(BUZZER_PIN, freq, dur);
|
---|
51 | }
|
---|
52 |
|
---|
53 | void ZumoBuzzer::playNote(unsigned char note, unsigned int dur,
|
---|
54 | unsigned char volume)
|
---|
55 | {
|
---|
56 | unsigned int freq = 0;
|
---|
57 | unsigned char offset_note = note - 16;
|
---|
58 |
|
---|
59 | if (note == SILENT_NOTE || volume == 0)
|
---|
60 | {
|
---|
61 | freq = 1000; // silent notes => use 1kHz freq (for cycle counter)
|
---|
62 | playFrequency(freq, dur, 0);
|
---|
63 | return;
|
---|
64 | }
|
---|
65 |
|
---|
66 | if (note <= 16)
|
---|
67 | offset_note = 0;
|
---|
68 | else if (offset_note > 95)
|
---|
69 | offset_note = 95;
|
---|
70 |
|
---|
71 | unsigned char exponent = offset_note / 12;
|
---|
72 |
|
---|
73 | // frequency table for the lowest 12 allowed notes
|
---|
74 | // frequencies are specified in tenths of a Hertz for added resolution
|
---|
75 | switch (offset_note - exponent * 12) // equivalent to (offset_note % 12)
|
---|
76 | {
|
---|
77 | case 0: // note E1 = 41.2 Hz
|
---|
78 | freq = 412;
|
---|
79 | break;
|
---|
80 | case 1: // note F1 = 43.7 Hz
|
---|
81 | freq = 437;
|
---|
82 | break;
|
---|
83 | case 2: // note F#1 = 46.3 Hz
|
---|
84 | freq = 463;
|
---|
85 | break;
|
---|
86 | case 3: // note G1 = 49.0 Hz
|
---|
87 | freq = 490;
|
---|
88 | break;
|
---|
89 | case 4: // note G#1 = 51.9 Hz
|
---|
90 | freq = 519;
|
---|
91 | break;
|
---|
92 | case 5: // note A1 = 55.0 Hz
|
---|
93 | freq = 550;
|
---|
94 | break;
|
---|
95 | case 6: // note A#1 = 58.3 Hz
|
---|
96 | freq = 583;
|
---|
97 | break;
|
---|
98 | case 7: // note B1 = 61.7 Hz
|
---|
99 | freq = 617;
|
---|
100 | break;
|
---|
101 | case 8: // note C2 = 65.4 Hz
|
---|
102 | freq = 654;
|
---|
103 | break;
|
---|
104 | case 9: // note C#2 = 69.3 Hz
|
---|
105 | freq = 693;
|
---|
106 | break;
|
---|
107 | case 10: // note D2 = 73.4 Hz
|
---|
108 | freq = 734;
|
---|
109 | break;
|
---|
110 | case 11: // note D#2 = 77.8 Hz
|
---|
111 | freq = 778;
|
---|
112 | break;
|
---|
113 | }
|
---|
114 |
|
---|
115 | if (exponent < 7)
|
---|
116 | {
|
---|
117 | freq = freq << exponent; // frequency *= 2 ^ exponent
|
---|
118 | if (exponent > 1) // if the frequency is greater than 160 Hz
|
---|
119 | freq = (freq + 5) / 10; // we don't need the extra resolution
|
---|
120 | else
|
---|
121 | freq += DIV_BY_10; // else keep the added digit of resolution
|
---|
122 | }
|
---|
123 | else
|
---|
124 | freq = (freq * 64 + 2) / 5; // == freq * 2^7 / 10 without int overflow
|
---|
125 |
|
---|
126 | if (volume > 15)
|
---|
127 | volume = 15;
|
---|
128 | playFrequency(freq, dur, volume); // set buzzer this freq/duration
|
---|
129 | }
|
---|
130 |
|
---|
131 | // Returns 1 if the buzzer is currently playing, otherwise it returns 0
|
---|
132 | unsigned char ZumoBuzzer::isPlaying()
|
---|
133 | {
|
---|
134 | if(buzzerSequence) {
|
---|
135 | return 1;
|
---|
136 | }
|
---|
137 | else {
|
---|
138 | return 0;
|
---|
139 | }
|
---|
140 | }
|
---|
141 |
|
---|
142 | #include "ZumoShield_cfg.h"
|
---|
143 | #include "r2ca.h"
|
---|
144 |
|
---|
145 | void
|
---|
146 | zumobuzzer_task(intptr_t exinf) {
|
---|
147 | while(buzzerSequence) {
|
---|
148 | nextNote();
|
---|
149 | }
|
---|
150 | }
|
---|
151 |
|
---|
152 | void ZumoBuzzer::play(const char *notes)
|
---|
153 | {
|
---|
154 | buzzerSequence = notes;
|
---|
155 | use_program_space = 0;
|
---|
156 | staccato_rest_duration = 0;
|
---|
157 | act_tsk(ZUMOBUZZER_TASK);
|
---|
158 | }
|
---|
159 |
|
---|
160 | void ZumoBuzzer::playFromProgramSpace(const char *notes_p)
|
---|
161 | {
|
---|
162 | buzzerSequence = notes_p;
|
---|
163 | use_program_space = 1;
|
---|
164 | staccato_rest_duration = 0;
|
---|
165 | act_tsk(ZUMOBUZZER_TASK);
|
---|
166 | }
|
---|
167 |
|
---|
168 | void ZumoBuzzer::stopPlaying()
|
---|
169 | {
|
---|
170 | }
|
---|
171 |
|
---|
172 | // Gets the current character, converting to lower-case and skipping
|
---|
173 | // spaces. For any spaces, this automatically increments sequence!
|
---|
174 | static char currentCharacter()
|
---|
175 | {
|
---|
176 | char c = 0;
|
---|
177 | do
|
---|
178 | {
|
---|
179 | if(use_program_space)
|
---|
180 | c = pgm_read_byte(buzzerSequence);
|
---|
181 | else
|
---|
182 | c = *buzzerSequence;
|
---|
183 |
|
---|
184 | if(c >= 'A' && c <= 'Z')
|
---|
185 | c += 'a'-'A';
|
---|
186 | } while(c == ' ' && (buzzerSequence ++));
|
---|
187 |
|
---|
188 | return c;
|
---|
189 | }
|
---|
190 |
|
---|
191 | // Returns the numerical argument specified at buzzerSequence[0] and
|
---|
192 | // increments sequence to point to the character immediately after the
|
---|
193 | // argument.
|
---|
194 | static unsigned int getNumber()
|
---|
195 | {
|
---|
196 | unsigned int arg = 0;
|
---|
197 |
|
---|
198 | // read all digits, one at a time
|
---|
199 | char c = currentCharacter();
|
---|
200 | while(c >= '0' && c <= '9')
|
---|
201 | {
|
---|
202 | arg *= 10;
|
---|
203 | arg += c-'0';
|
---|
204 | buzzerSequence ++;
|
---|
205 | c = currentCharacter();
|
---|
206 | }
|
---|
207 |
|
---|
208 | return arg;
|
---|
209 | }
|
---|
210 |
|
---|
211 | static void nextNote()
|
---|
212 | {
|
---|
213 | unsigned char note = 0;
|
---|
214 | unsigned char rest = 0;
|
---|
215 | unsigned char tmp_octave = octave; // the octave for this note
|
---|
216 | unsigned int tmp_duration; // the duration of this note
|
---|
217 | unsigned int dot_add;
|
---|
218 |
|
---|
219 | char c; // temporary variable
|
---|
220 |
|
---|
221 | // if we are playing staccato, after every note we play a rest
|
---|
222 | if(staccato && staccato_rest_duration)
|
---|
223 | {
|
---|
224 | ZumoBuzzer::playNote(SILENT_NOTE, staccato_rest_duration, 0);
|
---|
225 | staccato_rest_duration = 0;
|
---|
226 | return;
|
---|
227 | }
|
---|
228 |
|
---|
229 | parse_character:
|
---|
230 |
|
---|
231 | // Get current character
|
---|
232 | c = currentCharacter();
|
---|
233 | buzzerSequence ++;
|
---|
234 |
|
---|
235 | // Interpret the character.
|
---|
236 | switch(c)
|
---|
237 | {
|
---|
238 | case '>':
|
---|
239 | // shift the octave temporarily up
|
---|
240 | tmp_octave ++;
|
---|
241 | goto parse_character;
|
---|
242 | case '<':
|
---|
243 | // shift the octave temporarily down
|
---|
244 | tmp_octave --;
|
---|
245 | goto parse_character;
|
---|
246 | case 'a':
|
---|
247 | note = NOTE_A(0);
|
---|
248 | break;
|
---|
249 | case 'b':
|
---|
250 | note = NOTE_B(0);
|
---|
251 | break;
|
---|
252 | case 'c':
|
---|
253 | note = NOTE_C(0);
|
---|
254 | break;
|
---|
255 | case 'd':
|
---|
256 | note = NOTE_D(0);
|
---|
257 | break;
|
---|
258 | case 'e':
|
---|
259 | note = NOTE_E(0);
|
---|
260 | break;
|
---|
261 | case 'f':
|
---|
262 | note = NOTE_F(0);
|
---|
263 | break;
|
---|
264 | case 'g':
|
---|
265 | note = NOTE_G(0);
|
---|
266 | break;
|
---|
267 | case 'l':
|
---|
268 | // set the default note duration
|
---|
269 | note_type = getNumber();
|
---|
270 | duration = whole_note_duration/note_type;
|
---|
271 | goto parse_character;
|
---|
272 | case 'm':
|
---|
273 | // set music staccato or legato
|
---|
274 | if(currentCharacter() == 'l')
|
---|
275 | staccato = false;
|
---|
276 | else
|
---|
277 | {
|
---|
278 | staccato = true;
|
---|
279 | staccato_rest_duration = 0;
|
---|
280 | }
|
---|
281 | buzzerSequence ++;
|
---|
282 | goto parse_character;
|
---|
283 | case 'o':
|
---|
284 | // set the octave permanently
|
---|
285 | octave = getNumber();
|
---|
286 | tmp_octave = octave;
|
---|
287 | goto parse_character;
|
---|
288 | case 'r':
|
---|
289 | // Rest - the note value doesn't matter.
|
---|
290 | rest = 1;
|
---|
291 | break;
|
---|
292 | case 't':
|
---|
293 | // set the tempo
|
---|
294 | whole_note_duration = 60*400/getNumber()*10;
|
---|
295 | duration = whole_note_duration/note_type;
|
---|
296 | goto parse_character;
|
---|
297 | case 'v':
|
---|
298 | // set the volume
|
---|
299 | volume = getNumber();
|
---|
300 | goto parse_character;
|
---|
301 | case '!':
|
---|
302 | // reset to defaults
|
---|
303 | octave = 4;
|
---|
304 | whole_note_duration = 2000;
|
---|
305 | note_type = 4;
|
---|
306 | duration = 500;
|
---|
307 | volume = 15;
|
---|
308 | staccato = 0;
|
---|
309 | // reset temp variables that depend on the defaults
|
---|
310 | tmp_octave = octave;
|
---|
311 | tmp_duration = duration;
|
---|
312 | goto parse_character;
|
---|
313 | default:
|
---|
314 | buzzerSequence = 0;
|
---|
315 | return;
|
---|
316 | }
|
---|
317 |
|
---|
318 | note += tmp_octave*12;
|
---|
319 |
|
---|
320 | // handle sharps and flats
|
---|
321 | c = currentCharacter();
|
---|
322 | while(c == '+' || c == '#')
|
---|
323 | {
|
---|
324 | buzzerSequence ++;
|
---|
325 | note ++;
|
---|
326 | c = currentCharacter();
|
---|
327 | }
|
---|
328 | while(c == '-')
|
---|
329 | {
|
---|
330 | buzzerSequence ++;
|
---|
331 | note --;
|
---|
332 | c = currentCharacter();
|
---|
333 | }
|
---|
334 |
|
---|
335 | // set the duration of just this note
|
---|
336 | tmp_duration = duration;
|
---|
337 |
|
---|
338 | // If the input is 'c16', make it a 16th note, etc.
|
---|
339 | if(c > '0' && c < '9')
|
---|
340 | tmp_duration = whole_note_duration/getNumber();
|
---|
341 |
|
---|
342 | // Handle dotted notes - the first dot adds 50%, and each
|
---|
343 | // additional dot adds 50% of the previous dot.
|
---|
344 | dot_add = tmp_duration/2;
|
---|
345 | while(currentCharacter() == '.')
|
---|
346 | {
|
---|
347 | buzzerSequence ++;
|
---|
348 | tmp_duration += dot_add;
|
---|
349 | dot_add /= 2;
|
---|
350 | }
|
---|
351 |
|
---|
352 | if(staccato)
|
---|
353 | {
|
---|
354 | staccato_rest_duration = tmp_duration / 2;
|
---|
355 | tmp_duration -= staccato_rest_duration;
|
---|
356 | }
|
---|
357 |
|
---|
358 | // this will re-enable the timer1 overflow interrupt
|
---|
359 | ZumoBuzzer::playNote(rest ? SILENT_NOTE : note, tmp_duration, volume);
|
---|
360 | delay(tmp_duration*1.5);
|
---|
361 | }
|
---|
362 |
|
---|
363 | void ZumoBuzzer::playMode(unsigned char mode)
|
---|
364 | {
|
---|
365 | }
|
---|
366 |
|
---|
367 | unsigned char ZumoBuzzer::playCheck()
|
---|
368 | {
|
---|
369 | return 0;
|
---|
370 | }
|
---|