diff options
Diffstat (limited to 'quantum')
45 files changed, 3981 insertions, 2560 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 bc00cd19e..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,61 +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); | ||
| 96 | |||
| 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 | ||
| 110 | |||
| 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); | ||
| 125 | |||
| 126 | /** | ||
| 127 | * @brief stop a given tone/frequency | ||
| 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); | ||
| 52 | 137 | ||
| 53 | // Vibrato rate functions | 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); | ||
| 54 | 149 | ||
| 55 | #ifdef VIBRATO_ENABLE | 150 | /** |
| 151 | * @brief play a short tone of a specific frequency to emulate a 'click' | ||
| 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); | ||
| 56 | 162 | ||
| 57 | void set_vibrato_rate(float rate); | 163 | /** |
| 58 | void increase_vibrato_rate(float change); | 164 | * @brief stops all playback |
| 59 | void decrease_vibrato_rate(float change); | 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); | ||
| 60 | 170 | ||
| 61 | # ifdef VIBRATO_STRENGTH_ENABLE | 171 | /** |
| 172 | * @brief query if one/multiple tones are playing | ||
| 173 | */ | ||
| 174 | bool audio_is_playing_note(void); | ||
| 62 | 175 | ||
| 63 | void set_vibrato_strength(float strength); | 176 | /** |
| 64 | void increase_vibrato_strength(float change); | 177 | * @brief query if a melody/SONG is playing |
| 65 | void decrease_vibrato_strength(float change); | 178 | */ |
| 179 | bool audio_is_playing_melody(void); | ||
| 66 | 180 | ||
| 67 | # endif | 181 | // These macros are used to allow audio_play_melody to play an array of indeterminate |
| 182 | // length. This works around the limitation of C's sizeof operation on pointers. | ||
| 183 | // The global float array for the song must be used here. | ||
| 184 | #define NOTE_ARRAY_SIZE(x) ((int16_t)(sizeof(x) / (sizeof(x[0])))) | ||
| 185 | |||
| 186 | /** | ||
| 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) | ||
| 68 | 195 | ||
| 196 | // Tone-Multiplexing functions | ||
| 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); | ||
| 69 | #endif | 209 | #endif |
| 70 | 210 | ||
| 71 | // Polyphony functions | 211 | // Tempo functions |
| 72 | 212 | ||
| 73 | void set_polyphony_rate(float rate); | 213 | void audio_set_tempo(uint8_t tempo); |
| 74 | void enable_polyphony(void); | 214 | void audio_increase_tempo(uint8_t tempo_change); |
| 75 | void disable_polyphony(void); | 215 | void audio_decrease_tempo(uint8_t tempo_change); |
| 76 | void increase_polyphony_rate(float change); | ||
| 77 | void decrease_polyphony_rate(float change); | ||
| 78 | 216 | ||
| 79 | void set_timbre(float timbre); | 217 | // conversion macros, from 64parts-to-a-beat to milliseconds and back |
| 80 | void set_tempo(uint8_t tempo); | 218 | uint16_t audio_duration_to_ms(uint16_t duration_bpm); |
| 219 | uint16_t audio_ms_to_duration(uint16_t duration_ms); | ||
| 81 | 220 | ||
| 82 | void increase_tempo(uint8_t tempo_change); | 221 | void audio_startup(void); |
| 83 | void decrease_tempo(uint8_t tempo_change); | ||
| 84 | 222 | ||
| 85 | void audio_init(void); | 223 | // hardware interface |
| 86 | 224 | ||
| 87 | #ifdef PWM_AUDIO | 225 | // implementation in the driver_avr/arm_* respective parts |
| 88 | void play_sample(uint8_t* s, uint16_t l, bool r); | 226 | void audio_driver_initialize(void); |
| 89 | #endif | 227 | void audio_driver_start(void); |
| 90 | void play_note(float freq, int vol); | 228 | void audio_driver_stop(void); |
| 91 | void stop_note(float freq); | ||
| 92 | void stop_all_notes(void); | ||
| 93 | void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat); | ||
| 94 | 229 | ||
| 95 | #define SCALE \ | 230 | /** |
| 96 | (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), } | 231 | * @brief get the number of currently active tones |
| 232 | * @return number, 0=none active | ||
| 233 | */ | ||
| 234 | uint8_t audio_get_number_of_active_tones(void); | ||
| 97 | 235 | ||
| 98 | // These macros are used to allow play_notes to play an array of indeterminate | 236 | /** |
| 99 | // length. This works around the limitation of C's sizeof operation on pointers. | 237 | * @brief access to the raw/unprocessed frequency for a specific tone |
| 100 | // The global float array for the song must be used here. | 238 | * @details each active tone has a frequency associated with it, which |
| 101 | #define NOTE_ARRAY_SIZE(x) ((int16_t)(sizeof(x) / (sizeof(x[0])))) | 239 | * the internal state keeps track of, and is usually influenced |
| 102 | #define PLAY_SONG(note_array) play_notes(¬e_array, NOTE_ARRAY_SIZE((note_array)), false) | 240 | * by various effects |
| 103 | #define PLAY_LOOP(note_array) play_notes(¬e_array, NOTE_ARRAY_SIZE((note_array)), true) | 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) | ||
| 104 | 280 | ||
| 105 | 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 5a96bf643..000000000 --- a/quantum/audio/audio_avr.c +++ /dev/null | |||
| @@ -1,810 +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 | if (audio_config.enable) { | ||
| 232 | PLAY_SONG(startup_song); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | void stop_all_notes() { | ||
| 237 | dprintf("audio stop all notes"); | ||
| 238 | |||
| 239 | if (!audio_initialized) { | ||
| 240 | audio_init(); | ||
| 241 | } | ||
| 242 | voices = 0; | ||
| 243 | |||
| 244 | #ifdef CPIN_AUDIO | ||
| 245 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 246 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 247 | #endif | ||
| 248 | |||
| 249 | #ifdef BPIN_AUDIO | ||
| 250 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
| 251 | DISABLE_AUDIO_COUNTER_1_OUTPUT; | ||
| 252 | #endif | ||
| 253 | |||
| 254 | playing_notes = false; | ||
| 255 | playing_note = false; | ||
| 256 | frequency = 0; | ||
| 257 | frequency_alt = 0; | ||
| 258 | volume = 0; | ||
| 259 | |||
| 260 | for (uint8_t i = 0; i < 8; i++) { | ||
| 261 | frequencies[i] = 0; | ||
| 262 | volumes[i] = 0; | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | void stop_note(float freq) { | ||
| 267 | dprintf("audio stop note freq=%d", (int)freq); | ||
| 268 | |||
| 269 | if (playing_note) { | ||
| 270 | if (!audio_initialized) { | ||
| 271 | audio_init(); | ||
| 272 | } | ||
| 273 | for (int i = 7; i >= 0; i--) { | ||
| 274 | if (frequencies[i] == freq) { | ||
| 275 | frequencies[i] = 0; | ||
| 276 | volumes[i] = 0; | ||
| 277 | for (int j = i; (j < 7); j++) { | ||
| 278 | frequencies[j] = frequencies[j + 1]; | ||
| 279 | frequencies[j + 1] = 0; | ||
| 280 | volumes[j] = volumes[j + 1]; | ||
| 281 | volumes[j + 1] = 0; | ||
| 282 | } | ||
| 283 | break; | ||
| 284 | } | ||
| 285 | } | ||
| 286 | voices--; | ||
| 287 | if (voices < 0) voices = 0; | ||
| 288 | if (voice_place >= voices) { | ||
| 289 | voice_place = 0; | ||
| 290 | } | ||
| 291 | if (voices == 0) { | ||
| 292 | #ifdef CPIN_AUDIO | ||
| 293 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 294 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 295 | #endif | ||
| 296 | #ifdef BPIN_AUDIO | ||
| 297 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
| 298 | DISABLE_AUDIO_COUNTER_1_OUTPUT; | ||
| 299 | #endif | ||
| 300 | frequency = 0; | ||
| 301 | frequency_alt = 0; | ||
| 302 | volume = 0; | ||
| 303 | playing_note = false; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | #ifdef VIBRATO_ENABLE | ||
| 309 | |||
| 310 | float mod(float a, int b) { | ||
| 311 | float r = fmod(a, b); | ||
| 312 | return r < 0 ? r + b : r; | ||
| 313 | } | ||
| 314 | |||
| 315 | float vibrato(float average_freq) { | ||
| 316 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
| 317 | float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); | ||
| 318 | # else | ||
| 319 | float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; | ||
| 320 | # endif | ||
| 321 | vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); | ||
| 322 | return vibrated_freq; | ||
| 323 | } | ||
| 324 | |||
| 325 | #endif | ||
| 326 | |||
| 327 | #ifdef CPIN_AUDIO | ||
| 328 | ISR(TIMER3_AUDIO_vect) { | ||
| 329 | float freq; | ||
| 330 | |||
| 331 | if (playing_note) { | ||
| 332 | if (voices > 0) { | ||
| 333 | # ifdef BPIN_AUDIO | ||
| 334 | float freq_alt = 0; | ||
| 335 | if (voices > 1) { | ||
| 336 | if (polyphony_rate == 0) { | ||
| 337 | if (glissando) { | ||
| 338 | if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) { | ||
| 339 | frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2); | ||
| 340 | } else 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 { | ||
| 343 | frequency_alt = frequencies[voices - 2]; | ||
| 344 | } | ||
| 345 | } else { | ||
| 346 | frequency_alt = frequencies[voices - 2]; | ||
| 347 | } | ||
| 348 | |||
| 349 | # ifdef VIBRATO_ENABLE | ||
| 350 | if (vibrato_strength > 0) { | ||
| 351 | freq_alt = vibrato(frequency_alt); | ||
| 352 | } else { | ||
| 353 | freq_alt = frequency_alt; | ||
| 354 | } | ||
| 355 | # else | ||
| 356 | freq_alt = frequency_alt; | ||
| 357 | # endif | ||
| 358 | } | ||
| 359 | |||
| 360 | if (envelope_index < 65535) { | ||
| 361 | envelope_index++; | ||
| 362 | } | ||
| 363 | |||
| 364 | freq_alt = voice_envelope(freq_alt); | ||
| 365 | |||
| 366 | if (freq_alt < 30.517578125) { | ||
| 367 | freq_alt = 30.52; | ||
| 368 | } | ||
| 369 | |||
| 370 | TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq_alt * CPU_PRESCALER)); | ||
| 371 | TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq_alt * CPU_PRESCALER)) * note_timbre); | ||
| 372 | } | ||
| 373 | # endif | ||
| 374 | |||
| 375 | if (polyphony_rate > 0) { | ||
| 376 | if (voices > 1) { | ||
| 377 | voice_place %= voices; | ||
| 378 | if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { | ||
| 379 | voice_place = (voice_place + 1) % voices; | ||
| 380 | place = 0.0; | ||
| 381 | } | ||
| 382 | } | ||
| 383 | |||
| 384 | # ifdef VIBRATO_ENABLE | ||
| 385 | if (vibrato_strength > 0) { | ||
| 386 | freq = vibrato(frequencies[voice_place]); | ||
| 387 | } else { | ||
| 388 | freq = frequencies[voice_place]; | ||
| 389 | } | ||
| 390 | # else | ||
| 391 | freq = frequencies[voice_place]; | ||
| 392 | # endif | ||
| 393 | } else { | ||
| 394 | if (glissando) { | ||
| 395 | if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { | ||
| 396 | frequency = frequency * pow(2, 440 / frequency / 12 / 2); | ||
| 397 | } else 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 { | ||
| 400 | frequency = frequencies[voices - 1]; | ||
| 401 | } | ||
| 402 | } else { | ||
| 403 | frequency = frequencies[voices - 1]; | ||
| 404 | } | ||
| 405 | |||
| 406 | # ifdef VIBRATO_ENABLE | ||
| 407 | if (vibrato_strength > 0) { | ||
| 408 | freq = vibrato(frequency); | ||
| 409 | } else { | ||
| 410 | freq = frequency; | ||
| 411 | } | ||
| 412 | # else | ||
| 413 | freq = frequency; | ||
| 414 | # endif | ||
| 415 | } | ||
| 416 | |||
| 417 | if (envelope_index < 65535) { | ||
| 418 | envelope_index++; | ||
| 419 | } | ||
| 420 | |||
| 421 | freq = voice_envelope(freq); | ||
| 422 | |||
| 423 | if (freq < 30.517578125) { | ||
| 424 | freq = 30.52; | ||
| 425 | } | ||
| 426 | |||
| 427 | TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
| 428 | TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | if (playing_notes) { | ||
| 433 | if (note_frequency > 0) { | ||
| 434 | # ifdef VIBRATO_ENABLE | ||
| 435 | if (vibrato_strength > 0) { | ||
| 436 | freq = vibrato(note_frequency); | ||
| 437 | } else { | ||
| 438 | freq = note_frequency; | ||
| 439 | } | ||
| 440 | # else | ||
| 441 | freq = note_frequency; | ||
| 442 | # endif | ||
| 443 | |||
| 444 | if (envelope_index < 65535) { | ||
| 445 | envelope_index++; | ||
| 446 | } | ||
| 447 | freq = voice_envelope(freq); | ||
| 448 | |||
| 449 | TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
| 450 | TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); | ||
| 451 | } else { | ||
| 452 | TIMER_3_PERIOD = 0; | ||
| 453 | TIMER_3_DUTY_CYCLE = 0; | ||
| 454 | } | ||
| 455 | |||
| 456 | note_position++; | ||
| 457 | bool end_of_note = false; | ||
| 458 | if (TIMER_3_PERIOD > 0) { | ||
| 459 | if (!note_resting) | ||
| 460 | end_of_note = (note_position >= (note_length / TIMER_3_PERIOD * 0xFFFF - 1)); | ||
| 461 | else | ||
| 462 | end_of_note = (note_position >= (note_length)); | ||
| 463 | } else { | ||
| 464 | end_of_note = (note_position >= (note_length)); | ||
| 465 | } | ||
| 466 | |||
| 467 | if (end_of_note) { | ||
| 468 | current_note++; | ||
| 469 | if (current_note >= notes_count) { | ||
| 470 | if (notes_repeat) { | ||
| 471 | current_note = 0; | ||
| 472 | } else { | ||
| 473 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 474 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 475 | playing_notes = false; | ||
| 476 | return; | ||
| 477 | } | ||
| 478 | } | ||
| 479 | if (!note_resting) { | ||
| 480 | note_resting = true; | ||
| 481 | current_note--; | ||
| 482 | if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { | ||
| 483 | note_frequency = 0; | ||
| 484 | note_length = 1; | ||
| 485 | } else { | ||
| 486 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 487 | note_length = 1; | ||
| 488 | } | ||
| 489 | } else { | ||
| 490 | note_resting = false; | ||
| 491 | envelope_index = 0; | ||
| 492 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 493 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
| 494 | } | ||
| 495 | |||
| 496 | note_position = 0; | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | if (!audio_config.enable) { | ||
| 501 | playing_notes = false; | ||
| 502 | playing_note = false; | ||
| 503 | } | ||
| 504 | } | ||
| 505 | #endif | ||
| 506 | |||
| 507 | #ifdef BPIN_AUDIO | ||
| 508 | ISR(TIMER1_AUDIO_vect) { | ||
| 509 | # if defined(BPIN_AUDIO) && !defined(CPIN_AUDIO) | ||
| 510 | float freq = 0; | ||
| 511 | |||
| 512 | if (playing_note) { | ||
| 513 | if (voices > 0) { | ||
| 514 | if (polyphony_rate > 0) { | ||
| 515 | if (voices > 1) { | ||
| 516 | voice_place %= voices; | ||
| 517 | if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { | ||
| 518 | voice_place = (voice_place + 1) % voices; | ||
| 519 | place = 0.0; | ||
| 520 | } | ||
| 521 | } | ||
| 522 | |||
| 523 | # ifdef VIBRATO_ENABLE | ||
| 524 | if (vibrato_strength > 0) { | ||
| 525 | freq = vibrato(frequencies[voice_place]); | ||
| 526 | } else { | ||
| 527 | freq = frequencies[voice_place]; | ||
| 528 | } | ||
| 529 | # else | ||
| 530 | freq = frequencies[voice_place]; | ||
| 531 | # endif | ||
| 532 | } else { | ||
| 533 | if (glissando) { | ||
| 534 | if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { | ||
| 535 | frequency = frequency * pow(2, 440 / frequency / 12 / 2); | ||
| 536 | } else 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 { | ||
| 539 | frequency = frequencies[voices - 1]; | ||
| 540 | } | ||
| 541 | } else { | ||
| 542 | frequency = frequencies[voices - 1]; | ||
| 543 | } | ||
| 544 | |||
| 545 | # ifdef VIBRATO_ENABLE | ||
| 546 | if (vibrato_strength > 0) { | ||
| 547 | freq = vibrato(frequency); | ||
| 548 | } else { | ||
| 549 | freq = frequency; | ||
| 550 | } | ||
| 551 | # else | ||
| 552 | freq = frequency; | ||
| 553 | # endif | ||
| 554 | } | ||
| 555 | |||
| 556 | if (envelope_index < 65535) { | ||
| 557 | envelope_index++; | ||
| 558 | } | ||
| 559 | |||
| 560 | freq = voice_envelope(freq); | ||
| 561 | |||
| 562 | if (freq < 30.517578125) { | ||
| 563 | freq = 30.52; | ||
| 564 | } | ||
| 565 | |||
| 566 | TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
| 567 | TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); | ||
| 568 | } | ||
| 569 | } | ||
| 570 | |||
| 571 | if (playing_notes) { | ||
| 572 | if (note_frequency > 0) { | ||
| 573 | # ifdef VIBRATO_ENABLE | ||
| 574 | if (vibrato_strength > 0) { | ||
| 575 | freq = vibrato(note_frequency); | ||
| 576 | } else { | ||
| 577 | freq = note_frequency; | ||
| 578 | } | ||
| 579 | # else | ||
| 580 | freq = note_frequency; | ||
| 581 | # endif | ||
| 582 | |||
| 583 | if (envelope_index < 65535) { | ||
| 584 | envelope_index++; | ||
| 585 | } | ||
| 586 | freq = voice_envelope(freq); | ||
| 587 | |||
| 588 | TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); | ||
| 589 | TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); | ||
| 590 | } else { | ||
| 591 | TIMER_1_PERIOD = 0; | ||
| 592 | TIMER_1_DUTY_CYCLE = 0; | ||
| 593 | } | ||
| 594 | |||
| 595 | note_position++; | ||
| 596 | bool end_of_note = false; | ||
| 597 | if (TIMER_1_PERIOD > 0) { | ||
| 598 | if (!note_resting) | ||
| 599 | end_of_note = (note_position >= (note_length / TIMER_1_PERIOD * 0xFFFF - 1)); | ||
| 600 | else | ||
| 601 | end_of_note = (note_position >= (note_length)); | ||
| 602 | } else { | ||
| 603 | end_of_note = (note_position >= (note_length)); | ||
| 604 | } | ||
| 605 | |||
| 606 | if (end_of_note) { | ||
| 607 | current_note++; | ||
| 608 | if (current_note >= notes_count) { | ||
| 609 | if (notes_repeat) { | ||
| 610 | current_note = 0; | ||
| 611 | } else { | ||
| 612 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
| 613 | DISABLE_AUDIO_COUNTER_1_OUTPUT; | ||
| 614 | playing_notes = false; | ||
| 615 | return; | ||
| 616 | } | ||
| 617 | } | ||
| 618 | if (!note_resting) { | ||
| 619 | note_resting = true; | ||
| 620 | current_note--; | ||
| 621 | if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { | ||
| 622 | note_frequency = 0; | ||
| 623 | note_length = 1; | ||
| 624 | } else { | ||
| 625 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 626 | note_length = 1; | ||
| 627 | } | ||
| 628 | } else { | ||
| 629 | note_resting = false; | ||
| 630 | envelope_index = 0; | ||
| 631 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 632 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
| 633 | } | ||
| 634 | |||
| 635 | note_position = 0; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | if (!audio_config.enable) { | ||
| 640 | playing_notes = false; | ||
| 641 | playing_note = false; | ||
| 642 | } | ||
| 643 | # endif | ||
| 644 | } | ||
| 645 | #endif | ||
| 646 | |||
| 647 | void play_note(float freq, int vol) { | ||
| 648 | dprintf("audio play note freq=%d vol=%d", (int)freq, vol); | ||
| 649 | |||
| 650 | if (!audio_initialized) { | ||
| 651 | audio_init(); | ||
| 652 | } | ||
| 653 | |||
| 654 | if (audio_config.enable && voices < 8) { | ||
| 655 | #ifdef CPIN_AUDIO | ||
| 656 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 657 | #endif | ||
| 658 | #ifdef BPIN_AUDIO | ||
| 659 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
| 660 | #endif | ||
| 661 | |||
| 662 | // Cancel notes if notes are playing | ||
| 663 | if (playing_notes) stop_all_notes(); | ||
| 664 | |||
| 665 | playing_note = true; | ||
| 666 | |||
| 667 | envelope_index = 0; | ||
| 668 | |||
| 669 | if (freq > 0) { | ||
| 670 | frequencies[voices] = freq; | ||
| 671 | volumes[voices] = vol; | ||
| 672 | voices++; | ||
| 673 | } | ||
| 674 | |||
| 675 | #ifdef CPIN_AUDIO | ||
| 676 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
| 677 | ENABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 678 | #endif | ||
| 679 | #ifdef BPIN_AUDIO | ||
| 680 | # ifdef CPIN_AUDIO | ||
| 681 | if (voices > 1) { | ||
| 682 | ENABLE_AUDIO_COUNTER_1_ISR; | ||
| 683 | ENABLE_AUDIO_COUNTER_1_OUTPUT; | ||
| 684 | } | ||
| 685 | # else | ||
| 686 | ENABLE_AUDIO_COUNTER_1_ISR; | ||
| 687 | ENABLE_AUDIO_COUNTER_1_OUTPUT; | ||
| 688 | # endif | ||
| 689 | #endif | ||
| 690 | } | ||
| 691 | } | ||
| 692 | |||
| 693 | void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) { | ||
| 694 | if (!audio_initialized) { | ||
| 695 | audio_init(); | ||
| 696 | } | ||
| 697 | |||
| 698 | if (audio_config.enable) { | ||
| 699 | #ifdef CPIN_AUDIO | ||
| 700 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 701 | #endif | ||
| 702 | #ifdef BPIN_AUDIO | ||
| 703 | DISABLE_AUDIO_COUNTER_1_ISR; | ||
| 704 | #endif | ||
| 705 | |||
| 706 | // Cancel note if a note is playing | ||
| 707 | if (playing_note) stop_all_notes(); | ||
| 708 | |||
| 709 | playing_notes = true; | ||
| 710 | |||
| 711 | notes_pointer = np; | ||
| 712 | notes_count = n_count; | ||
| 713 | notes_repeat = n_repeat; | ||
| 714 | |||
| 715 | place = 0; | ||
| 716 | current_note = 0; | ||
| 717 | |||
| 718 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 719 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
| 720 | note_position = 0; | ||
| 721 | |||
| 722 | #ifdef CPIN_AUDIO | ||
| 723 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
| 724 | ENABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 725 | #endif | ||
| 726 | #ifdef BPIN_AUDIO | ||
| 727 | # ifndef CPIN_AUDIO | ||
| 728 | ENABLE_AUDIO_COUNTER_1_ISR; | ||
| 729 | ENABLE_AUDIO_COUNTER_1_OUTPUT; | ||
| 730 | # endif | ||
| 731 | #endif | ||
| 732 | } | ||
| 733 | } | ||
| 734 | |||
| 735 | bool is_playing_notes(void) { return playing_notes; } | ||
| 736 | |||
| 737 | bool is_audio_on(void) { return (audio_config.enable != 0); } | ||
| 738 | |||
| 739 | void audio_toggle(void) { | ||
| 740 | audio_config.enable ^= 1; | ||
| 741 | eeconfig_update_audio(audio_config.raw); | ||
| 742 | if (audio_config.enable) audio_on_user(); | ||
| 743 | } | ||
| 744 | |||
| 745 | void audio_on(void) { | ||
| 746 | audio_config.enable = 1; | ||
| 747 | eeconfig_update_audio(audio_config.raw); | ||
| 748 | audio_on_user(); | ||
| 749 | PLAY_SONG(audio_on_song); | ||
| 750 | } | ||
| 751 | |||
| 752 | void audio_off(void) { | ||
| 753 | PLAY_SONG(audio_off_song); | ||
| 754 | wait_ms(100); | ||
| 755 | stop_all_notes(); | ||
| 756 | audio_config.enable = 0; | ||
| 757 | eeconfig_update_audio(audio_config.raw); | ||
| 758 | } | ||
| 759 | |||
| 760 | #ifdef VIBRATO_ENABLE | ||
| 761 | |||
| 762 | // Vibrato rate functions | ||
| 763 | |||
| 764 | void set_vibrato_rate(float rate) { vibrato_rate = rate; } | ||
| 765 | |||
| 766 | void increase_vibrato_rate(float change) { vibrato_rate *= change; } | ||
| 767 | |||
| 768 | void decrease_vibrato_rate(float change) { vibrato_rate /= change; } | ||
| 769 | |||
| 770 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
| 771 | |||
| 772 | void set_vibrato_strength(float strength) { vibrato_strength = strength; } | ||
| 773 | |||
| 774 | void increase_vibrato_strength(float change) { vibrato_strength *= change; } | ||
| 775 | |||
| 776 | void decrease_vibrato_strength(float change) { vibrato_strength /= change; } | ||
| 777 | |||
| 778 | # endif /* VIBRATO_STRENGTH_ENABLE */ | ||
| 779 | |||
| 780 | #endif /* VIBRATO_ENABLE */ | ||
| 781 | |||
| 782 | // Polyphony functions | ||
| 783 | |||
| 784 | void set_polyphony_rate(float rate) { polyphony_rate = rate; } | ||
| 785 | |||
| 786 | void enable_polyphony() { polyphony_rate = 5; } | ||
| 787 | |||
| 788 | void disable_polyphony() { polyphony_rate = 0; } | ||
| 789 | |||
| 790 | void increase_polyphony_rate(float change) { polyphony_rate *= change; } | ||
| 791 | |||
| 792 | void decrease_polyphony_rate(float change) { polyphony_rate /= change; } | ||
| 793 | |||
| 794 | // Timbre function | ||
| 795 | |||
| 796 | void set_timbre(float timbre) { note_timbre = timbre; } | ||
| 797 | |||
| 798 | // Tempo functions | ||
| 799 | |||
| 800 | void set_tempo(uint8_t tempo) { note_tempo = tempo; } | ||
| 801 | |||
| 802 | void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } | ||
| 803 | |||
| 804 | void increase_tempo(uint8_t tempo_change) { | ||
| 805 | if (note_tempo - tempo_change < 10) { | ||
| 806 | note_tempo = 10; | ||
| 807 | } else { | ||
| 808 | note_tempo -= tempo_change; | ||
| 809 | } | ||
| 810 | } | ||
diff --git a/quantum/audio/audio_chibios.c b/quantum/audio/audio_chibios.c deleted file mode 100644 index 1f147f2c9..000000000 --- a/quantum/audio/audio_chibios.c +++ /dev/null | |||
| @@ -1,702 +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 | #define START_CHANNEL_2() \ | ||
| 91 | gptStart(&GPTD7, &gpt7cfg1); \ | ||
| 92 | gptStartContinuous(&GPTD7, 2U) | ||
| 93 | #define STOP_CHANNEL_1() gptStopTimer(&GPTD6) | ||
| 94 | #define STOP_CHANNEL_2() gptStopTimer(&GPTD7) | ||
| 95 | #define RESTART_CHANNEL_1() \ | ||
| 96 | STOP_CHANNEL_1(); \ | ||
| 97 | START_CHANNEL_1() | ||
| 98 | #define RESTART_CHANNEL_2() \ | ||
| 99 | STOP_CHANNEL_2(); \ | ||
| 100 | START_CHANNEL_2() | ||
| 101 | #define UPDATE_CHANNEL_1_FREQ(freq) \ | ||
| 102 | gpt6cfg1.frequency = freq * DAC_BUFFER_SIZE; \ | ||
| 103 | RESTART_CHANNEL_1() | ||
| 104 | #define UPDATE_CHANNEL_2_FREQ(freq) \ | ||
| 105 | gpt7cfg1.frequency = freq * DAC_BUFFER_SIZE; \ | ||
| 106 | RESTART_CHANNEL_2() | ||
| 107 | #define GET_CHANNEL_1_FREQ (uint16_t)(gpt6cfg1.frequency * DAC_BUFFER_SIZE) | ||
| 108 | #define GET_CHANNEL_2_FREQ (uint16_t)(gpt7cfg1.frequency * DAC_BUFFER_SIZE) | ||
| 109 | |||
| 110 | /* | ||
| 111 | * GPT6 configuration. | ||
| 112 | */ | ||
| 113 | // static const GPTConfig gpt6cfg1 = { | ||
| 114 | // .frequency = 1000000U, | ||
| 115 | // .callback = NULL, | ||
| 116 | // .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
| 117 | // .dier = 0U | ||
| 118 | // }; | ||
| 119 | |||
| 120 | GPTConfig gpt6cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE, | ||
| 121 | .callback = NULL, | ||
| 122 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
| 123 | .dier = 0U}; | ||
| 124 | |||
| 125 | GPTConfig gpt7cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE, | ||
| 126 | .callback = NULL, | ||
| 127 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
| 128 | .dier = 0U}; | ||
| 129 | |||
| 130 | GPTConfig gpt8cfg1 = {.frequency = 10, | ||
| 131 | .callback = gpt_cb8, | ||
| 132 | .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ | ||
| 133 | .dier = 0U}; | ||
| 134 | |||
| 135 | /* | ||
| 136 | * DAC test buffer (sine wave). | ||
| 137 | */ | ||
| 138 | // static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = { | ||
| 139 | // 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437, | ||
| 140 | // 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846, | ||
| 141 | // 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221, | ||
| 142 | // 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544, | ||
| 143 | // 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801, | ||
| 144 | // 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982, | ||
| 145 | // 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078, | ||
| 146 | // 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086, | ||
| 147 | // 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004, | ||
| 148 | // 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837, | ||
| 149 | // 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591, | ||
| 150 | // 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278, | ||
| 151 | // 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912, | ||
| 152 | // 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507, | ||
| 153 | // 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082, | ||
| 154 | // 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657, | ||
| 155 | // 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248, | ||
| 156 | // 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873, | ||
| 157 | // 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550, | ||
| 158 | // 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293, | ||
| 159 | // 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112, | ||
| 160 | // 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16, | ||
| 161 | // 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8, | ||
| 162 | // 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90, | ||
| 163 | // 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257, | ||
| 164 | // 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503, | ||
| 165 | // 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816, | ||
| 166 | // 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182, | ||
| 167 | // 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587, | ||
| 168 | // 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012 | ||
| 169 | // }; | ||
| 170 | |||
| 171 | // static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = { | ||
| 172 | // 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8, | ||
| 173 | // 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90, | ||
| 174 | // 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257, | ||
| 175 | // 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503, | ||
| 176 | // 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816, | ||
| 177 | // 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182, | ||
| 178 | // 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587, | ||
| 179 | // 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012, | ||
| 180 | // 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437, | ||
| 181 | // 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846, | ||
| 182 | // 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221, | ||
| 183 | // 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544, | ||
| 184 | // 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801, | ||
| 185 | // 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982, | ||
| 186 | // 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078, | ||
| 187 | // 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086, | ||
| 188 | // 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004, | ||
| 189 | // 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837, | ||
| 190 | // 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591, | ||
| 191 | // 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278, | ||
| 192 | // 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912, | ||
| 193 | // 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507, | ||
| 194 | // 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082, | ||
| 195 | // 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657, | ||
| 196 | // 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248, | ||
| 197 | // 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873, | ||
| 198 | // 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550, | ||
| 199 | // 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293, | ||
| 200 | // 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112, | ||
| 201 | // 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16 | ||
| 202 | // }; | ||
| 203 | |||
| 204 | // squarewave | ||
| 205 | static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = { | ||
| 206 | // First half is max, second half is 0 | ||
| 207 | [0 ... DAC_BUFFER_SIZE / 2 - 1] = DAC_SAMPLE_MAX, | ||
| 208 | [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = 0, | ||
| 209 | }; | ||
| 210 | |||
| 211 | // squarewave | ||
| 212 | static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = { | ||
| 213 | // opposite of dac_buffer above | ||
| 214 | [0 ... DAC_BUFFER_SIZE / 2 - 1] = 0, | ||
| 215 | [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = DAC_SAMPLE_MAX, | ||
| 216 | }; | ||
| 217 | |||
| 218 | /* | ||
| 219 | * DAC streaming callback. | ||
| 220 | */ | ||
| 221 | size_t nz = 0; | ||
| 222 | static void end_cb1(DACDriver *dacp) { | ||
| 223 | (void)dacp; | ||
| 224 | |||
| 225 | nz++; | ||
| 226 | if ((nz % 1000) == 0) { | ||
| 227 | // palTogglePad(GPIOD, GPIOD_LED3); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | /* | ||
| 232 | * DAC error callback. | ||
| 233 | */ | ||
| 234 | static void error_cb1(DACDriver *dacp, dacerror_t err) { | ||
| 235 | (void)dacp; | ||
| 236 | (void)err; | ||
| 237 | |||
| 238 | chSysHalt("DAC failure"); | ||
| 239 | } | ||
| 240 | |||
| 241 | static const DACConfig dac1cfg1 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT}; | ||
| 242 | |||
| 243 | static const DACConversionGroup dacgrpcfg1 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)}; | ||
| 244 | |||
| 245 | static const DACConfig dac1cfg2 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT}; | ||
| 246 | |||
| 247 | static const DACConversionGroup dacgrpcfg2 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)}; | ||
| 248 | |||
| 249 | void audio_init() { | ||
| 250 | if (audio_initialized) { | ||
| 251 | return; | ||
| 252 | } | ||
| 253 | |||
| 254 | // Check EEPROM | ||
| 255 | #ifdef EEPROM_ENABLE | ||
| 256 | if (!eeconfig_is_enabled()) { | ||
| 257 | eeconfig_init(); | ||
| 258 | } | ||
| 259 | audio_config.raw = eeconfig_read_audio(); | ||
| 260 | #else // ARM EEPROM | ||
| 261 | audio_config.enable = true; | ||
| 262 | # ifdef AUDIO_CLICKY_ON | ||
| 263 | audio_config.clicky_enable = true; | ||
| 264 | # endif | ||
| 265 | #endif // ARM EEPROM | ||
| 266 | |||
| 267 | /* | ||
| 268 | * Starting DAC1 driver, setting up the output pin as analog as suggested | ||
| 269 | * by the Reference Manual. | ||
| 270 | */ | ||
| 271 | palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG); | ||
| 272 | palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); | ||
| 273 | dacStart(&DACD1, &dac1cfg1); | ||
| 274 | dacStart(&DACD2, &dac1cfg2); | ||
| 275 | |||
| 276 | /* | ||
| 277 | * Starting GPT6/7 driver, it is used for triggering the DAC. | ||
| 278 | */ | ||
| 279 | START_CHANNEL_1(); | ||
| 280 | START_CHANNEL_2(); | ||
| 281 | |||
| 282 | /* | ||
| 283 | * Starting a continuous conversion. | ||
| 284 | */ | ||
| 285 | dacStartConversion(&DACD1, &dacgrpcfg1, (dacsample_t *)dac_buffer, DAC_BUFFER_SIZE); | ||
| 286 | dacStartConversion(&DACD2, &dacgrpcfg2, (dacsample_t *)dac_buffer_2, DAC_BUFFER_SIZE); | ||
| 287 | |||
| 288 | audio_initialized = true; | ||
| 289 | |||
| 290 | if (audio_config.enable) { | ||
| 291 | PLAY_SONG(startup_song); | ||
| 292 | } else { | ||
| 293 | stop_all_notes(); | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | void stop_all_notes() { | ||
| 298 | dprintf("audio stop all notes"); | ||
| 299 | |||
| 300 | if (!audio_initialized) { | ||
| 301 | audio_init(); | ||
| 302 | } | ||
| 303 | voices = 0; | ||
| 304 | |||
| 305 | gptStopTimer(&GPTD6); | ||
| 306 | gptStopTimer(&GPTD7); | ||
| 307 | gptStopTimer(&GPTD8); | ||
| 308 | |||
| 309 | playing_notes = false; | ||
| 310 | playing_note = false; | ||
| 311 | frequency = 0; | ||
| 312 | frequency_alt = 0; | ||
| 313 | volume = 0; | ||
| 314 | |||
| 315 | for (uint8_t i = 0; i < 8; i++) { | ||
| 316 | frequencies[i] = 0; | ||
| 317 | volumes[i] = 0; | ||
| 318 | } | ||
| 319 | } | ||
| 320 | |||
| 321 | void stop_note(float freq) { | ||
| 322 | dprintf("audio stop note freq=%d", (int)freq); | ||
| 323 | |||
| 324 | if (playing_note) { | ||
| 325 | if (!audio_initialized) { | ||
| 326 | audio_init(); | ||
| 327 | } | ||
| 328 | for (int i = 7; i >= 0; i--) { | ||
| 329 | if (frequencies[i] == freq) { | ||
| 330 | frequencies[i] = 0; | ||
| 331 | volumes[i] = 0; | ||
| 332 | for (int j = i; (j < 7); j++) { | ||
| 333 | frequencies[j] = frequencies[j + 1]; | ||
| 334 | frequencies[j + 1] = 0; | ||
| 335 | volumes[j] = volumes[j + 1]; | ||
| 336 | volumes[j + 1] = 0; | ||
| 337 | } | ||
| 338 | break; | ||
| 339 | } | ||
| 340 | } | ||
| 341 | voices--; | ||
| 342 | if (voices < 0) { | ||
| 343 | voices = 0; | ||
| 344 | } | ||
| 345 | if (voice_place >= voices) { | ||
| 346 | voice_place = 0; | ||
| 347 | } | ||
| 348 | if (voices == 0) { | ||
| 349 | STOP_CHANNEL_1(); | ||
| 350 | STOP_CHANNEL_2(); | ||
| 351 | gptStopTimer(&GPTD8); | ||
| 352 | frequency = 0; | ||
| 353 | frequency_alt = 0; | ||
| 354 | volume = 0; | ||
| 355 | playing_note = false; | ||
| 356 | } | ||
| 357 | } | ||
| 358 | } | ||
| 359 | |||
| 360 | #ifdef VIBRATO_ENABLE | ||
| 361 | |||
| 362 | float mod(float a, int b) { | ||
| 363 | float r = fmod(a, b); | ||
| 364 | return r < 0 ? r + b : r; | ||
| 365 | } | ||
| 366 | |||
| 367 | float vibrato(float average_freq) { | ||
| 368 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
| 369 | float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); | ||
| 370 | # else | ||
| 371 | float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; | ||
| 372 | # endif | ||
| 373 | vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); | ||
| 374 | return vibrated_freq; | ||
| 375 | } | ||
| 376 | |||
| 377 | #endif | ||
| 378 | |||
| 379 | static void gpt_cb8(GPTDriver *gptp) { | ||
| 380 | float freq; | ||
| 381 | |||
| 382 | if (playing_note) { | ||
| 383 | if (voices > 0) { | ||
| 384 | float freq_alt = 0; | ||
| 385 | if (voices > 1) { | ||
| 386 | if (polyphony_rate == 0) { | ||
| 387 | if (glissando) { | ||
| 388 | if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) { | ||
| 389 | frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2); | ||
| 390 | } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440 / frequencies[voices - 2] / 12 / 2)) { | ||
| 391 | frequency_alt = frequency_alt * pow(2, -440 / frequency_alt / 12 / 2); | ||
| 392 | } else { | ||
| 393 | frequency_alt = frequencies[voices - 2]; | ||
| 394 | } | ||
| 395 | } else { | ||
| 396 | frequency_alt = frequencies[voices - 2]; | ||
| 397 | } | ||
| 398 | |||
| 399 | #ifdef VIBRATO_ENABLE | ||
| 400 | if (vibrato_strength > 0) { | ||
| 401 | freq_alt = vibrato(frequency_alt); | ||
| 402 | } else { | ||
| 403 | freq_alt = frequency_alt; | ||
| 404 | } | ||
| 405 | #else | ||
| 406 | freq_alt = frequency_alt; | ||
| 407 | #endif | ||
| 408 | } | ||
| 409 | |||
| 410 | if (envelope_index < 65535) { | ||
| 411 | envelope_index++; | ||
| 412 | } | ||
| 413 | |||
| 414 | freq_alt = voice_envelope(freq_alt); | ||
| 415 | |||
| 416 | if (freq_alt < 30.517578125) { | ||
| 417 | freq_alt = 30.52; | ||
| 418 | } | ||
| 419 | |||
| 420 | if (GET_CHANNEL_2_FREQ != (uint16_t)freq_alt) { | ||
| 421 | UPDATE_CHANNEL_2_FREQ(freq_alt); | ||
| 422 | } else { | ||
| 423 | RESTART_CHANNEL_2(); | ||
| 424 | } | ||
| 425 | // note_timbre; | ||
| 426 | } | ||
| 427 | |||
| 428 | if (polyphony_rate > 0) { | ||
| 429 | if (voices > 1) { | ||
| 430 | voice_place %= voices; | ||
| 431 | if (place++ > (frequencies[voice_place] / polyphony_rate)) { | ||
| 432 | voice_place = (voice_place + 1) % voices; | ||
| 433 | place = 0.0; | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 437 | #ifdef VIBRATO_ENABLE | ||
| 438 | if (vibrato_strength > 0) { | ||
| 439 | freq = vibrato(frequencies[voice_place]); | ||
| 440 | } else { | ||
| 441 | freq = frequencies[voice_place]; | ||
| 442 | } | ||
| 443 | #else | ||
| 444 | freq = frequencies[voice_place]; | ||
| 445 | #endif | ||
| 446 | } else { | ||
| 447 | if (glissando) { | ||
| 448 | if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { | ||
| 449 | frequency = frequency * pow(2, 440 / frequency / 12 / 2); | ||
| 450 | } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { | ||
| 451 | frequency = frequency * pow(2, -440 / frequency / 12 / 2); | ||
| 452 | } else { | ||
| 453 | frequency = frequencies[voices - 1]; | ||
| 454 | } | ||
| 455 | } else { | ||
| 456 | frequency = frequencies[voices - 1]; | ||
| 457 | } | ||
| 458 | |||
| 459 | #ifdef VIBRATO_ENABLE | ||
| 460 | if (vibrato_strength > 0) { | ||
| 461 | freq = vibrato(frequency); | ||
| 462 | } else { | ||
| 463 | freq = frequency; | ||
| 464 | } | ||
| 465 | #else | ||
| 466 | freq = frequency; | ||
| 467 | #endif | ||
| 468 | } | ||
| 469 | |||
| 470 | if (envelope_index < 65535) { | ||
| 471 | envelope_index++; | ||
| 472 | } | ||
| 473 | |||
| 474 | freq = voice_envelope(freq); | ||
| 475 | |||
| 476 | if (freq < 30.517578125) { | ||
| 477 | freq = 30.52; | ||
| 478 | } | ||
| 479 | |||
| 480 | if (GET_CHANNEL_1_FREQ != (uint16_t)freq) { | ||
| 481 | UPDATE_CHANNEL_1_FREQ(freq); | ||
| 482 | } else { | ||
| 483 | RESTART_CHANNEL_1(); | ||
| 484 | } | ||
| 485 | // note_timbre; | ||
| 486 | } | ||
| 487 | } | ||
| 488 | |||
| 489 | if (playing_notes) { | ||
| 490 | if (note_frequency > 0) { | ||
| 491 | #ifdef VIBRATO_ENABLE | ||
| 492 | if (vibrato_strength > 0) { | ||
| 493 | freq = vibrato(note_frequency); | ||
| 494 | } else { | ||
| 495 | freq = note_frequency; | ||
| 496 | } | ||
| 497 | #else | ||
| 498 | freq = note_frequency; | ||
| 499 | #endif | ||
| 500 | |||
| 501 | if (envelope_index < 65535) { | ||
| 502 | envelope_index++; | ||
| 503 | } | ||
| 504 | freq = voice_envelope(freq); | ||
| 505 | |||
| 506 | if (GET_CHANNEL_1_FREQ != (uint16_t)freq) { | ||
| 507 | UPDATE_CHANNEL_1_FREQ(freq); | ||
| 508 | UPDATE_CHANNEL_2_FREQ(freq); | ||
| 509 | } | ||
| 510 | // note_timbre; | ||
| 511 | } else { | ||
| 512 | // gptStopTimer(&GPTD6); | ||
| 513 | // gptStopTimer(&GPTD7); | ||
| 514 | } | ||
| 515 | |||
| 516 | note_position++; | ||
| 517 | bool end_of_note = false; | ||
| 518 | if (GET_CHANNEL_1_FREQ > 0) { | ||
| 519 | if (!note_resting) | ||
| 520 | end_of_note = (note_position >= (note_length * 8 - 1)); | ||
| 521 | else | ||
| 522 | end_of_note = (note_position >= (note_length * 8)); | ||
| 523 | } else { | ||
| 524 | end_of_note = (note_position >= (note_length * 8)); | ||
| 525 | } | ||
| 526 | |||
| 527 | if (end_of_note) { | ||
| 528 | current_note++; | ||
| 529 | if (current_note >= notes_count) { | ||
| 530 | if (notes_repeat) { | ||
| 531 | current_note = 0; | ||
| 532 | } else { | ||
| 533 | STOP_CHANNEL_1(); | ||
| 534 | STOP_CHANNEL_2(); | ||
| 535 | // gptStopTimer(&GPTD8); | ||
| 536 | playing_notes = false; | ||
| 537 | return; | ||
| 538 | } | ||
| 539 | } | ||
| 540 | if (!note_resting) { | ||
| 541 | note_resting = true; | ||
| 542 | current_note--; | ||
| 543 | if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { | ||
| 544 | note_frequency = 0; | ||
| 545 | note_length = 1; | ||
| 546 | } else { | ||
| 547 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 548 | note_length = 1; | ||
| 549 | } | ||
| 550 | } else { | ||
| 551 | note_resting = false; | ||
| 552 | envelope_index = 0; | ||
| 553 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 554 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
| 555 | } | ||
| 556 | |||
| 557 | note_position = 0; | ||
| 558 | } | ||
| 559 | } | ||
| 560 | |||
| 561 | if (!audio_config.enable) { | ||
| 562 | playing_notes = false; | ||
| 563 | playing_note = false; | ||
| 564 | } | ||
| 565 | } | ||
| 566 | |||
| 567 | void play_note(float freq, int vol) { | ||
| 568 | dprintf("audio play note freq=%d vol=%d", (int)freq, vol); | ||
| 569 | |||
| 570 | if (!audio_initialized) { | ||
| 571 | audio_init(); | ||
| 572 | } | ||
| 573 | |||
| 574 | if (audio_config.enable && voices < 8) { | ||
| 575 | // Cancel notes if notes are playing | ||
| 576 | if (playing_notes) { | ||
| 577 | stop_all_notes(); | ||
| 578 | } | ||
| 579 | |||
| 580 | playing_note = true; | ||
| 581 | |||
| 582 | envelope_index = 0; | ||
| 583 | |||
| 584 | if (freq > 0) { | ||
| 585 | frequencies[voices] = freq; | ||
| 586 | volumes[voices] = vol; | ||
| 587 | voices++; | ||
| 588 | } | ||
| 589 | |||
| 590 | gptStart(&GPTD8, &gpt8cfg1); | ||
| 591 | gptStartContinuous(&GPTD8, 2U); | ||
| 592 | RESTART_CHANNEL_1(); | ||
| 593 | RESTART_CHANNEL_2(); | ||
| 594 | } | ||
| 595 | } | ||
| 596 | |||
| 597 | void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) { | ||
| 598 | if (!audio_initialized) { | ||
| 599 | audio_init(); | ||
| 600 | } | ||
| 601 | |||
| 602 | if (audio_config.enable) { | ||
| 603 | // Cancel note if a note is playing | ||
| 604 | if (playing_note) { | ||
| 605 | stop_all_notes(); | ||
| 606 | } | ||
| 607 | |||
| 608 | playing_notes = true; | ||
| 609 | |||
| 610 | notes_pointer = np; | ||
| 611 | notes_count = n_count; | ||
| 612 | notes_repeat = n_repeat; | ||
| 613 | |||
| 614 | place = 0; | ||
| 615 | current_note = 0; | ||
| 616 | |||
| 617 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 618 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
| 619 | note_position = 0; | ||
| 620 | |||
| 621 | gptStart(&GPTD8, &gpt8cfg1); | ||
| 622 | gptStartContinuous(&GPTD8, 2U); | ||
| 623 | RESTART_CHANNEL_1(); | ||
| 624 | RESTART_CHANNEL_2(); | ||
| 625 | } | ||
| 626 | } | ||
| 627 | |||
| 628 | bool is_playing_notes(void) { return playing_notes; } | ||
| 629 | |||
| 630 | bool is_audio_on(void) { return (audio_config.enable != 0); } | ||
| 631 | |||
| 632 | void audio_toggle(void) { | ||
| 633 | audio_config.enable ^= 1; | ||
| 634 | eeconfig_update_audio(audio_config.raw); | ||
| 635 | if (audio_config.enable) { | ||
| 636 | audio_on_user(); | ||
| 637 | } | ||
| 638 | } | ||
| 639 | |||
| 640 | void audio_on(void) { | ||
| 641 | audio_config.enable = 1; | ||
| 642 | eeconfig_update_audio(audio_config.raw); | ||
| 643 | audio_on_user(); | ||
| 644 | } | ||
| 645 | |||
| 646 | void audio_off(void) { | ||
| 647 | stop_all_notes(); | ||
| 648 | audio_config.enable = 0; | ||
| 649 | eeconfig_update_audio(audio_config.raw); | ||
| 650 | } | ||
| 651 | |||
| 652 | #ifdef VIBRATO_ENABLE | ||
| 653 | |||
| 654 | // Vibrato rate functions | ||
| 655 | |||
| 656 | void set_vibrato_rate(float rate) { vibrato_rate = rate; } | ||
| 657 | |||
| 658 | void increase_vibrato_rate(float change) { vibrato_rate *= change; } | ||
| 659 | |||
| 660 | void decrease_vibrato_rate(float change) { vibrato_rate /= change; } | ||
| 661 | |||
| 662 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
| 663 | |||
| 664 | void set_vibrato_strength(float strength) { vibrato_strength = strength; } | ||
| 665 | |||
| 666 | void increase_vibrato_strength(float change) { vibrato_strength *= change; } | ||
| 667 | |||
| 668 | void decrease_vibrato_strength(float change) { vibrato_strength /= change; } | ||
| 669 | |||
| 670 | # endif /* VIBRATO_STRENGTH_ENABLE */ | ||
| 671 | |||
| 672 | #endif /* VIBRATO_ENABLE */ | ||
| 673 | |||
| 674 | // Polyphony functions | ||
| 675 | |||
| 676 | void set_polyphony_rate(float rate) { polyphony_rate = rate; } | ||
| 677 | |||
| 678 | void enable_polyphony() { polyphony_rate = 5; } | ||
| 679 | |||
| 680 | void disable_polyphony() { polyphony_rate = 0; } | ||
| 681 | |||
| 682 | void increase_polyphony_rate(float change) { polyphony_rate *= change; } | ||
| 683 | |||
| 684 | void decrease_polyphony_rate(float change) { polyphony_rate /= change; } | ||
| 685 | |||
| 686 | // Timbre function | ||
| 687 | |||
| 688 | void set_timbre(float timbre) { note_timbre = timbre; } | ||
| 689 | |||
| 690 | // Tempo functions | ||
| 691 | |||
| 692 | void set_tempo(uint8_t tempo) { note_tempo = tempo; } | ||
| 693 | |||
| 694 | void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } | ||
| 695 | |||
| 696 | void increase_tempo(uint8_t tempo_change) { | ||
| 697 | if (note_tempo - tempo_change < 10) { | ||
| 698 | note_tempo = 10; | ||
| 699 | } else { | ||
| 700 | note_tempo -= tempo_change; | ||
| 701 | } | ||
| 702 | } | ||
diff --git a/quantum/audio/audio_pwm.c b/quantum/audio/audio_pwm.c deleted file mode 100644 index 545aef6dd..000000000 --- a/quantum/audio/audio_pwm.c +++ /dev/null | |||
| @@ -1,595 +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 | // Timer Abstractions | ||
| 33 | |||
| 34 | // TIMSK3 - Timer/Counter #3 Interrupt Mask Register | ||
| 35 | // Turn on/off 3A interputs, stopping/enabling the ISR calls | ||
| 36 | #define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) | ||
| 37 | #define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) | ||
| 38 | |||
| 39 | // TCCR3A: Timer/Counter #3 Control Register | ||
| 40 | // Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 | ||
| 41 | #define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); | ||
| 42 | #define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); | ||
| 43 | |||
| 44 | #define NOTE_PERIOD ICR3 | ||
| 45 | #define NOTE_DUTY_CYCLE OCR3A | ||
| 46 | |||
| 47 | #ifdef PWM_AUDIO | ||
| 48 | # include "wave.h" | ||
| 49 | # define SAMPLE_DIVIDER 39 | ||
| 50 | # define SAMPLE_RATE (2000000.0 / SAMPLE_DIVIDER / 2048) | ||
| 51 | // Resistor value of 1/ (2 * PI * 10nF * (2000000 hertz / SAMPLE_DIVIDER / 10)) for 10nF cap | ||
| 52 | |||
| 53 | float places[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
| 54 | uint16_t place_int = 0; | ||
| 55 | bool repeat = true; | ||
| 56 | #endif | ||
| 57 | |||
| 58 | void delay_us(int count) { | ||
| 59 | while (count--) { | ||
| 60 | _delay_us(1); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | int voices = 0; | ||
| 65 | int voice_place = 0; | ||
| 66 | float frequency = 0; | ||
| 67 | int volume = 0; | ||
| 68 | long position = 0; | ||
| 69 | |||
| 70 | float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
| 71 | int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | ||
| 72 | bool sliding = false; | ||
| 73 | |||
| 74 | float place = 0; | ||
| 75 | |||
| 76 | uint8_t* sample; | ||
| 77 | uint16_t sample_length = 0; | ||
| 78 | // float freq = 0; | ||
| 79 | |||
| 80 | bool playing_notes = false; | ||
| 81 | bool playing_note = false; | ||
| 82 | float note_frequency = 0; | ||
| 83 | float note_length = 0; | ||
| 84 | uint8_t note_tempo = TEMPO_DEFAULT; | ||
| 85 | float note_timbre = TIMBRE_DEFAULT; | ||
| 86 | uint16_t note_position = 0; | ||
| 87 | float (*notes_pointer)[][2]; | ||
| 88 | uint16_t notes_count; | ||
| 89 | bool notes_repeat; | ||
| 90 | float notes_rest; | ||
| 91 | bool note_resting = false; | ||
| 92 | |||
| 93 | uint16_t current_note = 0; | ||
| 94 | uint8_t rest_counter = 0; | ||
| 95 | |||
| 96 | #ifdef VIBRATO_ENABLE | ||
| 97 | float vibrato_counter = 0; | ||
| 98 | float vibrato_strength = .5; | ||
| 99 | float vibrato_rate = 0.125; | ||
| 100 | #endif | ||
| 101 | |||
| 102 | float polyphony_rate = 0; | ||
| 103 | |||
| 104 | static bool audio_initialized = false; | ||
| 105 | |||
| 106 | audio_config_t audio_config; | ||
| 107 | |||
| 108 | uint16_t envelope_index = 0; | ||
| 109 | |||
| 110 | void audio_init() { | ||
| 111 | // Check EEPROM | ||
| 112 | if (!eeconfig_is_enabled()) { | ||
| 113 | eeconfig_init(); | ||
| 114 | } | ||
| 115 | audio_config.raw = eeconfig_read_audio(); | ||
| 116 | |||
| 117 | #ifdef PWM_AUDIO | ||
| 118 | |||
| 119 | PLLFRQ = _BV(PDIV2); | ||
| 120 | PLLCSR = _BV(PLLE); | ||
| 121 | while (!(PLLCSR & _BV(PLOCK))) | ||
| 122 | ; | ||
| 123 | PLLFRQ |= _BV(PLLTM0); /* PCK 48MHz */ | ||
| 124 | |||
| 125 | /* Init a fast PWM on Timer4 */ | ||
| 126 | TCCR4A = _BV(COM4A0) | _BV(PWM4A); /* Clear OC4A on Compare Match */ | ||
| 127 | TCCR4B = _BV(CS40); /* No prescaling => f = PCK/256 = 187500Hz */ | ||
| 128 | OCR4A = 0; | ||
| 129 | |||
| 130 | /* Enable the OC4A output */ | ||
| 131 | DDRC |= _BV(PORTC6); | ||
| 132 | |||
| 133 | DISABLE_AUDIO_COUNTER_3_ISR; // Turn off 3A interputs | ||
| 134 | |||
| 135 | TCCR3A = 0x0; // Options not needed | ||
| 136 | TCCR3B = _BV(CS31) | _BV(CS30) | _BV(WGM32); // 64th prescaling and CTC | ||
| 137 | OCR3A = SAMPLE_DIVIDER - 1; // Correct count/compare, related to sample playback | ||
| 138 | |||
| 139 | #else | ||
| 140 | |||
| 141 | // Set port PC6 (OC3A and /OC4A) as output | ||
| 142 | DDRC |= _BV(PORTC6); | ||
| 143 | |||
| 144 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 145 | |||
| 146 | // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers | ||
| 147 | // Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 | ||
| 148 | // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14 (Period = ICR3, Duty Cycle = OCR3A) | ||
| 149 | // Clock Select (CS3n) = 0b010 = Clock / 8 | ||
| 150 | TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); | ||
| 151 | TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); | ||
| 152 | |||
| 153 | #endif | ||
| 154 | |||
| 155 | audio_initialized = true; | ||
| 156 | } | ||
| 157 | |||
| 158 | void stop_all_notes() { | ||
| 159 | if (!audio_initialized) { | ||
| 160 | audio_init(); | ||
| 161 | } | ||
| 162 | voices = 0; | ||
| 163 | #ifdef PWM_AUDIO | ||
| 164 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 165 | #else | ||
| 166 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 167 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 168 | #endif | ||
| 169 | |||
| 170 | playing_notes = false; | ||
| 171 | playing_note = false; | ||
| 172 | frequency = 0; | ||
| 173 | volume = 0; | ||
| 174 | |||
| 175 | for (uint8_t i = 0; i < 8; i++) { | ||
| 176 | frequencies[i] = 0; | ||
| 177 | volumes[i] = 0; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | void stop_note(float freq) { | ||
| 182 | if (playing_note) { | ||
| 183 | if (!audio_initialized) { | ||
| 184 | audio_init(); | ||
| 185 | } | ||
| 186 | #ifdef PWM_AUDIO | ||
| 187 | freq = freq / SAMPLE_RATE; | ||
| 188 | #endif | ||
| 189 | for (int i = 7; i >= 0; i--) { | ||
| 190 | if (frequencies[i] == freq) { | ||
| 191 | frequencies[i] = 0; | ||
| 192 | volumes[i] = 0; | ||
| 193 | for (int j = i; (j < 7); j++) { | ||
| 194 | frequencies[j] = frequencies[j + 1]; | ||
| 195 | frequencies[j + 1] = 0; | ||
| 196 | volumes[j] = volumes[j + 1]; | ||
| 197 | volumes[j + 1] = 0; | ||
| 198 | } | ||
| 199 | break; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | voices--; | ||
| 203 | if (voices < 0) voices = 0; | ||
| 204 | if (voice_place >= voices) { | ||
| 205 | voice_place = 0; | ||
| 206 | } | ||
| 207 | if (voices == 0) { | ||
| 208 | #ifdef PWM_AUDIO | ||
| 209 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 210 | #else | ||
| 211 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 212 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 213 | #endif | ||
| 214 | frequency = 0; | ||
| 215 | volume = 0; | ||
| 216 | playing_note = false; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | #ifdef VIBRATO_ENABLE | ||
| 222 | |||
| 223 | float mod(float a, int b) { | ||
| 224 | float r = fmod(a, b); | ||
| 225 | return r < 0 ? r + b : r; | ||
| 226 | } | ||
| 227 | |||
| 228 | float vibrato(float average_freq) { | ||
| 229 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
| 230 | float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); | ||
| 231 | # else | ||
| 232 | float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; | ||
| 233 | # endif | ||
| 234 | vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); | ||
| 235 | return vibrated_freq; | ||
| 236 | } | ||
| 237 | |||
| 238 | #endif | ||
| 239 | |||
| 240 | ISR(TIMER3_COMPA_vect) { | ||
| 241 | if (playing_note) { | ||
| 242 | #ifdef PWM_AUDIO | ||
| 243 | if (voices == 1) { | ||
| 244 | // SINE | ||
| 245 | OCR4A = pgm_read_byte(&sinewave[(uint16_t)place]) >> 2; | ||
| 246 | |||
| 247 | // SQUARE | ||
| 248 | // if (((int)place) >= 1024){ | ||
| 249 | // OCR4A = 0xFF >> 2; | ||
| 250 | // } else { | ||
| 251 | // OCR4A = 0x00; | ||
| 252 | // } | ||
| 253 | |||
| 254 | // SAWTOOTH | ||
| 255 | // OCR4A = (int)place / 4; | ||
| 256 | |||
| 257 | // TRIANGLE | ||
| 258 | // if (((int)place) >= 1024) { | ||
| 259 | // OCR4A = (int)place / 2; | ||
| 260 | // } else { | ||
| 261 | // OCR4A = 2048 - (int)place / 2; | ||
| 262 | // } | ||
| 263 | |||
| 264 | place += frequency; | ||
| 265 | |||
| 266 | if (place >= SINE_LENGTH) place -= SINE_LENGTH; | ||
| 267 | |||
| 268 | } else { | ||
| 269 | int sum = 0; | ||
| 270 | for (int i = 0; i < voices; i++) { | ||
| 271 | // SINE | ||
| 272 | sum += pgm_read_byte(&sinewave[(uint16_t)places[i]]) >> 2; | ||
| 273 | |||
| 274 | // SQUARE | ||
| 275 | // if (((int)places[i]) >= 1024){ | ||
| 276 | // sum += 0xFF >> 2; | ||
| 277 | // } else { | ||
| 278 | // sum += 0x00; | ||
| 279 | // } | ||
| 280 | |||
| 281 | places[i] += frequencies[i]; | ||
| 282 | |||
| 283 | if (places[i] >= SINE_LENGTH) places[i] -= SINE_LENGTH; | ||
| 284 | } | ||
| 285 | OCR4A = sum; | ||
| 286 | } | ||
| 287 | #else | ||
| 288 | if (voices > 0) { | ||
| 289 | float freq; | ||
| 290 | if (polyphony_rate > 0) { | ||
| 291 | if (voices > 1) { | ||
| 292 | voice_place %= voices; | ||
| 293 | if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { | ||
| 294 | voice_place = (voice_place + 1) % voices; | ||
| 295 | place = 0.0; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | # ifdef VIBRATO_ENABLE | ||
| 299 | if (vibrato_strength > 0) { | ||
| 300 | freq = vibrato(frequencies[voice_place]); | ||
| 301 | } else { | ||
| 302 | # else | ||
| 303 | { | ||
| 304 | # endif | ||
| 305 | freq = frequencies[voice_place]; | ||
| 306 | } | ||
| 307 | } else { | ||
| 308 | if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { | ||
| 309 | frequency = frequency * pow(2, 440 / frequency / 12 / 2); | ||
| 310 | } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { | ||
| 311 | frequency = frequency * pow(2, -440 / frequency / 12 / 2); | ||
| 312 | } else { | ||
| 313 | frequency = frequencies[voices - 1]; | ||
| 314 | } | ||
| 315 | |||
| 316 | # ifdef VIBRATO_ENABLE | ||
| 317 | if (vibrato_strength > 0) { | ||
| 318 | freq = vibrato(frequency); | ||
| 319 | } else { | ||
| 320 | # else | ||
| 321 | { | ||
| 322 | # endif | ||
| 323 | freq = frequency; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | |||
| 327 | if (envelope_index < 65535) { | ||
| 328 | envelope_index++; | ||
| 329 | } | ||
| 330 | freq = voice_envelope(freq); | ||
| 331 | |||
| 332 | if (freq < 30.517578125) freq = 30.52; | ||
| 333 | NOTE_PERIOD = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period | ||
| 334 | NOTE_DUTY_CYCLE = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period | ||
| 335 | } | ||
| 336 | #endif | ||
| 337 | } | ||
| 338 | |||
| 339 | // SAMPLE | ||
| 340 | // OCR4A = pgm_read_byte(&sample[(uint16_t)place_int]); | ||
| 341 | |||
| 342 | // place_int++; | ||
| 343 | |||
| 344 | // if (place_int >= sample_length) | ||
| 345 | // if (repeat) | ||
| 346 | // place_int -= sample_length; | ||
| 347 | // else | ||
| 348 | // DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 349 | |||
| 350 | if (playing_notes) { | ||
| 351 | #ifdef PWM_AUDIO | ||
| 352 | OCR4A = pgm_read_byte(&sinewave[(uint16_t)place]) >> 0; | ||
| 353 | |||
| 354 | place += note_frequency; | ||
| 355 | if (place >= SINE_LENGTH) place -= SINE_LENGTH; | ||
| 356 | #else | ||
| 357 | if (note_frequency > 0) { | ||
| 358 | float freq; | ||
| 359 | |||
| 360 | # ifdef VIBRATO_ENABLE | ||
| 361 | if (vibrato_strength > 0) { | ||
| 362 | freq = vibrato(note_frequency); | ||
| 363 | } else { | ||
| 364 | # else | ||
| 365 | { | ||
| 366 | # endif | ||
| 367 | freq = note_frequency; | ||
| 368 | } | ||
| 369 | |||
| 370 | if (envelope_index < 65535) { | ||
| 371 | envelope_index++; | ||
| 372 | } | ||
| 373 | freq = voice_envelope(freq); | ||
| 374 | |||
| 375 | NOTE_PERIOD = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period | ||
| 376 | NOTE_DUTY_CYCLE = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period | ||
| 377 | } else { | ||
| 378 | NOTE_PERIOD = 0; | ||
| 379 | NOTE_DUTY_CYCLE = 0; | ||
| 380 | } | ||
| 381 | #endif | ||
| 382 | |||
| 383 | note_position++; | ||
| 384 | bool end_of_note = false; | ||
| 385 | if (NOTE_PERIOD > 0) | ||
| 386 | end_of_note = (note_position >= (note_length / NOTE_PERIOD * 0xFFFF)); | ||
| 387 | else | ||
| 388 | end_of_note = (note_position >= (note_length * 0x7FF)); | ||
| 389 | if (end_of_note) { | ||
| 390 | current_note++; | ||
| 391 | if (current_note >= notes_count) { | ||
| 392 | if (notes_repeat) { | ||
| 393 | current_note = 0; | ||
| 394 | } else { | ||
| 395 | #ifdef PWM_AUDIO | ||
| 396 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 397 | #else | ||
| 398 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 399 | DISABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 400 | #endif | ||
| 401 | playing_notes = false; | ||
| 402 | return; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | if (!note_resting && (notes_rest > 0)) { | ||
| 406 | note_resting = true; | ||
| 407 | note_frequency = 0; | ||
| 408 | note_length = notes_rest; | ||
| 409 | current_note--; | ||
| 410 | } else { | ||
| 411 | note_resting = false; | ||
| 412 | #ifdef PWM_AUDIO | ||
| 413 | note_frequency = (*notes_pointer)[current_note][0] / SAMPLE_RATE; | ||
| 414 | note_length = (*notes_pointer)[current_note][1] * (((float)note_tempo) / 100); | ||
| 415 | #else | ||
| 416 | envelope_index = 0; | ||
| 417 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 418 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
| 419 | #endif | ||
| 420 | } | ||
| 421 | note_position = 0; | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | if (!audio_config.enable) { | ||
| 426 | playing_notes = false; | ||
| 427 | playing_note = false; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | void play_note(float freq, int vol) { | ||
| 432 | if (!audio_initialized) { | ||
| 433 | audio_init(); | ||
| 434 | } | ||
| 435 | |||
| 436 | if (audio_config.enable && voices < 8) { | ||
| 437 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 438 | |||
| 439 | // Cancel notes if notes are playing | ||
| 440 | if (playing_notes) stop_all_notes(); | ||
| 441 | |||
| 442 | playing_note = true; | ||
| 443 | |||
| 444 | envelope_index = 0; | ||
| 445 | |||
| 446 | #ifdef PWM_AUDIO | ||
| 447 | freq = freq / SAMPLE_RATE; | ||
| 448 | #endif | ||
| 449 | if (freq > 0) { | ||
| 450 | frequencies[voices] = freq; | ||
| 451 | volumes[voices] = vol; | ||
| 452 | voices++; | ||
| 453 | } | ||
| 454 | |||
| 455 | #ifdef PWM_AUDIO | ||
| 456 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
| 457 | #else | ||
| 458 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
| 459 | ENABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 460 | #endif | ||
| 461 | } | ||
| 462 | } | ||
| 463 | |||
| 464 | void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat, float n_rest) { | ||
| 465 | if (!audio_initialized) { | ||
| 466 | audio_init(); | ||
| 467 | } | ||
| 468 | |||
| 469 | if (audio_config.enable) { | ||
| 470 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 471 | |||
| 472 | // Cancel note if a note is playing | ||
| 473 | if (playing_note) stop_all_notes(); | ||
| 474 | |||
| 475 | playing_notes = true; | ||
| 476 | |||
| 477 | notes_pointer = np; | ||
| 478 | notes_count = n_count; | ||
| 479 | notes_repeat = n_repeat; | ||
| 480 | notes_rest = n_rest; | ||
| 481 | |||
| 482 | place = 0; | ||
| 483 | current_note = 0; | ||
| 484 | |||
| 485 | #ifdef PWM_AUDIO | ||
| 486 | note_frequency = (*notes_pointer)[current_note][0] / SAMPLE_RATE; | ||
| 487 | note_length = (*notes_pointer)[current_note][1] * (((float)note_tempo) / 100); | ||
| 488 | #else | ||
| 489 | note_frequency = (*notes_pointer)[current_note][0]; | ||
| 490 | note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); | ||
| 491 | #endif | ||
| 492 | note_position = 0; | ||
| 493 | |||
| 494 | #ifdef PWM_AUDIO | ||
| 495 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
| 496 | #else | ||
| 497 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
| 498 | ENABLE_AUDIO_COUNTER_3_OUTPUT; | ||
| 499 | #endif | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | #ifdef PWM_AUDIO | ||
| 504 | void play_sample(uint8_t* s, uint16_t l, bool r) { | ||
| 505 | if (!audio_initialized) { | ||
| 506 | audio_init(); | ||
| 507 | } | ||
| 508 | |||
| 509 | if (audio_config.enable) { | ||
| 510 | DISABLE_AUDIO_COUNTER_3_ISR; | ||
| 511 | stop_all_notes(); | ||
| 512 | place_int = 0; | ||
| 513 | sample = s; | ||
| 514 | sample_length = l; | ||
| 515 | repeat = r; | ||
| 516 | |||
| 517 | ENABLE_AUDIO_COUNTER_3_ISR; | ||
| 518 | } | ||
| 519 | } | ||
| 520 | #endif | ||
| 521 | |||
| 522 | void audio_toggle(void) { | ||
| 523 | audio_config.enable ^= 1; | ||
| 524 | eeconfig_update_audio(audio_config.raw); | ||
| 525 | } | ||
| 526 | |||
| 527 | void audio_on(void) { | ||
| 528 | audio_config.enable = 1; | ||
| 529 | eeconfig_update_audio(audio_config.raw); | ||
| 530 | } | ||
| 531 | |||
| 532 | void audio_off(void) { | ||
| 533 | audio_config.enable = 0; | ||
| 534 | eeconfig_update_audio(audio_config.raw); | ||
| 535 | } | ||
| 536 | |||
| 537 | #ifdef VIBRATO_ENABLE | ||
| 538 | |||
| 539 | // Vibrato rate functions | ||
| 540 | |||
| 541 | void set_vibrato_rate(float rate) { vibrato_rate = rate; } | ||
| 542 | |||
| 543 | void increase_vibrato_rate(float change) { vibrato_rate *= change; } | ||
| 544 | |||
| 545 | void decrease_vibrato_rate(float change) { vibrato_rate /= change; } | ||
| 546 | |||
| 547 | # ifdef VIBRATO_STRENGTH_ENABLE | ||
| 548 | |||
| 549 | void set_vibrato_strength(float strength) { vibrato_strength = strength; } | ||
| 550 | |||
| 551 | void increase_vibrato_strength(float change) { vibrato_strength *= change; } | ||
| 552 | |||
| 553 | void decrease_vibrato_strength(float change) { vibrato_strength /= change; } | ||
| 554 | |||
| 555 | # endif /* VIBRATO_STRENGTH_ENABLE */ | ||
| 556 | |||
| 557 | #endif /* VIBRATO_ENABLE */ | ||
| 558 | |||
| 559 | // Polyphony functions | ||
| 560 | |||
| 561 | void set_polyphony_rate(float rate) { polyphony_rate = rate; } | ||
| 562 | |||
| 563 | void enable_polyphony() { polyphony_rate = 5; } | ||
| 564 | |||
| 565 | void disable_polyphony() { polyphony_rate = 0; } | ||
| 566 | |||
| 567 | void increase_polyphony_rate(float change) { polyphony_rate *= change; } | ||
| 568 | |||
| 569 | void decrease_polyphony_rate(float change) { polyphony_rate /= change; } | ||
| 570 | |||
| 571 | // Timbre function | ||
| 572 | |||
| 573 | void set_timbre(float timbre) { note_timbre = timbre; } | ||
| 574 | |||
| 575 | // Tempo functions | ||
| 576 | |||
| 577 | void set_tempo(uint8_t tempo) { note_tempo = tempo; } | ||
| 578 | |||
| 579 | void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } | ||
| 580 | |||
| 581 | void increase_tempo(uint8_t tempo_change) { | ||
| 582 | if (note_tempo - tempo_change < 10) { | ||
| 583 | note_tempo = 10; | ||
| 584 | } else { | ||
| 585 | note_tempo -= tempo_change; | ||
| 586 | } | ||
| 587 | } | ||
| 588 | |||
| 589 | //------------------------------------------------------------------------------ | ||
| 590 | // Override these functions in your keymap file to play different tunes on | ||
| 591 | // startup and bootloader jump | ||
| 592 | __attribute__((weak)) void play_startup_tone() {} | ||
| 593 | |||
| 594 | __attribute__((weak)) void play_goodbye_tone() {} | ||
| 595 | //------------------------------------------------------------------------------ | ||
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..b8cec5ff1 --- /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 8ac6aafd3..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,11 +14,12 @@ | |||
| 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 | 19 | #ifndef TEMPO_DEFAULT |
| 20 | #define TEMPO_DEFAULT 100 | 20 | # define TEMPO_DEFAULT 120 |
| 21 | // in beats-per-minute | ||
| 22 | #endif | ||
| 21 | 23 | ||
| 22 | #define SONG(notes...) \ | 24 | #define SONG(notes...) \ |
| 23 | { notes } | 25 | { notes } |
| @@ -25,12 +27,14 @@ | |||
| 25 | // Note Types | 27 | // Note Types |
| 26 | #define MUSICAL_NOTE(note, duration) \ | 28 | #define MUSICAL_NOTE(note, duration) \ |
| 27 | { (NOTE##note), duration } | 29 | { (NOTE##note), duration } |
| 30 | |||
| 28 | #define BREVE_NOTE(note) MUSICAL_NOTE(note, 128) | 31 | #define BREVE_NOTE(note) MUSICAL_NOTE(note, 128) |
| 29 | #define WHOLE_NOTE(note) MUSICAL_NOTE(note, 64) | 32 | #define WHOLE_NOTE(note) MUSICAL_NOTE(note, 64) |
| 30 | #define HALF_NOTE(note) MUSICAL_NOTE(note, 32) | 33 | #define HALF_NOTE(note) MUSICAL_NOTE(note, 32) |
| 31 | #define QUARTER_NOTE(note) MUSICAL_NOTE(note, 16) | 34 | #define QUARTER_NOTE(note) MUSICAL_NOTE(note, 16) |
| 32 | #define EIGHTH_NOTE(note) MUSICAL_NOTE(note, 8) | 35 | #define EIGHTH_NOTE(note) MUSICAL_NOTE(note, 8) |
| 33 | #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) | ||
| 34 | 38 | ||
| 35 | #define BREVE_DOT_NOTE(note) MUSICAL_NOTE(note, 128 + 64) | 39 | #define BREVE_DOT_NOTE(note) MUSICAL_NOTE(note, 128 + 64) |
| 36 | #define WHOLE_DOT_NOTE(note) MUSICAL_NOTE(note, 64 + 32) | 40 | #define WHOLE_DOT_NOTE(note) MUSICAL_NOTE(note, 64 + 32) |
| @@ -38,6 +42,9 @@ | |||
| 38 | #define QUARTER_DOT_NOTE(note) MUSICAL_NOTE(note, 16 + 8) | 42 | #define QUARTER_DOT_NOTE(note) MUSICAL_NOTE(note, 16 + 8) |
| 39 | #define EIGHTH_DOT_NOTE(note) MUSICAL_NOTE(note, 8 + 4) | 43 | #define EIGHTH_DOT_NOTE(note) MUSICAL_NOTE(note, 8 + 4) |
| 40 | #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 | ||
| 41 | 48 | ||
| 42 | // Note Type Shortcuts | 49 | // Note Type Shortcuts |
| 43 | #define M__NOTE(note, duration) MUSICAL_NOTE(note, duration) | 50 | #define M__NOTE(note, duration) MUSICAL_NOTE(note, duration) |
| @@ -47,55 +54,52 @@ | |||
| 47 | #define Q__NOTE(n) QUARTER_NOTE(n) | 54 | #define Q__NOTE(n) QUARTER_NOTE(n) |
| 48 | #define E__NOTE(n) EIGHTH_NOTE(n) | 55 | #define E__NOTE(n) EIGHTH_NOTE(n) |
| 49 | #define S__NOTE(n) SIXTEENTH_NOTE(n) | 56 | #define S__NOTE(n) SIXTEENTH_NOTE(n) |
| 57 | #define T__NOTE(n) THIRTYSECOND_NOTE(n) | ||
| 50 | #define BD_NOTE(n) BREVE_DOT_NOTE(n) | 58 | #define BD_NOTE(n) BREVE_DOT_NOTE(n) |
| 51 | #define WD_NOTE(n) WHOLE_DOT_NOTE(n) | 59 | #define WD_NOTE(n) WHOLE_DOT_NOTE(n) |
| 52 | #define HD_NOTE(n) HALF_DOT_NOTE(n) | 60 | #define HD_NOTE(n) HALF_DOT_NOTE(n) |
| 53 | #define QD_NOTE(n) QUARTER_DOT_NOTE(n) | 61 | #define QD_NOTE(n) QUARTER_DOT_NOTE(n) |
| 54 | #define ED_NOTE(n) EIGHTH_DOT_NOTE(n) | 62 | #define ED_NOTE(n) EIGHTH_DOT_NOTE(n) |
| 55 | #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) | ||
| 56 | 65 | ||
| 57 | // Note Timbre | 66 | // Note Timbre |
| 58 | // Changes how the notes sound | 67 | // Changes how the notes sound |
| 59 | #define TIMBRE_12 0.125f | 68 | #define TIMBRE_12 12 |
| 60 | #define TIMBRE_25 0.250f | 69 | #define TIMBRE_25 25 |
| 61 | #define TIMBRE_50 0.500f | 70 | #define TIMBRE_50 50 |
| 62 | #define TIMBRE_75 0.750f | 71 | #define TIMBRE_75 75 |
| 63 | #define TIMBRE_DEFAULT TIMBRE_50 | 72 | #ifndef TIMBRE_DEFAULT |
| 73 | # define TIMBRE_DEFAULT TIMBRE_50 | ||
| 74 | #endif | ||
| 64 | 75 | ||
| 65 | // Notes - # = Octave | 76 | // Notes - # = Octave |
| 66 | 77 | ||
| 67 | #ifdef __arm__ | 78 | #define NOTE_REST 0.00f |
| 68 | # define NOTE_REST 1.00f | ||
| 69 | #else | ||
| 70 | # define NOTE_REST 0.00f | ||
| 71 | #endif | ||
| 72 | |||
| 73 | /* These notes are currently bugged | ||
| 74 | #define NOTE_C0 16.35f | ||
| 75 | #define NOTE_CS0 17.32f | ||
| 76 | #define NOTE_D0 18.35f | ||
| 77 | #define NOTE_DS0 19.45f | ||
| 78 | #define NOTE_E0 20.60f | ||
| 79 | #define NOTE_F0 21.83f | ||
| 80 | #define NOTE_FS0 23.12f | ||
| 81 | #define NOTE_G0 24.50f | ||
| 82 | #define NOTE_GS0 25.96f | ||
| 83 | #define NOTE_A0 27.50f | ||
| 84 | #define NOTE_AS0 29.14f | ||
| 85 | #define NOTE_B0 30.87f | ||
| 86 | #define NOTE_C1 32.70f | ||
| 87 | #define NOTE_CS1 34.65f | ||
| 88 | #define NOTE_D1 36.71f | ||
| 89 | #define NOTE_DS1 38.89f | ||
| 90 | #define NOTE_E1 41.20f | ||
| 91 | #define NOTE_F1 43.65f | ||
| 92 | #define NOTE_FS1 46.25f | ||
| 93 | #define NOTE_G1 49.00f | ||
| 94 | #define NOTE_GS1 51.91f | ||
| 95 | #define NOTE_A1 55.00f | ||
| 96 | #define NOTE_AS1 58.27f | ||
| 97 | */ | ||
| 98 | 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 | ||
| 99 | #define NOTE_B1 61.74f | 103 | #define NOTE_B1 61.74f |
| 100 | #define NOTE_C2 65.41f | 104 | #define NOTE_C2 65.41f |
| 101 | #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 4d66da80b..e47192de3 100644 --- a/quantum/backlight/backlight_avr.c +++ b/quantum/backlight/backlight_avr.c | |||
| @@ -68,7 +68,7 @@ | |||
| 68 | # define COMxx1 COM3A1 | 68 | # define COMxx1 COM3A1 |
| 69 | # define OCRxx OCR3A | 69 | # define OCRxx OCR3A |
| 70 | # endif | 70 | # endif |
| 71 | #elif (defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__)) && (BACKLIGHT_PIN == B7 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6) | 71 | #elif (defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__)) && (BACKLIGHT_PIN == B7 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6) |
| 72 | # define HARDWARE_PWM | 72 | # define HARDWARE_PWM |
| 73 | # define ICRx ICR1 | 73 | # define ICRx ICR1 |
| 74 | # define TCCRxA TCCR1A | 74 | # define TCCRxA TCCR1A |
| @@ -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 |
diff --git a/quantum/command.c b/quantum/command.c index 59aa4e4d3..34c4b36b1 100644 --- a/quantum/command.c +++ b/quantum/command.c | |||
| @@ -550,22 +550,22 @@ static void mousekey_param_print(void) { | |||
| 550 | # if !defined(NO_PRINT) && !defined(USER_PRINT) | 550 | # if !defined(NO_PRINT) && !defined(USER_PRINT) |
| 551 | print("\n\t- Values -\n"); | 551 | print("\n\t- Values -\n"); |
| 552 | print("1: delay(*10ms): "); | 552 | print("1: delay(*10ms): "); |
| 553 | pdec(mk_delay); | 553 | print_dec(mk_delay); |
| 554 | print("\n"); | 554 | print("\n"); |
| 555 | print("2: interval(ms): "); | 555 | print("2: interval(ms): "); |
| 556 | pdec(mk_interval); | 556 | print_dec(mk_interval); |
| 557 | print("\n"); | 557 | print("\n"); |
| 558 | print("3: max_speed: "); | 558 | print("3: max_speed: "); |
| 559 | pdec(mk_max_speed); | 559 | print_dec(mk_max_speed); |
| 560 | print("\n"); | 560 | print("\n"); |
| 561 | print("4: time_to_max: "); | 561 | print("4: time_to_max: "); |
| 562 | pdec(mk_time_to_max); | 562 | print_dec(mk_time_to_max); |
| 563 | print("\n"); | 563 | print("\n"); |
| 564 | print("5: wheel_max_speed: "); | 564 | print("5: wheel_max_speed: "); |
| 565 | pdec(mk_wheel_max_speed); | 565 | print_dec(mk_wheel_max_speed); |
| 566 | print("\n"); | 566 | print("\n"); |
| 567 | print("6: wheel_time_to_max: "); | 567 | print("6: wheel_time_to_max: "); |
| 568 | pdec(mk_wheel_time_to_max); | 568 | print_dec(mk_wheel_time_to_max); |
| 569 | print("\n"); | 569 | print("\n"); |
| 570 | # endif /* !NO_PRINT */ | 570 | # endif /* !NO_PRINT */ |
| 571 | } | 571 | } |
diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c index 6c0e3bb07..60513f98e 100644 --- a/quantum/debounce/sym_defer_pk.c +++ b/quantum/debounce/sym_defer_pk.c | |||
| @@ -23,6 +23,12 @@ When no state changes have occured for DEBOUNCE milliseconds, we push the state. | |||
| 23 | #include "quantum.h" | 23 | #include "quantum.h" |
| 24 | #include <stdlib.h> | 24 | #include <stdlib.h> |
| 25 | 25 | ||
| 26 | #ifdef PROTOCOL_CHIBIOS | ||
| 27 | # if CH_CFG_USE_MEMCORE == FALSE | ||
| 28 | # error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm. | ||
| 29 | # endif | ||
| 30 | #endif | ||
| 31 | |||
| 26 | #ifndef DEBOUNCE | 32 | #ifndef DEBOUNCE |
| 27 | # define DEBOUNCE 5 | 33 | # define DEBOUNCE 5 |
| 28 | #endif | 34 | #endif |
diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c index 93a40ad44..e66cf92d7 100644 --- a/quantum/debounce/sym_eager_pk.c +++ b/quantum/debounce/sym_eager_pk.c | |||
| @@ -23,6 +23,12 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred. | |||
| 23 | #include "quantum.h" | 23 | #include "quantum.h" |
| 24 | #include <stdlib.h> | 24 | #include <stdlib.h> |
| 25 | 25 | ||
| 26 | #ifdef PROTOCOL_CHIBIOS | ||
| 27 | # if CH_CFG_USE_MEMCORE == FALSE | ||
| 28 | # error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm. | ||
| 29 | # endif | ||
| 30 | #endif | ||
| 31 | |||
| 26 | #ifndef DEBOUNCE | 32 | #ifndef DEBOUNCE |
| 27 | # define DEBOUNCE 5 | 33 | # define DEBOUNCE 5 |
| 28 | #endif | 34 | #endif |
diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c index d12931fdd..20ccb46f1 100644 --- a/quantum/debounce/sym_eager_pr.c +++ b/quantum/debounce/sym_eager_pr.c | |||
| @@ -23,6 +23,12 @@ No further inputs are accepted until DEBOUNCE milliseconds have occurred. | |||
| 23 | #include "quantum.h" | 23 | #include "quantum.h" |
| 24 | #include <stdlib.h> | 24 | #include <stdlib.h> |
| 25 | 25 | ||
| 26 | #ifdef PROTOCOL_CHIBIOS | ||
| 27 | # if CH_CFG_USE_MEMCORE == FALSE | ||
| 28 | # error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm. | ||
| 29 | # endif | ||
| 30 | #endif | ||
| 31 | |||
| 26 | #ifndef DEBOUNCE | 32 | #ifndef DEBOUNCE |
| 27 | # define DEBOUNCE 5 | 33 | # define DEBOUNCE 5 |
| 28 | #endif | 34 | #endif |
diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c index 0608b469c..a860b9497 100644 --- a/quantum/dynamic_keymap.c +++ b/quantum/dynamic_keymap.c | |||
| @@ -37,6 +37,8 @@ | |||
| 37 | #ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR | 37 | #ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR |
| 38 | # if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) | 38 | # if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) |
| 39 | # define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 2047 | 39 | # define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 2047 |
| 40 | # elif defined(__AVR_AT90USB162__) | ||
| 41 | # define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 511 | ||
| 40 | # else | 42 | # else |
| 41 | # define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 1023 | 43 | # define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 1023 |
| 42 | # endif | 44 | # endif |
diff --git a/quantum/encoder.c b/quantum/encoder.c index 7ca31afed..2ed64c1e3 100644 --- a/quantum/encoder.c +++ b/quantum/encoder.c | |||
| @@ -94,8 +94,9 @@ void encoder_init(void) { | |||
| 94 | #endif | 94 | #endif |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | static void encoder_update(int8_t index, uint8_t state) { | 97 | static bool encoder_update(int8_t index, uint8_t state) { |
| 98 | uint8_t i = index; | 98 | bool changed = false; |
| 99 | uint8_t i = index; | ||
| 99 | 100 | ||
| 100 | #ifdef ENCODER_RESOLUTIONS | 101 | #ifdef ENCODER_RESOLUTIONS |
| 101 | int8_t resolution = encoder_resolutions[i]; | 102 | int8_t resolution = encoder_resolutions[i]; |
| @@ -109,40 +110,53 @@ static void encoder_update(int8_t index, uint8_t state) { | |||
| 109 | encoder_pulses[i] += encoder_LUT[state & 0xF]; | 110 | encoder_pulses[i] += encoder_LUT[state & 0xF]; |
| 110 | if (encoder_pulses[i] >= resolution) { | 111 | if (encoder_pulses[i] >= resolution) { |
| 111 | encoder_value[index]++; | 112 | encoder_value[index]++; |
| 113 | changed = true; | ||
| 112 | encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); | 114 | encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); |
| 113 | } | 115 | } |
| 114 | if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise | 116 | if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise |
| 115 | encoder_value[index]--; | 117 | encoder_value[index]--; |
| 118 | changed = true; | ||
| 116 | encoder_update_kb(index, ENCODER_CLOCKWISE); | 119 | encoder_update_kb(index, ENCODER_CLOCKWISE); |
| 117 | } | 120 | } |
| 118 | encoder_pulses[i] %= resolution; | 121 | encoder_pulses[i] %= resolution; |
| 122 | return changed; | ||
| 119 | } | 123 | } |
| 120 | 124 | ||
| 121 | void encoder_read(void) { | 125 | bool encoder_read(void) { |
| 126 | bool changed = false; | ||
| 122 | for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { | 127 | for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { |
| 123 | encoder_state[i] <<= 2; | 128 | encoder_state[i] <<= 2; |
| 124 | encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1); | 129 | encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1); |
| 125 | encoder_update(i, encoder_state[i]); | 130 | changed |= encoder_update(i, encoder_state[i]); |
| 126 | } | 131 | } |
| 132 | return changed; | ||
| 127 | } | 133 | } |
| 128 | 134 | ||
| 129 | #ifdef SPLIT_KEYBOARD | 135 | #ifdef SPLIT_KEYBOARD |
| 136 | void last_encoder_activity_trigger(void); | ||
| 137 | |||
| 130 | void encoder_state_raw(uint8_t* slave_state) { memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * NUMBER_OF_ENCODERS); } | 138 | void encoder_state_raw(uint8_t* slave_state) { memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * NUMBER_OF_ENCODERS); } |
| 131 | 139 | ||
| 132 | void encoder_update_raw(uint8_t* slave_state) { | 140 | void encoder_update_raw(uint8_t* slave_state) { |
| 141 | bool changed = false; | ||
| 133 | for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { | 142 | for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { |
| 134 | uint8_t index = i + thatHand; | 143 | uint8_t index = i + thatHand; |
| 135 | int8_t delta = slave_state[i] - encoder_value[index]; | 144 | int8_t delta = slave_state[i] - encoder_value[index]; |
| 136 | while (delta > 0) { | 145 | while (delta > 0) { |
| 137 | delta--; | 146 | delta--; |
| 138 | encoder_value[index]++; | 147 | encoder_value[index]++; |
| 148 | changed = true; | ||
| 139 | encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); | 149 | encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); |
| 140 | } | 150 | } |
| 141 | while (delta < 0) { | 151 | while (delta < 0) { |
| 142 | delta++; | 152 | delta++; |
| 143 | encoder_value[index]--; | 153 | encoder_value[index]--; |
| 154 | changed = true; | ||
| 144 | encoder_update_kb(index, ENCODER_CLOCKWISE); | 155 | encoder_update_kb(index, ENCODER_CLOCKWISE); |
| 145 | } | 156 | } |
| 146 | } | 157 | } |
| 158 | |||
| 159 | // Update the last encoder input time -- handled external to encoder_read() when we're running a split | ||
| 160 | if (changed) last_encoder_activity_trigger(); | ||
| 147 | } | 161 | } |
| 148 | #endif | 162 | #endif |
diff --git a/quantum/encoder.h b/quantum/encoder.h index ec09a8cc4..db6f220da 100644 --- a/quantum/encoder.h +++ b/quantum/encoder.h | |||
| @@ -20,7 +20,7 @@ | |||
| 20 | #include "quantum.h" | 20 | #include "quantum.h" |
| 21 | 21 | ||
| 22 | void encoder_init(void); | 22 | void encoder_init(void); |
| 23 | void encoder_read(void); | 23 | bool encoder_read(void); |
| 24 | 24 | ||
| 25 | void encoder_update_kb(int8_t index, bool clockwise); | 25 | void encoder_update_kb(int8_t index, bool clockwise); |
| 26 | void encoder_update_user(int8_t index, bool clockwise); | 26 | void encoder_update_user(int8_t index, bool clockwise); |
diff --git a/quantum/fauxclicky.c b/quantum/fauxclicky.c deleted file mode 100644 index 53499c9c1..000000000 --- a/quantum/fauxclicky.c +++ /dev/null | |||
| @@ -1,59 +0,0 @@ | |||
| 1 | /* | ||
| 2 | Copyright 2017 Priyadi Iman Nurcahyo | ||
| 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 | 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 | You should have received a copy of the GNU General Public License | ||
| 13 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <avr/interrupt.h> | ||
| 17 | #include <avr/io.h> | ||
| 18 | #include "timer.h" | ||
| 19 | #include "fauxclicky.h" | ||
| 20 | #include <stdbool.h> | ||
| 21 | #include "musical_notes.h" | ||
| 22 | |||
| 23 | bool fauxclicky_enabled = true; | ||
| 24 | uint16_t note_start = 0; | ||
| 25 | bool note_playing = false; | ||
| 26 | uint16_t note_period = 0; | ||
| 27 | |||
| 28 | void fauxclicky_init() { | ||
| 29 | // Set port PC6 (OC3A and /OC4A) as output | ||
| 30 | DDRC |= _BV(PORTC6); | ||
| 31 | |||
| 32 | // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers | ||
| 33 | TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); | ||
| 34 | TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); | ||
| 35 | } | ||
| 36 | |||
| 37 | void fauxclicky_stop() { | ||
| 38 | FAUXCLICKY_DISABLE_OUTPUT; | ||
| 39 | note_playing = false; | ||
| 40 | } | ||
| 41 | |||
| 42 | void fauxclicky_play(float note[]) { | ||
| 43 | if (!fauxclicky_enabled) return; | ||
| 44 | if (note_playing) fauxclicky_stop(); | ||
| 45 | FAUXCLICKY_TIMER_PERIOD = (uint16_t)(((float)F_CPU) / (note[0] * (float)FAUXCLICKY_CPU_PRESCALER)); | ||
| 46 | FAUXCLICKY_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (note[0] * (float)FAUXCLICKY_CPU_PRESCALER)) / (float)2); | ||
| 47 | note_playing = true; | ||
| 48 | note_period = (note[1] / (float)16) * ((float)60 / (float)FAUXCLICKY_TEMPO) * 1000; | ||
| 49 | note_start = timer_read(); | ||
| 50 | FAUXCLICKY_ENABLE_OUTPUT; | ||
| 51 | } | ||
| 52 | |||
| 53 | void fauxclicky_check() { | ||
| 54 | if (!note_playing) return; | ||
| 55 | |||
| 56 | if (timer_elapsed(note_start) > note_period) { | ||
| 57 | fauxclicky_stop(); | ||
| 58 | } | ||
| 59 | } | ||
diff --git a/quantum/fauxclicky.h b/quantum/fauxclicky.h deleted file mode 100644 index ed54d0edc..000000000 --- a/quantum/fauxclicky.h +++ /dev/null | |||
| @@ -1,97 +0,0 @@ | |||
| 1 | /* | ||
| 2 | Copyright 2017 Priyadi Iman Nurcahyo | ||
| 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 | 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 | You should have received a copy of the GNU General Public License | ||
| 13 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifdef AUDIO_ENABLE | ||
| 17 | # error "AUDIO_ENABLE and FAUXCLICKY_ENABLE cannot be both enabled" | ||
| 18 | #endif | ||
| 19 | |||
| 20 | #include "musical_notes.h" | ||
| 21 | #include <stdbool.h> | ||
| 22 | |||
| 23 | __attribute__((weak)) float fauxclicky_pressed_note[2] = MUSICAL_NOTE(_D4, 0.25); | ||
| 24 | __attribute__((weak)) float fauxclicky_released_note[2] = MUSICAL_NOTE(_C4, 0.125); | ||
| 25 | __attribute__((weak)) float fauxclicky_beep_note[2] = MUSICAL_NOTE(_C4, 0.25); | ||
| 26 | |||
| 27 | extern bool fauxclicky_enabled; | ||
| 28 | |||
| 29 | // | ||
| 30 | // tempo in BPM | ||
| 31 | // | ||
| 32 | |||
| 33 | #ifndef FAUXCLICKY_TEMPO | ||
| 34 | # define FAUXCLICKY_TEMPO TEMPO_DEFAULT | ||
| 35 | #endif | ||
| 36 | |||
| 37 | // beep on press | ||
| 38 | #define FAUXCLICKY_ACTION_PRESS fauxclicky_play(fauxclicky_pressed_note) | ||
| 39 | |||
| 40 | // beep on release | ||
| 41 | #define FAUXCLICKY_ACTION_RELEASE fauxclicky_play(fauxclicky_released_note) | ||
| 42 | |||
| 43 | // general purpose beep | ||
| 44 | #define FAUXCLICKY_BEEP fauxclicky_play(fauxclicky_beep_note) | ||
| 45 | |||
| 46 | // enable | ||
| 47 | #define FAUXCLICKY_ON fauxclicky_enabled = true | ||
| 48 | |||
| 49 | // disable | ||
| 50 | #define FAUXCLICKY_OFF \ | ||
| 51 | do { \ | ||
| 52 | fauxclicky_enabled = false; \ | ||
| 53 | fauxclicky_stop(); \ | ||
| 54 | } while (0) | ||
| 55 | |||
| 56 | // toggle | ||
| 57 | #define FAUXCLICKY_TOGGLE \ | ||
| 58 | do { \ | ||
| 59 | if (fauxclicky_enabled) { \ | ||
| 60 | FAUXCLICKY_OFF; \ | ||
| 61 | } else { \ | ||
| 62 | FAUXCLICKY_ON; \ | ||
| 63 | } \ | ||
| 64 | } while (0) | ||
| 65 | |||
| 66 | // | ||
| 67 | // pin configuration | ||
| 68 | // | ||
| 69 | |||
| 70 | #ifndef FAUXCLICKY_CPU_PRESCALER | ||
| 71 | # define FAUXCLICKY_CPU_PRESCALER 8 | ||
| 72 | #endif | ||
| 73 | |||
| 74 | #ifndef FAUXCLICKY_ENABLE_OUTPUT | ||
| 75 | # define FAUXCLICKY_ENABLE_OUTPUT TCCR3A |= _BV(COM3A1) | ||
| 76 | #endif | ||
| 77 | |||
| 78 | #ifndef FAUXCLICKY_DISABLE_OUTPUT | ||
| 79 | # define FAUXCLICKY_DISABLE_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)) | ||
| 80 | #endif | ||
| 81 | |||
| 82 | #ifndef FAUXCLICKY_TIMER_PERIOD | ||
| 83 | # define FAUXCLICKY_TIMER_PERIOD ICR3 | ||
| 84 | #endif | ||
| 85 | |||
| 86 | #ifndef FAUXCLICKY_DUTY_CYCLE | ||
| 87 | # define FAUXCLICKY_DUTY_CYCLE OCR3A | ||
| 88 | #endif | ||
| 89 | |||
| 90 | // | ||
| 91 | // definitions | ||
| 92 | // | ||
| 93 | |||
| 94 | void fauxclicky_init(void); | ||
| 95 | void fauxclicky_stop(void); | ||
| 96 | void fauxclicky_play(float note[2]); | ||
| 97 | void fauxclicky_check(void); | ||
diff --git a/quantum/keymap_extras/keymap_us_extended.h b/quantum/keymap_extras/keymap_us_extended.h new file mode 100644 index 000000000..b2b3a734c --- /dev/null +++ b/quantum/keymap_extras/keymap_us_extended.h | |||
| @@ -0,0 +1,227 @@ | |||
| 1 | /* Copyright 2020 | ||
| 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 | #pragma once | ||
| 18 | |||
| 19 | #include "keymap.h" | ||
| 20 | |||
| 21 | // clang-format off | ||
| 22 | |||
| 23 | /* | ||
| 24 | * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ | ||
| 25 | * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ │ | ||
| 26 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | ||
| 27 | * │ │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ \ │ | ||
| 28 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | ||
| 29 | * │ │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ │ | ||
| 30 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | ||
| 31 | * │ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ │ | ||
| 32 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | ||
| 33 | * │ │ │ │ │ │ │ │ │ | ||
| 34 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | ||
| 35 | */ | ||
| 36 | // Row 1 | ||
| 37 | #define US_GRV KC_GRV // ` | ||
| 38 | #define US_1 KC_1 // 1 | ||
| 39 | #define US_2 KC_2 // 2 | ||
| 40 | #define US_3 KC_3 // 3 | ||
| 41 | #define US_4 KC_4 // 4 | ||
| 42 | #define US_5 KC_5 // 5 | ||
| 43 | #define US_6 KC_6 // 6 | ||
| 44 | #define US_7 KC_7 // 7 | ||
| 45 | #define US_8 KC_8 // 8 | ||
| 46 | #define US_9 KC_9 // 9 | ||
| 47 | #define US_0 KC_0 // 0 | ||
| 48 | #define US_MINS KC_MINS // - | ||
| 49 | #define US_EQL KC_EQL // = | ||
| 50 | // Row 2 | ||
| 51 | #define US_Q KC_Q // Q | ||
| 52 | #define US_W KC_W // W | ||
| 53 | #define US_E KC_E // E | ||
| 54 | #define US_R KC_R // R | ||
| 55 | #define US_T KC_T // T | ||
| 56 | #define US_Y KC_Y // Y | ||
| 57 | #define US_U KC_U // U | ||
| 58 | #define US_I KC_I // I | ||
| 59 | #define US_O KC_O // O | ||
| 60 | #define US_P KC_P // P | ||
| 61 | #define US_LBRC KC_LBRC // [ | ||
| 62 | #define US_RBRC KC_RBRC // ] | ||
| 63 | #define US_BSLS KC_BSLS // (backslash) | ||
| 64 | // Row 3 | ||
| 65 | #define US_A KC_A // A | ||
| 66 | #define US_S KC_S // S | ||
| 67 | #define US_D KC_D // D | ||
| 68 | #define US_F KC_F // F | ||
| 69 | #define US_G KC_G // G | ||
| 70 | #define US_H KC_H // H | ||
| 71 | #define US_J KC_J // J | ||
| 72 | #define US_K KC_K // K | ||
| 73 | #define US_L KC_L // L | ||
| 74 | #define US_SCLN KC_SCLN // ; | ||
| 75 | #define US_QUOT KC_QUOT // ' | ||
| 76 | // Row 4 | ||
| 77 | #define US_Z KC_Z // Z | ||
| 78 | #define US_X KC_X // X | ||
| 79 | #define US_C KC_C // C | ||
| 80 | #define US_V KC_V // V | ||
| 81 | #define US_B KC_B // B | ||
| 82 | #define US_N KC_N // N | ||
| 83 | #define US_M KC_M // M | ||
| 84 | #define US_COMM KC_COMM // , | ||
| 85 | #define US_DOT KC_DOT // . | ||
| 86 | #define US_SLSH KC_SLSH // / | ||
| 87 | |||
| 88 | /* Shifted symbols | ||
| 89 | * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ | ||
| 90 | * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │ │ | ||
| 91 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | ||
| 92 | * │ │ │ │ │ │ │ │ │ │ │ │ { │ } │ | │ | ||
| 93 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | ||
| 94 | * │ │ │ │ │ │ │ │ │ │ │ : │ " │ │ | ||
| 95 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | ||
| 96 | * │ │ │ │ │ │ │ │ │ < │ > │ ? │ │ | ||
| 97 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | ||
| 98 | * │ │ │ │ │ │ │ │ │ | ||
| 99 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | ||
| 100 | */ | ||
| 101 | // Row 1 | ||
| 102 | #define US_TILD S(US_GRV) // ~ | ||
| 103 | #define US_EXLM S(US_1) // ! | ||
| 104 | #define US_AT S(US_2) // @ | ||
| 105 | #define US_HASH S(US_3) // # | ||
| 106 | #define US_DLR S(US_4) // $ | ||
| 107 | #define US_PERC S(US_5) // % | ||
| 108 | #define US_CIRC S(US_6) // ^ | ||
| 109 | #define US_AMPR S(US_7) // & | ||
| 110 | #define US_ASTR S(US_8) // * | ||
| 111 | #define US_LPRN S(US_9) // ( | ||
| 112 | #define US_RPRN S(US_0) // ) | ||
| 113 | #define US_UNDS S(US_MINS) // _ | ||
| 114 | #define US_PLUS S(US_EQL) // + | ||
| 115 | // Row 2 | ||
| 116 | #define US_LCBR S(US_LBRC) // { | ||
| 117 | #define US_RCBR S(US_RBRC) // } | ||
| 118 | #define US_PIPE S(US_BSLS) // | | ||
| 119 | // Row 3 | ||
| 120 | #define US_COLN S(US_SCLN) // : | ||
| 121 | #define US_DQUO S(US_QUOT) // " | ||
| 122 | // Row 4 | ||
| 123 | #define US_LABK S(US_COMM) // < | ||
| 124 | #define US_RABK S(US_DOT) // > | ||
| 125 | #define US_QUES S(US_SLSH) // ? | ||
| 126 | |||
| 127 | /* AltGr symbols | ||
| 128 | * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ | ||
| 129 | * │ ` │ ¹ │ ² │ ³ │ ¤ │ € │ ^ │ ̛ │ ¾ │ ‘ │ ’ │ ¥ │ × │ │ | ||
| 130 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | ||
| 131 | * │ │ Ä │ Å │ É │ ® │ Þ │ Ü │ Ú │ Í │ Ó │ Ö │ « │ » │ ¬ │ | ||
| 132 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | ||
| 133 | * │ │ Á │ ß │ Ð │ │ │ │ Ï │ Œ │ Ø │ ¶ │ ' │ │ | ||
| 134 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | ||
| 135 | * │ │ Æ │ │ © │ │ │ Ñ │ µ │ Ç │ ˙ │ ¿ │ │ | ||
| 136 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | ||
| 137 | * │ │ │ │ │ │ │ │ │ | ||
| 138 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | ||
| 139 | */ | ||
| 140 | // Row 1 | ||
| 141 | #define US_DGRV ALGR(US_GRV) // ` (dead) | ||
| 142 | #define US_SUP1 ALGR(US_1) // ¹ | ||
| 143 | #define US_SUP2 ALGR(US_2) // ² | ||
| 144 | #define US_SUP3 ALGR(US_3) // ³ | ||
| 145 | #define US_CURR ALGR(US_4) // ¤ | ||
| 146 | #define US_EURO ALGR(US_5) // € | ||
| 147 | #define US_DCIR ALGR(US_6) // ^ (dead) | ||
| 148 | #define US_HORN ALGR(US_7) // ̛̛ (dead) | ||
| 149 | #define US_OGON ALGR(US_8) // ˛ (dead) | ||
| 150 | #define US_LSQU ALGR(US_9) // ‘ | ||
| 151 | #define US_RSQU ALGR(US_0) // ’ | ||
| 152 | #define US_YEN ALGR(US_MINS) // ¥ | ||
| 153 | #define US_MUL ALGR(US_EQL) // × | ||
| 154 | // Row 2 | ||
| 155 | #define US_ADIA ALGR(US_Q) // Ä | ||
| 156 | #define US_ARNG ALGR(US_W) // Å | ||
| 157 | #define US_EACU ALGR(US_E) // É | ||
| 158 | #define US_EDIA ALGR(US_R) // Ë | ||
| 159 | #define US_THRN ALGR(US_T) // Þ | ||
| 160 | #define US_UDIA ALGR(US_Y) // Ü | ||
| 161 | #define US_UACU ALGR(US_U) // Ú | ||
| 162 | #define US_IACU ALGR(US_I) // Í | ||
| 163 | #define US_OACU ALGR(US_O) // Ó | ||
| 164 | #define US_ODIA ALGR(US_P) // Ö | ||
| 165 | #define US_LDAQ ALGR(US_LBRC) // « | ||
| 166 | #define US_RDAQ ALGR(US_RBRC) // » | ||
| 167 | #define US_NOT ALGR(US_BSLS) // ¬ | ||
| 168 | // Row 3 | ||
| 169 | #define US_AACU ALGR(US_A) // Á | ||
| 170 | #define US_SS ALGR(US_S) // ß | ||
| 171 | #define US_ETH ALGR(US_D) // Ð | ||
| 172 | #define US_IDIA ALGR(US_J) // Ï | ||
| 173 | #define US_OE ALGR(US_K) // Œ | ||
| 174 | #define US_OSTR ALGR(US_L) // Ø | ||
| 175 | #define US_PILC ALGR(US_SCLN) // ¶ | ||
| 176 | #define US_ACUT ALGR(US_QUOT) // ´ (dead) | ||
| 177 | // Row 4 | ||
| 178 | #define US_AE ALGR(US_Z) // Æ | ||
| 179 | #define US_OE_2 ALGR(US_X) // Œ | ||
| 180 | #define US_COPY ALGR(US_C) // © | ||
| 181 | #define US_REGD ALGR(US_V) // ® | ||
| 182 | #define US_NTIL ALGR(US_N) // Ñ | ||
| 183 | #define US_MICR ALGR(US_M) // µ | ||
| 184 | #define US_CCED ALGR(US_COMM) // Ç | ||
| 185 | #define US_DOTA ALGR(US_DOT) // ˙ (dead) | ||
| 186 | #define US_IQUE ALGR(US_SLSH) // ¿ | ||
| 187 | |||
| 188 | /* Shift+AltGr symbols | ||
| 189 | * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ | ||
| 190 | * │ ~ │ ¡ │ ˝ │ ¯ │ £ │ ¸ │ ¼ │ ½ │ ¾ │ ˘ │ ° │ ̣ │ ÷ │ │ | ||
| 191 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | ||
| 192 | * │ │ │ │ │ │ │ │ │ │ │ │ “ │ ” │ ¦ │ | ||
| 193 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | ||
| 194 | * │ │ │ § │ │ │ │ │ │ │ │ ° │ " │ │ | ||
| 195 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | ||
| 196 | * │ │ │ │ ¢ │ │ │ │ │ │ ˇ │ ̉ │ │ | ||
| 197 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | ||
| 198 | * │ │ │ │ │ │ │ │ │ | ||
| 199 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | ||
| 200 | */ | ||
| 201 | // Row 1 | ||
| 202 | #define US_DTIL S(ALGR(US_GRV)) // ~ (dead) | ||
| 203 | #define US_IEXL S(ALGR(US_1)) // ¡ | ||
| 204 | #define US_DACU S(ALGR(US_2)) // ˝ (dead) | ||
| 205 | #define US_MACR S(ALGR(US_3)) // ¯ (dead) | ||
| 206 | #define US_PND S(ALGR(US_4)) // £ | ||
| 207 | #define US_CEDL S(ALGR(US_5)) // ¸ (dead) | ||
| 208 | #define US_QRTR S(ALGR(US_6)) // ¼ | ||
| 209 | #define US_HALF S(ALGR(US_7)) // ½ | ||
| 210 | #define US_TQTR S(ALGR(US_8)) // ¾ | ||
| 211 | #define US_BREV S(ALGR(US_9)) // ˘ (dead) | ||
| 212 | #define US_RNGA S(ALGR(US_0)) // ° (dead) | ||
| 213 | #define US_DOTB S(ALGR(US_MINS)) // ̣ (dead) | ||
| 214 | #define US_DIV S(ALGR(US_EQL)) // ÷ | ||
| 215 | // Row 2 | ||
| 216 | #define US_LDQU S(ALGR(US_LBRC)) // “ | ||
| 217 | #define US_RDQU S(ALGR(US_LBRC)) // ” | ||
| 218 | #define US_BRKP S(ALGR(US_BSLS)) // ¦ | ||
| 219 | // Row 3 | ||
| 220 | #define US_SECT S(ALGR(US_S)) // § | ||
| 221 | #define US_DEG S(ALGR(US_SCLN)) // ° | ||
| 222 | #define US_DIAE S(ALGR(US_QUOT)) // ¨ (dead) | ||
| 223 | // Row 4 | ||
| 224 | #define US_CENT S(ALGR(US_C)) // ¢ | ||
| 225 | #define US_CARN S(ALGR(US_DOT)) // ˇ (dead) | ||
| 226 | #define US_HOKA S(ALGR(US_SLSH)) // ̉ (dead) | ||
| 227 | |||
diff --git a/quantum/keymap_extras/keymap_us_international.h b/quantum/keymap_extras/keymap_us_international.h index a3bc46597..49afcc4fb 100644 --- a/quantum/keymap_extras/keymap_us_international.h +++ b/quantum/keymap_extras/keymap_us_international.h | |||
| @@ -26,7 +26,7 @@ | |||
| 26 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | 26 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ |
| 27 | * │ │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ \ │ | 27 | * │ │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ \ │ |
| 28 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | 28 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ |
| 29 | * │ │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ │ | 29 | * │ │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ´ │ │ |
| 30 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | 30 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ |
| 31 | * │ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ │ | 31 | * │ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ │ |
| 32 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | 32 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ |
| @@ -34,7 +34,7 @@ | |||
| 34 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | 34 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ |
| 35 | */ | 35 | */ |
| 36 | // Row 1 | 36 | // Row 1 |
| 37 | #define US_GRV KC_GRV // ` (dead) | 37 | #define US_DGRV KC_GRV // ` (dead) |
| 38 | #define US_1 KC_1 // 1 | 38 | #define US_1 KC_1 // 1 |
| 39 | #define US_2 KC_2 // 2 | 39 | #define US_2 KC_2 // 2 |
| 40 | #define US_3 KC_3 // 3 | 40 | #define US_3 KC_3 // 3 |
| @@ -72,7 +72,7 @@ | |||
| 72 | #define US_K KC_K // K | 72 | #define US_K KC_K // K |
| 73 | #define US_L KC_L // L | 73 | #define US_L KC_L // L |
| 74 | #define US_SCLN KC_SCLN // ; | 74 | #define US_SCLN KC_SCLN // ; |
| 75 | #define US_QUOT KC_QUOT // ' (dead) | 75 | #define US_ACUT KC_QUOT // ´ (dead) |
| 76 | // Row 4 | 76 | // Row 4 |
| 77 | #define US_Z KC_Z // Z | 77 | #define US_Z KC_Z // Z |
| 78 | #define US_X KC_X // X | 78 | #define US_X KC_X // X |
| @@ -91,7 +91,7 @@ | |||
| 91 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | 91 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ |
| 92 | * │ │ │ │ │ │ │ │ │ │ │ │ { │ } │ | │ | 92 | * │ │ │ │ │ │ │ │ │ │ │ │ { │ } │ | │ |
| 93 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | 93 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ |
| 94 | * │ │ │ │ │ │ │ │ │ │ │ : │ " │ │ | 94 | * │ │ │ │ │ │ │ │ │ │ │ : │ ¨ │ │ |
| 95 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | 95 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ |
| 96 | * │ │ │ │ │ │ │ │ │ < │ > │ ? │ │ | 96 | * │ │ │ │ │ │ │ │ │ < │ > │ ? │ │ |
| 97 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | 97 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ |
| @@ -99,13 +99,13 @@ | |||
| 99 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | 99 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ |
| 100 | */ | 100 | */ |
| 101 | // Row 1 | 101 | // Row 1 |
| 102 | #define US_TILD S(US_GRV) // ~ (dead) | 102 | #define US_DTIL S(US_DGRV) // ~ (dead) |
| 103 | #define US_EXLM S(US_1) // ! | 103 | #define US_EXLM S(US_1) // ! |
| 104 | #define US_AT S(US_2) // " | 104 | #define US_AT S(US_2) // @ |
| 105 | #define US_HASH S(US_3) // # | 105 | #define US_HASH S(US_3) // # |
| 106 | #define US_DLR S(US_4) // $ | 106 | #define US_DLR S(US_4) // $ |
| 107 | #define US_PERC S(US_5) // % | 107 | #define US_PERC S(US_5) // % |
| 108 | #define US_CIRC S(US_6) // ^ | 108 | #define US_DCIR S(US_6) // ^ (dead) |
| 109 | #define US_AMPR S(US_7) // & | 109 | #define US_AMPR S(US_7) // & |
| 110 | #define US_ASTR S(US_8) // * | 110 | #define US_ASTR S(US_8) // * |
| 111 | #define US_LPRN S(US_9) // ( | 111 | #define US_LPRN S(US_9) // ( |
| @@ -118,7 +118,7 @@ | |||
| 118 | #define US_PIPE S(US_BSLS) // | | 118 | #define US_PIPE S(US_BSLS) // | |
| 119 | // Row 3 | 119 | // Row 3 |
| 120 | #define US_COLN S(US_SCLN) // : | 120 | #define US_COLN S(US_SCLN) // : |
| 121 | #define US_DQUO S(US_QUOT) // " (dead) | 121 | #define US_DIAE S(US_ACUT) // ¨ (dead) |
| 122 | // Row 4 | 122 | // Row 4 |
| 123 | #define US_LABK S(US_COMM) // < | 123 | #define US_LABK S(US_COMM) // < |
| 124 | #define US_RABK S(US_DOT) // > | 124 | #define US_RABK S(US_DOT) // > |
| @@ -170,7 +170,7 @@ | |||
| 170 | #define US_ETH ALGR(US_D) // Ð | 170 | #define US_ETH ALGR(US_D) // Ð |
| 171 | #define US_OSTR ALGR(US_L) // Ø | 171 | #define US_OSTR ALGR(US_L) // Ø |
| 172 | #define US_PILC ALGR(US_SCLN) // ¶ | 172 | #define US_PILC ALGR(US_SCLN) // ¶ |
| 173 | #define US_ACUT ALGR(US_QUOT) // ´ | 173 | #define US_NDAC ALGR(US_ACUT) // ´ |
| 174 | // Row 4 | 174 | // Row 4 |
| 175 | #define US_AE ALGR(US_Z) // Æ | 175 | #define US_AE ALGR(US_Z) // Æ |
| 176 | #define US_COPY ALGR(US_C) // © | 176 | #define US_COPY ALGR(US_C) // © |
| @@ -201,6 +201,6 @@ | |||
| 201 | // Row 3 | 201 | // Row 3 |
| 202 | #define US_SECT S(ALGR(US_S)) // § | 202 | #define US_SECT S(ALGR(US_S)) // § |
| 203 | #define US_DEG S(ALGR(US_SCLN)) // ° | 203 | #define US_DEG S(ALGR(US_SCLN)) // ° |
| 204 | #define US_DIAE S(ALGR(US_QUOT)) // ¨ | 204 | #define US_NDDR S(ALGR(US_ACUT)) // ¨ |
| 205 | // Row 4 | 205 | // Row 4 |
| 206 | #define US_CENT S(ALGR(US_C)) // ¢ | 206 | #define US_CENT S(ALGR(US_C)) // ¢ |
diff --git a/quantum/keymap_extras/keymap_us_international_linux.h b/quantum/keymap_extras/keymap_us_international_linux.h new file mode 100644 index 000000000..2c3e23039 --- /dev/null +++ b/quantum/keymap_extras/keymap_us_international_linux.h | |||
| @@ -0,0 +1,224 @@ | |||
| 1 | /* Copyright 2020 | ||
| 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 | #pragma once | ||
| 18 | |||
| 19 | #include "keymap.h" | ||
| 20 | |||
| 21 | // clang-format off | ||
| 22 | |||
| 23 | /* | ||
| 24 | * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ | ||
| 25 | * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ │ | ||
| 26 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | ||
| 27 | * │ │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ \ │ | ||
| 28 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | ||
| 29 | * │ │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ´ │ │ | ||
| 30 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | ||
| 31 | * │ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ │ | ||
| 32 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | ||
| 33 | * │ │ │ │ │ │ │ │ │ | ||
| 34 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | ||
| 35 | */ | ||
| 36 | // Row 1 | ||
| 37 | #define US_DGRV KC_GRV // ` (dead) | ||
| 38 | #define US_1 KC_1 // 1 | ||
| 39 | #define US_2 KC_2 // 2 | ||
| 40 | #define US_3 KC_3 // 3 | ||
| 41 | #define US_4 KC_4 // 4 | ||
| 42 | #define US_5 KC_5 // 5 | ||
| 43 | #define US_6 KC_6 // 6 | ||
| 44 | #define US_7 KC_7 // 7 | ||
| 45 | #define US_8 KC_8 // 8 | ||
| 46 | #define US_9 KC_9 // 9 | ||
| 47 | #define US_0 KC_0 // 0 | ||
| 48 | #define US_MINS KC_MINS // - | ||
| 49 | #define US_EQL KC_EQL // = | ||
| 50 | // Row 2 | ||
| 51 | #define US_Q KC_Q // Q | ||
| 52 | #define US_W KC_W // W | ||
| 53 | #define US_E KC_E // E | ||
| 54 | #define US_R KC_R // R | ||
| 55 | #define US_T KC_T // T | ||
| 56 | #define US_Y KC_Y // Y | ||
| 57 | #define US_U KC_U // U | ||
| 58 | #define US_I KC_I // I | ||
| 59 | #define US_O KC_O // O | ||
| 60 | #define US_P KC_P // P | ||
| 61 | #define US_LBRC KC_LBRC // [ | ||
| 62 | #define US_RBRC KC_RBRC // ] | ||
| 63 | #define US_BSLS KC_BSLS // (backslash) | ||
| 64 | // Row 3 | ||
| 65 | #define US_A KC_A // A | ||
| 66 | #define US_S KC_S // S | ||
| 67 | #define US_D KC_D // D | ||
| 68 | #define US_F KC_F // F | ||
| 69 | #define US_G KC_G // G | ||
| 70 | #define US_H KC_H // H | ||
| 71 | #define US_J KC_J // J | ||
| 72 | #define US_K KC_K // K | ||
| 73 | #define US_L KC_L // L | ||
| 74 | #define US_SCLN KC_SCLN // ; | ||
| 75 | #define US_ACUT KC_QUOT // ´ (dead) | ||
| 76 | // Row 4 | ||
| 77 | #define US_Z KC_Z // Z | ||
| 78 | #define US_X KC_X // X | ||
| 79 | #define US_C KC_C // C | ||
| 80 | #define US_V KC_V // V | ||
| 81 | #define US_B KC_B // B | ||
| 82 | #define US_N KC_N // N | ||
| 83 | #define US_M KC_M // M | ||
| 84 | #define US_COMM KC_COMM // , | ||
| 85 | #define US_DOT KC_DOT // . | ||
| 86 | #define US_SLSH KC_SLSH // / | ||
| 87 | |||
| 88 | /* Shifted symbols | ||
| 89 | * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ | ||
| 90 | * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │ │ | ||
| 91 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | ||
| 92 | * │ │ │ │ │ │ │ │ │ │ │ │ { │ } │ | │ | ||
| 93 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | ||
| 94 | * │ │ │ │ │ │ │ │ │ │ │ : │ ¨ │ │ | ||
| 95 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | ||
| 96 | * │ │ │ │ │ │ │ │ │ < │ > │ ? │ │ | ||
| 97 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | ||
| 98 | * │ │ │ │ │ │ │ │ │ | ||
| 99 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | ||
| 100 | */ | ||
| 101 | // Row 1 | ||
| 102 | #define US_DTIL S(US_DGRV) // ~ (dead) | ||
| 103 | #define US_EXLM S(US_1) // ! | ||
| 104 | #define US_AT S(US_2) // @ | ||
| 105 | #define US_HASH S(US_3) // # | ||
| 106 | #define US_DLR S(US_4) // $ | ||
| 107 | #define US_PERC S(US_5) // % | ||
| 108 | #define US_DCIR S(US_6) // ^ (dead) | ||
| 109 | #define US_AMPR S(US_7) // & | ||
| 110 | #define US_ASTR S(US_8) // * | ||
| 111 | #define US_LPRN S(US_9) // ( | ||
| 112 | #define US_RPRN S(US_0) // ) | ||
| 113 | #define US_UNDS S(US_MINS) // _ | ||
| 114 | #define US_PLUS S(US_EQL) // + | ||
| 115 | // Row 2 | ||
| 116 | #define US_LCBR S(US_LBRC) // { | ||
| 117 | #define US_RCBR S(US_RBRC) // } | ||
| 118 | #define US_PIPE S(US_BSLS) // | | ||
| 119 | // Row 3 | ||
| 120 | #define US_COLN S(US_SCLN) // : | ||
| 121 | #define US_DIAE S(US_ACUT) // ¨ (dead) | ||
| 122 | // Row 4 | ||
| 123 | #define US_LABK S(US_COMM) // < | ||
| 124 | #define US_RABK S(US_DOT) // > | ||
| 125 | #define US_QUES S(US_SLSH) // ? | ||
| 126 | |||
| 127 | /* AltGr symbols | ||
| 128 | * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ | ||
| 129 | * │ ` │ ¡ │ ² │ ³ │ ¤ │ € │ ¼ │ ½ │ ¾ │ ‘ │ ’ │ ¥ │ × │ │ | ||
| 130 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | ||
| 131 | * │ │ Ä │ Å │ É │ ® │ Þ │ Ü │ Ú │ Í │ Ó │ Ö │ « │ » │ ¬ │ | ||
| 132 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | ||
| 133 | * │ │ Á │ ß │ Ð │ │ │ │ │ Œ │ Ø │ ¶ │ ' │ │ | ||
| 134 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | ||
| 135 | * │ │ Æ │ │ © │ │ │ Ñ │ µ │ Ç │ ˙ │ ¿ │ │ | ||
| 136 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | ||
| 137 | * │ │ │ │ │ │ │ │ │ | ||
| 138 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | ||
| 139 | */ | ||
| 140 | |||
| 141 | // Row 1 | ||
| 142 | #define US_GRV ALGR(US_DGRV) // ` | ||
| 143 | #define US_IEXL ALGR(US_1) // ¡ | ||
| 144 | #define US_SUP2 ALGR(US_2) // ² | ||
| 145 | #define US_SUP3 ALGR(US_3) // ³ | ||
| 146 | #define US_CURR ALGR(US_4) // ¤ | ||
| 147 | #define US_EURO ALGR(US_5) // € | ||
| 148 | #define US_QRTR ALGR(US_6) // ¼ | ||
| 149 | #define US_HALF ALGR(US_7) // ½ | ||
| 150 | #define US_TQTR ALGR(US_8) // ¾ | ||
| 151 | #define US_LSQU ALGR(US_9) // ‘ | ||
| 152 | #define US_RSQU ALGR(US_0) // ’ | ||
| 153 | #define US_YEN ALGR(US_MINS) // ¥ | ||
| 154 | #define US_MUL ALGR(US_EQL) // × | ||
| 155 | // Row 2 | ||
| 156 | #define US_ADIA ALGR(US_Q) // Ä | ||
| 157 | #define US_ARNG ALGR(US_W) // Å | ||
| 158 | #define US_EACU ALGR(US_E) // É | ||
| 159 | #define US_REGD ALGR(US_R) // ® | ||
| 160 | #define US_THRN ALGR(US_T) // Þ | ||
| 161 | #define US_UDIA ALGR(US_Y) // Ü | ||
| 162 | #define US_UACU ALGR(US_U) // Ú | ||
| 163 | #define US_IACU ALGR(US_I) // Í | ||
| 164 | #define US_OACU ALGR(US_O) // Ó | ||
| 165 | #define US_ODIA ALGR(US_P) // Ö | ||
| 166 | #define US_LDAQ ALGR(US_LBRC) // « | ||
| 167 | #define US_RDAQ ALGR(US_RBRC) // » | ||
| 168 | #define US_NOT ALGR(US_BSLS) // ¬ | ||
| 169 | // Row 3 | ||
| 170 | #define US_AACU ALGR(US_A) // Á | ||
| 171 | #define US_SS ALGR(US_S) // ß | ||
| 172 | #define US_ETH ALGR(US_D) // Ð | ||
| 173 | #define US_OE ALGR(US_K) // Œ | ||
| 174 | #define US_OSTR ALGR(US_L) // Ø | ||
| 175 | #define US_PILC ALGR(US_SCLN) // ¶ | ||
| 176 | #define US_QUOT ALGR(US_ACUT) // ' | ||
| 177 | // Row 4 | ||
| 178 | #define US_AE ALGR(US_Z) // Æ | ||
| 179 | #define US_COPY ALGR(US_C) // © | ||
| 180 | #define US_NTIL ALGR(US_N) // Ñ | ||
| 181 | #define US_MICR ALGR(US_M) // µ | ||
| 182 | #define US_CCED ALGR(US_COMM) // Ç | ||
| 183 | #define US_DOTA ALGR(US_DOT) // ˙ (dead) | ||
| 184 | #define US_IQUE ALGR(US_SLSH) // ¿ | ||
| 185 | |||
| 186 | /* Shift+AltGr symbols | ||
| 187 | * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ | ||
| 188 | * │ ~ │ ¹ │ ˝ │ ¯ │ £ │ ¸ │ ^ │ ̛ │ ˛ │ ˘ │ ° │ ̣ │ ÷ │ │ | ||
| 189 | * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ | ||
| 190 | * │ │ │ │ │ │ │ │ │ │ │ │ “ │ ” │ ¦ │ | ||
| 191 | * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ | ||
| 192 | * │ │ │ § │ │ │ │ │ │ │ │ ° │ " │ │ | ||
| 193 | * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤ | ||
| 194 | * │ │ │ │ ¢ │ │ │ │ │ │ ˇ │ ̉ │ │ | ||
| 195 | * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ | ||
| 196 | * │ │ │ │ │ │ │ │ │ | ||
| 197 | * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ | ||
| 198 | */ | ||
| 199 | // Row 1 | ||
| 200 | #define US_TILD S(ALGR(US_DGRV)) // ~ | ||
| 201 | #define US_SUP1 S(ALGR(US_1)) // ¹ | ||
| 202 | #define US_DACU S(ALGR(US_2)) // ˝ (dead) | ||
| 203 | #define US_MACR S(ALGR(US_3)) // ¯ (dead) | ||
| 204 | #define US_PND S(ALGR(US_4)) // £ | ||
| 205 | #define US_CEDL S(ALGR(US_5)) // ¸ (dead) | ||
| 206 | #define US_CIRC S(ALGR(US_6)) // ^ | ||
| 207 | #define US_HORN S(ALGR(US_7)) // ̛ (dead) | ||
| 208 | #define US_OGON S(ALGR(US_8)) // ˛ (dead) | ||
| 209 | #define US_BREV S(ALGR(US_9)) // ˘ (dead) | ||
| 210 | #define US_RNGA S(ALGR(US_0)) // ° (dead) | ||
| 211 | #define US_DOTB S(ALGR(US_MINS)) // ̣ (dead) | ||
| 212 | #define US_DIV S(ALGR(US_EQL)) // ÷ | ||
| 213 | // Row 2 | ||
| 214 | #define US_LDQU S(ALGR(US_LBRC)) // “ | ||
| 215 | #define US_RDQU S(ALGR(US_LBRC)) // ” | ||
| 216 | #define US_BRKP S(ALGR(US_BSLS)) // ¦ | ||
| 217 | // Row 3 | ||
| 218 | #define US_SECT S(ALGR(US_S)) // § | ||
| 219 | #define US_DEG S(ALGR(US_SCLN)) // ° | ||
| 220 | #define US_DQUO S(ALGR(US_ACUT)) // " | ||
| 221 | // Row 4 | ||
| 222 | #define US_CENT S(ALGR(US_C)) // ¢ | ||
| 223 | #define US_CARN S(ALGR(US_DOT)) // ˇ (dead) | ||
| 224 | #define US_HOKA S(ALGR(US_SLSH)) // ̉ (dead) | ||
diff --git a/quantum/keymap_extras/sendstring_us_international.h b/quantum/keymap_extras/sendstring_us_international.h new file mode 100644 index 000000000..53a5891fb --- /dev/null +++ b/quantum/keymap_extras/sendstring_us_international.h | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | /* Copyright 2019 Rys Sommefeldt | ||
| 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 | // Sendstring lookup tables for UK layouts | ||
| 18 | |||
| 19 | #pragma once | ||
| 20 | |||
| 21 | #include "keymap_us_international.h" | ||
| 22 | #include "quantum.h" | ||
| 23 | |||
| 24 | // clang-format off | ||
| 25 | |||
| 26 | const uint8_t ascii_to_shift_lut[16] PROGMEM = { | ||
| 27 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 28 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 29 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 30 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 31 | |||
| 32 | KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0), | ||
| 33 | KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0), | ||
| 34 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 35 | KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1), | ||
| 36 | KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1), | ||
| 37 | KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1), | ||
| 38 | KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1), | ||
| 39 | KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1), | ||
| 40 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 41 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 42 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 43 | KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0), | ||
| 44 | }; | ||
| 45 | |||
| 46 | __attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = { | ||
| 47 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 48 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 49 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 50 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 51 | |||
| 52 | KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 1), | ||
| 53 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 54 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 55 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 56 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 57 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 58 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 59 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0), | ||
| 60 | KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0), | ||
| 61 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 62 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 63 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0), | ||
| 64 | }; | ||
| 65 | |||
| 66 | const uint8_t ascii_to_keycode_lut[128] PROGMEM = { | ||
| 67 | // NUL SOH STX ETX EOT ENQ ACK BEL | ||
| 68 | XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, | ||
| 69 | // BS TAB LF VT FF CR SO SI | ||
| 70 | KC_BSPC, KC_TAB, KC_ENT, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, | ||
| 71 | // DLE DC1 DC2 DC3 DC4 NAK SYN ETB | ||
| 72 | XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, | ||
| 73 | // CAN EM SUB ESC FS GS RS US | ||
| 74 | XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, | ||
| 75 | |||
| 76 | // ! " # $ % & ' | ||
| 77 | KC_SPC, US_1, US_ACUT, US_3, US_4, US_5, US_7, US_ACUT, | ||
| 78 | // ( ) * + , - . / | ||
| 79 | US_9, US_0, US_8, US_EQL, US_COMM, US_MINS, US_DOT, US_SLSH, | ||
| 80 | // 0 1 2 3 4 5 6 7 | ||
| 81 | US_0, US_1, US_2, US_3, US_4, US_5, US_6, US_7, | ||
| 82 | // 8 9 : ; < = > ? | ||
| 83 | US_8, US_9, US_SCLN, US_SCLN, US_COMM, US_EQL, US_DOT, US_SLSH, | ||
| 84 | // @ A B C D E F G | ||
| 85 | US_2, US_A, US_B, US_C, US_D, US_E, US_F, US_G, | ||
| 86 | // H I J K L M N O | ||
| 87 | US_H, US_I, US_J, US_K, US_L, US_M, US_N, US_O, | ||
| 88 | // P Q R S T U V W | ||
| 89 | US_P, US_Q, US_R, US_S, US_T, US_U, US_V, US_W, | ||
| 90 | // X Y Z [ \ ] ^ _ | ||
| 91 | US_X, US_Y, US_Z, US_LBRC, US_BSLS, US_RBRC, US_6, US_MINS, | ||
| 92 | // ` a b c d e f g | ||
| 93 | US_DGRV, US_A, US_B, US_C, US_D, US_E, US_F, US_G, | ||
| 94 | // h i j k l m n o | ||
| 95 | US_H, US_I, US_J, US_K, US_L, US_M, US_N, US_O, | ||
| 96 | // p q r s t u v w | ||
| 97 | US_P, US_Q, US_R, US_S, US_T, US_U, US_V, US_W, | ||
| 98 | // x y z { | } ~ DEL | ||
| 99 | US_X, US_Y, US_Z, US_LBRC, US_BSLS, US_RBRC, US_DGRV, KC_DEL | ||
| 100 | }; | ||
diff --git a/quantum/matrix.c b/quantum/matrix.c index 9083ff386..c027b7bf2 100644 --- a/quantum/matrix.c +++ b/quantum/matrix.c | |||
| @@ -101,9 +101,9 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) | |||
| 101 | // Start with a clear matrix row | 101 | // Start with a clear matrix row |
| 102 | matrix_row_t current_row_value = 0; | 102 | matrix_row_t current_row_value = 0; |
| 103 | 103 | ||
| 104 | // Select row and wait for row selecton to stabilize | 104 | // Select row |
| 105 | select_row(current_row); | 105 | select_row(current_row); |
| 106 | matrix_io_delay(); | 106 | matrix_output_select_delay(); |
| 107 | 107 | ||
| 108 | // For each col... | 108 | // For each col... |
| 109 | for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { | 109 | for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { |
| @@ -116,6 +116,9 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) | |||
| 116 | 116 | ||
| 117 | // Unselect row | 117 | // Unselect row |
| 118 | unselect_row(current_row); | 118 | unselect_row(current_row); |
| 119 | if (current_row + 1 < MATRIX_ROWS) { | ||
| 120 | matrix_output_unselect_delay(); // wait for row signal to go HIGH | ||
| 121 | } | ||
| 119 | 122 | ||
| 120 | // If the row has changed, store the row and return the changed flag. | 123 | // If the row has changed, store the row and return the changed flag. |
| 121 | if (current_matrix[current_row] != current_row_value) { | 124 | if (current_matrix[current_row] != current_row_value) { |
| @@ -147,9 +150,9 @@ static void init_pins(void) { | |||
| 147 | static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { | 150 | static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { |
| 148 | bool matrix_changed = false; | 151 | bool matrix_changed = false; |
| 149 | 152 | ||
| 150 | // Select col and wait for col selecton to stabilize | 153 | // Select col |
| 151 | select_col(current_col); | 154 | select_col(current_col); |
| 152 | matrix_io_delay(); | 155 | matrix_output_select_delay(); |
| 153 | 156 | ||
| 154 | // For each row... | 157 | // For each row... |
| 155 | for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) { | 158 | for (uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++) { |
| @@ -175,6 +178,9 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) | |||
| 175 | 178 | ||
| 176 | // Unselect col | 179 | // Unselect col |
| 177 | unselect_col(current_col); | 180 | unselect_col(current_col); |
| 181 | if (current_col + 1 < MATRIX_COLS) { | ||
| 182 | matrix_output_unselect_delay(); // wait for col signal to go HIGH | ||
| 183 | } | ||
| 178 | 184 | ||
| 179 | return matrix_changed; | 185 | return matrix_changed; |
| 180 | } | 186 | } |
diff --git a/quantum/matrix.h b/quantum/matrix.h index b570227a3..ce57010a4 100644 --- a/quantum/matrix.h +++ b/quantum/matrix.h | |||
| @@ -55,6 +55,9 @@ matrix_row_t matrix_get_row(uint8_t row); | |||
| 55 | /* print matrix for debug */ | 55 | /* print matrix for debug */ |
| 56 | void matrix_print(void); | 56 | void matrix_print(void); |
| 57 | /* delay between changing matrix pin state and reading values */ | 57 | /* delay between changing matrix pin state and reading values */ |
| 58 | void matrix_output_select_delay(void); | ||
| 59 | void matrix_output_unselect_delay(void); | ||
| 60 | /* only for backwards compatibility. delay between changing matrix pin state and reading values */ | ||
| 58 | void matrix_io_delay(void); | 61 | void matrix_io_delay(void); |
| 59 | 62 | ||
| 60 | /* power control */ | 63 | /* power control */ |
diff --git a/quantum/matrix_common.c b/quantum/matrix_common.c index 15f1e0e82..efbad6a5f 100644 --- a/quantum/matrix_common.c +++ b/quantum/matrix_common.c | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | #include "quantum.h" | ||
| 1 | #include "matrix.h" | 2 | #include "matrix.h" |
| 2 | #include "debounce.h" | 3 | #include "debounce.h" |
| 3 | #include "wait.h" | 4 | #include "wait.h" |
| @@ -68,7 +69,7 @@ void matrix_print(void) { | |||
| 68 | print_matrix_header(); | 69 | print_matrix_header(); |
| 69 | 70 | ||
| 70 | for (uint8_t row = 0; row < MATRIX_ROWS; row++) { | 71 | for (uint8_t row = 0; row < MATRIX_ROWS; row++) { |
| 71 | phex(row); | 72 | print_hex8(row); |
| 72 | print(": "); | 73 | print(": "); |
| 73 | print_matrix_row(row); | 74 | print_matrix_row(row); |
| 74 | print("\n"); | 75 | print("\n"); |
| @@ -83,8 +84,12 @@ uint8_t matrix_key_count(void) { | |||
| 83 | return count; | 84 | return count; |
| 84 | } | 85 | } |
| 85 | 86 | ||
| 87 | /* `matrix_io_delay ()` exists for backwards compatibility. From now on, use matrix_output_unselect_delay(). */ | ||
| 86 | __attribute__((weak)) void matrix_io_delay(void) { wait_us(MATRIX_IO_DELAY); } | 88 | __attribute__((weak)) void matrix_io_delay(void) { wait_us(MATRIX_IO_DELAY); } |
| 87 | 89 | ||
| 90 | __attribute__((weak)) void matrix_output_select_delay(void) { waitInputPinDelay(); } | ||
| 91 | __attribute__((weak)) void matrix_output_unselect_delay(void) { matrix_io_delay(); } | ||
| 92 | |||
| 88 | // CUSTOM MATRIX 'LITE' | 93 | // CUSTOM MATRIX 'LITE' |
| 89 | __attribute__((weak)) void matrix_init_custom(void) {} | 94 | __attribute__((weak)) void matrix_init_custom(void) {} |
| 90 | 95 | ||
diff --git a/quantum/mcu_selection.mk b/quantum/mcu_selection.mk index 6b11eb498..81c467c65 100644 --- a/quantum/mcu_selection.mk +++ b/quantum/mcu_selection.mk | |||
| @@ -279,7 +279,73 @@ ifneq ($(findstring STM32F411, $(MCU)),) | |||
| 279 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | 279 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 |
| 280 | endif | 280 | endif |
| 281 | 281 | ||
| 282 | ifneq (,$(filter $(MCU),atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287)) | 282 | ifneq ($(findstring STM32G431, $(MCU)),) |
| 283 | # Cortex version | ||
| 284 | MCU = cortex-m4 | ||
| 285 | |||
| 286 | # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 | ||
| 287 | ARMV = 7 | ||
| 288 | |||
| 289 | ## chip/board settings | ||
| 290 | # - the next two should match the directories in | ||
| 291 | # <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) | ||
| 292 | MCU_FAMILY = STM32 | ||
| 293 | MCU_SERIES = STM32G4xx | ||
| 294 | |||
| 295 | # Linker script to use | ||
| 296 | # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/ | ||
| 297 | # or <keyboard_dir>/ld/ | ||
| 298 | MCU_LDSCRIPT ?= STM32G431xB | ||
| 299 | |||
| 300 | # Startup code to use | ||
| 301 | # - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/ | ||
| 302 | MCU_STARTUP ?= stm32g4xx | ||
| 303 | |||
| 304 | # Board: it should exist either in <chibios>/os/hal/boards/, | ||
| 305 | # <keyboard_dir>/boards/, or drivers/boards/ | ||
| 306 | BOARD ?= GENERIC_STM32_G431XB | ||
| 307 | |||
| 308 | USE_FPU ?= yes | ||
| 309 | |||
| 310 | # Options to pass to dfu-util when flashing | ||
| 311 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | ||
| 312 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | ||
| 313 | endif | ||
| 314 | |||
| 315 | ifneq ($(findstring STM32G474, $(MCU)),) | ||
| 316 | # Cortex version | ||
| 317 | MCU = cortex-m4 | ||
| 318 | |||
| 319 | # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7 | ||
| 320 | ARMV = 7 | ||
| 321 | |||
| 322 | ## chip/board settings | ||
| 323 | # - the next two should match the directories in | ||
| 324 | # <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES) | ||
| 325 | MCU_FAMILY = STM32 | ||
| 326 | MCU_SERIES = STM32G4xx | ||
| 327 | |||
| 328 | # Linker script to use | ||
| 329 | # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/ | ||
| 330 | # or <keyboard_dir>/ld/ | ||
| 331 | MCU_LDSCRIPT ?= STM32G474xE | ||
| 332 | |||
| 333 | # Startup code to use | ||
| 334 | # - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/ | ||
| 335 | MCU_STARTUP ?= stm32g4xx | ||
| 336 | |||
| 337 | # Board: it should exist either in <chibios>/os/hal/boards/, | ||
| 338 | # <keyboard_dir>/boards/, or drivers/boards/ | ||
| 339 | BOARD ?= GENERIC_STM32_G474XE | ||
| 340 | |||
| 341 | USE_FPU ?= yes | ||
| 342 | |||
| 343 | # Options to pass to dfu-util when flashing | ||
| 344 | DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave | ||
| 345 | DFU_SUFFIX_ARGS ?= -v 0483 -p DF11 | ||
| 346 | endif | ||
| 347 | |||
| 348 | ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287)) | ||
| 283 | PROTOCOL = LUFA | 349 | PROTOCOL = LUFA |
| 284 | 350 | ||
| 285 | # Processor frequency. | 351 | # Processor frequency. |
| @@ -317,7 +383,7 @@ ifneq (,$(filter $(MCU),atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 a | |||
| 317 | ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes)) | 383 | ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes)) |
| 318 | OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT | 384 | OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT |
| 319 | endif | 385 | endif |
| 320 | ifneq (,$(filter $(MCU),atmega16u2 atmega32u2)) | 386 | ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2)) |
| 321 | NO_I2C = yes | 387 | NO_I2C = yes |
| 322 | endif | 388 | endif |
| 323 | endif | 389 | endif |
diff --git a/quantum/mousekey.c b/quantum/mousekey.c new file mode 100644 index 000000000..63e74baa9 --- /dev/null +++ b/quantum/mousekey.c | |||
| @@ -0,0 +1,488 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2011 Jun Wako <wakojun@gmail.com> | ||
| 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 <stdint.h> | ||
| 19 | #include "keycode.h" | ||
| 20 | #include "host.h" | ||
| 21 | #include "timer.h" | ||
| 22 | #include "print.h" | ||
| 23 | #include "debug.h" | ||
| 24 | #include "mousekey.h" | ||
| 25 | |||
| 26 | inline int8_t times_inv_sqrt2(int8_t x) { | ||
| 27 | // 181/256 is pretty close to 1/sqrt(2) | ||
| 28 | // 0.70703125 0.707106781 | ||
| 29 | // 1 too small for x=99 and x=198 | ||
| 30 | // This ends up being a mult and discard lower 8 bits | ||
| 31 | return (x * 181) >> 8; | ||
| 32 | } | ||
| 33 | |||
| 34 | static report_mouse_t mouse_report = {0}; | ||
| 35 | static void mousekey_debug(void); | ||
| 36 | static uint8_t mousekey_accel = 0; | ||
| 37 | static uint8_t mousekey_repeat = 0; | ||
| 38 | static uint8_t mousekey_wheel_repeat = 0; | ||
| 39 | #ifdef MK_KINETIC_SPEED | ||
| 40 | static uint16_t mouse_timer = 0; | ||
| 41 | #endif | ||
| 42 | |||
| 43 | #ifndef MK_3_SPEED | ||
| 44 | |||
| 45 | static uint16_t last_timer_c = 0; | ||
| 46 | static uint16_t last_timer_w = 0; | ||
| 47 | |||
| 48 | /* | ||
| 49 | * Mouse keys acceleration algorithm | ||
| 50 | * http://en.wikipedia.org/wiki/Mouse_keys | ||
| 51 | * | ||
| 52 | * speed = delta * max_speed * (repeat / time_to_max)**((1000+curve)/1000) | ||
| 53 | */ | ||
| 54 | /* milliseconds between the initial key press and first repeated motion event (0-2550) */ | ||
| 55 | uint8_t mk_delay = MOUSEKEY_DELAY / 10; | ||
| 56 | /* milliseconds between repeated motion events (0-255) */ | ||
| 57 | uint8_t mk_interval = MOUSEKEY_INTERVAL; | ||
| 58 | /* steady speed (in action_delta units) applied each event (0-255) */ | ||
| 59 | uint8_t mk_max_speed = MOUSEKEY_MAX_SPEED; | ||
| 60 | /* number of events (count) accelerating to steady speed (0-255) */ | ||
| 61 | uint8_t mk_time_to_max = MOUSEKEY_TIME_TO_MAX; | ||
| 62 | /* ramp used to reach maximum pointer speed (NOT SUPPORTED) */ | ||
| 63 | // int8_t mk_curve = 0; | ||
| 64 | /* wheel params */ | ||
| 65 | /* milliseconds between the initial key press and first repeated motion event (0-2550) */ | ||
| 66 | uint8_t mk_wheel_delay = MOUSEKEY_WHEEL_DELAY / 10; | ||
| 67 | /* milliseconds between repeated motion events (0-255) */ | ||
| 68 | uint8_t mk_wheel_interval = MOUSEKEY_WHEEL_INTERVAL; | ||
| 69 | uint8_t mk_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED; | ||
| 70 | uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX; | ||
| 71 | |||
| 72 | # ifndef MK_COMBINED | ||
| 73 | |||
| 74 | static uint8_t move_unit(void) { | ||
| 75 | uint16_t unit; | ||
| 76 | if (mousekey_accel & (1 << 0)) { | ||
| 77 | unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 4; | ||
| 78 | } else if (mousekey_accel & (1 << 1)) { | ||
| 79 | unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2; | ||
| 80 | } else if (mousekey_accel & (1 << 2)) { | ||
| 81 | unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed); | ||
| 82 | } else if (mousekey_repeat == 0) { | ||
| 83 | unit = MOUSEKEY_MOVE_DELTA; | ||
| 84 | } else if (mousekey_repeat >= mk_time_to_max) { | ||
| 85 | unit = MOUSEKEY_MOVE_DELTA * mk_max_speed; | ||
| 86 | } else { | ||
| 87 | unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max; | ||
| 88 | } | ||
| 89 | return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit)); | ||
| 90 | } | ||
| 91 | |||
| 92 | static uint8_t wheel_unit(void) { | ||
| 93 | uint16_t unit; | ||
| 94 | if (mousekey_accel & (1 << 0)) { | ||
| 95 | unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 4; | ||
| 96 | } else if (mousekey_accel & (1 << 1)) { | ||
| 97 | unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2; | ||
| 98 | } else if (mousekey_accel & (1 << 2)) { | ||
| 99 | unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed); | ||
| 100 | } else if (mousekey_wheel_repeat == 0) { | ||
| 101 | unit = MOUSEKEY_WHEEL_DELTA; | ||
| 102 | } else if (mousekey_wheel_repeat >= mk_wheel_time_to_max) { | ||
| 103 | unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed; | ||
| 104 | } else { | ||
| 105 | unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_wheel_repeat) / mk_wheel_time_to_max; | ||
| 106 | } | ||
| 107 | return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit)); | ||
| 108 | } | ||
| 109 | |||
| 110 | # else /* #ifndef MK_COMBINED */ | ||
| 111 | # ifndef MK_KINETIC_SPEED | ||
| 112 | |||
| 113 | /* | ||
| 114 | * Kinetic movement acceleration algorithm | ||
| 115 | * | ||
| 116 | * current speed = I + A * T/50 + A * 0.5 * T^2 | maximum B | ||
| 117 | * | ||
| 118 | * T: time since the mouse movement started | ||
| 119 | * E: mouse events per second (set through MOUSEKEY_INTERVAL, UHK sends 250, the | ||
| 120 | * pro micro on my Signum 3.0 sends only 125!) | ||
| 121 | * I: initial speed at time 0 | ||
| 122 | * A: acceleration | ||
| 123 | * B: base mouse travel speed | ||
| 124 | */ | ||
| 125 | const uint16_t mk_accelerated_speed = MOUSEKEY_ACCELERATED_SPEED; | ||
| 126 | const uint16_t mk_base_speed = MOUSEKEY_BASE_SPEED; | ||
| 127 | const uint16_t mk_decelerated_speed = MOUSEKEY_DECELERATED_SPEED; | ||
| 128 | const uint16_t mk_initial_speed = MOUSEKEY_INITIAL_SPEED; | ||
| 129 | |||
| 130 | static uint8_t move_unit(void) { | ||
| 131 | float speed = mk_initial_speed; | ||
| 132 | |||
| 133 | if (mousekey_accel & ((1 << 0) | (1 << 2))) { | ||
| 134 | speed = mousekey_accel & (1 << 2) ? mk_accelerated_speed : mk_decelerated_speed; | ||
| 135 | } else if (mousekey_repeat && mouse_timer) { | ||
| 136 | const float time_elapsed = timer_elapsed(mouse_timer) / 50; | ||
| 137 | speed = mk_initial_speed + MOUSEKEY_MOVE_DELTA * time_elapsed + MOUSEKEY_MOVE_DELTA * 0.5 * time_elapsed * time_elapsed; | ||
| 138 | |||
| 139 | speed = speed > mk_base_speed ? mk_base_speed : speed; | ||
| 140 | } | ||
| 141 | |||
| 142 | /* convert speed to USB mouse speed 1 to 127 */ | ||
| 143 | speed = (uint8_t)(speed / (1000.0f / mk_interval)); | ||
| 144 | speed = speed < 1 ? 1 : speed; | ||
| 145 | |||
| 146 | return speed > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : speed; | ||
| 147 | } | ||
| 148 | |||
| 149 | float mk_wheel_interval = 1000.0f / MOUSEKEY_WHEEL_INITIAL_MOVEMENTS; | ||
| 150 | |||
| 151 | static uint8_t wheel_unit(void) { | ||
| 152 | float speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS; | ||
| 153 | |||
| 154 | if (mousekey_accel & ((1 << 0) | (1 << 2))) { | ||
| 155 | speed = mousekey_accel & (1 << 2) ? MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS : MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS; | ||
| 156 | } else if (mousekey_repeat && mouse_timer) { | ||
| 157 | if (mk_wheel_interval != MOUSEKEY_WHEEL_BASE_MOVEMENTS) { | ||
| 158 | const float time_elapsed = timer_elapsed(mouse_timer) / 50; | ||
| 159 | speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS + 1 * time_elapsed + 1 * 0.5 * time_elapsed * time_elapsed; | ||
| 160 | } | ||
| 161 | speed = speed > MOUSEKEY_WHEEL_BASE_MOVEMENTS ? MOUSEKEY_WHEEL_BASE_MOVEMENTS : speed; | ||
| 162 | } | ||
| 163 | |||
| 164 | mk_wheel_interval = 1000.0f / speed; | ||
| 165 | |||
| 166 | return 1; | ||
| 167 | } | ||
| 168 | |||
| 169 | # else /* #ifndef MK_KINETIC_SPEED */ | ||
| 170 | |||
| 171 | static uint8_t move_unit(void) { | ||
| 172 | uint16_t unit; | ||
| 173 | if (mousekey_accel & (1 << 0)) { | ||
| 174 | unit = 1; | ||
| 175 | } else if (mousekey_accel & (1 << 1)) { | ||
| 176 | unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2; | ||
| 177 | } else if (mousekey_accel & (1 << 2)) { | ||
| 178 | unit = MOUSEKEY_MOVE_MAX; | ||
| 179 | } else if (mousekey_repeat == 0) { | ||
| 180 | unit = MOUSEKEY_MOVE_DELTA; | ||
| 181 | } else if (mousekey_repeat >= mk_time_to_max) { | ||
| 182 | unit = MOUSEKEY_MOVE_DELTA * mk_max_speed; | ||
| 183 | } else { | ||
| 184 | unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max; | ||
| 185 | } | ||
| 186 | return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit)); | ||
| 187 | } | ||
| 188 | |||
| 189 | static uint8_t wheel_unit(void) { | ||
| 190 | uint16_t unit; | ||
| 191 | if (mousekey_accel & (1 << 0)) { | ||
| 192 | unit = 1; | ||
| 193 | } else if (mousekey_accel & (1 << 1)) { | ||
| 194 | unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2; | ||
| 195 | } else if (mousekey_accel & (1 << 2)) { | ||
| 196 | unit = MOUSEKEY_WHEEL_MAX; | ||
| 197 | } else if (mousekey_repeat == 0) { | ||
| 198 | unit = MOUSEKEY_WHEEL_DELTA; | ||
| 199 | } else if (mousekey_repeat >= mk_wheel_time_to_max) { | ||
| 200 | unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed; | ||
| 201 | } else { | ||
| 202 | unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_repeat) / mk_wheel_time_to_max; | ||
| 203 | } | ||
| 204 | return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit)); | ||
| 205 | } | ||
| 206 | |||
| 207 | # endif /* #ifndef MK_KINETIC_SPEED */ | ||
| 208 | # endif /* #ifndef MK_COMBINED */ | ||
| 209 | |||
| 210 | void mousekey_task(void) { | ||
| 211 | // report cursor and scroll movement independently | ||
| 212 | report_mouse_t const tmpmr = mouse_report; | ||
| 213 | |||
| 214 | mouse_report.x = 0; | ||
| 215 | mouse_report.y = 0; | ||
| 216 | mouse_report.v = 0; | ||
| 217 | mouse_report.h = 0; | ||
| 218 | |||
| 219 | if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > (mousekey_repeat ? mk_interval : mk_delay * 10)) { | ||
| 220 | if (mousekey_repeat != UINT8_MAX) mousekey_repeat++; | ||
| 221 | if (tmpmr.x != 0) mouse_report.x = move_unit() * ((tmpmr.x > 0) ? 1 : -1); | ||
| 222 | if (tmpmr.y != 0) mouse_report.y = move_unit() * ((tmpmr.y > 0) ? 1 : -1); | ||
| 223 | |||
| 224 | /* diagonal move [1/sqrt(2)] */ | ||
| 225 | if (mouse_report.x && mouse_report.y) { | ||
| 226 | mouse_report.x = times_inv_sqrt2(mouse_report.x); | ||
| 227 | if (mouse_report.x == 0) { | ||
| 228 | mouse_report.x = 1; | ||
| 229 | } | ||
| 230 | mouse_report.y = times_inv_sqrt2(mouse_report.y); | ||
| 231 | if (mouse_report.y == 0) { | ||
| 232 | mouse_report.y = 1; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | } | ||
| 236 | if ((tmpmr.v || tmpmr.h) && timer_elapsed(last_timer_w) > (mousekey_wheel_repeat ? mk_wheel_interval : mk_wheel_delay * 10)) { | ||
| 237 | if (mousekey_wheel_repeat != UINT8_MAX) mousekey_wheel_repeat++; | ||
| 238 | if (tmpmr.v != 0) mouse_report.v = wheel_unit() * ((tmpmr.v > 0) ? 1 : -1); | ||
| 239 | if (tmpmr.h != 0) mouse_report.h = wheel_unit() * ((tmpmr.h > 0) ? 1 : -1); | ||
| 240 | |||
| 241 | /* diagonal move [1/sqrt(2)] */ | ||
| 242 | if (mouse_report.v && mouse_report.h) { | ||
| 243 | mouse_report.v = times_inv_sqrt2(mouse_report.v); | ||
| 244 | if (mouse_report.v == 0) { | ||
| 245 | mouse_report.v = 1; | ||
| 246 | } | ||
| 247 | mouse_report.h = times_inv_sqrt2(mouse_report.h); | ||
| 248 | if (mouse_report.h == 0) { | ||
| 249 | mouse_report.h = 1; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send(); | ||
| 255 | mouse_report = tmpmr; | ||
| 256 | } | ||
| 257 | |||
| 258 | void mousekey_on(uint8_t code) { | ||
| 259 | # ifdef MK_KINETIC_SPEED | ||
| 260 | if (mouse_timer == 0) { | ||
| 261 | mouse_timer = timer_read(); | ||
| 262 | } | ||
| 263 | # endif /* #ifdef MK_KINETIC_SPEED */ | ||
| 264 | |||
| 265 | if (code == KC_MS_UP) | ||
| 266 | mouse_report.y = move_unit() * -1; | ||
| 267 | else if (code == KC_MS_DOWN) | ||
| 268 | mouse_report.y = move_unit(); | ||
| 269 | else if (code == KC_MS_LEFT) | ||
| 270 | mouse_report.x = move_unit() * -1; | ||
| 271 | else if (code == KC_MS_RIGHT) | ||
| 272 | mouse_report.x = move_unit(); | ||
| 273 | else if (code == KC_MS_WH_UP) | ||
| 274 | mouse_report.v = wheel_unit(); | ||
| 275 | else if (code == KC_MS_WH_DOWN) | ||
| 276 | mouse_report.v = wheel_unit() * -1; | ||
| 277 | else if (code == KC_MS_WH_LEFT) | ||
| 278 | mouse_report.h = wheel_unit() * -1; | ||
| 279 | else if (code == KC_MS_WH_RIGHT) | ||
| 280 | mouse_report.h = wheel_unit(); | ||
| 281 | else if (IS_MOUSEKEY_BUTTON(code)) | ||
| 282 | mouse_report.buttons |= 1 << (code - KC_MS_BTN1); | ||
| 283 | else if (code == KC_MS_ACCEL0) | ||
| 284 | mousekey_accel |= (1 << 0); | ||
| 285 | else if (code == KC_MS_ACCEL1) | ||
| 286 | mousekey_accel |= (1 << 1); | ||
| 287 | else if (code == KC_MS_ACCEL2) | ||
| 288 | mousekey_accel |= (1 << 2); | ||
| 289 | } | ||
| 290 | |||
| 291 | void mousekey_off(uint8_t code) { | ||
| 292 | if (code == KC_MS_UP && mouse_report.y < 0) | ||
| 293 | mouse_report.y = 0; | ||
| 294 | else if (code == KC_MS_DOWN && mouse_report.y > 0) | ||
| 295 | mouse_report.y = 0; | ||
| 296 | else if (code == KC_MS_LEFT && mouse_report.x < 0) | ||
| 297 | mouse_report.x = 0; | ||
| 298 | else if (code == KC_MS_RIGHT && mouse_report.x > 0) | ||
| 299 | mouse_report.x = 0; | ||
| 300 | else if (code == KC_MS_WH_UP && mouse_report.v > 0) | ||
| 301 | mouse_report.v = 0; | ||
| 302 | else if (code == KC_MS_WH_DOWN && mouse_report.v < 0) | ||
| 303 | mouse_report.v = 0; | ||
| 304 | else if (code == KC_MS_WH_LEFT && mouse_report.h < 0) | ||
| 305 | mouse_report.h = 0; | ||
| 306 | else if (code == KC_MS_WH_RIGHT && mouse_report.h > 0) | ||
| 307 | mouse_report.h = 0; | ||
| 308 | else if (IS_MOUSEKEY_BUTTON(code)) | ||
| 309 | mouse_report.buttons &= ~(1 << (code - KC_MS_BTN1)); | ||
| 310 | else if (code == KC_MS_ACCEL0) | ||
| 311 | mousekey_accel &= ~(1 << 0); | ||
| 312 | else if (code == KC_MS_ACCEL1) | ||
| 313 | mousekey_accel &= ~(1 << 1); | ||
| 314 | else if (code == KC_MS_ACCEL2) | ||
| 315 | mousekey_accel &= ~(1 << 2); | ||
| 316 | if (mouse_report.x == 0 && mouse_report.y == 0) { | ||
| 317 | mousekey_repeat = 0; | ||
| 318 | # ifdef MK_KINETIC_SPEED | ||
| 319 | mouse_timer = 0; | ||
| 320 | # endif /* #ifdef MK_KINETIC_SPEED */ | ||
| 321 | } | ||
| 322 | if (mouse_report.v == 0 && mouse_report.h == 0) mousekey_wheel_repeat = 0; | ||
| 323 | } | ||
| 324 | |||
| 325 | #else /* #ifndef MK_3_SPEED */ | ||
| 326 | |||
| 327 | enum { mkspd_unmod, mkspd_0, mkspd_1, mkspd_2, mkspd_COUNT }; | ||
| 328 | # ifndef MK_MOMENTARY_ACCEL | ||
| 329 | static uint8_t mk_speed = mkspd_1; | ||
| 330 | # else | ||
| 331 | static uint8_t mk_speed = mkspd_unmod; | ||
| 332 | static uint8_t mkspd_DEFAULT = mkspd_unmod; | ||
| 333 | # endif | ||
| 334 | static uint16_t last_timer_c = 0; | ||
| 335 | static uint16_t last_timer_w = 0; | ||
| 336 | uint16_t c_offsets[mkspd_COUNT] = {MK_C_OFFSET_UNMOD, MK_C_OFFSET_0, MK_C_OFFSET_1, MK_C_OFFSET_2}; | ||
| 337 | uint16_t c_intervals[mkspd_COUNT] = {MK_C_INTERVAL_UNMOD, MK_C_INTERVAL_0, MK_C_INTERVAL_1, MK_C_INTERVAL_2}; | ||
| 338 | uint16_t w_offsets[mkspd_COUNT] = {MK_W_OFFSET_UNMOD, MK_W_OFFSET_0, MK_W_OFFSET_1, MK_W_OFFSET_2}; | ||
| 339 | uint16_t w_intervals[mkspd_COUNT] = {MK_W_INTERVAL_UNMOD, MK_W_INTERVAL_0, MK_W_INTERVAL_1, MK_W_INTERVAL_2}; | ||
| 340 | |||
| 341 | void mousekey_task(void) { | ||
| 342 | // report cursor and scroll movement independently | ||
| 343 | report_mouse_t const tmpmr = mouse_report; | ||
| 344 | mouse_report.x = 0; | ||
| 345 | mouse_report.y = 0; | ||
| 346 | mouse_report.v = 0; | ||
| 347 | mouse_report.h = 0; | ||
| 348 | |||
| 349 | if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > c_intervals[mk_speed]) { | ||
| 350 | mouse_report.x = tmpmr.x; | ||
| 351 | mouse_report.y = tmpmr.y; | ||
| 352 | } | ||
| 353 | if ((tmpmr.h || tmpmr.v) && timer_elapsed(last_timer_w) > w_intervals[mk_speed]) { | ||
| 354 | mouse_report.v = tmpmr.v; | ||
| 355 | mouse_report.h = tmpmr.h; | ||
| 356 | } | ||
| 357 | |||
| 358 | if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send(); | ||
| 359 | mouse_report = tmpmr; | ||
| 360 | } | ||
| 361 | |||
| 362 | void adjust_speed(void) { | ||
| 363 | uint16_t const c_offset = c_offsets[mk_speed]; | ||
| 364 | uint16_t const w_offset = w_offsets[mk_speed]; | ||
| 365 | if (mouse_report.x > 0) mouse_report.x = c_offset; | ||
| 366 | if (mouse_report.x < 0) mouse_report.x = c_offset * -1; | ||
| 367 | if (mouse_report.y > 0) mouse_report.y = c_offset; | ||
| 368 | if (mouse_report.y < 0) mouse_report.y = c_offset * -1; | ||
| 369 | if (mouse_report.h > 0) mouse_report.h = w_offset; | ||
| 370 | if (mouse_report.h < 0) mouse_report.h = w_offset * -1; | ||
| 371 | if (mouse_report.v > 0) mouse_report.v = w_offset; | ||
| 372 | if (mouse_report.v < 0) mouse_report.v = w_offset * -1; | ||
| 373 | // adjust for diagonals | ||
| 374 | if (mouse_report.x && mouse_report.y) { | ||
| 375 | mouse_report.x = times_inv_sqrt2(mouse_report.x); | ||
| 376 | if (mouse_report.x == 0) { | ||
| 377 | mouse_report.x = 1; | ||
| 378 | } | ||
| 379 | mouse_report.y = times_inv_sqrt2(mouse_report.y); | ||
| 380 | if (mouse_report.y == 0) { | ||
| 381 | mouse_report.y = 1; | ||
| 382 | } | ||
| 383 | } | ||
| 384 | if (mouse_report.h && mouse_report.v) { | ||
| 385 | mouse_report.h = times_inv_sqrt2(mouse_report.h); | ||
| 386 | mouse_report.v = times_inv_sqrt2(mouse_report.v); | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | void mousekey_on(uint8_t code) { | ||
| 391 | uint16_t const c_offset = c_offsets[mk_speed]; | ||
| 392 | uint16_t const w_offset = w_offsets[mk_speed]; | ||
| 393 | uint8_t const old_speed = mk_speed; | ||
| 394 | if (code == KC_MS_UP) | ||
| 395 | mouse_report.y = c_offset * -1; | ||
| 396 | else if (code == KC_MS_DOWN) | ||
| 397 | mouse_report.y = c_offset; | ||
| 398 | else if (code == KC_MS_LEFT) | ||
| 399 | mouse_report.x = c_offset * -1; | ||
| 400 | else if (code == KC_MS_RIGHT) | ||
| 401 | mouse_report.x = c_offset; | ||
| 402 | else if (code == KC_MS_WH_UP) | ||
| 403 | mouse_report.v = w_offset; | ||
| 404 | else if (code == KC_MS_WH_DOWN) | ||
| 405 | mouse_report.v = w_offset * -1; | ||
| 406 | else if (code == KC_MS_WH_LEFT) | ||
| 407 | mouse_report.h = w_offset * -1; | ||
| 408 | else if (code == KC_MS_WH_RIGHT) | ||
| 409 | mouse_report.h = w_offset; | ||
| 410 | else if (IS_MOUSEKEY_BUTTON(code)) | ||
| 411 | mouse_report.buttons |= 1 << (code - KC_MS_BTN1); | ||
| 412 | else if (code == KC_MS_ACCEL0) | ||
| 413 | mk_speed = mkspd_0; | ||
| 414 | else if (code == KC_MS_ACCEL1) | ||
| 415 | mk_speed = mkspd_1; | ||
| 416 | else if (code == KC_MS_ACCEL2) | ||
| 417 | mk_speed = mkspd_2; | ||
| 418 | if (mk_speed != old_speed) adjust_speed(); | ||
| 419 | } | ||
| 420 | |||
| 421 | void mousekey_off(uint8_t code) { | ||
| 422 | # ifdef MK_MOMENTARY_ACCEL | ||
| 423 | uint8_t const old_speed = mk_speed; | ||
| 424 | # endif | ||
| 425 | if (code == KC_MS_UP && mouse_report.y < 0) | ||
| 426 | mouse_report.y = 0; | ||
| 427 | else if (code == KC_MS_DOWN && mouse_report.y > 0) | ||
| 428 | mouse_report.y = 0; | ||
| 429 | else if (code == KC_MS_LEFT && mouse_report.x < 0) | ||
| 430 | mouse_report.x = 0; | ||
| 431 | else if (code == KC_MS_RIGHT && mouse_report.x > 0) | ||
| 432 | mouse_report.x = 0; | ||
| 433 | else if (code == KC_MS_WH_UP && mouse_report.v > 0) | ||
| 434 | mouse_report.v = 0; | ||
| 435 | else if (code == KC_MS_WH_DOWN && mouse_report.v < 0) | ||
| 436 | mouse_report.v = 0; | ||
| 437 | else if (code == KC_MS_WH_LEFT && mouse_report.h < 0) | ||
| 438 | mouse_report.h = 0; | ||
| 439 | else if (code == KC_MS_WH_RIGHT && mouse_report.h > 0) | ||
| 440 | mouse_report.h = 0; | ||
| 441 | else if (IS_MOUSEKEY_BUTTON(code)) | ||
| 442 | mouse_report.buttons &= ~(1 << (code - KC_MS_BTN1)); | ||
| 443 | # ifdef MK_MOMENTARY_ACCEL | ||
| 444 | else if (code == KC_MS_ACCEL0) | ||
| 445 | mk_speed = mkspd_DEFAULT; | ||
| 446 | else if (code == KC_MS_ACCEL1) | ||
| 447 | mk_speed = mkspd_DEFAULT; | ||
| 448 | else if (code == KC_MS_ACCEL2) | ||
| 449 | mk_speed = mkspd_DEFAULT; | ||
| 450 | if (mk_speed != old_speed) adjust_speed(); | ||
| 451 | # endif | ||
| 452 | } | ||
| 453 | |||
| 454 | #endif /* #ifndef MK_3_SPEED */ | ||
| 455 | |||
| 456 | void mousekey_send(void) { | ||
| 457 | mousekey_debug(); | ||
| 458 | uint16_t time = timer_read(); | ||
| 459 | if (mouse_report.x || mouse_report.y) last_timer_c = time; | ||
| 460 | if (mouse_report.v || mouse_report.h) last_timer_w = time; | ||
| 461 | host_mouse_send(&mouse_report); | ||
| 462 | } | ||
| 463 | |||
| 464 | void mousekey_clear(void) { | ||
| 465 | mouse_report = (report_mouse_t){}; | ||
| 466 | mousekey_repeat = 0; | ||
| 467 | mousekey_wheel_repeat = 0; | ||
| 468 | mousekey_accel = 0; | ||
| 469 | } | ||
| 470 | |||
| 471 | static void mousekey_debug(void) { | ||
| 472 | if (!debug_mouse) return; | ||
| 473 | print("mousekey [btn|x y v h](rep/acl): ["); | ||
| 474 | print_hex8(mouse_report.buttons); | ||
| 475 | print("|"); | ||
| 476 | print_decs(mouse_report.x); | ||
| 477 | print(" "); | ||
| 478 | print_decs(mouse_report.y); | ||
| 479 | print(" "); | ||
| 480 | print_decs(mouse_report.v); | ||
| 481 | print(" "); | ||
| 482 | print_decs(mouse_report.h); | ||
| 483 | print("]("); | ||
| 484 | print_dec(mousekey_repeat); | ||
| 485 | print("/"); | ||
| 486 | print_dec(mousekey_accel); | ||
| 487 | print(")\n"); | ||
| 488 | } | ||
diff --git a/quantum/mousekey.h b/quantum/mousekey.h new file mode 100644 index 000000000..70dc4bb5c --- /dev/null +++ b/quantum/mousekey.h | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | /* | ||
| 2 | Copyright 2011 Jun Wako <wakojun@gmail.com> | ||
| 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 | #pragma once | ||
| 19 | |||
| 20 | #include <stdint.h> | ||
| 21 | #include "host.h" | ||
| 22 | |||
| 23 | #ifndef MK_3_SPEED | ||
| 24 | |||
| 25 | /* max value on report descriptor */ | ||
| 26 | # ifndef MOUSEKEY_MOVE_MAX | ||
| 27 | # define MOUSEKEY_MOVE_MAX 127 | ||
| 28 | # elif MOUSEKEY_MOVE_MAX > 127 | ||
| 29 | # error MOUSEKEY_MOVE_MAX needs to be smaller than 127 | ||
| 30 | # endif | ||
| 31 | |||
| 32 | # ifndef MOUSEKEY_WHEEL_MAX | ||
| 33 | # define MOUSEKEY_WHEEL_MAX 127 | ||
| 34 | # elif MOUSEKEY_WHEEL_MAX > 127 | ||
| 35 | # error MOUSEKEY_WHEEL_MAX needs to be smaller than 127 | ||
| 36 | # endif | ||
| 37 | |||
| 38 | # ifndef MOUSEKEY_MOVE_DELTA | ||
| 39 | # ifndef MK_KINETIC_SPEED | ||
| 40 | # define MOUSEKEY_MOVE_DELTA 5 | ||
| 41 | # else | ||
| 42 | # define MOUSEKEY_MOVE_DELTA 25 | ||
| 43 | # endif | ||
| 44 | # endif | ||
| 45 | # ifndef MOUSEKEY_WHEEL_DELTA | ||
| 46 | # define MOUSEKEY_WHEEL_DELTA 1 | ||
| 47 | # endif | ||
| 48 | # ifndef MOUSEKEY_DELAY | ||
| 49 | # ifndef MK_KINETIC_SPEED | ||
| 50 | # define MOUSEKEY_DELAY 300 | ||
| 51 | # else | ||
| 52 | # define MOUSEKEY_DELAY 8 | ||
| 53 | # endif | ||
| 54 | # endif | ||
| 55 | # ifndef MOUSEKEY_INTERVAL | ||
| 56 | # ifndef MK_KINETIC_SPEED | ||
| 57 | # define MOUSEKEY_INTERVAL 50 | ||
| 58 | # else | ||
| 59 | # define MOUSEKEY_INTERVAL 8 | ||
| 60 | # endif | ||
| 61 | # endif | ||
| 62 | # ifndef MOUSEKEY_MAX_SPEED | ||
| 63 | # define MOUSEKEY_MAX_SPEED 10 | ||
| 64 | # endif | ||
| 65 | # ifndef MOUSEKEY_TIME_TO_MAX | ||
| 66 | # define MOUSEKEY_TIME_TO_MAX 20 | ||
| 67 | # endif | ||
| 68 | # ifndef MOUSEKEY_WHEEL_DELAY | ||
| 69 | # define MOUSEKEY_WHEEL_DELAY 300 | ||
| 70 | # endif | ||
| 71 | # ifndef MOUSEKEY_WHEEL_INTERVAL | ||
| 72 | # define MOUSEKEY_WHEEL_INTERVAL 100 | ||
| 73 | # endif | ||
| 74 | # ifndef MOUSEKEY_WHEEL_MAX_SPEED | ||
| 75 | # define MOUSEKEY_WHEEL_MAX_SPEED 8 | ||
| 76 | # endif | ||
| 77 | # ifndef MOUSEKEY_WHEEL_TIME_TO_MAX | ||
| 78 | # define MOUSEKEY_WHEEL_TIME_TO_MAX 40 | ||
| 79 | # endif | ||
| 80 | |||
| 81 | # ifndef MOUSEKEY_INITIAL_SPEED | ||
| 82 | # define MOUSEKEY_INITIAL_SPEED 100 | ||
| 83 | # endif | ||
| 84 | # ifndef MOUSEKEY_BASE_SPEED | ||
| 85 | # define MOUSEKEY_BASE_SPEED 1000 | ||
| 86 | # endif | ||
| 87 | # ifndef MOUSEKEY_DECELERATED_SPEED | ||
| 88 | # define MOUSEKEY_DECELERATED_SPEED 400 | ||
| 89 | # endif | ||
| 90 | # ifndef MOUSEKEY_ACCELERATED_SPEED | ||
| 91 | # define MOUSEKEY_ACCELERATED_SPEED 3000 | ||
| 92 | # endif | ||
| 93 | # ifndef MOUSEKEY_WHEEL_INITIAL_MOVEMENTS | ||
| 94 | # define MOUSEKEY_WHEEL_INITIAL_MOVEMENTS 16 | ||
| 95 | # endif | ||
| 96 | # ifndef MOUSEKEY_WHEEL_BASE_MOVEMENTS | ||
| 97 | # define MOUSEKEY_WHEEL_BASE_MOVEMENTS 32 | ||
| 98 | # endif | ||
| 99 | # ifndef MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS | ||
| 100 | # define MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS 48 | ||
| 101 | # endif | ||
| 102 | # ifndef MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS | ||
| 103 | # define MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS 8 | ||
| 104 | # endif | ||
| 105 | |||
| 106 | #else /* #ifndef MK_3_SPEED */ | ||
| 107 | |||
| 108 | # ifndef MK_C_OFFSET_UNMOD | ||
| 109 | # define MK_C_OFFSET_UNMOD 16 | ||
| 110 | # endif | ||
| 111 | # ifndef MK_C_INTERVAL_UNMOD | ||
| 112 | # define MK_C_INTERVAL_UNMOD 16 | ||
| 113 | # endif | ||
| 114 | # ifndef MK_C_OFFSET_0 | ||
| 115 | # define MK_C_OFFSET_0 1 | ||
| 116 | # endif | ||
| 117 | # ifndef MK_C_INTERVAL_0 | ||
| 118 | # define MK_C_INTERVAL_0 32 | ||
| 119 | # endif | ||
| 120 | # ifndef MK_C_OFFSET_1 | ||
| 121 | # define MK_C_OFFSET_1 4 | ||
| 122 | # endif | ||
| 123 | # ifndef MK_C_INTERVAL_1 | ||
| 124 | # define MK_C_INTERVAL_1 16 | ||
| 125 | # endif | ||
| 126 | # ifndef MK_C_OFFSET_2 | ||
| 127 | # define MK_C_OFFSET_2 32 | ||
| 128 | # endif | ||
| 129 | # ifndef MK_C_INTERVAL_2 | ||
| 130 | # define MK_C_INTERVAL_2 16 | ||
| 131 | # endif | ||
| 132 | |||
| 133 | # ifndef MK_W_OFFSET_UNMOD | ||
| 134 | # define MK_W_OFFSET_UNMOD 1 | ||
| 135 | # endif | ||
| 136 | # ifndef MK_W_INTERVAL_UNMOD | ||
| 137 | # define MK_W_INTERVAL_UNMOD 40 | ||
| 138 | # endif | ||
| 139 | # ifndef MK_W_OFFSET_0 | ||
| 140 | # define MK_W_OFFSET_0 1 | ||
| 141 | # endif | ||
| 142 | # ifndef MK_W_INTERVAL_0 | ||
| 143 | # define MK_W_INTERVAL_0 360 | ||
| 144 | # endif | ||
| 145 | # ifndef MK_W_OFFSET_1 | ||
| 146 | # define MK_W_OFFSET_1 1 | ||
| 147 | # endif | ||
| 148 | # ifndef MK_W_INTERVAL_1 | ||
| 149 | # define MK_W_INTERVAL_1 120 | ||
| 150 | # endif | ||
| 151 | # ifndef MK_W_OFFSET_2 | ||
| 152 | # define MK_W_OFFSET_2 1 | ||
| 153 | # endif | ||
| 154 | # ifndef MK_W_INTERVAL_2 | ||
| 155 | # define MK_W_INTERVAL_2 20 | ||
| 156 | # endif | ||
| 157 | |||
| 158 | #endif /* #ifndef MK_3_SPEED */ | ||
| 159 | |||
| 160 | #ifdef __cplusplus | ||
| 161 | extern "C" { | ||
| 162 | #endif | ||
| 163 | |||
| 164 | extern uint8_t mk_delay; | ||
| 165 | extern uint8_t mk_interval; | ||
| 166 | extern uint8_t mk_max_speed; | ||
| 167 | extern uint8_t mk_time_to_max; | ||
| 168 | extern uint8_t mk_wheel_max_speed; | ||
| 169 | extern uint8_t mk_wheel_time_to_max; | ||
| 170 | |||
| 171 | void mousekey_task(void); | ||
| 172 | void mousekey_on(uint8_t code); | ||
| 173 | void mousekey_off(uint8_t code); | ||
| 174 | void mousekey_clear(void); | ||
| 175 | void mousekey_send(void); | ||
| 176 | |||
| 177 | #ifdef __cplusplus | ||
| 178 | } | ||
| 179 | #endif | ||
diff --git a/quantum/quantum.c b/quantum/quantum.c index cf16e953a..6d202c515 100644 --- a/quantum/quantum.c +++ b/quantum/quantum.c | |||
| @@ -25,10 +25,6 @@ | |||
| 25 | # include "backlight.h" | 25 | # include "backlight.h" |
| 26 | #endif | 26 | #endif |
| 27 | 27 | ||
| 28 | #ifdef FAUXCLICKY_ENABLE | ||
| 29 | # include "fauxclicky.h" | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #ifdef API_ENABLE | 28 | #ifdef API_ENABLE |
| 33 | # include "api.h" | 29 | # include "api.h" |
| 34 | #endif | 30 | #endif |
| @@ -310,17 +306,6 @@ bool process_record_quantum(keyrecord_t *record) { | |||
| 310 | case EEPROM_RESET: | 306 | case EEPROM_RESET: |
| 311 | eeconfig_init(); | 307 | eeconfig_init(); |
| 312 | return false; | 308 | return false; |
| 313 | #ifdef FAUXCLICKY_ENABLE | ||
| 314 | case FC_TOG: | ||
| 315 | FAUXCLICKY_TOGGLE; | ||
| 316 | return false; | ||
| 317 | case FC_ON: | ||
| 318 | FAUXCLICKY_ON; | ||
| 319 | return false; | ||
| 320 | case FC_OFF: | ||
| 321 | FAUXCLICKY_OFF; | ||
| 322 | return false; | ||
| 323 | #endif | ||
| 324 | #ifdef VELOCIKEY_ENABLE | 309 | #ifdef VELOCIKEY_ENABLE |
| 325 | case VLK_TOG: | 310 | case VLK_TOG: |
| 326 | velocikey_toggle(); | 311 | velocikey_toggle(); |
| @@ -391,6 +376,29 @@ __attribute__((weak)) const uint8_t ascii_to_altgr_lut[16] PROGMEM = { | |||
| 391 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | 376 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), |
| 392 | }; | 377 | }; |
| 393 | 378 | ||
| 379 | /* Bit-Packed look-up table to convert an ASCII character to whether | ||
| 380 | * [Space] needs to be sent after the keycode | ||
| 381 | */ | ||
| 382 | __attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = { | ||
| 383 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 384 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 385 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 386 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 387 | |||
| 388 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 389 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 390 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 391 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 392 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 393 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 394 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 395 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 396 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 397 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 398 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 399 | KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0), | ||
| 400 | }; | ||
| 401 | |||
| 394 | /* Look-up table to convert an ASCII character to a keycode. | 402 | /* Look-up table to convert an ASCII character to a keycode. |
| 395 | */ | 403 | */ |
| 396 | __attribute__((weak)) const uint8_t ascii_to_keycode_lut[128] PROGMEM = { | 404 | __attribute__((weak)) const uint8_t ascii_to_keycode_lut[128] PROGMEM = { |
| @@ -531,6 +539,7 @@ void send_char(char ascii_code) { | |||
| 531 | uint8_t keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]); | 539 | uint8_t keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]); |
| 532 | bool is_shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)ascii_code); | 540 | bool is_shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)ascii_code); |
| 533 | bool is_altgred = PGM_LOADBIT(ascii_to_altgr_lut, (uint8_t)ascii_code); | 541 | bool is_altgred = PGM_LOADBIT(ascii_to_altgr_lut, (uint8_t)ascii_code); |
| 542 | bool is_dead = PGM_LOADBIT(ascii_to_dead_lut, (uint8_t)ascii_code); | ||
| 534 | 543 | ||
| 535 | if (is_shifted) { | 544 | if (is_shifted) { |
| 536 | register_code(KC_LSFT); | 545 | register_code(KC_LSFT); |
| @@ -545,6 +554,9 @@ void send_char(char ascii_code) { | |||
| 545 | if (is_shifted) { | 554 | if (is_shifted) { |
| 546 | unregister_code(KC_LSFT); | 555 | unregister_code(KC_LSFT); |
| 547 | } | 556 | } |
| 557 | if (is_dead) { | ||
| 558 | tap_code(KC_SPACE); | ||
| 559 | } | ||
| 548 | } | 560 | } |
| 549 | 561 | ||
| 550 | void set_single_persistent_default_layer(uint8_t default_layer) { | 562 | void set_single_persistent_default_layer(uint8_t default_layer) { |
| @@ -629,6 +641,26 @@ void matrix_init_quantum() { | |||
| 629 | } | 641 | } |
| 630 | 642 | ||
| 631 | void matrix_scan_quantum() { | 643 | void matrix_scan_quantum() { |
| 644 | #if defined(AUDIO_ENABLE) | ||
| 645 | // There are some tasks that need to be run a little bit | ||
| 646 | // after keyboard startup, or else they will not work correctly | ||
| 647 | // because of interaction with the USB device state, which | ||
| 648 | // may still be in flux... | ||
| 649 | // | ||
| 650 | // At the moment the only feature that needs this is the | ||
| 651 | // startup song. | ||
| 652 | static bool delayed_tasks_run = false; | ||
| 653 | static uint16_t delayed_task_timer = 0; | ||
| 654 | if (!delayed_tasks_run) { | ||
| 655 | if (!delayed_task_timer) { | ||
| 656 | delayed_task_timer = timer_read(); | ||
| 657 | } else if (timer_elapsed(delayed_task_timer) > 300) { | ||
| 658 | audio_startup(); | ||
| 659 | delayed_tasks_run = true; | ||
| 660 | } | ||
| 661 | } | ||
| 662 | #endif | ||
| 663 | |||
| 632 | #if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE) | 664 | #if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE) |
| 633 | matrix_scan_music(); | 665 | matrix_scan_music(); |
| 634 | #endif | 666 | #endif |
diff --git a/quantum/quantum.h b/quantum/quantum.h index dd2a6dd53..36a983d57 100644 --- a/quantum/quantum.h +++ b/quantum/quantum.h | |||
| @@ -53,6 +53,7 @@ | |||
| 53 | #include "eeconfig.h" | 53 | #include "eeconfig.h" |
| 54 | #include "bootloader.h" | 54 | #include "bootloader.h" |
| 55 | #include "timer.h" | 55 | #include "timer.h" |
| 56 | #include "sync_timer.h" | ||
| 56 | #include "config_common.h" | 57 | #include "config_common.h" |
| 57 | #include "gpio.h" | 58 | #include "gpio.h" |
| 58 | #include "atomic_util.h" | 59 | #include "atomic_util.h" |
| @@ -194,6 +195,42 @@ extern layer_state_t layer_state; | |||
| 194 | # include "wpm.h" | 195 | # include "wpm.h" |
| 195 | #endif | 196 | #endif |
| 196 | 197 | ||
| 198 | #ifdef USBPD_ENABLE | ||
| 199 | # include "usbpd.h" | ||
| 200 | #endif | ||
| 201 | |||
| 202 | // Function substitutions to ease GPIO manipulation | ||
| 203 | #if defined(__AVR__) | ||
| 204 | |||
| 205 | /* The AVR series GPIOs have a one clock read delay for changes in the digital input signal. | ||
| 206 | * But here's more margin to make it two clocks. */ | ||
| 207 | # if !defined(GPIO_INPUT_PIN_DELAY) | ||
| 208 | # define GPIO_INPUT_PIN_DELAY 2 | ||
| 209 | # endif | ||
| 210 | # define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) | ||
| 211 | |||
| 212 | #elif defined(__ARMEL__) || defined(__ARMEB__) | ||
| 213 | |||
| 214 | /* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus | ||
| 215 | * to which the GPIO is connected. | ||
| 216 | * The connected buses differ depending on the various series of MCUs. | ||
| 217 | * And since the instruction execution clock of the CPU and the bus clock of GPIO are different, | ||
| 218 | * there is a delay of several clocks to read the change of the input signal. | ||
| 219 | * | ||
| 220 | * Define this delay with the GPIO_INPUT_PIN_DELAY macro. | ||
| 221 | * If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used. | ||
| 222 | * (A fairly large value of 0.25 microseconds is set.) | ||
| 223 | */ | ||
| 224 | # if !defined(GPIO_INPUT_PIN_DELAY) | ||
| 225 | # if defined(STM32_SYSCLK) | ||
| 226 | # define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4) | ||
| 227 | # elif defined(KINETIS_SYSCLK_FREQUENCY) | ||
| 228 | # define GPIO_INPUT_PIN_DELAY (KINETIS_SYSCLK_FREQUENCY / 1000000L / 4) | ||
| 229 | # endif | ||
| 230 | # endif | ||
| 231 | # define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY) | ||
| 232 | |||
| 233 | #endif | ||
| 197 | #define SEND_STRING(string) send_string_P(PSTR(string)) | 234 | #define SEND_STRING(string) send_string_P(PSTR(string)) |
| 198 | #define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval) | 235 | #define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval) |
| 199 | 236 | ||
| @@ -201,6 +238,7 @@ extern layer_state_t layer_state; | |||
| 201 | extern const uint8_t ascii_to_keycode_lut[128]; | 238 | extern const uint8_t ascii_to_keycode_lut[128]; |
| 202 | extern const uint8_t ascii_to_shift_lut[16]; | 239 | extern const uint8_t ascii_to_shift_lut[16]; |
| 203 | extern const uint8_t ascii_to_altgr_lut[16]; | 240 | extern const uint8_t ascii_to_altgr_lut[16]; |
| 241 | extern const uint8_t ascii_to_dead_lut[16]; | ||
| 204 | // clang-format off | 242 | // clang-format off |
| 205 | #define KCLUT_ENTRY(a, b, c, d, e, f, g, h) \ | 243 | #define KCLUT_ENTRY(a, b, c, d, e, f, g, h) \ |
| 206 | ( ((a) ? 1 : 0) << 0 \ | 244 | ( ((a) ? 1 : 0) << 0 \ |
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h index 0160c5586..e0f5dbc61 100644 --- a/quantum/quantum_keycodes.h +++ b/quantum/quantum_keycodes.h | |||
| @@ -150,13 +150,6 @@ enum quantum_keycodes { | |||
| 150 | CLICKY_DOWN, | 150 | CLICKY_DOWN, |
| 151 | CLICKY_RESET, | 151 | CLICKY_RESET, |
| 152 | 152 | ||
| 153 | #ifdef FAUXCLICKY_ENABLE | ||
| 154 | // Faux clicky | ||
| 155 | FC_ON, | ||
| 156 | FC_OFF, | ||
| 157 | FC_TOG, | ||
| 158 | #endif | ||
| 159 | |||
| 160 | // Music mode on/off/toggle | 153 | // Music mode on/off/toggle |
| 161 | MU_ON, | 154 | MU_ON, |
| 162 | MU_OFF, | 155 | MU_OFF, |
| @@ -717,6 +710,9 @@ enum quantum_keycodes { | |||
| 717 | #define CK_DOWN CLICKY_DOWN | 710 | #define CK_DOWN CLICKY_DOWN |
| 718 | #define CK_ON CLICKY_ENABLE | 711 | #define CK_ON CLICKY_ENABLE |
| 719 | #define CK_OFF CLICKY_DISABLE | 712 | #define CK_OFF CLICKY_DISABLE |
| 713 | #define FC_ON CLICKY_ENABLE | ||
| 714 | #define FC_OFF CLICKY_DISABLE | ||
| 715 | #define FC_TOGG CLICKY_TOGGLE | ||
| 720 | 716 | ||
| 721 | #define RGB_MOD RGB_MODE_FORWARD | 717 | #define RGB_MOD RGB_MODE_FORWARD |
| 722 | #define RGB_RMOD RGB_MODE_REVERSE | 718 | #define RGB_RMOD RGB_MODE_REVERSE |
diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c index 04af3ae9e..a945df68e 100644 --- a/quantum/rgb_matrix.c +++ b/quantum/rgb_matrix.c | |||
| @@ -266,9 +266,9 @@ static bool rgb_matrix_none(effect_params_t *params) { | |||
| 266 | 266 | ||
| 267 | static void rgb_task_timers(void) { | 267 | static void rgb_task_timers(void) { |
| 268 | #if defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0 | 268 | #if defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0 |
| 269 | uint32_t deltaTime = timer_elapsed32(rgb_timer_buffer); | 269 | uint32_t deltaTime = sync_timer_elapsed32(rgb_timer_buffer); |
| 270 | #endif // defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0 | 270 | #endif // defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0 |
| 271 | rgb_timer_buffer = timer_read32(); | 271 | rgb_timer_buffer = sync_timer_read32(); |
| 272 | 272 | ||
| 273 | // Update double buffer timers | 273 | // Update double buffer timers |
| 274 | #if RGB_DISABLE_TIMEOUT > 0 | 274 | #if RGB_DISABLE_TIMEOUT > 0 |
| @@ -296,7 +296,7 @@ static void rgb_task_timers(void) { | |||
| 296 | 296 | ||
| 297 | static void rgb_task_sync(void) { | 297 | static void rgb_task_sync(void) { |
| 298 | // next task | 298 | // next task |
| 299 | if (timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING; | 299 | if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING; |
| 300 | } | 300 | } |
| 301 | 301 | ||
| 302 | static void rgb_task_start(void) { | 302 | static void rgb_task_start(void) { |
diff --git a/quantum/rgblight.c b/quantum/rgblight.c index ac4ff9bfd..7d7d015ba 100644 --- a/quantum/rgblight.c +++ b/quantum/rgblight.c | |||
| @@ -29,7 +29,7 @@ | |||
| 29 | #endif | 29 | #endif |
| 30 | #include "wait.h" | 30 | #include "wait.h" |
| 31 | #include "progmem.h" | 31 | #include "progmem.h" |
| 32 | #include "timer.h" | 32 | #include "sync_timer.h" |
| 33 | #include "rgblight.h" | 33 | #include "rgblight.h" |
| 34 | #include "color.h" | 34 | #include "color.h" |
| 35 | #include "debug.h" | 35 | #include "debug.h" |
| @@ -42,6 +42,9 @@ | |||
| 42 | #ifndef MIN | 42 | #ifndef MIN |
| 43 | # define MIN(a, b) (((a) < (b)) ? (a) : (b)) | 43 | # define MIN(a, b) (((a) < (b)) ? (a) : (b)) |
| 44 | #endif | 44 | #endif |
| 45 | #ifndef MAX | ||
| 46 | # define MAX(a, b) (((a) > (b)) ? (a) : (b)) | ||
| 47 | #endif | ||
| 45 | 48 | ||
| 46 | #ifdef RGBLIGHT_SPLIT | 49 | #ifdef RGBLIGHT_SPLIT |
| 47 | /* for split keyboard */ | 50 | /* for split keyboard */ |
| @@ -700,18 +703,16 @@ static void rgblight_layers_write(void) { | |||
| 700 | 703 | ||
| 701 | # ifdef RGBLIGHT_LAYER_BLINK | 704 | # ifdef RGBLIGHT_LAYER_BLINK |
| 702 | rgblight_layer_mask_t _blinked_layer_mask = 0; | 705 | rgblight_layer_mask_t _blinked_layer_mask = 0; |
| 703 | uint16_t _blink_duration = 0; | ||
| 704 | static uint16_t _blink_timer; | 706 | static uint16_t _blink_timer; |
| 705 | 707 | ||
| 706 | void rgblight_blink_layer(uint8_t layer, uint16_t duration_ms) { | 708 | void rgblight_blink_layer(uint8_t layer, uint16_t duration_ms) { |
| 707 | rgblight_set_layer_state(layer, true); | 709 | rgblight_set_layer_state(layer, true); |
| 708 | _blinked_layer_mask |= (rgblight_layer_mask_t)1 << layer; | 710 | _blinked_layer_mask |= (rgblight_layer_mask_t)1 << layer; |
| 709 | _blink_timer = timer_read(); | 711 | _blink_timer = sync_timer_read() + duration_ms; |
| 710 | _blink_duration = duration_ms; | ||
| 711 | } | 712 | } |
| 712 | 713 | ||
| 713 | void rgblight_unblink_layers(void) { | 714 | void rgblight_unblink_layers(void) { |
| 714 | if (_blinked_layer_mask != 0 && timer_elapsed(_blink_timer) > _blink_duration) { | 715 | if (_blinked_layer_mask != 0 && timer_expired(sync_timer_read(), _blink_timer)) { |
| 715 | for (uint8_t layer = 0; layer < RGBLIGHT_MAX_LAYERS; layer++) { | 716 | for (uint8_t layer = 0; layer < RGBLIGHT_MAX_LAYERS; layer++) { |
| 716 | if ((_blinked_layer_mask & (rgblight_layer_mask_t)1 << layer) != 0) { | 717 | if ((_blinked_layer_mask & (rgblight_layer_mask_t)1 << layer) != 0) { |
| 717 | rgblight_set_layer_state(layer, false); | 718 | rgblight_set_layer_state(layer, false); |
| @@ -886,7 +887,7 @@ void rgblight_timer_enable(void) { | |||
| 886 | if (!is_static_effect(rgblight_config.mode)) { | 887 | if (!is_static_effect(rgblight_config.mode)) { |
| 887 | rgblight_status.timer_enabled = true; | 888 | rgblight_status.timer_enabled = true; |
| 888 | } | 889 | } |
| 889 | animation_status.last_timer = timer_read(); | 890 | animation_status.last_timer = sync_timer_read(); |
| 890 | RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE; | 891 | RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE; |
| 891 | dprintf("rgblight timer enabled.\n"); | 892 | dprintf("rgblight timer enabled.\n"); |
| 892 | } | 893 | } |
| @@ -989,24 +990,25 @@ void rgblight_task(void) { | |||
| 989 | # endif | 990 | # endif |
| 990 | # ifdef RGBLIGHT_EFFECT_TWINKLE | 991 | # ifdef RGBLIGHT_EFFECT_TWINKLE |
| 991 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) { | 992 | else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) { |
| 992 | interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 50); | 993 | interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 30); |
| 993 | effect_func = (effect_func_t)rgblight_effect_twinkle; | 994 | effect_func = (effect_func_t)rgblight_effect_twinkle; |
| 994 | } | 995 | } |
| 995 | # endif | 996 | # endif |
| 996 | if (animation_status.restart) { | 997 | if (animation_status.restart) { |
| 997 | animation_status.restart = false; | 998 | animation_status.restart = false; |
| 998 | animation_status.last_timer = timer_read() - interval_time - 1; | 999 | animation_status.last_timer = sync_timer_read(); |
| 999 | animation_status.pos16 = 0; // restart signal to local each effect | 1000 | animation_status.pos16 = 0; // restart signal to local each effect |
| 1000 | } | 1001 | } |
| 1001 | if (timer_elapsed(animation_status.last_timer) >= interval_time) { | 1002 | uint16_t now = sync_timer_read(); |
| 1003 | if (timer_expired(now, animation_status.last_timer)) { | ||
| 1002 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) | 1004 | # if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC) |
| 1003 | static uint16_t report_last_timer = 0; | 1005 | static uint16_t report_last_timer = 0; |
| 1004 | static bool tick_flag = false; | 1006 | static bool tick_flag = false; |
| 1005 | uint16_t oldpos16; | 1007 | uint16_t oldpos16; |
| 1006 | if (tick_flag) { | 1008 | if (tick_flag) { |
| 1007 | tick_flag = false; | 1009 | tick_flag = false; |
| 1008 | if (timer_elapsed(report_last_timer) >= 30000) { | 1010 | if (timer_expired(now, report_last_timer)) { |
| 1009 | report_last_timer = timer_read(); | 1011 | report_last_timer += 30000; |
| 1010 | dprintf("rgblight animation tick report to slave\n"); | 1012 | dprintf("rgblight animation tick report to slave\n"); |
| 1011 | RGBLIGHT_SPLIT_ANIMATION_TICK; | 1013 | RGBLIGHT_SPLIT_ANIMATION_TICK; |
| 1012 | } | 1014 | } |
| @@ -1030,8 +1032,7 @@ void rgblight_task(void) { | |||
| 1030 | 1032 | ||
| 1031 | #endif /* RGBLIGHT_USE_TIMER */ | 1033 | #endif /* RGBLIGHT_USE_TIMER */ |
| 1032 | 1034 | ||
| 1033 | // Effects | 1035 | #if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_TWINKLE) |
| 1034 | #ifdef RGBLIGHT_EFFECT_BREATHING | ||
| 1035 | 1036 | ||
| 1036 | # ifndef RGBLIGHT_EFFECT_BREATHE_CENTER | 1037 | # ifndef RGBLIGHT_EFFECT_BREATHE_CENTER |
| 1037 | # ifndef RGBLIGHT_BREATHE_TABLE_SIZE | 1038 | # ifndef RGBLIGHT_BREATHE_TABLE_SIZE |
| @@ -1040,17 +1041,24 @@ void rgblight_task(void) { | |||
| 1040 | # include <rgblight_breathe_table.h> | 1041 | # include <rgblight_breathe_table.h> |
| 1041 | # endif | 1042 | # endif |
| 1042 | 1043 | ||
| 1043 | __attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5}; | 1044 | static uint8_t breathe_calc(uint8_t pos) { |
| 1044 | |||
| 1045 | void rgblight_effect_breathing(animation_status_t *anim) { | ||
| 1046 | float val; | ||
| 1047 | |||
| 1048 | // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/ | 1045 | // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/ |
| 1049 | # ifdef RGBLIGHT_EFFECT_BREATHE_TABLE | 1046 | # ifdef RGBLIGHT_EFFECT_BREATHE_TABLE |
| 1050 | val = pgm_read_byte(&rgblight_effect_breathe_table[anim->pos / table_scale]); | 1047 | return pgm_read_byte(&rgblight_effect_breathe_table[pos / table_scale]); |
| 1051 | # else | 1048 | # else |
| 1052 | val = (exp(sin((anim->pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E)); | 1049 | return (exp(sin((pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E)); |
| 1053 | # endif | 1050 | # endif |
| 1051 | } | ||
| 1052 | |||
| 1053 | #endif | ||
| 1054 | |||
| 1055 | // Effects | ||
| 1056 | #ifdef RGBLIGHT_EFFECT_BREATHING | ||
| 1057 | |||
| 1058 | __attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5}; | ||
| 1059 | |||
| 1060 | void rgblight_effect_breathing(animation_status_t *anim) { | ||
| 1061 | uint8_t val = breathe_calc(anim->pos); | ||
| 1054 | rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val); | 1062 | rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val); |
| 1055 | anim->pos = (anim->pos + 1); | 1063 | anim->pos = (anim->pos + 1); |
| 1056 | } | 1064 | } |
| @@ -1302,48 +1310,54 @@ void rgblight_effect_alternating(animation_status_t *anim) { | |||
| 1302 | #endif | 1310 | #endif |
| 1303 | 1311 | ||
| 1304 | #ifdef RGBLIGHT_EFFECT_TWINKLE | 1312 | #ifdef RGBLIGHT_EFFECT_TWINKLE |
| 1305 | __attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {50, 25, 10}; | 1313 | __attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {30, 15, 5}; |
| 1306 | 1314 | ||
| 1307 | typedef struct PACKED { | 1315 | typedef struct PACKED { |
| 1308 | HSV hsv; | 1316 | HSV hsv; |
| 1309 | uint8_t life; | 1317 | uint8_t life; |
| 1310 | bool up; | 1318 | uint8_t max_life; |
| 1311 | } TwinkleState; | 1319 | } TwinkleState; |
| 1312 | 1320 | ||
| 1313 | static TwinkleState led_twinkle_state[RGBLED_NUM]; | 1321 | static TwinkleState led_twinkle_state[RGBLED_NUM]; |
| 1314 | 1322 | ||
| 1315 | void rgblight_effect_twinkle(animation_status_t *anim) { | 1323 | void rgblight_effect_twinkle(animation_status_t *anim) { |
| 1316 | bool random_color = anim->delta / 3; | 1324 | const bool random_color = anim->delta / 3; |
| 1317 | bool restart = anim->pos == 0; | 1325 | const bool restart = anim->pos == 0; |
| 1318 | anim->pos = 1; | 1326 | anim->pos = 1; |
| 1327 | |||
| 1328 | const uint8_t bottom = breathe_calc(0); | ||
| 1329 | const uint8_t top = breathe_calc(127); | ||
| 1330 | |||
| 1331 | uint8_t frac(uint8_t n, uint8_t d) { return (uint16_t)255 * n / d; } | ||
| 1332 | uint8_t scale(uint16_t v, uint8_t scale) { return (v * scale) >> 8; } | ||
| 1319 | 1333 | ||
| 1320 | for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) { | 1334 | for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) { |
| 1321 | TwinkleState *t = &(led_twinkle_state[i]); | 1335 | TwinkleState *t = &(led_twinkle_state[i]); |
| 1322 | HSV * c = &(t->hsv); | 1336 | HSV * c = &(t->hsv); |
| 1337 | |||
| 1338 | if (!random_color) { | ||
| 1339 | c->h = rgblight_config.hue; | ||
| 1340 | c->s = rgblight_config.sat; | ||
| 1341 | } | ||
| 1342 | |||
| 1323 | if (restart) { | 1343 | if (restart) { |
| 1324 | // Restart | 1344 | // Restart |
| 1325 | t->life = 0; | 1345 | t->life = 0; |
| 1326 | t->hsv.v = 0; | 1346 | c->v = 0; |
| 1327 | } else if (t->life) { | 1347 | } else if (t->life) { |
| 1328 | // This LED is already on, either brightening or dimming | 1348 | // This LED is already on, either brightening or dimming |
| 1329 | t->life--; | 1349 | t->life--; |
| 1330 | uint8_t on = t->up ? RGBLIGHT_EFFECT_TWINKLE_LIFE - t->life : t->life; | 1350 | uint8_t unscaled = frac(breathe_calc(frac(t->life, t->max_life)) - bottom, top - bottom); |
| 1331 | c->v = (uint16_t)rgblight_config.val * on / RGBLIGHT_EFFECT_TWINKLE_LIFE; | 1351 | c->v = scale(rgblight_config.val, unscaled); |
| 1332 | if (t->life == 0 && t->up) { | 1352 | } else if (rand() < scale((uint16_t)RAND_MAX * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY, 127 + rgblight_config.val / 2)) { |
| 1333 | t->up = false; | ||
| 1334 | t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE; | ||
| 1335 | } | ||
| 1336 | if (!random_color) { | ||
| 1337 | c->h = rgblight_config.hue; | ||
| 1338 | c->s = rgblight_config.sat; | ||
| 1339 | } | ||
| 1340 | } else if (rand() < RAND_MAX * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY) { | ||
| 1341 | // This LED is off, but was randomly selected to start brightening | 1353 | // This LED is off, but was randomly selected to start brightening |
| 1342 | c->h = random_color ? rand() % 0xFF : rgblight_config.hue; | 1354 | if (random_color) { |
| 1343 | c->s = random_color ? (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2) : rgblight_config.sat; | 1355 | c->h = rand() % 0xFF; |
| 1344 | c->v = 0; | 1356 | c->s = (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2); |
| 1345 | t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE; | 1357 | } |
| 1346 | t->up = true; | 1358 | c->v = 0; |
| 1359 | t->max_life = MAX(20, MIN(RGBLIGHT_EFFECT_TWINKLE_LIFE, rgblight_config.val)); | ||
| 1360 | t->life = t->max_life; | ||
| 1347 | } else { | 1361 | } else { |
| 1348 | // This LED is off, and was NOT selected to start brightening | 1362 | // This LED is off, and was NOT selected to start brightening |
| 1349 | } | 1363 | } |
diff --git a/quantum/rgblight.h b/quantum/rgblight.h index 1854fee99..028b3ea41 100644 --- a/quantum/rgblight.h +++ b/quantum/rgblight.h | |||
| @@ -150,7 +150,7 @@ enum RGBLIGHT_EFFECT_MODE { | |||
| 150 | # endif | 150 | # endif |
| 151 | 151 | ||
| 152 | # ifndef RGBLIGHT_EFFECT_TWINKLE_LIFE | 152 | # ifndef RGBLIGHT_EFFECT_TWINKLE_LIFE |
| 153 | # define RGBLIGHT_EFFECT_TWINKLE_LIFE 75 | 153 | # define RGBLIGHT_EFFECT_TWINKLE_LIFE 200 |
| 154 | # endif | 154 | # endif |
| 155 | 155 | ||
| 156 | # ifndef RGBLIGHT_EFFECT_TWINKLE_PROBABILITY | 156 | # ifndef RGBLIGHT_EFFECT_TWINKLE_PROBABILITY |
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c index 51bf8b109..bad762b49 100644 --- a/quantum/split_common/matrix.c +++ b/quantum/split_common/matrix.c | |||
| @@ -114,9 +114,9 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) | |||
| 114 | // Start with a clear matrix row | 114 | // Start with a clear matrix row |
| 115 | matrix_row_t current_row_value = 0; | 115 | matrix_row_t current_row_value = 0; |
| 116 | 116 | ||
| 117 | // Select row and wait for row selecton to stabilize | 117 | // Select row |
| 118 | select_row(current_row); | 118 | select_row(current_row); |
| 119 | matrix_io_delay(); | 119 | matrix_output_select_delay(); |
| 120 | 120 | ||
| 121 | // For each col... | 121 | // For each col... |
| 122 | for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { | 122 | for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) { |
| @@ -129,6 +129,9 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) | |||
| 129 | 129 | ||
| 130 | // Unselect row | 130 | // Unselect row |
| 131 | unselect_row(current_row); | 131 | unselect_row(current_row); |
| 132 | if (current_row + 1 < MATRIX_ROWS) { | ||
| 133 | matrix_output_unselect_delay(); // wait for row signal to go HIGH | ||
| 134 | } | ||
| 132 | 135 | ||
| 133 | // If the row has changed, store the row and return the changed flag. | 136 | // If the row has changed, store the row and return the changed flag. |
| 134 | if (current_matrix[current_row] != current_row_value) { | 137 | if (current_matrix[current_row] != current_row_value) { |
| @@ -160,9 +163,9 @@ static void init_pins(void) { | |||
| 160 | static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { | 163 | static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) { |
| 161 | bool matrix_changed = false; | 164 | bool matrix_changed = false; |
| 162 | 165 | ||
| 163 | // Select col and wait for col selecton to stabilize | 166 | // Select col |
| 164 | select_col(current_col); | 167 | select_col(current_col); |
| 165 | matrix_io_delay(); | 168 | matrix_output_select_delay(); |
| 166 | 169 | ||
| 167 | // For each row... | 170 | // For each row... |
| 168 | for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { | 171 | for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) { |
| @@ -188,6 +191,9 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) | |||
| 188 | 191 | ||
| 189 | // Unselect col | 192 | // Unselect col |
| 190 | unselect_col(current_col); | 193 | unselect_col(current_col); |
| 194 | if (current_col + 1 < MATRIX_COLS) { | ||
| 195 | matrix_output_unselect_delay(); // wait for col signal to go HIGH | ||
| 196 | } | ||
| 191 | 197 | ||
| 192 | return matrix_changed; | 198 | return matrix_changed; |
| 193 | } | 199 | } |
| @@ -245,21 +251,33 @@ void matrix_init(void) { | |||
| 245 | split_post_init(); | 251 | split_post_init(); |
| 246 | } | 252 | } |
| 247 | 253 | ||
| 248 | void matrix_post_scan(void) { | 254 | bool matrix_post_scan(void) { |
| 255 | bool changed = false; | ||
| 249 | if (is_keyboard_master()) { | 256 | if (is_keyboard_master()) { |
| 250 | static uint8_t error_count; | 257 | static uint8_t error_count; |
| 251 | 258 | ||
| 252 | if (!transport_master(matrix + thatHand)) { | 259 | matrix_row_t slave_matrix[ROWS_PER_HAND] = {0}; |
| 260 | if (!transport_master(slave_matrix)) { | ||
| 253 | error_count++; | 261 | error_count++; |
| 254 | 262 | ||
| 255 | if (error_count > ERROR_DISCONNECT_COUNT) { | 263 | if (error_count > ERROR_DISCONNECT_COUNT) { |
| 256 | // reset other half if disconnected | 264 | // reset other half if disconnected |
| 257 | for (int i = 0; i < ROWS_PER_HAND; ++i) { | 265 | for (int i = 0; i < ROWS_PER_HAND; ++i) { |
| 258 | matrix[thatHand + i] = 0; | 266 | matrix[thatHand + i] = 0; |
| 267 | slave_matrix[i] = 0; | ||
| 259 | } | 268 | } |
| 269 | |||
| 270 | changed = true; | ||
| 260 | } | 271 | } |
| 261 | } else { | 272 | } else { |
| 262 | error_count = 0; | 273 | error_count = 0; |
| 274 | |||
| 275 | for (int i = 0; i < ROWS_PER_HAND; ++i) { | ||
| 276 | if (matrix[thatHand + i] != slave_matrix[i]) { | ||
| 277 | matrix[thatHand + i] = slave_matrix[i]; | ||
| 278 | changed = true; | ||
| 279 | } | ||
| 280 | } | ||
| 263 | } | 281 | } |
| 264 | 282 | ||
| 265 | matrix_scan_quantum(); | 283 | matrix_scan_quantum(); |
| @@ -268,25 +286,27 @@ void matrix_post_scan(void) { | |||
| 268 | 286 | ||
| 269 | matrix_slave_scan_user(); | 287 | matrix_slave_scan_user(); |
| 270 | } | 288 | } |
| 289 | |||
| 290 | return changed; | ||
| 271 | } | 291 | } |
| 272 | 292 | ||
| 273 | uint8_t matrix_scan(void) { | 293 | uint8_t matrix_scan(void) { |
| 274 | bool changed = false; | 294 | bool local_changed = false; |
| 275 | 295 | ||
| 276 | #if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) | 296 | #if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW) |
| 277 | // Set row, read cols | 297 | // Set row, read cols |
| 278 | for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { | 298 | for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) { |
| 279 | changed |= read_cols_on_row(raw_matrix, current_row); | 299 | local_changed |= read_cols_on_row(raw_matrix, current_row); |
| 280 | } | 300 | } |
| 281 | #elif (DIODE_DIRECTION == ROW2COL) | 301 | #elif (DIODE_DIRECTION == ROW2COL) |
| 282 | // Set col, read rows | 302 | // Set col, read rows |
| 283 | for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { | 303 | for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) { |
| 284 | changed |= read_rows_on_col(raw_matrix, current_col); | 304 | local_changed |= read_rows_on_col(raw_matrix, current_col); |
| 285 | } | 305 | } |
| 286 | #endif | 306 | #endif |
| 287 | 307 | ||
| 288 | debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed); | 308 | debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed); |
| 289 | 309 | ||
| 290 | matrix_post_scan(); | 310 | bool remote_changed = matrix_post_scan(); |
| 291 | return (uint8_t)changed; | 311 | return (uint8_t)(local_changed || remote_changed); |
| 292 | } | 312 | } |
diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c index 467ff81a9..b45ba92c3 100644 --- a/quantum/split_common/transport.c +++ b/quantum/split_common/transport.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "quantum.h" | 6 | #include "quantum.h" |
| 7 | 7 | ||
| 8 | #define ROWS_PER_HAND (MATRIX_ROWS / 2) | 8 | #define ROWS_PER_HAND (MATRIX_ROWS / 2) |
| 9 | #define SYNC_TIMER_OFFSET 2 | ||
| 9 | 10 | ||
| 10 | #ifdef RGBLIGHT_ENABLE | 11 | #ifdef RGBLIGHT_ENABLE |
| 11 | # include "rgblight.h" | 12 | # include "rgblight.h" |
| @@ -27,8 +28,20 @@ static pin_t encoders_pad[] = ENCODERS_PAD_A; | |||
| 27 | # include "i2c_slave.h" | 28 | # include "i2c_slave.h" |
| 28 | 29 | ||
| 29 | typedef struct _I2C_slave_buffer_t { | 30 | typedef struct _I2C_slave_buffer_t { |
| 31 | # ifndef DISABLE_SYNC_TIMER | ||
| 32 | uint32_t sync_timer; | ||
| 33 | # endif | ||
| 30 | matrix_row_t smatrix[ROWS_PER_HAND]; | 34 | matrix_row_t smatrix[ROWS_PER_HAND]; |
| 31 | uint8_t backlight_level; | 35 | # ifdef SPLIT_MODS_ENABLE |
| 36 | uint8_t real_mods; | ||
| 37 | uint8_t weak_mods; | ||
| 38 | # ifndef NO_ACTION_ONESHOT | ||
| 39 | uint8_t oneshot_mods; | ||
| 40 | # endif | ||
| 41 | # endif | ||
| 42 | # ifdef BACKLIGHT_ENABLE | ||
| 43 | uint8_t backlight_level; | ||
| 44 | # endif | ||
| 32 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) | 45 | # if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT) |
| 33 | rgblight_syncinfo_t rgblight_sync; | 46 | rgblight_syncinfo_t rgblight_sync; |
| 34 | # endif | 47 | # endif |
| @@ -42,9 +55,13 @@ typedef struct _I2C_slave_buffer_t { | |||
| 42 | 55 | ||
| 43 | static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; | 56 | static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg; |
| 44 | 57 | ||
| 58 | # define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer) | ||
| 59 | # define I2C_KEYMAP_START offsetof(I2C_slave_buffer_t, smatrix) | ||
| 60 | # define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods) | ||
| 61 | # define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods) | ||
| 62 | # define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods) | ||
| 45 | # define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level) | 63 | # define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level) |
| 46 | # define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync) | 64 | # define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync) |
| 47 | # define I2C_KEYMAP_START offsetof(I2C_slave_buffer_t, smatrix) | ||
| 48 | # define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state) | 65 | # define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state) |
| 49 | # define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm) | 66 | # define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm) |
| 50 | 67 | ||
| @@ -91,10 +108,43 @@ bool transport_master(matrix_row_t matrix[]) { | |||
| 91 | } | 108 | } |
| 92 | } | 109 | } |
| 93 | # endif | 110 | # endif |
| 111 | |||
| 112 | # ifdef SPLIT_MODS_ENABLE | ||
| 113 | uint8_t real_mods = get_mods(); | ||
| 114 | if (real_mods != i2c_buffer->real_mods) { | ||
| 115 | if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) { | ||
| 116 | i2c_buffer->real_mods = real_mods; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | uint8_t weak_mods = get_weak_mods(); | ||
| 121 | if (weak_mods != i2c_buffer->weak_mods) { | ||
| 122 | if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) { | ||
| 123 | i2c_buffer->weak_mods = weak_mods; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | # ifndef NO_ACTION_ONESHOT | ||
| 128 | uint8_t oneshot_mods = get_oneshot_mods(); | ||
| 129 | if (oneshot_mods != i2c_buffer->oneshot_mods) { | ||
| 130 | if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) { | ||
| 131 | i2c_buffer->oneshot_mods = oneshot_mods; | ||
| 132 | } | ||
| 133 | } | ||
| 134 | # endif | ||
| 135 | # endif | ||
| 136 | |||
| 137 | # ifndef DISABLE_SYNC_TIMER | ||
| 138 | i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; | ||
| 139 | i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT); | ||
| 140 | # endif | ||
| 94 | return true; | 141 | return true; |
| 95 | } | 142 | } |
| 96 | 143 | ||
| 97 | void transport_slave(matrix_row_t matrix[]) { | 144 | void transport_slave(matrix_row_t matrix[]) { |
| 145 | # ifndef DISABLE_SYNC_TIMER | ||
| 146 | sync_timer_update(i2c_buffer->sync_timer); | ||
| 147 | # endif | ||
| 98 | // Copy matrix to I2C buffer | 148 | // Copy matrix to I2C buffer |
| 99 | memcpy((void *)i2c_buffer->smatrix, (void *)matrix, sizeof(i2c_buffer->smatrix)); | 149 | memcpy((void *)i2c_buffer->smatrix, (void *)matrix, sizeof(i2c_buffer->smatrix)); |
| 100 | 150 | ||
| @@ -118,6 +168,14 @@ void transport_slave(matrix_row_t matrix[]) { | |||
| 118 | # ifdef WPM_ENABLE | 168 | # ifdef WPM_ENABLE |
| 119 | set_current_wpm(i2c_buffer->current_wpm); | 169 | set_current_wpm(i2c_buffer->current_wpm); |
| 120 | # endif | 170 | # endif |
| 171 | |||
| 172 | # ifdef SPLIT_MODS_ENABLE | ||
| 173 | set_mods(i2c_buffer->real_mods); | ||
| 174 | set_weak_mods(i2c_buffer->weak_mods); | ||
| 175 | # ifndef NO_ACTION_ONESHOT | ||
| 176 | set_oneshot_mods(i2c_buffer->oneshot_mods); | ||
| 177 | # endif | ||
| 178 | # endif | ||
| 121 | } | 179 | } |
| 122 | 180 | ||
| 123 | void transport_master_init(void) { i2c_init(); } | 181 | void transport_master_init(void) { i2c_init(); } |
| @@ -139,11 +197,21 @@ typedef struct _Serial_s2m_buffer_t { | |||
| 139 | } Serial_s2m_buffer_t; | 197 | } Serial_s2m_buffer_t; |
| 140 | 198 | ||
| 141 | typedef struct _Serial_m2s_buffer_t { | 199 | typedef struct _Serial_m2s_buffer_t { |
| 200 | # ifdef SPLIT_MODS_ENABLE | ||
| 201 | uint8_t real_mods; | ||
| 202 | uint8_t weak_mods; | ||
| 203 | # ifndef NO_ACTION_ONESHOT | ||
| 204 | uint8_t oneshot_mods; | ||
| 205 | # endif | ||
| 206 | # endif | ||
| 207 | # ifndef DISABLE_SYNC_TIMER | ||
| 208 | uint32_t sync_timer; | ||
| 209 | # endif | ||
| 142 | # ifdef BACKLIGHT_ENABLE | 210 | # ifdef BACKLIGHT_ENABLE |
| 143 | uint8_t backlight_level; | 211 | uint8_t backlight_level; |
| 144 | # endif | 212 | # endif |
| 145 | # ifdef WPM_ENABLE | 213 | # ifdef WPM_ENABLE |
| 146 | uint8_t current_wpm; | 214 | uint8_t current_wpm; |
| 147 | # endif | 215 | # endif |
| 148 | } Serial_m2s_buffer_t; | 216 | } Serial_m2s_buffer_t; |
| 149 | 217 | ||
| @@ -249,13 +317,28 @@ bool transport_master(matrix_row_t matrix[]) { | |||
| 249 | 317 | ||
| 250 | # ifdef WPM_ENABLE | 318 | # ifdef WPM_ENABLE |
| 251 | // Write wpm to slave | 319 | // Write wpm to slave |
| 252 | serial_m2s_buffer.current_wpm = get_current_wpm(); | 320 | serial_m2s_buffer.current_wpm = get_current_wpm(); |
| 321 | # endif | ||
| 322 | |||
| 323 | # ifdef SPLIT_MODS_ENABLE | ||
| 324 | serial_m2s_buffer.real_mods = get_mods(); | ||
| 325 | serial_m2s_buffer.weak_mods = get_weak_mods(); | ||
| 326 | # ifndef NO_ACTION_ONESHOT | ||
| 327 | serial_m2s_buffer.oneshot_mods = get_oneshot_mods(); | ||
| 328 | # endif | ||
| 329 | # endif | ||
| 330 | # ifndef DISABLE_SYNC_TIMER | ||
| 331 | serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET; | ||
| 253 | # endif | 332 | # endif |
| 254 | return true; | 333 | return true; |
| 255 | } | 334 | } |
| 256 | 335 | ||
| 257 | void transport_slave(matrix_row_t matrix[]) { | 336 | void transport_slave(matrix_row_t matrix[]) { |
| 258 | transport_rgblight_slave(); | 337 | transport_rgblight_slave(); |
| 338 | # ifndef DISABLE_SYNC_TIMER | ||
| 339 | sync_timer_update(serial_m2s_buffer.sync_timer); | ||
| 340 | # endif | ||
| 341 | |||
| 259 | // TODO: if MATRIX_COLS > 8 change to pack() | 342 | // TODO: if MATRIX_COLS > 8 change to pack() |
| 260 | for (int i = 0; i < ROWS_PER_HAND; ++i) { | 343 | for (int i = 0; i < ROWS_PER_HAND; ++i) { |
| 261 | serial_s2m_buffer.smatrix[i] = matrix[i]; | 344 | serial_s2m_buffer.smatrix[i] = matrix[i]; |
| @@ -271,6 +354,14 @@ void transport_slave(matrix_row_t matrix[]) { | |||
| 271 | # ifdef WPM_ENABLE | 354 | # ifdef WPM_ENABLE |
| 272 | set_current_wpm(serial_m2s_buffer.current_wpm); | 355 | set_current_wpm(serial_m2s_buffer.current_wpm); |
| 273 | # endif | 356 | # endif |
| 357 | |||
| 358 | # ifdef SPLIT_MODS_ENABLE | ||
| 359 | set_mods(serial_m2s_buffer.real_mods); | ||
| 360 | set_weak_mods(serial_m2s_buffer.weak_mods); | ||
| 361 | # ifndef NO_ACTION_ONESHOT | ||
| 362 | set_oneshot_mods(serial_m2s_buffer.oneshot_mods); | ||
| 363 | # endif | ||
| 364 | # endif | ||
| 274 | } | 365 | } |
| 275 | 366 | ||
| 276 | #endif | 367 | #endif |
