diff options
Diffstat (limited to 'quantum/audio')
-rw-r--r-- | quantum/audio/audio.c | 539 | ||||
-rw-r--r-- | quantum/audio/audio.h | 281 | ||||
-rw-r--r-- | quantum/audio/audio_avr.c | 812 | ||||
-rw-r--r-- | quantum/audio/audio_chibios.c | 721 | ||||
-rw-r--r-- | quantum/audio/audio_pwm.c | 606 | ||||
-rw-r--r-- | quantum/audio/driver_avr_pwm.h | 17 | ||||
-rw-r--r-- | quantum/audio/driver_avr_pwm_hardware.c | 322 | ||||
-rw-r--r-- | quantum/audio/driver_chibios_dac.h | 126 | ||||
-rw-r--r-- | quantum/audio/driver_chibios_dac_additive.c | 335 | ||||
-rw-r--r-- | quantum/audio/driver_chibios_dac_basic.c | 245 | ||||
-rw-r--r-- | quantum/audio/driver_chibios_pwm.h | 40 | ||||
-rw-r--r-- | quantum/audio/driver_chibios_pwm_hardware.c | 144 | ||||
-rw-r--r-- | quantum/audio/driver_chibios_pwm_software.c | 164 | ||||
-rw-r--r-- | quantum/audio/musical_notes.h | 77 | ||||
-rw-r--r-- | quantum/audio/voices.c | 170 | ||||
-rw-r--r-- | quantum/audio/voices.h | 21 | ||||
-rw-r--r-- | quantum/audio/wave.h | 36 |
17 files changed, 2337 insertions, 2319 deletions
diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c new file mode 100644 index 000000000..46277dd70 --- /dev/null +++ b/quantum/audio/audio.c | |||
@@ -0,0 +1,539 @@ | |||
1 | /* Copyright 2016-2020 Jack Humbert | ||
2 | * Copyright 2020 JohSchneider | ||
3 | |||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | #include "audio.h" | ||
18 | #include "eeconfig.h" | ||
19 | #include "timer.h" | ||
20 | #include "wait.h" | ||
21 | |||
22 | /* audio system: | ||
23 | * | ||
24 | * audio.[ch] takes care of all overall state, tracking the actively playing | ||
25 | * notes/tones; the notes a SONG consists of; | ||
26 | * ... | ||
27 | * = everything audio-related that is platform agnostic | ||
28 | * | ||
29 | * driver_[avr|chibios]_[dac|pwm] take care of the lower hardware dependent parts, | ||
30 | * specific to each platform and the used subsystem/driver to drive | ||
31 | * the output pins/channels with the calculated frequencies for each | ||
32 | * active tone | ||
33 | * as part of this, the driver has to trigger regular state updates by | ||
34 | * calling 'audio_update_state' through some sort of timer - be it a | ||
35 | * dedicated one or piggybacking on for example the timer used to | ||
36 | * generate a pwm signal/clock. | ||
37 | * | ||
38 | * | ||
39 | * A Note on terminology: | ||
40 | * tone, pitch and frequency are used somewhat interchangeably, in a strict Wikipedia-sense: | ||
41 | * "(Musical) tone, a sound characterized by its duration, pitch (=frequency), | ||
42 | * intensity (=volume), and timbre" | ||
43 | * - intensity/volume is currently not handled at all, although the 'dac_additive' driver could do so | ||
44 | * - timbre is handled globally (TODO: only used with the pwm drivers at the moment) | ||
45 | * | ||
46 | * in musical_note.h a 'note' is the combination of a pitch and a duration | ||
47 | * these are used to create SONG arrays; during playback their frequencies | ||
48 | * are handled as single successive tones, while the durations are | ||
49 | * kept track of in 'audio_update_state' | ||
50 | * | ||
51 | * 'voice' as it is used here, equates to a sort of instrument with its own | ||
52 | * characteristics sound and effects | ||
53 | * the audio system as-is deals only with (possibly multiple) tones of one | ||
54 | * instrument/voice at a time (think: chords). since the number of tones that | ||
55 | * can be reproduced depends on the hardware/driver in use: pwm can only | ||
56 | * reproduce one tone per output/speaker; DACs can reproduce/mix multiple | ||
57 | * when doing additive synthesis. | ||
58 | * | ||
59 | * 'duration' can either be in the beats-per-minute related unit found in | ||
60 | * musical_notes.h, OR in ms; keyboards create SONGs with the former, while | ||
61 | * the internal state of the audio system does its calculations with the later - ms | ||
62 | */ | ||
63 | |||
64 | #ifndef AUDIO_TONE_STACKSIZE | ||
65 | # define AUDIO_TONE_STACKSIZE 8 | ||
66 | #endif | ||
67 | uint8_t active_tones = 0; // number of tones pushed onto the stack by audio_play_tone - might be more than the hardware is able to reproduce at any single time | ||
68 | musical_tone_t tones[AUDIO_TONE_STACKSIZE]; // stack of currently active tones | ||
69 | |||
70 | bool playing_melody = false; // playing a SONG? | ||
71 | bool playing_note = false; // or (possibly multiple simultaneous) tones | ||
72 | bool state_changed = false; // global flag, which is set if anything changes with the active_tones | ||
73 | |||
74 | // melody/SONG related state variables | ||
75 | float (*notes_pointer)[][2]; // SONG, an array of MUSICAL_NOTEs | ||
76 | uint16_t notes_count; // length of the notes_pointer array | ||
77 | bool notes_repeat; // PLAY_SONG or PLAY_LOOP? | ||
78 | uint16_t melody_current_note_duration = 0; // duration of the currently playing note from the active melody, in ms | ||
79 | uint8_t note_tempo = TEMPO_DEFAULT; // beats-per-minute | ||
80 | uint16_t current_note = 0; // index into the array at notes_pointer | ||
81 | bool note_resting = false; // if a short pause was introduced between two notes with the same frequency while playing a melody | ||
82 | uint16_t last_timestamp = 0; | ||
83 | |||
84 | #ifdef AUDIO_ENABLE_TONE_MULTIPLEXING | ||
85 | # ifndef AUDIO_MAX_SIMULTANEOUS_TONES | ||
86 | # define AUDIO_MAX_SIMULTANEOUS_TONES 3 | ||
87 | # endif | ||
88 | uint16_t tone_multiplexing_rate = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT; | ||
89 | uint8_t tone_multiplexing_index_shift = 0; // offset used on active-tone array access | ||
90 | #endif | ||
91 | |||
92 | // provided and used by voices.c | ||
93 | extern uint8_t note_timbre; | ||
94 | extern bool glissando; | ||
95 | extern bool vibrato; | ||
96 | extern uint16_t voices_timer; | ||
97 | |||
98 | #ifndef STARTUP_SONG | ||
99 | # define STARTUP_SONG SONG(STARTUP_SOUND) | ||
100 | #endif | ||
101 | #ifndef AUDIO_ON_SONG | ||
102 | # define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND) | ||
103 | #endif | ||
104 | #ifndef AUDIO_OFF_SONG | ||
105 | # define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND) | ||
106 | #endif | ||
107 | float startup_song[][2] = STARTUP_SONG; | ||
108 | float audio_on_song[][2] = AUDIO_ON_SONG; | ||
109 | float audio_off_song[][2] = AUDIO_OFF_SONG; | ||
110 | |||
111 | static bool audio_initialized = false; | ||
112 | static bool audio_driver_stopped = true; | ||
113 | audio_config_t audio_config; | ||
114 | |||
115 | void audio_init() { | ||
116 | if (audio_initialized) { | ||
117 | return; | ||
118 | } | ||
119 | |||
120 | // Check EEPROM | ||
121 | #ifdef EEPROM_ENABLE | ||
122 | if (!eeconfig_is_enabled()) { | ||
123 | eeconfig_init(); | ||
124 | } | ||
125 | audio_config.raw = eeconfig_read_audio(); | ||
126 | #else // EEPROM settings | ||
127 | audio_config.enable = true; | ||
128 | # ifdef AUDIO_CLICKY_ON | ||
129 | audio_config.clicky_enable = true; | ||
130 | # endif | ||
131 | #endif // EEPROM settings | ||
132 | |||
133 | for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) { | ||
134 | tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0}; | ||
135 | } | ||
136 | |||
137 | if (!audio_initialized) { | ||
138 | audio_driver_initialize(); | ||
139 | audio_initialized = true; | ||
140 | } | ||
141 | stop_all_notes(); | ||
142 | } | ||
143 | |||
144 | void audio_startup(void) { | ||
145 | if (audio_config.enable) { | ||
146 | PLAY_SONG(startup_song); | ||
147 | } | ||
148 | |||
149 | last_timestamp = timer_read(); | ||
150 | } | ||
151 | |||
152 | void audio_toggle(void) { | ||
153 | if (audio_config.enable) { | ||
154 | stop_all_notes(); | ||
155 | } | ||
156 | audio_config.enable ^= 1; | ||
157 | eeconfig_update_audio(audio_config.raw); | ||
158 | if (audio_config.enable) { | ||
159 | audio_on_user(); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | void audio_on(void) { | ||
164 | audio_config.enable = 1; | ||
165 | eeconfig_update_audio(audio_config.raw); | ||
166 | audio_on_user(); | ||
167 | PLAY_SONG(audio_on_song); | ||
168 | } | ||
169 | |||
170 | void audio_off(void) { | ||
171 | PLAY_SONG(audio_off_song); | ||
172 | wait_ms(100); | ||
173 | audio_stop_all(); | ||
174 | audio_config.enable = 0; | ||
175 | eeconfig_update_audio(audio_config.raw); | ||
176 | } | ||
177 | |||
178 | bool audio_is_on(void) { return (audio_config.enable != 0); } | ||
179 | |||
180 | void audio_stop_all() { | ||
181 | if (audio_driver_stopped) { | ||
182 | return; | ||
183 | } | ||
184 | |||
185 | active_tones = 0; | ||
186 | |||
187 | audio_driver_stop(); | ||
188 | |||
189 | playing_melody = false; | ||
190 | playing_note = false; | ||
191 | |||
192 | melody_current_note_duration = 0; | ||
193 | |||
194 | for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) { | ||
195 | tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0}; | ||
196 | } | ||
197 | |||
198 | audio_driver_stopped = true; | ||
199 | } | ||
200 | |||
201 | void audio_stop_tone(float pitch) { | ||
202 | if (pitch < 0.0f) { | ||
203 | pitch = -1 * pitch; | ||
204 | } | ||
205 | |||
206 | if (playing_note) { | ||
207 | if (!audio_initialized) { | ||
208 | audio_init(); | ||
209 | } | ||
210 | bool found = false; | ||
211 | for (int i = AUDIO_TONE_STACKSIZE - 1; i >= 0; i--) { | ||
212 | found = (tones[i].pitch == pitch); | ||
213 | if (found) { | ||
214 | tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0}; | ||
215 | for (int j = i; (j < AUDIO_TONE_STACKSIZE - 1); j++) { | ||
216 | tones[j] = tones[j + 1]; | ||
217 | tones[j + 1] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0}; | ||
218 | } | ||
219 | break; | ||
220 | } | ||
221 | } | ||
222 | if (!found) { | ||
223 | return; | ||
224 | } | ||
225 | |||
226 | state_changed = true; | ||
227 | active_tones--; | ||
228 | if (active_tones < 0) active_tones = 0; | ||
229 | #ifdef AUDIO_ENABLE_TONE_MULTIPLEXING | ||
230 | if (tone_multiplexing_index_shift >= active_tones) { | ||
231 | tone_multiplexing_index_shift = 0; | ||
232 | } | ||
233 | #endif | ||
234 | if (active_tones == 0) { | ||
235 | audio_driver_stop(); | ||
236 | audio_driver_stopped = true; | ||
237 | playing_note = false; | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | |||
242 | void audio_play_note(float pitch, uint16_t duration) { | ||
243 | if (!audio_config.enable) { | ||
244 | return; | ||
245 | } | ||
246 | |||
247 | if (!audio_initialized) { | ||
248 | audio_init(); | ||
249 | } | ||
250 | |||
251 | if (pitch < 0.0f) { | ||
252 | pitch = -1 * pitch; | ||
253 | } | ||
254 | |||
255 | // round-robin: shifting out old tones, keeping only unique ones | ||
256 | // if the new frequency is already amongst the active tones, shift it to the top of the stack | ||
257 | bool found = false; | ||
258 | for (int i = active_tones - 1; i >= 0; i--) { | ||
259 | found = (tones[i].pitch == pitch); | ||
260 | if (found) { | ||
261 | for (int j = i; (j < active_tones - 1); j++) { | ||
262 | tones[j] = tones[j + 1]; | ||
263 | tones[j + 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration}; | ||
264 | } | ||
265 | return; // since this frequency played already, the hardware was already started | ||
266 | } | ||
267 | } | ||
268 | |||
269 | // frequency/tone is actually new, so we put it on the top of the stack | ||
270 | active_tones++; | ||
271 | if (active_tones > AUDIO_TONE_STACKSIZE) { | ||
272 | active_tones = AUDIO_TONE_STACKSIZE; | ||
273 | // shift out the oldest tone to make room | ||
274 | for (int i = 0; i < active_tones - 1; i++) { | ||
275 | tones[i] = tones[i + 1]; | ||
276 | } | ||
277 | } | ||
278 | state_changed = true; | ||
279 | playing_note = true; | ||
280 | tones[active_tones - 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration}; | ||
281 | |||
282 | // TODO: needs to be handled per note/tone -> use its timestamp instead? | ||
283 | voices_timer = timer_read(); // reset to zero, for the effects added by voices.c | ||
284 | |||
285 | if (audio_driver_stopped) { | ||
286 | audio_driver_start(); | ||
287 | audio_driver_stopped = false; | ||
288 | } | ||
289 | } | ||
290 | |||
291 | void audio_play_tone(float pitch) { audio_play_note(pitch, 0xffff); } | ||
292 | |||
293 | void audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat) { | ||
294 | if (!audio_config.enable) { | ||
295 | audio_stop_all(); | ||
296 | return; | ||
297 | } | ||
298 | |||
299 | if (!audio_initialized) { | ||
300 | audio_init(); | ||
301 | } | ||
302 | |||
303 | // Cancel note if a note is playing | ||
304 | if (playing_note) audio_stop_all(); | ||
305 | |||
306 | playing_melody = true; | ||
307 | note_resting = false; | ||
308 | |||
309 | notes_pointer = np; | ||
310 | notes_count = n_count; | ||
311 | notes_repeat = n_repeat; | ||
312 | |||
313 | current_note = 0; // note in the melody-array/list at note_pointer | ||
314 | |||
315 | // start first note manually, which also starts the audio_driver | ||
316 | // all following/remaining notes are played by 'audio_update_state' | ||
317 | audio_play_note((*notes_pointer)[current_note][0], audio_duration_to_ms((*notes_pointer)[current_note][1])); | ||
318 | last_timestamp = timer_read(); | ||
319 | melody_current_note_duration = audio_duration_to_ms((*notes_pointer)[current_note][1]); | ||
320 | } | ||
321 | |||
322 | float click[2][2]; | ||
323 | void audio_play_click(uint16_t delay, float pitch, uint16_t duration) { | ||
324 | uint16_t duration_tone = audio_ms_to_duration(duration); | ||
325 | uint16_t duration_delay = audio_ms_to_duration(delay); | ||
326 | |||
327 | if (delay <= 0.0f) { | ||
328 | click[0][0] = pitch; | ||
329 | click[0][1] = duration_tone; | ||
330 | click[1][0] = 0.0f; | ||
331 | click[1][1] = 0.0f; | ||
332 | audio_play_melody(&click, 1, false); | ||
333 | } else { | ||
334 | // first note is a rest/pause | ||
335 | click[0][0] = 0.0f; | ||
336 | click[0][1] = duration_delay; | ||
337 | // second note is the actual click | ||
338 | click[1][0] = pitch; | ||
339 | click[1][1] = duration_tone; | ||
340 | audio_play_melody(&click, 2, false); | ||
341 | } | ||
342 | } | ||
343 | |||
344 | bool audio_is_playing_note(void) { return playing_note; } | ||
345 | |||
346 | bool audio_is_playing_melody(void) { return playing_melody; } | ||
347 | |||
348 | uint8_t audio_get_number_of_active_tones(void) { return active_tones; } | ||
349 | |||
350 | float audio_get_frequency(uint8_t tone_index) { | ||
351 | if (tone_index >= active_tones) { | ||
352 | return 0.0f; | ||
353 | } | ||
354 | return tones[active_tones - tone_index - 1].pitch; | ||
355 | } | ||
356 | |||
357 | float audio_get_processed_frequency(uint8_t tone_index) { | ||
358 | if (tone_index >= active_tones) { | ||
359 | return 0.0f; | ||
360 | } | ||
361 | |||
362 | int8_t index = active_tones - tone_index - 1; | ||
363 | // new tones are stacked on top (= appended at the end), so the most recent/current is MAX-1 | ||
364 | |||
365 | #ifdef AUDIO_ENABLE_TONE_MULTIPLEXING | ||
366 | index = index - tone_multiplexing_index_shift; | ||
367 | if (index < 0) // wrap around | ||
368 | index += active_tones; | ||
369 | #endif | ||
370 | |||
371 | if (tones[index].pitch <= 0.0f) { | ||
372 | return 0.0f; | ||
373 | } | ||
374 | |||
375 | return voice_envelope(tones[index].pitch); | ||
376 | } | ||
377 | |||
378 | bool audio_update_state(void) { | ||
379 | if (!playing_note && !playing_melody) { | ||
380 | return false; | ||
381 | } | ||
382 | |||
383 | bool goto_next_note = false; | ||
384 | uint16_t current_time = timer_read(); | ||
385 | |||
386 | if (playing_melody) { | ||
387 | goto_next_note = timer_elapsed(last_timestamp) >= melody_current_note_duration; | ||
388 | if (goto_next_note) { | ||
389 | uint16_t delta = timer_elapsed(last_timestamp) - melody_current_note_duration; | ||
390 | last_timestamp = current_time; | ||
391 | uint16_t previous_note = current_note; | ||
392 | current_note++; | ||
393 | voices_timer = timer_read(); // reset to zero, for the effects added by voices.c | ||
394 | |||
395 | if (current_note >= notes_count) { | ||
396 | if (notes_repeat) { | ||
397 | current_note = 0; | ||
398 | } else { | ||
399 | audio_stop_all(); | ||
400 | return false; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | if (!note_resting && (*notes_pointer)[previous_note][0] == (*notes_pointer)[current_note][0]) { | ||
405 | note_resting = true; | ||
406 | |||
407 | // special handling for successive notes of the same frequency: | ||
408 | // insert a short pause to separate them audibly | ||
409 | audio_play_note(0.0f, audio_duration_to_ms(2)); | ||
410 | current_note = previous_note; | ||
411 | melody_current_note_duration = audio_duration_to_ms(2); | ||
412 | |||
413 | } else { | ||
414 | note_resting = false; | ||
415 | |||
416 | // TODO: handle glissando here (or remember previous and current tone) | ||
417 | /* there would need to be a freq(here we are) -> freq(next note) | ||
418 | * and do slide/glissando in between problem here is to know which | ||
419 | * frequency on the stack relates to what other? e.g. a melody starts | ||
420 | * tones in a sequence, and stops expiring one, so the most recently | ||
421 | * stopped is the starting point for a glissando to the most recently started? | ||
422 | * how to detect and preserve this relation? | ||
423 | * and what about user input, chords, ...? | ||
424 | */ | ||
425 | |||
426 | // '- delta': Skip forward in the next note's length if we've over shot | ||
427 | // the last, so the overall length of the song is the same | ||
428 | uint16_t duration = audio_duration_to_ms((*notes_pointer)[current_note][1]); | ||
429 | |||
430 | // Skip forward past any completely missed notes | ||
431 | while (delta > duration && current_note < notes_count - 1) { | ||
432 | delta -= duration; | ||
433 | current_note++; | ||
434 | duration = audio_duration_to_ms((*notes_pointer)[current_note][1]); | ||
435 | } | ||
436 | |||
437 | if (delta < duration) { | ||
438 | duration -= delta; | ||
439 | } else { | ||
440 | // Only way to get here is if it is the last note and | ||
441 | // we have completely missed it. Play it for 1ms... | ||
442 | duration = 1; | ||
443 | } | ||
444 | |||
445 | audio_play_note((*notes_pointer)[current_note][0], duration); | ||
446 | melody_current_note_duration = duration; | ||
447 | } | ||
448 | } | ||
449 | } | ||
450 | |||
451 | if (playing_note) { | ||
452 | #ifdef AUDIO_ENABLE_TONE_MULTIPLEXING | ||
453 | tone_multiplexing_index_shift = (int)(current_time / tone_multiplexing_rate) % MIN(AUDIO_MAX_SIMULTANEOUS_TONES, active_tones); | ||
454 | goto_next_note = true; | ||
455 | #endif | ||
456 | if (vibrato || glissando) { | ||
457 | // force update on each cycle, since vibrato shifts the frequency slightly | ||
458 | goto_next_note = true; | ||
459 | } | ||
460 | |||
461 | // housekeeping: stop notes that have no playtime left | ||
462 | for (int i = 0; i < active_tones; i++) { | ||
463 | if ((tones[i].duration != 0xffff) // indefinitely playing notes, started by 'audio_play_tone' | ||
464 | && (tones[i].duration != 0) // 'uninitialized' | ||
465 | ) { | ||
466 | if (timer_elapsed(tones[i].time_started) >= tones[i].duration) { | ||
467 | audio_stop_tone(tones[i].pitch); // also sets 'state_changed=true' | ||
468 | } | ||
469 | } | ||
470 | } | ||
471 | } | ||
472 | |||
473 | // state-changes have a higher priority, always triggering the hardware to update | ||
474 | if (state_changed) { | ||
475 | state_changed = false; | ||
476 | return true; | ||
477 | } | ||
478 | |||
479 | return goto_next_note; | ||
480 | } | ||
481 | |||
482 | // Tone-multiplexing functions | ||
483 | #ifdef AUDIO_ENABLE_TONE_MULTIPLEXING | ||
484 | void audio_set_tone_multiplexing_rate(uint16_t rate) { tone_multiplexing_rate = rate; } | ||
485 | void audio_enable_tone_multiplexing(void) { tone_multiplexing_rate = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT; } | ||
486 | void audio_disable_tone_multiplexing(void) { tone_multiplexing_rate = 0; } | ||
487 | void audio_increase_tone_multiplexing_rate(uint16_t change) { | ||
488 | if ((0xffff - change) > tone_multiplexing_rate) { | ||
489 | tone_multiplexing_rate += change; | ||
490 | } | ||
491 | } | ||
492 | void audio_decrease_tone_multiplexing_rate(uint16_t change) { | ||
493 | if (change <= tone_multiplexing_rate) { | ||
494 | tone_multiplexing_rate -= change; | ||
495 | } | ||
496 | } | ||
497 | #endif | ||
498 | |||
499 | // Tempo functions | ||
500 | |||
501 | void audio_set_tempo(uint8_t tempo) { | ||
502 | if (tempo < 10) note_tempo = 10; | ||
503 | // else if (tempo > 250) | ||
504 | // note_tempo = 250; | ||
505 | else | ||
506 | note_tempo = tempo; | ||
507 | } | ||
508 | |||
509 | void audio_increase_tempo(uint8_t tempo_change) { | ||
510 | if (tempo_change > 255 - note_tempo) | ||
511 | note_tempo = 255; | ||
512 | else | ||
513 | note_tempo += tempo_change; | ||
514 | } | ||
515 | |||
516 | void audio_decrease_tempo(uint8_t tempo_change) { | ||
517 | if (tempo_change >= note_tempo - 10) | ||
518 | note_tempo = 10; | ||
519 | else | ||
520 | note_tempo -= tempo_change; | ||
521 | } | ||
522 | |||
523 | // TODO in the int-math version are some bugs; songs sometimes abruptly end - maybe an issue with the timer/system-tick wrapping around? | ||
524 | uint16_t audio_duration_to_ms(uint16_t duration_bpm) { | ||
525 | #if defined(__AVR__) | ||
526 | // doing int-math saves us some bytes in the overall firmware size, but the intermediate result is less accurate before being cast to/returned as uint | ||
527 | return ((uint32_t)duration_bpm * 60 * 1000) / (64 * note_tempo); | ||
528 | // NOTE: beware of uint16_t overflows when note_tempo is low and/or the duration is long | ||
529 | #else | ||
530 | return ((float)duration_bpm * 60) / (64 * note_tempo) * 1000; | ||
531 | #endif | ||
532 | } | ||
533 | uint16_t audio_ms_to_duration(uint16_t duration_ms) { | ||
534 | #if defined(__AVR__) | ||
535 | return ((uint32_t)duration_ms * 64 * note_tempo) / 60 / 1000; | ||
536 | #else | ||
537 | return ((float)duration_ms * 64 * note_tempo) / 60 / 1000; | ||
538 | #endif | ||
539 | } | ||
diff --git a/quantum/audio/audio.h b/quantum/audio/audio.h index dccf03d5f..56b9158a1 100644 --- a/quantum/audio/audio.h +++ b/quantum/audio/audio.h | |||
@@ -1,4 +1,5 @@ | |||
1 | /* Copyright 2016 Jack Humbert | 1 | /* Copyright 2016-2020 Jack Humbert |
2 | * Copyright 2020 JohSchneider | ||
2 | * | 3 | * |
3 | * This program is free software: you can redistribute it and/or modify | 4 | * This program is free software: you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by | 5 | * it under the terms of the GNU General Public License as published by |
@@ -13,28 +14,30 @@ | |||
13 | * You should have received a copy of the GNU General Public License | 14 | * You should have received a copy of the GNU General Public License |
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
15 | */ | 16 | */ |
16 | |||
17 | #pragma once | 17 | #pragma once |
18 | 18 | ||
19 | #include <stdint.h> | 19 | #include <stdint.h> |
20 | #include <stdbool.h> | 20 | #include <stdbool.h> |
21 | #if defined(__AVR__) | ||
22 | # include <avr/io.h> | ||
23 | #endif | ||
24 | #include "wait.h" | ||
25 | #include "musical_notes.h" | 21 | #include "musical_notes.h" |
26 | #include "song_list.h" | 22 | #include "song_list.h" |
27 | #include "voices.h" | 23 | #include "voices.h" |
28 | #include "quantum.h" | 24 | #include "quantum.h" |
29 | #include <math.h> | 25 | #include <math.h> |
30 | 26 | ||
31 | // Largely untested PWM audio mode (doesn't sound as good) | 27 | #if defined(__AVR__) |
32 | // #define PWM_AUDIO | 28 | # include <avr/io.h> |
33 | 29 | # if defined(AUDIO_DRIVER_PWM) | |
34 | // #define VIBRATO_ENABLE | 30 | # include "driver_avr_pwm.h" |
31 | # endif | ||
32 | #endif | ||
35 | 33 | ||
36 | // Enable vibrato strength/amplitude - slows down ISR too much | 34 | #if defined(PROTOCOL_CHIBIOS) |
37 | // #define VIBRATO_STRENGTH_ENABLE | 35 | # if defined(AUDIO_DRIVER_PWM) |
36 | # include "driver_chibios_pwm.h" | ||
37 | # elif defined(AUDIO_DRIVER_DAC) | ||
38 | # include "driver_chibios_dac.h" | ||
39 | # endif | ||
40 | #endif | ||
38 | 41 | ||
39 | typedef union { | 42 | typedef union { |
40 | uint8_t raw; | 43 | uint8_t raw; |
@@ -45,62 +48,238 @@ typedef union { | |||
45 | }; | 48 | }; |
46 | } audio_config_t; | 49 | } audio_config_t; |
47 | 50 | ||
48 | bool is_audio_on(void); | 51 | // AVR/LUFA has a MIN, arm/chibios does not |
52 | #ifndef MIN | ||
53 | # define MIN(a, b) (((a) < (b)) ? (a) : (b)) | ||
54 | #endif | ||
55 | |||
56 | /* | ||
57 | * a 'musical note' is represented by pitch and duration; a 'musical tone' adds intensity and timbre | ||
58 | * https://en.wikipedia.org/wiki/Musical_tone | ||
59 | * "A musical tone is characterized by its duration, pitch, intensity (or loudness), and timbre (or quality)" | ||
60 | */ | ||
61 | typedef struct { | ||
62 | uint16_t time_started; // timestamp the tone/note was started, system time runs with 1ms resolution -> 16bit timer overflows every ~64 seconds, long enough under normal circumstances; but might be too soon for long-duration notes when the note_tempo is set to a very low value | ||
63 | float pitch; // aka frequency, in Hz | ||
64 | uint16_t duration; // in ms, converted from the musical_notes.h unit which has 64parts to a beat, factoring in the current tempo in beats-per-minute | ||
65 | // float intensity; // aka volume [0,1] TODO: not used at the moment; pwm drivers can't handle it | ||
66 | // uint8_t timbre; // range: [0,100] TODO: this currently kept track of globally, should we do this per tone instead? | ||
67 | } musical_tone_t; | ||
68 | |||
69 | // public interface | ||
70 | |||
71 | /** | ||
72 | * @brief one-time initialization called by quantum/quantum.c | ||
73 | * @details usually done lazy, when some tones are to be played | ||
74 | * | ||
75 | * @post audio system (and hardware) initialized and ready to play tones | ||
76 | */ | ||
77 | void audio_init(void); | ||
78 | void audio_startup(void); | ||
79 | |||
80 | /** | ||
81 | * @brief en-/disable audio output, save this choice to the eeprom | ||
82 | */ | ||
49 | void audio_toggle(void); | 83 | void audio_toggle(void); |
84 | /** | ||
85 | * @brief enable audio output, save this choice to the eeprom | ||
86 | */ | ||
50 | void audio_on(void); | 87 | void audio_on(void); |
88 | /** | ||
89 | * @brief disable audio output, save this choice to the eeprom | ||
90 | */ | ||
51 | void audio_off(void); | 91 | void audio_off(void); |
92 | /** | ||
93 | * @brief query the if audio output is enabled | ||
94 | */ | ||
95 | bool audio_is_on(void); | ||
52 | 96 | ||
53 | // Vibrato rate functions | 97 | /** |
98 | * @brief start playback of a tone with the given frequency and duration | ||
99 | * | ||
100 | * @details starts the playback of a given note, which is automatically stopped | ||
101 | * at the the end of its duration = fire&forget | ||
102 | * | ||
103 | * @param[in] pitch frequency of the tone be played | ||
104 | * @param[in] duration in milliseconds, use 'audio_duration_to_ms' to convert | ||
105 | * from the musical_notes.h unit to ms | ||
106 | */ | ||
107 | void audio_play_note(float pitch, uint16_t duration); | ||
108 | // TODO: audio_play_note(float pitch, uint16_t duration, float intensity, float timbre); | ||
109 | // audio_play_note_with_instrument ifdef AUDIO_ENABLE_VOICES | ||
54 | 110 | ||
55 | #ifdef VIBRATO_ENABLE | 111 | /** |
112 | * @brief start playback of a tone with the given frequency | ||
113 | * | ||
114 | * @details the 'frequency' is put on-top the internal stack of active tones, | ||
115 | * as a new tone with indefinite duration. this tone is played by | ||
116 | * the hardware until a call to 'audio_stop_tone'. | ||
117 | * should a tone with that frequency already be active, its entry | ||
118 | * is put on the top of said internal stack - so no duplicate | ||
119 | * entries are kept. | ||
120 | * 'hardware_start' is called upon the first note. | ||
121 | * | ||
122 | * @param[in] pitch frequency of the tone be played | ||
123 | */ | ||
124 | void audio_play_tone(float pitch); | ||
56 | 125 | ||
57 | void set_vibrato_rate(float rate); | 126 | /** |
58 | void increase_vibrato_rate(float change); | 127 | * @brief stop a given tone/frequency |
59 | void decrease_vibrato_rate(float change); | 128 | * |
129 | * @details removes a tone matching the given frequency from the internal | ||
130 | * playback stack | ||
131 | * the hardware is stopped in case this was the last/only frequency | ||
132 | * being played. | ||
133 | * | ||
134 | * @param[in] pitch tone/frequency to be stopped | ||
135 | */ | ||
136 | void audio_stop_tone(float pitch); | ||
60 | 137 | ||
61 | # ifdef VIBRATO_STRENGTH_ENABLE | 138 | /** |
139 | * @brief play a melody | ||
140 | * | ||
141 | * @details starts playback of a melody passed in from a SONG definition - an | ||
142 | * array of {pitch, duration} float-tuples | ||
143 | * | ||
144 | * @param[in] np note-pointer to the SONG array | ||
145 | * @param[in] n_count number of MUSICAL_NOTES of the SONG | ||
146 | * @param[in] n_repeat false for onetime, true for looped playback | ||
147 | */ | ||
148 | void audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat); | ||
62 | 149 | ||
63 | void set_vibrato_strength(float strength); | 150 | /** |
64 | void increase_vibrato_strength(float change); | 151 | * @brief play a short tone of a specific frequency to emulate a 'click' |
65 | void decrease_vibrato_strength(float change); | 152 | * |
153 | * @details constructs a two-note melody (one pause plus a note) and plays it through | ||
154 | * audio_play_melody. very short durations might not quite work due to | ||
155 | * hardware limitations (DAC: added pulses from zero-crossing feature;...) | ||
156 | * | ||
157 | * @param[in] delay in milliseconds, length for the pause before the pulses, can be zero | ||
158 | * @param[in] pitch | ||
159 | * @param[in] duration in milliseconds, length of the 'click' | ||
160 | */ | ||
161 | void audio_play_click(uint16_t delay, float pitch, uint16_t duration); | ||
66 | 162 | ||
67 | # endif | 163 | /** |
164 | * @brief stops all playback | ||
165 | * | ||
166 | * @details stops playback of both a melody as well as single tones, resetting | ||
167 | * the internal state | ||
168 | */ | ||
169 | void audio_stop_all(void); | ||
68 | 170 | ||
69 | #endif | 171 | /** |
172 | * @brief query if one/multiple tones are playing | ||
173 | */ | ||
174 | bool audio_is_playing_note(void); | ||
70 | 175 | ||
71 | // Polyphony functions | 176 | /** |
177 | * @brief query if a melody/SONG is playing | ||
178 | */ | ||
179 | bool audio_is_playing_melody(void); | ||
72 | 180 | ||
73 | void set_polyphony_rate(float rate); | 181 | // These macros are used to allow audio_play_melody to play an array of indeterminate |
74 | void enable_polyphony(void); | 182 | // length. This works around the limitation of C's sizeof operation on pointers. |
75 | void disable_polyphony(void); | 183 | // The global float array for the song must be used here. |
76 | void increase_polyphony_rate(float change); | 184 | #define NOTE_ARRAY_SIZE(x) ((int16_t)(sizeof(x) / (sizeof(x[0])))) |
77 | void decrease_polyphony_rate(float change); | ||
78 | 185 | ||
79 | void set_timbre(float timbre); | 186 | /** |
80 | void set_tempo(uint8_t tempo); | 187 | * @brief convenience macro, to play a melody/SONG once |
188 | */ | ||
189 | #define PLAY_SONG(note_array) audio_play_melody(¬e_array, NOTE_ARRAY_SIZE((note_array)), false) | ||
190 | // TODO: a 'song' is a melody plus singing/vocals -> PLAY_MELODY | ||
191 | /** | ||
192 | * @brief convenience macro, to play a melody/SONG in a loop, until stopped by 'audio_stop_all' | ||
193 | */ | ||
194 | #define PLAY_LOOP(note_array) audio_play_melody(¬e_array, NOTE_ARRAY_SIZE((note_array)), true) | ||
81 | 195 | ||
82 | void increase_tempo(uint8_t tempo_change); | 196 | // Tone-Multiplexing functions |
83 | void decrease_tempo(uint8_t tempo_change); | 197 | // this feature only makes sense for hardware setups which can't do proper |
198 | // audio-wave synthesis = have no DAC and need to use PWM for tone generation | ||
199 | #ifdef AUDIO_ENABLE_TONE_MULTIPLEXING | ||
200 | # ifndef AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT | ||
201 | # define AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT 0 | ||
202 | // 0=off, good starting value is 4; the lower the value the higher the cpu-load | ||
203 | # endif | ||
204 | void audio_set_tone_multiplexing_rate(uint16_t rate); | ||
205 | void audio_enable_tone_multiplexing(void); | ||
206 | void audio_disable_tone_multiplexing(void); | ||
207 | void audio_increase_tone_multiplexing_rate(uint16_t change); | ||
208 | void audio_decrease_tone_multiplexing_rate(uint16_t change); | ||
209 | #endif | ||
210 | |||
211 | // Tempo functions | ||
212 | |||
213 | void audio_set_tempo(uint8_t tempo); | ||
214 | void audio_increase_tempo(uint8_t tempo_change); | ||
215 | void audio_decrease_tempo(uint8_t tempo_change); | ||
216 | |||
217 | // conversion macros, from 64parts-to-a-beat to milliseconds and back | ||
218 | uint16_t audio_duration_to_ms(uint16_t duration_bpm); | ||
219 | uint16_t audio_ms_to_duration(uint16_t duration_ms); | ||
84 | 220 | ||
85 | void audio_init(void); | ||
86 | void audio_startup(void); | 221 | void audio_startup(void); |
87 | 222 | ||
88 | #ifdef PWM_AUDIO | 223 | // hardware interface |
89 | void play_sample(uint8_t* s, uint16_t l, bool r); | ||
90 | #endif | ||
91 | void play_note(float freq, int vol); | ||
92 | void stop_note(float freq); | ||
93 | void stop_all_notes(void); | ||
94 | void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat); | ||
95 | 224 | ||
96 | #define SCALE \ | 225 | // implementation in the driver_avr/arm_* respective parts |
97 | (int8_t[]) { 0 + (12 * 0), 2 + (12 * 0), 4 + (12 * 0), 5 + (12 * 0), 7 + (12 * 0), 9 + (12 * 0), 11 + (12 * 0), 0 + (12 * 1), 2 + (12 * 1), 4 + (12 * 1), 5 + (12 * 1), 7 + (12 * 1), 9 + (12 * 1), 11 + (12 * 1), 0 + (12 * 2), 2 + (12 * 2), 4 + (12 * 2), 5 + (12 * 2), 7 + (12 * 2), 9 + (12 * 2), 11 + (12 * 2), 0 + (12 * 3), 2 + (12 * 3), 4 + (12 * 3), 5 + (12 * 3), 7 + (12 * 3), 9 + (12 * 3), 11 + (12 * 3), 0 + (12 * 4), 2 + (12 * 4), 4 + (12 * 4), 5 + (12 * 4), 7 + (12 * 4), 9 + (12 * 4), 11 + (12 * 4), } | 226 | void audio_driver_initialize(void); |
227 | void audio_driver_start(void); | ||
228 | void audio_driver_stop(void); | ||
98 | 229 | ||
99 | // These macros are used to allow play_notes to play an array of indeterminate | 230 | /** |
100 | // length. This works around the limitation of C's sizeof operation on pointers. | 231 | * @brief get the number of currently active tones |
101 | // The global float array for the song must be used here. | 232 | * @return number, 0=none active |
102 | #define NOTE_ARRAY_SIZE(x) ((int16_t)(sizeof(x) / (sizeof(x[0])))) | 233 | */ |
103 | #define PLAY_SONG(note_array) play_notes(¬e_array, NOTE_ARRAY_SIZE((note_array)), false) | 234 | uint8_t audio_get_number_of_active_tones(void); |
104 | #define PLAY_LOOP(note_array) play_notes(¬e_array, NOTE_ARRAY_SIZE((note_array)), true) | 235 | |
236 | /** | ||
237 | * @brief access to the raw/unprocessed frequency for a specific tone | ||
238 | * @details each active tone has a frequency associated with it, which | ||
239 | * the internal state keeps track of, and is usually influenced | ||
240 | * by various effects | ||
241 | * @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the | ||
242 | * first being the most recent and each increment yielding the next | ||
243 | * older one | ||
244 | * @return a positive frequency, in Hz; or zero if the tone is a pause | ||
245 | */ | ||
246 | float audio_get_frequency(uint8_t tone_index); | ||
247 | |||
248 | /** | ||
249 | * @brief calculate and return the frequency for the requested tone | ||
250 | * @details effects like glissando, vibrato, ... are post-processed onto the | ||
251 | * each active tones 'base'-frequency; this function returns the | ||
252 | * post-processed result. | ||
253 | * @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the | ||
254 | * first being the most recent and each increment yielding the next | ||
255 | * older one | ||
256 | * @return a positive frequency, in Hz; or zero if the tone is a pause | ||
257 | */ | ||
258 | float audio_get_processed_frequency(uint8_t tone_index); | ||
259 | |||
260 | /** | ||
261 | * @brief update audio internal state: currently playing and active tones,... | ||
262 | * @details This function is intended to be called by the audio-hardware | ||
263 | * specific implementation on a somewhat regular basis while a SONG | ||
264 | * or notes (pitch+duration) are playing to 'advance' the internal | ||
265 | * state (current playing notes, position in the melody, ...) | ||
266 | * | ||
267 | * @return true if something changed in the currently active tones, which the | ||
268 | * hardware might need to react to | ||
269 | */ | ||
270 | bool audio_update_state(void); | ||
271 | |||
272 | // legacy and back-warts compatibility stuff | ||
273 | |||
274 | #define is_audio_on() audio_is_on() | ||
275 | #define is_playing_notes() audio_is_playing_melody() | ||
276 | #define is_playing_note() audio_is_playing_note() | ||
277 | #define stop_all_notes() audio_stop_all() | ||
278 | #define stop_note(f) audio_stop_tone(f) | ||
279 | #define play_note(f, v) audio_play_tone(f) | ||
105 | 280 | ||
106 | bool is_playing_notes(void); | 281 | #define set_timbre(t) voice_set_timbre(t) |
282 | #define set_tempo(t) audio_set_tempo(t) | ||
283 | #define increase_tempo(t) audio_increase_tempo(t) | ||
284 | #define decrease_tempo(t) audio_decrease_tempo(t) | ||
285 | // vibrato functions are not used in any keyboards | ||
diff --git a/quantum/audio/audio_avr.c b/quantum/audio/audio_avr.c deleted file mode 100644 index 1bac43bb4..000000000 --- a/quantum/audio/audio_avr.c +++ /dev/null | |||
@@ -1,812 +0,0 @@ | |||
1 | /* Copyright 2016 Jack Humbert | ||
2 | * | ||
3 | * This program is free software: you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License as published by | ||
5 | * the Free Software Foundation, either version 2 of the License, or | ||
6 | * (at your option) any later version. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <stdio.h> | ||
18 | #include <string.h> | ||
19 | //#include <math.h> | ||
20 | #if defined(__AVR__) | ||
21 | # include <avr/pgmspace.h> | ||
22 | # include <avr/interrupt.h> | ||
23 | # include <avr/io.h> | ||
24 | #endif | ||
25 | #include "print.h" | ||
26 | #include "audio.h" | ||
27 | #include "keymap.h" | ||
28 | #include "wait.h" | ||
29 | |||
30 | #include "eeconfig.h" | ||
31 | |||
32 | #define CPU_PRESCALER 8 | ||
33 | |||
34 | // ----------------------------------------------------------------------------- | ||
35 | // Timer Abstractions | ||
36 | // ----------------------------------------------------------------------------- | ||
37 | |||
38 | // Currently we support timers 1 and 3 used at the sime time, channels A-C, | ||
39 | // pins PB5, PB6, PB7, PC4, PC5, and PC6 | ||
40 | #if defined(C6_AUDIO) | ||
41 | # define CPIN_AUDIO | ||
42 | # define CPIN_SET_DIRECTION DDRC |= _BV(PORTC6); | ||
43 | # define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); | ||
44 | # define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) | ||
45 | # define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) | ||
46 | # define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); | ||
47 | # define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); | ||
48 | # define TIMER_3_PERIOD ICR3 | ||
49 | # define TIMER_3_DUTY_CYCLE OCR3A | ||
50 | # define TIMER3_AUDIO_vect TIMER3_COMPA_vect | ||
51 | #endif | ||
52 | #if defined(C5_AUDIO) | ||
53 | # define CPIN_AUDIO | ||
54 | # define CPIN_SET_DIRECTION DDRC |= _BV(PORTC5); | ||
55 | # define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3B1) | (0 << COM3B0) | (1 << WGM31) | (0 << WGM30); | ||
56 | # define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3B) | ||
57 | # define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3B) | ||
58 | # define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3B1); | ||
59 | # define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3B1) | _BV(COM3B0)); | ||
60 | # define TIMER_3_PERIOD ICR3 | ||
61 | # define TIMER_3_DUTY_CYCLE OCR3B | ||
62 | # define TIMER3_AUDIO_vect TIMER3_COMPB_vect | ||
63 | #endif | ||
64 | #if defined(C4_AUDIO) | ||
65 | # define CPIN_AUDIO | ||
66 | # define CPIN_SET_DIRECTION DDRC |= _BV(PORTC4); | ||
67 | # define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3C1) | (0 << COM3C0) | (1 << WGM31) | (0 << WGM30); | ||
68 | # define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3C) | ||
69 | # define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3C) | ||
70 | # define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3C1); | ||
71 | # define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3C1) | _BV(COM3C0)); | ||
72 | # define TIMER_3_PERIOD ICR3 | ||
73 | # define TIMER_3_DUTY_CYCLE OCR3C | ||
74 | # define TIMER3_AUDIO_vect TIMER3_COMPC_vect | ||
75 | #endif | ||
76 | |||
77 | #if defined(B5_AUDIO) | ||
78 | # define BPIN_AUDIO | ||
79 | # define BPIN_SET_DIRECTION DDRB |= _BV(PORTB5); | ||
80 | # define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10); | ||
81 | # define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A) | ||
82 | # define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A) | ||
83 | # define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1); | ||
84 | # define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0)); | ||
85 | # define TIMER_1_PERIOD ICR1 | ||
86 | # define TIMER_1_DUTY_CYCLE OCR1A | ||
87 | # define TIMER1_AUDIO_vect TIMER1_COMPA_vect | ||
88 | #endif | ||
89 | #if defined(B6_AUDIO) | ||
90 | # define BPIN_AUDIO | ||
91 | # define BPIN_SET_DIRECTION DDRB |= _BV(PORTB6); | ||
92 | # define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10); | ||
93 | # define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1B) | ||
94 | # define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1B) | ||
95 | # define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1B1); | ||
96 | # define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1B1) | _BV(COM1B0)); | ||
97 | # define TIMER_1_PERIOD ICR1 | ||
98 | # define TIMER_1_DUTY_CYCLE OCR1B | ||
99 | # define TIMER1_AUDIO_vect TIMER1_COMPB_vect | ||
100 | #endif | ||
101 | #if defined(B7_AUDIO) | ||
102 | # define BPIN_AUDIO | ||
103 | # define BPIN_SET_DIRECTION DDRB |= _BV(PORTB7); | ||
104 | # define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1C1) | (0 << COM1C0) | (1 << WGM11) | (0 << WGM10); | ||
105 | # define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1C) | ||
106 | # define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1C) | ||
107 | # define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1C1); | ||
108 | # define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0)); | ||
109 | # define TIMER_1_PERIOD ICR1 | ||
110 | # define TIMER_1_DUTY_CYCLE OCR1C | ||
111 | # define TIMER1_AUDIO_vect TIMER1_COMPC_vect | ||
112 | #endif | ||
113 | |||
114 | #if !defined(BPIN_AUDIO) && !defined(CPIN_AUDIO) | ||
115 | # error "Audio feature enabled, but no suitable pin selected - see docs/feature_audio.md under the AVR settings for available options." | ||
116 | #endif | ||
117 | |||
118 | // ----------------------------------------------------------------------------- | ||
119 | |||
120 | int voices = 0; | ||
121 | int voice_place = 0; | ||
122 | float frequency = 0; | ||
123 | float frequency_alt = 0; | ||
124 | int volume = 0; | ||
125 | long position = 0; | ||
126 | |||
127 | float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
128 | int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
129 | bool sliding = false; | ||
130 | |||
131 | float place = 0; | ||
132 | |||
133 | uint8_t* sample; | ||
134 | uint16_t sample_length = 0; | ||
135 | |||
136 | bool playing_notes = false; | ||
137 | bool playing_note = false; | ||
138 | float note_frequency = 0; | ||
139 | float note_length = 0; | ||
140 | uint8_t note_tempo = TEMPO_DEFAULT; | ||
141 | float note_timbre = TIMBRE_DEFAULT; | ||
142 | uint16_t note_position = 0; | ||
143 | float (*notes_pointer)[][2]; | ||
144 | uint16_t notes_count; | ||
145 | bool notes_repeat; | ||
146 | bool note_resting = false; | ||
147 | |||
148 | uint16_t current_note = 0; | ||
149 | uint8_t rest_counter = 0; | ||
150 | |||
151 | #ifdef VIBRATO_ENABLE | ||
152 | float vibrato_counter = 0; | ||
153 | float vibrato_strength = .5; | ||
154 | float vibrato_rate = 0.125; | ||
155 | #endif | ||
156 | |||
157 | float polyphony_rate = 0; | ||
158 | |||
159 | static bool audio_initialized = false; | ||
160 | |||
161 | audio_config_t audio_config; | ||
162 | |||
163 | uint16_t envelope_index = 0; | ||
164 | bool glissando = true; | ||
165 | |||
166 | #ifndef STARTUP_SONG | ||
167 | # define STARTUP_SONG SONG(STARTUP_SOUND) | ||
168 | #endif | ||
169 | #ifndef AUDIO_ON_SONG | ||
170 | # define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND) | ||
171 | #endif | ||
172 | #ifndef AUDIO_OFF_SONG | ||
173 | # define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND) | ||
174 | #endif | ||
175 | float startup_song[][2] = STARTUP_SONG; | ||
176 | float audio_on_song[][2] = AUDIO_ON_SONG; | ||
177 | float audio_off_song[][2] = AUDIO_OFF_SONG; | ||
178 | |||
179 | void audio_init() { | ||
180 | // Check EEPROM | ||
181 | if (!eeconfig_is_enabled()) { | ||
182 | eeconfig_init(); | ||
183 | } | ||
184 | audio_config.raw = eeconfig_read_audio(); | ||
185 | |||
186 | if (!audio_initialized) { | ||
187 | // Set audio ports as output | ||
188 | #ifdef CPIN_AUDIO | ||
189 | CPIN_SET_DIRECTION | ||
190 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
191 | #endif | ||
192 | #ifdef BPIN_AUDIO | ||
193 | BPIN_SET_DIRECTION | ||
194 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
195 | #endif | ||
196 | |||
197 | // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B | ||
198 | // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation | ||
199 | // OC3A -- PC6 | ||
200 | // OC3B -- PC5 | ||
201 | // OC3C -- PC4 | ||
202 | // OC1A -- PB5 | ||
203 | // OC1B -- PB6 | ||
204 | // OC1C -- PB7 | ||
205 | |||
206 | // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A) | ||
207 | // OCR3A - PC6 | ||
208 | // OCR3B - PC5 | ||
209 | // OCR3C - PC4 | ||
210 | // OCR1A - PB5 | ||
211 | // OCR1B - PB6 | ||
212 | // OCR1C - PB7 | ||
213 | |||
214 | // Clock Select (CS3n) = 0b010 = Clock / 8 | ||
215 | #ifdef CPIN_AUDIO | ||
216 | INIT_AUDIO_COUNTER_3 | ||
217 | TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); | ||
218 | TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER)); | ||
219 | TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre); | ||
220 | #endif | ||
221 | #ifdef BPIN_AUDIO | ||
222 | INIT_AUDIO_COUNTER_1 | ||
223 | TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10); | ||
224 | TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER)); | ||
225 | TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre); | ||
226 | #endif | ||
227 | |||
228 | audio_initialized = true; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | void audio_startup() { | ||
233 | if (audio_config.enable) { | ||
234 | PLAY_SONG(startup_song); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | void stop_all_notes() { | ||
239 | dprintf("audio stop all notes"); | ||
240 | |||
241 | if (!audio_initialized) { | ||
242 | audio_init(); | ||
243 | } | ||
244 | voices = 0; | ||
245 | |||
246 | #ifdef CPIN_AUDIO | ||
247 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
248 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
249 | #endif | ||
250 | |||
251 | #ifdef BPIN_AUDIO | ||
252 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
253 | DISABLE_AUDIO_COUNTER_1_OUTPUT; | ||
254 | #endif | ||
255 | |||
256 | playing_notes = false; | ||
257 | playing_note = false; | ||
258 | frequency = 0; | ||
259 | frequency_alt = 0; | ||
260 | volume = 0; | ||
261 | |||
262 | for (uint8_t i = 0; i < 8; i++) { | ||
263 | frequencies[i] = 0; | ||
264 | volumes[i] = 0; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | void stop_note(float freq) { | ||
269 | dprintf("audio stop note freq=%d", (int)freq); | ||
270 | |||
271 | if (playing_note) { | ||
272 | if (!audio_initialized) { | ||
273 | audio_init(); | ||
274 | } | ||
275 | for (int i = 7; i >= 0; i--) { | ||
276 | if (frequencies[i] == freq) { | ||
277 | frequencies[i] = 0; | ||
278 | volumes[i] = 0; | ||
279 | for (int j = i; (j < 7); j++) { | ||
280 | frequencies[j] = frequencies[j + 1]; | ||
281 | frequencies[j + 1] = 0; | ||
282 | volumes[j] = volumes[j + 1]; | ||
283 | volumes[j + 1] = 0; | ||
284 | } | ||
285 | break; | ||
286 | } | ||
287 | } | ||
288 | voices--; | ||
289 | if (voices < 0) voices = 0; | ||
290 | if (voice_place >= voices) { | ||
291 | voice_place = 0; | ||
292 | } | ||
293 | if (voices == 0) { | ||
294 | #ifdef CPIN_AUDIO | ||
295 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
296 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
297 | #endif | ||
298 | #ifdef BPIN_AUDIO | ||
299 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
300 | DISABLE_AUDIO_COUNTER_1_OUTPUT; | ||
301 | #endif | ||
302 | frequency = 0; | ||
303 | frequency_alt = 0; | ||
304 | volume = 0; | ||
305 | playing_note = false; | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
310 | #ifdef VIBRATO_ENABLE | ||
311 | |||
312 | float mod(float a, int b) { | ||
313 | float r = fmod(a, b); | ||
314 | return r < 0 ? r + b : r; | ||
315 | } | ||
316 | |||
317 | float vibrato(float average_freq) { | ||
318 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
319 | float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); | ||
320 | # else | ||
321 | float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; | ||
322 | # endif | ||
323 | vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); | ||
324 | return vibrated_freq; | ||
325 | } | ||
326 | |||
327 | #endif | ||
328 | |||
329 | #ifdef CPIN_AUDIO | ||
330 | ISR(TIMER3_AUDIO_vect) { | ||
331 | float freq; | ||
332 | |||
333 | if (playing_note) { | ||
334 | if (voices > 0) { | ||
335 | # ifdef BPIN_AUDIO | ||
336 | float freq_alt = 0; | ||
337 | if (voices > 1) { | ||
338 | if (polyphony_rate == 0) { | ||
339 | if (glissando) { | ||
340 | if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) { | ||
341 | frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2); | ||
342 | } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440 / frequencies[voices - 2] / 12 / 2)) { | ||
343 | frequency_alt = frequency_alt * pow(2, -440 / frequency_alt / 12 / 2); | ||
344 | } else { | ||
345 | frequency_alt = frequencies[voices - 2]; | ||
346 | } | ||
347 | } else { | ||
348 | frequency_alt = frequencies[voices - 2]; | ||
349 | } | ||
350 | |||
351 | # ifdef VIBRATO_ENABLE | ||
352 | if (vibrato_strength > 0) { | ||
353 | freq_alt = vibrato(frequency_alt); | ||
354 | } else { | ||
355 | freq_alt = frequency_alt; | ||
356 | } | ||
357 | # else | ||
358 | freq_alt = frequency_alt; | ||
359 | # endif | ||
360 | } | ||
361 | |||
362 | if (envelope_index < 65535) { | ||
363 | envelope_index++; | ||
364 | } | ||
365 | |||
366 | freq_alt = voice_envelope(freq_alt); | ||
367 | |||
368 | if (freq_alt < 30.517578125) { | ||
369 | freq_alt = 30.52; | ||
370 | } | ||
371 | |||
372 | TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq_alt * CPU_PRESCALER)); | ||
373 | TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq_alt * CPU_PRESCALER)) * note_timbre); | ||
374 | } | ||
375 | # endif | ||
376 | |||
377 | if (polyphony_rate > 0) { | ||
378 | if (voices > 1) { | ||
379 | voice_place %= voices; | ||
380 | if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { | ||
381 | voice_place = (voice_place + 1) % voices; | ||
382 | place = 0.0; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | # ifdef VIBRATO_ENABLE | ||
387 | if (vibrato_strength > 0) { | ||
388 | freq = vibrato(frequencies[voice_place]); | ||
389 | } else { | ||
390 | freq = frequencies[voice_place]; | ||
391 | } | ||
392 | # else | ||
393 | freq = frequencies[voice_place]; | ||
394 | # endif | ||
395 | } else { | ||
396 | if (glissando) { | ||
397 | if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { | ||
398 | frequency = frequency * pow(2, 440 / frequency / 12 / 2); | ||
399 | } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { | ||
400 | frequency = frequency * pow(2, -440 / frequency / 12 / 2); | ||
401 | } else { | ||
402 | frequency = frequencies[voices - 1]; | ||
403 | } | ||
404 | } else { | ||
405 | frequency = frequencies[voices - 1]; | ||
406 | } | ||
407 | |||
408 | # ifdef VIBRATO_ENABLE | ||
409 | if (vibrato_strength > 0) { | ||
410 | freq = vibrato(frequency); | ||
411 | } else { | ||
412 | freq = frequency; | ||
413 | } | ||
414 | # else | ||
415 | freq = frequency; | ||
416 | # endif | ||
417 | } | ||
418 | |||
419 | if (envelope_index < 65535) { | ||
420 | envelope_index++; | ||
421 | } | ||
422 | |||
423 | freq = voice_envelope(freq); | ||
424 | |||
425 | if (freq < 30.517578125) { | ||
426 | freq = 30.52; | ||
427 | } | ||
428 | |||
429 | TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
430 | TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); | ||
431 | } | ||
432 | } | ||
433 | |||
434 | if (playing_notes) { | ||
435 | if (note_frequency > 0) { | ||
436 | # ifdef VIBRATO_ENABLE | ||
437 | if (vibrato_strength > 0) { | ||
438 | freq = vibrato(note_frequency); | ||
439 | } else { | ||
440 | freq = note_frequency; | ||
441 | } | ||
442 | # else | ||
443 | freq = note_frequency; | ||
444 | # endif | ||
445 | |||
446 | if (envelope_index < 65535) { | ||
447 | envelope_index++; | ||
448 | } | ||
449 | freq = voice_envelope(freq); | ||
450 | |||
451 | TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
452 | TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); | ||
453 | } else { | ||
454 | TIMER_3_PERIOD = 0; | ||
455 | TIMER_3_DUTY_CYCLE = 0; | ||
456 | } | ||
457 | |||
458 | note_position++; | ||
459 | bool end_of_note = false; | ||
460 | if (TIMER_3_PERIOD > 0) { | ||
461 | if (!note_resting) | ||
462 | end_of_note = (note_position >= (note_length / TIMER_3_PERIOD * 0xFFFF - 1)); | ||
463 | else | ||
464 | end_of_note = (note_position >= (note_length)); | ||
465 | } else { | ||
466 | end_of_note = (note_position >= (note_length)); | ||
467 | } | ||
468 | |||
469 | if (end_of_note) { | ||
470 | current_note++; | ||
471 | if (current_note >= notes_count) { | ||
472 | if (notes_repeat) { | ||
473 | current_note = 0; | ||
474 | } else { | ||
475 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
476 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
477 | playing_notes = false; | ||
478 | return; | ||
479 | } | ||
480 | } | ||
481 | if (!note_resting) { | ||
482 | note_resting = true; | ||
483 | current_note--; | ||
484 | if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { | ||
485 | note_frequency = 0; | ||
486 | note_length = 1; | ||
487 | } else { | ||
488 | note_frequency = (*notes_pointer)[current_note][0]; | ||
489 | note_length = 1; | ||
490 | } | ||
491 | } else { | ||
492 | note_resting = false; | ||
493 | envelope_index = 0; | ||
494 | note_frequency = (*notes_pointer)[current_note][0]; | ||
495 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
496 | } | ||
497 | |||
498 | note_position = 0; | ||
499 | } | ||
500 | } | ||
501 | |||
502 | if (!audio_config.enable) { | ||
503 | playing_notes = false; | ||
504 | playing_note = false; | ||
505 | } | ||
506 | } | ||
507 | #endif | ||
508 | |||
509 | #ifdef BPIN_AUDIO | ||
510 | ISR(TIMER1_AUDIO_vect) { | ||
511 | # if defined(BPIN_AUDIO) && !defined(CPIN_AUDIO) | ||
512 | float freq = 0; | ||
513 | |||
514 | if (playing_note) { | ||
515 | if (voices > 0) { | ||
516 | if (polyphony_rate > 0) { | ||
517 | if (voices > 1) { | ||
518 | voice_place %= voices; | ||
519 | if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { | ||
520 | voice_place = (voice_place + 1) % voices; | ||
521 | place = 0.0; | ||
522 | } | ||
523 | } | ||
524 | |||
525 | # ifdef VIBRATO_ENABLE | ||
526 | if (vibrato_strength > 0) { | ||
527 | freq = vibrato(frequencies[voice_place]); | ||
528 | } else { | ||
529 | freq = frequencies[voice_place]; | ||
530 | } | ||
531 | # else | ||
532 | freq = frequencies[voice_place]; | ||
533 | # endif | ||
534 | } else { | ||
535 | if (glissando) { | ||
536 | if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { | ||
537 | frequency = frequency * pow(2, 440 / frequency / 12 / 2); | ||
538 | } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { | ||
539 | frequency = frequency * pow(2, -440 / frequency / 12 / 2); | ||
540 | } else { | ||
541 | frequency = frequencies[voices - 1]; | ||
542 | } | ||
543 | } else { | ||
544 | frequency = frequencies[voices - 1]; | ||
545 | } | ||
546 | |||
547 | # ifdef VIBRATO_ENABLE | ||
548 | if (vibrato_strength > 0) { | ||
549 | freq = vibrato(frequency); | ||
550 | } else { | ||
551 | freq = frequency; | ||
552 | } | ||
553 | # else | ||
554 | freq = frequency; | ||
555 | # endif | ||
556 | } | ||
557 | |||
558 | if (envelope_index < 65535) { | ||
559 | envelope_index++; | ||
560 | } | ||
561 | |||
562 | freq = voice_envelope(freq); | ||
563 | |||
564 | if (freq < 30.517578125) { | ||
565 | freq = 30.52; | ||
566 | } | ||
567 | |||
568 | TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
569 | TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); | ||
570 | } | ||
571 | } | ||
572 | |||
573 | if (playing_notes) { | ||
574 | if (note_frequency > 0) { | ||
575 | # ifdef VIBRATO_ENABLE | ||
576 | if (vibrato_strength > 0) { | ||
577 | freq = vibrato(note_frequency); | ||
578 | } else { | ||
579 | freq = note_frequency; | ||
580 | } | ||
581 | # else | ||
582 | freq = note_frequency; | ||
583 | # endif | ||
584 | |||
585 | if (envelope_index < 65535) { | ||
586 | envelope_index++; | ||
587 | } | ||
588 | freq = voice_envelope(freq); | ||
589 | |||
590 | TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
591 | TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); | ||
592 | } else { | ||
593 | TIMER_1_PERIOD = 0; | ||
594 | TIMER_1_DUTY_CYCLE = 0; | ||
595 | } | ||
596 | |||
597 | note_position++; | ||
598 | bool end_of_note = false; | ||
599 | if (TIMER_1_PERIOD > 0) { | ||
600 | if (!note_resting) | ||
601 | end_of_note = (note_position >= (note_length / TIMER_1_PERIOD * 0xFFFF - 1)); | ||
602 | else | ||
603 | end_of_note = (note_position >= (note_length)); | ||
604 | } else { | ||
605 | end_of_note = (note_position >= (note_length)); | ||
606 | } | ||
607 | |||
608 | if (end_of_note) { | ||
609 | current_note++; | ||
610 | if (current_note >= notes_count) { | ||
611 | if (notes_repeat) { | ||
612 | current_note = 0; | ||
613 | } else { | ||
614 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
615 | DISABLE_AUDIO_COUNTER_1_OUTPUT; | ||
616 | playing_notes = false; | ||
617 | return; | ||
618 | } | ||
619 | } | ||
620 | if (!note_resting) { | ||
621 | note_resting = true; | ||
622 | current_note--; | ||
623 | if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { | ||
624 | note_frequency = 0; | ||
625 | note_length = 1; | ||
626 | } else { | ||
627 | note_frequency = (*notes_pointer)[current_note][0]; | ||
628 | note_length = 1; | ||
629 | } | ||
630 | } else { | ||
631 | note_resting = false; | ||
632 | envelope_index = 0; | ||
633 | note_frequency = (*notes_pointer)[current_note][0]; | ||
634 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
635 | } | ||
636 | |||
637 | note_position = 0; | ||
638 | } | ||
639 | } | ||
640 | |||
641 | if (!audio_config.enable) { | ||
642 | playing_notes = false; | ||
643 | playing_note = false; | ||
644 | } | ||
645 | # endif | ||
646 | } | ||
647 | #endif | ||
648 | |||
649 | void play_note(float freq, int vol) { | ||
650 | dprintf("audio play note freq=%d vol=%d", (int)freq, vol); | ||
651 | |||
652 | if (!audio_initialized) { | ||
653 | audio_init(); | ||
654 | } | ||
655 | |||
656 | if (audio_config.enable && voices < 8) { | ||
657 | #ifdef CPIN_AUDIO | ||
658 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
659 | #endif | ||
660 | #ifdef BPIN_AUDIO | ||
661 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
662 | #endif | ||
663 | |||
664 | // Cancel notes if notes are playing | ||
665 | if (playing_notes) stop_all_notes(); | ||
666 | |||
667 | playing_note = true; | ||
668 | |||
669 | envelope_index = 0; | ||
670 | |||
671 | if (freq > 0) { | ||
672 | frequencies[voices] = freq; | ||
673 | volumes[voices] = vol; | ||
674 | voices++; | ||
675 | } | ||
676 | |||
677 | #ifdef CPIN_AUDIO | ||
678 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
679 | ENABLE_AUDIO_COUNTER_3_OUTPUT; | ||
680 | #endif | ||
681 | #ifdef BPIN_AUDIO | ||
682 | # ifdef CPIN_AUDIO | ||
683 | if (voices > 1) { | ||
684 | ENABLE_AUDIO_COUNTER_1_ISR; | ||
685 | ENABLE_AUDIO_COUNTER_1_OUTPUT; | ||
686 | } | ||
687 | # else | ||
688 | ENABLE_AUDIO_COUNTER_1_ISR; | ||
689 | ENABLE_AUDIO_COUNTER_1_OUTPUT; | ||
690 | # endif | ||
691 | #endif | ||
692 | } | ||
693 | } | ||
694 | |||
695 | void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) { | ||
696 | if (!audio_initialized) { | ||
697 | audio_init(); | ||
698 | } | ||
699 | |||
700 | if (audio_config.enable) { | ||
701 | #ifdef CPIN_AUDIO | ||
702 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
703 | #endif | ||
704 | #ifdef BPIN_AUDIO | ||
705 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
706 | #endif | ||
707 | |||
708 | // Cancel note if a note is playing | ||
709 | if (playing_note) stop_all_notes(); | ||
710 | |||
711 | playing_notes = true; | ||
712 | |||
713 | notes_pointer = np; | ||
714 | notes_count = n_count; | ||
715 | notes_repeat = n_repeat; | ||
716 | |||
717 | place = 0; | ||
718 | current_note = 0; | ||
719 | |||
720 | note_frequency = (*notes_pointer)[current_note][0]; | ||
721 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
722 | note_position = 0; | ||
723 | |||
724 | #ifdef CPIN_AUDIO | ||
725 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
726 | ENABLE_AUDIO_COUNTER_3_OUTPUT; | ||
727 | #endif | ||
728 | #ifdef BPIN_AUDIO | ||
729 | # ifndef CPIN_AUDIO | ||
730 | ENABLE_AUDIO_COUNTER_1_ISR; | ||
731 | ENABLE_AUDIO_COUNTER_1_OUTPUT; | ||
732 | # endif | ||
733 | #endif | ||
734 | } | ||
735 | } | ||
736 | |||
737 | bool is_playing_notes(void) { return playing_notes; } | ||
738 | |||
739 | bool is_audio_on(void) { return (audio_config.enable != 0); } | ||
740 | |||
741 | void audio_toggle(void) { | ||
742 | audio_config.enable ^= 1; | ||
743 | eeconfig_update_audio(audio_config.raw); | ||
744 | if (audio_config.enable) audio_on_user(); | ||
745 | } | ||
746 | |||
747 | void audio_on(void) { | ||
748 | audio_config.enable = 1; | ||
749 | eeconfig_update_audio(audio_config.raw); | ||
750 | audio_on_user(); | ||
751 | PLAY_SONG(audio_on_song); | ||
752 | } | ||
753 | |||
754 | void audio_off(void) { | ||
755 | PLAY_SONG(audio_off_song); | ||
756 | wait_ms(100); | ||
757 | stop_all_notes(); | ||
758 | audio_config.enable = 0; | ||
759 | eeconfig_update_audio(audio_config.raw); | ||
760 | } | ||
761 | |||
762 | #ifdef VIBRATO_ENABLE | ||
763 | |||
764 | // Vibrato rate functions | ||
765 | |||
766 | void set_vibrato_rate(float rate) { vibrato_rate = rate; } | ||
767 | |||
768 | void increase_vibrato_rate(float change) { vibrato_rate *= change; } | ||
769 | |||
770 | void decrease_vibrato_rate(float change) { vibrato_rate /= change; } | ||
771 | |||
772 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
773 | |||
774 | void set_vibrato_strength(float strength) { vibrato_strength = strength; } | ||
775 | |||
776 | void increase_vibrato_strength(float change) { vibrato_strength *= change; } | ||
777 | |||
778 | void decrease_vibrato_strength(float change) { vibrato_strength /= change; } | ||
779 | |||
780 | # endif /* VIBRATO_STRENGTH_ENABLE */ | ||
781 | |||
782 | #endif /* VIBRATO_ENABLE */ | ||
783 | |||
784 | // Polyphony functions | ||
785 | |||
786 | void set_polyphony_rate(float rate) { polyphony_rate = rate; } | ||
787 | |||
788 | void enable_polyphony() { polyphony_rate = 5; } | ||
789 | |||
790 | void disable_polyphony() { polyphony_rate = 0; } | ||
791 | |||
792 | void increase_polyphony_rate(float change) { polyphony_rate *= change; } | ||
793 | |||
794 | void decrease_polyphony_rate(float change) { polyphony_rate /= change; } | ||
795 | |||
796 | // Timbre function | ||
797 | |||
798 | void set_timbre(float timbre) { note_timbre = timbre; } | ||
799 | |||
800 | // Tempo functions | ||
801 | |||
802 | void set_tempo(uint8_t tempo) { note_tempo = tempo; } | ||
803 | |||
804 | void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } | ||
805 | |||
806 | void increase_tempo(uint8_t tempo_change) { | ||
807 | if (note_tempo - tempo_change < 10) { | ||
808 | note_tempo = 10; | ||
809 | } else { | ||
810 | note_tempo -= tempo_change; | ||
811 | } | ||
812 | } | ||
diff --git a/quantum/audio/audio_chibios.c b/quantum/audio/audio_chibios.c deleted file mode 100644 index b267e5746..000000000 --- a/quantum/audio/audio_chibios.c +++ /dev/null | |||
@@ -1,721 +0,0 @@ | |||
1 | /* Copyright 2016 Jack Humbert | ||
2 | * | ||
3 | * This program is free software: you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License as published by | ||
5 | * the Free Software Foundation, either version 2 of the License, or | ||
6 | * (at your option) any later version. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include "audio.h" | ||
18 | #include <ch.h> | ||
19 | #include <hal.h> | ||
20 | |||
21 | #include <string.h> | ||
22 | #include "print.h" | ||
23 | #include "keymap.h" | ||
24 | |||
25 | #include "eeconfig.h" | ||
26 | |||
27 | // ----------------------------------------------------------------------------- | ||
28 | |||
29 | int voices = 0; | ||
30 | int voice_place = 0; | ||
31 | float frequency = 0; | ||
32 | float frequency_alt = 0; | ||
33 | int volume = 0; | ||
34 | long position = 0; | ||
35 | |||
36 | float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
37 | int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
38 | bool sliding = false; | ||
39 | |||
40 | float place = 0; | ||
41 | |||
42 | uint8_t *sample; | ||
43 | uint16_t sample_length = 0; | ||
44 | |||
45 | bool playing_notes = false; | ||
46 | bool playing_note = false; | ||
47 | float note_frequency = 0; | ||
48 | float note_length = 0; | ||
49 | uint8_t note_tempo = TEMPO_DEFAULT; | ||
50 | float note_timbre = TIMBRE_DEFAULT; | ||
51 | uint16_t note_position = 0; | ||
52 | float (*notes_pointer)[][2]; | ||
53 | uint16_t notes_count; | ||
54 | bool notes_repeat; | ||
55 | bool note_resting = false; | ||
56 | |||
57 | uint16_t current_note = 0; | ||
58 | uint8_t rest_counter = 0; | ||
59 | |||
60 | #ifdef VIBRATO_ENABLE | ||
61 | float vibrato_counter = 0; | ||
62 | float vibrato_strength = .5; | ||
63 | float vibrato_rate = 0.125; | ||
64 | #endif | ||
65 | |||
66 | float polyphony_rate = 0; | ||
67 | |||
68 | static bool audio_initialized = false; | ||
69 | |||
70 | audio_config_t audio_config; | ||
71 | |||
72 | uint16_t envelope_index = 0; | ||
73 | bool glissando = true; | ||
74 | |||
75 | #ifndef STARTUP_SONG | ||
76 | # define STARTUP_SONG SONG(STARTUP_SOUND) | ||
77 | #endif | ||
78 | float startup_song[][2] = STARTUP_SONG; | ||
79 | |||
80 | static void gpt_cb8(GPTDriver *gptp); | ||
81 | |||
82 | #define DAC_BUFFER_SIZE 100 | ||
83 | #ifndef DAC_SAMPLE_MAX | ||
84 | # define DAC_SAMPLE_MAX 65535U | ||
85 | #endif | ||
86 | |||
87 | #define START_CHANNEL_1() \ | ||
88 | gptStart(&GPTD6, &gpt6cfg1); \ | ||
89 | gptStartContinuous(&GPTD6, 2U); \ | ||
90 | palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG) | ||
91 | #define START_CHANNEL_2() \ | ||
92 | gptStart(&GPTD7, &gpt7cfg1); \ | ||
93 | gptStartContinuous(&GPTD7, 2U); \ | ||
94 | palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG) | ||
95 | #define STOP_CHANNEL_1() \ | ||
96 | gptStopTimer(&GPTD6); \ | ||
97 | palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL); \ | ||
98 | palSetPad(GPIOA, 4) | ||
99 | #define STOP_CHANNEL_2() \ | ||
100 | gptStopTimer(&GPTD7); \ | ||
101 | palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL); \ | ||
102 | palSetPad(GPIOA, 5) | ||
103 | #define RESTART_CHANNEL_1() \ | ||
104 | STOP_CHANNEL_1(); \ | ||
105 | START_CHANNEL_1() | ||
106 | #define RESTART_CHANNEL_2() \ | ||
107 | STOP_CHANNEL_2(); \ | ||
108 | START_CHANNEL_2() | ||
109 | #define UPDATE_CHANNEL_1_FREQ(freq) \ | ||
110 | gpt6cfg1.frequency = freq * DAC_BUFFER_SIZE; \ | ||
111 | RESTART_CHANNEL_1() | ||
112 | #define UPDATE_CHANNEL_2_FREQ(freq) \ | ||
113 | gpt7cfg1.frequency = freq * DAC_BUFFER_SIZE; \ | ||
114 | RESTART_CHANNEL_2() | ||
115 | #define GET_CHANNEL_1_FREQ (uint16_t)(gpt6cfg1.frequency * DAC_BUFFER_SIZE) | ||
116 | #define GET_CHANNEL_2_FREQ (uint16_t)(gpt7cfg1.frequency * DAC_BUFFER_SIZE) | ||
117 | |||
118 | /* | ||
119 | * GPT6 configuration. | ||
120 | */ | ||
121 | // static const GPTConfig gpt6cfg1 = { | ||
122 | // .frequency = 1000000U, | ||
123 | // .callback = NULL, | ||
124 | // .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
125 | // .dier = 0U | ||
126 | // }; | ||
127 | |||
128 | GPTConfig gpt6cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE, | ||
129 | .callback = NULL, | ||
130 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
131 | .dier = 0U}; | ||
132 | |||
133 | GPTConfig gpt7cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE, | ||
134 | .callback = NULL, | ||
135 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
136 | .dier = 0U}; | ||
137 | |||
138 | GPTConfig gpt8cfg1 = {.frequency = 10, | ||
139 | .callback = gpt_cb8, | ||
140 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
141 | .dier = 0U}; | ||
142 | |||
143 | /* | ||
144 | * DAC test buffer (sine wave). | ||
145 | */ | ||
146 | // static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = { | ||
147 | // 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437, | ||
148 | // 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846, | ||
149 | // 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221, | ||
150 | // 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544, | ||
151 | // 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801, | ||
152 | // 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982, | ||
153 | // 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078, | ||
154 | // 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086, | ||
155 | // 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004, | ||
156 | // 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837, | ||
157 | // 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591, | ||
158 | // 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278, | ||
159 | // 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912, | ||
160 | // 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507, | ||
161 | // 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082, | ||
162 | // 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657, | ||
163 | // 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248, | ||
164 | // 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873, | ||
165 | // 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550, | ||
166 | // 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293, | ||
167 | // 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112, | ||
168 | // 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16, | ||
169 | // 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8, | ||
170 | // 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90, | ||
171 | // 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257, | ||
172 | // 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503, | ||
173 | // 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816, | ||
174 | // 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182, | ||
175 | // 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587, | ||
176 | // 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012 | ||
177 | // }; | ||
178 | |||
179 | // static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = { | ||
180 | // 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8, | ||
181 | // 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90, | ||
182 | // 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257, | ||
183 | // 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503, | ||
184 | // 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816, | ||
185 | // 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182, | ||
186 | // 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587, | ||
187 | // 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012, | ||
188 | // 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437, | ||
189 | // 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846, | ||
190 | // 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221, | ||
191 | // 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544, | ||
192 | // 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801, | ||
193 | // 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982, | ||
194 | // 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078, | ||
195 | // 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086, | ||
196 | // 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004, | ||
197 | // 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837, | ||
198 | // 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591, | ||
199 | // 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278, | ||
200 | // 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912, | ||
201 | // 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507, | ||
202 | // 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082, | ||
203 | // 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657, | ||
204 | // 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248, | ||
205 | // 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873, | ||
206 | // 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550, | ||
207 | // 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293, | ||
208 | // 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112, | ||
209 | // 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16 | ||
210 | // }; | ||
211 | |||
212 | // squarewave | ||
213 | static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = { | ||
214 | // First half is max, second half is 0 | ||
215 | [0 ... DAC_BUFFER_SIZE / 2 - 1] = DAC_SAMPLE_MAX, | ||
216 | [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = 0, | ||
217 | }; | ||
218 | |||
219 | // squarewave | ||
220 | static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = { | ||
221 | // opposite of dac_buffer above | ||
222 | [0 ... DAC_BUFFER_SIZE / 2 - 1] = 0, | ||
223 | [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = DAC_SAMPLE_MAX, | ||
224 | }; | ||
225 | |||
226 | /* | ||
227 | * DAC streaming callback. | ||
228 | */ | ||
229 | size_t nz = 0; | ||
230 | static void end_cb1(DACDriver *dacp) { | ||
231 | (void)dacp; | ||
232 | |||
233 | nz++; | ||
234 | if ((nz % 1000) == 0) { | ||
235 | // palTogglePad(GPIOD, GPIOD_LED3); | ||
236 | } | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * DAC error callback. | ||
241 | */ | ||
242 | static void error_cb1(DACDriver *dacp, dacerror_t err) { | ||
243 | (void)dacp; | ||
244 | (void)err; | ||
245 | |||
246 | chSysHalt("DAC failure"); | ||
247 | } | ||
248 | |||
249 | static const DACConfig dac1cfg1 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT}; | ||
250 | |||
251 | static const DACConversionGroup dacgrpcfg1 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)}; | ||
252 | |||
253 | static const DACConfig dac1cfg2 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT}; | ||
254 | |||
255 | static const DACConversionGroup dacgrpcfg2 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)}; | ||
256 | |||
257 | void audio_init() { | ||
258 | if (audio_initialized) { | ||
259 | return; | ||
260 | } | ||
261 | |||
262 | // Check EEPROM | ||
263 | #ifdef EEPROM_ENABLE | ||
264 | if (!eeconfig_is_enabled()) { | ||
265 | eeconfig_init(); | ||
266 | } | ||
267 | audio_config.raw = eeconfig_read_audio(); | ||
268 | #else // ARM EEPROM | ||
269 | audio_config.enable = true; | ||
270 | # ifdef AUDIO_CLICKY_ON | ||
271 | audio_config.clicky_enable = true; | ||
272 | # endif | ||
273 | #endif // ARM EEPROM | ||
274 | |||
275 | /* | ||
276 | * Starting DAC1 driver, setting up the output pin as analog as suggested | ||
277 | * by the Reference Manual. | ||
278 | */ | ||
279 | palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG); | ||
280 | palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); | ||
281 | dacStart(&DACD1, &dac1cfg1); | ||
282 | dacStart(&DACD2, &dac1cfg2); | ||
283 | |||
284 | /* | ||
285 | * Start the note timer | ||
286 | */ | ||
287 | gptStart(&GPTD8, &gpt8cfg1); | ||
288 | gptStartContinuous(&GPTD8, 2U); | ||
289 | |||
290 | /* | ||
291 | * Starting GPT6/7 driver, it is used for triggering the DAC. | ||
292 | */ | ||
293 | START_CHANNEL_1(); | ||
294 | START_CHANNEL_2(); | ||
295 | |||
296 | /* | ||
297 | * Starting a continuous conversion. | ||
298 | */ | ||
299 | dacStartConversion(&DACD1, &dacgrpcfg1, (dacsample_t *)dac_buffer, DAC_BUFFER_SIZE); | ||
300 | dacStartConversion(&DACD2, &dacgrpcfg2, (dacsample_t *)dac_buffer_2, DAC_BUFFER_SIZE); | ||
301 | |||
302 | audio_initialized = true; | ||
303 | |||
304 | stop_all_notes(); | ||
305 | } | ||
306 | |||
307 | void audio_startup() { | ||
308 | if (audio_config.enable) { | ||
309 | PLAY_SONG(startup_song); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | void stop_all_notes() { | ||
314 | dprintf("audio stop all notes"); | ||
315 | |||
316 | if (!audio_initialized) { | ||
317 | audio_init(); | ||
318 | } | ||
319 | voices = 0; | ||
320 | |||
321 | gptStopTimer(&GPTD6); | ||
322 | gptStopTimer(&GPTD7); | ||
323 | gptStopTimer(&GPTD8); | ||
324 | |||
325 | playing_notes = false; | ||
326 | playing_note = false; | ||
327 | frequency = 0; | ||
328 | frequency_alt = 0; | ||
329 | volume = 0; | ||
330 | |||
331 | for (uint8_t i = 0; i < 8; i++) { | ||
332 | frequencies[i] = 0; | ||
333 | volumes[i] = 0; | ||
334 | } | ||
335 | } | ||
336 | |||
337 | void stop_note(float freq) { | ||
338 | dprintf("audio stop note freq=%d", (int)freq); | ||
339 | |||
340 | if (playing_note) { | ||
341 | if (!audio_initialized) { | ||
342 | audio_init(); | ||
343 | } | ||
344 | for (int i = 7; i >= 0; i--) { | ||
345 | if (frequencies[i] == freq) { | ||
346 | frequencies[i] = 0; | ||
347 | volumes[i] = 0; | ||
348 | for (int j = i; (j < 7); j++) { | ||
349 | frequencies[j] = frequencies[j + 1]; | ||
350 | frequencies[j + 1] = 0; | ||
351 | volumes[j] = volumes[j + 1]; | ||
352 | volumes[j + 1] = 0; | ||
353 | } | ||
354 | break; | ||
355 | } | ||
356 | } | ||
357 | voices--; | ||
358 | if (voices < 0) { | ||
359 | voices = 0; | ||
360 | } | ||
361 | if (voice_place >= voices) { | ||
362 | voice_place = 0; | ||
363 | } | ||
364 | if (voices == 0) { | ||
365 | STOP_CHANNEL_1(); | ||
366 | STOP_CHANNEL_2(); | ||
367 | gptStopTimer(&GPTD8); | ||
368 | frequency = 0; | ||
369 | frequency_alt = 0; | ||
370 | volume = 0; | ||
371 | playing_note = false; | ||
372 | } | ||
373 | } | ||
374 | } | ||
375 | |||
376 | #ifdef VIBRATO_ENABLE | ||
377 | |||
378 | float mod(float a, int b) { | ||
379 | float r = fmod(a, b); | ||
380 | return r < 0 ? r + b : r; | ||
381 | } | ||
382 | |||
383 | float vibrato(float average_freq) { | ||
384 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
385 | float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); | ||
386 | # else | ||
387 | float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; | ||
388 | # endif | ||
389 | vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); | ||
390 | return vibrated_freq; | ||
391 | } | ||
392 | |||
393 | #endif | ||
394 | |||
395 | static void gpt_cb8(GPTDriver *gptp) { | ||
396 | float freq; | ||
397 | |||
398 | if (playing_note) { | ||
399 | if (voices > 0) { | ||
400 | float freq_alt = 0; | ||
401 | if (voices > 1) { | ||
402 | if (polyphony_rate == 0) { | ||
403 | if (glissando) { | ||
404 | if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) { | ||
405 | frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2); | ||
406 | } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440 / frequencies[voices - 2] / 12 / 2)) { | ||
407 | frequency_alt = frequency_alt * pow(2, -440 / frequency_alt / 12 / 2); | ||
408 | } else { | ||
409 | frequency_alt = frequencies[voices - 2]; | ||
410 | } | ||
411 | } else { | ||
412 | frequency_alt = frequencies[voices - 2]; | ||
413 | } | ||
414 | |||
415 | #ifdef VIBRATO_ENABLE | ||
416 | if (vibrato_strength > 0) { | ||
417 | freq_alt = vibrato(frequency_alt); | ||
418 | } else { | ||
419 | freq_alt = frequency_alt; | ||
420 | } | ||
421 | #else | ||
422 | freq_alt = frequency_alt; | ||
423 | #endif | ||
424 | } | ||
425 | |||
426 | if (envelope_index < 65535) { | ||
427 | envelope_index++; | ||
428 | } | ||
429 | |||
430 | freq_alt = voice_envelope(freq_alt); | ||
431 | |||
432 | if (freq_alt < 30.517578125) { | ||
433 | freq_alt = 30.52; | ||
434 | } | ||
435 | |||
436 | if (GET_CHANNEL_2_FREQ != (uint16_t)freq_alt) { | ||
437 | UPDATE_CHANNEL_2_FREQ(freq_alt); | ||
438 | } else { | ||
439 | RESTART_CHANNEL_2(); | ||
440 | } | ||
441 | // note_timbre; | ||
442 | } | ||
443 | |||
444 | if (polyphony_rate > 0) { | ||
445 | if (voices > 1) { | ||
446 | voice_place %= voices; | ||
447 | if (place++ > (frequencies[voice_place] / polyphony_rate)) { | ||
448 | voice_place = (voice_place + 1) % voices; | ||
449 | place = 0.0; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | #ifdef VIBRATO_ENABLE | ||
454 | if (vibrato_strength > 0) { | ||
455 | freq = vibrato(frequencies[voice_place]); | ||
456 | } else { | ||
457 | freq = frequencies[voice_place]; | ||
458 | } | ||
459 | #else | ||
460 | freq = frequencies[voice_place]; | ||
461 | #endif | ||
462 | } else { | ||
463 | if (glissando) { | ||
464 | if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { | ||
465 | frequency = frequency * pow(2, 440 / frequency / 12 / 2); | ||
466 | } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { | ||
467 | frequency = frequency * pow(2, -440 / frequency / 12 / 2); | ||
468 | } else { | ||
469 | frequency = frequencies[voices - 1]; | ||
470 | } | ||
471 | } else { | ||
472 | frequency = frequencies[voices - 1]; | ||
473 | } | ||
474 | |||
475 | #ifdef VIBRATO_ENABLE | ||
476 | if (vibrato_strength > 0) { | ||
477 | freq = vibrato(frequency); | ||
478 | } else { | ||
479 | freq = frequency; | ||
480 | } | ||
481 | #else | ||
482 | freq = frequency; | ||
483 | #endif | ||
484 | } | ||
485 | |||
486 | if (envelope_index < 65535) { | ||
487 | envelope_index++; | ||
488 | } | ||
489 | |||
490 | freq = voice_envelope(freq); | ||
491 | |||
492 | if (freq < 30.517578125) { | ||
493 | freq = 30.52; | ||
494 | } | ||
495 | |||
496 | if (GET_CHANNEL_1_FREQ != (uint16_t)freq) { | ||
497 | UPDATE_CHANNEL_1_FREQ(freq); | ||
498 | } else { | ||
499 | RESTART_CHANNEL_1(); | ||
500 | } | ||
501 | // note_timbre; | ||
502 | } | ||
503 | } | ||
504 | |||
505 | if (playing_notes) { | ||
506 | if (note_frequency > 0) { | ||
507 | #ifdef VIBRATO_ENABLE | ||
508 | if (vibrato_strength > 0) { | ||
509 | freq = vibrato(note_frequency); | ||
510 | } else { | ||
511 | freq = note_frequency; | ||
512 | } | ||
513 | #else | ||
514 | freq = note_frequency; | ||
515 | #endif | ||
516 | |||
517 | if (envelope_index < 65535) { | ||
518 | envelope_index++; | ||
519 | } | ||
520 | freq = voice_envelope(freq); | ||
521 | |||
522 | if (GET_CHANNEL_1_FREQ != (uint16_t)freq) { | ||
523 | UPDATE_CHANNEL_1_FREQ(freq); | ||
524 | UPDATE_CHANNEL_2_FREQ(freq); | ||
525 | } | ||
526 | // note_timbre; | ||
527 | } else { | ||
528 | // gptStopTimer(&GPTD6); | ||
529 | // gptStopTimer(&GPTD7); | ||
530 | } | ||
531 | |||
532 | note_position++; | ||
533 | bool end_of_note = false; | ||
534 | if (GET_CHANNEL_1_FREQ > 0) { | ||
535 | if (!note_resting) | ||
536 | end_of_note = (note_position >= (note_length * 8 - 1)); | ||
537 | else | ||
538 | end_of_note = (note_position >= (note_length * 8)); | ||
539 | } else { | ||
540 | end_of_note = (note_position >= (note_length * 8)); | ||
541 | } | ||
542 | |||
543 | if (end_of_note) { | ||
544 | current_note++; | ||
545 | if (current_note >= notes_count) { | ||
546 | if (notes_repeat) { | ||
547 | current_note = 0; | ||
548 | } else { | ||
549 | STOP_CHANNEL_1(); | ||
550 | STOP_CHANNEL_2(); | ||
551 | // gptStopTimer(&GPTD8); | ||
552 | playing_notes = false; | ||
553 | return; | ||
554 | } | ||
555 | } | ||
556 | if (!note_resting) { | ||
557 | note_resting = true; | ||
558 | current_note--; | ||
559 | if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { | ||
560 | note_frequency = 0; | ||
561 | note_length = 1; | ||
562 | } else { | ||
563 | note_frequency = (*notes_pointer)[current_note][0]; | ||
564 | note_length = 1; | ||
565 | } | ||
566 | } else { | ||
567 | note_resting = false; | ||
568 | envelope_index = 0; | ||
569 | note_frequency = (*notes_pointer)[current_note][0]; | ||
570 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
571 | } | ||
572 | |||
573 | note_position = 0; | ||
574 | } | ||
575 | } | ||
576 | |||
577 | if (!audio_config.enable) { | ||
578 | playing_notes = false; | ||
579 | playing_note = false; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | void play_note(float freq, int vol) { | ||
584 | dprintf("audio play note freq=%d vol=%d", (int)freq, vol); | ||
585 | |||
586 | if (!audio_initialized) { | ||
587 | audio_init(); | ||
588 | } | ||
589 | |||
590 | if (audio_config.enable && voices < 8) { | ||
591 | // Cancel notes if notes are playing | ||
592 | if (playing_notes) { | ||
593 | stop_all_notes(); | ||
594 | } | ||
595 | |||
596 | playing_note = true; | ||
597 | |||
598 | envelope_index = 0; | ||
599 | |||
600 | if (freq > 0) { | ||
601 | frequencies[voices] = freq; | ||
602 | volumes[voices] = vol; | ||
603 | voices++; | ||
604 | } | ||
605 | |||
606 | gptStart(&GPTD8, &gpt8cfg1); | ||
607 | gptStartContinuous(&GPTD8, 2U); | ||
608 | RESTART_CHANNEL_1(); | ||
609 | RESTART_CHANNEL_2(); | ||
610 | } | ||
611 | } | ||
612 | |||
613 | void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) { | ||
614 | if (!audio_initialized) { | ||
615 | audio_init(); | ||
616 | } | ||
617 | |||
618 | if (audio_config.enable) { | ||
619 | // Cancel note if a note is playing | ||
620 | if (playing_note) { | ||
621 | stop_all_notes(); | ||
622 | } | ||
623 | |||
624 | playing_notes = true; | ||
625 | |||
626 | notes_pointer = np; | ||
627 | notes_count = n_count; | ||
628 | notes_repeat = n_repeat; | ||
629 | |||
630 | place = 0; | ||
631 | current_note = 0; | ||
632 | |||
633 | note_frequency = (*notes_pointer)[current_note][0]; | ||
634 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
635 | note_position = 0; | ||
636 | |||
637 | gptStart(&GPTD8, &gpt8cfg1); | ||
638 | gptStartContinuous(&GPTD8, 2U); | ||
639 | RESTART_CHANNEL_1(); | ||
640 | RESTART_CHANNEL_2(); | ||
641 | } | ||
642 | } | ||
643 | |||
644 | bool is_playing_notes(void) { return playing_notes; } | ||
645 | |||
646 | bool is_audio_on(void) { return (audio_config.enable != 0); } | ||
647 | |||
648 | void audio_toggle(void) { | ||
649 | if (audio_config.enable) { | ||
650 | stop_all_notes(); | ||
651 | } | ||
652 | audio_config.enable ^= 1; | ||
653 | eeconfig_update_audio(audio_config.raw); | ||
654 | if (audio_config.enable) { | ||
655 | audio_on_user(); | ||
656 | } | ||
657 | } | ||
658 | |||
659 | void audio_on(void) { | ||
660 | audio_config.enable = 1; | ||
661 | eeconfig_update_audio(audio_config.raw); | ||
662 | audio_on_user(); | ||
663 | } | ||
664 | |||
665 | void audio_off(void) { | ||
666 | stop_all_notes(); | ||
667 | audio_config.enable = 0; | ||
668 | eeconfig_update_audio(audio_config.raw); | ||
669 | } | ||
670 | |||
671 | #ifdef VIBRATO_ENABLE | ||
672 | |||
673 | // Vibrato rate functions | ||
674 | |||
675 | void set_vibrato_rate(float rate) { vibrato_rate = rate; } | ||
676 | |||
677 | void increase_vibrato_rate(float change) { vibrato_rate *= change; } | ||
678 | |||
679 | void decrease_vibrato_rate(float change) { vibrato_rate /= change; } | ||
680 | |||
681 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
682 | |||
683 | void set_vibrato_strength(float strength) { vibrato_strength = strength; } | ||
684 | |||
685 | void increase_vibrato_strength(float change) { vibrato_strength *= change; } | ||
686 | |||
687 | void decrease_vibrato_strength(float change) { vibrato_strength /= change; } | ||
688 | |||
689 | # endif /* VIBRATO_STRENGTH_ENABLE */ | ||
690 | |||
691 | #endif /* VIBRATO_ENABLE */ | ||
692 | |||
693 | // Polyphony functions | ||
694 | |||
695 | void set_polyphony_rate(float rate) { polyphony_rate = rate; } | ||
696 | |||
697 | void enable_polyphony() { polyphony_rate = 5; } | ||
698 | |||
699 | void disable_polyphony() { polyphony_rate = 0; } | ||
700 | |||
701 | void increase_polyphony_rate(float change) { polyphony_rate *= change; } | ||
702 | |||
703 | void decrease_polyphony_rate(float change) { polyphony_rate /= change; } | ||
704 | |||
705 | // Timbre function | ||
706 | |||
707 | void set_timbre(float timbre) { note_timbre = timbre; } | ||
708 | |||
709 | // Tempo functions | ||
710 | |||
711 | void set_tempo(uint8_t tempo) { note_tempo = tempo; } | ||
712 | |||
713 | void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } | ||
714 | |||
715 | void increase_tempo(uint8_t tempo_change) { | ||
716 | if (note_tempo - tempo_change < 10) { | ||
717 | note_tempo = 10; | ||
718 | } else { | ||
719 | note_tempo -= tempo_change; | ||
720 | } | ||
721 | } | ||
diff --git a/quantum/audio/audio_pwm.c b/quantum/audio/audio_pwm.c deleted file mode 100644 index d93ac4bb4..000000000 --- a/quantum/audio/audio_pwm.c +++ /dev/null | |||
@@ -1,606 +0,0 @@ | |||
1 | /* Copyright 2016 Jack Humbert | ||
2 | * | ||
3 | * This program is free software: you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License as published by | ||
5 | * the Free Software Foundation, either version 2 of the License, or | ||
6 | * (at your option) any later version. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | #include <stdio.h> | ||
17 | #include <string.h> | ||
18 | //#include <math.h> | ||
19 | #include <avr/pgmspace.h> | ||
20 | #include <avr/interrupt.h> | ||
21 | #include <avr/io.h> | ||
22 | #include "print.h" | ||
23 | #include "audio.h" | ||
24 | #include "keymap.h" | ||
25 | |||
26 | #include "eeconfig.h" | ||
27 | |||
28 | #define PI 3.14159265 | ||
29 | |||
30 | #define CPU_PRESCALER 8 | ||
31 | |||
32 | #ifndef STARTUP_SONG | ||
33 | # define STARTUP_SONG SONG(STARTUP_SOUND) | ||
34 | #endif | ||
35 | float startup_song[][2] = STARTUP_SONG; | ||
36 | |||
37 | // Timer Abstractions | ||
38 | |||
39 | // TIMSK3 - Timer/Counter #3 Interrupt Mask Register | ||
40 | // Turn on/off 3A interputs, stopping/enabling the ISR calls | ||
41 | #define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) | ||
42 | #define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) | ||
43 | |||
44 | // TCCR3A: Timer/Counter #3 Control Register | ||
45 | // Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 | ||
46 | #define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); | ||
47 | #define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); | ||
48 | |||
49 | #define NOTE_PERIOD ICR3 | ||
50 | #define NOTE_DUTY_CYCLE OCR3A | ||
51 | |||
52 | #ifdef PWM_AUDIO | ||
53 | # include "wave.h" | ||
54 | # define SAMPLE_DIVIDER 39 | ||
55 | # define SAMPLE_RATE (2000000.0 / SAMPLE_DIVIDER / 2048) | ||
56 | // Resistor value of 1/ (2 * PI * 10nF * (2000000 hertz / SAMPLE_DIVIDER / 10)) for 10nF cap | ||
57 | |||
58 | float places[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
59 | uint16_t place_int = 0; | ||
60 | bool repeat = true; | ||
61 | #endif | ||
62 | |||
63 | void delay_us(int count) { | ||
64 | while (count--) { | ||
65 | _delay_us(1); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | int voices = 0; | ||
70 | int voice_place = 0; | ||
71 | float frequency = 0; | ||
72 | int volume = 0; | ||
73 | long position = 0; | ||
74 | |||
75 | float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
76 | int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
77 | bool sliding = false; | ||
78 | |||
79 | float place = 0; | ||
80 | |||
81 | uint8_t* sample; | ||
82 | uint16_t sample_length = 0; | ||
83 | // float freq = 0; | ||
84 | |||
85 | bool playing_notes = false; | ||
86 | bool playing_note = false; | ||
87 | float note_frequency = 0; | ||
88 | float note_length = 0; | ||
89 | uint8_t note_tempo = TEMPO_DEFAULT; | ||
90 | float note_timbre = TIMBRE_DEFAULT; | ||
91 | uint16_t note_position = 0; | ||
92 | float (*notes_pointer)[][2]; | ||
93 | uint16_t notes_count; | ||
94 | bool notes_repeat; | ||
95 | float notes_rest; | ||
96 | bool note_resting = false; | ||
97 | |||
98 | uint16_t current_note = 0; | ||
99 | uint8_t rest_counter = 0; | ||
100 | |||
101 | #ifdef VIBRATO_ENABLE | ||
102 | float vibrato_counter = 0; | ||
103 | float vibrato_strength = .5; | ||
104 | float vibrato_rate = 0.125; | ||
105 | #endif | ||
106 | |||
107 | float polyphony_rate = 0; | ||
108 | |||
109 | static bool audio_initialized = false; | ||
110 | |||
111 | audio_config_t audio_config; | ||
112 | |||
113 | uint16_t envelope_index = 0; | ||
114 | |||
115 | void audio_init() { | ||
116 | // Check EEPROM | ||
117 | if (!eeconfig_is_enabled()) { | ||
118 | eeconfig_init(); | ||
119 | } | ||
120 | audio_config.raw = eeconfig_read_audio(); | ||
121 | |||
122 | #ifdef PWM_AUDIO | ||
123 | |||
124 | PLLFRQ = _BV(PDIV2); | ||
125 | PLLCSR = _BV(PLLE); | ||
126 | while (!(PLLCSR & _BV(PLOCK))) | ||
127 | ; | ||
128 | PLLFRQ |= _BV(PLLTM0); /* PCK 48MHz */ | ||
129 | |||
130 | /* Init a fast PWM on Timer4 */ | ||
131 | TCCR4A = _BV(COM4A0) | _BV(PWM4A); /* Clear OC4A on Compare Match */ | ||
132 | TCCR4B = _BV(CS40); /* No prescaling => f = PCK/256 = 187500Hz */ | ||
133 | OCR4A = 0; | ||
134 | |||
135 | /* Enable the OC4A output */ | ||
136 | DDRC |= _BV(PORTC6); | ||
137 | |||
138 | DISABLE_AUDIO_COUNTER_3_ISR; // Turn off 3A interputs | ||
139 | |||
140 | TCCR3A = 0x0; // Options not needed | ||
141 | TCCR3B = _BV(CS31) | _BV(CS30) | _BV(WGM32); // 64th prescaling and CTC | ||
142 | OCR3A = SAMPLE_DIVIDER - 1; // Correct count/compare, related to sample playback | ||
143 | |||
144 | #else | ||
145 | |||
146 | // Set port PC6 (OC3A and /OC4A) as output | ||
147 | DDRC |= _BV(PORTC6); | ||
148 | |||
149 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
150 | |||
151 | // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers | ||
152 | // Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 | ||
153 | // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14 (Period = ICR3, Duty Cycle = OCR3A) | ||
154 | // Clock Select (CS3n) = 0b010 = Clock / 8 | ||
155 | TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); | ||
156 | TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); | ||
157 | |||
158 | #endif | ||
159 | |||
160 | audio_initialized = true; | ||
161 | } | ||
162 | |||
163 | void audio_startup() { | ||
164 | if (audio_config.enable) { | ||
165 | PLAY_SONG(startup_song); | ||
166 | } | ||
167 | } | ||
168 | |||
169 | void stop_all_notes() { | ||
170 | if (!audio_initialized) { | ||
171 | audio_init(); | ||
172 | } | ||
173 | voices = 0; | ||
174 | #ifdef PWM_AUDIO | ||
175 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
176 | #else | ||
177 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
178 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
179 | #endif | ||
180 | |||
181 | playing_notes = false; | ||
182 | playing_note = false; | ||
183 | frequency = 0; | ||
184 | volume = 0; | ||
185 | |||
186 | for (uint8_t i = 0; i < 8; i++) { | ||
187 | frequencies[i] = 0; | ||
188 | volumes[i] = 0; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | void stop_note(float freq) { | ||
193 | if (playing_note) { | ||
194 | if (!audio_initialized) { | ||
195 | audio_init(); | ||
196 | } | ||
197 | #ifdef PWM_AUDIO | ||
198 | freq = freq / SAMPLE_RATE; | ||
199 | #endif | ||
200 | for (int i = 7; i >= 0; i--) { | ||
201 | if (frequencies[i] == freq) { | ||
202 | frequencies[i] = 0; | ||
203 | volumes[i] = 0; | ||
204 | for (int j = i; (j < 7); j++) { | ||
205 | frequencies[j] = frequencies[j + 1]; | ||
206 | frequencies[j + 1] = 0; | ||
207 | volumes[j] = volumes[j + 1]; | ||
208 | volumes[j + 1] = 0; | ||
209 | } | ||
210 | break; | ||
211 | } | ||
212 | } | ||
213 | voices--; | ||
214 | if (voices < 0) voices = 0; | ||
215 | if (voice_place >= voices) { | ||
216 | voice_place = 0; | ||
217 | } | ||
218 | if (voices == 0) { | ||
219 | #ifdef PWM_AUDIO | ||
220 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
221 | #else | ||
222 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
223 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
224 | #endif | ||
225 | frequency = 0; | ||
226 | volume = 0; | ||
227 | playing_note = false; | ||
228 | } | ||
229 | } | ||
230 | } | ||
231 | |||
232 | #ifdef VIBRATO_ENABLE | ||
233 | |||
234 | float mod(float a, int b) { | ||
235 | float r = fmod(a, b); | ||
236 | return r < 0 ? r + b : r; | ||
237 | } | ||
238 | |||
239 | float vibrato(float average_freq) { | ||
240 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
241 | float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); | ||
242 | # else | ||
243 | float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; | ||
244 | # endif | ||
245 | vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); | ||
246 | return vibrated_freq; | ||
247 | } | ||
248 | |||
249 | #endif | ||
250 | |||
251 | ISR(TIMER3_COMPA_vect) { | ||
252 | if (playing_note) { | ||
253 | #ifdef PWM_AUDIO | ||
254 | if (voices == 1) { | ||
255 | // SINE | ||
256 | OCR4A = pgm_read_byte(&sinewave[(uint16_t)place]) >> 2; | ||
257 | |||
258 | // SQUARE | ||
259 | // if (((int)place) >= 1024){ | ||
260 | // OCR4A = 0xFF >> 2; | ||
261 | // } else { | ||
262 | // OCR4A = 0x00; | ||
263 | // } | ||
264 | |||
265 | // SAWTOOTH | ||
266 | // OCR4A = (int)place / 4; | ||
267 | |||
268 | // TRIANGLE | ||
269 | // if (((int)place) >= 1024) { | ||
270 | // OCR4A = (int)place / 2; | ||
271 | // } else { | ||
272 | // OCR4A = 2048 - (int)place / 2; | ||
273 | // } | ||
274 | |||
275 | place += frequency; | ||
276 | |||
277 | if (place >= SINE_LENGTH) place -= SINE_LENGTH; | ||
278 | |||
279 | } else { | ||
280 | int sum = 0; | ||
281 | for (int i = 0; i < voices; i++) { | ||
282 | // SINE | ||
283 | sum += pgm_read_byte(&sinewave[(uint16_t)places[i]]) >> 2; | ||
284 | |||
285 | // SQUARE | ||
286 | // if (((int)places[i]) >= 1024){ | ||
287 | // sum += 0xFF >> 2; | ||
288 | // } else { | ||
289 | // sum += 0x00; | ||
290 | // } | ||
291 | |||
292 | places[i] += frequencies[i]; | ||
293 | |||
294 | if (places[i] >= SINE_LENGTH) places[i] -= SINE_LENGTH; | ||
295 | } | ||
296 | OCR4A = sum; | ||
297 | } | ||
298 | #else | ||
299 | if (voices > 0) { | ||
300 | float freq; | ||
301 | if (polyphony_rate > 0) { | ||
302 | if (voices > 1) { | ||
303 | voice_place %= voices; | ||
304 | if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { | ||
305 | voice_place = (voice_place + 1) % voices; | ||
306 | place = 0.0; | ||
307 | } | ||
308 | } | ||
309 | # ifdef VIBRATO_ENABLE | ||
310 | if (vibrato_strength > 0) { | ||
311 | freq = vibrato(frequencies[voice_place]); | ||
312 | } else { | ||
313 | # else | ||
314 | { | ||
315 | # endif | ||
316 | freq = frequencies[voice_place]; | ||
317 | } | ||
318 | } else { | ||
319 | if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { | ||
320 | frequency = frequency * pow(2, 440 / frequency / 12 / 2); | ||
321 | } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { | ||
322 | frequency = frequency * pow(2, -440 / frequency / 12 / 2); | ||
323 | } else { | ||
324 | frequency = frequencies[voices - 1]; | ||
325 | } | ||
326 | |||
327 | # ifdef VIBRATO_ENABLE | ||
328 | if (vibrato_strength > 0) { | ||
329 | freq = vibrato(frequency); | ||
330 | } else { | ||
331 | # else | ||
332 | { | ||
333 | # endif | ||
334 | freq = frequency; | ||
335 | } | ||
336 | } | ||
337 | |||
338 | if (envelope_index < 65535) { | ||
339 | envelope_index++; | ||
340 | } | ||
341 | freq = voice_envelope(freq); | ||
342 | |||
343 | if (freq < 30.517578125) freq = 30.52; | ||
344 | NOTE_PERIOD = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period | ||
345 | NOTE_DUTY_CYCLE = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period | ||
346 | } | ||
347 | #endif | ||
348 | } | ||
349 | |||
350 | // SAMPLE | ||
351 | // OCR4A = pgm_read_byte(&sample[(uint16_t)place_int]); | ||
352 | |||
353 | // place_int++; | ||
354 | |||
355 | // if (place_int >= sample_length) | ||
356 | // if (repeat) | ||
357 | // place_int -= sample_length; | ||
358 | // else | ||
359 | // DISABLE_AUDIO_COUNTER_3_ISR; | ||
360 | |||
361 | if (playing_notes) { | ||
362 | #ifdef PWM_AUDIO | ||
363 | OCR4A = pgm_read_byte(&sinewave[(uint16_t)place]) >> 0; | ||
364 | |||
365 | place += note_frequency; | ||
366 | if (place >= SINE_LENGTH) place -= SINE_LENGTH; | ||
367 | #else | ||
368 | if (note_frequency > 0) { | ||
369 | float freq; | ||
370 | |||
371 | # ifdef VIBRATO_ENABLE | ||
372 | if (vibrato_strength > 0) { | ||
373 | freq = vibrato(note_frequency); | ||
374 | } else { | ||
375 | # else | ||
376 | { | ||
377 | # endif | ||
378 | freq = note_frequency; | ||
379 | } | ||
380 | |||
381 | if (envelope_index < 65535) { | ||
382 | envelope_index++; | ||
383 | } | ||
384 | freq = voice_envelope(freq); | ||
385 | |||
386 | NOTE_PERIOD = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period | ||
387 | NOTE_DUTY_CYCLE = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period | ||
388 | } else { | ||
389 | NOTE_PERIOD = 0; | ||
390 | NOTE_DUTY_CYCLE = 0; | ||
391 | } | ||
392 | #endif | ||
393 | |||
394 | note_position++; | ||
395 | bool end_of_note = false; | ||
396 | if (NOTE_PERIOD > 0) | ||
397 | end_of_note = (note_position >= (note_length / NOTE_PERIOD * 0xFFFF)); | ||
398 | else | ||
399 | end_of_note = (note_position >= (note_length * 0x7FF)); | ||
400 | if (end_of_note) { | ||
401 | current_note++; | ||
402 | if (current_note >= notes_count) { | ||
403 | if (notes_repeat) { | ||
404 | current_note = 0; | ||
405 | } else { | ||
406 | #ifdef PWM_AUDIO | ||
407 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
408 | #else | ||
409 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
410 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
411 | #endif | ||
412 | playing_notes = false; | ||
413 | return; | ||
414 | } | ||
415 | } | ||
416 | if (!note_resting && (notes_rest > 0)) { | ||
417 | note_resting = true; | ||
418 | note_frequency = 0; | ||
419 | note_length = notes_rest; | ||
420 | current_note--; | ||
421 | } else { | ||
422 | note_resting = false; | ||
423 | #ifdef PWM_AUDIO | ||
424 | note_frequency = (*notes_pointer)[current_note][0] / SAMPLE_RATE; | ||
425 | note_length = (*notes_pointer)[current_note][1] * (((float)note_tempo) / 100); | ||
426 | #else | ||
427 | envelope_index = 0; | ||
428 | note_frequency = (*notes_pointer)[current_note][0]; | ||
429 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
430 | #endif | ||
431 | } | ||
432 | note_position = 0; | ||
433 | } | ||
434 | } | ||
435 | |||
436 | if (!audio_config.enable) { | ||
437 | playing_notes = false; | ||
438 | playing_note = false; | ||
439 | } | ||
440 | } | ||
441 | |||
442 | void play_note(float freq, int vol) { | ||
443 | if (!audio_initialized) { | ||
444 | audio_init(); | ||
445 | } | ||
446 | |||
447 | if (audio_config.enable && voices < 8) { | ||
448 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
449 | |||
450 | // Cancel notes if notes are playing | ||
451 | if (playing_notes) stop_all_notes(); | ||
452 | |||
453 | playing_note = true; | ||
454 | |||
455 | envelope_index = 0; | ||
456 | |||
457 | #ifdef PWM_AUDIO | ||
458 | freq = freq / SAMPLE_RATE; | ||
459 | #endif | ||
460 | if (freq > 0) { | ||
461 | frequencies[voices] = freq; | ||
462 | volumes[voices] = vol; | ||
463 | voices++; | ||
464 | } | ||
465 | |||
466 | #ifdef PWM_AUDIO | ||
467 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
468 | #else | ||
469 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
470 | ENABLE_AUDIO_COUNTER_3_OUTPUT; | ||
471 | #endif | ||
472 | } | ||
473 | } | ||
474 | |||
475 | void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat, float n_rest) { | ||
476 | if (!audio_initialized) { | ||
477 | audio_init(); | ||
478 | } | ||
479 | |||
480 | if (audio_config.enable) { | ||
481 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
482 | |||
483 | // Cancel note if a note is playing | ||
484 | if (playing_note) stop_all_notes(); | ||
485 | |||
486 | playing_notes = true; | ||
487 | |||
488 | notes_pointer = np; | ||
489 | notes_count = n_count; | ||
490 | notes_repeat = n_repeat; | ||
491 | notes_rest = n_rest; | ||
492 | |||
493 | place = 0; | ||
494 | current_note = 0; | ||
495 | |||
496 | #ifdef PWM_AUDIO | ||
497 | note_frequency = (*notes_pointer)[current_note][0] / SAMPLE_RATE; | ||
498 | note_length = (*notes_pointer)[current_note][1] * (((float)note_tempo) / 100); | ||
499 | #else | ||
500 | note_frequency = (*notes_pointer)[current_note][0]; | ||
501 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
502 | #endif | ||
503 | note_position = 0; | ||
504 | |||
505 | #ifdef PWM_AUDIO | ||
506 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
507 | #else | ||
508 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
509 | ENABLE_AUDIO_COUNTER_3_OUTPUT; | ||
510 | #endif | ||
511 | } | ||
512 | } | ||
513 | |||
514 | #ifdef PWM_AUDIO | ||
515 | void play_sample(uint8_t* s, uint16_t l, bool r) { | ||
516 | if (!audio_initialized) { | ||
517 | audio_init(); | ||
518 | } | ||
519 | |||
520 | if (audio_config.enable) { | ||
521 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
522 | stop_all_notes(); | ||
523 | place_int = 0; | ||
524 | sample = s; | ||
525 | sample_length = l; | ||
526 | repeat = r; | ||
527 | |||
528 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
529 | } | ||
530 | } | ||
531 | #endif | ||
532 | |||
533 | void audio_toggle(void) { | ||
534 | audio_config.enable ^= 1; | ||
535 | eeconfig_update_audio(audio_config.raw); | ||
536 | } | ||
537 | |||
538 | void audio_on(void) { | ||
539 | audio_config.enable = 1; | ||
540 | eeconfig_update_audio(audio_config.raw); | ||
541 | } | ||
542 | |||
543 | void audio_off(void) { | ||
544 | audio_config.enable = 0; | ||
545 | eeconfig_update_audio(audio_config.raw); | ||
546 | } | ||
547 | |||
548 | #ifdef VIBRATO_ENABLE | ||
549 | |||
550 | // Vibrato rate functions | ||
551 | |||
552 | void set_vibrato_rate(float rate) { vibrato_rate = rate; } | ||
553 | |||
554 | void increase_vibrato_rate(float change) { vibrato_rate *= change; } | ||
555 | |||
556 | void decrease_vibrato_rate(float change) { vibrato_rate /= change; } | ||
557 | |||
558 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
559 | |||
560 | void set_vibrato_strength(float strength) { vibrato_strength = strength; } | ||
561 | |||
562 | void increase_vibrato_strength(float change) { vibrato_strength *= change; } | ||
563 | |||
564 | void decrease_vibrato_strength(float change) { vibrato_strength /= change; } | ||
565 | |||
566 | # endif /* VIBRATO_STRENGTH_ENABLE */ | ||
567 | |||
568 | #endif /* VIBRATO_ENABLE */ | ||
569 | |||
570 | // Polyphony functions | ||
571 | |||
572 | void set_polyphony_rate(float rate) { polyphony_rate = rate; } | ||
573 | |||
574 | void enable_polyphony() { polyphony_rate = 5; } | ||
575 | |||
576 | void disable_polyphony() { polyphony_rate = 0; } | ||
577 | |||
578 | void increase_polyphony_rate(float change) { polyphony_rate *= change; } | ||
579 | |||
580 | void decrease_polyphony_rate(float change) { polyphony_rate /= change; } | ||
581 | |||
582 | // Timbre function | ||
583 | |||
584 | void set_timbre(float timbre) { note_timbre = timbre; } | ||
585 | |||
586 | // Tempo functions | ||
587 | |||
588 | void set_tempo(uint8_t tempo) { note_tempo = tempo; } | ||
589 | |||
590 | void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } | ||
591 | |||
592 | void increase_tempo(uint8_t tempo_change) { | ||
593 | if (note_tempo - tempo_change < 10) { | ||
594 | note_tempo = 10; | ||
595 | } else { | ||
596 | note_tempo -= tempo_change; | ||
597 | } | ||
598 | } | ||
599 | |||
600 | //------------------------------------------------------------------------------ | ||
601 | // Override these functions in your keymap file to play different tunes on | ||
602 | // startup and bootloader jump | ||
603 | __attribute__((weak)) void play_startup_tone() {} | ||
604 | |||
605 | __attribute__((weak)) void play_goodbye_tone() {} | ||
606 | //------------------------------------------------------------------------------ | ||
diff --git a/quantum/audio/driver_avr_pwm.h b/quantum/audio/driver_avr_pwm.h new file mode 100644 index 000000000..d6eb3571d --- /dev/null +++ b/quantum/audio/driver_avr_pwm.h | |||
@@ -0,0 +1,17 @@ | |||
1 | /* Copyright 2020 Jack Humbert | ||
2 | * Copyright 2020 JohSchneider | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | #pragma once | ||
diff --git a/quantum/audio/driver_avr_pwm_hardware.c b/quantum/audio/driver_avr_pwm_hardware.c new file mode 100644 index 000000000..492b9bfb0 --- /dev/null +++ b/quantum/audio/driver_avr_pwm_hardware.c | |||
@@ -0,0 +1,322 @@ | |||
1 | /* Copyright 2016 Jack Humbert | ||
2 | * Copyright 2020 JohSchneider | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #if defined(__AVR__) | ||
19 | # include <avr/pgmspace.h> | ||
20 | # include <avr/interrupt.h> | ||
21 | # include <avr/io.h> | ||
22 | #endif | ||
23 | |||
24 | #include "audio.h" | ||
25 | |||
26 | extern bool playing_note; | ||
27 | extern bool playing_melody; | ||
28 | extern uint8_t note_timbre; | ||
29 | |||
30 | #define CPU_PRESCALER 8 | ||
31 | |||
32 | /* | ||
33 | Audio Driver: PWM | ||
34 | |||
35 | drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4. | ||
36 | |||
37 | the primary channel_1 can be connected to either pin PC4 PC5 or PC6 (the later being used by most AVR based keyboards) with a PMW signal generated by timer3 | ||
38 | and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1 | ||
39 | |||
40 | alternatively, the PWM pins on PORTB can be used as only/primary speaker | ||
41 | */ | ||
42 | |||
43 | #if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) | ||
44 | # error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options." | ||
45 | #endif | ||
46 | |||
47 | #if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6) | ||
48 | # define AUDIO1_PIN_SET | ||
49 | # define AUDIO1_TIMSKx TIMSK3 | ||
50 | # define AUDIO1_TCCRxA TCCR3A | ||
51 | # define AUDIO1_TCCRxB TCCR3B | ||
52 | # define AUDIO1_ICRx ICR3 | ||
53 | # define AUDIO1_WGMx0 WGM30 | ||
54 | # define AUDIO1_WGMx1 WGM31 | ||
55 | # define AUDIO1_WGMx2 WGM32 | ||
56 | # define AUDIO1_WGMx3 WGM33 | ||
57 | # define AUDIO1_CSx0 CS30 | ||
58 | # define AUDIO1_CSx1 CS31 | ||
59 | # define AUDIO1_CSx2 CS32 | ||
60 | |||
61 | # if (AUDIO_PIN == C6) | ||
62 | # define AUDIO1_COMxy0 COM3A0 | ||
63 | # define AUDIO1_COMxy1 COM3A1 | ||
64 | # define AUDIO1_OCIExy OCIE3A | ||
65 | # define AUDIO1_OCRxy OCR3A | ||
66 | # define AUDIO1_PIN C6 | ||
67 | # define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect | ||
68 | # elif (AUDIO_PIN == C5) | ||
69 | # define AUDIO1_COMxy0 COM3B0 | ||
70 | # define AUDIO1_COMxy1 COM3B1 | ||
71 | # define AUDIO1_OCIExy OCIE3B | ||
72 | # define AUDIO1_OCRxy OCR3B | ||
73 | # define AUDIO1_PIN C5 | ||
74 | # define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect | ||
75 | # elif (AUDIO_PIN == C4) | ||
76 | # define AUDIO1_COMxy0 COM3C0 | ||
77 | # define AUDIO1_COMxy1 COM3C1 | ||
78 | # define AUDIO1_OCIExy OCIE3C | ||
79 | # define AUDIO1_OCRxy OCR3C | ||
80 | # define AUDIO1_PIN C4 | ||
81 | # define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect | ||
82 | # endif | ||
83 | #endif | ||
84 | |||
85 | #if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT) | ||
86 | # error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense." | ||
87 | #endif | ||
88 | |||
89 | #if ((AUDIO_PIN == B5) && ((AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B6) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B7) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6))) | ||
90 | # error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported." | ||
91 | #endif | ||
92 | |||
93 | #if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7) | ||
94 | # error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported." | ||
95 | #endif | ||
96 | |||
97 | #if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7) | ||
98 | # define AUDIO2_PIN_SET | ||
99 | # define AUDIO2_TIMSKx TIMSK1 | ||
100 | # define AUDIO2_TCCRxA TCCR1A | ||
101 | # define AUDIO2_TCCRxB TCCR1B | ||
102 | # define AUDIO2_ICRx ICR1 | ||
103 | # define AUDIO2_WGMx0 WGM10 | ||
104 | # define AUDIO2_WGMx1 WGM11 | ||
105 | # define AUDIO2_WGMx2 WGM12 | ||
106 | # define AUDIO2_WGMx3 WGM13 | ||
107 | # define AUDIO2_CSx0 CS10 | ||
108 | # define AUDIO2_CSx1 CS11 | ||
109 | # define AUDIO2_CSx2 CS12 | ||
110 | |||
111 | # if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5) | ||
112 | # define AUDIO2_COMxy0 COM1A0 | ||
113 | # define AUDIO2_COMxy1 COM1A1 | ||
114 | # define AUDIO2_OCIExy OCIE1A | ||
115 | # define AUDIO2_OCRxy OCR1A | ||
116 | # define AUDIO2_PIN B5 | ||
117 | # define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect | ||
118 | # elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6) | ||
119 | # define AUDIO2_COMxy0 COM1B0 | ||
120 | # define AUDIO2_COMxy1 COM1B1 | ||
121 | # define AUDIO2_OCIExy OCIE1B | ||
122 | # define AUDIO2_OCRxy OCR1B | ||
123 | # define AUDIO2_PIN B6 | ||
124 | # define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect | ||
125 | # elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7) | ||
126 | # define AUDIO2_COMxy0 COM1C0 | ||
127 | # define AUDIO2_COMxy1 COM1C1 | ||
128 | # define AUDIO2_OCIExy OCIE1C | ||
129 | # define AUDIO2_OCRxy OCR1C | ||
130 | # define AUDIO2_PIN B7 | ||
131 | # define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect | ||
132 | # endif | ||
133 | #endif | ||
134 | |||
135 | // C6 seems to be the assumed default by many existing keyboard - but sill warn the user | ||
136 | #if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET) | ||
137 | # pragma message "Audio feature enabled, but no suitable pin selected - see docs/feature_audio under the AVR settings for available options. Don't expect to hear anything... :-)" | ||
138 | // TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define | ||
139 | #endif | ||
140 | // ----------------------------------------------------------------------------- | ||
141 | |||
142 | #ifdef AUDIO1_PIN_SET | ||
143 | static float channel_1_frequency = 0.0f; | ||
144 | void channel_1_set_frequency(float freq) { | ||
145 | if (freq == 0.0f) // a pause/rest is a valid "note" with freq=0 | ||
146 | { | ||
147 | // disable the output, but keep the pwm-ISR going (with the previous | ||
148 | // frequency) so the audio-state keeps getting updated | ||
149 | // Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet | ||
150 | AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0)); | ||
151 | return; | ||
152 | } else { | ||
153 | AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode | ||
154 | } | ||
155 | |||
156 | channel_1_frequency = freq; | ||
157 | |||
158 | // set pwm period | ||
159 | AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
160 | // and duty cycle | ||
161 | AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100); | ||
162 | } | ||
163 | |||
164 | void channel_1_start(void) { | ||
165 | // enable timer-counter ISR | ||
166 | AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy); | ||
167 | // enable timer-counter output | ||
168 | AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); | ||
169 | } | ||
170 | |||
171 | void channel_1_stop(void) { | ||
172 | // disable timer-counter ISR | ||
173 | AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy); | ||
174 | // disable timer-counter output | ||
175 | AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0)); | ||
176 | } | ||
177 | #endif | ||
178 | |||
179 | #ifdef AUDIO2_PIN_SET | ||
180 | static float channel_2_frequency = 0.0f; | ||
181 | void channel_2_set_frequency(float freq) { | ||
182 | if (freq == 0.0f) { | ||
183 | AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0)); | ||
184 | return; | ||
185 | } else { | ||
186 | AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1); | ||
187 | } | ||
188 | |||
189 | channel_2_frequency = freq; | ||
190 | |||
191 | AUDIO2_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
192 | AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100); | ||
193 | } | ||
194 | |||
195 | float channel_2_get_frequency(void) { return channel_2_frequency; } | ||
196 | |||
197 | void channel_2_start(void) { | ||
198 | AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy); | ||
199 | AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1); | ||
200 | } | ||
201 | |||
202 | void channel_2_stop(void) { | ||
203 | AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy); | ||
204 | AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0)); | ||
205 | } | ||
206 | #endif | ||
207 | |||
208 | void audio_driver_initialize() { | ||
209 | #ifdef AUDIO1_PIN_SET | ||
210 | channel_1_stop(); | ||
211 | setPinOutput(AUDIO1_PIN); | ||
212 | #endif | ||
213 | |||
214 | #ifdef AUDIO2_PIN_SET | ||
215 | channel_2_stop(); | ||
216 | setPinOutput(AUDIO2_PIN); | ||
217 | #endif | ||
218 | |||
219 | // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B | ||
220 | // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation | ||
221 | // OC3A -- PC6 | ||
222 | // OC3B -- PC5 | ||
223 | // OC3C -- PC4 | ||
224 | // OC1A -- PB5 | ||
225 | // OC1B -- PB6 | ||
226 | // OC1C -- PB7 | ||
227 | |||
228 | // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A) | ||
229 | // OCR3A - PC6 | ||
230 | // OCR3B - PC5 | ||
231 | // OCR3C - PC4 | ||
232 | // OCR1A - PB5 | ||
233 | // OCR1B - PB6 | ||
234 | // OCR1C - PB7 | ||
235 | |||
236 | // Clock Select (CS3n) = 0b010 = Clock / 8 | ||
237 | #ifdef AUDIO1_PIN_SET | ||
238 | // initialize timer-counter | ||
239 | AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0); | ||
240 | AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0); | ||
241 | #endif | ||
242 | |||
243 | #ifdef AUDIO2_PIN_SET | ||
244 | AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0); | ||
245 | AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0); | ||
246 | #endif | ||
247 | } | ||
248 | |||
249 | void audio_driver_stop() { | ||
250 | #ifdef AUDIO1_PIN_SET | ||
251 | channel_1_stop(); | ||
252 | #endif | ||
253 | |||
254 | #ifdef AUDIO2_PIN_SET | ||
255 | channel_2_stop(); | ||
256 | #endif | ||
257 | } | ||
258 | |||
259 | void audio_driver_start(void) { | ||
260 | #ifdef AUDIO1_PIN_SET | ||
261 | channel_1_start(); | ||
262 | if (playing_note) { | ||
263 | channel_1_set_frequency(audio_get_processed_frequency(0)); | ||
264 | } | ||
265 | #endif | ||
266 | |||
267 | #if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET) | ||
268 | channel_2_start(); | ||
269 | if (playing_note) { | ||
270 | channel_2_set_frequency(audio_get_processed_frequency(0)); | ||
271 | } | ||
272 | #endif | ||
273 | } | ||
274 | |||
275 | static volatile uint32_t isr_counter = 0; | ||
276 | #ifdef AUDIO1_PIN_SET | ||
277 | ISR(AUDIO1_TIMERx_COMPy_vect) { | ||
278 | isr_counter++; | ||
279 | if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return; | ||
280 | |||
281 | isr_counter = 0; | ||
282 | bool state_changed = audio_update_state(); | ||
283 | |||
284 | if (!playing_note && !playing_melody) { | ||
285 | channel_1_stop(); | ||
286 | # ifdef AUDIO2_PIN_SET | ||
287 | channel_2_stop(); | ||
288 | # endif | ||
289 | return; | ||
290 | } | ||
291 | |||
292 | if (state_changed) { | ||
293 | channel_1_set_frequency(audio_get_processed_frequency(0)); | ||
294 | # ifdef AUDIO2_PIN_SET | ||
295 | if (audio_get_number_of_active_tones() > 1) { | ||
296 | channel_2_set_frequency(audio_get_processed_frequency(1)); | ||
297 | } else { | ||
298 | channel_2_stop(); | ||
299 | } | ||
300 | # endif | ||
301 | } | ||
302 | } | ||
303 | #endif | ||
304 | |||
305 | #if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET) | ||
306 | ISR(AUDIO2_TIMERx_COMPy_vect) { | ||
307 | isr_counter++; | ||
308 | if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return; | ||
309 | |||
310 | isr_counter = 0; | ||
311 | bool state_changed = audio_update_state(); | ||
312 | |||
313 | if (!playing_note && !playing_melody) { | ||
314 | channel_2_stop(); | ||
315 | return; | ||
316 | } | ||
317 | |||
318 | if (state_changed) { | ||
319 | channel_2_set_frequency(audio_get_processed_frequency(0)); | ||
320 | } | ||
321 | } | ||
322 | #endif | ||
diff --git a/quantum/audio/driver_chibios_dac.h b/quantum/audio/driver_chibios_dac.h new file mode 100644 index 000000000..07cd622ea --- /dev/null +++ b/quantum/audio/driver_chibios_dac.h | |||
@@ -0,0 +1,126 @@ | |||
1 | /* Copyright 2019 Jack Humbert | ||
2 | * Copyright 2020 JohSchneider | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | #pragma once | ||
18 | |||
19 | #ifndef A4 | ||
20 | # define A4 PAL_LINE(GPIOA, 4) | ||
21 | #endif | ||
22 | #ifndef A5 | ||
23 | # define A5 PAL_LINE(GPIOA, 5) | ||
24 | #endif | ||
25 | |||
26 | /** | ||
27 | * Size of the dac_buffer arrays. All must be the same size. | ||
28 | */ | ||
29 | #define AUDIO_DAC_BUFFER_SIZE 256U | ||
30 | |||
31 | /** | ||
32 | * Highest value allowed sample value. | ||
33 | |||
34 | * since the DAC is limited to 12 bit, the absolute max is 0xfff = 4095U; | ||
35 | * lower values adjust the peak-voltage aka volume down. | ||
36 | * adjusting this value has only an effect on a sample-buffer whose values are | ||
37 | * are NOT pregenerated - see square-wave | ||
38 | */ | ||
39 | #ifndef AUDIO_DAC_SAMPLE_MAX | ||
40 | # define AUDIO_DAC_SAMPLE_MAX 4095U | ||
41 | #endif | ||
42 | |||
43 | #if !defined(AUDIO_DAC_SAMPLE_RATE) && !defined(AUDIO_MAX_SIMULTANEOUS_TONES) && !defined(AUDIO_DAC_QUALITY_VERY_LOW) && !defined(AUDIO_DAC_QUALITY_LOW) && !defined(AUDIO_DAC_QUALITY_HIGH) && !defined(AUDIO_DAC_QUALITY_VERY_HIGH) | ||
44 | # define AUDIO_DAC_QUALITY_SANE_MINIMUM | ||
45 | #endif | ||
46 | |||
47 | /** | ||
48 | * These presets allow you to quickly switch between quality settings for | ||
49 | * the DAC. The sample rate and maximum number of simultaneous tones roughly | ||
50 | * has an inverse relationship - slightly higher sample rates may be possible. | ||
51 | * | ||
52 | * NOTE: a high sample-rate results in a higher cpu-load, which might lead to | ||
53 | * (audible) discontinuities and/or starve other processes of cpu-time | ||
54 | * (like RGB-led back-lighting, ...) | ||
55 | */ | ||
56 | #ifdef AUDIO_DAC_QUALITY_VERY_LOW | ||
57 | # define AUDIO_DAC_SAMPLE_RATE 11025U | ||
58 | # define AUDIO_MAX_SIMULTANEOUS_TONES 8 | ||
59 | #endif | ||
60 | |||
61 | #ifdef AUDIO_DAC_QUALITY_LOW | ||
62 | # define AUDIO_DAC_SAMPLE_RATE 22050U | ||
63 | # define AUDIO_MAX_SIMULTANEOUS_TONES 4 | ||
64 | #endif | ||
65 | |||
66 | #ifdef AUDIO_DAC_QUALITY_HIGH | ||
67 | # define AUDIO_DAC_SAMPLE_RATE 44100U | ||
68 | # define AUDIO_MAX_SIMULTANEOUS_TONES 2 | ||
69 | #endif | ||
70 | |||
71 | #ifdef AUDIO_DAC_QUALITY_VERY_HIGH | ||
72 | # define AUDIO_DAC_SAMPLE_RATE 88200U | ||
73 | # define AUDIO_MAX_SIMULTANEOUS_TONES 1 | ||
74 | #endif | ||
75 | |||
76 | #ifdef AUDIO_DAC_QUALITY_SANE_MINIMUM | ||
77 | /* a sane-minimum config: with a trade-off between cpu-load and tone-range | ||
78 | * | ||
79 | * the (currently) highest defined note is NOTE_B8 with 7902Hz; if we now | ||
80 | * aim for an even even multiple of the buffer-size, we end up with: | ||
81 | * ( roundUptoPow2(highest note / AUDIO_DAC_BUFFER_SIZE) * nyquist-rate * AUDIO_DAC_BUFFER_SIZE) | ||
82 | * 7902/256 = 30.867 * 2 * 256 ~= 16384 | ||
83 | * which works out (but the 'scope shows some sampling artifacts with lower harmonics :-P) | ||
84 | */ | ||
85 | # define AUDIO_DAC_SAMPLE_RATE 16384U | ||
86 | # define AUDIO_MAX_SIMULTANEOUS_TONES 8 | ||
87 | #endif | ||
88 | |||
89 | /** | ||
90 | * Effective bit-rate of the DAC. 44.1khz is the standard for most audio - any | ||
91 | * lower will sacrifice perceptible audio quality. Any higher will limit the | ||
92 | * number of simultaneous tones. In most situations, a tenth (1/10) of the | ||
93 | * sample rate is where notes become unbearable. | ||
94 | */ | ||
95 | #ifndef AUDIO_DAC_SAMPLE_RATE | ||
96 | # define AUDIO_DAC_SAMPLE_RATE 44100U | ||
97 | #endif | ||
98 | |||
99 | /** | ||
100 | * The number of tones that can be played simultaneously. If too high a value | ||
101 | * is used here, the keyboard will freeze and glitch-out when that many tones | ||
102 | * are being played. | ||
103 | */ | ||
104 | #ifndef AUDIO_MAX_SIMULTANEOUS_TONES | ||
105 | # define AUDIO_MAX_SIMULTANEOUS_TONES 2 | ||
106 | #endif | ||
107 | |||
108 | /** | ||
109 | * The default value of the DAC when not playing anything. Certain hardware | ||
110 | * setups may require a high (AUDIO_DAC_SAMPLE_MAX) or low (0) value here. | ||
111 | * Since multiple added sine waves tend to oscillate around the midpoint, | ||
112 | * and possibly never/rarely reach either 0 of MAX, 1/2 MAX can be a | ||
113 | * reasonable default value. | ||
114 | */ | ||
115 | #ifndef AUDIO_DAC_OFF_VALUE | ||
116 | # define AUDIO_DAC_OFF_VALUE AUDIO_DAC_SAMPLE_MAX / 2 | ||
117 | #endif | ||
118 | |||
119 | #if AUDIO_DAC_OFF_VALUE > AUDIO_DAC_SAMPLE_MAX | ||
120 | # error "AUDIO_DAC: OFF_VALUE may not be larger than SAMPLE_MAX" | ||
121 | #endif | ||
122 | |||
123 | /** | ||
124 | *user overridable sample generation/processing | ||
125 | */ | ||
126 | uint16_t dac_value_generate(void); | ||
diff --git a/quantum/audio/driver_chibios_dac_additive.c b/quantum/audio/driver_chibios_dac_additive.c new file mode 100644 index 000000000..db304adb8 --- /dev/null +++ b/quantum/audio/driver_chibios_dac_additive.c | |||
@@ -0,0 +1,335 @@ | |||
1 | /* Copyright 2016-2019 Jack Humbert | ||
2 | * Copyright 2020 JohSchneider | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include "audio.h" | ||
19 | #include <ch.h> | ||
20 | #include <hal.h> | ||
21 | |||
22 | /* | ||
23 | Audio Driver: DAC | ||
24 | |||
25 | which utilizes the dac unit many STM32 are equipped with, to output a modulated waveform from samples stored in the dac_buffer_* array who are passed to the hardware through DMA | ||
26 | |||
27 | it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate' | ||
28 | |||
29 | this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis | ||
30 | */ | ||
31 | |||
32 | #if !defined(AUDIO_PIN) | ||
33 | # error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options." | ||
34 | #endif | ||
35 | #if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
36 | # pragma message "Audio feature: AUDIO_PIN_ALT set, but not AUDIO_PIN_ALT_AS_NEGATIVE - pin will be left unused; audio might still work though." | ||
37 | #endif | ||
38 | |||
39 | #if !defined(AUDIO_PIN_ALT) | ||
40 | // no ALT pin defined is valid, but the c-ifs below need some value set | ||
41 | # define AUDIO_PIN_ALT PAL_NOLINE | ||
42 | #endif | ||
43 | |||
44 | #if !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID) | ||
45 | # define AUDIO_DAC_SAMPLE_WAVEFORM_SINE | ||
46 | #endif | ||
47 | |||
48 | #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE | ||
49 | /* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0 | ||
50 | */ | ||
51 | static const dacsample_t dac_buffer_sine[AUDIO_DAC_BUFFER_SIZE] = { | ||
52 | // 256 values, max 4095 | ||
53 | 0x0, 0x1, 0x2, 0x6, 0xa, 0xf, 0x16, 0x1e, 0x27, 0x32, 0x3d, 0x4a, 0x58, 0x67, 0x78, 0x89, 0x9c, 0xb0, 0xc5, 0xdb, 0xf2, 0x10a, 0x123, 0x13e, 0x159, 0x175, 0x193, 0x1b1, 0x1d1, 0x1f1, 0x212, 0x235, 0x258, 0x27c, 0x2a0, 0x2c6, 0x2ed, 0x314, 0x33c, 0x365, 0x38e, 0x3b8, 0x3e3, 0x40e, 0x43a, 0x467, 0x494, 0x4c2, 0x4f0, 0x51f, 0x54e, 0x57d, 0x5ad, 0x5dd, 0x60e, 0x63f, 0x670, 0x6a1, 0x6d3, 0x705, 0x737, 0x769, 0x79b, 0x7cd, 0x800, 0x832, 0x864, 0x896, 0x8c8, 0x8fa, 0x92c, 0x95e, 0x98f, 0x9c0, 0x9f1, 0xa22, 0xa52, 0xa82, 0xab1, 0xae0, 0xb0f, 0xb3d, 0xb6b, 0xb98, 0xbc5, 0xbf1, 0xc1c, 0xc47, 0xc71, 0xc9a, 0xcc3, 0xceb, 0xd12, 0xd39, 0xd5f, 0xd83, 0xda7, 0xdca, 0xded, 0xe0e, 0xe2e, 0xe4e, 0xe6c, 0xe8a, 0xea6, 0xec1, 0xedc, 0xef5, 0xf0d, 0xf24, 0xf3a, 0xf4f, 0xf63, 0xf76, 0xf87, 0xf98, 0xfa7, 0xfb5, 0xfc2, 0xfcd, 0xfd8, 0xfe1, 0xfe9, 0xff0, 0xff5, 0xff9, 0xffd, 0xffe, | ||
54 | 0xfff, 0xffe, 0xffd, 0xff9, 0xff5, 0xff0, 0xfe9, 0xfe1, 0xfd8, 0xfcd, 0xfc2, 0xfb5, 0xfa7, 0xf98, 0xf87, 0xf76, 0xf63, 0xf4f, 0xf3a, 0xf24, 0xf0d, 0xef5, 0xedc, 0xec1, 0xea6, 0xe8a, 0xe6c, 0xe4e, 0xe2e, 0xe0e, 0xded, 0xdca, 0xda7, 0xd83, 0xd5f, 0xd39, 0xd12, 0xceb, 0xcc3, 0xc9a, 0xc71, 0xc47, 0xc1c, 0xbf1, 0xbc5, 0xb98, 0xb6b, 0xb3d, 0xb0f, 0xae0, 0xab1, 0xa82, 0xa52, 0xa22, 0x9f1, 0x9c0, 0x98f, 0x95e, 0x92c, 0x8fa, 0x8c8, 0x896, 0x864, 0x832, 0x800, 0x7cd, 0x79b, 0x769, 0x737, 0x705, 0x6d3, 0x6a1, 0x670, 0x63f, 0x60e, 0x5dd, 0x5ad, 0x57d, 0x54e, 0x51f, 0x4f0, 0x4c2, 0x494, 0x467, 0x43a, 0x40e, 0x3e3, 0x3b8, 0x38e, 0x365, 0x33c, 0x314, 0x2ed, 0x2c6, 0x2a0, 0x27c, 0x258, 0x235, 0x212, 0x1f1, 0x1d1, 0x1b1, 0x193, 0x175, 0x159, 0x13e, 0x123, 0x10a, 0xf2, 0xdb, 0xc5, 0xb0, 0x9c, 0x89, 0x78, 0x67, 0x58, 0x4a, 0x3d, 0x32, 0x27, 0x1e, 0x16, 0xf, 0xa, 0x6, 0x2, 0x1}; | ||
55 | #endif // AUDIO_DAC_SAMPLE_WAVEFORM_SINE | ||
56 | #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE | ||
57 | static const dacsample_t dac_buffer_triangle[AUDIO_DAC_BUFFER_SIZE] = { | ||
58 | // 256 values, max 4095 | ||
59 | 0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x100, 0x120, 0x140, 0x160, 0x180, 0x1a0, 0x1c0, 0x1e0, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0x400, 0x420, 0x440, 0x460, 0x480, 0x4a0, 0x4c0, 0x4e0, 0x500, 0x520, 0x540, 0x560, 0x580, 0x5a0, 0x5c0, 0x5e0, 0x600, 0x620, 0x640, 0x660, 0x680, 0x6a0, 0x6c0, 0x6e0, 0x700, 0x720, 0x740, 0x760, 0x780, 0x7a0, 0x7c0, 0x7e0, 0x800, 0x81f, 0x83f, 0x85f, 0x87f, 0x89f, 0x8bf, 0x8df, 0x8ff, 0x91f, 0x93f, 0x95f, 0x97f, 0x99f, 0x9bf, 0x9df, 0x9ff, 0xa1f, 0xa3f, 0xa5f, 0xa7f, 0xa9f, 0xabf, 0xadf, 0xaff, 0xb1f, 0xb3f, 0xb5f, 0xb7f, 0xb9f, 0xbbf, 0xbdf, 0xbff, 0xc1f, 0xc3f, 0xc5f, 0xc7f, 0xc9f, 0xcbf, 0xcdf, 0xcff, 0xd1f, 0xd3f, 0xd5f, 0xd7f, 0xd9f, 0xdbf, 0xddf, 0xdff, 0xe1f, 0xe3f, 0xe5f, 0xe7f, 0xe9f, 0xebf, 0xedf, 0xeff, 0xf1f, 0xf3f, 0xf5f, 0xf7f, 0xf9f, 0xfbf, 0xfdf, | ||
60 | 0xfff, 0xfdf, 0xfbf, 0xf9f, 0xf7f, 0xf5f, 0xf3f, 0xf1f, 0xeff, 0xedf, 0xebf, 0xe9f, 0xe7f, 0xe5f, 0xe3f, 0xe1f, 0xdff, 0xddf, 0xdbf, 0xd9f, 0xd7f, 0xd5f, 0xd3f, 0xd1f, 0xcff, 0xcdf, 0xcbf, 0xc9f, 0xc7f, 0xc5f, 0xc3f, 0xc1f, 0xbff, 0xbdf, 0xbbf, 0xb9f, 0xb7f, 0xb5f, 0xb3f, 0xb1f, 0xaff, 0xadf, 0xabf, 0xa9f, 0xa7f, 0xa5f, 0xa3f, 0xa1f, 0x9ff, 0x9df, 0x9bf, 0x99f, 0x97f, 0x95f, 0x93f, 0x91f, 0x8ff, 0x8df, 0x8bf, 0x89f, 0x87f, 0x85f, 0x83f, 0x81f, 0x800, 0x7e0, 0x7c0, 0x7a0, 0x780, 0x760, 0x740, 0x720, 0x700, 0x6e0, 0x6c0, 0x6a0, 0x680, 0x660, 0x640, 0x620, 0x600, 0x5e0, 0x5c0, 0x5a0, 0x580, 0x560, 0x540, 0x520, 0x500, 0x4e0, 0x4c0, 0x4a0, 0x480, 0x460, 0x440, 0x420, 0x400, 0x3e0, 0x3c0, 0x3a0, 0x380, 0x360, 0x340, 0x320, 0x300, 0x2e0, 0x2c0, 0x2a0, 0x280, 0x260, 0x240, 0x220, 0x200, 0x1e0, 0x1c0, 0x1a0, 0x180, 0x160, 0x140, 0x120, 0x100, 0xe0, 0xc0, 0xa0, 0x80, 0x60, 0x40, 0x20}; | ||
61 | #endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE | ||
62 | #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE | ||
63 | static const dacsample_t dac_buffer_square[AUDIO_DAC_BUFFER_SIZE] = { | ||
64 | [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0, // first and | ||
65 | [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX, // second half | ||
66 | }; | ||
67 | #endif // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE | ||
68 | /* | ||
69 | // four steps: 0, 1/3, 2/3 and 1 | ||
70 | static const dacsample_t dac_buffer_staircase[AUDIO_DAC_BUFFER_SIZE] = { | ||
71 | [0 ... AUDIO_DAC_BUFFER_SIZE/3 -1 ] = 0, | ||
72 | [AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE / 2 -1 ] = AUDIO_DAC_SAMPLE_MAX / 3, | ||
73 | [AUDIO_DAC_BUFFER_SIZE / 2 ... 3 * AUDIO_DAC_BUFFER_SIZE / 4 -1 ] = 2 * AUDIO_DAC_SAMPLE_MAX / 3, | ||
74 | [3 * AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE -1 ] = AUDIO_DAC_SAMPLE_MAX, | ||
75 | } | ||
76 | */ | ||
77 | #ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID | ||
78 | static const dacsample_t dac_buffer_trapezoid[AUDIO_DAC_BUFFER_SIZE] = {0x0, 0x1f, 0x7f, 0xdf, 0x13f, 0x19f, 0x1ff, 0x25f, 0x2bf, 0x31f, 0x37f, 0x3df, 0x43f, 0x49f, 0x4ff, 0x55f, 0x5bf, 0x61f, 0x67f, 0x6df, 0x73f, 0x79f, 0x7ff, 0x85f, 0x8bf, 0x91f, 0x97f, 0x9df, 0xa3f, 0xa9f, 0xaff, 0xb5f, 0xbbf, 0xc1f, 0xc7f, 0xcdf, 0xd3f, 0xd9f, 0xdff, 0xe5f, 0xebf, 0xf1f, 0xf7f, 0xfdf, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, | ||
79 | 0xfff, 0xfdf, 0xf7f, 0xf1f, 0xebf, 0xe5f, 0xdff, 0xd9f, 0xd3f, 0xcdf, 0xc7f, 0xc1f, 0xbbf, 0xb5f, 0xaff, 0xa9f, 0xa3f, 0x9df, 0x97f, 0x91f, 0x8bf, 0x85f, 0x7ff, 0x79f, 0x73f, 0x6df, 0x67f, 0x61f, 0x5bf, 0x55f, 0x4ff, 0x49f, 0x43f, 0x3df, 0x37f, 0x31f, 0x2bf, 0x25f, 0x1ff, 0x19f, 0x13f, 0xdf, 0x7f, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; | ||
80 | #endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID | ||
81 | |||
82 | static dacsample_t dac_buffer_empty[AUDIO_DAC_BUFFER_SIZE] = {AUDIO_DAC_OFF_VALUE}; | ||
83 | |||
84 | /* keep track of the sample position for for each frequency */ | ||
85 | static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0}; | ||
86 | |||
87 | static float active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0, 0}; | ||
88 | static uint8_t active_tones_snapshot_length = 0; | ||
89 | |||
90 | typedef enum { | ||
91 | OUTPUT_SHOULD_START, | ||
92 | OUTPUT_RUN_NORMALLY, | ||
93 | // path 1: wait for zero, then change/update active tones | ||
94 | OUTPUT_TONES_CHANGED, | ||
95 | OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE, | ||
96 | // path 2: hardware should stop, wait for zero then turn output off = stop the timer | ||
97 | OUTPUT_SHOULD_STOP, | ||
98 | OUTPUT_REACHED_ZERO_BEFORE_OFF, | ||
99 | OUTPUT_OFF, | ||
100 | OUTPUT_OFF_1, | ||
101 | OUTPUT_OFF_2, // trailing off: giving the DAC two more conversion cycles until the AUDIO_DAC_OFF_VALUE reaches the output, then turn the timer off, which leaves the output at that level | ||
102 | number_of_output_states | ||
103 | } output_states_t; | ||
104 | output_states_t state = OUTPUT_OFF_2; | ||
105 | |||
106 | /** | ||
107 | * Generation of the waveform being passed to the callback. Declared weak so users | ||
108 | * can override it with their own wave-forms/noises. | ||
109 | */ | ||
110 | __attribute__((weak)) uint16_t dac_value_generate(void) { | ||
111 | // DAC is running/asking for values but snapshot length is zero -> must be playing a pause | ||
112 | if (active_tones_snapshot_length == 0) { | ||
113 | return AUDIO_DAC_OFF_VALUE; | ||
114 | } | ||
115 | |||
116 | /* doing additive wave synthesis over all currently playing tones = adding up | ||
117 | * sine-wave-samples for each frequency, scaled by the number of active tones | ||
118 | */ | ||
119 | uint16_t value = 0; | ||
120 | float frequency = 0.0f; | ||
121 | |||
122 | for (uint8_t i = 0; i < active_tones_snapshot_length; i++) { | ||
123 | /* Note: a user implementation does not have to rely on the active_tones_snapshot, but | ||
124 | * could directly query the active frequencies through audio_get_processed_frequency */ | ||
125 | frequency = active_tones_snapshot[i]; | ||
126 | |||
127 | dac_if[i] = dac_if[i] + ((frequency * AUDIO_DAC_BUFFER_SIZE) / AUDIO_DAC_SAMPLE_RATE) * 2 / 3; | ||
128 | /*Note: the 2/3 are necessary to get the correct frequencies on the | ||
129 | * DAC output (as measured with an oscilloscope), since the gpt | ||
130 | * timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback | ||
131 | * is called twice per conversion.*/ | ||
132 | |||
133 | dac_if[i] = fmod(dac_if[i], AUDIO_DAC_BUFFER_SIZE); | ||
134 | |||
135 | // Wavetable generation/lookup | ||
136 | uint16_t dac_i = (uint16_t)dac_if[i]; | ||
137 | |||
138 | #if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) | ||
139 | value += dac_buffer_sine[dac_i] / active_tones_snapshot_length; | ||
140 | #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) | ||
141 | value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length; | ||
142 | #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID) | ||
143 | value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length; | ||
144 | #elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) | ||
145 | value += dac_buffer_square[dac_i] / active_tones_snapshot_length; | ||
146 | #endif | ||
147 | /* | ||
148 | // SINE | ||
149 | value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3; | ||
150 | // TRIANGLE | ||
151 | value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3; | ||
152 | // SQUARE | ||
153 | value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3; | ||
154 | //NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P | ||
155 | */ | ||
156 | |||
157 | // STAIRS (mostly usefully as test-pattern) | ||
158 | // value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length; | ||
159 | } | ||
160 | |||
161 | return value; | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * DAC streaming callback. Does all of the main computing for playing songs. | ||
166 | * | ||
167 | * Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'. | ||
168 | */ | ||
169 | static void dac_end(DACDriver *dacp) { | ||
170 | dacsample_t *sample_p = (dacp)->samples; | ||
171 | |||
172 | // work on the other half of the buffer | ||
173 | if (dacIsBufferComplete(dacp)) { | ||
174 | sample_p += AUDIO_DAC_BUFFER_SIZE / 2; // 'half_index' | ||
175 | } | ||
176 | |||
177 | for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) { | ||
178 | if (OUTPUT_OFF <= state) { | ||
179 | sample_p[s] = AUDIO_DAC_OFF_VALUE; | ||
180 | continue; | ||
181 | } else { | ||
182 | sample_p[s] = dac_value_generate(); | ||
183 | } | ||
184 | |||
185 | /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX) | ||
186 | * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX | ||
187 | * * * | ||
188 | * * * | ||
189 | * --------------------------------------------------------- | ||
190 | * * * } AUDIO_DAC_SAMPLE_MAX/100 | ||
191 | * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE | ||
192 | * * * } AUDIO_DAC_SAMPLE_MAX/100 | ||
193 | * --------------------------------------------------------- | ||
194 | * * | ||
195 | * * * | ||
196 | * * * | ||
197 | * =====*=*================================================= 0x0 | ||
198 | */ | ||
199 | if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) && // value approaches from below | ||
200 | (sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100))) // or above | ||
201 | ) { | ||
202 | if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) { | ||
203 | state = OUTPUT_RUN_NORMALLY; | ||
204 | } else if (OUTPUT_TONES_CHANGED == state) { | ||
205 | state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE; | ||
206 | } else if (OUTPUT_SHOULD_STOP == state) { | ||
207 | state = OUTPUT_REACHED_ZERO_BEFORE_OFF; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | // still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover | ||
212 | if (OUTPUT_SHOULD_START == state) { | ||
213 | sample_p[s] = AUDIO_DAC_OFF_VALUE; | ||
214 | } | ||
215 | |||
216 | if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) { | ||
217 | uint8_t active_tones = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones()); | ||
218 | active_tones_snapshot_length = 0; | ||
219 | // update the snapshot - once, and only on occasion that something changed; | ||
220 | // -> saves cpu cycles (?) | ||
221 | for (uint8_t i = 0; i < active_tones; i++) { | ||
222 | float freq = audio_get_processed_frequency(i); | ||
223 | if (freq > 0) { // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step | ||
224 | active_tones_snapshot[active_tones_snapshot_length++] = freq; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) { | ||
229 | state = OUTPUT_OFF; | ||
230 | } | ||
231 | if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) { | ||
232 | state = OUTPUT_RUN_NORMALLY; | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | |||
237 | // update audio internal state (note position, current_note, ...) | ||
238 | if (audio_update_state()) { | ||
239 | if (OUTPUT_SHOULD_STOP != state) { | ||
240 | state = OUTPUT_TONES_CHANGED; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | if (OUTPUT_OFF <= state) { | ||
245 | if (OUTPUT_OFF_2 == state) { | ||
246 | // stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE | ||
247 | gptStopTimer(&GPTD6); | ||
248 | } else { | ||
249 | state++; | ||
250 | } | ||
251 | } | ||
252 | } | ||
253 | |||
254 | static void dac_error(DACDriver *dacp, dacerror_t err) { | ||
255 | (void)dacp; | ||
256 | (void)err; | ||
257 | |||
258 | chSysHalt("DAC failure. halp"); | ||
259 | } | ||
260 | |||
261 | static const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3, | ||
262 | .callback = NULL, | ||
263 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
264 | .dier = 0U}; | ||
265 | |||
266 | static const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT}; | ||
267 | |||
268 | /** | ||
269 | * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered | ||
270 | * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency | ||
271 | * to be a third of what we expect. | ||
272 | * | ||
273 | * Here are all the values for DAC_TRG (TSEL in the ref manual) | ||
274 | * TIM15_TRGO 0b011 | ||
275 | * TIM2_TRGO 0b100 | ||
276 | * TIM3_TRGO 0b001 | ||
277 | * TIM6_TRGO 0b000 | ||
278 | * TIM7_TRGO 0b010 | ||
279 | * EXTI9 0b110 | ||
280 | * SWTRIG 0b111 | ||
281 | */ | ||
282 | static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)}; | ||
283 | |||
284 | void audio_driver_initialize() { | ||
285 | if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) { | ||
286 | palSetLineMode(A4, PAL_MODE_INPUT_ANALOG); | ||
287 | dacStart(&DACD1, &dac_conf); | ||
288 | } | ||
289 | if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) { | ||
290 | palSetLineMode(A5, PAL_MODE_INPUT_ANALOG); | ||
291 | dacStart(&DACD2, &dac_conf); | ||
292 | } | ||
293 | |||
294 | /* enable the output buffer, to directly drive external loads with no additional circuitry | ||
295 | * | ||
296 | * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers | ||
297 | * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer | ||
298 | * Note: enabling the output buffer imparts an additional dc-offset of a couple mV | ||
299 | * | ||
300 | * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet | ||
301 | * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.' | ||
302 | */ | ||
303 | DACD1.params->dac->CR &= ~DAC_CR_BOFF1; | ||
304 | DACD2.params->dac->CR &= ~DAC_CR_BOFF2; | ||
305 | |||
306 | if (AUDIO_PIN == A4) { | ||
307 | dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE); | ||
308 | } else if (AUDIO_PIN == A5) { | ||
309 | dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE); | ||
310 | } | ||
311 | |||
312 | // no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE | ||
313 | #if defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
314 | if (AUDIO_PIN_ALT == A4) { | ||
315 | dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE); | ||
316 | } else if (AUDIO_PIN_ALT == A5) { | ||
317 | dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE); | ||
318 | } | ||
319 | #endif | ||
320 | |||
321 | gptStart(&GPTD6, &gpt6cfg1); | ||
322 | } | ||
323 | |||
324 | void audio_driver_stop(void) { state = OUTPUT_SHOULD_STOP; } | ||
325 | |||
326 | void audio_driver_start(void) { | ||
327 | gptStartContinuous(&GPTD6, 2U); | ||
328 | |||
329 | for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) { | ||
330 | dac_if[i] = 0.0f; | ||
331 | active_tones_snapshot[i] = 0.0f; | ||
332 | } | ||
333 | active_tones_snapshot_length = 0; | ||
334 | state = OUTPUT_SHOULD_START; | ||
335 | } | ||
diff --git a/quantum/audio/driver_chibios_dac_basic.c b/quantum/audio/driver_chibios_dac_basic.c new file mode 100644 index 000000000..9a1c9a8c1 --- /dev/null +++ b/quantum/audio/driver_chibios_dac_basic.c | |||
@@ -0,0 +1,245 @@ | |||
1 | /* Copyright 2016-2020 Jack Humbert | ||
2 | * Copyright 2020 JohSchneider | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #include "audio.h" | ||
19 | #include "ch.h" | ||
20 | #include "hal.h" | ||
21 | |||
22 | /* | ||
23 | Audio Driver: DAC | ||
24 | |||
25 | which utilizes both channels of the DAC unit many STM32 are equipped with to output a modulated square-wave, from precomputed samples stored in a buffer, which is passed to the hardware through DMA | ||
26 | |||
27 | this driver can either be used to drive to separate speakers, wired to A4+Gnd and A5+Gnd, which allows two tones to be played simultaneously | ||
28 | OR | ||
29 | one speaker wired to A4+A5 with the AUDIO_PIN_ALT_AS_NEGATIVE define set - see docs/feature_audio | ||
30 | |||
31 | */ | ||
32 | |||
33 | #if !defined(AUDIO_PIN) | ||
34 | # pragma message "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC basic)' for available options." | ||
35 | // TODO: make this an 'error' instead; go through a breaking change, and add AUDIO_PIN A5 to all keyboards currently using AUDIO on STM32 based boards? - for now: set the define here | ||
36 | # define AUDIO_PIN A5 | ||
37 | #endif | ||
38 | // check configuration for ONE speaker, connected to both DAC pins | ||
39 | #if defined(AUDIO_PIN_ALT_AS_NEGATIVE) && !defined(AUDIO_PIN_ALT) | ||
40 | # error "Audio feature: AUDIO_PIN_ALT_AS_NEGATIVE set, but no pin configured as AUDIO_PIN_ALT" | ||
41 | #endif | ||
42 | |||
43 | #ifndef AUDIO_PIN_ALT | ||
44 | // no ALT pin defined is valid, but the c-ifs below need some value set | ||
45 | # define AUDIO_PIN_ALT -1 | ||
46 | #endif | ||
47 | |||
48 | #if !defined(AUDIO_STATE_TIMER) | ||
49 | # define AUDIO_STATE_TIMER GPTD8 | ||
50 | #endif | ||
51 | |||
52 | // square-wave | ||
53 | static const dacsample_t dac_buffer_1[AUDIO_DAC_BUFFER_SIZE] = { | ||
54 | // First half is max, second half is 0 | ||
55 | [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = AUDIO_DAC_SAMPLE_MAX, | ||
56 | [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = 0, | ||
57 | }; | ||
58 | |||
59 | // square-wave | ||
60 | static const dacsample_t dac_buffer_2[AUDIO_DAC_BUFFER_SIZE] = { | ||
61 | // opposite of dac_buffer above | ||
62 | [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0, | ||
63 | [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX, | ||
64 | }; | ||
65 | |||
66 | GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE, | ||
67 | .callback = NULL, | ||
68 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
69 | .dier = 0U}; | ||
70 | GPTConfig gpt7cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE, | ||
71 | .callback = NULL, | ||
72 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
73 | .dier = 0U}; | ||
74 | |||
75 | static void gpt_audio_state_cb(GPTDriver *gptp); | ||
76 | GPTConfig gptStateUpdateCfg = {.frequency = 10, | ||
77 | .callback = gpt_audio_state_cb, | ||
78 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
79 | .dier = 0U}; | ||
80 | |||
81 | static const DACConfig dac_conf_ch1 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT}; | ||
82 | static const DACConfig dac_conf_ch2 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT}; | ||
83 | |||
84 | /** | ||
85 | * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered | ||
86 | * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency | ||
87 | * to be a third of what we expect. | ||
88 | * | ||
89 | * Here are all the values for DAC_TRG (TSEL in the ref manual) | ||
90 | * TIM15_TRGO 0b011 | ||
91 | * TIM2_TRGO 0b100 | ||
92 | * TIM3_TRGO 0b001 | ||
93 | * TIM6_TRGO 0b000 | ||
94 | * TIM7_TRGO 0b010 | ||
95 | * EXTI9 0b110 | ||
96 | * SWTRIG 0b111 | ||
97 | */ | ||
98 | static const DACConversionGroup dac_conv_grp_ch1 = {.num_channels = 1U, .trigger = DAC_TRG(0b000)}; | ||
99 | static const DACConversionGroup dac_conv_grp_ch2 = {.num_channels = 1U, .trigger = DAC_TRG(0b010)}; | ||
100 | |||
101 | void channel_1_start(void) { | ||
102 | gptStart(&GPTD6, &gpt6cfg1); | ||
103 | gptStartContinuous(&GPTD6, 2U); | ||
104 | palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); | ||
105 | } | ||
106 | |||
107 | void channel_1_stop(void) { | ||
108 | gptStopTimer(&GPTD6); | ||
109 | palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL); | ||
110 | palSetPad(GPIOA, 4); | ||
111 | } | ||
112 | |||
113 | static float channel_1_frequency = 0.0f; | ||
114 | void channel_1_set_frequency(float freq) { | ||
115 | channel_1_frequency = freq; | ||
116 | |||
117 | channel_1_stop(); | ||
118 | if (freq <= 0.0) // a pause/rest has freq=0 | ||
119 | return; | ||
120 | |||
121 | gpt6cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE; | ||
122 | channel_1_start(); | ||
123 | } | ||
124 | float channel_1_get_frequency(void) { return channel_1_frequency; } | ||
125 | |||
126 | void channel_2_start(void) { | ||
127 | gptStart(&GPTD7, &gpt7cfg1); | ||
128 | gptStartContinuous(&GPTD7, 2U); | ||
129 | palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); | ||
130 | } | ||
131 | |||
132 | void channel_2_stop(void) { | ||
133 | gptStopTimer(&GPTD7); | ||
134 | palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL); \ | ||
135 | palSetPad(GPIOA, 5); | ||
136 | } | ||
137 | |||
138 | static float channel_2_frequency = 0.0f; | ||
139 | void channel_2_set_frequency(float freq) { | ||
140 | channel_2_frequency = freq; | ||
141 | |||
142 | channel_2_stop(); | ||
143 | if (freq <= 0.0) // a pause/rest has freq=0 | ||
144 | return; | ||
145 | |||
146 | gpt7cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE; | ||
147 | channel_2_start(); | ||
148 | } | ||
149 | float channel_2_get_frequency(void) { return channel_2_frequency; } | ||
150 | |||
151 | static void gpt_audio_state_cb(GPTDriver *gptp) { | ||
152 | if (audio_update_state()) { | ||
153 | #if defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
154 | // one piezo/speaker connected to both audio pins, the generated square-waves are inverted | ||
155 | channel_1_set_frequency(audio_get_processed_frequency(0)); | ||
156 | channel_2_set_frequency(audio_get_processed_frequency(0)); | ||
157 | |||
158 | #else // two separate audio outputs/speakers | ||
159 | // primary speaker on A4, optional secondary on A5 | ||
160 | if (AUDIO_PIN == A4) { | ||
161 | channel_1_set_frequency(audio_get_processed_frequency(0)); | ||
162 | if (AUDIO_PIN_ALT == A5) { | ||
163 | if (audio_get_number_of_active_tones() > 1) { | ||
164 | channel_2_set_frequency(audio_get_processed_frequency(1)); | ||
165 | } else { | ||
166 | channel_2_stop(); | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | |||
171 | // primary speaker on A5, optional secondary on A4 | ||
172 | if (AUDIO_PIN == A5) { | ||
173 | channel_2_set_frequency(audio_get_processed_frequency(0)); | ||
174 | if (AUDIO_PIN_ALT == A4) { | ||
175 | if (audio_get_number_of_active_tones() > 1) { | ||
176 | channel_1_set_frequency(audio_get_processed_frequency(1)); | ||
177 | } else { | ||
178 | channel_1_stop(); | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | #endif | ||
183 | } | ||
184 | } | ||
185 | |||
186 | void audio_driver_initialize() { | ||
187 | if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) { | ||
188 | palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG); | ||
189 | dacStart(&DACD1, &dac_conf_ch1); | ||
190 | |||
191 | // initial setup of the dac-triggering timer is still required, even | ||
192 | // though it gets reconfigured and restarted later on | ||
193 | gptStart(&GPTD6, &gpt6cfg1); | ||
194 | } | ||
195 | |||
196 | if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) { | ||
197 | palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); | ||
198 | dacStart(&DACD2, &dac_conf_ch2); | ||
199 | |||
200 | gptStart(&GPTD7, &gpt7cfg1); | ||
201 | } | ||
202 | |||
203 | /* enable the output buffer, to directly drive external loads with no additional circuitry | ||
204 | * | ||
205 | * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers | ||
206 | * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer | ||
207 | * Note: enabling the output buffer imparts an additional dc-offset of a couple mV | ||
208 | * | ||
209 | * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet | ||
210 | * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.' | ||
211 | */ | ||
212 | DACD1.params->dac->CR &= ~DAC_CR_BOFF1; | ||
213 | DACD2.params->dac->CR &= ~DAC_CR_BOFF2; | ||
214 | |||
215 | // start state-updater | ||
216 | gptStart(&AUDIO_STATE_TIMER, &gptStateUpdateCfg); | ||
217 | } | ||
218 | |||
219 | void audio_driver_stop(void) { | ||
220 | if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) { | ||
221 | gptStopTimer(&GPTD6); | ||
222 | |||
223 | // stop the ongoing conversion and put the output in a known state | ||
224 | dacStopConversion(&DACD1); | ||
225 | dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE); | ||
226 | } | ||
227 | |||
228 | if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) { | ||
229 | gptStopTimer(&GPTD7); | ||
230 | |||
231 | dacStopConversion(&DACD2); | ||
232 | dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE); | ||
233 | } | ||
234 | gptStopTimer(&AUDIO_STATE_TIMER); | ||
235 | } | ||
236 | |||
237 | void audio_driver_start(void) { | ||
238 | if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) { | ||
239 | dacStartConversion(&DACD1, &dac_conv_grp_ch1, (dacsample_t *)dac_buffer_1, AUDIO_DAC_BUFFER_SIZE); | ||
240 | } | ||
241 | if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) { | ||
242 | dacStartConversion(&DACD2, &dac_conv_grp_ch2, (dacsample_t *)dac_buffer_2, AUDIO_DAC_BUFFER_SIZE); | ||
243 | } | ||
244 | gptStartContinuous(&AUDIO_STATE_TIMER, 2U); | ||
245 | } | ||
diff --git a/quantum/audio/driver_chibios_pwm.h b/quantum/audio/driver_chibios_pwm.h new file mode 100644 index 000000000..86cab916e --- /dev/null +++ b/quantum/audio/driver_chibios_pwm.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /* Copyright 2020 Jack Humbert | ||
2 | * Copyright 2020 JohSchneider | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | #pragma once | ||
18 | |||
19 | #if !defined(AUDIO_PWM_DRIVER) | ||
20 | // NOTE: Timer2 seems to be used otherwise in QMK, otherwise we could default to A5 (= TIM2_CH1, with PWMD2 and alternate-function(1)) | ||
21 | # define AUDIO_PWM_DRIVER PWMD1 | ||
22 | #endif | ||
23 | |||
24 | #if !defined(AUDIO_PWM_CHANNEL) | ||
25 | // NOTE: sticking to the STM data-sheet numbering: TIMxCH1 to TIMxCH4 | ||
26 | // default: STM32F303CC PA8+TIM1_CH1 -> 1 | ||
27 | # define AUDIO_PWM_CHANNEL 1 | ||
28 | #endif | ||
29 | |||
30 | #if !defined(AUDIO_PWM_PAL_MODE) | ||
31 | // pin-alternate function: see the data-sheet for which pin needs what AF to connect to TIMx_CHy | ||
32 | // default: STM32F303CC PA8+TIM1_CH1 -> 6 | ||
33 | # define AUDIO_PWM_PAL_MODE 6 | ||
34 | #endif | ||
35 | |||
36 | #if !defined(AUDIO_STATE_TIMER) | ||
37 | // timer used to trigger updates in the audio-system, configured/enabled in chibios mcuconf. | ||
38 | // Tim6 is the default for "larger" STMs, smaller ones might not have this one (enabled) and need to switch to a different one (e.g.: STM32F103 has only Tim1-Tim4) | ||
39 | # define AUDIO_STATE_TIMER GPTD6 | ||
40 | #endif | ||
diff --git a/quantum/audio/driver_chibios_pwm_hardware.c b/quantum/audio/driver_chibios_pwm_hardware.c new file mode 100644 index 000000000..3c7d89b29 --- /dev/null +++ b/quantum/audio/driver_chibios_pwm_hardware.c | |||
@@ -0,0 +1,144 @@ | |||
1 | /* Copyright 2020 Jack Humbert | ||
2 | * Copyright 2020 JohSchneider | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | Audio Driver: PWM | ||
20 | |||
21 | the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back. | ||
22 | |||
23 | this driver uses the chibios-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware. | ||
24 | The hardware directly toggles the pin via its alternate function. see your MCUs data-sheet for which pin can be driven by what timer - looking for TIMx_CHy and the corresponding alternate function. | ||
25 | |||
26 | */ | ||
27 | |||
28 | #include "audio.h" | ||
29 | #include "ch.h" | ||
30 | #include "hal.h" | ||
31 | |||
32 | #if !defined(AUDIO_PIN) | ||
33 | # error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings" | ||
34 | #endif | ||
35 | |||
36 | extern bool playing_note; | ||
37 | extern bool playing_melody; | ||
38 | extern uint8_t note_timbre; | ||
39 | |||
40 | static PWMConfig pwmCFG = { | ||
41 | .frequency = 100000, /* PWM clock frequency */ | ||
42 | // CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime | ||
43 | .period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ | ||
44 | .callback = NULL, /* no callback, the hardware directly toggles the pin */ | ||
45 | .channels = | ||
46 | { | ||
47 | #if AUDIO_PWM_CHANNEL == 4 | ||
48 | {PWM_OUTPUT_DISABLED, NULL}, /* channel 0 -> TIMx_CH1 */ | ||
49 | {PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */ | ||
50 | {PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */ | ||
51 | {PWM_OUTPUT_ACTIVE_HIGH, NULL} /* channel 3 -> TIMx_CH4 */ | ||
52 | #elif AUDIO_PWM_CHANNEL == 3 | ||
53 | {PWM_OUTPUT_DISABLED, NULL}, | ||
54 | {PWM_OUTPUT_DISABLED, NULL}, | ||
55 | {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH3 */ | ||
56 | {PWM_OUTPUT_DISABLED, NULL} | ||
57 | #elif AUDIO_PWM_CHANNEL == 2 | ||
58 | {PWM_OUTPUT_DISABLED, NULL}, | ||
59 | {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH2 */ | ||
60 | {PWM_OUTPUT_DISABLED, NULL}, | ||
61 | {PWM_OUTPUT_DISABLED, NULL} | ||
62 | #else /*fallback to CH1 */ | ||
63 | {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH1 */ | ||
64 | {PWM_OUTPUT_DISABLED, NULL}, | ||
65 | {PWM_OUTPUT_DISABLED, NULL}, | ||
66 | {PWM_OUTPUT_DISABLED, NULL} | ||
67 | #endif | ||
68 | }, | ||
69 | }; | ||
70 | |||
71 | static float channel_1_frequency = 0.0f; | ||
72 | void channel_1_set_frequency(float freq) { | ||
73 | channel_1_frequency = freq; | ||
74 | |||
75 | if (freq <= 0.0) // a pause/rest has freq=0 | ||
76 | return; | ||
77 | |||
78 | pwmcnt_t period = (pwmCFG.frequency / freq); | ||
79 | pwmChangePeriod(&AUDIO_PWM_DRIVER, period); | ||
80 | pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1, | ||
81 | // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH | ||
82 | PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100)); | ||
83 | } | ||
84 | |||
85 | float channel_1_get_frequency(void) { return channel_1_frequency; } | ||
86 | |||
87 | void channel_1_start(void) { | ||
88 | pwmStop(&AUDIO_PWM_DRIVER); | ||
89 | pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); | ||
90 | } | ||
91 | |||
92 | void channel_1_stop(void) { pwmStop(&AUDIO_PWM_DRIVER); } | ||
93 | |||
94 | static void gpt_callback(GPTDriver *gptp); | ||
95 | GPTConfig gptCFG = { | ||
96 | /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64 | ||
97 | the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4 | ||
98 | the tempo (which might vary!) is in bpm (beats per minute) | ||
99 | therefore: if the timer ticks away at .frequency = (60*64)Hz, | ||
100 | and the .interval counts from 64 downwards - audio_update_state is | ||
101 | called just often enough to not miss any notes | ||
102 | */ | ||
103 | .frequency = 60 * 64, | ||
104 | .callback = gpt_callback, | ||
105 | }; | ||
106 | |||
107 | void audio_driver_initialize(void) { | ||
108 | pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); | ||
109 | |||
110 | // connect the AUDIO_PIN to the PWM hardware | ||
111 | #if defined(USE_GPIOV1) // STM32F103C8 | ||
112 | palSetLineMode(AUDIO_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); | ||
113 | #else // GPIOv2 (or GPIOv3 for f4xx, which is the same/compatible at this command) | ||
114 | palSetLineMode(AUDIO_PIN, PAL_STM32_MODE_ALTERNATE | PAL_STM32_ALTERNATE(AUDIO_PWM_PAL_MODE)); | ||
115 | #endif | ||
116 | |||
117 | gptStart(&AUDIO_STATE_TIMER, &gptCFG); | ||
118 | } | ||
119 | |||
120 | void audio_driver_start(void) { | ||
121 | channel_1_stop(); | ||
122 | channel_1_start(); | ||
123 | |||
124 | if (playing_note || playing_melody) { | ||
125 | gptStartContinuous(&AUDIO_STATE_TIMER, 64); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | void audio_driver_stop(void) { | ||
130 | channel_1_stop(); | ||
131 | gptStopTimer(&AUDIO_STATE_TIMER); | ||
132 | } | ||
133 | |||
134 | /* a regular timer task, that checks the note to be currently played | ||
135 | * and updates the pwm to output that frequency | ||
136 | */ | ||
137 | static void gpt_callback(GPTDriver *gptp) { | ||
138 | float freq; // TODO: freq_alt | ||
139 | |||
140 | if (audio_update_state()) { | ||
141 | freq = audio_get_processed_frequency(0); // freq_alt would be index=1 | ||
142 | channel_1_set_frequency(freq); | ||
143 | } | ||
144 | } | ||
diff --git a/quantum/audio/driver_chibios_pwm_software.c b/quantum/audio/driver_chibios_pwm_software.c new file mode 100644 index 000000000..15c3e98b6 --- /dev/null +++ b/quantum/audio/driver_chibios_pwm_software.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* Copyright 2020 Jack Humbert | ||
2 | * Copyright 2020 JohSchneider | ||
3 | * | ||
4 | * This program is free software: you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation, either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | Audio Driver: PWM | ||
20 | |||
21 | the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back. | ||
22 | |||
23 | this driver uses the chibios-PWM system to produce a square-wave on any given output pin in software | ||
24 | - a pwm callback is used to set/clear the configured pin. | ||
25 | |||
26 | */ | ||
27 | #include "audio.h" | ||
28 | #include "ch.h" | ||
29 | #include "hal.h" | ||
30 | |||
31 | #if !defined(AUDIO_PIN) | ||
32 | # error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings" | ||
33 | #endif | ||
34 | extern bool playing_note; | ||
35 | extern bool playing_melody; | ||
36 | extern uint8_t note_timbre; | ||
37 | |||
38 | static void pwm_audio_period_callback(PWMDriver *pwmp); | ||
39 | static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp); | ||
40 | |||
41 | static PWMConfig pwmCFG = { | ||
42 | .frequency = 100000, /* PWM clock frequency */ | ||
43 | // CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime | ||
44 | .period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ | ||
45 | .callback = pwm_audio_period_callback, | ||
46 | .channels = | ||
47 | { | ||
48 | // software-PWM just needs another callback on any channel | ||
49 | {PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback}, /* channel 0 -> TIMx_CH1 */ | ||
50 | {PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */ | ||
51 | {PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */ | ||
52 | {PWM_OUTPUT_DISABLED, NULL} /* channel 3 -> TIMx_CH4 */ | ||
53 | }, | ||
54 | }; | ||
55 | |||
56 | static float channel_1_frequency = 0.0f; | ||
57 | void channel_1_set_frequency(float freq) { | ||
58 | channel_1_frequency = freq; | ||
59 | |||
60 | if (freq <= 0.0) // a pause/rest has freq=0 | ||
61 | return; | ||
62 | |||
63 | pwmcnt_t period = (pwmCFG.frequency / freq); | ||
64 | pwmChangePeriod(&AUDIO_PWM_DRIVER, period); | ||
65 | |||
66 | pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1, | ||
67 | // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH | ||
68 | PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100)); | ||
69 | } | ||
70 | |||
71 | float channel_1_get_frequency(void) { return channel_1_frequency; } | ||
72 | |||
73 | void channel_1_start(void) { | ||
74 | pwmStop(&AUDIO_PWM_DRIVER); | ||
75 | pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); | ||
76 | |||
77 | pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); | ||
78 | pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1); | ||
79 | } | ||
80 | |||
81 | void channel_1_stop(void) { | ||
82 | pwmStop(&AUDIO_PWM_DRIVER); | ||
83 | |||
84 | palClearLine(AUDIO_PIN); // leave the line low, after last note was played | ||
85 | |||
86 | #if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
87 | palClearLine(AUDIO_PIN_ALT); // leave the line low, after last note was played | ||
88 | #endif | ||
89 | } | ||
90 | |||
91 | // generate a PWM signal on any pin, not necessarily the one connected to the timer | ||
92 | static void pwm_audio_period_callback(PWMDriver *pwmp) { | ||
93 | (void)pwmp; | ||
94 | palClearLine(AUDIO_PIN); | ||
95 | |||
96 | #if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
97 | palSetLine(AUDIO_PIN_ALT); | ||
98 | #endif | ||
99 | } | ||
100 | static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) { | ||
101 | (void)pwmp; | ||
102 | if (channel_1_frequency > 0) { | ||
103 | palSetLine(AUDIO_PIN); // generate a PWM signal on any pin, not necessarily the one connected to the timer | ||
104 | #if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
105 | palClearLine(AUDIO_PIN_ALT); | ||
106 | #endif | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static void gpt_callback(GPTDriver *gptp); | ||
111 | GPTConfig gptCFG = { | ||
112 | /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64 | ||
113 | the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4 | ||
114 | the tempo (which might vary!) is in bpm (beats per minute) | ||
115 | therefore: if the timer ticks away at .frequency = (60*64)Hz, | ||
116 | and the .interval counts from 64 downwards - audio_update_state is | ||
117 | called just often enough to not miss anything | ||
118 | */ | ||
119 | .frequency = 60 * 64, | ||
120 | .callback = gpt_callback, | ||
121 | }; | ||
122 | |||
123 | void audio_driver_initialize(void) { | ||
124 | pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); | ||
125 | |||
126 | palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL); | ||
127 | palClearLine(AUDIO_PIN); | ||
128 | |||
129 | #if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) | ||
130 | palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL); | ||
131 | palClearLine(AUDIO_PIN_ALT); | ||
132 | #endif | ||
133 | |||
134 | pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); // enable pwm callbacks | ||
135 | pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1); | ||
136 | |||
137 | gptStart(&AUDIO_STATE_TIMER, &gptCFG); | ||
138 | } | ||
139 | |||
140 | void audio_driver_start(void) { | ||
141 | channel_1_stop(); | ||
142 | channel_1_start(); | ||
143 | |||
144 | if (playing_note || playing_melody) { | ||
145 | gptStartContinuous(&AUDIO_STATE_TIMER, 64); | ||
146 | } | ||
147 | } | ||
148 | |||
149 | void audio_driver_stop(void) { | ||
150 | channel_1_stop(); | ||
151 | gptStopTimer(&AUDIO_STATE_TIMER); | ||
152 | } | ||
153 | |||
154 | /* a regular timer task, that checks the note to be currently played | ||
155 | * and updates the pwm to output that frequency | ||
156 | */ | ||
157 | static void gpt_callback(GPTDriver *gptp) { | ||
158 | float freq; // TODO: freq_alt | ||
159 | |||
160 | if (audio_update_state()) { | ||
161 | freq = audio_get_processed_frequency(0); // freq_alt would be index=1 | ||
162 | channel_1_set_frequency(freq); | ||
163 | } | ||
164 | } | ||
diff --git a/quantum/audio/musical_notes.h b/quantum/audio/musical_notes.h index 0ba572c34..ddd7d374f 100644 --- a/quantum/audio/musical_notes.h +++ b/quantum/audio/musical_notes.h | |||
@@ -1,4 +1,5 @@ | |||
1 | /* Copyright 2016 Jack Humbert | 1 | /* Copyright 2016 Jack Humbert |
2 | * Copyright 2020 JohSchneider | ||
2 | * | 3 | * |
3 | * This program is free software: you can redistribute it and/or modify | 4 | * This program is free software: you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by | 5 | * it under the terms of the GNU General Public License as published by |
@@ -13,12 +14,11 @@ | |||
13 | * You should have received a copy of the GNU General Public License | 14 | * You should have received a copy of the GNU General Public License |
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
15 | */ | 16 | */ |
16 | |||
17 | #pragma once | 17 | #pragma once |
18 | 18 | ||
19 | // Tempo Placeholder | ||
20 | #ifndef TEMPO_DEFAULT | 19 | #ifndef TEMPO_DEFAULT |
21 | # define TEMPO_DEFAULT 100 | 20 | # define TEMPO_DEFAULT 120 |
21 | // in beats-per-minute | ||
22 | #endif | 22 | #endif |
23 | 23 | ||
24 | #define SONG(notes...) \ | 24 | #define SONG(notes...) \ |
@@ -27,12 +27,14 @@ | |||
27 | // Note Types | 27 | // Note Types |
28 | #define MUSICAL_NOTE(note, duration) \ | 28 | #define MUSICAL_NOTE(note, duration) \ |
29 | { (NOTE##note), duration } | 29 | { (NOTE##note), duration } |
30 | |||
30 | #define BREVE_NOTE(note) MUSICAL_NOTE(note, 128) | 31 | #define BREVE_NOTE(note) MUSICAL_NOTE(note, 128) |
31 | #define WHOLE_NOTE(note) MUSICAL_NOTE(note, 64) | 32 | #define WHOLE_NOTE(note) MUSICAL_NOTE(note, 64) |
32 | #define HALF_NOTE(note) MUSICAL_NOTE(note, 32) | 33 | #define HALF_NOTE(note) MUSICAL_NOTE(note, 32) |
33 | #define QUARTER_NOTE(note) MUSICAL_NOTE(note, 16) | 34 | #define QUARTER_NOTE(note) MUSICAL_NOTE(note, 16) |
34 | #define EIGHTH_NOTE(note) MUSICAL_NOTE(note, 8) | 35 | #define EIGHTH_NOTE(note) MUSICAL_NOTE(note, 8) |
35 | #define SIXTEENTH_NOTE(note) MUSICAL_NOTE(note, 4) | 36 | #define SIXTEENTH_NOTE(note) MUSICAL_NOTE(note, 4) |
37 | #define THIRTYSECOND_NOTE(note) MUSICAL_NOTE(note, 2) | ||
36 | 38 | ||
37 | #define BREVE_DOT_NOTE(note) MUSICAL_NOTE(note, 128 + 64) | 39 | #define BREVE_DOT_NOTE(note) MUSICAL_NOTE(note, 128 + 64) |
38 | #define WHOLE_DOT_NOTE(note) MUSICAL_NOTE(note, 64 + 32) | 40 | #define WHOLE_DOT_NOTE(note) MUSICAL_NOTE(note, 64 + 32) |
@@ -40,6 +42,9 @@ | |||
40 | #define QUARTER_DOT_NOTE(note) MUSICAL_NOTE(note, 16 + 8) | 42 | #define QUARTER_DOT_NOTE(note) MUSICAL_NOTE(note, 16 + 8) |
41 | #define EIGHTH_DOT_NOTE(note) MUSICAL_NOTE(note, 8 + 4) | 43 | #define EIGHTH_DOT_NOTE(note) MUSICAL_NOTE(note, 8 + 4) |
42 | #define SIXTEENTH_DOT_NOTE(note) MUSICAL_NOTE(note, 4 + 2) | 44 | #define SIXTEENTH_DOT_NOTE(note) MUSICAL_NOTE(note, 4 + 2) |
45 | #define THIRTYSECOND_DOT_NOTE(note) MUSICAL_NOTE(note, 2 + 1) | ||
46 | // duration of 64 units == one beat == one whole note | ||
47 | // with a tempo of 60bpm this comes to a length of one second | ||
43 | 48 | ||
44 | // Note Type Shortcuts | 49 | // Note Type Shortcuts |
45 | #define M__NOTE(note, duration) MUSICAL_NOTE(note, duration) | 50 | #define M__NOTE(note, duration) MUSICAL_NOTE(note, duration) |
@@ -49,56 +54,52 @@ | |||
49 | #define Q__NOTE(n) QUARTER_NOTE(n) | 54 | #define Q__NOTE(n) QUARTER_NOTE(n) |
50 | #define E__NOTE(n) EIGHTH_NOTE(n) | 55 | #define E__NOTE(n) EIGHTH_NOTE(n) |
51 | #define S__NOTE(n) SIXTEENTH_NOTE(n) | 56 | #define S__NOTE(n) SIXTEENTH_NOTE(n) |
57 | #define T__NOTE(n) THIRTYSECOND_NOTE(n) | ||
52 | #define BD_NOTE(n) BREVE_DOT_NOTE(n) | 58 | #define BD_NOTE(n) BREVE_DOT_NOTE(n) |
53 | #define WD_NOTE(n) WHOLE_DOT_NOTE(n) | 59 | #define WD_NOTE(n) WHOLE_DOT_NOTE(n) |
54 | #define HD_NOTE(n) HALF_DOT_NOTE(n) | 60 | #define HD_NOTE(n) HALF_DOT_NOTE(n) |
55 | #define QD_NOTE(n) QUARTER_DOT_NOTE(n) | 61 | #define QD_NOTE(n) QUARTER_DOT_NOTE(n) |
56 | #define ED_NOTE(n) EIGHTH_DOT_NOTE(n) | 62 | #define ED_NOTE(n) EIGHTH_DOT_NOTE(n) |
57 | #define SD_NOTE(n) SIXTEENTH_DOT_NOTE(n) | 63 | #define SD_NOTE(n) SIXTEENTH_DOT_NOTE(n) |
64 | #define TD_NOTE(n) THIRTYSECOND_DOT_NOTE(n) | ||
58 | 65 | ||
59 | // Note Timbre | 66 | // Note Timbre |
60 | // Changes how the notes sound | 67 | // Changes how the notes sound |
61 | #define TIMBRE_12 0.125f | 68 | #define TIMBRE_12 12 |
62 | #define TIMBRE_25 0.250f | 69 | #define TIMBRE_25 25 |
63 | #define TIMBRE_50 0.500f | 70 | #define TIMBRE_50 50 |
64 | #define TIMBRE_75 0.750f | 71 | #define TIMBRE_75 75 |
65 | #ifndef TIMBRE_DEFAULT | 72 | #ifndef TIMBRE_DEFAULT |
66 | # define TIMBRE_DEFAULT TIMBRE_50 | 73 | # define TIMBRE_DEFAULT TIMBRE_50 |
67 | #endif | 74 | #endif |
68 | // Notes - # = Octave | ||
69 | 75 | ||
70 | #ifdef __arm__ | 76 | // Notes - # = Octave |
71 | # define NOTE_REST 1.00f | ||
72 | #else | ||
73 | # define NOTE_REST 0.00f | ||
74 | #endif | ||
75 | 77 | ||
76 | /* These notes are currently bugged | 78 | #define NOTE_REST 0.00f |
77 | #define NOTE_C0 16.35f | ||
78 | #define NOTE_CS0 17.32f | ||
79 | #define NOTE_D0 18.35f | ||
80 | #define NOTE_DS0 19.45f | ||
81 | #define NOTE_E0 20.60f | ||
82 | #define NOTE_F0 21.83f | ||
83 | #define NOTE_FS0 23.12f | ||
84 | #define NOTE_G0 24.50f | ||
85 | #define NOTE_GS0 25.96f | ||
86 | #define NOTE_A0 27.50f | ||
87 | #define NOTE_AS0 29.14f | ||
88 | #define NOTE_B0 30.87f | ||
89 | #define NOTE_C1 32.70f | ||
90 | #define NOTE_CS1 34.65f | ||
91 | #define NOTE_D1 36.71f | ||
92 | #define NOTE_DS1 38.89f | ||
93 | #define NOTE_E1 41.20f | ||
94 | #define NOTE_F1 43.65f | ||
95 | #define NOTE_FS1 46.25f | ||
96 | #define NOTE_G1 49.00f | ||
97 | #define NOTE_GS1 51.91f | ||
98 | #define NOTE_A1 55.00f | ||
99 | #define NOTE_AS1 58.27f | ||
100 | */ | ||
101 | 79 | ||
80 | #define NOTE_C0 16.35f | ||
81 | #define NOTE_CS0 17.32f | ||
82 | #define NOTE_D0 18.35f | ||
83 | #define NOTE_DS0 19.45f | ||
84 | #define NOTE_E0 20.60f | ||
85 | #define NOTE_F0 21.83f | ||
86 | #define NOTE_FS0 23.12f | ||
87 | #define NOTE_G0 24.50f | ||
88 | #define NOTE_GS0 25.96f | ||
89 | #define NOTE_A0 27.50f | ||
90 | #define NOTE_AS0 29.14f | ||
91 | #define NOTE_B0 30.87f | ||
92 | #define NOTE_C1 32.70f | ||
93 | #define NOTE_CS1 34.65f | ||
94 | #define NOTE_D1 36.71f | ||
95 | #define NOTE_DS1 38.89f | ||
96 | #define NOTE_E1 41.20f | ||
97 | #define NOTE_F1 43.65f | ||
98 | #define NOTE_FS1 46.25f | ||
99 | #define NOTE_G1 49.00f | ||
100 | #define NOTE_GS1 51.91f | ||
101 | #define NOTE_A1 55.00f | ||
102 | #define NOTE_AS1 58.27f | ||
102 | #define NOTE_B1 61.74f | 103 | #define NOTE_B1 61.74f |
103 | #define NOTE_C2 65.41f | 104 | #define NOTE_C2 65.41f |
104 | #define NOTE_CS2 69.30f | 105 | #define NOTE_CS2 69.30f |
diff --git a/quantum/audio/voices.c b/quantum/audio/voices.c index 1592618be..8988d827e 100644 --- a/quantum/audio/voices.c +++ b/quantum/audio/voices.c | |||
@@ -1,4 +1,5 @@ | |||
1 | /* Copyright 2016 Jack Humbert | 1 | /* Copyright 2016 Jack Humbert |
2 | * Copyright 2020 JohSchneider | ||
2 | * | 3 | * |
3 | * This program is free software: you can redistribute it and/or modify | 4 | * This program is free software: you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by | 5 | * it under the terms of the GNU General Public License as published by |
@@ -17,13 +18,19 @@ | |||
17 | #include "audio.h" | 18 | #include "audio.h" |
18 | #include <stdlib.h> | 19 | #include <stdlib.h> |
19 | 20 | ||
20 | // these are imported from audio.c | 21 | uint8_t note_timbre = TIMBRE_DEFAULT; |
21 | extern uint16_t envelope_index; | 22 | bool glissando = false; |
22 | extern float note_timbre; | 23 | bool vibrato = false; |
23 | extern float polyphony_rate; | 24 | float vibrato_strength = 0.5; |
24 | extern bool glissando; | 25 | float vibrato_rate = 0.125; |
25 | 26 | ||
27 | uint16_t voices_timer = 0; | ||
28 | |||
29 | #ifdef AUDIO_VOICE_DEFAULT | ||
30 | voice_type voice = AUDIO_VOICE_DEFAULT; | ||
31 | #else | ||
26 | voice_type voice = default_voice; | 32 | voice_type voice = default_voice; |
33 | #endif | ||
27 | 34 | ||
28 | void set_voice(voice_type v) { voice = v; } | 35 | void set_voice(voice_type v) { voice = v; } |
29 | 36 | ||
@@ -31,22 +38,54 @@ void voice_iterate() { voice = (voice + 1) % number_of_voices; } | |||
31 | 38 | ||
32 | void voice_deiterate() { voice = (voice - 1 + number_of_voices) % number_of_voices; } | 39 | void voice_deiterate() { voice = (voice - 1 + number_of_voices) % number_of_voices; } |
33 | 40 | ||
41 | #ifdef AUDIO_VOICES | ||
42 | float mod(float a, int b) { | ||
43 | float r = fmod(a, b); | ||
44 | return r < 0 ? r + b : r; | ||
45 | } | ||
46 | |||
47 | // Effect: 'vibrate' a given target frequency slightly above/below its initial value | ||
48 | float voice_add_vibrato(float average_freq) { | ||
49 | float vibrato_counter = mod(timer_read() / (100 * vibrato_rate), VIBRATO_LUT_LENGTH); | ||
50 | |||
51 | return average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); | ||
52 | } | ||
53 | |||
54 | // Effect: 'slides' the 'frequency' from the starting-point, to the target frequency | ||
55 | float voice_add_glissando(float from_freq, float to_freq) { | ||
56 | if (to_freq != 0 && from_freq < to_freq && from_freq < to_freq * pow(2, -440 / to_freq / 12 / 2)) { | ||
57 | return from_freq * pow(2, 440 / from_freq / 12 / 2); | ||
58 | } else if (to_freq != 0 && from_freq > to_freq && from_freq > to_freq * pow(2, 440 / to_freq / 12 / 2)) { | ||
59 | return from_freq * pow(2, -440 / from_freq / 12 / 2); | ||
60 | } else { | ||
61 | return to_freq; | ||
62 | } | ||
63 | } | ||
64 | #endif | ||
65 | |||
34 | float voice_envelope(float frequency) { | 66 | float voice_envelope(float frequency) { |
35 | // envelope_index ranges from 0 to 0xFFFF, which is preserved at 880.0 Hz | 67 | // envelope_index ranges from 0 to 0xFFFF, which is preserved at 880.0 Hz |
36 | __attribute__((unused)) uint16_t compensated_index = (uint16_t)((float)envelope_index * (880.0 / frequency)); | 68 | // __attribute__((unused)) uint16_t compensated_index = (uint16_t)((float)envelope_index * (880.0 / frequency)); |
69 | #ifdef AUDIO_VOICES | ||
70 | uint16_t envelope_index = timer_elapsed(voices_timer); // TODO: multiply in some factor? | ||
71 | uint16_t compensated_index = envelope_index / 100; // TODO: correct factor would be? | ||
72 | #endif | ||
37 | 73 | ||
38 | switch (voice) { | 74 | switch (voice) { |
39 | case default_voice: | 75 | case default_voice: |
40 | glissando = false; | 76 | glissando = false; |
41 | note_timbre = TIMBRE_50; | 77 | // note_timbre = TIMBRE_50; //Note: leave the user the possibility to adjust the timbre with 'audio_set_timbre' |
42 | polyphony_rate = 0; | ||
43 | break; | 78 | break; |
44 | 79 | ||
45 | #ifdef AUDIO_VOICES | 80 | #ifdef AUDIO_VOICES |
46 | 81 | ||
82 | case vibrating: | ||
83 | glissando = false; | ||
84 | vibrato = true; | ||
85 | break; | ||
86 | |||
47 | case something: | 87 | case something: |
48 | glissando = false; | 88 | glissando = false; |
49 | polyphony_rate = 0; | ||
50 | switch (compensated_index) { | 89 | switch (compensated_index) { |
51 | case 0 ... 9: | 90 | case 0 ... 9: |
52 | note_timbre = TIMBRE_12; | 91 | note_timbre = TIMBRE_12; |
@@ -57,24 +96,23 @@ float voice_envelope(float frequency) { | |||
57 | break; | 96 | break; |
58 | 97 | ||
59 | case 20 ... 200: | 98 | case 20 ... 200: |
60 | note_timbre = .125 + .125; | 99 | note_timbre = 12 + 12; |
61 | break; | 100 | break; |
62 | 101 | ||
63 | default: | 102 | default: |
64 | note_timbre = .125; | 103 | note_timbre = 12; |
65 | break; | 104 | break; |
66 | } | 105 | } |
67 | break; | 106 | break; |
68 | 107 | ||
69 | case drums: | 108 | case drums: |
70 | glissando = false; | 109 | glissando = false; |
71 | polyphony_rate = 0; | ||
72 | // switch (compensated_index) { | 110 | // switch (compensated_index) { |
73 | // case 0 ... 10: | 111 | // case 0 ... 10: |
74 | // note_timbre = 0.5; | 112 | // note_timbre = 50; |
75 | // break; | 113 | // break; |
76 | // case 11 ... 20: | 114 | // case 11 ... 20: |
77 | // note_timbre = 0.5 * (21 - compensated_index) / 10; | 115 | // note_timbre = 50 * (21 - compensated_index) / 10; |
78 | // break; | 116 | // break; |
79 | // default: | 117 | // default: |
80 | // note_timbre = 0; | 118 | // note_timbre = 0; |
@@ -88,10 +126,10 @@ float voice_envelope(float frequency) { | |||
88 | frequency = (rand() % (int)(40)) + 60; | 126 | frequency = (rand() % (int)(40)) + 60; |
89 | switch (envelope_index) { | 127 | switch (envelope_index) { |
90 | case 0 ... 10: | 128 | case 0 ... 10: |
91 | note_timbre = 0.5; | 129 | note_timbre = 50; |
92 | break; | 130 | break; |
93 | case 11 ... 20: | 131 | case 11 ... 20: |
94 | note_timbre = 0.5 * (21 - envelope_index) / 10; | 132 | note_timbre = 50 * (21 - envelope_index) / 10; |
95 | break; | 133 | break; |
96 | default: | 134 | default: |
97 | note_timbre = 0; | 135 | note_timbre = 0; |
@@ -103,10 +141,10 @@ float voice_envelope(float frequency) { | |||
103 | frequency = (rand() % (int)(1000)) + 1000; | 141 | frequency = (rand() % (int)(1000)) + 1000; |
104 | switch (envelope_index) { | 142 | switch (envelope_index) { |
105 | case 0 ... 5: | 143 | case 0 ... 5: |
106 | note_timbre = 0.5; | 144 | note_timbre = 50; |
107 | break; | 145 | break; |
108 | case 6 ... 20: | 146 | case 6 ... 20: |
109 | note_timbre = 0.5 * (21 - envelope_index) / 15; | 147 | note_timbre = 50 * (21 - envelope_index) / 15; |
110 | break; | 148 | break; |
111 | default: | 149 | default: |
112 | note_timbre = 0; | 150 | note_timbre = 0; |
@@ -118,10 +156,10 @@ float voice_envelope(float frequency) { | |||
118 | frequency = (rand() % (int)(2000)) + 3000; | 156 | frequency = (rand() % (int)(2000)) + 3000; |
119 | switch (envelope_index) { | 157 | switch (envelope_index) { |
120 | case 0 ... 15: | 158 | case 0 ... 15: |
121 | note_timbre = 0.5; | 159 | note_timbre = 50; |
122 | break; | 160 | break; |
123 | case 16 ... 20: | 161 | case 16 ... 20: |
124 | note_timbre = 0.5 * (21 - envelope_index) / 5; | 162 | note_timbre = 50 * (21 - envelope_index) / 5; |
125 | break; | 163 | break; |
126 | default: | 164 | default: |
127 | note_timbre = 0; | 165 | note_timbre = 0; |
@@ -133,10 +171,10 @@ float voice_envelope(float frequency) { | |||
133 | frequency = (rand() % (int)(2000)) + 3000; | 171 | frequency = (rand() % (int)(2000)) + 3000; |
134 | switch (envelope_index) { | 172 | switch (envelope_index) { |
135 | case 0 ... 35: | 173 | case 0 ... 35: |
136 | note_timbre = 0.5; | 174 | note_timbre = 50; |
137 | break; | 175 | break; |
138 | case 36 ... 50: | 176 | case 36 ... 50: |
139 | note_timbre = 0.5 * (51 - envelope_index) / 15; | 177 | note_timbre = 50 * (51 - envelope_index) / 15; |
140 | break; | 178 | break; |
141 | default: | 179 | default: |
142 | note_timbre = 0; | 180 | note_timbre = 0; |
@@ -145,8 +183,7 @@ float voice_envelope(float frequency) { | |||
145 | } | 183 | } |
146 | break; | 184 | break; |
147 | case butts_fader: | 185 | case butts_fader: |
148 | glissando = true; | 186 | glissando = true; |
149 | polyphony_rate = 0; | ||
150 | switch (compensated_index) { | 187 | switch (compensated_index) { |
151 | case 0 ... 9: | 188 | case 0 ... 9: |
152 | frequency = frequency / 4; | 189 | frequency = frequency / 4; |
@@ -159,7 +196,7 @@ float voice_envelope(float frequency) { | |||
159 | break; | 196 | break; |
160 | 197 | ||
161 | case 20 ... 200: | 198 | case 20 ... 200: |
162 | note_timbre = .125 - pow(((float)compensated_index - 20) / (200 - 20), 2) * .125; | 199 | note_timbre = 12 - (uint8_t)(pow(((float)compensated_index - 20) / (200 - 20), 2) * 12.5); |
163 | break; | 200 | break; |
164 | 201 | ||
165 | default: | 202 | default: |
@@ -169,7 +206,6 @@ float voice_envelope(float frequency) { | |||
169 | break; | 206 | break; |
170 | 207 | ||
171 | // case octave_crunch: | 208 | // case octave_crunch: |
172 | // polyphony_rate = 0; | ||
173 | // switch (compensated_index) { | 209 | // switch (compensated_index) { |
174 | // case 0 ... 9: | 210 | // case 0 ... 9: |
175 | // case 20 ... 24: | 211 | // case 20 ... 24: |
@@ -187,14 +223,13 @@ float voice_envelope(float frequency) { | |||
187 | 223 | ||
188 | // default: | 224 | // default: |
189 | // note_timbre = TIMBRE_12; | 225 | // note_timbre = TIMBRE_12; |
190 | // break; | 226 | // break; |
191 | // } | 227 | // } |
192 | // break; | 228 | // break; |
193 | 229 | ||
194 | case duty_osc: | 230 | case duty_osc: |
195 | // This slows the loop down a substantial amount, so higher notes may freeze | 231 | // This slows the loop down a substantial amount, so higher notes may freeze |
196 | glissando = true; | 232 | glissando = true; |
197 | polyphony_rate = 0; | ||
198 | switch (compensated_index) { | 233 | switch (compensated_index) { |
199 | default: | 234 | default: |
200 | # define OCS_SPEED 10 | 235 | # define OCS_SPEED 10 |
@@ -202,38 +237,36 @@ float voice_envelope(float frequency) { | |||
202 | // sine wave is slow | 237 | // sine wave is slow |
203 | // note_timbre = (sin((float)compensated_index/10000*OCS_SPEED) * OCS_AMP / 2) + .5; | 238 | // note_timbre = (sin((float)compensated_index/10000*OCS_SPEED) * OCS_AMP / 2) + .5; |
204 | // triangle wave is a bit faster | 239 | // triangle wave is a bit faster |
205 | note_timbre = (float)abs((compensated_index * OCS_SPEED % 3000) - 1500) * (OCS_AMP / 1500) + (1 - OCS_AMP) / 2; | 240 | note_timbre = (uint8_t)abs((compensated_index * OCS_SPEED % 3000) - 1500) * (OCS_AMP / 1500) + (1 - OCS_AMP) / 2; |
206 | break; | 241 | break; |
207 | } | 242 | } |
208 | break; | 243 | break; |
209 | 244 | ||
210 | case duty_octave_down: | 245 | case duty_octave_down: |
211 | glissando = true; | 246 | glissando = true; |
212 | polyphony_rate = 0; | 247 | note_timbre = (uint8_t)(100 * (envelope_index % 2) * .125 + .375 * 2); |
213 | note_timbre = (envelope_index % 2) * .125 + .375 * 2; | 248 | if ((envelope_index % 4) == 0) note_timbre = 50; |
214 | if ((envelope_index % 4) == 0) note_timbre = 0.5; | ||
215 | if ((envelope_index % 8) == 0) note_timbre = 0; | 249 | if ((envelope_index % 8) == 0) note_timbre = 0; |
216 | break; | 250 | break; |
217 | case delayed_vibrato: | 251 | case delayed_vibrato: |
218 | glissando = true; | 252 | glissando = true; |
219 | polyphony_rate = 0; | 253 | note_timbre = TIMBRE_50; |
220 | note_timbre = TIMBRE_50; | ||
221 | # define VOICE_VIBRATO_DELAY 150 | 254 | # define VOICE_VIBRATO_DELAY 150 |
222 | # define VOICE_VIBRATO_SPEED 50 | 255 | # define VOICE_VIBRATO_SPEED 50 |
223 | switch (compensated_index) { | 256 | switch (compensated_index) { |
224 | case 0 ... VOICE_VIBRATO_DELAY: | 257 | case 0 ... VOICE_VIBRATO_DELAY: |
225 | break; | 258 | break; |
226 | default: | 259 | default: |
260 | // TODO: merge/replace with voice_add_vibrato above | ||
227 | frequency = frequency * vibrato_lut[(int)fmod((((float)compensated_index - (VOICE_VIBRATO_DELAY + 1)) / 1000 * VOICE_VIBRATO_SPEED), VIBRATO_LUT_LENGTH)]; | 261 | frequency = frequency * vibrato_lut[(int)fmod((((float)compensated_index - (VOICE_VIBRATO_DELAY + 1)) / 1000 * VOICE_VIBRATO_SPEED), VIBRATO_LUT_LENGTH)]; |
228 | break; | 262 | break; |
229 | } | 263 | } |
230 | break; | 264 | break; |
231 | // case delayed_vibrato_octave: | 265 | // case delayed_vibrato_octave: |
232 | // polyphony_rate = 0; | ||
233 | // if ((envelope_index % 2) == 1) { | 266 | // if ((envelope_index % 2) == 1) { |
234 | // note_timbre = 0.55; | 267 | // note_timbre = 55; |
235 | // } else { | 268 | // } else { |
236 | // note_timbre = 0.45; | 269 | // note_timbre = 45; |
237 | // } | 270 | // } |
238 | // #define VOICE_VIBRATO_DELAY 150 | 271 | // #define VOICE_VIBRATO_DELAY 150 |
239 | // #define VOICE_VIBRATO_SPEED 50 | 272 | // #define VOICE_VIBRATO_SPEED 50 |
@@ -246,35 +279,64 @@ float voice_envelope(float frequency) { | |||
246 | // } | 279 | // } |
247 | // break; | 280 | // break; |
248 | // case duty_fifth_down: | 281 | // case duty_fifth_down: |
249 | // note_timbre = 0.5; | 282 | // note_timbre = TIMBRE_50; |
250 | // if ((envelope_index % 3) == 0) | 283 | // if ((envelope_index % 3) == 0) |
251 | // note_timbre = 0.75; | 284 | // note_timbre = TIMBRE_75; |
252 | // break; | 285 | // break; |
253 | // case duty_fourth_down: | 286 | // case duty_fourth_down: |
254 | // note_timbre = 0.0; | 287 | // note_timbre = 0; |
255 | // if ((envelope_index % 12) == 0) | 288 | // if ((envelope_index % 12) == 0) |
256 | // note_timbre = 0.75; | 289 | // note_timbre = TIMBRE_75; |
257 | // if (((envelope_index % 12) % 4) != 1) | 290 | // if (((envelope_index % 12) % 4) != 1) |
258 | // note_timbre = 0.75; | 291 | // note_timbre = TIMBRE_75; |
259 | // break; | 292 | // break; |
260 | // case duty_third_down: | 293 | // case duty_third_down: |
261 | // note_timbre = 0.5; | 294 | // note_timbre = TIMBRE_50; |
262 | // if ((envelope_index % 5) == 0) | 295 | // if ((envelope_index % 5) == 0) |
263 | // note_timbre = 0.75; | 296 | // note_timbre = TIMBRE_75; |
264 | // break; | 297 | // break; |
265 | // case duty_fifth_third_down: | 298 | // case duty_fifth_third_down: |
266 | // note_timbre = 0.5; | 299 | // note_timbre = TIMBRE_50; |
267 | // if ((envelope_index % 5) == 0) | 300 | // if ((envelope_index % 5) == 0) |
268 | // note_timbre = 0.75; | 301 | // note_timbre = TIMBRE_75; |
269 | // if ((envelope_index % 3) == 0) | 302 | // if ((envelope_index % 3) == 0) |
270 | // note_timbre = 0.25; | 303 | // note_timbre = TIMBRE_25; |
271 | // break; | 304 | // break; |
272 | 305 | ||
273 | #endif | 306 | #endif // AUDIO_VOICES |
274 | 307 | ||
275 | default: | 308 | default: |
276 | break; | 309 | break; |
277 | } | 310 | } |
278 | 311 | ||
312 | #ifdef AUDIO_VOICES | ||
313 | if (vibrato && (vibrato_strength > 0)) { | ||
314 | frequency = voice_add_vibrato(frequency); | ||
315 | } | ||
316 | |||
317 | if (glissando) { | ||
318 | // TODO: where to keep track of the start-frequency? | ||
319 | // frequency = voice_add_glissando(??, frequency); | ||
320 | } | ||
321 | #endif // AUDIO_VOICES | ||
322 | |||
279 | return frequency; | 323 | return frequency; |
280 | } | 324 | } |
325 | |||
326 | // Vibrato functions | ||
327 | |||
328 | void voice_set_vibrato_rate(float rate) { vibrato_rate = rate; } | ||
329 | void voice_increase_vibrato_rate(float change) { vibrato_rate *= change; } | ||
330 | void voice_decrease_vibrato_rate(float change) { vibrato_rate /= change; } | ||
331 | void voice_set_vibrato_strength(float strength) { vibrato_strength = strength; } | ||
332 | void voice_increase_vibrato_strength(float change) { vibrato_strength *= change; } | ||
333 | void voice_decrease_vibrato_strength(float change) { vibrato_strength /= change; } | ||
334 | |||
335 | // Timbre functions | ||
336 | |||
337 | void voice_set_timbre(uint8_t timbre) { | ||
338 | if ((timbre > 0) && (timbre < 100)) { | ||
339 | note_timbre = timbre; | ||
340 | } | ||
341 | } | ||
342 | uint8_t voice_get_timbre(void) { return note_timbre; } | ||
diff --git a/quantum/audio/voices.h b/quantum/audio/voices.h index abafa2b40..578350d33 100644 --- a/quantum/audio/voices.h +++ b/quantum/audio/voices.h | |||
@@ -1,4 +1,5 @@ | |||
1 | /* Copyright 2016 Jack Humbert | 1 | /* Copyright 2016 Jack Humbert |
2 | * Copyright 2020 JohSchneider | ||
2 | * | 3 | * |
3 | * This program is free software: you can redistribute it and/or modify | 4 | * This program is free software: you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by | 5 | * it under the terms of the GNU General Public License as published by |
@@ -13,7 +14,6 @@ | |||
13 | * You should have received a copy of the GNU General Public License | 14 | * You should have received a copy of the GNU General Public License |
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
15 | */ | 16 | */ |
16 | |||
17 | #pragma once | 17 | #pragma once |
18 | 18 | ||
19 | #include <stdint.h> | 19 | #include <stdint.h> |
@@ -29,6 +29,7 @@ float voice_envelope(float frequency); | |||
29 | typedef enum { | 29 | typedef enum { |
30 | default_voice, | 30 | default_voice, |
31 | #ifdef AUDIO_VOICES | 31 | #ifdef AUDIO_VOICES |
32 | vibrating, | ||
32 | something, | 33 | something, |
33 | drums, | 34 | drums, |
34 | butts_fader, | 35 | butts_fader, |
@@ -48,3 +49,21 @@ typedef enum { | |||
48 | void set_voice(voice_type v); | 49 | void set_voice(voice_type v); |
49 | void voice_iterate(void); | 50 | void voice_iterate(void); |
50 | void voice_deiterate(void); | 51 | void voice_deiterate(void); |
52 | |||
53 | // Vibrato functions | ||
54 | void voice_set_vibrato_rate(float rate); | ||
55 | void voice_increase_vibrato_rate(float change); | ||
56 | void voice_decrease_vibrato_rate(float change); | ||
57 | void voice_set_vibrato_strength(float strength); | ||
58 | void voice_increase_vibrato_strength(float change); | ||
59 | void voice_decrease_vibrato_strength(float change); | ||
60 | |||
61 | // Timbre functions | ||
62 | /** | ||
63 | * @brief set the global timbre for tones to be played | ||
64 | * @note: only applies to pwm implementations - where it adjusts the duty-cycle | ||
65 | * @note: using any instrument from voices.[ch] other than 'default' may override the set value | ||
66 | * @param[in]: timbre: valid range is (0,100) | ||
67 | */ | ||
68 | void voice_set_timbre(uint8_t timbre); | ||
69 | uint8_t voice_get_timbre(void); | ||
diff --git a/quantum/audio/wave.h b/quantum/audio/wave.h deleted file mode 100644 index 48210a944..000000000 --- a/quantum/audio/wave.h +++ /dev/null | |||
@@ -1,36 +0,0 @@ | |||
1 | /* Copyright 2016 Jack Humbert | ||
2 | * | ||
3 | * This program is free software: you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License as published by | ||
5 | * the Free Software Foundation, either version 2 of the License, or | ||
6 | * (at your option) any later version. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <avr/io.h> | ||
18 | #include <avr/interrupt.h> | ||
19 | #include <avr/pgmspace.h> | ||
20 | |||
21 | #define SINE_LENGTH 2048 | ||
22 | |||
23 | const uint8_t sinewave[] PROGMEM = // 2048 values | ||
24 | {0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x87, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x93, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x98, 0x98, 0x98, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbb, | ||
25 | 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, | ||
26 | 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, | ||
27 | 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, | ||
28 | 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, | ||
29 | 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb, 0xcb, 0xcb, 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbd, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xba, 0xba, 0xba, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb7, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xb0, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xad, 0xad, 0xad, 0xac, 0xac, 0xac, 0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa9, 0xa8, 0xa8, 0xa7, 0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9d, | ||
30 | 0x9d, 0x9d, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x9a, 0x99, 0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x94, 0x94, 0x93, 0x93, 0x93, 0x92, 0x92, 0x91, 0x91, 0x91, 0x90, 0x90, 0x8f, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x88, 0x88, 0x88, 0x87, 0x87, 0x87, 0x86, 0x86, 0x85, 0x85, 0x85, 0x84, 0x84, 0x83, 0x83, 0x83, 0x82, 0x82, 0x81, 0x81, 0x81, 0x80, 0x80, 0x80, 0x7f, 0x7f, 0x7e, 0x7e, 0x7e, 0x7d, 0x7d, 0x7c, 0x7c, 0x7c, 0x7b, 0x7b, 0x7a, 0x7a, 0x7a, 0x79, 0x79, 0x78, 0x78, 0x78, 0x77, 0x77, 0x77, 0x76, 0x76, 0x75, 0x75, 0x75, 0x74, 0x74, 0x73, 0x73, 0x73, 0x72, 0x72, 0x71, 0x71, 0x71, 0x70, 0x70, 0x70, 0x6f, 0x6f, 0x6e, 0x6e, 0x6e, 0x6d, 0x6d, 0x6c, 0x6c, 0x6c, 0x6b, 0x6b, 0x6a, 0x6a, 0x6a, 0x69, 0x69, 0x69, 0x68, 0x68, 0x67, 0x67, 0x67, 0x66, 0x66, 0x65, 0x65, 0x65, 0x64, 0x64, 0x64, 0x63, 0x63, 0x62, 0x62, 0x62, 0x61, 0x61, 0x61, 0x60, | ||
31 | 0x60, 0x5f, 0x5f, 0x5f, 0x5e, 0x5e, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5c, 0x5b, 0x5b, 0x5a, 0x5a, 0x5a, 0x59, 0x59, 0x59, 0x58, 0x58, 0x58, 0x57, 0x57, 0x56, 0x56, 0x56, 0x55, 0x55, 0x55, 0x54, 0x54, 0x53, 0x53, 0x53, 0x52, 0x52, 0x52, 0x51, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, 0x4f, 0x4e, 0x4e, 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, 0x4b, 0x4a, 0x4a, 0x4a, 0x49, 0x49, 0x49, 0x48, 0x48, 0x48, 0x47, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, 0x44, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2e, 0x2e, 0x2e, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, | ||
32 | 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26, 0x26, 0x26, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23, 0x23, 0x23, 0x23, 0x22, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, 0x1a, 0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x10, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0xe, 0xe, 0xe, 0xe, 0xe, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x8, 0x8, 0x8, 0x8, 0x8, | ||
33 | 0x8, 0x8, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, | ||
34 | 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x5, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x6, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x7, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xe, 0xe, 0xe, 0xe, 0xe, 0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, | ||
35 | 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x46, | ||
36 | 0x46, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0x57, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72, 0x73, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f}; | ||