diff options
Diffstat (limited to 'quantum')
| -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 | ||||
| -rw-r--r-- | quantum/backlight/backlight_avr.c | 4 |
18 files changed, 2339 insertions, 2321 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}; | ||
diff --git a/quantum/backlight/backlight_avr.c b/quantum/backlight/backlight_avr.c index 2ecdd4f2c..e47192de3 100644 --- a/quantum/backlight/backlight_avr.c +++ b/quantum/backlight/backlight_avr.c | |||
| @@ -126,7 +126,7 @@ | |||
| 126 | # define COMxx1 COM1B1 | 126 | # define COMxx1 COM1B1 |
| 127 | # define OCRxx OCR1B | 127 | # define OCRxx OCR1B |
| 128 | # endif | 128 | # endif |
| 129 | #elif !defined(B5_AUDIO) && !defined(B6_AUDIO) && !defined(B7_AUDIO) | 129 | #elif (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7) |
| 130 | // Timer 1 is not in use by Audio feature, Backlight can use it | 130 | // Timer 1 is not in use by Audio feature, Backlight can use it |
| 131 | # pragma message "Using hardware timer 1 with software PWM" | 131 | # pragma message "Using hardware timer 1 with software PWM" |
| 132 | # define HARDWARE_PWM | 132 | # define HARDWARE_PWM |
| @@ -145,7 +145,7 @@ | |||
| 145 | 145 | ||
| 146 | # define OCIExA OCIE1A | 146 | # define OCIExA OCIE1A |
| 147 | # define OCRxx OCR1A | 147 | # define OCRxx OCR1A |
| 148 | #elif !defined(C6_AUDIO) && !defined(C5_AUDIO) && !defined(C4_AUDIO) | 148 | #elif (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) |
| 149 | # pragma message "Using hardware timer 3 with software PWM" | 149 | # pragma message "Using hardware timer 3 with software PWM" |
| 150 | // Timer 3 is not in use by Audio feature, Backlight can use it | 150 | // Timer 3 is not in use by Audio feature, Backlight can use it |
| 151 | # define HARDWARE_PWM | 151 | # define HARDWARE_PWM |
