summaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/audio/audio.c539
-rw-r--r--quantum/audio/audio.h280
-rw-r--r--quantum/audio/audio_avr.c810
-rw-r--r--quantum/audio/audio_chibios.c702
-rw-r--r--quantum/audio/audio_pwm.c595
-rw-r--r--quantum/audio/driver_avr_pwm.h17
-rw-r--r--quantum/audio/driver_avr_pwm_hardware.c322
-rw-r--r--quantum/audio/driver_chibios_dac.h126
-rw-r--r--quantum/audio/driver_chibios_dac_additive.c335
-rw-r--r--quantum/audio/driver_chibios_dac_basic.c245
-rw-r--r--quantum/audio/driver_chibios_pwm.h40
-rw-r--r--quantum/audio/driver_chibios_pwm_hardware.c144
-rw-r--r--quantum/audio/driver_chibios_pwm_software.c164
-rw-r--r--quantum/audio/musical_notes.h82
-rw-r--r--quantum/audio/voices.c170
-rw-r--r--quantum/audio/voices.h21
-rw-r--r--quantum/audio/wave.h36
-rw-r--r--quantum/backlight/backlight_avr.c6
-rw-r--r--quantum/command.c12
-rw-r--r--quantum/debounce/sym_defer_pk.c6
-rw-r--r--quantum/debounce/sym_eager_pk.c6
-rw-r--r--quantum/debounce/sym_eager_pr.c6
-rw-r--r--quantum/dynamic_keymap.c2
-rw-r--r--quantum/encoder.c22
-rw-r--r--quantum/encoder.h2
-rw-r--r--quantum/fauxclicky.c59
-rw-r--r--quantum/fauxclicky.h97
-rw-r--r--quantum/keymap_extras/keymap_us_extended.h227
-rw-r--r--quantum/keymap_extras/keymap_us_international.h20
-rw-r--r--quantum/keymap_extras/keymap_us_international_linux.h224
-rw-r--r--quantum/keymap_extras/sendstring_us_international.h100
-rw-r--r--quantum/matrix.c14
-rw-r--r--quantum/matrix.h3
-rw-r--r--quantum/matrix_common.c7
-rw-r--r--quantum/mcu_selection.mk70
-rw-r--r--quantum/mousekey.c488
-rw-r--r--quantum/mousekey.h179
-rw-r--r--quantum/quantum.c62
-rw-r--r--quantum/quantum.h38
-rw-r--r--quantum/quantum_keycodes.h10
-rw-r--r--quantum/rgb_matrix.c6
-rw-r--r--quantum/rgblight.c100
-rw-r--r--quantum/rgblight.h2
-rw-r--r--quantum/split_common/matrix.c44
-rw-r--r--quantum/split_common/transport.c101
45 files changed, 3981 insertions, 2560 deletions
diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c
new file mode 100644
index 0000000000..46277dd70b
--- /dev/null
+++ b/quantum/audio/audio.c
@@ -0,0 +1,539 @@
+/* Copyright 2016-2020 Jack Humbert
+ * Copyright 2020 JohSchneider
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "audio.h"
+#include "eeconfig.h"
+#include "timer.h"
+#include "wait.h"
+
+/* audio system:
+ *
+ * audio.[ch] takes care of all overall state, tracking the actively playing
+ * notes/tones; the notes a SONG consists of;
+ * ...
+ * = everything audio-related that is platform agnostic
+ *
+ * driver_[avr|chibios]_[dac|pwm] take care of the lower hardware dependent parts,
+ * specific to each platform and the used subsystem/driver to drive
+ * the output pins/channels with the calculated frequencies for each
+ * active tone
+ * as part of this, the driver has to trigger regular state updates by
+ * calling 'audio_update_state' through some sort of timer - be it a
+ * dedicated one or piggybacking on for example the timer used to
+ * generate a pwm signal/clock.
+ *
+ *
+ * A Note on terminology:
+ * tone, pitch and frequency are used somewhat interchangeably, in a strict Wikipedia-sense:
+ * "(Musical) tone, a sound characterized by its duration, pitch (=frequency),
+ * intensity (=volume), and timbre"
+ * - intensity/volume is currently not handled at all, although the 'dac_additive' driver could do so
+ * - timbre is handled globally (TODO: only used with the pwm drivers at the moment)
+ *
+ * in musical_note.h a 'note' is the combination of a pitch and a duration
+ * these are used to create SONG arrays; during playback their frequencies
+ * are handled as single successive tones, while the durations are
+ * kept track of in 'audio_update_state'
+ *
+ * 'voice' as it is used here, equates to a sort of instrument with its own
+ * characteristics sound and effects
+ * the audio system as-is deals only with (possibly multiple) tones of one
+ * instrument/voice at a time (think: chords). since the number of tones that
+ * can be reproduced depends on the hardware/driver in use: pwm can only
+ * reproduce one tone per output/speaker; DACs can reproduce/mix multiple
+ * when doing additive synthesis.
+ *
+ * 'duration' can either be in the beats-per-minute related unit found in
+ * musical_notes.h, OR in ms; keyboards create SONGs with the former, while
+ * the internal state of the audio system does its calculations with the later - ms
+ */
+
+#ifndef AUDIO_TONE_STACKSIZE
+# define AUDIO_TONE_STACKSIZE 8
+#endif
+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
+musical_tone_t tones[AUDIO_TONE_STACKSIZE]; // stack of currently active tones
+
+bool playing_melody = false; // playing a SONG?
+bool playing_note = false; // or (possibly multiple simultaneous) tones
+bool state_changed = false; // global flag, which is set if anything changes with the active_tones
+
+// melody/SONG related state variables
+float (*notes_pointer)[][2]; // SONG, an array of MUSICAL_NOTEs
+uint16_t notes_count; // length of the notes_pointer array
+bool notes_repeat; // PLAY_SONG or PLAY_LOOP?
+uint16_t melody_current_note_duration = 0; // duration of the currently playing note from the active melody, in ms
+uint8_t note_tempo = TEMPO_DEFAULT; // beats-per-minute
+uint16_t current_note = 0; // index into the array at notes_pointer
+bool note_resting = false; // if a short pause was introduced between two notes with the same frequency while playing a melody
+uint16_t last_timestamp = 0;
+
+#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
+# ifndef AUDIO_MAX_SIMULTANEOUS_TONES
+# define AUDIO_MAX_SIMULTANEOUS_TONES 3
+# endif
+uint16_t tone_multiplexing_rate = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT;
+uint8_t tone_multiplexing_index_shift = 0; // offset used on active-tone array access
+#endif
+
+// provided and used by voices.c
+extern uint8_t note_timbre;
+extern bool glissando;
+extern bool vibrato;
+extern uint16_t voices_timer;
+
+#ifndef STARTUP_SONG
+# define STARTUP_SONG SONG(STARTUP_SOUND)
+#endif
+#ifndef AUDIO_ON_SONG
+# define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND)
+#endif
+#ifndef AUDIO_OFF_SONG
+# define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND)
+#endif
+float startup_song[][2] = STARTUP_SONG;
+float audio_on_song[][2] = AUDIO_ON_SONG;
+float audio_off_song[][2] = AUDIO_OFF_SONG;
+
+static bool audio_initialized = false;
+static bool audio_driver_stopped = true;
+audio_config_t audio_config;
+
+void audio_init() {
+ if (audio_initialized) {
+ return;
+ }
+
+ // Check EEPROM
+#ifdef EEPROM_ENABLE
+ if (!eeconfig_is_enabled()) {
+ eeconfig_init();
+ }
+ audio_config.raw = eeconfig_read_audio();
+#else // EEPROM settings
+ audio_config.enable = true;
+# ifdef AUDIO_CLICKY_ON
+ audio_config.clicky_enable = true;
+# endif
+#endif // EEPROM settings
+
+ for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) {
+ tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
+ }
+
+ if (!audio_initialized) {
+ audio_driver_initialize();
+ audio_initialized = true;
+ }
+ stop_all_notes();
+}
+
+void audio_startup(void) {
+ if (audio_config.enable) {
+ PLAY_SONG(startup_song);
+ }
+
+ last_timestamp = timer_read();
+}
+
+void audio_toggle(void) {
+ if (audio_config.enable) {
+ stop_all_notes();
+ }
+ audio_config.enable ^= 1;
+ eeconfig_update_audio(audio_config.raw);
+ if (audio_config.enable) {
+ audio_on_user();
+ }
+}
+
+void audio_on(void) {
+ audio_config.enable = 1;
+ eeconfig_update_audio(audio_config.raw);
+ audio_on_user();
+ PLAY_SONG(audio_on_song);
+}
+
+void audio_off(void) {
+ PLAY_SONG(audio_off_song);
+ wait_ms(100);
+ audio_stop_all();
+ audio_config.enable = 0;
+ eeconfig_update_audio(audio_config.raw);
+}
+
+bool audio_is_on(void) { return (audio_config.enable != 0); }
+
+void audio_stop_all() {
+ if (audio_driver_stopped) {
+ return;
+ }
+
+ active_tones = 0;
+
+ audio_driver_stop();
+
+ playing_melody = false;
+ playing_note = false;
+
+ melody_current_note_duration = 0;
+
+ for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) {
+ tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
+ }
+
+ audio_driver_stopped = true;
+}
+
+void audio_stop_tone(float pitch) {
+ if (pitch < 0.0f) {
+ pitch = -1 * pitch;
+ }
+
+ if (playing_note) {
+ if (!audio_initialized) {
+ audio_init();
+ }
+ bool found = false;
+ for (int i = AUDIO_TONE_STACKSIZE - 1; i >= 0; i--) {
+ found = (tones[i].pitch == pitch);
+ if (found) {
+ tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
+ for (int j = i; (j < AUDIO_TONE_STACKSIZE - 1); j++) {
+ tones[j] = tones[j + 1];
+ tones[j + 1] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};
+ }
+ break;
+ }
+ }
+ if (!found) {
+ return;
+ }
+
+ state_changed = true;
+ active_tones--;
+ if (active_tones < 0) active_tones = 0;
+#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
+ if (tone_multiplexing_index_shift >= active_tones) {
+ tone_multiplexing_index_shift = 0;
+ }
+#endif
+ if (active_tones == 0) {
+ audio_driver_stop();
+ audio_driver_stopped = true;
+ playing_note = false;
+ }
+ }
+}
+
+void audio_play_note(float pitch, uint16_t duration) {
+ if (!audio_config.enable) {
+ return;
+ }
+
+ if (!audio_initialized) {
+ audio_init();
+ }
+
+ if (pitch < 0.0f) {
+ pitch = -1 * pitch;
+ }
+
+ // round-robin: shifting out old tones, keeping only unique ones
+ // if the new frequency is already amongst the active tones, shift it to the top of the stack
+ bool found = false;
+ for (int i = active_tones - 1; i >= 0; i--) {
+ found = (tones[i].pitch == pitch);
+ if (found) {
+ for (int j = i; (j < active_tones - 1); j++) {
+ tones[j] = tones[j + 1];
+ tones[j + 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration};
+ }
+ return; // since this frequency played already, the hardware was already started
+ }
+ }
+
+ // frequency/tone is actually new, so we put it on the top of the stack
+ active_tones++;
+ if (active_tones > AUDIO_TONE_STACKSIZE) {
+ active_tones = AUDIO_TONE_STACKSIZE;
+ // shift out the oldest tone to make room
+ for (int i = 0; i < active_tones - 1; i++) {
+ tones[i] = tones[i + 1];
+ }
+ }
+ state_changed = true;
+ playing_note = true;
+ tones[active_tones - 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration};
+
+ // TODO: needs to be handled per note/tone -> use its timestamp instead?
+ voices_timer = timer_read(); // reset to zero, for the effects added by voices.c
+
+ if (audio_driver_stopped) {
+ audio_driver_start();
+ audio_driver_stopped = false;
+ }
+}
+
+void audio_play_tone(float pitch) { audio_play_note(pitch, 0xffff); }
+
+void audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat) {
+ if (!audio_config.enable) {
+ audio_stop_all();
+ return;
+ }
+
+ if (!audio_initialized) {
+ audio_init();
+ }
+
+ // Cancel note if a note is playing
+ if (playing_note) audio_stop_all();
+
+ playing_melody = true;
+ note_resting = false;
+
+ notes_pointer = np;
+ notes_count = n_count;
+ notes_repeat = n_repeat;
+
+ current_note = 0; // note in the melody-array/list at note_pointer
+
+ // start first note manually, which also starts the audio_driver
+ // all following/remaining notes are played by 'audio_update_state'
+ audio_play_note((*notes_pointer)[current_note][0], audio_duration_to_ms((*notes_pointer)[current_note][1]));
+ last_timestamp = timer_read();
+ melody_current_note_duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);
+}
+
+float click[2][2];
+void audio_play_click(uint16_t delay, float pitch, uint16_t duration) {
+ uint16_t duration_tone = audio_ms_to_duration(duration);
+ uint16_t duration_delay = audio_ms_to_duration(delay);
+
+ if (delay <= 0.0f) {
+ click[0][0] = pitch;
+ click[0][1] = duration_tone;
+ click[1][0] = 0.0f;
+ click[1][1] = 0.0f;
+ audio_play_melody(&click, 1, false);
+ } else {
+ // first note is a rest/pause
+ click[0][0] = 0.0f;
+ click[0][1] = duration_delay;
+ // second note is the actual click
+ click[1][0] = pitch;
+ click[1][1] = duration_tone;
+ audio_play_melody(&click, 2, false);
+ }
+}
+
+bool audio_is_playing_note(void) { return playing_note; }
+
+bool audio_is_playing_melody(void) { return playing_melody; }
+
+uint8_t audio_get_number_of_active_tones(void) { return active_tones; }
+
+float audio_get_frequency(uint8_t tone_index) {
+ if (tone_index >= active_tones) {
+ return 0.0f;
+ }
+ return tones[active_tones - tone_index - 1].pitch;
+}
+
+float audio_get_processed_frequency(uint8_t tone_index) {
+ if (tone_index >= active_tones) {
+ return 0.0f;
+ }
+
+ int8_t index = active_tones - tone_index - 1;
+ // new tones are stacked on top (= appended at the end), so the most recent/current is MAX-1
+
+#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
+ index = index - tone_multiplexing_index_shift;
+ if (index < 0) // wrap around
+ index += active_tones;
+#endif
+
+ if (tones[index].pitch <= 0.0f) {
+ return 0.0f;
+ }
+
+ return voice_envelope(tones[index].pitch);
+}
+
+bool audio_update_state(void) {
+ if (!playing_note && !playing_melody) {
+ return false;
+ }
+
+ bool goto_next_note = false;
+ uint16_t current_time = timer_read();
+
+ if (playing_melody) {
+ goto_next_note = timer_elapsed(last_timestamp) >= melody_current_note_duration;
+ if (goto_next_note) {
+ uint16_t delta = timer_elapsed(last_timestamp) - melody_current_note_duration;
+ last_timestamp = current_time;
+ uint16_t previous_note = current_note;
+ current_note++;
+ voices_timer = timer_read(); // reset to zero, for the effects added by voices.c
+
+ if (current_note >= notes_count) {
+ if (notes_repeat) {
+ current_note = 0;
+ } else {
+ audio_stop_all();
+ return false;
+ }
+ }
+
+ if (!note_resting && (*notes_pointer)[previous_note][0] == (*notes_pointer)[current_note][0]) {
+ note_resting = true;
+
+ // special handling for successive notes of the same frequency:
+ // insert a short pause to separate them audibly
+ audio_play_note(0.0f, audio_duration_to_ms(2));
+ current_note = previous_note;
+ melody_current_note_duration = audio_duration_to_ms(2);
+
+ } else {
+ note_resting = false;
+
+ // TODO: handle glissando here (or remember previous and current tone)
+ /* there would need to be a freq(here we are) -> freq(next note)
+ * and do slide/glissando in between problem here is to know which
+ * frequency on the stack relates to what other? e.g. a melody starts
+ * tones in a sequence, and stops expiring one, so the most recently
+ * stopped is the starting point for a glissando to the most recently started?
+ * how to detect and preserve this relation?
+ * and what about user input, chords, ...?
+ */
+
+ // '- delta': Skip forward in the next note's length if we've over shot
+ // the last, so the overall length of the song is the same
+ uint16_t duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);
+
+ // Skip forward past any completely missed notes
+ while (delta > duration && current_note < notes_count - 1) {
+ delta -= duration;
+ current_note++;
+ duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);
+ }
+
+ if (delta < duration) {
+ duration -= delta;
+ } else {
+ // Only way to get here is if it is the last note and
+ // we have completely missed it. Play it for 1ms...
+ duration = 1;
+ }
+
+ audio_play_note((*notes_pointer)[current_note][0], duration);
+ melody_current_note_duration = duration;
+ }
+ }
+ }
+
+ if (playing_note) {
+#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
+ tone_multiplexing_index_shift = (int)(current_time / tone_multiplexing_rate) % MIN(AUDIO_MAX_SIMULTANEOUS_TONES, active_tones);
+ goto_next_note = true;
+#endif
+ if (vibrato || glissando) {
+ // force update on each cycle, since vibrato shifts the frequency slightly
+ goto_next_note = true;
+ }
+
+ // housekeeping: stop notes that have no playtime left
+ for (int i = 0; i < active_tones; i++) {
+ if ((tones[i].duration != 0xffff) // indefinitely playing notes, started by 'audio_play_tone'
+ && (tones[i].duration != 0) // 'uninitialized'
+ ) {
+ if (timer_elapsed(tones[i].time_started) >= tones[i].duration) {
+ audio_stop_tone(tones[i].pitch); // also sets 'state_changed=true'
+ }
+ }
+ }
+ }
+
+ // state-changes have a higher priority, always triggering the hardware to update
+ if (state_changed) {
+ state_changed = false;
+ return true;
+ }
+
+ return goto_next_note;
+}
+
+// Tone-multiplexing functions
+#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
+void audio_set_tone_multiplexing_rate(uint16_t rate) { tone_multiplexing_rate = rate; }
+void audio_enable_tone_multiplexing(void) { tone_multiplexing_rate = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT; }
+void audio_disable_tone_multiplexing(void) { tone_multiplexing_rate = 0; }
+void audio_increase_tone_multiplexing_rate(uint16_t change) {
+ if ((0xffff - change) > tone_multiplexing_rate) {
+ tone_multiplexing_rate += change;
+ }
+}
+void audio_decrease_tone_multiplexing_rate(uint16_t change) {
+ if (change <= tone_multiplexing_rate) {
+ tone_multiplexing_rate -= change;
+ }
+}
+#endif
+
+// Tempo functions
+
+void audio_set_tempo(uint8_t tempo) {
+ if (tempo < 10) note_tempo = 10;
+ // else if (tempo > 250)
+ // note_tempo = 250;
+ else
+ note_tempo = tempo;
+}
+
+void audio_increase_tempo(uint8_t tempo_change) {
+ if (tempo_change > 255 - note_tempo)
+ note_tempo = 255;
+ else
+ note_tempo += tempo_change;
+}
+
+void audio_decrease_tempo(uint8_t tempo_change) {
+ if (tempo_change >= note_tempo - 10)
+ note_tempo = 10;
+ else
+ note_tempo -= tempo_change;
+}
+
+// TODO in the int-math version are some bugs; songs sometimes abruptly end - maybe an issue with the timer/system-tick wrapping around?
+uint16_t audio_duration_to_ms(uint16_t duration_bpm) {
+#if defined(__AVR__)
+ // 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
+ return ((uint32_t)duration_bpm * 60 * 1000) / (64 * note_tempo);
+ // NOTE: beware of uint16_t overflows when note_tempo is low and/or the duration is long
+#else
+ return ((float)duration_bpm * 60) / (64 * note_tempo) * 1000;
+#endif
+}
+uint16_t audio_ms_to_duration(uint16_t duration_ms) {
+#if defined(__AVR__)
+ return ((uint32_t)duration_ms * 64 * note_tempo) / 60 / 1000;
+#else
+ return ((float)duration_ms * 64 * note_tempo) / 60 / 1000;
+#endif
+}
diff --git a/quantum/audio/audio.h b/quantum/audio/audio.h
index bc00cd19e6..56b9158a1a 100644
--- a/quantum/audio/audio.h
+++ b/quantum/audio/audio.h
@@ -1,4 +1,5 @@
-/* Copyright 2016 Jack Humbert
+/* Copyright 2016-2020 Jack Humbert
+ * Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -13,28 +14,30 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
#pragma once
#include <stdint.h>
#include <stdbool.h>
-#if defined(__AVR__)
-# include <avr/io.h>
-#endif
-#include "wait.h"
#include "musical_notes.h"
#include "song_list.h"
#include "voices.h"
#include "quantum.h"
#include <math.h>
-// Largely untested PWM audio mode (doesn't sound as good)
-// #define PWM_AUDIO
-
-// #define VIBRATO_ENABLE
+#if defined(__AVR__)
+# include <avr/io.h>
+# if defined(AUDIO_DRIVER_PWM)
+# include "driver_avr_pwm.h"
+# endif
+#endif
-// Enable vibrato strength/amplitude - slows down ISR too much
-// #define VIBRATO_STRENGTH_ENABLE
+#if defined(PROTOCOL_CHIBIOS)
+# if defined(AUDIO_DRIVER_PWM)
+# include "driver_chibios_pwm.h"
+# elif defined(AUDIO_DRIVER_DAC)
+# include "driver_chibios_dac.h"
+# endif
+#endif
typedef union {
uint8_t raw;
@@ -45,61 +48,238 @@ typedef union {
};
} audio_config_t;
-bool is_audio_on(void);
+// AVR/LUFA has a MIN, arm/chibios does not
+#ifndef MIN
+# define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/*
+ * a 'musical note' is represented by pitch and duration; a 'musical tone' adds intensity and timbre
+ * https://en.wikipedia.org/wiki/Musical_tone
+ * "A musical tone is characterized by its duration, pitch, intensity (or loudness), and timbre (or quality)"
+ */
+typedef struct {
+ 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
+ float pitch; // aka frequency, in Hz
+ 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
+ // float intensity; // aka volume [0,1] TODO: not used at the moment; pwm drivers can't handle it
+ // uint8_t timbre; // range: [0,100] TODO: this currently kept track of globally, should we do this per tone instead?
+} musical_tone_t;
+
+// public interface
+
+/**
+ * @brief one-time initialization called by quantum/quantum.c
+ * @details usually done lazy, when some tones are to be played
+ *
+ * @post audio system (and hardware) initialized and ready to play tones
+ */
+void audio_init(void);
+void audio_startup(void);
+
+/**
+ * @brief en-/disable audio output, save this choice to the eeprom
+ */
void audio_toggle(void);
+/**
+ * @brief enable audio output, save this choice to the eeprom
+ */
void audio_on(void);
+/**
+ * @brief disable audio output, save this choice to the eeprom
+ */
void audio_off(void);
+/**
+ * @brief query the if audio output is enabled
+ */
+bool audio_is_on(void);
+
+/**
+ * @brief start playback of a tone with the given frequency and duration
+ *
+ * @details starts the playback of a given note, which is automatically stopped
+ * at the the end of its duration = fire&forget
+ *
+ * @param[in] pitch frequency of the tone be played
+ * @param[in] duration in milliseconds, use 'audio_duration_to_ms' to convert
+ * from the musical_notes.h unit to ms
+ */
+void audio_play_note(float pitch, uint16_t duration);
+// TODO: audio_play_note(float pitch, uint16_t duration, float intensity, float timbre);
+// audio_play_note_with_instrument ifdef AUDIO_ENABLE_VOICES
+
+/**
+ * @brief start playback of a tone with the given frequency
+ *
+ * @details the 'frequency' is put on-top the internal stack of active tones,
+ * as a new tone with indefinite duration. this tone is played by
+ * the hardware until a call to 'audio_stop_tone'.
+ * should a tone with that frequency already be active, its entry
+ * is put on the top of said internal stack - so no duplicate
+ * entries are kept.
+ * 'hardware_start' is called upon the first note.
+ *
+ * @param[in] pitch frequency of the tone be played
+ */
+void audio_play_tone(float pitch);
+
+/**
+ * @brief stop a given tone/frequency
+ *
+ * @details removes a tone matching the given frequency from the internal
+ * playback stack
+ * the hardware is stopped in case this was the last/only frequency
+ * being played.
+ *
+ * @param[in] pitch tone/frequency to be stopped
+ */
+void audio_stop_tone(float pitch);
-// Vibrato rate functions
+/**
+ * @brief play a melody
+ *
+ * @details starts playback of a melody passed in from a SONG definition - an
+ * array of {pitch, duration} float-tuples
+ *
+ * @param[in] np note-pointer to the SONG array
+ * @param[in] n_count number of MUSICAL_NOTES of the SONG
+ * @param[in] n_repeat false for onetime, true for looped playback
+ */
+void audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat);
-#ifdef VIBRATO_ENABLE
+/**
+ * @brief play a short tone of a specific frequency to emulate a 'click'
+ *
+ * @details constructs a two-note melody (one pause plus a note) and plays it through
+ * audio_play_melody. very short durations might not quite work due to
+ * hardware limitations (DAC: added pulses from zero-crossing feature;...)
+ *
+ * @param[in] delay in milliseconds, length for the pause before the pulses, can be zero
+ * @param[in] pitch
+ * @param[in] duration in milliseconds, length of the 'click'
+ */
+void audio_play_click(uint16_t delay, float pitch, uint16_t duration);
-void set_vibrato_rate(float rate);
-void increase_vibrato_rate(float change);
-void decrease_vibrato_rate(float change);
+/**
+ * @brief stops all playback
+ *
+ * @details stops playback of both a melody as well as single tones, resetting
+ * the internal state
+ */
+void audio_stop_all(void);
-# ifdef VIBRATO_STRENGTH_ENABLE
+/**
+ * @brief query if one/multiple tones are playing
+ */
+bool audio_is_playing_note(void);
-void set_vibrato_strength(float strength);
-void increase_vibrato_strength(float change);
-void decrease_vibrato_strength(float change);
+/**
+ * @brief query if a melody/SONG is playing
+ */
+bool audio_is_playing_melody(void);
-# endif
+// These macros are used to allow audio_play_melody to play an array of indeterminate
+// length. This works around the limitation of C's sizeof operation on pointers.
+// The global float array for the song must be used here.
+#define NOTE_ARRAY_SIZE(x) ((int16_t)(sizeof(x) / (sizeof(x[0]))))
+
+/**
+ * @brief convenience macro, to play a melody/SONG once
+ */
+#define PLAY_SONG(note_array) audio_play_melody(&note_array, NOTE_ARRAY_SIZE((note_array)), false)
+// TODO: a 'song' is a melody plus singing/vocals -> PLAY_MELODY
+/**
+ * @brief convenience macro, to play a melody/SONG in a loop, until stopped by 'audio_stop_all'
+ */
+#define PLAY_LOOP(note_array) audio_play_melody(&note_array, NOTE_ARRAY_SIZE((note_array)), true)
+// Tone-Multiplexing functions
+// this feature only makes sense for hardware setups which can't do proper
+// audio-wave synthesis = have no DAC and need to use PWM for tone generation
+#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
+# ifndef AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT
+# define AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT 0
+// 0=off, good starting value is 4; the lower the value the higher the cpu-load
+# endif
+void audio_set_tone_multiplexing_rate(uint16_t rate);
+void audio_enable_tone_multiplexing(void);
+void audio_disable_tone_multiplexing(void);
+void audio_increase_tone_multiplexing_rate(uint16_t change);
+void audio_decrease_tone_multiplexing_rate(uint16_t change);
#endif
-// Polyphony functions
+// Tempo functions
-void set_polyphony_rate(float rate);
-void enable_polyphony(void);
-void disable_polyphony(void);
-void increase_polyphony_rate(float change);
-void decrease_polyphony_rate(float change);
+void audio_set_tempo(uint8_t tempo);
+void audio_increase_tempo(uint8_t tempo_change);
+void audio_decrease_tempo(uint8_t tempo_change);
-void set_timbre(float timbre);
-void set_tempo(uint8_t tempo);
+// conversion macros, from 64parts-to-a-beat to milliseconds and back
+uint16_t audio_duration_to_ms(uint16_t duration_bpm);
+uint16_t audio_ms_to_duration(uint16_t duration_ms);
-void increase_tempo(uint8_t tempo_change);
-void decrease_tempo(uint8_t tempo_change);
+void audio_startup(void);
-void audio_init(void);
+// hardware interface
-#ifdef PWM_AUDIO
-void play_sample(uint8_t* s, uint16_t l, bool r);
-#endif
-void play_note(float freq, int vol);
-void stop_note(float freq);
-void stop_all_notes(void);
-void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat);
+// implementation in the driver_avr/arm_* respective parts
+void audio_driver_initialize(void);
+void audio_driver_start(void);
+void audio_driver_stop(void);
-#define SCALE \
- (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), }
+/**
+ * @brief get the number of currently active tones
+ * @return number, 0=none active
+ */
+uint8_t audio_get_number_of_active_tones(void);
-// These macros are used to allow play_notes to play an array of indeterminate
-// length. This works around the limitation of C's sizeof operation on pointers.
-// The global float array for the song must be used here.
-#define NOTE_ARRAY_SIZE(x) ((int16_t)(sizeof(x) / (sizeof(x[0]))))
-#define PLAY_SONG(note_array) play_notes(&note_array, NOTE_ARRAY_SIZE((note_array)), false)
-#define PLAY_LOOP(note_array) play_notes(&note_array, NOTE_ARRAY_SIZE((note_array)), true)
+/**
+ * @brief access to the raw/unprocessed frequency for a specific tone
+ * @details each active tone has a frequency associated with it, which
+ * the internal state keeps track of, and is usually influenced
+ * by various effects
+ * @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the
+ * first being the most recent and each increment yielding the next
+ * older one
+ * @return a positive frequency, in Hz; or zero if the tone is a pause
+ */
+float audio_get_frequency(uint8_t tone_index);
+
+/**
+ * @brief calculate and return the frequency for the requested tone
+ * @details effects like glissando, vibrato, ... are post-processed onto the
+ * each active tones 'base'-frequency; this function returns the
+ * post-processed result.
+ * @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the
+ * first being the most recent and each increment yielding the next
+ * older one
+ * @return a positive frequency, in Hz; or zero if the tone is a pause
+ */
+float audio_get_processed_frequency(uint8_t tone_index);
+
+/**
+ * @brief update audio internal state: currently playing and active tones,...
+ * @details This function is intended to be called by the audio-hardware
+ * specific implementation on a somewhat regular basis while a SONG
+ * or notes (pitch+duration) are playing to 'advance' the internal
+ * state (current playing notes, position in the melody, ...)
+ *
+ * @return true if something changed in the currently active tones, which the
+ * hardware might need to react to
+ */
+bool audio_update_state(void);
+
+// legacy and back-warts compatibility stuff
+
+#define is_audio_on() audio_is_on()
+#define is_playing_notes() audio_is_playing_melody()
+#define is_playing_note() audio_is_playing_note()
+#define stop_all_notes() audio_stop_all()
+#define stop_note(f) audio_stop_tone(f)
+#define play_note(f, v) audio_play_tone(f)
-bool is_playing_notes(void);
+#define set_timbre(t) voice_set_timbre(t)
+#define set_tempo(t) audio_set_tempo(t)
+#define increase_tempo(t) audio_increase_tempo(t)
+#define decrease_tempo(t) audio_decrease_tempo(t)
+// 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 5a96bf6439..0000000000
--- a/quantum/audio/audio_avr.c
+++ /dev/null
@@ -1,810 +0,0 @@
-/* Copyright 2016 Jack Humbert
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <string.h>
-//#include <math.h>
-#if defined(__AVR__)
-# include <avr/pgmspace.h>
-# include <avr/interrupt.h>
-# include <avr/io.h>
-#endif
-#include "print.h"
-#include "audio.h"
-#include "keymap.h"
-#include "wait.h"
-
-#include "eeconfig.h"
-
-#define CPU_PRESCALER 8
-
-// -----------------------------------------------------------------------------
-// Timer Abstractions
-// -----------------------------------------------------------------------------
-
-// Currently we support timers 1 and 3 used at the sime time, channels A-C,
-// pins PB5, PB6, PB7, PC4, PC5, and PC6
-#if defined(C6_AUDIO)
-# define CPIN_AUDIO
-# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC6);
-# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30);
-# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A)
-# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A)
-# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1);
-# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0));
-# define TIMER_3_PERIOD ICR3
-# define TIMER_3_DUTY_CYCLE OCR3A
-# define TIMER3_AUDIO_vect TIMER3_COMPA_vect
-#endif
-#if defined(C5_AUDIO)
-# define CPIN_AUDIO
-# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC5);
-# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3B1) | (0 << COM3B0) | (1 << WGM31) | (0 << WGM30);
-# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3B)
-# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3B)
-# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3B1);
-# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3B1) | _BV(COM3B0));
-# define TIMER_3_PERIOD ICR3
-# define TIMER_3_DUTY_CYCLE OCR3B
-# define TIMER3_AUDIO_vect TIMER3_COMPB_vect
-#endif
-#if defined(C4_AUDIO)
-# define CPIN_AUDIO
-# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC4);
-# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3C1) | (0 << COM3C0) | (1 << WGM31) | (0 << WGM30);
-# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3C)
-# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3C)
-# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3C1);
-# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3C1) | _BV(COM3C0));
-# define TIMER_3_PERIOD ICR3
-# define TIMER_3_DUTY_CYCLE OCR3C
-# define TIMER3_AUDIO_vect TIMER3_COMPC_vect
-#endif
-
-#if defined(B5_AUDIO)
-# define BPIN_AUDIO
-# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB5);
-# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10);
-# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A)
-# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A)
-# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1);
-# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0));
-# define TIMER_1_PERIOD ICR1
-# define TIMER_1_DUTY_CYCLE OCR1A
-# define TIMER1_AUDIO_vect TIMER1_COMPA_vect
-#endif
-#if defined(B6_AUDIO)
-# define BPIN_AUDIO
-# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB6);
-# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10);
-# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1B)
-# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1B)
-# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1B1);
-# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1B1) | _BV(COM1B0));
-# define TIMER_1_PERIOD ICR1
-# define TIMER_1_DUTY_CYCLE OCR1B
-# define TIMER1_AUDIO_vect TIMER1_COMPB_vect
-#endif
-#if defined(B7_AUDIO)
-# define BPIN_AUDIO
-# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB7);
-# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1C1) | (0 << COM1C0) | (1 << WGM11) | (0 << WGM10);
-# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1C)
-# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1C)
-# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1C1);
-# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0));
-# define TIMER_1_PERIOD ICR1
-# define TIMER_1_DUTY_CYCLE OCR1C
-# define TIMER1_AUDIO_vect TIMER1_COMPC_vect
-#endif
-
-#if !defined(BPIN_AUDIO) && !defined(CPIN_AUDIO)
-# error "Audio feature enabled, but no suitable pin selected - see docs/feature_audio.md under the AVR settings for available options."
-#endif
-
-// -----------------------------------------------------------------------------
-
-int voices = 0;
-int voice_place = 0;
-float frequency = 0;
-float frequency_alt = 0;
-int volume = 0;
-long position = 0;
-
-float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-bool sliding = false;
-
-float place = 0;
-
-uint8_t* sample;
-uint16_t sample_length = 0;
-
-bool playing_notes = false;
-bool playing_note = false;
-float note_frequency = 0;
-float note_length = 0;
-uint8_t note_tempo = TEMPO_DEFAULT;
-float note_timbre = TIMBRE_DEFAULT;
-uint16_t note_position = 0;
-float (*notes_pointer)[][2];
-uint16_t notes_count;
-bool notes_repeat;
-bool note_resting = false;
-
-uint16_t current_note = 0;
-uint8_t rest_counter = 0;
-
-#ifdef VIBRATO_ENABLE
-float vibrato_counter = 0;
-float vibrato_strength = .5;
-float vibrato_rate = 0.125;
-#endif
-
-float polyphony_rate = 0;
-
-static bool audio_initialized = false;
-
-audio_config_t audio_config;
-
-uint16_t envelope_index = 0;
-bool glissando = true;
-
-#ifndef STARTUP_SONG
-# define STARTUP_SONG SONG(STARTUP_SOUND)
-#endif
-#ifndef AUDIO_ON_SONG
-# define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND)
-#endif
-#ifndef AUDIO_OFF_SONG
-# define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND)
-#endif
-float startup_song[][2] = STARTUP_SONG;
-float audio_on_song[][2] = AUDIO_ON_SONG;
-float audio_off_song[][2] = AUDIO_OFF_SONG;
-
-void audio_init() {
- // Check EEPROM
- if (!eeconfig_is_enabled()) {
- eeconfig_init();
- }
- audio_config.raw = eeconfig_read_audio();
-
- if (!audio_initialized) {
-// Set audio ports as output
-#ifdef CPIN_AUDIO
- CPIN_SET_DIRECTION
- DISABLE_AUDIO_COUNTER_3_ISR;
-#endif
-#ifdef BPIN_AUDIO
- BPIN_SET_DIRECTION
- DISABLE_AUDIO_COUNTER_1_ISR;
-#endif
-
-// TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
-// Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
-// OC3A -- PC6
-// OC3B -- PC5
-// OC3C -- PC4
-// OC1A -- PB5
-// OC1B -- PB6
-// OC1C -- PB7
-
-// Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
-// OCR3A - PC6
-// OCR3B - PC5
-// OCR3C - PC4
-// OCR1A - PB5
-// OCR1B - PB6
-// OCR1C - PB7
-
-// Clock Select (CS3n) = 0b010 = Clock / 8
-#ifdef CPIN_AUDIO
- INIT_AUDIO_COUNTER_3
- TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30);
- TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER));
- TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre);
-#endif
-#ifdef BPIN_AUDIO
- INIT_AUDIO_COUNTER_1
- TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
- TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER));
- TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre);
-#endif
-
- audio_initialized = true;
- }
-
- if (audio_config.enable) {
- PLAY_SONG(startup_song);
- }
-}
-
-void stop_all_notes() {
- dprintf("audio stop all notes");
-
- if (!audio_initialized) {
- audio_init();
- }
- voices = 0;
-
-#ifdef CPIN_AUDIO
- DISABLE_AUDIO_COUNTER_3_ISR;
- DISABLE_AUDIO_COUNTER_3_OUTPUT;
-#endif
-
-#ifdef BPIN_AUDIO
- DISABLE_AUDIO_COUNTER_1_ISR;
- DISABLE_AUDIO_COUNTER_1_OUTPUT;
-#endif
-
- playing_notes = false;
- playing_note = false;
- frequency = 0;
- frequency_alt = 0;
- volume = 0;
-
- for (uint8_t i = 0; i < 8; i++) {
- frequencies[i] = 0;
- volumes[i] = 0;
- }
-}
-
-void stop_note(float freq) {
- dprintf("audio stop note freq=%d", (int)freq);
-
- if (playing_note) {
- if (!audio_initialized) {
- audio_init();
- }
- for (int i = 7; i >= 0; i--) {
- if (frequencies[i] == freq) {
- frequencies[i] = 0;
- volumes[i] = 0;
- for (int j = i; (j < 7); j++) {
- frequencies[j] = frequencies[j + 1];
- frequencies[j + 1] = 0;
- volumes[j] = volumes[j + 1];
- volumes[j + 1] = 0;
- }
- break;
- }
- }
- voices--;
- if (voices < 0) voices = 0;
- if (voice_place >= voices) {
- voice_place = 0;
- }
- if (voices == 0) {
-#ifdef CPIN_AUDIO
- DISABLE_AUDIO_COUNTER_3_ISR;
- DISABLE_AUDIO_COUNTER_3_OUTPUT;
-#endif
-#ifdef BPIN_AUDIO
- DISABLE_AUDIO_COUNTER_1_ISR;
- DISABLE_AUDIO_COUNTER_1_OUTPUT;
-#endif
- frequency = 0;
- frequency_alt = 0;
- volume = 0;
- playing_note = false;
- }
- }
-}
-
-#ifdef VIBRATO_ENABLE
-
-float mod(float a, int b) {
- float r = fmod(a, b);
- return r < 0 ? r + b : r;
-}
-
-float vibrato(float average_freq) {
-# ifdef VIBRATO_STRENGTH_ENABLE
- float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength);
-# else
- float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter];
-# endif
- vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH);
- return vibrated_freq;
-}
-
-#endif
-
-#ifdef CPIN_AUDIO
-ISR(TIMER3_AUDIO_vect) {
- float freq;
-
- if (playing_note) {
- if (voices > 0) {
-# ifdef BPIN_AUDIO
- float freq_alt = 0;
- if (voices > 1) {
- if (polyphony_rate == 0) {
- if (glissando) {
- if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) {
- frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2);
- } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440 / frequencies[voices - 2] / 12 / 2)) {
- frequency_alt = frequency_alt * pow(2, -440 / frequency_alt / 12 / 2);
- } else {
- frequency_alt = frequencies[voices - 2];
- }
- } else {
- frequency_alt = frequencies[voices - 2];
- }
-
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq_alt = vibrato(frequency_alt);
- } else {
- freq_alt = frequency_alt;
- }
-# else
- freq_alt = frequency_alt;
-# endif
- }
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
-
- freq_alt = voice_envelope(freq_alt);
-
- if (freq_alt < 30.517578125) {
- freq_alt = 30.52;
- }
-
- TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq_alt * CPU_PRESCALER));
- TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq_alt * CPU_PRESCALER)) * note_timbre);
- }
-# endif
-
- if (polyphony_rate > 0) {
- if (voices > 1) {
- voice_place %= voices;
- if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) {
- voice_place = (voice_place + 1) % voices;
- place = 0.0;
- }
- }
-
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(frequencies[voice_place]);
- } else {
- freq = frequencies[voice_place];
- }
-# else
- freq = frequencies[voice_place];
-# endif
- } else {
- if (glissando) {
- if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) {
- frequency = frequency * pow(2, 440 / frequency / 12 / 2);
- } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) {
- frequency = frequency * pow(2, -440 / frequency / 12 / 2);
- } else {
- frequency = frequencies[voices - 1];
- }
- } else {
- frequency = frequencies[voices - 1];
- }
-
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(frequency);
- } else {
- freq = frequency;
- }
-# else
- freq = frequency;
-# endif
- }
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
-
- freq = voice_envelope(freq);
-
- if (freq < 30.517578125) {
- freq = 30.52;
- }
-
- TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
- TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre);
- }
- }
-
- if (playing_notes) {
- if (note_frequency > 0) {
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(note_frequency);
- } else {
- freq = note_frequency;
- }
-# else
- freq = note_frequency;
-# endif
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
- freq = voice_envelope(freq);
-
- TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
- TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre);
- } else {
- TIMER_3_PERIOD = 0;
- TIMER_3_DUTY_CYCLE = 0;
- }
-
- note_position++;
- bool end_of_note = false;
- if (TIMER_3_PERIOD > 0) {
- if (!note_resting)
- end_of_note = (note_position >= (note_length / TIMER_3_PERIOD * 0xFFFF - 1));
- else
- end_of_note = (note_position >= (note_length));
- } else {
- end_of_note = (note_position >= (note_length));
- }
-
- if (end_of_note) {
- current_note++;
- if (current_note >= notes_count) {
- if (notes_repeat) {
- current_note = 0;
- } else {
- DISABLE_AUDIO_COUNTER_3_ISR;
- DISABLE_AUDIO_COUNTER_3_OUTPUT;
- playing_notes = false;
- return;
- }
- }
- if (!note_resting) {
- note_resting = true;
- current_note--;
- if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) {
- note_frequency = 0;
- note_length = 1;
- } else {
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = 1;
- }
- } else {
- note_resting = false;
- envelope_index = 0;
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100);
- }
-
- note_position = 0;
- }
- }
-
- if (!audio_config.enable) {
- playing_notes = false;
- playing_note = false;
- }
-}
-#endif
-
-#ifdef BPIN_AUDIO
-ISR(TIMER1_AUDIO_vect) {
-# if defined(BPIN_AUDIO) && !defined(CPIN_AUDIO)
- float freq = 0;
-
- if (playing_note) {
- if (voices > 0) {
- if (polyphony_rate > 0) {
- if (voices > 1) {
- voice_place %= voices;
- if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) {
- voice_place = (voice_place + 1) % voices;
- place = 0.0;
- }
- }
-
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(frequencies[voice_place]);
- } else {
- freq = frequencies[voice_place];
- }
-# else
- freq = frequencies[voice_place];
-# endif
- } else {
- if (glissando) {
- if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) {
- frequency = frequency * pow(2, 440 / frequency / 12 / 2);
- } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) {
- frequency = frequency * pow(2, -440 / frequency / 12 / 2);
- } else {
- frequency = frequencies[voices - 1];
- }
- } else {
- frequency = frequencies[voices - 1];
- }
-
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(frequency);
- } else {
- freq = frequency;
- }
-# else
- freq = frequency;
-# endif
- }
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
-
- freq = voice_envelope(freq);
-
- if (freq < 30.517578125) {
- freq = 30.52;
- }
-
- TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
- TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre);
- }
- }
-
- if (playing_notes) {
- if (note_frequency > 0) {
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(note_frequency);
- } else {
- freq = note_frequency;
- }
-# else
- freq = note_frequency;
-# endif
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
- freq = voice_envelope(freq);
-
- TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
- TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre);
- } else {
- TIMER_1_PERIOD = 0;
- TIMER_1_DUTY_CYCLE = 0;
- }
-
- note_position++;
- bool end_of_note = false;
- if (TIMER_1_PERIOD > 0) {
- if (!note_resting)
- end_of_note = (note_position >= (note_length / TIMER_1_PERIOD * 0xFFFF - 1));
- else
- end_of_note = (note_position >= (note_length));
- } else {
- end_of_note = (note_position >= (note_length));
- }
-
- if (end_of_note) {
- current_note++;
- if (current_note >= notes_count) {
- if (notes_repeat) {
- current_note = 0;
- } else {
- DISABLE_AUDIO_COUNTER_1_ISR;
- DISABLE_AUDIO_COUNTER_1_OUTPUT;
- playing_notes = false;
- return;
- }
- }
- if (!note_resting) {
- note_resting = true;
- current_note--;
- if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) {
- note_frequency = 0;
- note_length = 1;
- } else {
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = 1;
- }
- } else {
- note_resting = false;
- envelope_index = 0;
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100);
- }
-
- note_position = 0;
- }
- }
-
- if (!audio_config.enable) {
- playing_notes = false;
- playing_note = false;
- }
-# endif
-}
-#endif
-
-void play_note(float freq, int vol) {
- dprintf("audio play note freq=%d vol=%d", (int)freq, vol);
-
- if (!audio_initialized) {
- audio_init();
- }
-
- if (audio_config.enable && voices < 8) {
-#ifdef CPIN_AUDIO
- DISABLE_AUDIO_COUNTER_3_ISR;
-#endif
-#ifdef BPIN_AUDIO
- DISABLE_AUDIO_COUNTER_1_ISR;
-#endif
-
- // Cancel notes if notes are playing
- if (playing_notes) stop_all_notes();
-
- playing_note = true;
-
- envelope_index = 0;
-
- if (freq > 0) {
- frequencies[voices] = freq;
- volumes[voices] = vol;
- voices++;
- }
-
-#ifdef CPIN_AUDIO
- ENABLE_AUDIO_COUNTER_3_ISR;
- ENABLE_AUDIO_COUNTER_3_OUTPUT;
-#endif
-#ifdef BPIN_AUDIO
-# ifdef CPIN_AUDIO
- if (voices > 1) {
- ENABLE_AUDIO_COUNTER_1_ISR;
- ENABLE_AUDIO_COUNTER_1_OUTPUT;
- }
-# else
- ENABLE_AUDIO_COUNTER_1_ISR;
- ENABLE_AUDIO_COUNTER_1_OUTPUT;
-# endif
-#endif
- }
-}
-
-void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) {
- if (!audio_initialized) {
- audio_init();
- }
-
- if (audio_config.enable) {
-#ifdef CPIN_AUDIO
- DISABLE_AUDIO_COUNTER_3_ISR;
-#endif
-#ifdef BPIN_AUDIO
- DISABLE_AUDIO_COUNTER_1_ISR;
-#endif
-
- // Cancel note if a note is playing
- if (playing_note) stop_all_notes();
-
- playing_notes = true;
-
- notes_pointer = np;
- notes_count = n_count;
- notes_repeat = n_repeat;
-
- place = 0;
- current_note = 0;
-
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100);
- note_position = 0;
-
-#ifdef CPIN_AUDIO
- ENABLE_AUDIO_COUNTER_3_ISR;
- ENABLE_AUDIO_COUNTER_3_OUTPUT;
-#endif
-#ifdef BPIN_AUDIO
-# ifndef CPIN_AUDIO
- ENABLE_AUDIO_COUNTER_1_ISR;
- ENABLE_AUDIO_COUNTER_1_OUTPUT;
-# endif
-#endif
- }
-}
-
-bool is_playing_notes(void) { return playing_notes; }
-
-bool is_audio_on(void) { return (audio_config.enable != 0); }
-
-void audio_toggle(void) {
- audio_config.enable ^= 1;
- eeconfig_update_audio(audio_config.raw);
- if (audio_config.enable) audio_on_user();
-}
-
-void audio_on(void) {
- audio_config.enable = 1;
- eeconfig_update_audio(audio_config.raw);
- audio_on_user();
- PLAY_SONG(audio_on_song);
-}
-
-void audio_off(void) {
- PLAY_SONG(audio_off_song);
- wait_ms(100);
- stop_all_notes();
- audio_config.enable = 0;
- eeconfig_update_audio(audio_config.raw);
-}
-
-#ifdef VIBRATO_ENABLE
-
-// Vibrato rate functions
-
-void set_vibrato_rate(float rate) { vibrato_rate = rate; }
-
-void increase_vibrato_rate(float change) { vibrato_rate *= change; }
-
-void decrease_vibrato_rate(float change) { vibrato_rate /= change; }
-
-# ifdef VIBRATO_STRENGTH_ENABLE
-
-void set_vibrato_strength(float strength) { vibrato_strength = strength; }
-
-void increase_vibrato_strength(float change) { vibrato_strength *= change; }
-
-void decrease_vibrato_strength(float change) { vibrato_strength /= change; }
-
-# endif /* VIBRATO_STRENGTH_ENABLE */
-
-#endif /* VIBRATO_ENABLE */
-
-// Polyphony functions
-
-void set_polyphony_rate(float rate) { polyphony_rate = rate; }
-
-void enable_polyphony() { polyphony_rate = 5; }
-
-void disable_polyphony() { polyphony_rate = 0; }
-
-void increase_polyphony_rate(float change) { polyphony_rate *= change; }
-
-void decrease_polyphony_rate(float change) { polyphony_rate /= change; }
-
-// Timbre function
-
-void set_timbre(float timbre) { note_timbre = timbre; }
-
-// Tempo functions
-
-void set_tempo(uint8_t tempo) { note_tempo = tempo; }
-
-void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; }
-
-void increase_tempo(uint8_t tempo_change) {
- if (note_tempo - tempo_change < 10) {
- note_tempo = 10;
- } else {
- note_tempo -= tempo_change;
- }
-}
diff --git a/quantum/audio/audio_chibios.c b/quantum/audio/audio_chibios.c
deleted file mode 100644
index 1f147f2c92..0000000000
--- a/quantum/audio/audio_chibios.c
+++ /dev/null
@@ -1,702 +0,0 @@
-/* Copyright 2016 Jack Humbert
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "audio.h"
-#include <ch.h>
-#include <hal.h>
-
-#include <string.h>
-#include "print.h"
-#include "keymap.h"
-
-#include "eeconfig.h"
-
-// -----------------------------------------------------------------------------
-
-int voices = 0;
-int voice_place = 0;
-float frequency = 0;
-float frequency_alt = 0;
-int volume = 0;
-long position = 0;
-
-float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-bool sliding = false;
-
-float place = 0;
-
-uint8_t *sample;
-uint16_t sample_length = 0;
-
-bool playing_notes = false;
-bool playing_note = false;
-float note_frequency = 0;
-float note_length = 0;
-uint8_t note_tempo = TEMPO_DEFAULT;
-float note_timbre = TIMBRE_DEFAULT;
-uint16_t note_position = 0;
-float (*notes_pointer)[][2];
-uint16_t notes_count;
-bool notes_repeat;
-bool note_resting = false;
-
-uint16_t current_note = 0;
-uint8_t rest_counter = 0;
-
-#ifdef VIBRATO_ENABLE
-float vibrato_counter = 0;
-float vibrato_strength = .5;
-float vibrato_rate = 0.125;
-#endif
-
-float polyphony_rate = 0;
-
-static bool audio_initialized = false;
-
-audio_config_t audio_config;
-
-uint16_t envelope_index = 0;
-bool glissando = true;
-
-#ifndef STARTUP_SONG
-# define STARTUP_SONG SONG(STARTUP_SOUND)
-#endif
-float startup_song[][2] = STARTUP_SONG;
-
-static void gpt_cb8(GPTDriver *gptp);
-
-#define DAC_BUFFER_SIZE 100
-#ifndef DAC_SAMPLE_MAX
-# define DAC_SAMPLE_MAX 65535U
-#endif
-
-#define START_CHANNEL_1() \
- gptStart(&GPTD6, &gpt6cfg1); \
- gptStartContinuous(&GPTD6, 2U)
-#define START_CHANNEL_2() \
- gptStart(&GPTD7, &gpt7cfg1); \
- gptStartContinuous(&GPTD7, 2U)
-#define STOP_CHANNEL_1() gptStopTimer(&GPTD6)
-#define STOP_CHANNEL_2() gptStopTimer(&GPTD7)
-#define RESTART_CHANNEL_1() \
- STOP_CHANNEL_1(); \
- START_CHANNEL_1()
-#define RESTART_CHANNEL_2() \
- STOP_CHANNEL_2(); \
- START_CHANNEL_2()
-#define UPDATE_CHANNEL_1_FREQ(freq) \
- gpt6cfg1.frequency = freq * DAC_BUFFER_SIZE; \
- RESTART_CHANNEL_1()
-#define UPDATE_CHANNEL_2_FREQ(freq) \
- gpt7cfg1.frequency = freq * DAC_BUFFER_SIZE; \
- RESTART_CHANNEL_2()
-#define GET_CHANNEL_1_FREQ (uint16_t)(gpt6cfg1.frequency * DAC_BUFFER_SIZE)
-#define GET_CHANNEL_2_FREQ (uint16_t)(gpt7cfg1.frequency * DAC_BUFFER_SIZE)
-
-/*
- * GPT6 configuration.
- */
-// static const GPTConfig gpt6cfg1 = {
-// .frequency = 1000000U,
-// .callback = NULL,
-// .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
-// .dier = 0U
-// };
-
-GPTConfig gpt6cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE,
- .callback = NULL,
- .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
- .dier = 0U};
-
-GPTConfig gpt7cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE,
- .callback = NULL,
- .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
- .dier = 0U};
-
-GPTConfig gpt8cfg1 = {.frequency = 10,
- .callback = gpt_cb8,
- .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
- .dier = 0U};
-
-/*
- * DAC test buffer (sine wave).
- */
-// static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = {
-// 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437,
-// 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846,
-// 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221,
-// 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544,
-// 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801,
-// 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982,
-// 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078,
-// 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086,
-// 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004,
-// 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837,
-// 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591,
-// 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278,
-// 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912,
-// 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507,
-// 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082,
-// 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657,
-// 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248,
-// 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873,
-// 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550,
-// 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293,
-// 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112,
-// 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16,
-// 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8,
-// 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90,
-// 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257,
-// 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503,
-// 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816,
-// 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182,
-// 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587,
-// 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012
-// };
-
-// static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = {
-// 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8,
-// 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90,
-// 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257,
-// 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503,
-// 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816,
-// 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182,
-// 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587,
-// 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012,
-// 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437,
-// 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846,
-// 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221,
-// 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544,
-// 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801,
-// 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982,
-// 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078,
-// 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086,
-// 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004,
-// 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837,
-// 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591,
-// 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278,
-// 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912,
-// 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507,
-// 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082,
-// 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657,
-// 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248,
-// 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873,
-// 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550,
-// 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293,
-// 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112,
-// 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16
-// };
-
-// squarewave
-static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = {
- // First half is max, second half is 0
- [0 ... DAC_BUFFER_SIZE / 2 - 1] = DAC_SAMPLE_MAX,
- [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = 0,
-};
-
-// squarewave
-static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = {
- // opposite of dac_buffer above
- [0 ... DAC_BUFFER_SIZE / 2 - 1] = 0,
- [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = DAC_SAMPLE_MAX,
-};
-
-/*
- * DAC streaming callback.
- */
-size_t nz = 0;
-static void end_cb1(DACDriver *dacp) {
- (void)dacp;
-
- nz++;
- if ((nz % 1000) == 0) {
- // palTogglePad(GPIOD, GPIOD_LED3);
- }
-}
-
-/*
- * DAC error callback.
- */
-static void error_cb1(DACDriver *dacp, dacerror_t err) {
- (void)dacp;
- (void)err;
-
- chSysHalt("DAC failure");
-}
-
-static const DACConfig dac1cfg1 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT};
-
-static const DACConversionGroup dacgrpcfg1 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)};
-
-static const DACConfig dac1cfg2 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT};
-
-static const DACConversionGroup dacgrpcfg2 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)};
-
-void audio_init() {
- if (audio_initialized) {
- return;
- }
-
-// Check EEPROM
-#ifdef EEPROM_ENABLE
- if (!eeconfig_is_enabled()) {
- eeconfig_init();
- }
- audio_config.raw = eeconfig_read_audio();
-#else // ARM EEPROM
- audio_config.enable = true;
-# ifdef AUDIO_CLICKY_ON
- audio_config.clicky_enable = true;
-# endif
-#endif // ARM EEPROM
-
- /*
- * Starting DAC1 driver, setting up the output pin as analog as suggested
- * by the Reference Manual.
- */
- palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
- palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
- dacStart(&DACD1, &dac1cfg1);
- dacStart(&DACD2, &dac1cfg2);
-
- /*
- * Starting GPT6/7 driver, it is used for triggering the DAC.
- */
- START_CHANNEL_1();
- START_CHANNEL_2();
-
- /*
- * Starting a continuous conversion.
- */
- dacStartConversion(&DACD1, &dacgrpcfg1, (dacsample_t *)dac_buffer, DAC_BUFFER_SIZE);
- dacStartConversion(&DACD2, &dacgrpcfg2, (dacsample_t *)dac_buffer_2, DAC_BUFFER_SIZE);
-
- audio_initialized = true;
-
- if (audio_config.enable) {
- PLAY_SONG(startup_song);
- } else {
- stop_all_notes();
- }
-}
-
-void stop_all_notes() {
- dprintf("audio stop all notes");
-
- if (!audio_initialized) {
- audio_init();
- }
- voices = 0;
-
- gptStopTimer(&GPTD6);
- gptStopTimer(&GPTD7);
- gptStopTimer(&GPTD8);
-
- playing_notes = false;
- playing_note = false;
- frequency = 0;
- frequency_alt = 0;
- volume = 0;
-
- for (uint8_t i = 0; i < 8; i++) {
- frequencies[i] = 0;
- volumes[i] = 0;
- }
-}
-
-void stop_note(float freq) {
- dprintf("audio stop note freq=%d", (int)freq);
-
- if (playing_note) {
- if (!audio_initialized) {
- audio_init();
- }
- for (int i = 7; i >= 0; i--) {
- if (frequencies[i] == freq) {
- frequencies[i] = 0;
- volumes[i] = 0;
- for (int j = i; (j < 7); j++) {
- frequencies[j] = frequencies[j + 1];
- frequencies[j + 1] = 0;
- volumes[j] = volumes[j + 1];
- volumes[j + 1] = 0;
- }
- break;
- }
- }
- voices--;
- if (voices < 0) {
- voices = 0;
- }
- if (voice_place >= voices) {
- voice_place = 0;
- }
- if (voices == 0) {
- STOP_CHANNEL_1();
- STOP_CHANNEL_2();
- gptStopTimer(&GPTD8);
- frequency = 0;
- frequency_alt = 0;
- volume = 0;
- playing_note = false;
- }
- }
-}
-
-#ifdef VIBRATO_ENABLE
-
-float mod(float a, int b) {
- float r = fmod(a, b);
- return r < 0 ? r + b : r;
-}
-
-float vibrato(float average_freq) {
-# ifdef VIBRATO_STRENGTH_ENABLE
- float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength);
-# else
- float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter];
-# endif
- vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH);
- return vibrated_freq;
-}
-
-#endif
-
-static void gpt_cb8(GPTDriver *gptp) {
- float freq;
-
- if (playing_note) {
- if (voices > 0) {
- float freq_alt = 0;
- if (voices > 1) {
- if (polyphony_rate == 0) {
- if (glissando) {
- if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) {
- frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2);
- } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440 / frequencies[voices - 2] / 12 / 2)) {
- frequency_alt = frequency_alt * pow(2, -440 / frequency_alt / 12 / 2);
- } else {
- frequency_alt = frequencies[voices - 2];
- }
- } else {
- frequency_alt = frequencies[voices - 2];
- }
-
-#ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq_alt = vibrato(frequency_alt);
- } else {
- freq_alt = frequency_alt;
- }
-#else
- freq_alt = frequency_alt;
-#endif
- }
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
-
- freq_alt = voice_envelope(freq_alt);
-
- if (freq_alt < 30.517578125) {
- freq_alt = 30.52;
- }
-
- if (GET_CHANNEL_2_FREQ != (uint16_t)freq_alt) {
- UPDATE_CHANNEL_2_FREQ(freq_alt);
- } else {
- RESTART_CHANNEL_2();
- }
- // note_timbre;
- }
-
- if (polyphony_rate > 0) {
- if (voices > 1) {
- voice_place %= voices;
- if (place++ > (frequencies[voice_place] / polyphony_rate)) {
- voice_place = (voice_place + 1) % voices;
- place = 0.0;
- }
- }
-
-#ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(frequencies[voice_place]);
- } else {
- freq = frequencies[voice_place];
- }
-#else
- freq = frequencies[voice_place];
-#endif
- } else {
- if (glissando) {
- if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) {
- frequency = frequency * pow(2, 440 / frequency / 12 / 2);
- } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) {
- frequency = frequency * pow(2, -440 / frequency / 12 / 2);
- } else {
- frequency = frequencies[voices - 1];
- }
- } else {
- frequency = frequencies[voices - 1];
- }
-
-#ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(frequency);
- } else {
- freq = frequency;
- }
-#else
- freq = frequency;
-#endif
- }
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
-
- freq = voice_envelope(freq);
-
- if (freq < 30.517578125) {
- freq = 30.52;
- }
-
- if (GET_CHANNEL_1_FREQ != (uint16_t)freq) {
- UPDATE_CHANNEL_1_FREQ(freq);
- } else {
- RESTART_CHANNEL_1();
- }
- // note_timbre;
- }
- }
-
- if (playing_notes) {
- if (note_frequency > 0) {
-#ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(note_frequency);
- } else {
- freq = note_frequency;
- }
-#else
- freq = note_frequency;
-#endif
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
- freq = voice_envelope(freq);
-
- if (GET_CHANNEL_1_FREQ != (uint16_t)freq) {
- UPDATE_CHANNEL_1_FREQ(freq);
- UPDATE_CHANNEL_2_FREQ(freq);
- }
- // note_timbre;
- } else {
- // gptStopTimer(&GPTD6);
- // gptStopTimer(&GPTD7);
- }
-
- note_position++;
- bool end_of_note = false;
- if (GET_CHANNEL_1_FREQ > 0) {
- if (!note_resting)
- end_of_note = (note_position >= (note_length * 8 - 1));
- else
- end_of_note = (note_position >= (note_length * 8));
- } else {
- end_of_note = (note_position >= (note_length * 8));
- }
-
- if (end_of_note) {
- current_note++;
- if (current_note >= notes_count) {
- if (notes_repeat) {
- current_note = 0;
- } else {
- STOP_CHANNEL_1();
- STOP_CHANNEL_2();
- // gptStopTimer(&GPTD8);
- playing_notes = false;
- return;
- }
- }
- if (!note_resting) {
- note_resting = true;
- current_note--;
- if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) {
- note_frequency = 0;
- note_length = 1;
- } else {
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = 1;
- }
- } else {
- note_resting = false;
- envelope_index = 0;
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100);
- }
-
- note_position = 0;
- }
- }
-
- if (!audio_config.enable) {
- playing_notes = false;
- playing_note = false;
- }
-}
-
-void play_note(float freq, int vol) {
- dprintf("audio play note freq=%d vol=%d", (int)freq, vol);
-
- if (!audio_initialized) {
- audio_init();
- }
-
- if (audio_config.enable && voices < 8) {
- // Cancel notes if notes are playing
- if (playing_notes) {
- stop_all_notes();
- }
-
- playing_note = true;
-
- envelope_index = 0;
-
- if (freq > 0) {
- frequencies[voices] = freq;
- volumes[voices] = vol;
- voices++;
- }
-
- gptStart(&GPTD8, &gpt8cfg1);
- gptStartContinuous(&GPTD8, 2U);
- RESTART_CHANNEL_1();
- RESTART_CHANNEL_2();
- }
-}
-
-void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) {
- if (!audio_initialized) {
- audio_init();
- }
-
- if (audio_config.enable) {
- // Cancel note if a note is playing
- if (playing_note) {
- stop_all_notes();
- }
-
- playing_notes = true;
-
- notes_pointer = np;
- notes_count = n_count;
- notes_repeat = n_repeat;
-
- place = 0;
- current_note = 0;
-
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100);
- note_position = 0;
-
- gptStart(&GPTD8, &gpt8cfg1);
- gptStartContinuous(&GPTD8, 2U);
- RESTART_CHANNEL_1();
- RESTART_CHANNEL_2();
- }
-}
-
-bool is_playing_notes(void) { return playing_notes; }
-
-bool is_audio_on(void) { return (audio_config.enable != 0); }
-
-void audio_toggle(void) {
- audio_config.enable ^= 1;
- eeconfig_update_audio(audio_config.raw);
- if (audio_config.enable) {
- audio_on_user();
- }
-}
-
-void audio_on(void) {
- audio_config.enable = 1;
- eeconfig_update_audio(audio_config.raw);
- audio_on_user();
-}
-
-void audio_off(void) {
- stop_all_notes();
- audio_config.enable = 0;
- eeconfig_update_audio(audio_config.raw);
-}
-
-#ifdef VIBRATO_ENABLE
-
-// Vibrato rate functions
-
-void set_vibrato_rate(float rate) { vibrato_rate = rate; }
-
-void increase_vibrato_rate(float change) { vibrato_rate *= change; }
-
-void decrease_vibrato_rate(float change) { vibrato_rate /= change; }
-
-# ifdef VIBRATO_STRENGTH_ENABLE
-
-void set_vibrato_strength(float strength) { vibrato_strength = strength; }
-
-void increase_vibrato_strength(float change) { vibrato_strength *= change; }
-
-void decrease_vibrato_strength(float change) { vibrato_strength /= change; }
-
-# endif /* VIBRATO_STRENGTH_ENABLE */
-
-#endif /* VIBRATO_ENABLE */
-
-// Polyphony functions
-
-void set_polyphony_rate(float rate) { polyphony_rate = rate; }
-
-void enable_polyphony() { polyphony_rate = 5; }
-
-void disable_polyphony() { polyphony_rate = 0; }
-
-void increase_polyphony_rate(float change) { polyphony_rate *= change; }
-
-void decrease_polyphony_rate(float change) { polyphony_rate /= change; }
-
-// Timbre function
-
-void set_timbre(float timbre) { note_timbre = timbre; }
-
-// Tempo functions
-
-void set_tempo(uint8_t tempo) { note_tempo = tempo; }
-
-void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; }
-
-void increase_tempo(uint8_t tempo_change) {
- if (note_tempo - tempo_change < 10) {
- note_tempo = 10;
- } else {
- note_tempo -= tempo_change;
- }
-}
diff --git a/quantum/audio/audio_pwm.c b/quantum/audio/audio_pwm.c
deleted file mode 100644
index 545aef6dd7..0000000000
--- a/quantum/audio/audio_pwm.c
+++ /dev/null
@@ -1,595 +0,0 @@
-/* Copyright 2016 Jack Humbert
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-#include <stdio.h>
-#include <string.h>
-//#include <math.h>
-#include <avr/pgmspace.h>
-#include <avr/interrupt.h>
-#include <avr/io.h>
-#include "print.h"
-#include "audio.h"
-#include "keymap.h"
-
-#include "eeconfig.h"
-
-#define PI 3.14159265
-
-#define CPU_PRESCALER 8
-
-// Timer Abstractions
-
-// TIMSK3 - Timer/Counter #3 Interrupt Mask Register
-// Turn on/off 3A interputs, stopping/enabling the ISR calls
-#define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A)
-#define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A)
-
-// TCCR3A: Timer/Counter #3 Control Register
-// Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6
-#define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1);
-#define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0));
-
-#define NOTE_PERIOD ICR3
-#define NOTE_DUTY_CYCLE OCR3A
-
-#ifdef PWM_AUDIO
-# include "wave.h"
-# define SAMPLE_DIVIDER 39
-# define SAMPLE_RATE (2000000.0 / SAMPLE_DIVIDER / 2048)
-// Resistor value of 1/ (2 * PI * 10nF * (2000000 hertz / SAMPLE_DIVIDER / 10)) for 10nF cap
-
-float places[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-uint16_t place_int = 0;
-bool repeat = true;
-#endif
-
-void delay_us(int count) {
- while (count--) {
- _delay_us(1);
- }
-}
-
-int voices = 0;
-int voice_place = 0;
-float frequency = 0;
-int volume = 0;
-long position = 0;
-
-float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-bool sliding = false;
-
-float place = 0;
-
-uint8_t* sample;
-uint16_t sample_length = 0;
-// float freq = 0;
-
-bool playing_notes = false;
-bool playing_note = false;
-float note_frequency = 0;
-float note_length = 0;
-uint8_t note_tempo = TEMPO_DEFAULT;
-float note_timbre = TIMBRE_DEFAULT;
-uint16_t note_position = 0;
-float (*notes_pointer)[][2];
-uint16_t notes_count;
-bool notes_repeat;
-float notes_rest;
-bool note_resting = false;
-
-uint16_t current_note = 0;
-uint8_t rest_counter = 0;
-
-#ifdef VIBRATO_ENABLE
-float vibrato_counter = 0;
-float vibrato_strength = .5;
-float vibrato_rate = 0.125;
-#endif
-
-float polyphony_rate = 0;
-
-static bool audio_initialized = false;
-
-audio_config_t audio_config;
-
-uint16_t envelope_index = 0;
-
-void audio_init() {
- // Check EEPROM
- if (!eeconfig_is_enabled()) {
- eeconfig_init();
- }
- audio_config.raw = eeconfig_read_audio();
-
-#ifdef PWM_AUDIO
-
- PLLFRQ = _BV(PDIV2);
- PLLCSR = _BV(PLLE);
- while (!(PLLCSR & _BV(PLOCK)))
- ;
- PLLFRQ |= _BV(PLLTM0); /* PCK 48MHz */
-
- /* Init a fast PWM on Timer4 */
- TCCR4A = _BV(COM4A0) | _BV(PWM4A); /* Clear OC4A on Compare Match */
- TCCR4B = _BV(CS40); /* No prescaling => f = PCK/256 = 187500Hz */
- OCR4A = 0;
-
- /* Enable the OC4A output */
- DDRC |= _BV(PORTC6);
-
- DISABLE_AUDIO_COUNTER_3_ISR; // Turn off 3A interputs
-
- TCCR3A = 0x0; // Options not needed
- TCCR3B = _BV(CS31) | _BV(CS30) | _BV(WGM32); // 64th prescaling and CTC
- OCR3A = SAMPLE_DIVIDER - 1; // Correct count/compare, related to sample playback
-
-#else
-
- // Set port PC6 (OC3A and /OC4A) as output
- DDRC |= _BV(PORTC6);
-
- DISABLE_AUDIO_COUNTER_3_ISR;
-
- // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers
- // Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6
- // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14 (Period = ICR3, Duty Cycle = OCR3A)
- // Clock Select (CS3n) = 0b010 = Clock / 8
- TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30);
- TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30);
-
-#endif
-
- audio_initialized = true;
-}
-
-void stop_all_notes() {
- if (!audio_initialized) {
- audio_init();
- }
- voices = 0;
-#ifdef PWM_AUDIO
- DISABLE_AUDIO_COUNTER_3_ISR;
-#else
- DISABLE_AUDIO_COUNTER_3_ISR;
- DISABLE_AUDIO_COUNTER_3_OUTPUT;
-#endif
-
- playing_notes = false;
- playing_note = false;
- frequency = 0;
- volume = 0;
-
- for (uint8_t i = 0; i < 8; i++) {
- frequencies[i] = 0;
- volumes[i] = 0;
- }
-}
-
-void stop_note(float freq) {
- if (playing_note) {
- if (!audio_initialized) {
- audio_init();
- }
-#ifdef PWM_AUDIO
- freq = freq / SAMPLE_RATE;
-#endif
- for (int i = 7; i >= 0; i--) {
- if (frequencies[i] == freq) {
- frequencies[i] = 0;
- volumes[i] = 0;
- for (int j = i; (j < 7); j++) {
- frequencies[j] = frequencies[j + 1];
- frequencies[j + 1] = 0;
- volumes[j] = volumes[j + 1];
- volumes[j + 1] = 0;
- }
- break;
- }
- }
- voices--;
- if (voices < 0) voices = 0;
- if (voice_place >= voices) {
- voice_place = 0;
- }
- if (voices == 0) {
-#ifdef PWM_AUDIO
- DISABLE_AUDIO_COUNTER_3_ISR;
-#else
- DISABLE_AUDIO_COUNTER_3_ISR;
- DISABLE_AUDIO_COUNTER_3_OUTPUT;
-#endif
- frequency = 0;
- volume = 0;
- playing_note = false;
- }
- }
-}
-
-#ifdef VIBRATO_ENABLE
-
-float mod(float a, int b) {
- float r = fmod(a, b);
- return r < 0 ? r + b : r;
-}
-
-float vibrato(float average_freq) {
-# ifdef VIBRATO_STRENGTH_ENABLE
- float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength);
-# else
- float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter];
-# endif
- vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH);
- return vibrated_freq;
-}
-
-#endif
-
-ISR(TIMER3_COMPA_vect) {
- if (playing_note) {
-#ifdef PWM_AUDIO
- if (voices == 1) {
- // SINE
- OCR4A = pgm_read_byte(&sinewave[(uint16_t)place]) >> 2;
-
- // SQUARE
- // if (((int)place) >= 1024){
- // OCR4A = 0xFF >> 2;
- // } else {
- // OCR4A = 0x00;
- // }
-
- // SAWTOOTH
- // OCR4A = (int)place / 4;
-
- // TRIANGLE
- // if (((int)place) >= 1024) {
- // OCR4A = (int)place / 2;
- // } else {
- // OCR4A = 2048 - (int)place / 2;
- // }
-
- place += frequency;
-
- if (place >= SINE_LENGTH) place -= SINE_LENGTH;
-
- } else {
- int sum = 0;
- for (int i = 0; i < voices; i++) {
- // SINE
- sum += pgm_read_byte(&sinewave[(uint16_t)places[i]]) >> 2;
-
- // SQUARE
- // if (((int)places[i]) >= 1024){
- // sum += 0xFF >> 2;
- // } else {
- // sum += 0x00;
- // }
-
- places[i] += frequencies[i];
-
- if (places[i] >= SINE_LENGTH) places[i] -= SINE_LENGTH;
- }
- OCR4A = sum;
- }
-#else
- if (voices > 0) {
- float freq;
- if (polyphony_rate > 0) {
- if (voices > 1) {
- voice_place %= voices;
- if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) {
- voice_place = (voice_place + 1) % voices;
- place = 0.0;
- }
- }
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(frequencies[voice_place]);
- } else {
-# else
- {
-# endif
- freq = frequencies[voice_place];
- }
- } else {
- if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) {
- frequency = frequency * pow(2, 440 / frequency / 12 / 2);
- } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) {
- frequency = frequency * pow(2, -440 / frequency / 12 / 2);
- } else {
- frequency = frequencies[voices - 1];
- }
-
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(frequency);
- } else {
-# else
- {
-# endif
- freq = frequency;
- }
- }
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
- freq = voice_envelope(freq);
-
- if (freq < 30.517578125) freq = 30.52;
- NOTE_PERIOD = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period
- NOTE_DUTY_CYCLE = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period
- }
-#endif
- }
-
- // SAMPLE
- // OCR4A = pgm_read_byte(&sample[(uint16_t)place_int]);
-
- // place_int++;
-
- // if (place_int >= sample_length)
- // if (repeat)
- // place_int -= sample_length;
- // else
- // DISABLE_AUDIO_COUNTER_3_ISR;
-
- if (playing_notes) {
-#ifdef PWM_AUDIO
- OCR4A = pgm_read_byte(&sinewave[(uint16_t)place]) >> 0;
-
- place += note_frequency;
- if (place >= SINE_LENGTH) place -= SINE_LENGTH;
-#else
- if (note_frequency > 0) {
- float freq;
-
-# ifdef VIBRATO_ENABLE
- if (vibrato_strength > 0) {
- freq = vibrato(note_frequency);
- } else {
-# else
- {
-# endif
- freq = note_frequency;
- }
-
- if (envelope_index < 65535) {
- envelope_index++;
- }
- freq = voice_envelope(freq);
-
- NOTE_PERIOD = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period
- NOTE_DUTY_CYCLE = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period
- } else {
- NOTE_PERIOD = 0;
- NOTE_DUTY_CYCLE = 0;
- }
-#endif
-
- note_position++;
- bool end_of_note = false;
- if (NOTE_PERIOD > 0)
- end_of_note = (note_position >= (note_length / NOTE_PERIOD * 0xFFFF));
- else
- end_of_note = (note_position >= (note_length * 0x7FF));
- if (end_of_note) {
- current_note++;
- if (current_note >= notes_count) {
- if (notes_repeat) {
- current_note = 0;
- } else {
-#ifdef PWM_AUDIO
- DISABLE_AUDIO_COUNTER_3_ISR;
-#else
- DISABLE_AUDIO_COUNTER_3_ISR;
- DISABLE_AUDIO_COUNTER_3_OUTPUT;
-#endif
- playing_notes = false;
- return;
- }
- }
- if (!note_resting && (notes_rest > 0)) {
- note_resting = true;
- note_frequency = 0;
- note_length = notes_rest;
- current_note--;
- } else {
- note_resting = false;
-#ifdef PWM_AUDIO
- note_frequency = (*notes_pointer)[current_note][0] / SAMPLE_RATE;
- note_length = (*notes_pointer)[current_note][1] * (((float)note_tempo) / 100);
-#else
- envelope_index = 0;
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100);
-#endif
- }
- note_position = 0;
- }
- }
-
- if (!audio_config.enable) {
- playing_notes = false;
- playing_note = false;
- }
-}
-
-void play_note(float freq, int vol) {
- if (!audio_initialized) {
- audio_init();
- }
-
- if (audio_config.enable && voices < 8) {
- DISABLE_AUDIO_COUNTER_3_ISR;
-
- // Cancel notes if notes are playing
- if (playing_notes) stop_all_notes();
-
- playing_note = true;
-
- envelope_index = 0;
-
-#ifdef PWM_AUDIO
- freq = freq / SAMPLE_RATE;
-#endif
- if (freq > 0) {
- frequencies[voices] = freq;
- volumes[voices] = vol;
- voices++;
- }
-
-#ifdef PWM_AUDIO
- ENABLE_AUDIO_COUNTER_3_ISR;
-#else
- ENABLE_AUDIO_COUNTER_3_ISR;
- ENABLE_AUDIO_COUNTER_3_OUTPUT;
-#endif
- }
-}
-
-void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat, float n_rest) {
- if (!audio_initialized) {
- audio_init();
- }
-
- if (audio_config.enable) {
- DISABLE_AUDIO_COUNTER_3_ISR;
-
- // Cancel note if a note is playing
- if (playing_note) stop_all_notes();
-
- playing_notes = true;
-
- notes_pointer = np;
- notes_count = n_count;
- notes_repeat = n_repeat;
- notes_rest = n_rest;
-
- place = 0;
- current_note = 0;
-
-#ifdef PWM_AUDIO
- note_frequency = (*notes_pointer)[current_note][0] / SAMPLE_RATE;
- note_length = (*notes_pointer)[current_note][1] * (((float)note_tempo) / 100);
-#else
- note_frequency = (*notes_pointer)[current_note][0];
- note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100);
-#endif
- note_position = 0;
-
-#ifdef PWM_AUDIO
- ENABLE_AUDIO_COUNTER_3_ISR;
-#else
- ENABLE_AUDIO_COUNTER_3_ISR;
- ENABLE_AUDIO_COUNTER_3_OUTPUT;
-#endif
- }
-}
-
-#ifdef PWM_AUDIO
-void play_sample(uint8_t* s, uint16_t l, bool r) {
- if (!audio_initialized) {
- audio_init();
- }
-
- if (audio_config.enable) {
- DISABLE_AUDIO_COUNTER_3_ISR;
- stop_all_notes();
- place_int = 0;
- sample = s;
- sample_length = l;
- repeat = r;
-
- ENABLE_AUDIO_COUNTER_3_ISR;
- }
-}
-#endif
-
-void audio_toggle(void) {
- audio_config.enable ^= 1;
- eeconfig_update_audio(audio_config.raw);
-}
-
-void audio_on(void) {
- audio_config.enable = 1;
- eeconfig_update_audio(audio_config.raw);
-}
-
-void audio_off(void) {
- audio_config.enable = 0;
- eeconfig_update_audio(audio_config.raw);
-}
-
-#ifdef VIBRATO_ENABLE
-
-// Vibrato rate functions
-
-void set_vibrato_rate(float rate) { vibrato_rate = rate; }
-
-void increase_vibrato_rate(float change) { vibrato_rate *= change; }
-
-void decrease_vibrato_rate(float change) { vibrato_rate /= change; }
-
-# ifdef VIBRATO_STRENGTH_ENABLE
-
-void set_vibrato_strength(float strength) { vibrato_strength = strength; }
-
-void increase_vibrato_strength(float change) { vibrato_strength *= change; }
-
-void decrease_vibrato_strength(float change) { vibrato_strength /= change; }
-
-# endif /* VIBRATO_STRENGTH_ENABLE */
-
-#endif /* VIBRATO_ENABLE */
-
-// Polyphony functions
-
-void set_polyphony_rate(float rate) { polyphony_rate = rate; }
-
-void enable_polyphony() { polyphony_rate = 5; }
-
-void disable_polyphony() { polyphony_rate = 0; }
-
-void increase_polyphony_rate(float change) { polyphony_rate *= change; }
-
-void decrease_polyphony_rate(float change) { polyphony_rate /= change; }
-
-// Timbre function
-
-void set_timbre(float timbre) { note_timbre = timbre; }
-
-// Tempo functions
-
-void set_tempo(uint8_t tempo) { note_tempo = tempo; }
-
-void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; }
-
-void increase_tempo(uint8_t tempo_change) {
- if (note_tempo - tempo_change < 10) {
- note_tempo = 10;
- } else {
- note_tempo -= tempo_change;
- }
-}
-
-//------------------------------------------------------------------------------
-// Override these functions in your keymap file to play different tunes on
-// startup and bootloader jump
-__attribute__((weak)) void play_startup_tone() {}
-
-__attribute__((weak)) void play_goodbye_tone() {}
-//------------------------------------------------------------------------------
diff --git a/quantum/audio/driver_avr_pwm.h b/quantum/audio/driver_avr_pwm.h
new file mode 100644
index 0000000000..d6eb3571da
--- /dev/null
+++ b/quantum/audio/driver_avr_pwm.h
@@ -0,0 +1,17 @@
+/* Copyright 2020 Jack Humbert
+ * Copyright 2020 JohSchneider
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#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 0000000000..492b9bfb04
--- /dev/null
+++ b/quantum/audio/driver_avr_pwm_hardware.c
@@ -0,0 +1,322 @@
+/* Copyright 2016 Jack Humbert
+ * Copyright 2020 JohSchneider
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if defined(__AVR__)
+# include <avr/pgmspace.h>
+# include <avr/interrupt.h>
+# include <avr/io.h>
+#endif
+
+#include "audio.h"
+
+extern bool playing_note;
+extern bool playing_melody;
+extern uint8_t note_timbre;
+
+#define CPU_PRESCALER 8
+
+/*
+ Audio Driver: PWM
+
+ drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.
+
+ 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
+ and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1
+
+ alternatively, the PWM pins on PORTB can be used as only/primary speaker
+*/
+
+#if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7)
+# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options."
+#endif
+
+#if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)
+# define AUDIO1_PIN_SET
+# define AUDIO1_TIMSKx TIMSK3
+# define AUDIO1_TCCRxA TCCR3A
+# define AUDIO1_TCCRxB TCCR3B
+# define AUDIO1_ICRx ICR3
+# define AUDIO1_WGMx0 WGM30
+# define AUDIO1_WGMx1 WGM31
+# define AUDIO1_WGMx2 WGM32
+# define AUDIO1_WGMx3 WGM33
+# define AUDIO1_CSx0 CS30
+# define AUDIO1_CSx1 CS31
+# define AUDIO1_CSx2 CS32
+
+# if (AUDIO_PIN == C6)
+# define AUDIO1_COMxy0 COM3A0
+# define AUDIO1_COMxy1 COM3A1
+# define AUDIO1_OCIExy OCIE3A
+# define AUDIO1_OCRxy OCR3A
+# define AUDIO1_PIN C6
+# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect
+# elif (AUDIO_PIN == C5)
+# define AUDIO1_COMxy0 COM3B0
+# define AUDIO1_COMxy1 COM3B1
+# define AUDIO1_OCIExy OCIE3B
+# define AUDIO1_OCRxy OCR3B
+# define AUDIO1_PIN C5
+# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect
+# elif (AUDIO_PIN == C4)
+# define AUDIO1_COMxy0 COM3C0
+# define AUDIO1_COMxy1 COM3C1
+# define AUDIO1_OCIExy OCIE3C
+# define AUDIO1_OCRxy OCR3C
+# define AUDIO1_PIN C4
+# define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect
+# endif
+#endif
+
+#if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)
+# error "Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense."
+#endif
+
+#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)))
+# error "Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported."
+#endif
+
+#if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
+# error "Audio feature: the pin selected as AUDIO_PIN_ALT is not supported."
+#endif
+
+#if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7)
+# define AUDIO2_PIN_SET
+# define AUDIO2_TIMSKx TIMSK1
+# define AUDIO2_TCCRxA TCCR1A
+# define AUDIO2_TCCRxB TCCR1B
+# define AUDIO2_ICRx ICR1
+# define AUDIO2_WGMx0 WGM10
+# define AUDIO2_WGMx1 WGM11
+# define AUDIO2_WGMx2 WGM12
+# define AUDIO2_WGMx3 WGM13
+# define AUDIO2_CSx0 CS10
+# define AUDIO2_CSx1 CS11
+# define AUDIO2_CSx2 CS12
+
+# if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)
+# define AUDIO2_COMxy0 COM1A0
+# define AUDIO2_COMxy1 COM1A1
+# define AUDIO2_OCIExy OCIE1A
+# define AUDIO2_OCRxy OCR1A
+# define AUDIO2_PIN B5
+# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect
+# elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)
+# define AUDIO2_COMxy0 COM1B0
+# define AUDIO2_COMxy1 COM1B1
+# define AUDIO2_OCIExy OCIE1B
+# define AUDIO2_OCRxy OCR1B
+# define AUDIO2_PIN B6
+# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect
+# elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)
+# define AUDIO2_COMxy0 COM1C0
+# define AUDIO2_COMxy1 COM1C1
+# define AUDIO2_OCIExy OCIE1C
+# define AUDIO2_OCRxy OCR1C
+# define AUDIO2_PIN B7
+# define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect
+# endif
+#endif
+
+// C6 seems to be the assumed default by many existing keyboard - but sill warn the user
+#if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)
+# 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... :-)"
+// TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define
+#endif
+// -----------------------------------------------------------------------------
+
+#ifdef AUDIO1_PIN_SET
+static float channel_1_frequency = 0.0f;
+void channel_1_set_frequency(float freq) {
+ if (freq == 0.0f) // a pause/rest is a valid "note" with freq=0
+ {
+ // disable the output, but keep the pwm-ISR going (with the previous
+ // frequency) so the audio-state keeps getting updated
+ // Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet
+ AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
+ return;
+ } else {
+ AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode
+ }
+
+ channel_1_frequency = freq;
+
+ // set pwm period
+ AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
+ // and duty cycle
+ AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
+}
+
+void channel_1_start(void) {
+ // enable timer-counter ISR
+ AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);
+ // enable timer-counter output
+ AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);
+}
+
+void channel_1_stop(void) {
+ // disable timer-counter ISR
+ AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);
+ // disable timer-counter output
+ AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));
+}
+#endif
+
+#ifdef AUDIO2_PIN_SET
+static float channel_2_frequency = 0.0f;
+void channel_2_set_frequency(float freq) {
+ if (freq == 0.0f) {
+ AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
+ return;
+ } else {
+ AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
+ }
+
+ channel_2_frequency = freq;
+
+ AUDIO2_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
+ AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);
+}
+
+float channel_2_get_frequency(void) { return channel_2_frequency; }
+
+void channel_2_start(void) {
+ AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);
+ AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);
+}
+
+void channel_2_stop(void) {
+ AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);
+ AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));
+}
+#endif
+
+void audio_driver_initialize() {
+#ifdef AUDIO1_PIN_SET
+ channel_1_stop();
+ setPinOutput(AUDIO1_PIN);
+#endif
+
+#ifdef AUDIO2_PIN_SET
+ channel_2_stop();
+ setPinOutput(AUDIO2_PIN);
+#endif
+
+ // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B
+ // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation
+ // OC3A -- PC6
+ // OC3B -- PC5
+ // OC3C -- PC4
+ // OC1A -- PB5
+ // OC1B -- PB6
+ // OC1C -- PB7
+
+ // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)
+ // OCR3A - PC6
+ // OCR3B - PC5
+ // OCR3C - PC4
+ // OCR1A - PB5
+ // OCR1B - PB6
+ // OCR1C - PB7
+
+ // Clock Select (CS3n) = 0b010 = Clock / 8
+#ifdef AUDIO1_PIN_SET
+ // initialize timer-counter
+ AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);
+ AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);
+#endif
+
+#ifdef AUDIO2_PIN_SET
+ AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);
+ AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);
+#endif
+}
+
+void audio_driver_stop() {
+#ifdef AUDIO1_PIN_SET
+ channel_1_stop();
+#endif
+
+#ifdef AUDIO2_PIN_SET
+ channel_2_stop();
+#endif
+}
+
+void audio_driver_start(void) {
+#ifdef AUDIO1_PIN_SET
+ channel_1_start();
+ if (playing_note) {
+ channel_1_set_frequency(audio_get_processed_frequency(0));
+ }
+#endif
+
+#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
+ channel_2_start();
+ if (playing_note) {
+ channel_2_set_frequency(audio_get_processed_frequency(0));
+ }
+#endif
+}
+
+static volatile uint32_t isr_counter = 0;
+#ifdef AUDIO1_PIN_SET
+ISR(AUDIO1_TIMERx_COMPy_vect) {
+ isr_counter++;
+ if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;
+
+ isr_counter = 0;
+ bool state_changed = audio_update_state();
+
+ if (!playing_note && !playing_melody) {
+ channel_1_stop();
+# ifdef AUDIO2_PIN_SET
+ channel_2_stop();
+# endif
+ return;
+ }
+
+ if (state_changed) {
+ channel_1_set_frequency(audio_get_processed_frequency(0));
+# ifdef AUDIO2_PIN_SET
+ if (audio_get_number_of_active_tones() > 1) {
+ channel_2_set_frequency(audio_get_processed_frequency(1));
+ } else {
+ channel_2_stop();
+ }
+# endif
+ }
+}
+#endif
+
+#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)
+ISR(AUDIO2_TIMERx_COMPy_vect) {
+ isr_counter++;
+ if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;
+
+ isr_counter = 0;
+ bool state_changed = audio_update_state();
+
+ if (!playing_note && !playing_melody) {
+ channel_2_stop();
+ return;
+ }
+
+ if (state_changed) {
+ channel_2_set_frequency(audio_get_processed_frequency(0));
+ }
+}
+#endif
diff --git a/quantum/audio/driver_chibios_dac.h b/quantum/audio/driver_chibios_dac.h
new file mode 100644
index 0000000000..07cd622ead
--- /dev/null
+++ b/quantum/audio/driver_chibios_dac.h
@@ -0,0 +1,126 @@
+/* Copyright 2019 Jack Humbert
+ * Copyright 2020 JohSchneider
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#ifndef A4
+# define A4 PAL_LINE(GPIOA, 4)
+#endif
+#ifndef A5
+# define A5 PAL_LINE(GPIOA, 5)
+#endif
+
+/**
+ * Size of the dac_buffer arrays. All must be the same size.
+ */
+#define AUDIO_DAC_BUFFER_SIZE 256U
+
+/**
+ * Highest value allowed sample value.
+
+ * since the DAC is limited to 12 bit, the absolute max is 0xfff = 4095U;
+ * lower values adjust the peak-voltage aka volume down.
+ * adjusting this value has only an effect on a sample-buffer whose values are
+ * are NOT pregenerated - see square-wave
+ */
+#ifndef AUDIO_DAC_SAMPLE_MAX
+# define AUDIO_DAC_SAMPLE_MAX 4095U
+#endif
+
+#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)
+# define AUDIO_DAC_QUALITY_SANE_MINIMUM
+#endif
+
+/**
+ * These presets allow you to quickly switch between quality settings for
+ * the DAC. The sample rate and maximum number of simultaneous tones roughly
+ * has an inverse relationship - slightly higher sample rates may be possible.
+ *
+ * NOTE: a high sample-rate results in a higher cpu-load, which might lead to
+ * (audible) discontinuities and/or starve other processes of cpu-time
+ * (like RGB-led back-lighting, ...)
+ */
+#ifdef AUDIO_DAC_QUALITY_VERY_LOW
+# define AUDIO_DAC_SAMPLE_RATE 11025U
+# define AUDIO_MAX_SIMULTANEOUS_TONES 8
+#endif
+
+#ifdef AUDIO_DAC_QUALITY_LOW
+# define AUDIO_DAC_SAMPLE_RATE 22050U
+# define AUDIO_MAX_SIMULTANEOUS_TONES 4
+#endif
+
+#ifdef AUDIO_DAC_QUALITY_HIGH
+# define AUDIO_DAC_SAMPLE_RATE 44100U
+# define AUDIO_MAX_SIMULTANEOUS_TONES 2
+#endif
+
+#ifdef AUDIO_DAC_QUALITY_VERY_HIGH
+# define AUDIO_DAC_SAMPLE_RATE 88200U
+# define AUDIO_MAX_SIMULTANEOUS_TONES 1
+#endif
+
+#ifdef AUDIO_DAC_QUALITY_SANE_MINIMUM
+/* a sane-minimum config: with a trade-off between cpu-load and tone-range
+ *
+ * the (currently) highest defined note is NOTE_B8 with 7902Hz; if we now
+ * aim for an even even multiple of the buffer-size, we end up with:
+ * ( roundUptoPow2(highest note / AUDIO_DAC_BUFFER_SIZE) * nyquist-rate * AUDIO_DAC_BUFFER_SIZE)
+ * 7902/256 = 30.867 * 2 * 256 ~= 16384
+ * which works out (but the 'scope shows some sampling artifacts with lower harmonics :-P)
+ */
+# define AUDIO_DAC_SAMPLE_RATE 16384U
+# define AUDIO_MAX_SIMULTANEOUS_TONES 8
+#endif
+
+/**
+ * Effective bit-rate of the DAC. 44.1khz is the standard for most audio - any
+ * lower will sacrifice perceptible audio quality. Any higher will limit the
+ * number of simultaneous tones. In most situations, a tenth (1/10) of the
+ * sample rate is where notes become unbearable.
+ */
+#ifndef AUDIO_DAC_SAMPLE_RATE
+# define AUDIO_DAC_SAMPLE_RATE 44100U
+#endif
+
+/**
+ * The number of tones that can be played simultaneously. If too high a value
+ * is used here, the keyboard will freeze and glitch-out when that many tones
+ * are being played.
+ */
+#ifndef AUDIO_MAX_SIMULTANEOUS_TONES
+# define AUDIO_MAX_SIMULTANEOUS_TONES 2
+#endif
+
+/**
+ * The default value of the DAC when not playing anything. Certain hardware
+ * setups may require a high (AUDIO_DAC_SAMPLE_MAX) or low (0) value here.
+ * Since multiple added sine waves tend to oscillate around the midpoint,
+ * and possibly never/rarely reach either 0 of MAX, 1/2 MAX can be a
+ * reasonable default value.
+ */
+#ifndef AUDIO_DAC_OFF_VALUE
+# define AUDIO_DAC_OFF_VALUE AUDIO_DAC_SAMPLE_MAX / 2
+#endif
+
+#if AUDIO_DAC_OFF_VALUE > AUDIO_DAC_SAMPLE_MAX
+# error "AUDIO_DAC: OFF_VALUE may not be larger than SAMPLE_MAX"
+#endif
+
+/**
+ *user overridable sample generation/processing
+ */
+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 0000000000..db304adb87
--- /dev/null
+++ b/quantum/audio/driver_chibios_dac_additive.c
@@ -0,0 +1,335 @@
+/* Copyright 2016-2019 Jack Humbert
+ * Copyright 2020 JohSchneider
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "audio.h"
+#include <ch.h>
+#include <hal.h>
+
+/*
+ Audio Driver: DAC
+
+ 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
+
+ it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate'
+
+ this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis
+*/
+
+#if !defined(AUDIO_PIN)
+# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options."
+#endif
+#if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE)
+# 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."
+#endif
+
+#if !defined(AUDIO_PIN_ALT)
+// no ALT pin defined is valid, but the c-ifs below need some value set
+# define AUDIO_PIN_ALT PAL_NOLINE
+#endif
+
+#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)
+# define AUDIO_DAC_SAMPLE_WAVEFORM_SINE
+#endif
+
+#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE
+/* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0
+ */
+static const dacsample_t dac_buffer_sine[AUDIO_DAC_BUFFER_SIZE] = {
+ // 256 values, max 4095
+ 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,
+ 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};
+#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SINE
+#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
+static const dacsample_t dac_buffer_triangle[AUDIO_DAC_BUFFER_SIZE] = {
+ // 256 values, max 4095
+ 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,
+ 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};
+#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE
+#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
+static const dacsample_t dac_buffer_square[AUDIO_DAC_BUFFER_SIZE] = {
+ [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0, // first and
+ [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX, // second half
+};
+#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE
+/*
+// four steps: 0, 1/3, 2/3 and 1
+static const dacsample_t dac_buffer_staircase[AUDIO_DAC_BUFFER_SIZE] = {
+ [0 ... AUDIO_DAC_BUFFER_SIZE/3 -1 ] = 0,
+ [AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE / 2 -1 ] = AUDIO_DAC_SAMPLE_MAX / 3,
+ [AUDIO_DAC_BUFFER_SIZE / 2 ... 3 * AUDIO_DAC_BUFFER_SIZE / 4 -1 ] = 2 * AUDIO_DAC_SAMPLE_MAX / 3,
+ [3 * AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE -1 ] = AUDIO_DAC_SAMPLE_MAX,
+}
+*/
+#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
+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,
+ 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};
+#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID
+
+static dacsample_t dac_buffer_empty[AUDIO_DAC_BUFFER_SIZE] = {AUDIO_DAC_OFF_VALUE};
+
+/* keep track of the sample position for for each frequency */
+static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0};
+
+static float active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0, 0};
+static uint8_t active_tones_snapshot_length = 0;
+
+typedef enum {
+ OUTPUT_SHOULD_START,
+ OUTPUT_RUN_NORMALLY,
+ // path 1: wait for zero, then change/update active tones
+ OUTPUT_TONES_CHANGED,
+ OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE,
+ // path 2: hardware should stop, wait for zero then turn output off = stop the timer
+ OUTPUT_SHOULD_STOP,
+ OUTPUT_REACHED_ZERO_BEFORE_OFF,
+ OUTPUT_OFF,
+ OUTPUT_OFF_1,
+ 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
+ number_of_output_states
+} output_states_t;
+output_states_t state = OUTPUT_OFF_2;
+
+/**
+ * Generation of the waveform being passed to the callback. Declared weak so users
+ * can override it with their own wave-forms/noises.
+ */
+__attribute__((weak)) uint16_t dac_value_generate(void) {
+ // DAC is running/asking for values but snapshot length is zero -> must be playing a pause
+ if (active_tones_snapshot_length == 0) {
+ return AUDIO_DAC_OFF_VALUE;
+ }
+
+ /* doing additive wave synthesis over all currently playing tones = adding up
+ * sine-wave-samples for each frequency, scaled by the number of active tones
+ */
+ uint16_t value = 0;
+ float frequency = 0.0f;
+
+ for (uint8_t i = 0; i < active_tones_snapshot_length; i++) {
+ /* Note: a user implementation does not have to rely on the active_tones_snapshot, but
+ * could directly query the active frequencies through audio_get_processed_frequency */
+ frequency = active_tones_snapshot[i];
+
+ dac_if[i] = dac_if[i] + ((frequency * AUDIO_DAC_BUFFER_SIZE) / AUDIO_DAC_SAMPLE_RATE) * 2 / 3;
+ /*Note: the 2/3 are necessary to get the correct frequencies on the
+ * DAC output (as measured with an oscilloscope), since the gpt
+ * timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback
+ * is called twice per conversion.*/
+
+ dac_if[i] = fmod(dac_if[i], AUDIO_DAC_BUFFER_SIZE);
+
+ // Wavetable generation/lookup
+ uint16_t dac_i = (uint16_t)dac_if[i];
+
+#if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE)
+ value += dac_buffer_sine[dac_i] / active_tones_snapshot_length;
+#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE)
+ value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length;
+#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)
+ value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length;
+#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE)
+ value += dac_buffer_square[dac_i] / active_tones_snapshot_length;
+#endif
+ /*
+ // SINE
+ value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3;
+ // TRIANGLE
+ value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3;
+ // SQUARE
+ value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3;
+ //NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P
+ */
+
+ // STAIRS (mostly usefully as test-pattern)
+ // value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length;
+ }
+
+ return value;
+}
+
+/**
+ * DAC streaming callback. Does all of the main computing for playing songs.
+ *
+ * Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'.
+ */
+static void dac_end(DACDriver *dacp) {
+ dacsample_t *sample_p = (dacp)->samples;
+
+ // work on the other half of the buffer
+ if (dacIsBufferComplete(dacp)) {
+ sample_p += AUDIO_DAC_BUFFER_SIZE / 2; // 'half_index'
+ }
+
+ for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) {
+ if (OUTPUT_OFF <= state) {
+ sample_p[s] = AUDIO_DAC_OFF_VALUE;
+ continue;
+ } else {
+ sample_p[s] = dac_value_generate();
+ }
+
+ /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX)
+ * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX
+ * * *
+ * * *
+ * ---------------------------------------------------------
+ * * * } AUDIO_DAC_SAMPLE_MAX/100
+ * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE
+ * * * } AUDIO_DAC_SAMPLE_MAX/100
+ * ---------------------------------------------------------
+ * *
+ * * *
+ * * *
+ * =====*=*================================================= 0x0
+ */
+ if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) && // value approaches from below
+ (sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100))) // or above
+ ) {
+ if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) {
+ state = OUTPUT_RUN_NORMALLY;
+ } else if (OUTPUT_TONES_CHANGED == state) {
+ state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE;
+ } else if (OUTPUT_SHOULD_STOP == state) {
+ state = OUTPUT_REACHED_ZERO_BEFORE_OFF;
+ }
+ }
+
+ // still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover
+ if (OUTPUT_SHOULD_START == state) {
+ sample_p[s] = AUDIO_DAC_OFF_VALUE;
+ }
+
+ if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) {
+ uint8_t active_tones = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones());
+ active_tones_snapshot_length = 0;
+ // update the snapshot - once, and only on occasion that something changed;
+ // -> saves cpu cycles (?)
+ for (uint8_t i = 0; i < active_tones; i++) {
+ float freq = audio_get_processed_frequency(i);
+ if (freq > 0) { // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step
+ active_tones_snapshot[active_tones_snapshot_length++] = freq;
+ }
+ }
+
+ if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) {
+ state = OUTPUT_OFF;
+ }
+ if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) {
+ state = OUTPUT_RUN_NORMALLY;
+ }
+ }
+ }
+
+ // update audio internal state (note position, current_note, ...)
+ if (audio_update_state()) {
+ if (OUTPUT_SHOULD_STOP != state) {
+ state = OUTPUT_TONES_CHANGED;
+ }
+ }
+
+ if (OUTPUT_OFF <= state) {
+ if (OUTPUT_OFF_2 == state) {
+ // stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE
+ gptStopTimer(&GPTD6);
+ } else {
+ state++;
+ }
+ }
+}
+
+static void dac_error(DACDriver *dacp, dacerror_t err) {
+ (void)dacp;
+ (void)err;
+
+ chSysHalt("DAC failure. halp");
+}
+
+static const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3,
+ .callback = NULL,
+ .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
+ .dier = 0U};
+
+static const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
+
+/**
+ * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
+ * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
+ * to be a third of what we expect.
+ *
+ * Here are all the values for DAC_TRG (TSEL in the ref manual)
+ * TIM15_TRGO 0b011
+ * TIM2_TRGO 0b100
+ * TIM3_TRGO 0b001
+ * TIM6_TRGO 0b000
+ * TIM7_TRGO 0b010
+ * EXTI9 0b110
+ * SWTRIG 0b111
+ */
+static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)};
+
+void audio_driver_initialize() {
+ if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
+ palSetLineMode(A4, PAL_MODE_INPUT_ANALOG);
+ dacStart(&DACD1, &dac_conf);
+ }
+ if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
+ palSetLineMode(A5, PAL_MODE_INPUT_ANALOG);
+ dacStart(&DACD2, &dac_conf);
+ }
+
+ /* enable the output buffer, to directly drive external loads with no additional circuitry
+ *
+ * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
+ * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
+ * Note: enabling the output buffer imparts an additional dc-offset of a couple mV
+ *
+ * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
+ * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
+ */
+ DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
+ DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
+
+ if (AUDIO_PIN == A4) {
+ dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
+ } else if (AUDIO_PIN == A5) {
+ dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE);
+ }
+
+ // no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE
+#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
+ if (AUDIO_PIN_ALT == A4) {
+ dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
+ } else if (AUDIO_PIN_ALT == A5) {
+ dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
+ }
+#endif
+
+ gptStart(&GPTD6, &gpt6cfg1);
+}
+
+void audio_driver_stop(void) { state = OUTPUT_SHOULD_STOP; }
+
+void audio_driver_start(void) {
+ gptStartContinuous(&GPTD6, 2U);
+
+ for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) {
+ dac_if[i] = 0.0f;
+ active_tones_snapshot[i] = 0.0f;
+ }
+ active_tones_snapshot_length = 0;
+ state = OUTPUT_SHOULD_START;
+}
diff --git a/quantum/audio/driver_chibios_dac_basic.c b/quantum/audio/driver_chibios_dac_basic.c
new file mode 100644
index 0000000000..9a1c9a8c19
--- /dev/null
+++ b/quantum/audio/driver_chibios_dac_basic.c
@@ -0,0 +1,245 @@
+/* Copyright 2016-2020 Jack Humbert
+ * Copyright 2020 JohSchneider
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "audio.h"
+#include "ch.h"
+#include "hal.h"
+
+/*
+ Audio Driver: DAC
+
+ 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
+
+ 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
+ OR
+ one speaker wired to A4+A5 with the AUDIO_PIN_ALT_AS_NEGATIVE define set - see docs/feature_audio
+
+*/
+
+#if !defined(AUDIO_PIN)
+# pragma message "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC basic)' for available options."
+// 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
+# define AUDIO_PIN A5
+#endif
+// check configuration for ONE speaker, connected to both DAC pins
+#if defined(AUDIO_PIN_ALT_AS_NEGATIVE) && !defined(AUDIO_PIN_ALT)
+# error "Audio feature: AUDIO_PIN_ALT_AS_NEGATIVE set, but no pin configured as AUDIO_PIN_ALT"
+#endif
+
+#ifndef AUDIO_PIN_ALT
+// no ALT pin defined is valid, but the c-ifs below need some value set
+# define AUDIO_PIN_ALT -1
+#endif
+
+#if !defined(AUDIO_STATE_TIMER)
+# define AUDIO_STATE_TIMER GPTD8
+#endif
+
+// square-wave
+static const dacsample_t dac_buffer_1[AUDIO_DAC_BUFFER_SIZE] = {
+ // First half is max, second half is 0
+ [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = AUDIO_DAC_SAMPLE_MAX,
+ [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = 0,
+};
+
+// square-wave
+static const dacsample_t dac_buffer_2[AUDIO_DAC_BUFFER_SIZE] = {
+ // opposite of dac_buffer above
+ [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0,
+ [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX,
+};
+
+GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
+ .callback = NULL,
+ .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
+ .dier = 0U};
+GPTConfig gpt7cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,
+ .callback = NULL,
+ .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
+ .dier = 0U};
+
+static void gpt_audio_state_cb(GPTDriver *gptp);
+GPTConfig gptStateUpdateCfg = {.frequency = 10,
+ .callback = gpt_audio_state_cb,
+ .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */
+ .dier = 0U};
+
+static const DACConfig dac_conf_ch1 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
+static const DACConfig dac_conf_ch2 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};
+
+/**
+ * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered
+ * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency
+ * to be a third of what we expect.
+ *
+ * Here are all the values for DAC_TRG (TSEL in the ref manual)
+ * TIM15_TRGO 0b011
+ * TIM2_TRGO 0b100
+ * TIM3_TRGO 0b001
+ * TIM6_TRGO 0b000
+ * TIM7_TRGO 0b010
+ * EXTI9 0b110
+ * SWTRIG 0b111
+ */
+static const DACConversionGroup dac_conv_grp_ch1 = {.num_channels = 1U, .trigger = DAC_TRG(0b000)};
+static const DACConversionGroup dac_conv_grp_ch2 = {.num_channels = 1U, .trigger = DAC_TRG(0b010)};
+
+void channel_1_start(void) {
+ gptStart(&GPTD6, &gpt6cfg1);
+ gptStartContinuous(&GPTD6, 2U);
+ palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+}
+
+void channel_1_stop(void) {
+ gptStopTimer(&GPTD6);
+ palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPad(GPIOA, 4);
+}
+
+static float channel_1_frequency = 0.0f;
+void channel_1_set_frequency(float freq) {
+ channel_1_frequency = freq;
+
+ channel_1_stop();
+ if (freq <= 0.0) // a pause/rest has freq=0
+ return;
+
+ gpt6cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
+ channel_1_start();
+}
+float channel_1_get_frequency(void) { return channel_1_frequency; }
+
+void channel_2_start(void) {
+ gptStart(&GPTD7, &gpt7cfg1);
+ gptStartContinuous(&GPTD7, 2U);
+ palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+}
+
+void channel_2_stop(void) {
+ gptStopTimer(&GPTD7);
+ palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL); \
+ palSetPad(GPIOA, 5);
+}
+
+static float channel_2_frequency = 0.0f;
+void channel_2_set_frequency(float freq) {
+ channel_2_frequency = freq;
+
+ channel_2_stop();
+ if (freq <= 0.0) // a pause/rest has freq=0
+ return;
+
+ gpt7cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;
+ channel_2_start();
+}
+float channel_2_get_frequency(void) { return channel_2_frequency; }
+
+static void gpt_audio_state_cb(GPTDriver *gptp) {
+ if (audio_update_state()) {
+#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)
+ // one piezo/speaker connected to both audio pins, the generated square-waves are inverted
+ channel_1_set_frequency(audio_get_processed_frequency(0));
+ channel_2_set_frequency(audio_get_processed_frequency(0));
+
+#else // two separate audio outputs/speakers
+ // primary speaker on A4, optional secondary on A5
+ if (AUDIO_PIN == A4) {
+ channel_1_set_frequency(audio_get_processed_frequency(0));
+ if (AUDIO_PIN_ALT == A5) {
+ if (audio_get_number_of_active_tones() > 1) {
+ channel_2_set_frequency(audio_get_processed_frequency(1));
+ } else {
+ channel_2_stop();
+ }
+ }
+ }
+
+ // primary speaker on A5, optional secondary on A4
+ if (AUDIO_PIN == A5) {
+ channel_2_set_frequency(audio_get_processed_frequency(0));
+ if (AUDIO_PIN_ALT == A4) {
+ if (audio_get_number_of_active_tones() > 1) {
+ channel_1_set_frequency(audio_get_processed_frequency(1));
+ } else {
+ channel_1_stop();
+ }
+ }
+ }
+#endif
+ }
+}
+
+void audio_driver_initialize() {
+ if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
+ palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);
+ dacStart(&DACD1, &dac_conf_ch1);
+
+ // initial setup of the dac-triggering timer is still required, even
+ // though it gets reconfigured and restarted later on
+ gptStart(&GPTD6, &gpt6cfg1);
+ }
+
+ if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
+ palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);
+ dacStart(&DACD2, &dac_conf_ch2);
+
+ gptStart(&GPTD7, &gpt7cfg1);
+ }
+
+ /* enable the output buffer, to directly drive external loads with no additional circuitry
+ *
+ * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers
+ * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer
+ * Note: enabling the output buffer imparts an additional dc-offset of a couple mV
+ *
+ * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet
+ * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'
+ */
+ DACD1.params->dac->CR &= ~DAC_CR_BOFF1;
+ DACD2.params->dac->CR &= ~DAC_CR_BOFF2;
+
+ // start state-updater
+ gptStart(&AUDIO_STATE_TIMER, &gptStateUpdateCfg);
+}
+
+void audio_driver_stop(void) {
+ if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
+ gptStopTimer(&GPTD6);
+
+ // stop the ongoing conversion and put the output in a known state
+ dacStopConversion(&DACD1);
+ dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);
+ }
+
+ if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
+ gptStopTimer(&GPTD7);
+
+ dacStopConversion(&DACD2);
+ dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);
+ }
+ gptStopTimer(&AUDIO_STATE_TIMER);
+}
+
+void audio_driver_start(void) {
+ if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {
+ dacStartConversion(&DACD1, &dac_conv_grp_ch1, (dacsample_t *)dac_buffer_1, AUDIO_DAC_BUFFER_SIZE);
+ }
+ if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {
+ dacStartConversion(&DACD2, &dac_conv_grp_ch2, (dacsample_t *)dac_buffer_2, AUDIO_DAC_BUFFER_SIZE);
+ }
+ gptStartContinuous(&AUDIO_STATE_TIMER, 2U);
+}
diff --git a/quantum/audio/driver_chibios_pwm.h b/quantum/audio/driver_chibios_pwm.h
new file mode 100644
index 0000000000..86cab916e1
--- /dev/null
+++ b/quantum/audio/driver_chibios_pwm.h
@@ -0,0 +1,40 @@
+/* Copyright 2020 Jack Humbert
+ * Copyright 2020 JohSchneider
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#if !defined(AUDIO_PWM_DRIVER)
+// NOTE: Timer2 seems to be used otherwise in QMK, otherwise we could default to A5 (= TIM2_CH1, with PWMD2 and alternate-function(1))
+# define AUDIO_PWM_DRIVER PWMD1
+#endif
+
+#if !defined(AUDIO_PWM_CHANNEL)
+// NOTE: sticking to the STM data-sheet numbering: TIMxCH1 to TIMxCH4
+// default: STM32F303CC PA8+TIM1_CH1 -> 1
+# define AUDIO_PWM_CHANNEL 1
+#endif
+
+#if !defined(AUDIO_PWM_PAL_MODE)
+// pin-alternate function: see the data-sheet for which pin needs what AF to connect to TIMx_CHy
+// default: STM32F303CC PA8+TIM1_CH1 -> 6
+# define AUDIO_PWM_PAL_MODE 6
+#endif
+
+#if !defined(AUDIO_STATE_TIMER)
+// timer used to trigger updates in the audio-system, configured/enabled in chibios mcuconf.
+// 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)
+# define AUDIO_STATE_TIMER GPTD6
+#endif
diff --git a/quantum/audio/driver_chibios_pwm_hardware.c b/quantum/audio/driver_chibios_pwm_hardware.c
new file mode 100644
index 0000000000..3c7d89b290
--- /dev/null
+++ b/quantum/audio/driver_chibios_pwm_hardware.c
@@ -0,0 +1,144 @@
+/* Copyright 2020 Jack Humbert
+ * Copyright 2020 JohSchneider
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+Audio Driver: PWM
+
+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.
+
+this driver uses the chibios-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware.
+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.
+
+ */
+
+#include "audio.h"
+#include "ch.h"
+#include "hal.h"
+
+#if !defined(AUDIO_PIN)
+# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
+#endif
+
+extern bool playing_note;
+extern bool playing_melody;
+extern uint8_t note_timbre;
+
+static PWMConfig pwmCFG = {
+ .frequency = 100000, /* PWM clock frequency */
+ // 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
+ .period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
+ .callback = NULL, /* no callback, the hardware directly toggles the pin */
+ .channels =
+ {
+#if AUDIO_PWM_CHANNEL == 4
+ {PWM_OUTPUT_DISABLED, NULL}, /* channel 0 -> TIMx_CH1 */
+ {PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */
+ {PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */
+ {PWM_OUTPUT_ACTIVE_HIGH, NULL} /* channel 3 -> TIMx_CH4 */
+#elif AUDIO_PWM_CHANNEL == 3
+ {PWM_OUTPUT_DISABLED, NULL},
+ {PWM_OUTPUT_DISABLED, NULL},
+ {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH3 */
+ {PWM_OUTPUT_DISABLED, NULL}
+#elif AUDIO_PWM_CHANNEL == 2
+ {PWM_OUTPUT_DISABLED, NULL},
+ {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH2 */
+ {PWM_OUTPUT_DISABLED, NULL},
+ {PWM_OUTPUT_DISABLED, NULL}
+#else /*fallback to CH1 */
+ {PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH1 */
+ {PWM_OUTPUT_DISABLED, NULL},
+ {PWM_OUTPUT_DISABLED, NULL},
+ {PWM_OUTPUT_DISABLED, NULL}
+#endif
+ },
+};
+
+static float channel_1_frequency = 0.0f;
+void channel_1_set_frequency(float freq) {
+ channel_1_frequency = freq;
+
+ if (freq <= 0.0) // a pause/rest has freq=0
+ return;
+
+ pwmcnt_t period = (pwmCFG.frequency / freq);
+ pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
+ pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
+ // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
+ PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
+}
+
+float channel_1_get_frequency(void) { return channel_1_frequency; }
+
+void channel_1_start(void) {
+ pwmStop(&AUDIO_PWM_DRIVER);
+ pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
+}
+
+void channel_1_stop(void) { pwmStop(&AUDIO_PWM_DRIVER); }
+
+static void gpt_callback(GPTDriver *gptp);
+GPTConfig gptCFG = {
+ /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
+ the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
+ the tempo (which might vary!) is in bpm (beats per minute)
+ therefore: if the timer ticks away at .frequency = (60*64)Hz,
+ and the .interval counts from 64 downwards - audio_update_state is
+ called just often enough to not miss any notes
+ */
+ .frequency = 60 * 64,
+ .callback = gpt_callback,
+};
+
+void audio_driver_initialize(void) {
+ pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
+
+ // connect the AUDIO_PIN to the PWM hardware
+#if defined(USE_GPIOV1) // STM32F103C8
+ palSetLineMode(AUDIO_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
+#else // GPIOv2 (or GPIOv3 for f4xx, which is the same/compatible at this command)
+ palSetLineMode(AUDIO_PIN, PAL_STM32_MODE_ALTERNATE | PAL_STM32_ALTERNATE(AUDIO_PWM_PAL_MODE));
+#endif
+
+ gptStart(&AUDIO_STATE_TIMER, &gptCFG);
+}
+
+void audio_driver_start(void) {
+ channel_1_stop();
+ channel_1_start();
+
+ if (playing_note || playing_melody) {
+ gptStartContinuous(&AUDIO_STATE_TIMER, 64);
+ }
+}
+
+void audio_driver_stop(void) {
+ channel_1_stop();
+ gptStopTimer(&AUDIO_STATE_TIMER);
+}
+
+/* a regular timer task, that checks the note to be currently played
+ * and updates the pwm to output that frequency
+ */
+static void gpt_callback(GPTDriver *gptp) {
+ float freq; // TODO: freq_alt
+
+ if (audio_update_state()) {
+ freq = audio_get_processed_frequency(0); // freq_alt would be index=1
+ channel_1_set_frequency(freq);
+ }
+}
diff --git a/quantum/audio/driver_chibios_pwm_software.c b/quantum/audio/driver_chibios_pwm_software.c
new file mode 100644
index 0000000000..15c3e98b6a
--- /dev/null
+++ b/quantum/audio/driver_chibios_pwm_software.c
@@ -0,0 +1,164 @@
+/* Copyright 2020 Jack Humbert
+ * Copyright 2020 JohSchneider
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+Audio Driver: PWM
+
+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.
+
+this driver uses the chibios-PWM system to produce a square-wave on any given output pin in software
+- a pwm callback is used to set/clear the configured pin.
+
+ */
+#include "audio.h"
+#include "ch.h"
+#include "hal.h"
+
+#if !defined(AUDIO_PIN)
+# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
+#endif
+extern bool playing_note;
+extern bool playing_melody;
+extern uint8_t note_timbre;
+
+static void pwm_audio_period_callback(PWMDriver *pwmp);
+static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp);
+
+static PWMConfig pwmCFG = {
+ .frequency = 100000, /* PWM clock frequency */
+ // 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
+ .period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
+ .callback = pwm_audio_period_callback,
+ .channels =
+ {
+ // software-PWM just needs another callback on any channel
+ {PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback}, /* channel 0 -> TIMx_CH1 */
+ {PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */
+ {PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */
+ {PWM_OUTPUT_DISABLED, NULL} /* channel 3 -> TIMx_CH4 */
+ },
+};
+
+static float channel_1_frequency = 0.0f;
+void channel_1_set_frequency(float freq) {
+ channel_1_frequency = freq;
+
+ if (freq <= 0.0) // a pause/rest has freq=0
+ return;
+
+ pwmcnt_t period = (pwmCFG.frequency / freq);
+ pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
+
+ pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
+ // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
+ PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
+}
+
+float channel_1_get_frequency(void) { return channel_1_frequency; }
+
+void channel_1_start(void) {
+ pwmStop(&AUDIO_PWM_DRIVER);
+ pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
+
+ pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER);
+ pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
+}
+
+void channel_1_stop(void) {
+ pwmStop(&AUDIO_PWM_DRIVER);
+
+ palClearLine(AUDIO_PIN); // leave the line low, after last note was played
+
+#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
+ palClearLine(AUDIO_PIN_ALT); // leave the line low, after last note was played
+#endif
+}
+
+// generate a PWM signal on any pin, not necessarily the one connected to the timer
+static void pwm_audio_period_callback(PWMDriver *pwmp) {
+ (void)pwmp;
+ palClearLine(AUDIO_PIN);
+
+#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
+ palSetLine(AUDIO_PIN_ALT);
+#endif
+}
+static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) {
+ (void)pwmp;
+ if (channel_1_frequency > 0) {
+ palSetLine(AUDIO_PIN); // generate a PWM signal on any pin, not necessarily the one connected to the timer
+#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
+ palClearLine(AUDIO_PIN_ALT);
+#endif
+ }
+}
+
+static void gpt_callback(GPTDriver *gptp);
+GPTConfig gptCFG = {
+ /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
+ the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
+ the tempo (which might vary!) is in bpm (beats per minute)
+ therefore: if the timer ticks away at .frequency = (60*64)Hz,
+ and the .interval counts from 64 downwards - audio_update_state is
+ called just often enough to not miss anything
+ */
+ .frequency = 60 * 64,
+ .callback = gpt_callback,
+};
+
+void audio_driver_initialize(void) {
+ pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
+
+ palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearLine(AUDIO_PIN);
+
+#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)
+ palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearLine(AUDIO_PIN_ALT);
+#endif
+
+ pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); // enable pwm callbacks
+ pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);
+
+ gptStart(&AUDIO_STATE_TIMER, &gptCFG);
+}
+
+void audio_driver_start(void) {
+ channel_1_stop();
+ channel_1_start();
+
+ if (playing_note || playing_melody) {
+ gptStartContinuous(&AUDIO_STATE_TIMER, 64);
+ }
+}
+
+void audio_driver_stop(void) {
+ channel_1_stop();
+ gptStopTimer(&AUDIO_STATE_TIMER);
+}
+
+/* a regular timer task, that checks the note to be currently played
+ * and updates the pwm to output that frequency
+ */
+static void gpt_callback(GPTDriver *gptp) {
+ float freq; // TODO: freq_alt
+
+ if (audio_update_state()) {
+ freq = audio_get_processed_frequency(0); // freq_alt would be index=1
+ channel_1_set_frequency(freq);
+ }
+}
diff --git a/quantum/audio/musical_notes.h b/quantum/audio/musical_notes.h
index 8ac6aafd38..ddd7d374f5 100644
--- a/quantum/audio/musical_notes.h
+++ b/quantum/audio/musical_notes.h
@@ -1,4 +1,5 @@
/* Copyright 2016 Jack Humbert
+ * Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -13,11 +14,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
#pragma once
-// Tempo Placeholder
-#define TEMPO_DEFAULT 100
+#ifndef TEMPO_DEFAULT
+# define TEMPO_DEFAULT 120
+// in beats-per-minute
+#endif
#define SONG(notes...) \
{ notes }
@@ -25,12 +27,14 @@
// Note Types
#define MUSICAL_NOTE(note, duration) \
{ (NOTE##note), duration }
+
#define BREVE_NOTE(note) MUSICAL_NOTE(note, 128)
#define WHOLE_NOTE(note) MUSICAL_NOTE(note, 64)
#define HALF_NOTE(note) MUSICAL_NOTE(note, 32)
#define QUARTER_NOTE(note) MUSICAL_NOTE(note, 16)
#define EIGHTH_NOTE(note) MUSICAL_NOTE(note, 8)
#define SIXTEENTH_NOTE(note) MUSICAL_NOTE(note, 4)
+#define THIRTYSECOND_NOTE(note) MUSICAL_NOTE(note, 2)
#define BREVE_DOT_NOTE(note) MUSICAL_NOTE(note, 128 + 64)
#define WHOLE_DOT_NOTE(note) MUSICAL_NOTE(note, 64 + 32)
@@ -38,6 +42,9 @@
#define QUARTER_DOT_NOTE(note) MUSICAL_NOTE(note, 16 + 8)
#define EIGHTH_DOT_NOTE(note) MUSICAL_NOTE(note, 8 + 4)
#define SIXTEENTH_DOT_NOTE(note) MUSICAL_NOTE(note, 4 + 2)
+#define THIRTYSECOND_DOT_NOTE(note) MUSICAL_NOTE(note, 2 + 1)
+// duration of 64 units == one beat == one whole note
+// with a tempo of 60bpm this comes to a length of one second
// Note Type Shortcuts
#define M__NOTE(note, duration) MUSICAL_NOTE(note, duration)
@@ -47,55 +54,52 @@
#define Q__NOTE(n) QUARTER_NOTE(n)
#define E__NOTE(n) EIGHTH_NOTE(n)
#define S__NOTE(n) SIXTEENTH_NOTE(n)
+#define T__NOTE(n) THIRTYSECOND_NOTE(n)
#define BD_NOTE(n) BREVE_DOT_NOTE(n)
#define WD_NOTE(n) WHOLE_DOT_NOTE(n)
#define HD_NOTE(n) HALF_DOT_NOTE(n)
#define QD_NOTE(n) QUARTER_DOT_NOTE(n)
#define ED_NOTE(n) EIGHTH_DOT_NOTE(n)
#define SD_NOTE(n) SIXTEENTH_DOT_NOTE(n)
+#define TD_NOTE(n) THIRTYSECOND_DOT_NOTE(n)
// Note Timbre
// Changes how the notes sound
-#define TIMBRE_12 0.125f
-#define TIMBRE_25 0.250f
-#define TIMBRE_50 0.500f
-#define TIMBRE_75 0.750f
-#define TIMBRE_DEFAULT TIMBRE_50
+#define TIMBRE_12 12
+#define TIMBRE_25 25
+#define TIMBRE_50 50
+#define TIMBRE_75 75
+#ifndef TIMBRE_DEFAULT
+# define TIMBRE_DEFAULT TIMBRE_50
+#endif
// Notes - # = Octave
-#ifdef __arm__
-# define NOTE_REST 1.00f
-#else
-# define NOTE_REST 0.00f
-#endif
-
-/* These notes are currently bugged
-#define NOTE_C0 16.35f
-#define NOTE_CS0 17.32f
-#define NOTE_D0 18.35f
-#define NOTE_DS0 19.45f
-#define NOTE_E0 20.60f
-#define NOTE_F0 21.83f
-#define NOTE_FS0 23.12f
-#define NOTE_G0 24.50f
-#define NOTE_GS0 25.96f
-#define NOTE_A0 27.50f
-#define NOTE_AS0 29.14f
-#define NOTE_B0 30.87f
-#define NOTE_C1 32.70f
-#define NOTE_CS1 34.65f
-#define NOTE_D1 36.71f
-#define NOTE_DS1 38.89f
-#define NOTE_E1 41.20f
-#define NOTE_F1 43.65f
-#define NOTE_FS1 46.25f
-#define NOTE_G1 49.00f
-#define NOTE_GS1 51.91f
-#define NOTE_A1 55.00f
-#define NOTE_AS1 58.27f
-*/
+#define NOTE_REST 0.00f
+#define NOTE_C0 16.35f
+#define NOTE_CS0 17.32f
+#define NOTE_D0 18.35f
+#define NOTE_DS0 19.45f
+#define NOTE_E0 20.60f
+#define NOTE_F0 21.83f
+#define NOTE_FS0 23.12f
+#define NOTE_G0 24.50f
+#define NOTE_GS0 25.96f
+#define NOTE_A0 27.50f
+#define NOTE_AS0 29.14f
+#define NOTE_B0 30.87f
+#define NOTE_C1 32.70f
+#define NOTE_CS1 34.65f
+#define NOTE_D1 36.71f
+#define NOTE_DS1 38.89f
+#define NOTE_E1 41.20f
+#define NOTE_F1 43.65f
+#define NOTE_FS1 46.25f
+#define NOTE_G1 49.00f
+#define NOTE_GS1 51.91f
+#define NOTE_A1 55.00f
+#define NOTE_AS1 58.27f
#define NOTE_B1 61.74f
#define NOTE_C2 65.41f
#define NOTE_CS2 69.30f
diff --git a/quantum/audio/voices.c b/quantum/audio/voices.c
index 1592618be4..8988d827e9 100644
--- a/quantum/audio/voices.c
+++ b/quantum/audio/voices.c
@@ -1,4 +1,5 @@
/* Copyright 2016 Jack Humbert
+ * Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,13 +18,19 @@
#include "audio.h"
#include <stdlib.h>
-// these are imported from audio.c
-extern uint16_t envelope_index;
-extern float note_timbre;
-extern float polyphony_rate;
-extern bool glissando;
+uint8_t note_timbre = TIMBRE_DEFAULT;
+bool glissando = false;
+bool vibrato = false;
+float vibrato_strength = 0.5;
+float vibrato_rate = 0.125;
+uint16_t voices_timer = 0;
+
+#ifdef AUDIO_VOICE_DEFAULT
+voice_type voice = AUDIO_VOICE_DEFAULT;
+#else
voice_type voice = default_voice;
+#endif
void set_voice(voice_type v) { voice = v; }
@@ -31,22 +38,54 @@ void voice_iterate() { voice = (voice + 1) % number_of_voices; }
void voice_deiterate() { voice = (voice - 1 + number_of_voices) % number_of_voices; }
+#ifdef AUDIO_VOICES
+float mod(float a, int b) {
+ float r = fmod(a, b);
+ return r < 0 ? r + b : r;
+}
+
+// Effect: 'vibrate' a given target frequency slightly above/below its initial value
+float voice_add_vibrato(float average_freq) {
+ float vibrato_counter = mod(timer_read() / (100 * vibrato_rate), VIBRATO_LUT_LENGTH);
+
+ return average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength);
+}
+
+// Effect: 'slides' the 'frequency' from the starting-point, to the target frequency
+float voice_add_glissando(float from_freq, float to_freq) {
+ if (to_freq != 0 && from_freq < to_freq && from_freq < to_freq * pow(2, -440 / to_freq / 12 / 2)) {
+ return from_freq * pow(2, 440 / from_freq / 12 / 2);
+ } else if (to_freq != 0 && from_freq > to_freq && from_freq > to_freq * pow(2, 440 / to_freq / 12 / 2)) {
+ return from_freq * pow(2, -440 / from_freq / 12 / 2);
+ } else {
+ return to_freq;
+ }
+}
+#endif
+
float voice_envelope(float frequency) {
// envelope_index ranges from 0 to 0xFFFF, which is preserved at 880.0 Hz
- __attribute__((unused)) uint16_t compensated_index = (uint16_t)((float)envelope_index * (880.0 / frequency));
+// __attribute__((unused)) uint16_t compensated_index = (uint16_t)((float)envelope_index * (880.0 / frequency));
+#ifdef AUDIO_VOICES
+ uint16_t envelope_index = timer_elapsed(voices_timer); // TODO: multiply in some factor?
+ uint16_t compensated_index = envelope_index / 100; // TODO: correct factor would be?
+#endif
switch (voice) {
case default_voice:
- glissando = false;
- note_timbre = TIMBRE_50;
- polyphony_rate = 0;
+ glissando = false;
+ // note_timbre = TIMBRE_50; //Note: leave the user the possibility to adjust the timbre with 'audio_set_timbre'
break;
#ifdef AUDIO_VOICES
+ case vibrating:
+ glissando = false;
+ vibrato = true;
+ break;
+
case something:
- glissando = false;
- polyphony_rate = 0;
+ glissando = false;
switch (compensated_index) {
case 0 ... 9:
note_timbre = TIMBRE_12;
@@ -57,24 +96,23 @@ float voice_envelope(float frequency) {
break;
case 20 ... 200:
- note_timbre = .125 + .125;
+ note_timbre = 12 + 12;
break;
default:
- note_timbre = .125;
+ note_timbre = 12;
break;
}
break;
case drums:
- glissando = false;
- polyphony_rate = 0;
+ glissando = false;
// switch (compensated_index) {
// case 0 ... 10:
- // note_timbre = 0.5;
+ // note_timbre = 50;
// break;
// case 11 ... 20:
- // note_timbre = 0.5 * (21 - compensated_index) / 10;
+ // note_timbre = 50 * (21 - compensated_index) / 10;
// break;
// default:
// note_timbre = 0;
@@ -88,10 +126,10 @@ float voice_envelope(float frequency) {
frequency = (rand() % (int)(40)) + 60;
switch (envelope_index) {
case 0 ... 10:
- note_timbre = 0.5;
+ note_timbre = 50;
break;
case 11 ... 20:
- note_timbre = 0.5 * (21 - envelope_index) / 10;
+ note_timbre = 50 * (21 - envelope_index) / 10;
break;
default:
note_timbre = 0;
@@ -103,10 +141,10 @@ float voice_envelope(float frequency) {
frequency = (rand() % (int)(1000)) + 1000;
switch (envelope_index) {
case 0 ... 5:
- note_timbre = 0.5;
+ note_timbre = 50;
break;
case 6 ... 20:
- note_timbre = 0.5 * (21 - envelope_index) / 15;
+ note_timbre = 50 * (21 - envelope_index) / 15;
break;
default:
note_timbre = 0;
@@ -118,10 +156,10 @@ float voice_envelope(float frequency) {
frequency = (rand() % (int)(2000)) + 3000;
switch (envelope_index) {
case 0 ... 15:
- note_timbre = 0.5;
+ note_timbre = 50;
break;
case 16 ... 20:
- note_timbre = 0.5 * (21 - envelope_index) / 5;
+ note_timbre = 50 * (21 - envelope_index) / 5;
break;
default:
note_timbre = 0;
@@ -133,10 +171,10 @@ float voice_envelope(float frequency) {
frequency = (rand() % (int)(2000)) + 3000;
switch (envelope_index) {
case 0 ... 35:
- note_timbre = 0.5;
+ note_timbre = 50;
break;
case 36 ... 50:
- note_timbre = 0.5 * (51 - envelope_index) / 15;
+ note_timbre = 50 * (51 - envelope_index) / 15;
break;
default:
note_timbre = 0;
@@ -145,8 +183,7 @@ float voice_envelope(float frequency) {
}
break;
case butts_fader:
- glissando = true;
- polyphony_rate = 0;
+ glissando = true;
switch (compensated_index) {
case 0 ... 9:
frequency = frequency / 4;
@@ -159,7 +196,7 @@ float voice_envelope(float frequency) {
break;
case 20 ... 200:
- note_timbre = .125 - pow(((float)compensated_index - 20) / (200 - 20), 2) * .125;
+ note_timbre = 12 - (uint8_t)(pow(((float)compensated_index - 20) / (200 - 20), 2) * 12.5);
break;
default:
@@ -169,7 +206,6 @@ float voice_envelope(float frequency) {
break;
// case octave_crunch:
- // polyphony_rate = 0;
// switch (compensated_index) {
// case 0 ... 9:
// case 20 ... 24:
@@ -187,14 +223,13 @@ float voice_envelope(float frequency) {
// default:
// note_timbre = TIMBRE_12;
- // break;
+ // break;
// }
// break;
case duty_osc:
// This slows the loop down a substantial amount, so higher notes may freeze
- glissando = true;
- polyphony_rate = 0;
+ glissando = true;
switch (compensated_index) {
default:
# define OCS_SPEED 10
@@ -202,38 +237,36 @@ float voice_envelope(float frequency) {
// sine wave is slow
// note_timbre = (sin((float)compensated_index/10000*OCS_SPEED) * OCS_AMP / 2) + .5;
// triangle wave is a bit faster
- note_timbre = (float)abs((compensated_index * OCS_SPEED % 3000) - 1500) * (OCS_AMP / 1500) + (1 - OCS_AMP) / 2;
+ note_timbre = (uint8_t)abs((compensated_index * OCS_SPEED % 3000) - 1500) * (OCS_AMP / 1500) + (1 - OCS_AMP) / 2;
break;
}
break;
case duty_octave_down:
- glissando = true;
- polyphony_rate = 0;
- note_timbre = (envelope_index % 2) * .125 + .375 * 2;
- if ((envelope_index % 4) == 0) note_timbre = 0.5;
+ glissando = true;
+ note_timbre = (uint8_t)(100 * (envelope_index % 2) * .125 + .375 * 2);
+ if ((envelope_index % 4) == 0) note_timbre = 50;
if ((envelope_index % 8) == 0) note_timbre = 0;
break;
case delayed_vibrato:
- glissando = true;
- polyphony_rate = 0;
- note_timbre = TIMBRE_50;
+ glissando = true;
+ note_timbre = TIMBRE_50;
# define VOICE_VIBRATO_DELAY 150
# define VOICE_VIBRATO_SPEED 50
switch (compensated_index) {
case 0 ... VOICE_VIBRATO_DELAY:
break;
default:
+ // TODO: merge/replace with voice_add_vibrato above
frequency = frequency * vibrato_lut[(int)fmod((((float)compensated_index - (VOICE_VIBRATO_DELAY + 1)) / 1000 * VOICE_VIBRATO_SPEED), VIBRATO_LUT_LENGTH)];
break;
}
break;
// case delayed_vibrato_octave:
- // polyphony_rate = 0;
// if ((envelope_index % 2) == 1) {
- // note_timbre = 0.55;
+ // note_timbre = 55;
// } else {
- // note_timbre = 0.45;
+ // note_timbre = 45;
// }
// #define VOICE_VIBRATO_DELAY 150
// #define VOICE_VIBRATO_SPEED 50
@@ -246,35 +279,64 @@ float voice_envelope(float frequency) {
// }
// break;
// case duty_fifth_down:
- // note_timbre = 0.5;
+ // note_timbre = TIMBRE_50;
// if ((envelope_index % 3) == 0)
- // note_timbre = 0.75;
+ // note_timbre = TIMBRE_75;
// break;
// case duty_fourth_down:
- // note_timbre = 0.0;
+ // note_timbre = 0;
// if ((envelope_index % 12) == 0)
- // note_timbre = 0.75;
+ // note_timbre = TIMBRE_75;
// if (((envelope_index % 12) % 4) != 1)
- // note_timbre = 0.75;
+ // note_timbre = TIMBRE_75;
// break;
// case duty_third_down:
- // note_timbre = 0.5;
+ // note_timbre = TIMBRE_50;
// if ((envelope_index % 5) == 0)
- // note_timbre = 0.75;
+ // note_timbre = TIMBRE_75;
// break;
// case duty_fifth_third_down:
- // note_timbre = 0.5;
+ // note_timbre = TIMBRE_50;
// if ((envelope_index % 5) == 0)
- // note_timbre = 0.75;
+ // note_timbre = TIMBRE_75;
// if ((envelope_index % 3) == 0)
- // note_timbre = 0.25;
+ // note_timbre = TIMBRE_25;
// break;
-#endif
+#endif // AUDIO_VOICES
default:
break;
}
+#ifdef AUDIO_VOICES
+ if (vibrato && (vibrato_strength > 0)) {
+ frequency = voice_add_vibrato(frequency);
+ }
+
+ if (glissando) {
+ // TODO: where to keep track of the start-frequency?
+ // frequency = voice_add_glissando(??, frequency);
+ }
+#endif // AUDIO_VOICES
+
return frequency;
}
+
+// Vibrato functions
+
+void voice_set_vibrato_rate(float rate) { vibrato_rate = rate; }
+void voice_increase_vibrato_rate(float change) { vibrato_rate *= change; }
+void voice_decrease_vibrato_rate(float change) { vibrato_rate /= change; }
+void voice_set_vibrato_strength(float strength) { vibrato_strength = strength; }
+void voice_increase_vibrato_strength(float change) { vibrato_strength *= change; }
+void voice_decrease_vibrato_strength(float change) { vibrato_strength /= change; }
+
+// Timbre functions
+
+void voice_set_timbre(uint8_t timbre) {
+ if ((timbre > 0) && (timbre < 100)) {
+ note_timbre = timbre;
+ }
+}
+uint8_t voice_get_timbre(void) { return note_timbre; }
diff --git a/quantum/audio/voices.h b/quantum/audio/voices.h
index abafa2b404..578350d337 100644
--- a/quantum/audio/voices.h
+++ b/quantum/audio/voices.h
@@ -1,4 +1,5 @@
/* Copyright 2016 Jack Humbert
+ * Copyright 2020 JohSchneider
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -13,7 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
#pragma once
#include <stdint.h>
@@ -29,6 +29,7 @@ float voice_envelope(float frequency);
typedef enum {
default_voice,
#ifdef AUDIO_VOICES
+ vibrating,
something,
drums,
butts_fader,
@@ -48,3 +49,21 @@ typedef enum {
void set_voice(voice_type v);
void voice_iterate(void);
void voice_deiterate(void);
+
+// Vibrato functions
+void voice_set_vibrato_rate(float rate);
+void voice_increase_vibrato_rate(float change);
+void voice_decrease_vibrato_rate(float change);
+void voice_set_vibrato_strength(float strength);
+void voice_increase_vibrato_strength(float change);
+void voice_decrease_vibrato_strength(float change);
+
+// Timbre functions
+/**
+ * @brief set the global timbre for tones to be played
+ * @note: only applies to pwm implementations - where it adjusts the duty-cycle
+ * @note: using any instrument from voices.[ch] other than 'default' may override the set value
+ * @param[in]: timbre: valid range is (0,100)
+ */
+void voice_set_timbre(uint8_t timbre);
+uint8_t voice_get_timbre(void);
diff --git a/quantum/audio/wave.h b/quantum/audio/wave.h
deleted file mode 100644
index 48210a944e..0000000000
--- a/quantum/audio/wave.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright 2016 Jack Humbert
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <avr/io.h>
-#include <avr/interrupt.h>
-#include <avr/pgmspace.h>
-
-#define SINE_LENGTH 2048
-
-const uint8_t sinewave[] PROGMEM = // 2048 values
- {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,
- 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,
- 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,
- 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,
- 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,
- 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,
- 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,
- 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,
- 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,
- 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,
- 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,
- 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,
- 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 4d66da80ba..e47192de34 100644
--- a/quantum/backlight/backlight_avr.c
+++ b/quantum/backlight/backlight_avr.c
@@ -68,7 +68,7 @@
# define COMxx1 COM3A1
# define OCRxx OCR3A
# endif
-#elif (defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__)) && (BACKLIGHT_PIN == B7 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6)
+#elif (defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__)) && (BACKLIGHT_PIN == B7 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6)
# define HARDWARE_PWM
# define ICRx ICR1
# define TCCRxA TCCR1A
@@ -126,7 +126,7 @@
# define COMxx1 COM1B1
# define OCRxx OCR1B
# endif
-#elif !defined(B5_AUDIO) && !defined(B6_AUDIO) && !defined(B7_AUDIO)
+#elif (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)
// Timer 1 is not in use by Audio feature, Backlight can use it
# pragma message "Using hardware timer 1 with software PWM"
# define HARDWARE_PWM
@@ -145,7 +145,7 @@
# define OCIExA OCIE1A
# define OCRxx OCR1A
-#elif !defined(C6_AUDIO) && !defined(C5_AUDIO) && !defined(C4_AUDIO)
+#elif (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6)
# pragma message "Using hardware timer 3 with software PWM"
// Timer 3 is not in use by Audio feature, Backlight can use it
# define HARDWARE_PWM
diff --git a/quantum/command.c b/quantum/command.c
index 59aa4e4d34..34c4b36b1c 100644
--- a/quantum/command.c
+++ b/quantum/command.c
@@ -550,22 +550,22 @@ static void mousekey_param_print(void) {
# if !defined(NO_PRINT) && !defined(USER_PRINT)
print("\n\t- Values -\n");
print("1: delay(*10ms): ");
- pdec(mk_delay);
+ print_dec(mk_delay);
print("\n");
print("2: interval(ms): ");
- pdec(mk_interval);
+ print_dec(mk_interval);
print("\n");
print("3: max_speed: ");
- pdec(mk_max_speed);
+ print_dec(mk_max_speed);
print("\n");
print("4: time_to_max: ");
- pdec(mk_time_to_max);
+ print_dec(mk_time_to_max);
print("\n");
print("5: wheel_max_speed: ");
- pdec(mk_wheel_max_speed);
+ print_dec(mk_wheel_max_speed);
print("\n");
print("6: wheel_time_to_max: ");
- pdec(mk_wheel_time_to_max);
+ print_dec(mk_wheel_time_to_max);
print("\n");
# endif /* !NO_PRINT */
}
diff --git a/quantum/debounce/sym_defer_pk.c b/quantum/debounce/sym_defer_pk.c
index 6c0e3bb071..60513f98e1 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.
#include "quantum.h"
#include <stdlib.h>
+#ifdef PROTOCOL_CHIBIOS
+# if CH_CFG_USE_MEMCORE == FALSE
+# 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.
+# endif
+#endif
+
#ifndef DEBOUNCE
# define DEBOUNCE 5
#endif
diff --git a/quantum/debounce/sym_eager_pk.c b/quantum/debounce/sym_eager_pk.c
index 93a40ad441..e66cf92d79 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.
#include "quantum.h"
#include <stdlib.h>
+#ifdef PROTOCOL_CHIBIOS
+# if CH_CFG_USE_MEMCORE == FALSE
+# 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.
+# endif
+#endif
+
#ifndef DEBOUNCE
# define DEBOUNCE 5
#endif
diff --git a/quantum/debounce/sym_eager_pr.c b/quantum/debounce/sym_eager_pr.c
index d12931fddb..20ccb46f1d 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.
#include "quantum.h"
#include <stdlib.h>
+#ifdef PROTOCOL_CHIBIOS
+# if CH_CFG_USE_MEMCORE == FALSE
+# 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.
+# endif
+#endif
+
#ifndef DEBOUNCE
# define DEBOUNCE 5
#endif
diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c
index 0608b469c0..a860b94979 100644
--- a/quantum/dynamic_keymap.c
+++ b/quantum/dynamic_keymap.c
@@ -37,6 +37,8 @@
#ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR
# if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 2047
+# elif defined(__AVR_AT90USB162__)
+# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 511
# else
# define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 1023
# endif
diff --git a/quantum/encoder.c b/quantum/encoder.c
index 7ca31afedc..2ed64c1e30 100644
--- a/quantum/encoder.c
+++ b/quantum/encoder.c
@@ -94,8 +94,9 @@ void encoder_init(void) {
#endif
}
-static void encoder_update(int8_t index, uint8_t state) {
- uint8_t i = index;
+static bool encoder_update(int8_t index, uint8_t state) {
+ bool changed = false;
+ uint8_t i = index;
#ifdef ENCODER_RESOLUTIONS
int8_t resolution = encoder_resolutions[i];
@@ -109,40 +110,53 @@ static void encoder_update(int8_t index, uint8_t state) {
encoder_pulses[i] += encoder_LUT[state & 0xF];
if (encoder_pulses[i] >= resolution) {
encoder_value[index]++;
+ changed = true;
encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
}
if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise
encoder_value[index]--;
+ changed = true;
encoder_update_kb(index, ENCODER_CLOCKWISE);
}
encoder_pulses[i] %= resolution;
+ return changed;
}
-void encoder_read(void) {
+bool encoder_read(void) {
+ bool changed = false;
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
encoder_state[i] <<= 2;
encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
- encoder_update(i, encoder_state[i]);
+ changed |= encoder_update(i, encoder_state[i]);
}
+ return changed;
}
#ifdef SPLIT_KEYBOARD
+void last_encoder_activity_trigger(void);
+
void encoder_state_raw(uint8_t* slave_state) { memcpy(slave_state, &encoder_value[thisHand], sizeof(uint8_t) * NUMBER_OF_ENCODERS); }
void encoder_update_raw(uint8_t* slave_state) {
+ bool changed = false;
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
uint8_t index = i + thatHand;
int8_t delta = slave_state[i] - encoder_value[index];
while (delta > 0) {
delta--;
encoder_value[index]++;
+ changed = true;
encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
}
while (delta < 0) {
delta++;
encoder_value[index]--;
+ changed = true;
encoder_update_kb(index, ENCODER_CLOCKWISE);
}
}
+
+ // Update the last encoder input time -- handled external to encoder_read() when we're running a split
+ if (changed) last_encoder_activity_trigger();
}
#endif
diff --git a/quantum/encoder.h b/quantum/encoder.h
index ec09a8cc47..db6f220da4 100644
--- a/quantum/encoder.h
+++ b/quantum/encoder.h
@@ -20,7 +20,7 @@
#include "quantum.h"
void encoder_init(void);
-void encoder_read(void);
+bool encoder_read(void);
void encoder_update_kb(int8_t index, bool clockwise);
void encoder_update_user(int8_t index, bool clockwise);
diff --git a/quantum/fauxclicky.c b/quantum/fauxclicky.c
deleted file mode 100644
index 53499c9c1e..0000000000
--- a/quantum/fauxclicky.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-Copyright 2017 Priyadi Iman Nurcahyo
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <avr/interrupt.h>
-#include <avr/io.h>
-#include "timer.h"
-#include "fauxclicky.h"
-#include <stdbool.h>
-#include "musical_notes.h"
-
-bool fauxclicky_enabled = true;
-uint16_t note_start = 0;
-bool note_playing = false;
-uint16_t note_period = 0;
-
-void fauxclicky_init() {
- // Set port PC6 (OC3A and /OC4A) as output
- DDRC |= _BV(PORTC6);
-
- // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers
- TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30);
- TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30);
-}
-
-void fauxclicky_stop() {
- FAUXCLICKY_DISABLE_OUTPUT;
- note_playing = false;
-}
-
-void fauxclicky_play(float note[]) {
- if (!fauxclicky_enabled) return;
- if (note_playing) fauxclicky_stop();
- FAUXCLICKY_TIMER_PERIOD = (uint16_t)(((float)F_CPU) / (note[0] * (float)FAUXCLICKY_CPU_PRESCALER));
- FAUXCLICKY_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (note[0] * (float)FAUXCLICKY_CPU_PRESCALER)) / (float)2);
- note_playing = true;
- note_period = (note[1] / (float)16) * ((float)60 / (float)FAUXCLICKY_TEMPO) * 1000;
- note_start = timer_read();
- FAUXCLICKY_ENABLE_OUTPUT;
-}
-
-void fauxclicky_check() {
- if (!note_playing) return;
-
- if (timer_elapsed(note_start) > note_period) {
- fauxclicky_stop();
- }
-}
diff --git a/quantum/fauxclicky.h b/quantum/fauxclicky.h
deleted file mode 100644
index ed54d0edcf..0000000000
--- a/quantum/fauxclicky.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
-Copyright 2017 Priyadi Iman Nurcahyo
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or
-(at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#ifdef AUDIO_ENABLE
-# error "AUDIO_ENABLE and FAUXCLICKY_ENABLE cannot be both enabled"
-#endif
-
-#include "musical_notes.h"
-#include <stdbool.h>
-
-__attribute__((weak)) float fauxclicky_pressed_note[2] = MUSICAL_NOTE(_D4, 0.25);
-__attribute__((weak)) float fauxclicky_released_note[2] = MUSICAL_NOTE(_C4, 0.125);
-__attribute__((weak)) float fauxclicky_beep_note[2] = MUSICAL_NOTE(_C4, 0.25);
-
-extern bool fauxclicky_enabled;
-
-//
-// tempo in BPM
-//
-
-#ifndef FAUXCLICKY_TEMPO
-# define FAUXCLICKY_TEMPO TEMPO_DEFAULT
-#endif
-
-// beep on press
-#define FAUXCLICKY_ACTION_PRESS fauxclicky_play(fauxclicky_pressed_note)
-
-// beep on release
-#define FAUXCLICKY_ACTION_RELEASE fauxclicky_play(fauxclicky_released_note)
-
-// general purpose beep
-#define FAUXCLICKY_BEEP fauxclicky_play(fauxclicky_beep_note)
-
-// enable
-#define FAUXCLICKY_ON fauxclicky_enabled = true
-
-// disable
-#define FAUXCLICKY_OFF \
- do { \
- fauxclicky_enabled = false; \
- fauxclicky_stop(); \
- } while (0)
-
-// toggle
-#define FAUXCLICKY_TOGGLE \
- do { \
- if (fauxclicky_enabled) { \
- FAUXCLICKY_OFF; \
- } else { \
- FAUXCLICKY_ON; \
- } \
- } while (0)
-
-//
-// pin configuration
-//
-
-#ifndef FAUXCLICKY_CPU_PRESCALER
-# define FAUXCLICKY_CPU_PRESCALER 8
-#endif
-
-#ifndef FAUXCLICKY_ENABLE_OUTPUT
-# define FAUXCLICKY_ENABLE_OUTPUT TCCR3A |= _BV(COM3A1)
-#endif
-
-#ifndef FAUXCLICKY_DISABLE_OUTPUT
-# define FAUXCLICKY_DISABLE_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0))
-#endif
-
-#ifndef FAUXCLICKY_TIMER_PERIOD
-# define FAUXCLICKY_TIMER_PERIOD ICR3
-#endif
-
-#ifndef FAUXCLICKY_DUTY_CYCLE
-# define FAUXCLICKY_DUTY_CYCLE OCR3A
-#endif
-
-//
-// definitions
-//
-
-void fauxclicky_init(void);
-void fauxclicky_stop(void);
-void fauxclicky_play(float note[2]);
-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 0000000000..b2b3a734c9
--- /dev/null
+++ b/quantum/keymap_extras/keymap_us_extended.h
@@ -0,0 +1,227 @@
+/* Copyright 2020
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "keymap.h"
+
+// clang-format off
+
+/*
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \  │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │        │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │    │    │    │                        │    │    │    │    │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+// Row 1
+#define US_GRV KC_GRV // `
+#define US_1 KC_1 // 1
+#define US_2 KC_2 // 2
+#define US_3 KC_3 // 3
+#define US_4 KC_4 // 4
+#define US_5 KC_5 // 5
+#define US_6 KC_6 // 6
+#define US_7 KC_7 // 7
+#define US_8 KC_8 // 8
+#define US_9 KC_9 // 9
+#define US_0 KC_0 // 0
+#define US_MINS KC_MINS // -
+#define US_EQL KC_EQL // =
+// Row 2
+#define US_Q KC_Q // Q
+#define US_W KC_W // W
+#define US_E KC_E // E
+#define US_R KC_R // R
+#define US_T KC_T // T
+#define US_Y KC_Y // Y
+#define US_U KC_U // U
+#define US_I KC_I // I
+#define US_O KC_O // O
+#define US_P KC_P // P
+#define US_LBRC KC_LBRC // [
+#define US_RBRC KC_RBRC // ]
+#define US_BSLS KC_BSLS // (backslash)
+// Row 3
+#define US_A KC_A // A
+#define US_S KC_S // S
+#define US_D KC_D // D
+#define US_F KC_F // F
+#define US_G KC_G // G
+#define US_H KC_H // H
+#define US_J KC_J // J
+#define US_K KC_K // K
+#define US_L KC_L // L
+#define US_SCLN KC_SCLN // ;
+#define US_QUOT KC_QUOT // '
+// Row 4
+#define US_Z KC_Z // Z
+#define US_X KC_X // X
+#define US_C KC_C // C
+#define US_V KC_V // V
+#define US_B KC_B // B
+#define US_N KC_N // N
+#define US_M KC_M // M
+#define US_COMM KC_COMM // ,
+#define US_DOT KC_DOT // .
+#define US_SLSH KC_SLSH // /
+
+/* Shifted symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │      │   │   │   │   │   │   │   │   │   │ : │ " │        │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │    │    │    │                        │    │    │    │    │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+// Row 1
+#define US_TILD S(US_GRV) // ~
+#define US_EXLM S(US_1) // !
+#define US_AT S(US_2) // @
+#define US_HASH S(US_3) // #
+#define US_DLR S(US_4) // $
+#define US_PERC S(US_5) // %
+#define US_CIRC S(US_6) // ^
+#define US_AMPR S(US_7) // &
+#define US_ASTR S(US_8) // *
+#define US_LPRN S(US_9) // (
+#define US_RPRN S(US_0) // )
+#define US_UNDS S(US_MINS) // _
+#define US_PLUS S(US_EQL) // +
+// Row 2
+#define US_LCBR S(US_LBRC) // {
+#define US_RCBR S(US_RBRC) // }
+#define US_PIPE S(US_BSLS) // |
+// Row 3
+#define US_COLN S(US_SCLN) // :
+#define US_DQUO S(US_QUOT) // "
+// Row 4
+#define US_LABK S(US_COMM) // <
+#define US_RABK S(US_DOT) // >
+#define US_QUES S(US_SLSH) // ?
+
+/* AltGr symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ` │ ¹ │ ² │ ³ │ ¤ │ € │ ^ │ ̛  │ ¾ │ ‘ │ ’ │ ¥ │ × │       │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │     │ Ä │ Å │ É │ ® │ Þ │ Ü │ Ú │ Í │ Ó │ Ö │ « │ » │  ¬  │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │      │ Á │ ß │ Ð │   │   │   │ Ï │ Œ │ Ø │ ¶ │ ' │        │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │        │ Æ │   │ © │   │   │ Ñ │ µ │ Ç │ ˙ │ ¿ │          │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │    │    │    │                        │    │    │    │    │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+// Row 1
+#define US_DGRV ALGR(US_GRV) // ` (dead)
+#define US_SUP1 ALGR(US_1) // ¹
+#define US_SUP2 ALGR(US_2) // ²
+#define US_SUP3 ALGR(US_3) // ³
+#define US_CURR ALGR(US_4) // ¤
+#define US_EURO ALGR(US_5) // €
+#define US_DCIR ALGR(US_6) // ^ (dead)
+#define US_HORN ALGR(US_7) // ̛̛ (dead)
+#define US_OGON ALGR(US_8) // ˛ (dead)
+#define US_LSQU ALGR(US_9) // ‘
+#define US_RSQU ALGR(US_0) // ’
+#define US_YEN ALGR(US_MINS) // ¥
+#define US_MUL ALGR(US_EQL) // ×
+// Row 2
+#define US_ADIA ALGR(US_Q) // Ä
+#define US_ARNG ALGR(US_W) // Å
+#define US_EACU ALGR(US_E) // É
+#define US_EDIA ALGR(US_R) // Ë
+#define US_THRN ALGR(US_T) // Þ
+#define US_UDIA ALGR(US_Y) // Ü
+#define US_UACU ALGR(US_U) // Ú
+#define US_IACU ALGR(US_I) // Í
+#define US_OACU ALGR(US_O) // Ó
+#define US_ODIA ALGR(US_P) // Ö
+#define US_LDAQ ALGR(US_LBRC) // «
+#define US_RDAQ ALGR(US_RBRC) // »
+#define US_NOT ALGR(US_BSLS) // ¬
+// Row 3
+#define US_AACU ALGR(US_A) // Á
+#define US_SS ALGR(US_S) // ß
+#define US_ETH ALGR(US_D) // Ð
+#define US_IDIA ALGR(US_J) // Ï
+#define US_OE ALGR(US_K) // Œ
+#define US_OSTR ALGR(US_L) // Ø
+#define US_PILC ALGR(US_SCLN) // ¶
+#define US_ACUT ALGR(US_QUOT) // ´ (dead)
+// Row 4
+#define US_AE ALGR(US_Z) // Æ
+#define US_OE_2 ALGR(US_X) // Œ
+#define US_COPY ALGR(US_C) // ©
+#define US_REGD ALGR(US_V) // ®
+#define US_NTIL ALGR(US_N) // Ñ
+#define US_MICR ALGR(US_M) // µ
+#define US_CCED ALGR(US_COMM) // Ç
+#define US_DOTA ALGR(US_DOT) // ˙ (dead)
+#define US_IQUE ALGR(US_SLSH) // ¿
+
+/* Shift+AltGr symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ~ │ ¡ │ ˝ │ ¯ │ £ │ ¸ │ ¼ │ ½ │ ¾ │ ˘ │ ° │  ̣ │ ÷ │       │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │     │   │   │   │   │   │   │   │   │   │   │ “ │ ” │  ¦  │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │      │   │ § │   │   │   │   │   │   │   │ ° │ " │        │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │        │   │   │ ¢ │   │   │   │   │   │ ˇ │  ̉ │          │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │    │    │    │                        │    │    │    │    │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+// Row 1
+#define US_DTIL S(ALGR(US_GRV)) // ~ (dead)
+#define US_IEXL S(ALGR(US_1)) // ¡
+#define US_DACU S(ALGR(US_2)) // ˝ (dead)
+#define US_MACR S(ALGR(US_3)) // ¯ (dead)
+#define US_PND S(ALGR(US_4)) // £
+#define US_CEDL S(ALGR(US_5)) // ¸ (dead)
+#define US_QRTR S(ALGR(US_6)) // ¼
+#define US_HALF S(ALGR(US_7)) // ½
+#define US_TQTR S(ALGR(US_8)) // ¾
+#define US_BREV S(ALGR(US_9)) // ˘ (dead)
+#define US_RNGA S(ALGR(US_0)) // ° (dead)
+#define US_DOTB S(ALGR(US_MINS)) // ̣ (dead)
+#define US_DIV S(ALGR(US_EQL)) // ÷
+// Row 2
+#define US_LDQU S(ALGR(US_LBRC)) // “
+#define US_RDQU S(ALGR(US_LBRC)) // ”
+#define US_BRKP S(ALGR(US_BSLS)) // ¦
+// Row 3
+#define US_SECT S(ALGR(US_S)) // §
+#define US_DEG S(ALGR(US_SCLN)) // °
+#define US_DIAE S(ALGR(US_QUOT)) // ¨ (dead)
+// Row 4
+#define US_CENT S(ALGR(US_C)) // ¢
+#define US_CARN S(ALGR(US_DOT)) // ˇ (dead)
+#define US_HOKA S(ALGR(US_SLSH)) // ̉ (dead)
+
diff --git a/quantum/keymap_extras/keymap_us_international.h b/quantum/keymap_extras/keymap_us_international.h
index a3bc465971..49afcc4fb2 100644
--- a/quantum/keymap_extras/keymap_us_international.h
+++ b/quantum/keymap_extras/keymap_us_international.h
@@ -26,7 +26,7 @@
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
- * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │        │
+ * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ´ │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
@@ -34,7 +34,7 @@
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
-#define US_GRV KC_GRV // ` (dead)
+#define US_DGRV KC_GRV // ` (dead)
#define US_1 KC_1 // 1
#define US_2 KC_2 // 2
#define US_3 KC_3 // 3
@@ -72,7 +72,7 @@
#define US_K KC_K // K
#define US_L KC_L // L
#define US_SCLN KC_SCLN // ;
-#define US_QUOT KC_QUOT // ' (dead)
+#define US_ACUT KC_QUOT // ´ (dead)
// Row 4
#define US_Z KC_Z // Z
#define US_X KC_X // X
@@ -91,7 +91,7 @@
* ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
* │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │
* ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
- * │      │   │   │   │   │   │   │   │   │   │ : │ " │        │
+ * │      │   │   │   │   │   │   │   │   │   │ : │ ¨ │        │
* ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
* │        │   │   │   │   │   │   │   │ < │ > │ ? │          │
* ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
@@ -99,13 +99,13 @@
* └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
*/
// Row 1
-#define US_TILD S(US_GRV) // ~ (dead)
+#define US_DTIL S(US_DGRV) // ~ (dead)
#define US_EXLM S(US_1) // !
-#define US_AT S(US_2) // "
+#define US_AT S(US_2) // @
#define US_HASH S(US_3) // #
#define US_DLR S(US_4) // $
#define US_PERC S(US_5) // %
-#define US_CIRC S(US_6) // ^
+#define US_DCIR S(US_6) // ^ (dead)
#define US_AMPR S(US_7) // &
#define US_ASTR S(US_8) // *
#define US_LPRN S(US_9) // (
@@ -118,7 +118,7 @@
#define US_PIPE S(US_BSLS) // |
// Row 3
#define US_COLN S(US_SCLN) // :
-#define US_DQUO S(US_QUOT) // " (dead)
+#define US_DIAE S(US_ACUT) // ¨ (dead)
// Row 4
#define US_LABK S(US_COMM) // <
#define US_RABK S(US_DOT) // >
@@ -170,7 +170,7 @@
#define US_ETH ALGR(US_D) // Ð
#define US_OSTR ALGR(US_L) // Ø
#define US_PILC ALGR(US_SCLN) // ¶
-#define US_ACUT ALGR(US_QUOT) // ´
+#define US_NDAC ALGR(US_ACUT) // ´
// Row 4
#define US_AE ALGR(US_Z) // Æ
#define US_COPY ALGR(US_C) // ©
@@ -201,6 +201,6 @@
// Row 3
#define US_SECT S(ALGR(US_S)) // §
#define US_DEG S(ALGR(US_SCLN)) // °
-#define US_DIAE S(ALGR(US_QUOT)) // ¨
+#define US_NDDR S(ALGR(US_ACUT)) // ¨
// Row 4
#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 0000000000..2c3e230393
--- /dev/null
+++ b/quantum/keymap_extras/keymap_us_international_linux.h
@@ -0,0 +1,224 @@
+/* Copyright 2020
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "keymap.h"
+
+// clang-format off
+
+/*
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \  │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ´ │        │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │    │    │    │                        │    │    │    │    │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+// Row 1
+#define US_DGRV KC_GRV // ` (dead)
+#define US_1 KC_1 // 1
+#define US_2 KC_2 // 2
+#define US_3 KC_3 // 3
+#define US_4 KC_4 // 4
+#define US_5 KC_5 // 5
+#define US_6 KC_6 // 6
+#define US_7 KC_7 // 7
+#define US_8 KC_8 // 8
+#define US_9 KC_9 // 9
+#define US_0 KC_0 // 0
+#define US_MINS KC_MINS // -
+#define US_EQL KC_EQL // =
+// Row 2
+#define US_Q KC_Q // Q
+#define US_W KC_W // W
+#define US_E KC_E // E
+#define US_R KC_R // R
+#define US_T KC_T // T
+#define US_Y KC_Y // Y
+#define US_U KC_U // U
+#define US_I KC_I // I
+#define US_O KC_O // O
+#define US_P KC_P // P
+#define US_LBRC KC_LBRC // [
+#define US_RBRC KC_RBRC // ]
+#define US_BSLS KC_BSLS // (backslash)
+// Row 3
+#define US_A KC_A // A
+#define US_S KC_S // S
+#define US_D KC_D // D
+#define US_F KC_F // F
+#define US_G KC_G // G
+#define US_H KC_H // H
+#define US_J KC_J // J
+#define US_K KC_K // K
+#define US_L KC_L // L
+#define US_SCLN KC_SCLN // ;
+#define US_ACUT KC_QUOT // ´ (dead)
+// Row 4
+#define US_Z KC_Z // Z
+#define US_X KC_X // X
+#define US_C KC_C // C
+#define US_V KC_V // V
+#define US_B KC_B // B
+#define US_N KC_N // N
+#define US_M KC_M // M
+#define US_COMM KC_COMM // ,
+#define US_DOT KC_DOT // .
+#define US_SLSH KC_SLSH // /
+
+/* Shifted symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │      │   │   │   │   │   │   │   │   │   │ : │ ¨ │        │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │    │    │    │                        │    │    │    │    │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+// Row 1
+#define US_DTIL S(US_DGRV) // ~ (dead)
+#define US_EXLM S(US_1) // !
+#define US_AT S(US_2) // @
+#define US_HASH S(US_3) // #
+#define US_DLR S(US_4) // $
+#define US_PERC S(US_5) // %
+#define US_DCIR S(US_6) // ^ (dead)
+#define US_AMPR S(US_7) // &
+#define US_ASTR S(US_8) // *
+#define US_LPRN S(US_9) // (
+#define US_RPRN S(US_0) // )
+#define US_UNDS S(US_MINS) // _
+#define US_PLUS S(US_EQL) // +
+// Row 2
+#define US_LCBR S(US_LBRC) // {
+#define US_RCBR S(US_RBRC) // }
+#define US_PIPE S(US_BSLS) // |
+// Row 3
+#define US_COLN S(US_SCLN) // :
+#define US_DIAE S(US_ACUT) // ¨ (dead)
+// Row 4
+#define US_LABK S(US_COMM) // <
+#define US_RABK S(US_DOT) // >
+#define US_QUES S(US_SLSH) // ?
+
+/* AltGr symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ` │ ¡ │ ² │ ³ │ ¤ │ € │ ¼ │ ½ │ ¾ │ ‘ │ ’ │ ¥ │ × │       │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │     │ Ä │ Å │ É │ ® │ Þ │ Ü │ Ú │ Í │ Ó │ Ö │ « │ » │  ¬  │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │      │ Á │ ß │ Ð │   │   │   │   │ Œ │ Ø │ ¶ │ ' │        │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │        │ Æ │   │ © │   │   │ Ñ │ µ │ Ç │ ˙ │ ¿ │          │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │    │    │    │                        │    │    │    │    │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+
+// Row 1
+#define US_GRV ALGR(US_DGRV) // `
+#define US_IEXL ALGR(US_1) // ¡
+#define US_SUP2 ALGR(US_2) // ²
+#define US_SUP3 ALGR(US_3) // ³
+#define US_CURR ALGR(US_4) // ¤
+#define US_EURO ALGR(US_5) // €
+#define US_QRTR ALGR(US_6) // ¼
+#define US_HALF ALGR(US_7) // ½
+#define US_TQTR ALGR(US_8) // ¾
+#define US_LSQU ALGR(US_9) // ‘
+#define US_RSQU ALGR(US_0) // ’
+#define US_YEN ALGR(US_MINS) // ¥
+#define US_MUL ALGR(US_EQL) // ×
+// Row 2
+#define US_ADIA ALGR(US_Q) // Ä
+#define US_ARNG ALGR(US_W) // Å
+#define US_EACU ALGR(US_E) // É
+#define US_REGD ALGR(US_R) // ®
+#define US_THRN ALGR(US_T) // Þ
+#define US_UDIA ALGR(US_Y) // Ü
+#define US_UACU ALGR(US_U) // Ú
+#define US_IACU ALGR(US_I) // Í
+#define US_OACU ALGR(US_O) // Ó
+#define US_ODIA ALGR(US_P) // Ö
+#define US_LDAQ ALGR(US_LBRC) // «
+#define US_RDAQ ALGR(US_RBRC) // »
+#define US_NOT ALGR(US_BSLS) // ¬
+// Row 3
+#define US_AACU ALGR(US_A) // Á
+#define US_SS ALGR(US_S) // ß
+#define US_ETH ALGR(US_D) // Ð
+#define US_OE ALGR(US_K) // Œ
+#define US_OSTR ALGR(US_L) // Ø
+#define US_PILC ALGR(US_SCLN) // ¶
+#define US_QUOT ALGR(US_ACUT) // '
+// Row 4
+#define US_AE ALGR(US_Z) // Æ
+#define US_COPY ALGR(US_C) // ©
+#define US_NTIL ALGR(US_N) // Ñ
+#define US_MICR ALGR(US_M) // µ
+#define US_CCED ALGR(US_COMM) // Ç
+#define US_DOTA ALGR(US_DOT) // ˙ (dead)
+#define US_IQUE ALGR(US_SLSH) // ¿
+
+/* Shift+AltGr symbols
+ * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐
+ * │ ~ │ ¹ │ ˝ │ ¯ │ £ │ ¸ │ ^ │ ̛  │ ˛ │ ˘ │ ° │  ̣ │ ÷ │       │
+ * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤
+ * │     │   │   │   │   │   │   │   │   │   │   │ “ │ ” │  ¦  │
+ * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤
+ * │      │   │ § │   │   │   │   │   │   │   │ ° │ " │        │
+ * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤
+ * │        │   │   │ ¢ │   │   │   │   │   │ ˇ │  ̉ │          │
+ * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤
+ * │    │    │    │                        │    │    │    │    │
+ * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘
+ */
+// Row 1
+#define US_TILD S(ALGR(US_DGRV)) // ~
+#define US_SUP1 S(ALGR(US_1)) // ¹
+#define US_DACU S(ALGR(US_2)) // ˝ (dead)
+#define US_MACR S(ALGR(US_3)) // ¯ (dead)
+#define US_PND S(ALGR(US_4)) // £
+#define US_CEDL S(ALGR(US_5)) // ¸ (dead)
+#define US_CIRC S(ALGR(US_6)) // ^
+#define US_HORN S(ALGR(US_7)) // ̛ (dead)
+#define US_OGON S(ALGR(US_8)) // ˛ (dead)
+#define US_BREV S(ALGR(US_9)) // ˘ (dead)
+#define US_RNGA S(ALGR(US_0)) // ° (dead)
+#define US_DOTB S(ALGR(US_MINS)) // ̣ (dead)
+#define US_DIV S(ALGR(US_EQL)) // ÷
+// Row 2
+#define US_LDQU S(ALGR(US_LBRC)) // “
+#define US_RDQU S(ALGR(US_LBRC)) // ”
+#define US_BRKP S(ALGR(US_BSLS)) // ¦
+// Row 3
+#define US_SECT S(ALGR(US_S)) // §
+#define US_DEG S(ALGR(US_SCLN)) // °
+#define US_DQUO S(ALGR(US_ACUT)) // "
+// Row 4
+#define US_CENT S(ALGR(US_C)) // ¢
+#define US_CARN S(ALGR(US_DOT)) // ˇ (dead)
+#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 0000000000..53a5891fb1
--- /dev/null
+++ b/quantum/keymap_extras/sendstring_us_international.h
@@ -0,0 +1,100 @@
+/* Copyright 2019 Rys Sommefeldt
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+// Sendstring lookup tables for UK layouts
+
+#pragma once
+
+#include "keymap_us_international.h"
+#include "quantum.h"
+
+// clang-format off
+
+const uint8_t ascii_to_shift_lut[16] PROGMEM = {
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+
+ KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),
+ KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1),
+ KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
+ KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
+ KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
+ KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),
+};
+
+__attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = {
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+
+ KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 1),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
+ KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),
+};
+
+const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
+ // NUL SOH STX ETX EOT ENQ ACK BEL
+ XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
+ // BS TAB LF VT FF CR SO SI
+ KC_BSPC, KC_TAB, KC_ENT, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
+ // DLE DC1 DC2 DC3 DC4 NAK SYN ETB
+ XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
+ // CAN EM SUB ESC FS GS RS US
+ XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
+
+ // ! " # $ % & '
+ KC_SPC, US_1, US_ACUT, US_3, US_4, US_5, US_7, US_ACUT,
+ // ( ) * + , - . /
+ US_9, US_0, US_8, US_EQL, US_COMM, US_MINS, US_DOT, US_SLSH,
+ // 0 1 2 3 4 5 6 7
+ US_0, US_1, US_2, US_3, US_4, US_5, US_6, US_7,
+ // 8 9 : ; < = > ?
+ US_8, US_9, US_SCLN, US_SCLN, US_COMM, US_EQL, US_DOT, US_SLSH,
+ // @ A B C D E F G
+ US_2, US_A, US_B, US_C, US_D, US_E, US_F, US_G,
+ // H I J K L M N O
+ US_H, US_I, US_J, US_K, US_L, US_M, US_N, US_O,
+ // P Q R S T U V W
+ US_P, US_Q, US_R, US_S, US_T, US_U, US_V, US_W,
+ // X Y Z [ \ ] ^ _
+ US_X, US_Y, US_Z, US_LBRC, US_BSLS, US_RBRC, US_6, US_MINS,
+ // ` a b c d e f g
+ US_DGRV, US_A, US_B, US_C, US_D, US_E, US_F, US_G,
+ // h i j k l m n o
+ US_H, US_I, US_J, US_K, US_L, US_M, US_N, US_O,
+ // p q r s t u v w
+ US_P, US_Q, US_R, US_S, US_T, US_U, US_V, US_W,
+ // x y z { | } ~ DEL
+ US_X, US_Y, US_Z, US_LBRC, US_BSLS, US_RBRC, US_DGRV, KC_DEL
+};
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 9083ff3861..c027b7bf27 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)
// Start with a clear matrix row
matrix_row_t current_row_value = 0;
- // Select row and wait for row selecton to stabilize
+ // Select row
select_row(current_row);
- matrix_io_delay();
+ matrix_output_select_delay();
// For each col...
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)
// Unselect row
unselect_row(current_row);
+ if (current_row + 1 < MATRIX_ROWS) {
+ matrix_output_unselect_delay(); // wait for row signal to go HIGH
+ }
// If the row has changed, store the row and return the changed flag.
if (current_matrix[current_row] != current_row_value) {
@@ -147,9 +150,9 @@ static void init_pins(void) {
static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
bool matrix_changed = false;
- // Select col and wait for col selecton to stabilize
+ // Select col
select_col(current_col);
- matrix_io_delay();
+ matrix_output_select_delay();
// For each row...
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)
// Unselect col
unselect_col(current_col);
+ if (current_col + 1 < MATRIX_COLS) {
+ matrix_output_unselect_delay(); // wait for col signal to go HIGH
+ }
return matrix_changed;
}
diff --git a/quantum/matrix.h b/quantum/matrix.h
index b570227a31..ce57010a47 100644
--- a/quantum/matrix.h
+++ b/quantum/matrix.h
@@ -55,6 +55,9 @@ matrix_row_t matrix_get_row(uint8_t row);
/* print matrix for debug */
void matrix_print(void);
/* delay between changing matrix pin state and reading values */
+void matrix_output_select_delay(void);
+void matrix_output_unselect_delay(void);
+/* only for backwards compatibility. delay between changing matrix pin state and reading values */
void matrix_io_delay(void);
/* power control */
diff --git a/quantum/matrix_common.c b/quantum/matrix_common.c
index 15f1e0e82e..efbad6a5fd 100644
--- a/quantum/matrix_common.c
+++ b/quantum/matrix_common.c
@@ -1,3 +1,4 @@
+#include "quantum.h"
#include "matrix.h"
#include "debounce.h"
#include "wait.h"
@@ -68,7 +69,7 @@ void matrix_print(void) {
print_matrix_header();
for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
- phex(row);
+ print_hex8(row);
print(": ");
print_matrix_row(row);
print("\n");
@@ -83,8 +84,12 @@ uint8_t matrix_key_count(void) {
return count;
}
+/* `matrix_io_delay ()` exists for backwards compatibility. From now on, use matrix_output_unselect_delay(). */
__attribute__((weak)) void matrix_io_delay(void) { wait_us(MATRIX_IO_DELAY); }
+__attribute__((weak)) void matrix_output_select_delay(void) { waitInputPinDelay(); }
+__attribute__((weak)) void matrix_output_unselect_delay(void) { matrix_io_delay(); }
+
// CUSTOM MATRIX 'LITE'
__attribute__((weak)) void matrix_init_custom(void) {}
diff --git a/quantum/mcu_selection.mk b/quantum/mcu_selection.mk
index 6b11eb4987..81c467c656 100644
--- a/quantum/mcu_selection.mk
+++ b/quantum/mcu_selection.mk
@@ -279,7 +279,73 @@ ifneq ($(findstring STM32F411, $(MCU)),)
DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
endif
-ifneq (,$(filter $(MCU),atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287))
+ifneq ($(findstring STM32G431, $(MCU)),)
+ # Cortex version
+ MCU = cortex-m4
+
+ # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
+ ARMV = 7
+
+ ## chip/board settings
+ # - the next two should match the directories in
+ # <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
+ MCU_FAMILY = STM32
+ MCU_SERIES = STM32G4xx
+
+ # Linker script to use
+ # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
+ # or <keyboard_dir>/ld/
+ MCU_LDSCRIPT ?= STM32G431xB
+
+ # Startup code to use
+ # - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
+ MCU_STARTUP ?= stm32g4xx
+
+ # Board: it should exist either in <chibios>/os/hal/boards/,
+ # <keyboard_dir>/boards/, or drivers/boards/
+ BOARD ?= GENERIC_STM32_G431XB
+
+ USE_FPU ?= yes
+
+ # Options to pass to dfu-util when flashing
+ DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
+ DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
+endif
+
+ifneq ($(findstring STM32G474, $(MCU)),)
+ # Cortex version
+ MCU = cortex-m4
+
+ # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7
+ ARMV = 7
+
+ ## chip/board settings
+ # - the next two should match the directories in
+ # <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)
+ MCU_FAMILY = STM32
+ MCU_SERIES = STM32G4xx
+
+ # Linker script to use
+ # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/
+ # or <keyboard_dir>/ld/
+ MCU_LDSCRIPT ?= STM32G474xE
+
+ # Startup code to use
+ # - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/
+ MCU_STARTUP ?= stm32g4xx
+
+ # Board: it should exist either in <chibios>/os/hal/boards/,
+ # <keyboard_dir>/boards/, or drivers/boards/
+ BOARD ?= GENERIC_STM32_G474XE
+
+ USE_FPU ?= yes
+
+ # Options to pass to dfu-util when flashing
+ DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave
+ DFU_SUFFIX_ARGS ?= -v 0483 -p DF11
+endif
+
+ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287))
PROTOCOL = LUFA
# Processor frequency.
@@ -317,7 +383,7 @@ ifneq (,$(filter $(MCU),atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 a
ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes))
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
endif
- ifneq (,$(filter $(MCU),atmega16u2 atmega32u2))
+ ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2))
NO_I2C = yes
endif
endif
diff --git a/quantum/mousekey.c b/quantum/mousekey.c
new file mode 100644
index 0000000000..63e74baa93
--- /dev/null
+++ b/quantum/mousekey.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2011 Jun Wako <wakojun@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include "keycode.h"
+#include "host.h"
+#include "timer.h"
+#include "print.h"
+#include "debug.h"
+#include "mousekey.h"
+
+inline int8_t times_inv_sqrt2(int8_t x) {
+ // 181/256 is pretty close to 1/sqrt(2)
+ // 0.70703125 0.707106781
+ // 1 too small for x=99 and x=198
+ // This ends up being a mult and discard lower 8 bits
+ return (x * 181) >> 8;
+}
+
+static report_mouse_t mouse_report = {0};
+static void mousekey_debug(void);
+static uint8_t mousekey_accel = 0;
+static uint8_t mousekey_repeat = 0;
+static uint8_t mousekey_wheel_repeat = 0;
+#ifdef MK_KINETIC_SPEED
+static uint16_t mouse_timer = 0;
+#endif
+
+#ifndef MK_3_SPEED
+
+static uint16_t last_timer_c = 0;
+static uint16_t last_timer_w = 0;
+
+/*
+ * Mouse keys acceleration algorithm
+ * http://en.wikipedia.org/wiki/Mouse_keys
+ *
+ * speed = delta * max_speed * (repeat / time_to_max)**((1000+curve)/1000)
+ */
+/* milliseconds between the initial key press and first repeated motion event (0-2550) */
+uint8_t mk_delay = MOUSEKEY_DELAY / 10;
+/* milliseconds between repeated motion events (0-255) */
+uint8_t mk_interval = MOUSEKEY_INTERVAL;
+/* steady speed (in action_delta units) applied each event (0-255) */
+uint8_t mk_max_speed = MOUSEKEY_MAX_SPEED;
+/* number of events (count) accelerating to steady speed (0-255) */
+uint8_t mk_time_to_max = MOUSEKEY_TIME_TO_MAX;
+/* ramp used to reach maximum pointer speed (NOT SUPPORTED) */
+// int8_t mk_curve = 0;
+/* wheel params */
+/* milliseconds between the initial key press and first repeated motion event (0-2550) */
+uint8_t mk_wheel_delay = MOUSEKEY_WHEEL_DELAY / 10;
+/* milliseconds between repeated motion events (0-255) */
+uint8_t mk_wheel_interval = MOUSEKEY_WHEEL_INTERVAL;
+uint8_t mk_wheel_max_speed = MOUSEKEY_WHEEL_MAX_SPEED;
+uint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;
+
+# ifndef MK_COMBINED
+
+static uint8_t move_unit(void) {
+ uint16_t unit;
+ if (mousekey_accel & (1 << 0)) {
+ unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 4;
+ } else if (mousekey_accel & (1 << 1)) {
+ unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2;
+ } else if (mousekey_accel & (1 << 2)) {
+ unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed);
+ } else if (mousekey_repeat == 0) {
+ unit = MOUSEKEY_MOVE_DELTA;
+ } else if (mousekey_repeat >= mk_time_to_max) {
+ unit = MOUSEKEY_MOVE_DELTA * mk_max_speed;
+ } else {
+ unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max;
+ }
+ return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));
+}
+
+static uint8_t wheel_unit(void) {
+ uint16_t unit;
+ if (mousekey_accel & (1 << 0)) {
+ unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 4;
+ } else if (mousekey_accel & (1 << 1)) {
+ unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2;
+ } else if (mousekey_accel & (1 << 2)) {
+ unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed);
+ } else if (mousekey_wheel_repeat == 0) {
+ unit = MOUSEKEY_WHEEL_DELTA;
+ } else if (mousekey_wheel_repeat >= mk_wheel_time_to_max) {
+ unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed;
+ } else {
+ unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_wheel_repeat) / mk_wheel_time_to_max;
+ }
+ return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));
+}
+
+# else /* #ifndef MK_COMBINED */
+# ifndef MK_KINETIC_SPEED
+
+/*
+ * Kinetic movement acceleration algorithm
+ *
+ * current speed = I + A * T/50 + A * 0.5 * T^2 | maximum B
+ *
+ * T: time since the mouse movement started
+ * E: mouse events per second (set through MOUSEKEY_INTERVAL, UHK sends 250, the
+ * pro micro on my Signum 3.0 sends only 125!)
+ * I: initial speed at time 0
+ * A: acceleration
+ * B: base mouse travel speed
+ */
+const uint16_t mk_accelerated_speed = MOUSEKEY_ACCELERATED_SPEED;
+const uint16_t mk_base_speed = MOUSEKEY_BASE_SPEED;
+const uint16_t mk_decelerated_speed = MOUSEKEY_DECELERATED_SPEED;
+const uint16_t mk_initial_speed = MOUSEKEY_INITIAL_SPEED;
+
+static uint8_t move_unit(void) {
+ float speed = mk_initial_speed;
+
+ if (mousekey_accel & ((1 << 0) | (1 << 2))) {
+ speed = mousekey_accel & (1 << 2) ? mk_accelerated_speed : mk_decelerated_speed;
+ } else if (mousekey_repeat && mouse_timer) {
+ const float time_elapsed = timer_elapsed(mouse_timer) / 50;
+ speed = mk_initial_speed + MOUSEKEY_MOVE_DELTA * time_elapsed + MOUSEKEY_MOVE_DELTA * 0.5 * time_elapsed * time_elapsed;
+
+ speed = speed > mk_base_speed ? mk_base_speed : speed;
+ }
+
+ /* convert speed to USB mouse speed 1 to 127 */
+ speed = (uint8_t)(speed / (1000.0f / mk_interval));
+ speed = speed < 1 ? 1 : speed;
+
+ return speed > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : speed;
+}
+
+float mk_wheel_interval = 1000.0f / MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;
+
+static uint8_t wheel_unit(void) {
+ float speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;
+
+ if (mousekey_accel & ((1 << 0) | (1 << 2))) {
+ speed = mousekey_accel & (1 << 2) ? MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS : MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS;
+ } else if (mousekey_repeat && mouse_timer) {
+ if (mk_wheel_interval != MOUSEKEY_WHEEL_BASE_MOVEMENTS) {
+ const float time_elapsed = timer_elapsed(mouse_timer) / 50;
+ speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS + 1 * time_elapsed + 1 * 0.5 * time_elapsed * time_elapsed;
+ }
+ speed = speed > MOUSEKEY_WHEEL_BASE_MOVEMENTS ? MOUSEKEY_WHEEL_BASE_MOVEMENTS : speed;
+ }
+
+ mk_wheel_interval = 1000.0f / speed;
+
+ return 1;
+}
+
+# else /* #ifndef MK_KINETIC_SPEED */
+
+static uint8_t move_unit(void) {
+ uint16_t unit;
+ if (mousekey_accel & (1 << 0)) {
+ unit = 1;
+ } else if (mousekey_accel & (1 << 1)) {
+ unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2;
+ } else if (mousekey_accel & (1 << 2)) {
+ unit = MOUSEKEY_MOVE_MAX;
+ } else if (mousekey_repeat == 0) {
+ unit = MOUSEKEY_MOVE_DELTA;
+ } else if (mousekey_repeat >= mk_time_to_max) {
+ unit = MOUSEKEY_MOVE_DELTA * mk_max_speed;
+ } else {
+ unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max;
+ }
+ return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));
+}
+
+static uint8_t wheel_unit(void) {
+ uint16_t unit;
+ if (mousekey_accel & (1 << 0)) {
+ unit = 1;
+ } else if (mousekey_accel & (1 << 1)) {
+ unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2;
+ } else if (mousekey_accel & (1 << 2)) {
+ unit = MOUSEKEY_WHEEL_MAX;
+ } else if (mousekey_repeat == 0) {
+ unit = MOUSEKEY_WHEEL_DELTA;
+ } else if (mousekey_repeat >= mk_wheel_time_to_max) {
+ unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed;
+ } else {
+ unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_repeat) / mk_wheel_time_to_max;
+ }
+ return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));
+}
+
+# endif /* #ifndef MK_KINETIC_SPEED */
+# endif /* #ifndef MK_COMBINED */
+
+void mousekey_task(void) {
+ // report cursor and scroll movement independently
+ report_mouse_t const tmpmr = mouse_report;
+
+ mouse_report.x = 0;
+ mouse_report.y = 0;
+ mouse_report.v = 0;
+ mouse_report.h = 0;
+
+ if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > (mousekey_repeat ? mk_interval : mk_delay * 10)) {
+ if (mousekey_repeat != UINT8_MAX) mousekey_repeat++;
+ if (tmpmr.x != 0) mouse_report.x = move_unit() * ((tmpmr.x > 0) ? 1 : -1);
+ if (tmpmr.y != 0) mouse_report.y = move_unit() * ((tmpmr.y > 0) ? 1 : -1);
+
+ /* diagonal move [1/sqrt(2)] */
+ if (mouse_report.x && mouse_report.y) {
+ mouse_report.x = times_inv_sqrt2(mouse_report.x);
+ if (mouse_report.x == 0) {
+ mouse_report.x = 1;
+ }
+ mouse_report.y = times_inv_sqrt2(mouse_report.y);
+ if (mouse_report.y == 0) {
+ mouse_report.y = 1;
+ }
+ }
+ }
+ if ((tmpmr.v || tmpmr.h) && timer_elapsed(last_timer_w) > (mousekey_wheel_repeat ? mk_wheel_interval : mk_wheel_delay * 10)) {
+ if (mousekey_wheel_repeat != UINT8_MAX) mousekey_wheel_repeat++;
+ if (tmpmr.v != 0) mouse_report.v = wheel_unit() * ((tmpmr.v > 0) ? 1 : -1);
+ if (tmpmr.h != 0) mouse_report.h = wheel_unit() * ((tmpmr.h > 0) ? 1 : -1);
+
+ /* diagonal move [1/sqrt(2)] */
+ if (mouse_report.v && mouse_report.h) {
+ mouse_report.v = times_inv_sqrt2(mouse_report.v);
+ if (mouse_report.v == 0) {
+ mouse_report.v = 1;
+ }
+ mouse_report.h = times_inv_sqrt2(mouse_report.h);
+ if (mouse_report.h == 0) {
+ mouse_report.h = 1;
+ }
+ }
+ }
+
+ if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send();
+ mouse_report = tmpmr;
+}
+
+void mousekey_on(uint8_t code) {
+# ifdef MK_KINETIC_SPEED
+ if (mouse_timer == 0) {
+ mouse_timer = timer_read();
+ }
+# endif /* #ifdef MK_KINETIC_SPEED */
+
+ if (code == KC_MS_UP)
+ mouse_report.y = move_unit() * -1;
+ else if (code == KC_MS_DOWN)
+ mouse_report.y = move_unit();
+ else if (code == KC_MS_LEFT)
+ mouse_report.x = move_unit() * -1;
+ else if (code == KC_MS_RIGHT)
+ mouse_report.x = move_unit();
+ else if (code == KC_MS_WH_UP)
+ mouse_report.v = wheel_unit();
+ else if (code == KC_MS_WH_DOWN)
+ mouse_report.v = wheel_unit() * -1;
+ else if (code == KC_MS_WH_LEFT)
+ mouse_report.h = wheel_unit() * -1;
+ else if (code == KC_MS_WH_RIGHT)
+ mouse_report.h = wheel_unit();
+ else if (IS_MOUSEKEY_BUTTON(code))
+ mouse_report.buttons |= 1 << (code - KC_MS_BTN1);
+ else if (code == KC_MS_ACCEL0)
+ mousekey_accel |= (1 << 0);
+ else if (code == KC_MS_ACCEL1)
+ mousekey_accel |= (1 << 1);
+ else if (code == KC_MS_ACCEL2)
+ mousekey_accel |= (1 << 2);
+}
+
+void mousekey_off(uint8_t code) {
+ if (code == KC_MS_UP && mouse_report.y < 0)
+ mouse_report.y = 0;
+ else if (code == KC_MS_DOWN && mouse_report.y > 0)
+ mouse_report.y = 0;
+ else if (code == KC_MS_LEFT && mouse_report.x < 0)
+ mouse_report.x = 0;
+ else if (code == KC_MS_RIGHT && mouse_report.x > 0)
+ mouse_report.x = 0;
+ else if (code == KC_MS_WH_UP && mouse_report.v > 0)
+ mouse_report.v = 0;
+ else if (code == KC_MS_WH_DOWN && mouse_report.v < 0)
+ mouse_report.v = 0;
+ else if (code == KC_MS_WH_LEFT && mouse_report.h < 0)
+ mouse_report.h = 0;
+ else if (code == KC_MS_WH_RIGHT && mouse_report.h > 0)
+ mouse_report.h = 0;
+ else if (IS_MOUSEKEY_BUTTON(code))
+ mouse_report.buttons &= ~(1 << (code - KC_MS_BTN1));
+ else if (code == KC_MS_ACCEL0)
+ mousekey_accel &= ~(1 << 0);
+ else if (code == KC_MS_ACCEL1)
+ mousekey_accel &= ~(1 << 1);
+ else if (code == KC_MS_ACCEL2)
+ mousekey_accel &= ~(1 << 2);
+ if (mouse_report.x == 0 && mouse_report.y == 0) {
+ mousekey_repeat = 0;
+# ifdef MK_KINETIC_SPEED
+ mouse_timer = 0;
+# endif /* #ifdef MK_KINETIC_SPEED */
+ }
+ if (mouse_report.v == 0 && mouse_report.h == 0) mousekey_wheel_repeat = 0;
+}
+
+#else /* #ifndef MK_3_SPEED */
+
+enum { mkspd_unmod, mkspd_0, mkspd_1, mkspd_2, mkspd_COUNT };
+# ifndef MK_MOMENTARY_ACCEL
+static uint8_t mk_speed = mkspd_1;
+# else
+static uint8_t mk_speed = mkspd_unmod;
+static uint8_t mkspd_DEFAULT = mkspd_unmod;
+# endif
+static uint16_t last_timer_c = 0;
+static uint16_t last_timer_w = 0;
+uint16_t c_offsets[mkspd_COUNT] = {MK_C_OFFSET_UNMOD, MK_C_OFFSET_0, MK_C_OFFSET_1, MK_C_OFFSET_2};
+uint16_t c_intervals[mkspd_COUNT] = {MK_C_INTERVAL_UNMOD, MK_C_INTERVAL_0, MK_C_INTERVAL_1, MK_C_INTERVAL_2};
+uint16_t w_offsets[mkspd_COUNT] = {MK_W_OFFSET_UNMOD, MK_W_OFFSET_0, MK_W_OFFSET_1, MK_W_OFFSET_2};
+uint16_t w_intervals[mkspd_COUNT] = {MK_W_INTERVAL_UNMOD, MK_W_INTERVAL_0, MK_W_INTERVAL_1, MK_W_INTERVAL_2};
+
+void mousekey_task(void) {
+ // report cursor and scroll movement independently
+ report_mouse_t const tmpmr = mouse_report;
+ mouse_report.x = 0;
+ mouse_report.y = 0;
+ mouse_report.v = 0;
+ mouse_report.h = 0;
+
+ if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > c_intervals[mk_speed]) {
+ mouse_report.x = tmpmr.x;
+ mouse_report.y = tmpmr.y;
+ }
+ if ((tmpmr.h || tmpmr.v) && timer_elapsed(last_timer_w) > w_intervals[mk_speed]) {
+ mouse_report.v = tmpmr.v;
+ mouse_report.h = tmpmr.h;
+ }
+
+ if (mouse_report.x || mouse_report.y || mouse_report.v || mouse_report.h) mousekey_send();
+ mouse_report = tmpmr;
+}
+
+void adjust_speed(void) {
+ uint16_t const c_offset = c_offsets[mk_speed];
+ uint16_t const w_offset = w_offsets[mk_speed];
+ if (mouse_report.x > 0) mouse_report.x = c_offset;
+ if (mouse_report.x < 0) mouse_report.x = c_offset * -1;
+ if (mouse_report.y > 0) mouse_report.y = c_offset;
+ if (mouse_report.y < 0) mouse_report.y = c_offset * -1;
+ if (mouse_report.h > 0) mouse_report.h = w_offset;
+ if (mouse_report.h < 0) mouse_report.h = w_offset * -1;
+ if (mouse_report.v > 0) mouse_report.v = w_offset;
+ if (mouse_report.v < 0) mouse_report.v = w_offset * -1;
+ // adjust for diagonals
+ if (mouse_report.x && mouse_report.y) {
+ mouse_report.x = times_inv_sqrt2(mouse_report.x);
+ if (mouse_report.x == 0) {
+ mouse_report.x = 1;
+ }
+ mouse_report.y = times_inv_sqrt2(mouse_report.y);
+ if (mouse_report.y == 0) {
+ mouse_report.y = 1;
+ }
+ }
+ if (mouse_report.h && mouse_report.v) {
+ mouse_report.h = times_inv_sqrt2(mouse_report.h);
+ mouse_report.v = times_inv_sqrt2(mouse_report.v);
+ }
+}
+
+void mousekey_on(uint8_t code) {
+ uint16_t const c_offset = c_offsets[mk_speed];
+ uint16_t const w_offset = w_offsets[mk_speed];
+ uint8_t const old_speed = mk_speed;
+ if (code == KC_MS_UP)
+ mouse_report.y = c_offset * -1;
+ else if (code == KC_MS_DOWN)
+ mouse_report.y = c_offset;
+ else if (code == KC_MS_LEFT)
+ mouse_report.x = c_offset * -1;
+ else if (code == KC_MS_RIGHT)
+ mouse_report.x = c_offset;
+ else if (code == KC_MS_WH_UP)
+ mouse_report.v = w_offset;
+ else if (code == KC_MS_WH_DOWN)
+ mouse_report.v = w_offset * -1;
+ else if (code == KC_MS_WH_LEFT)
+ mouse_report.h = w_offset * -1;
+ else if (code == KC_MS_WH_RIGHT)
+ mouse_report.h = w_offset;
+ else if (IS_MOUSEKEY_BUTTON(code))
+ mouse_report.buttons |= 1 << (code - KC_MS_BTN1);
+ else if (code == KC_MS_ACCEL0)
+ mk_speed = mkspd_0;
+ else if (code == KC_MS_ACCEL1)
+ mk_speed = mkspd_1;
+ else if (code == KC_MS_ACCEL2)
+ mk_speed = mkspd_2;
+ if (mk_speed != old_speed) adjust_speed();
+}
+
+void mousekey_off(uint8_t code) {
+# ifdef MK_MOMENTARY_ACCEL
+ uint8_t const old_speed = mk_speed;
+# endif
+ if (code == KC_MS_UP && mouse_report.y < 0)
+ mouse_report.y = 0;
+ else if (code == KC_MS_DOWN && mouse_report.y > 0)
+ mouse_report.y = 0;
+ else if (code == KC_MS_LEFT && mouse_report.x < 0)
+ mouse_report.x = 0;
+ else if (code == KC_MS_RIGHT && mouse_report.x > 0)
+ mouse_report.x = 0;
+ else if (code == KC_MS_WH_UP && mouse_report.v > 0)
+ mouse_report.v = 0;
+ else if (code == KC_MS_WH_DOWN && mouse_report.v < 0)
+ mouse_report.v = 0;
+ else if (code == KC_MS_WH_LEFT && mouse_report.h < 0)
+ mouse_report.h = 0;
+ else if (code == KC_MS_WH_RIGHT && mouse_report.h > 0)
+ mouse_report.h = 0;
+ else if (IS_MOUSEKEY_BUTTON(code))
+ mouse_report.buttons &= ~(1 << (code - KC_MS_BTN1));
+# ifdef MK_MOMENTARY_ACCEL
+ else if (code == KC_MS_ACCEL0)
+ mk_speed = mkspd_DEFAULT;
+ else if (code == KC_MS_ACCEL1)
+ mk_speed = mkspd_DEFAULT;
+ else if (code == KC_MS_ACCEL2)
+ mk_speed = mkspd_DEFAULT;
+ if (mk_speed != old_speed) adjust_speed();
+# endif
+}
+
+#endif /* #ifndef MK_3_SPEED */
+
+void mousekey_send(void) {
+ mousekey_debug();
+ uint16_t time = timer_read();
+ if (mouse_report.x || mouse_report.y) last_timer_c = time;
+ if (mouse_report.v || mouse_report.h) last_timer_w = time;
+ host_mouse_send(&mouse_report);
+}
+
+void mousekey_clear(void) {
+ mouse_report = (report_mouse_t){};
+ mousekey_repeat = 0;
+ mousekey_wheel_repeat = 0;
+ mousekey_accel = 0;
+}
+
+static void mousekey_debug(void) {
+ if (!debug_mouse) return;
+ print("mousekey [btn|x y v h](rep/acl): [");
+ print_hex8(mouse_report.buttons);
+ print("|");
+ print_decs(mouse_report.x);
+ print(" ");
+ print_decs(mouse_report.y);
+ print(" ");
+ print_decs(mouse_report.v);
+ print(" ");
+ print_decs(mouse_report.h);
+ print("](");
+ print_dec(mousekey_repeat);
+ print("/");
+ print_dec(mousekey_accel);
+ print(")\n");
+}
diff --git a/quantum/mousekey.h b/quantum/mousekey.h
new file mode 100644
index 0000000000..70dc4bb5c5
--- /dev/null
+++ b/quantum/mousekey.h
@@ -0,0 +1,179 @@
+/*
+Copyright 2011 Jun Wako <wakojun@gmail.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include "host.h"
+
+#ifndef MK_3_SPEED
+
+/* max value on report descriptor */
+# ifndef MOUSEKEY_MOVE_MAX
+# define MOUSEKEY_MOVE_MAX 127
+# elif MOUSEKEY_MOVE_MAX > 127
+# error MOUSEKEY_MOVE_MAX needs to be smaller than 127
+# endif
+
+# ifndef MOUSEKEY_WHEEL_MAX
+# define MOUSEKEY_WHEEL_MAX 127
+# elif MOUSEKEY_WHEEL_MAX > 127
+# error MOUSEKEY_WHEEL_MAX needs to be smaller than 127
+# endif
+
+# ifndef MOUSEKEY_MOVE_DELTA
+# ifndef MK_KINETIC_SPEED
+# define MOUSEKEY_MOVE_DELTA 5
+# else
+# define MOUSEKEY_MOVE_DELTA 25
+# endif
+# endif
+# ifndef MOUSEKEY_WHEEL_DELTA
+# define MOUSEKEY_WHEEL_DELTA 1
+# endif
+# ifndef MOUSEKEY_DELAY
+# ifndef MK_KINETIC_SPEED
+# define MOUSEKEY_DELAY 300
+# else
+# define MOUSEKEY_DELAY 8
+# endif
+# endif
+# ifndef MOUSEKEY_INTERVAL
+# ifndef MK_KINETIC_SPEED
+# define MOUSEKEY_INTERVAL 50
+# else
+# define MOUSEKEY_INTERVAL 8
+# endif
+# endif
+# ifndef MOUSEKEY_MAX_SPEED
+# define MOUSEKEY_MAX_SPEED 10
+# endif
+# ifndef MOUSEKEY_TIME_TO_MAX
+# define MOUSEKEY_TIME_TO_MAX 20
+# endif
+# ifndef MOUSEKEY_WHEEL_DELAY
+# define MOUSEKEY_WHEEL_DELAY 300
+# endif
+# ifndef MOUSEKEY_WHEEL_INTERVAL
+# define MOUSEKEY_WHEEL_INTERVAL 100
+# endif
+# ifndef MOUSEKEY_WHEEL_MAX_SPEED
+# define MOUSEKEY_WHEEL_MAX_SPEED 8
+# endif
+# ifndef MOUSEKEY_WHEEL_TIME_TO_MAX
+# define MOUSEKEY_WHEEL_TIME_TO_MAX 40
+# endif
+
+# ifndef MOUSEKEY_INITIAL_SPEED
+# define MOUSEKEY_INITIAL_SPEED 100
+# endif
+# ifndef MOUSEKEY_BASE_SPEED
+# define MOUSEKEY_BASE_SPEED 1000
+# endif
+# ifndef MOUSEKEY_DECELERATED_SPEED
+# define MOUSEKEY_DECELERATED_SPEED 400
+# endif
+# ifndef MOUSEKEY_ACCELERATED_SPEED
+# define MOUSEKEY_ACCELERATED_SPEED 3000
+# endif
+# ifndef MOUSEKEY_WHEEL_INITIAL_MOVEMENTS
+# define MOUSEKEY_WHEEL_INITIAL_MOVEMENTS 16
+# endif
+# ifndef MOUSEKEY_WHEEL_BASE_MOVEMENTS
+# define MOUSEKEY_WHEEL_BASE_MOVEMENTS 32
+# endif
+# ifndef MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS
+# define MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS 48
+# endif
+# ifndef MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS
+# define MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS 8
+# endif
+
+#else /* #ifndef MK_3_SPEED */
+
+# ifndef MK_C_OFFSET_UNMOD
+# define MK_C_OFFSET_UNMOD 16
+# endif
+# ifndef MK_C_INTERVAL_UNMOD
+# define MK_C_INTERVAL_UNMOD 16
+# endif
+# ifndef MK_C_OFFSET_0
+# define MK_C_OFFSET_0 1
+# endif
+# ifndef MK_C_INTERVAL_0
+# define MK_C_INTERVAL_0 32
+# endif
+# ifndef MK_C_OFFSET_1
+# define MK_C_OFFSET_1 4
+# endif
+# ifndef MK_C_INTERVAL_1
+# define MK_C_INTERVAL_1 16
+# endif
+# ifndef MK_C_OFFSET_2
+# define MK_C_OFFSET_2 32
+# endif
+# ifndef MK_C_INTERVAL_2
+# define MK_C_INTERVAL_2 16
+# endif
+
+# ifndef MK_W_OFFSET_UNMOD
+# define MK_W_OFFSET_UNMOD 1
+# endif
+# ifndef MK_W_INTERVAL_UNMOD
+# define MK_W_INTERVAL_UNMOD 40
+# endif
+# ifndef MK_W_OFFSET_0
+# define MK_W_OFFSET_0 1
+# endif
+# ifndef MK_W_INTERVAL_0
+# define MK_W_INTERVAL_0 360
+# endif
+# ifndef MK_W_OFFSET_1
+# define MK_W_OFFSET_1 1
+# endif
+# ifndef MK_W_INTERVAL_1
+# define MK_W_INTERVAL_1 120
+# endif
+# ifndef MK_W_OFFSET_2
+# define MK_W_OFFSET_2 1
+# endif
+# ifndef MK_W_INTERVAL_2
+# define MK_W_INTERVAL_2 20
+# endif
+
+#endif /* #ifndef MK_3_SPEED */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern uint8_t mk_delay;
+extern uint8_t mk_interval;
+extern uint8_t mk_max_speed;
+extern uint8_t mk_time_to_max;
+extern uint8_t mk_wheel_max_speed;
+extern uint8_t mk_wheel_time_to_max;
+
+void mousekey_task(void);
+void mousekey_on(uint8_t code);
+void mousekey_off(uint8_t code);
+void mousekey_clear(void);
+void mousekey_send(void);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/quantum/quantum.c b/quantum/quantum.c
index cf16e953a2..6d202c515c 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -25,10 +25,6 @@
# include "backlight.h"
#endif
-#ifdef FAUXCLICKY_ENABLE
-# include "fauxclicky.h"
-#endif
-
#ifdef API_ENABLE
# include "api.h"
#endif
@@ -310,17 +306,6 @@ bool process_record_quantum(keyrecord_t *record) {
case EEPROM_RESET:
eeconfig_init();
return false;
-#ifdef FAUXCLICKY_ENABLE
- case FC_TOG:
- FAUXCLICKY_TOGGLE;
- return false;
- case FC_ON:
- FAUXCLICKY_ON;
- return false;
- case FC_OFF:
- FAUXCLICKY_OFF;
- return false;
-#endif
#ifdef VELOCIKEY_ENABLE
case VLK_TOG:
velocikey_toggle();
@@ -391,6 +376,29 @@ __attribute__((weak)) const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
};
+/* Bit-Packed look-up table to convert an ASCII character to whether
+ * [Space] needs to be sent after the keycode
+ */
+__attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = {
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+ KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+};
+
/* Look-up table to convert an ASCII character to a keycode.
*/
__attribute__((weak)) const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
@@ -531,6 +539,7 @@ void send_char(char ascii_code) {
uint8_t keycode = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]);
bool is_shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)ascii_code);
bool is_altgred = PGM_LOADBIT(ascii_to_altgr_lut, (uint8_t)ascii_code);
+ bool is_dead = PGM_LOADBIT(ascii_to_dead_lut, (uint8_t)ascii_code);
if (is_shifted) {
register_code(KC_LSFT);
@@ -545,6 +554,9 @@ void send_char(char ascii_code) {
if (is_shifted) {
unregister_code(KC_LSFT);
}
+ if (is_dead) {
+ tap_code(KC_SPACE);
+ }
}
void set_single_persistent_default_layer(uint8_t default_layer) {
@@ -629,6 +641,26 @@ void matrix_init_quantum() {
}
void matrix_scan_quantum() {
+#if defined(AUDIO_ENABLE)
+ // There are some tasks that need to be run a little bit
+ // after keyboard startup, or else they will not work correctly
+ // because of interaction with the USB device state, which
+ // may still be in flux...
+ //
+ // At the moment the only feature that needs this is the
+ // startup song.
+ static bool delayed_tasks_run = false;
+ static uint16_t delayed_task_timer = 0;
+ if (!delayed_tasks_run) {
+ if (!delayed_task_timer) {
+ delayed_task_timer = timer_read();
+ } else if (timer_elapsed(delayed_task_timer) > 300) {
+ audio_startup();
+ delayed_tasks_run = true;
+ }
+ }
+#endif
+
#if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE)
matrix_scan_music();
#endif
diff --git a/quantum/quantum.h b/quantum/quantum.h
index dd2a6dd53a..36a983d575 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -53,6 +53,7 @@
#include "eeconfig.h"
#include "bootloader.h"
#include "timer.h"
+#include "sync_timer.h"
#include "config_common.h"
#include "gpio.h"
#include "atomic_util.h"
@@ -194,6 +195,42 @@ extern layer_state_t layer_state;
# include "wpm.h"
#endif
+#ifdef USBPD_ENABLE
+# include "usbpd.h"
+#endif
+
+// Function substitutions to ease GPIO manipulation
+#if defined(__AVR__)
+
+/* The AVR series GPIOs have a one clock read delay for changes in the digital input signal.
+ * But here's more margin to make it two clocks. */
+# if !defined(GPIO_INPUT_PIN_DELAY)
+# define GPIO_INPUT_PIN_DELAY 2
+# endif
+# define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)
+
+#elif defined(__ARMEL__) || defined(__ARMEB__)
+
+/* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus
+ * to which the GPIO is connected.
+ * The connected buses differ depending on the various series of MCUs.
+ * And since the instruction execution clock of the CPU and the bus clock of GPIO are different,
+ * there is a delay of several clocks to read the change of the input signal.
+ *
+ * Define this delay with the GPIO_INPUT_PIN_DELAY macro.
+ * If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used.
+ * (A fairly large value of 0.25 microseconds is set.)
+ */
+# if !defined(GPIO_INPUT_PIN_DELAY)
+# if defined(STM32_SYSCLK)
+# define GPIO_INPUT_PIN_DELAY (STM32_SYSCLK / 1000000L / 4)
+# elif defined(KINETIS_SYSCLK_FREQUENCY)
+# define GPIO_INPUT_PIN_DELAY (KINETIS_SYSCLK_FREQUENCY / 1000000L / 4)
+# endif
+# endif
+# define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)
+
+#endif
#define SEND_STRING(string) send_string_P(PSTR(string))
#define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval)
@@ -201,6 +238,7 @@ extern layer_state_t layer_state;
extern const uint8_t ascii_to_keycode_lut[128];
extern const uint8_t ascii_to_shift_lut[16];
extern const uint8_t ascii_to_altgr_lut[16];
+extern const uint8_t ascii_to_dead_lut[16];
// clang-format off
#define KCLUT_ENTRY(a, b, c, d, e, f, g, h) \
( ((a) ? 1 : 0) << 0 \
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index 0160c5586d..e0f5dbc61e 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -150,13 +150,6 @@ enum quantum_keycodes {
CLICKY_DOWN,
CLICKY_RESET,
-#ifdef FAUXCLICKY_ENABLE
- // Faux clicky
- FC_ON,
- FC_OFF,
- FC_TOG,
-#endif
-
// Music mode on/off/toggle
MU_ON,
MU_OFF,
@@ -717,6 +710,9 @@ enum quantum_keycodes {
#define CK_DOWN CLICKY_DOWN
#define CK_ON CLICKY_ENABLE
#define CK_OFF CLICKY_DISABLE
+#define FC_ON CLICKY_ENABLE
+#define FC_OFF CLICKY_DISABLE
+#define FC_TOGG CLICKY_TOGGLE
#define RGB_MOD RGB_MODE_FORWARD
#define RGB_RMOD RGB_MODE_REVERSE
diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c
index 04af3ae9ec..a945df68e0 100644
--- a/quantum/rgb_matrix.c
+++ b/quantum/rgb_matrix.c
@@ -266,9 +266,9 @@ static bool rgb_matrix_none(effect_params_t *params) {
static void rgb_task_timers(void) {
#if defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0
- uint32_t deltaTime = timer_elapsed32(rgb_timer_buffer);
+ uint32_t deltaTime = sync_timer_elapsed32(rgb_timer_buffer);
#endif // defined(RGB_MATRIX_KEYREACTIVE_ENABLED) || RGB_DISABLE_TIMEOUT > 0
- rgb_timer_buffer = timer_read32();
+ rgb_timer_buffer = sync_timer_read32();
// Update double buffer timers
#if RGB_DISABLE_TIMEOUT > 0
@@ -296,7 +296,7 @@ static void rgb_task_timers(void) {
static void rgb_task_sync(void) {
// next task
- if (timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING;
+ if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING;
}
static void rgb_task_start(void) {
diff --git a/quantum/rgblight.c b/quantum/rgblight.c
index ac4ff9bfda..7d7d015ba0 100644
--- a/quantum/rgblight.c
+++ b/quantum/rgblight.c
@@ -29,7 +29,7 @@
#endif
#include "wait.h"
#include "progmem.h"
-#include "timer.h"
+#include "sync_timer.h"
#include "rgblight.h"
#include "color.h"
#include "debug.h"
@@ -42,6 +42,9 @@
#ifndef MIN
# define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
+#ifndef MAX
+# define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
#ifdef RGBLIGHT_SPLIT
/* for split keyboard */
@@ -700,18 +703,16 @@ static void rgblight_layers_write(void) {
# ifdef RGBLIGHT_LAYER_BLINK
rgblight_layer_mask_t _blinked_layer_mask = 0;
-uint16_t _blink_duration = 0;
static uint16_t _blink_timer;
void rgblight_blink_layer(uint8_t layer, uint16_t duration_ms) {
rgblight_set_layer_state(layer, true);
_blinked_layer_mask |= (rgblight_layer_mask_t)1 << layer;
- _blink_timer = timer_read();
- _blink_duration = duration_ms;
+ _blink_timer = sync_timer_read() + duration_ms;
}
void rgblight_unblink_layers(void) {
- if (_blinked_layer_mask != 0 && timer_elapsed(_blink_timer) > _blink_duration) {
+ if (_blinked_layer_mask != 0 && timer_expired(sync_timer_read(), _blink_timer)) {
for (uint8_t layer = 0; layer < RGBLIGHT_MAX_LAYERS; layer++) {
if ((_blinked_layer_mask & (rgblight_layer_mask_t)1 << layer) != 0) {
rgblight_set_layer_state(layer, false);
@@ -886,7 +887,7 @@ void rgblight_timer_enable(void) {
if (!is_static_effect(rgblight_config.mode)) {
rgblight_status.timer_enabled = true;
}
- animation_status.last_timer = timer_read();
+ animation_status.last_timer = sync_timer_read();
RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;
dprintf("rgblight timer enabled.\n");
}
@@ -989,24 +990,25 @@ void rgblight_task(void) {
# endif
# ifdef RGBLIGHT_EFFECT_TWINKLE
else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) {
- interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 50);
+ interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 30);
effect_func = (effect_func_t)rgblight_effect_twinkle;
}
# endif
if (animation_status.restart) {
animation_status.restart = false;
- animation_status.last_timer = timer_read() - interval_time - 1;
+ animation_status.last_timer = sync_timer_read();
animation_status.pos16 = 0; // restart signal to local each effect
}
- if (timer_elapsed(animation_status.last_timer) >= interval_time) {
+ uint16_t now = sync_timer_read();
+ if (timer_expired(now, animation_status.last_timer)) {
# if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)
static uint16_t report_last_timer = 0;
static bool tick_flag = false;
uint16_t oldpos16;
if (tick_flag) {
tick_flag = false;
- if (timer_elapsed(report_last_timer) >= 30000) {
- report_last_timer = timer_read();
+ if (timer_expired(now, report_last_timer)) {
+ report_last_timer += 30000;
dprintf("rgblight animation tick report to slave\n");
RGBLIGHT_SPLIT_ANIMATION_TICK;
}
@@ -1030,8 +1032,7 @@ void rgblight_task(void) {
#endif /* RGBLIGHT_USE_TIMER */
-// Effects
-#ifdef RGBLIGHT_EFFECT_BREATHING
+#if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_TWINKLE)
# ifndef RGBLIGHT_EFFECT_BREATHE_CENTER
# ifndef RGBLIGHT_BREATHE_TABLE_SIZE
@@ -1040,17 +1041,24 @@ void rgblight_task(void) {
# include <rgblight_breathe_table.h>
# endif
-__attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5};
-
-void rgblight_effect_breathing(animation_status_t *anim) {
- float val;
-
+static uint8_t breathe_calc(uint8_t pos) {
// http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/
# ifdef RGBLIGHT_EFFECT_BREATHE_TABLE
- val = pgm_read_byte(&rgblight_effect_breathe_table[anim->pos / table_scale]);
+ return pgm_read_byte(&rgblight_effect_breathe_table[pos / table_scale]);
# else
- val = (exp(sin((anim->pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E));
+ return (exp(sin((pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E));
# endif
+}
+
+#endif
+
+// Effects
+#ifdef RGBLIGHT_EFFECT_BREATHING
+
+__attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5};
+
+void rgblight_effect_breathing(animation_status_t *anim) {
+ uint8_t val = breathe_calc(anim->pos);
rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val);
anim->pos = (anim->pos + 1);
}
@@ -1302,48 +1310,54 @@ void rgblight_effect_alternating(animation_status_t *anim) {
#endif
#ifdef RGBLIGHT_EFFECT_TWINKLE
-__attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {50, 25, 10};
+__attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {30, 15, 5};
typedef struct PACKED {
HSV hsv;
uint8_t life;
- bool up;
+ uint8_t max_life;
} TwinkleState;
static TwinkleState led_twinkle_state[RGBLED_NUM];
void rgblight_effect_twinkle(animation_status_t *anim) {
- bool random_color = anim->delta / 3;
- bool restart = anim->pos == 0;
- anim->pos = 1;
+ const bool random_color = anim->delta / 3;
+ const bool restart = anim->pos == 0;
+ anim->pos = 1;
+
+ const uint8_t bottom = breathe_calc(0);
+ const uint8_t top = breathe_calc(127);
+
+ uint8_t frac(uint8_t n, uint8_t d) { return (uint16_t)255 * n / d; }
+ uint8_t scale(uint16_t v, uint8_t scale) { return (v * scale) >> 8; }
for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) {
TwinkleState *t = &(led_twinkle_state[i]);
HSV * c = &(t->hsv);
+
+ if (!random_color) {
+ c->h = rgblight_config.hue;
+ c->s = rgblight_config.sat;
+ }
+
if (restart) {
// Restart
- t->life = 0;
- t->hsv.v = 0;
+ t->life = 0;
+ c->v = 0;
} else if (t->life) {
// This LED is already on, either brightening or dimming
t->life--;
- uint8_t on = t->up ? RGBLIGHT_EFFECT_TWINKLE_LIFE - t->life : t->life;
- c->v = (uint16_t)rgblight_config.val * on / RGBLIGHT_EFFECT_TWINKLE_LIFE;
- if (t->life == 0 && t->up) {
- t->up = false;
- t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE;
- }
- if (!random_color) {
- c->h = rgblight_config.hue;
- c->s = rgblight_config.sat;
- }
- } else if (rand() < RAND_MAX * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY) {
+ uint8_t unscaled = frac(breathe_calc(frac(t->life, t->max_life)) - bottom, top - bottom);
+ c->v = scale(rgblight_config.val, unscaled);
+ } else if (rand() < scale((uint16_t)RAND_MAX * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY, 127 + rgblight_config.val / 2)) {
// This LED is off, but was randomly selected to start brightening
- c->h = random_color ? rand() % 0xFF : rgblight_config.hue;
- c->s = random_color ? (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2) : rgblight_config.sat;
- c->v = 0;
- t->life = RGBLIGHT_EFFECT_TWINKLE_LIFE;
- t->up = true;
+ if (random_color) {
+ c->h = rand() % 0xFF;
+ c->s = (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2);
+ }
+ c->v = 0;
+ t->max_life = MAX(20, MIN(RGBLIGHT_EFFECT_TWINKLE_LIFE, rgblight_config.val));
+ t->life = t->max_life;
} else {
// This LED is off, and was NOT selected to start brightening
}
diff --git a/quantum/rgblight.h b/quantum/rgblight.h
index 1854fee999..028b3ea416 100644
--- a/quantum/rgblight.h
+++ b/quantum/rgblight.h
@@ -150,7 +150,7 @@ enum RGBLIGHT_EFFECT_MODE {
# endif
# ifndef RGBLIGHT_EFFECT_TWINKLE_LIFE
-# define RGBLIGHT_EFFECT_TWINKLE_LIFE 75
+# define RGBLIGHT_EFFECT_TWINKLE_LIFE 200
# endif
# ifndef RGBLIGHT_EFFECT_TWINKLE_PROBABILITY
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c
index 51bf8b1095..bad762b493 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)
// Start with a clear matrix row
matrix_row_t current_row_value = 0;
- // Select row and wait for row selecton to stabilize
+ // Select row
select_row(current_row);
- matrix_io_delay();
+ matrix_output_select_delay();
// For each col...
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)
// Unselect row
unselect_row(current_row);
+ if (current_row + 1 < MATRIX_ROWS) {
+ matrix_output_unselect_delay(); // wait for row signal to go HIGH
+ }
// If the row has changed, store the row and return the changed flag.
if (current_matrix[current_row] != current_row_value) {
@@ -160,9 +163,9 @@ static void init_pins(void) {
static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col) {
bool matrix_changed = false;
- // Select col and wait for col selecton to stabilize
+ // Select col
select_col(current_col);
- matrix_io_delay();
+ matrix_output_select_delay();
// For each row...
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)
// Unselect col
unselect_col(current_col);
+ if (current_col + 1 < MATRIX_COLS) {
+ matrix_output_unselect_delay(); // wait for col signal to go HIGH
+ }
return matrix_changed;
}
@@ -245,21 +251,33 @@ void matrix_init(void) {
split_post_init();
}
-void matrix_post_scan(void) {
+bool matrix_post_scan(void) {
+ bool changed = false;
if (is_keyboard_master()) {
static uint8_t error_count;
- if (!transport_master(matrix + thatHand)) {
+ matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};
+ if (!transport_master(slave_matrix)) {
error_count++;
if (error_count > ERROR_DISCONNECT_COUNT) {
// reset other half if disconnected
for (int i = 0; i < ROWS_PER_HAND; ++i) {
matrix[thatHand + i] = 0;
+ slave_matrix[i] = 0;
}
+
+ changed = true;
}
} else {
error_count = 0;
+
+ for (int i = 0; i < ROWS_PER_HAND; ++i) {
+ if (matrix[thatHand + i] != slave_matrix[i]) {
+ matrix[thatHand + i] = slave_matrix[i];
+ changed = true;
+ }
+ }
}
matrix_scan_quantum();
@@ -268,25 +286,27 @@ void matrix_post_scan(void) {
matrix_slave_scan_user();
}
+
+ return changed;
}
uint8_t matrix_scan(void) {
- bool changed = false;
+ bool local_changed = false;
#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)
// Set row, read cols
for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {
- changed |= read_cols_on_row(raw_matrix, current_row);
+ local_changed |= read_cols_on_row(raw_matrix, current_row);
}
#elif (DIODE_DIRECTION == ROW2COL)
// Set col, read rows
for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
- changed |= read_rows_on_col(raw_matrix, current_col);
+ local_changed |= read_rows_on_col(raw_matrix, current_col);
}
#endif
- debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed);
+ debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, local_changed);
- matrix_post_scan();
- return (uint8_t)changed;
+ bool remote_changed = matrix_post_scan();
+ return (uint8_t)(local_changed || remote_changed);
}
diff --git a/quantum/split_common/transport.c b/quantum/split_common/transport.c
index 467ff81a97..b45ba92c3b 100644
--- a/quantum/split_common/transport.c
+++ b/quantum/split_common/transport.c
@@ -6,6 +6,7 @@
#include "quantum.h"
#define ROWS_PER_HAND (MATRIX_ROWS / 2)
+#define SYNC_TIMER_OFFSET 2
#ifdef RGBLIGHT_ENABLE
# include "rgblight.h"
@@ -27,8 +28,20 @@ static pin_t encoders_pad[] = ENCODERS_PAD_A;
# include "i2c_slave.h"
typedef struct _I2C_slave_buffer_t {
+# ifndef DISABLE_SYNC_TIMER
+ uint32_t sync_timer;
+# endif
matrix_row_t smatrix[ROWS_PER_HAND];
- uint8_t backlight_level;
+# ifdef SPLIT_MODS_ENABLE
+ uint8_t real_mods;
+ uint8_t weak_mods;
+# ifndef NO_ACTION_ONESHOT
+ uint8_t oneshot_mods;
+# endif
+# endif
+# ifdef BACKLIGHT_ENABLE
+ uint8_t backlight_level;
+# endif
# if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
rgblight_syncinfo_t rgblight_sync;
# endif
@@ -42,9 +55,13 @@ typedef struct _I2C_slave_buffer_t {
static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg;
+# define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer)
+# define I2C_KEYMAP_START offsetof(I2C_slave_buffer_t, smatrix)
+# define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods)
+# define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods)
+# define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods)
# define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level)
# define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync)
-# define I2C_KEYMAP_START offsetof(I2C_slave_buffer_t, smatrix)
# define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state)
# define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm)
@@ -91,10 +108,43 @@ bool transport_master(matrix_row_t matrix[]) {
}
}
# endif
+
+# ifdef SPLIT_MODS_ENABLE
+ uint8_t real_mods = get_mods();
+ if (real_mods != i2c_buffer->real_mods) {
+ if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) {
+ i2c_buffer->real_mods = real_mods;
+ }
+ }
+
+ uint8_t weak_mods = get_weak_mods();
+ if (weak_mods != i2c_buffer->weak_mods) {
+ if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) {
+ i2c_buffer->weak_mods = weak_mods;
+ }
+ }
+
+# ifndef NO_ACTION_ONESHOT
+ uint8_t oneshot_mods = get_oneshot_mods();
+ if (oneshot_mods != i2c_buffer->oneshot_mods) {
+ if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) {
+ i2c_buffer->oneshot_mods = oneshot_mods;
+ }
+ }
+# endif
+# endif
+
+# ifndef DISABLE_SYNC_TIMER
+ i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
+ i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT);
+# endif
return true;
}
void transport_slave(matrix_row_t matrix[]) {
+# ifndef DISABLE_SYNC_TIMER
+ sync_timer_update(i2c_buffer->sync_timer);
+# endif
// Copy matrix to I2C buffer
memcpy((void *)i2c_buffer->smatrix, (void *)matrix, sizeof(i2c_buffer->smatrix));
@@ -118,6 +168,14 @@ void transport_slave(matrix_row_t matrix[]) {
# ifdef WPM_ENABLE
set_current_wpm(i2c_buffer->current_wpm);
# endif
+
+# ifdef SPLIT_MODS_ENABLE
+ set_mods(i2c_buffer->real_mods);
+ set_weak_mods(i2c_buffer->weak_mods);
+# ifndef NO_ACTION_ONESHOT
+ set_oneshot_mods(i2c_buffer->oneshot_mods);
+# endif
+# endif
}
void transport_master_init(void) { i2c_init(); }
@@ -139,11 +197,21 @@ typedef struct _Serial_s2m_buffer_t {
} Serial_s2m_buffer_t;
typedef struct _Serial_m2s_buffer_t {
+# ifdef SPLIT_MODS_ENABLE
+ uint8_t real_mods;
+ uint8_t weak_mods;
+# ifndef NO_ACTION_ONESHOT
+ uint8_t oneshot_mods;
+# endif
+# endif
+# ifndef DISABLE_SYNC_TIMER
+ uint32_t sync_timer;
+# endif
# ifdef BACKLIGHT_ENABLE
- uint8_t backlight_level;
+ uint8_t backlight_level;
# endif
# ifdef WPM_ENABLE
- uint8_t current_wpm;
+ uint8_t current_wpm;
# endif
} Serial_m2s_buffer_t;
@@ -249,13 +317,28 @@ bool transport_master(matrix_row_t matrix[]) {
# ifdef WPM_ENABLE
// Write wpm to slave
- serial_m2s_buffer.current_wpm = get_current_wpm();
+ serial_m2s_buffer.current_wpm = get_current_wpm();
+# endif
+
+# ifdef SPLIT_MODS_ENABLE
+ serial_m2s_buffer.real_mods = get_mods();
+ serial_m2s_buffer.weak_mods = get_weak_mods();
+# ifndef NO_ACTION_ONESHOT
+ serial_m2s_buffer.oneshot_mods = get_oneshot_mods();
+# endif
+# endif
+# ifndef DISABLE_SYNC_TIMER
+ serial_m2s_buffer.sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
# endif
return true;
}
void transport_slave(matrix_row_t matrix[]) {
transport_rgblight_slave();
+# ifndef DISABLE_SYNC_TIMER
+ sync_timer_update(serial_m2s_buffer.sync_timer);
+# endif
+
// TODO: if MATRIX_COLS > 8 change to pack()
for (int i = 0; i < ROWS_PER_HAND; ++i) {
serial_s2m_buffer.smatrix[i] = matrix[i];
@@ -271,6 +354,14 @@ void transport_slave(matrix_row_t matrix[]) {
# ifdef WPM_ENABLE
set_current_wpm(serial_m2s_buffer.current_wpm);
# endif
+
+# ifdef SPLIT_MODS_ENABLE
+ set_mods(serial_m2s_buffer.real_mods);
+ set_weak_mods(serial_m2s_buffer.weak_mods);
+# ifndef NO_ACTION_ONESHOT
+ set_oneshot_mods(serial_m2s_buffer.oneshot_mods);
+# endif
+# endif
}
#endif