aboutsummaryrefslogtreecommitdiff
path: root/quantum
diff options
context:
space:
mode:
authorJames Young <18669334+noroadsleft@users.noreply.github.com>2020-11-28 12:02:18 -0800
committerGitHub <noreply@github.com>2020-11-28 12:02:18 -0800
commitc66df1664497546f32662409778731143e45a552 (patch)
treeda73a2d532a27685a31d932b3a44a707d4a3af81 /quantum
parent15385d4113414d42bd062c60c9de5df797d3157f (diff)
downloadqmk_firmware-c66df1664497546f32662409778731143e45a552.tar.gz
qmk_firmware-c66df1664497546f32662409778731143e45a552.zip
2020 November 28 Breaking Changes Update (#11053)
* Branch point for 2020 November 28 Breaking Change * Remove matrix_col_t to allow MATRIX_ROWS > 32 (#10183) * Add support for soft serial to ATmega32U2 (#10204) * Change MIDI velocity implementation to allow direct control of velocity value (#9940) * Add ability to build a subset of all keyboards based on platform. * Actually use eeprom_driver_init(). * Make bootloader_jump weak for ChibiOS. (#10417) * Joystick 16-bit support (#10439) * Per-encoder resolutions (#10259) * Share button state from mousekey to pointing_device (#10179) * Add hotfix for chibios keyboards not wake (#10088) * Add advanced/efficient RGB Matrix Indicators (#8564) * Naming change. * Support for STM32 GPIOF,G,H,I,J,K (#10206) * Add milc as a dependency and remove the installed milc (#10563) * ChibiOS upgrade: early init conversions (#10214) * ChibiOS upgrade: configuration file migrator (#9952) * Haptic and solenoid cleanup (#9700) * XD75 cleanup (#10524) * OLED display update interval support (#10388) * Add definition based on currently-selected serial driver. (#10716) * New feature: Retro Tapping per key (#10622) * Allow for modification of output RGB values when using rgblight/rgb_matrix. (#10638) * Add housekeeping task callbacks so that keyboards/keymaps are capable of executing code for each main loop iteration. (#10530) * Rescale both ChibiOS and AVR backlighting. * Reduce Helix keyboard build variation (#8669) * Minor change to behavior allowing display updates to continue between task ticks (#10750) * Some GPIO manipulations in matrix.c change to atomic. (#10491) * qmk cformat (#10767) * [Keyboard] Update the Speedo firmware for v3.0 (#10657) * Maartenwut/Maarten namechange to evyd13/Evy (#10274) * [quantum] combine repeated lines of code (#10837) * Add step sequencer feature (#9703) * aeboards/ext65 refactor (#10820) * Refactor xelus/dawn60 for Rev2 later (#10584) * add DEBUG_MATRIX_SCAN_RATE_ENABLE to common_features.mk (#10824) * [Core] Added `add_oneshot_mods` & `del_oneshot_mods` (#10549) * update chibios os usb for the otg driver (#8893) * Remove HD44780 References, Part 4 (#10735) * [Keyboard] Add Valor FRL TKL (+refactor) (#10512) * Fix cursor position bug in oled_write_raw functions (#10800) * Fixup version.h writing when using SKIP_VERSION=yes (#10972) * Allow for certain code in the codebase assuming length of string. (#10974) * Add AT90USB support for serial.c (#10706) * Auto shift: support repeats and early registration (#9826) * Rename ledmatrix.h to match .c file (#7949) * Split RGB_MATRIX_ENABLE into _ENABLE and _DRIVER (#10231) * Split LED_MATRIX_ENABLE into _ENABLE and _DRIVER (#10840) * Merge point for 2020 Nov 28 Breaking Change
Diffstat (limited to 'quantum')
-rw-r--r--quantum/backlight/backlight_avr.c12
-rw-r--r--quantum/backlight/backlight_chibios.c14
-rw-r--r--quantum/config_common.h92
-rw-r--r--quantum/encoder.c24
-rw-r--r--quantum/joystick.h12
-rw-r--r--quantum/led_matrix.c2
-rw-r--r--quantum/led_matrix.h (renamed from quantum/ledmatrix.h)0
-rw-r--r--quantum/led_matrix_drivers.c2
-rw-r--r--quantum/matrix.c35
-rw-r--r--quantum/mcu_selection.mk3
-rw-r--r--quantum/process_keycode/process_auto_shift.c199
-rw-r--r--quantum/process_keycode/process_auto_shift.h1
-rw-r--r--quantum/process_keycode/process_joystick.c8
-rw-r--r--quantum/process_keycode/process_midi.c25
-rw-r--r--quantum/process_keycode/process_midi.h2
-rw-r--r--quantum/process_keycode/process_sequencer.c62
-rw-r--r--quantum/process_keycode/process_sequencer.h21
-rw-r--r--quantum/quantum.c15
-rw-r--r--quantum/quantum.h62
-rw-r--r--quantum/quantum_keycodes.h38
-rw-r--r--quantum/rgb_matrix.c32
-rw-r--r--quantum/rgb_matrix.h9
-rw-r--r--quantum/rgb_matrix_animations/alpha_mods_anim.h4
-rw-r--r--quantum/rgb_matrix_animations/breathing_anim.h2
-rw-r--r--quantum/rgb_matrix_animations/gradient_left_right_anim.h2
-rw-r--r--quantum/rgb_matrix_animations/gradient_up_down_anim.h2
-rw-r--r--quantum/rgb_matrix_animations/jellybean_raindrops_anim.h2
-rw-r--r--quantum/rgb_matrix_animations/raindrops_anim.h2
-rw-r--r--quantum/rgb_matrix_animations/solid_color_anim.h2
-rw-r--r--quantum/rgb_matrix_animations/typing_heatmap_anim.h2
-rw-r--r--quantum/rgb_matrix_runners/effect_runner_dx_dy.h2
-rw-r--r--quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h2
-rw-r--r--quantum/rgb_matrix_runners/effect_runner_i.h2
-rw-r--r--quantum/rgb_matrix_runners/effect_runner_reactive.h2
-rw-r--r--quantum/rgb_matrix_runners/effect_runner_reactive_splash.h2
-rw-r--r--quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h2
-rw-r--r--quantum/rgblight.c4
-rw-r--r--quantum/sequencer/sequencer.c275
-rw-r--r--quantum/sequencer/sequencer.h122
-rw-r--r--quantum/sequencer/tests/midi_mock.c26
-rw-r--r--quantum/sequencer/tests/midi_mock.h26
-rw-r--r--quantum/sequencer/tests/rules.mk11
-rw-r--r--quantum/sequencer/tests/sequencer_tests.cpp590
-rw-r--r--quantum/sequencer/tests/testlist.mk1
-rw-r--r--quantum/split_common/matrix.c35
45 files changed, 1668 insertions, 122 deletions
diff --git a/quantum/backlight/backlight_avr.c b/quantum/backlight/backlight_avr.c
index b3e882ffe..4d66da80b 100644
--- a/quantum/backlight/backlight_avr.c
+++ b/quantum/backlight/backlight_avr.c
@@ -3,6 +3,11 @@
3#include "backlight_driver_common.h" 3#include "backlight_driver_common.h"
4#include "debug.h" 4#include "debug.h"
5 5
6// Maximum duty cycle limit
7#ifndef BACKLIGHT_LIMIT_VAL
8# define BACKLIGHT_LIMIT_VAL 255
9#endif
10
6// This logic is a bit complex, we support 3 setups: 11// This logic is a bit complex, we support 3 setups:
7// 12//
8// 1. Hardware PWM when backlight is wired to a PWM pin. 13// 1. Hardware PWM when backlight is wired to a PWM pin.
@@ -240,6 +245,9 @@ static uint16_t cie_lightness(uint16_t v) {
240 } 245 }
241} 246}
242 247
248// rescale the supplied backlight value to be in terms of the value limit
249static uint32_t rescale_limit_val(uint32_t val) { return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256; }
250
243// range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val. 251// range for val is [0..TIMER_TOP]. PWM pin is high while the timer count is below val.
244static inline void set_pwm(uint16_t val) { OCRxx = val; } 252static inline void set_pwm(uint16_t val) { OCRxx = val; }
245 253
@@ -269,7 +277,7 @@ void backlight_set(uint8_t level) {
269#endif 277#endif
270 } 278 }
271 // Set the brightness 279 // Set the brightness
272 set_pwm(cie_lightness(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS)); 280 set_pwm(cie_lightness(rescale_limit_val(TIMER_TOP * (uint32_t)level / BACKLIGHT_LEVELS)));
273} 281}
274 282
275void backlight_task(void) {} 283void backlight_task(void) {}
@@ -375,7 +383,7 @@ ISR(TIMERx_OVF_vect)
375 breathing_interrupt_disable(); 383 breathing_interrupt_disable();
376 } 384 }
377 385
378 set_pwm(cie_lightness(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * 0x0101U))); 386 set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint16_t)pgm_read_byte(&breathing_table[index]) * 0x0101U))));
379} 387}
380 388
381#endif // BACKLIGHT_BREATHING 389#endif // BACKLIGHT_BREATHING
diff --git a/quantum/backlight/backlight_chibios.c b/quantum/backlight/backlight_chibios.c
index 0fe812bf2..4d5a69e14 100644
--- a/quantum/backlight/backlight_chibios.c
+++ b/quantum/backlight/backlight_chibios.c
@@ -3,6 +3,11 @@
3#include <hal.h> 3#include <hal.h>
4#include "debug.h" 4#include "debug.h"
5 5
6// Maximum duty cycle limit
7#ifndef BACKLIGHT_LIMIT_VAL
8# define BACKLIGHT_LIMIT_VAL 255
9#endif
10
6// GPIOV2 && GPIOV3 11// GPIOV2 && GPIOV3
7#ifndef BACKLIGHT_PAL_MODE 12#ifndef BACKLIGHT_PAL_MODE
8# define BACKLIGHT_PAL_MODE 2 13# define BACKLIGHT_PAL_MODE 2
@@ -58,6 +63,11 @@ static uint16_t cie_lightness(uint16_t v) {
58 } 63 }
59} 64}
60 65
66static uint32_t rescale_limit_val(uint32_t val) {
67 // rescale the supplied backlight value to be in terms of the value limit
68 return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256;
69}
70
61void backlight_init_ports(void) { 71void backlight_init_ports(void) {
62#ifdef USE_GPIOV1 72#ifdef USE_GPIOV1
63 palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); 73 palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL);
@@ -85,7 +95,7 @@ void backlight_set(uint8_t level) {
85 pwmDisableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1); 95 pwmDisableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1);
86 } else { 96 } else {
87 // Turn backlight on 97 // Turn backlight on
88 uint32_t duty = (uint32_t)(cie_lightness(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS)); 98 uint32_t duty = (uint32_t)(cie_lightness(rescale_limit_val(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS)));
89 pwmEnableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); 99 pwmEnableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty));
90 } 100 }
91} 101}
@@ -129,7 +139,7 @@ void breathing_callback(PWMDriver *pwmp) {
129 static uint16_t breathing_counter = 0; 139 static uint16_t breathing_counter = 0;
130 breathing_counter = (breathing_counter + 1) % (breathing_period * 256); 140 breathing_counter = (breathing_counter + 1) % (breathing_period * 256);
131 uint8_t index = breathing_counter / interval % BREATHING_STEPS; 141 uint8_t index = breathing_counter / interval % BREATHING_STEPS;
132 uint32_t duty = cie_lightness(scale_backlight(breathing_table[index] * 256)); 142 uint32_t duty = cie_lightness(rescale_limit_val(scale_backlight(breathing_table[index] * 256)));
133 143
134 chSysLockFromISR(); 144 chSysLockFromISR();
135 pwmEnableChannelI(pwmp, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); 145 pwmEnableChannelI(pwmp, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty));
diff --git a/quantum/config_common.h b/quantum/config_common.h
index c1e6698e5..2d9c70b08 100644
--- a/quantum/config_common.h
+++ b/quantum/config_common.h
@@ -39,7 +39,7 @@
39# define PIND_ADDRESS 0x9 39# define PIND_ADDRESS 0x9
40# define PINE_ADDRESS 0xC 40# define PINE_ADDRESS 0xC
41# define PINF_ADDRESS 0xF 41# define PINF_ADDRESS 0xF
42# elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) 42# elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
43# define ADDRESS_BASE 0x00 43# define ADDRESS_BASE 0x00
44# define PINB_ADDRESS 0x3 44# define PINB_ADDRESS 0x3
45# define PINC_ADDRESS 0x6 45# define PINC_ADDRESS 0x6
@@ -58,11 +58,6 @@
58# define PINC_ADDRESS 0x3 58# define PINC_ADDRESS 0x3
59# define PINB_ADDRESS 0x6 59# define PINB_ADDRESS 0x6
60# define PINA_ADDRESS 0x9 60# define PINA_ADDRESS 0x9
61# elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
62# define ADDRESS_BASE 0x00
63# define PINB_ADDRESS 0x3
64# define PINC_ADDRESS 0x6
65# define PIND_ADDRESS 0x9
66# elif defined(__AVR_ATtiny85__) 61# elif defined(__AVR_ATtiny85__)
67# define ADDRESS_BASE 0x10 62# define ADDRESS_BASE 0x10
68# define PINB_ADDRESS 0x6 63# define PINB_ADDRESS 0x6
@@ -284,6 +279,91 @@
284# define F13 PAL_LINE(GPIOF, 13) 279# define F13 PAL_LINE(GPIOF, 13)
285# define F14 PAL_LINE(GPIOF, 14) 280# define F14 PAL_LINE(GPIOF, 14)
286# define F15 PAL_LINE(GPIOF, 15) 281# define F15 PAL_LINE(GPIOF, 15)
282# define G0 PAL_LINE(GPIOG, 0)
283# define G1 PAL_LINE(GPIOG, 1)
284# define G2 PAL_LINE(GPIOG, 2)
285# define G3 PAL_LINE(GPIOG, 3)
286# define G4 PAL_LINE(GPIOG, 4)
287# define G5 PAL_LINE(GPIOG, 5)
288# define G6 PAL_LINE(GPIOG, 6)
289# define G7 PAL_LINE(GPIOG, 7)
290# define G8 PAL_LINE(GPIOG, 8)
291# define G9 PAL_LINE(GPIOG, 9)
292# define G10 PAL_LINE(GPIOG, 10)
293# define G11 PAL_LINE(GPIOG, 11)
294# define G12 PAL_LINE(GPIOG, 12)
295# define G13 PAL_LINE(GPIOG, 13)
296# define G14 PAL_LINE(GPIOG, 14)
297# define G15 PAL_LINE(GPIOG, 15)
298# define H0 PAL_LINE(GPIOH, 0)
299# define H1 PAL_LINE(GPIOH, 1)
300# define H2 PAL_LINE(GPIOH, 2)
301# define H3 PAL_LINE(GPIOH, 3)
302# define H4 PAL_LINE(GPIOH, 4)
303# define H5 PAL_LINE(GPIOH, 5)
304# define H6 PAL_LINE(GPIOH, 6)
305# define H7 PAL_LINE(GPIOH, 7)
306# define H8 PAL_LINE(GPIOH, 8)
307# define H9 PAL_LINE(GPIOH, 9)
308# define H10 PAL_LINE(GPIOH, 10)
309# define H11 PAL_LINE(GPIOH, 11)
310# define H12 PAL_LINE(GPIOH, 12)
311# define H13 PAL_LINE(GPIOH, 13)
312# define H14 PAL_LINE(GPIOH, 14)
313# define H15 PAL_LINE(GPIOH, 15)
314# define I0 PAL_LINE(GPIOI, 0)
315# define I1 PAL_LINE(GPIOI, 1)
316# define I2 PAL_LINE(GPIOI, 2)
317# define I3 PAL_LINE(GPIOI, 3)
318# define I4 PAL_LINE(GPIOI, 4)
319# define I5 PAL_LINE(GPIOI, 5)
320# define I6 PAL_LINE(GPIOI, 6)
321# define I7 PAL_LINE(GPIOI, 7)
322# define I8 PAL_LINE(GPIOI, 8)
323# define I9 PAL_LINE(GPIOI, 9)
324# define I10 PAL_LINE(GPIOI, 10)
325# define I11 PAL_LINE(GPIOI, 11)
326# define I12 PAL_LINE(GPIOI, 12)
327# define I13 PAL_LINE(GPIOI, 13)
328# define I14 PAL_LINE(GPIOI, 14)
329# define I15 PAL_LINE(GPIOI, 15)
330# define J0 PAL_LINE(GPIOJ, 0)
331# define J1 PAL_LINE(GPIOJ, 1)
332# define J2 PAL_LINE(GPIOJ, 2)
333# define J3 PAL_LINE(GPIOJ, 3)
334# define J4 PAL_LINE(GPIOJ, 4)
335# define J5 PAL_LINE(GPIOJ, 5)
336# define J6 PAL_LINE(GPIOJ, 6)
337# define J7 PAL_LINE(GPIOJ, 7)
338# define J8 PAL_LINE(GPIOJ, 8)
339# define J9 PAL_LINE(GPIOJ, 9)
340# define J10 PAL_LINE(GPIOJ, 10)
341# define J11 PAL_LINE(GPIOJ, 11)
342# define J12 PAL_LINE(GPIOJ, 12)
343# define J13 PAL_LINE(GPIOJ, 13)
344# define J14 PAL_LINE(GPIOJ, 14)
345# define J15 PAL_LINE(GPIOJ, 15)
346// Keyboards can `#define KEYBOARD_REQUIRES_GPIOK` if they need to access GPIO-K pins. These conflict with a whole
347// bunch of layout definitions, so it's intentionally left out unless absolutely required -- in that case, the
348// keyboard designer should use a different symbol when defining their layout macros.
349# ifdef KEYBOARD_REQUIRES_GPIOK
350# define K0 PAL_LINE(GPIOK, 0)
351# define K1 PAL_LINE(GPIOK, 1)
352# define K2 PAL_LINE(GPIOK, 2)
353# define K3 PAL_LINE(GPIOK, 3)
354# define K4 PAL_LINE(GPIOK, 4)
355# define K5 PAL_LINE(GPIOK, 5)
356# define K6 PAL_LINE(GPIOK, 6)
357# define K7 PAL_LINE(GPIOK, 7)
358# define K8 PAL_LINE(GPIOK, 8)
359# define K9 PAL_LINE(GPIOK, 9)
360# define K10 PAL_LINE(GPIOK, 10)
361# define K11 PAL_LINE(GPIOK, 11)
362# define K12 PAL_LINE(GPIOK, 12)
363# define K13 PAL_LINE(GPIOK, 13)
364# define K14 PAL_LINE(GPIOK, 14)
365# define K15 PAL_LINE(GPIOK, 15)
366# endif
287# endif 367# endif
288#endif 368#endif
289 369
diff --git a/quantum/encoder.c b/quantum/encoder.c
index 81ec1bb37..7ca31afed 100644
--- a/quantum/encoder.c
+++ b/quantum/encoder.c
@@ -23,7 +23,7 @@
23// for memcpy 23// for memcpy
24#include <string.h> 24#include <string.h>
25 25
26#ifndef ENCODER_RESOLUTION 26#if !defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTION)
27# define ENCODER_RESOLUTION 4 27# define ENCODER_RESOLUTION 4
28#endif 28#endif
29 29
@@ -34,6 +34,9 @@
34#define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t)) 34#define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t))
35static pin_t encoders_pad_a[] = ENCODERS_PAD_A; 35static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
36static pin_t encoders_pad_b[] = ENCODERS_PAD_B; 36static pin_t encoders_pad_b[] = ENCODERS_PAD_B;
37#ifdef ENCODER_RESOLUTIONS
38static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;
39#endif
37 40
38#ifndef ENCODER_DIRECTION_FLIP 41#ifndef ENCODER_DIRECTION_FLIP
39# define ENCODER_CLOCKWISE true 42# define ENCODER_CLOCKWISE true
@@ -65,9 +68,15 @@ void encoder_init(void) {
65 if (!isLeftHand) { 68 if (!isLeftHand) {
66 const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT; 69 const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT;
67 const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT; 70 const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT;
71# if defined(ENCODER_RESOLUTIONS_RIGHT)
72 const uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT;
73# endif
68 for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) { 74 for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
69 encoders_pad_a[i] = encoders_pad_a_right[i]; 75 encoders_pad_a[i] = encoders_pad_a_right[i];
70 encoders_pad_b[i] = encoders_pad_b_right[i]; 76 encoders_pad_b[i] = encoders_pad_b_right[i];
77# if defined(ENCODER_RESOLUTIONS_RIGHT)
78 encoder_resolutions[i] = encoder_resolutions_right[i];
79# endif
71 } 80 }
72 } 81 }
73#endif 82#endif
@@ -87,19 +96,26 @@ void encoder_init(void) {
87 96
88static void encoder_update(int8_t index, uint8_t state) { 97static void encoder_update(int8_t index, uint8_t state) {
89 uint8_t i = index; 98 uint8_t i = index;
99
100#ifdef ENCODER_RESOLUTIONS
101 int8_t resolution = encoder_resolutions[i];
102#else
103 int8_t resolution = ENCODER_RESOLUTION;
104#endif
105
90#ifdef SPLIT_KEYBOARD 106#ifdef SPLIT_KEYBOARD
91 index += thisHand; 107 index += thisHand;
92#endif 108#endif
93 encoder_pulses[i] += encoder_LUT[state & 0xF]; 109 encoder_pulses[i] += encoder_LUT[state & 0xF];
94 if (encoder_pulses[i] >= ENCODER_RESOLUTION) { 110 if (encoder_pulses[i] >= resolution) {
95 encoder_value[index]++; 111 encoder_value[index]++;
96 encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE); 112 encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
97 } 113 }
98 if (encoder_pulses[i] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise 114 if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise
99 encoder_value[index]--; 115 encoder_value[index]--;
100 encoder_update_kb(index, ENCODER_CLOCKWISE); 116 encoder_update_kb(index, ENCODER_CLOCKWISE);
101 } 117 }
102 encoder_pulses[i] %= ENCODER_RESOLUTION; 118 encoder_pulses[i] %= resolution;
103} 119}
104 120
105void encoder_read(void) { 121void encoder_read(void) {
diff --git a/quantum/joystick.h b/quantum/joystick.h
index a95472b9f..87dbc24af 100644
--- a/quantum/joystick.h
+++ b/quantum/joystick.h
@@ -1,5 +1,9 @@
1#pragma once 1#pragma once
2 2
3#include "quantum.h"
4
5#include <stdint.h>
6
3#ifndef JOYSTICK_BUTTON_COUNT 7#ifndef JOYSTICK_BUTTON_COUNT
4# define JOYSTICK_BUTTON_COUNT 8 8# define JOYSTICK_BUTTON_COUNT 8
5#endif 9#endif
@@ -8,9 +12,13 @@
8# define JOYSTICK_AXES_COUNT 4 12# define JOYSTICK_AXES_COUNT 4
9#endif 13#endif
10 14
11#include "quantum.h" 15#ifndef JOYSTICK_AXES_RESOLUTION
16# define JOYSTICK_AXES_RESOLUTION 8
17#elif JOYSTICK_AXES_RESOLUTION < 8 || JOYSTICK_AXES_RESOLUTION > 16
18# error JOYSTICK_AXES_RESOLUTION must be between 8 and 16
19#endif
12 20
13#include <stdint.h> 21#define JOYSTICK_RESOLUTION ((1L << (JOYSTICK_AXES_RESOLUTION - 1)) - 1)
14 22
15// configure on input_pin of the joystick_axes array entry to JS_VIRTUAL_AXIS 23// configure on input_pin of the joystick_axes array entry to JS_VIRTUAL_AXIS
16// to prevent it from being read from the ADC. This allows outputing forged axis value. 24// to prevent it from being read from the ADC. This allows outputing forged axis value.
diff --git a/quantum/led_matrix.c b/quantum/led_matrix.c
index 5c24c797a..eb523990a 100644
--- a/quantum/led_matrix.c
+++ b/quantum/led_matrix.c
@@ -20,7 +20,7 @@
20#include <stdint.h> 20#include <stdint.h>
21#include <stdbool.h> 21#include <stdbool.h>
22#include "quantum.h" 22#include "quantum.h"
23#include "ledmatrix.h" 23#include "led_matrix.h"
24#include "progmem.h" 24#include "progmem.h"
25#include "config.h" 25#include "config.h"
26#include "eeprom.h" 26#include "eeprom.h"
diff --git a/quantum/ledmatrix.h b/quantum/led_matrix.h
index 5867ba987..5867ba987 100644
--- a/quantum/ledmatrix.h
+++ b/quantum/led_matrix.h
diff --git a/quantum/led_matrix_drivers.c b/quantum/led_matrix_drivers.c
index 6877bf4c6..9decaa33c 100644
--- a/quantum/led_matrix_drivers.c
+++ b/quantum/led_matrix_drivers.c
@@ -18,7 +18,7 @@
18#include <stdint.h> 18#include <stdint.h>
19#include <stdbool.h> 19#include <stdbool.h>
20#include "quantum.h" 20#include "quantum.h"
21#include "ledmatrix.h" 21#include "led_matrix.h"
22 22
23/* Each driver needs to define a struct: 23/* Each driver needs to define a struct:
24 * 24 *
diff --git a/quantum/matrix.c b/quantum/matrix.c
index c68c56cac..cab0d2ddc 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -32,6 +32,19 @@ static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
32extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values 32extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
33extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values 33extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
34 34
35static inline void setPinOutput_writeLow(pin_t pin) {
36 ATOMIC_BLOCK_FORCEON {
37 setPinOutput(pin);
38 writePinLow(pin);
39 }
40}
41
42static inline void setPinInputHigh_atomic(pin_t pin) {
43 ATOMIC_BLOCK_FORCEON {
44 setPinInputHigh(pin);
45 }
46}
47
35// matrix code 48// matrix code
36 49
37#ifdef DIRECT_PINS 50#ifdef DIRECT_PINS
@@ -70,22 +83,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
70# if (DIODE_DIRECTION == COL2ROW) 83# if (DIODE_DIRECTION == COL2ROW)
71 84
72static void select_row(uint8_t row) { 85static void select_row(uint8_t row) {
73 setPinOutput(row_pins[row]); 86 setPinOutput_writeLow(row_pins[row]);
74 writePinLow(row_pins[row]);
75} 87}
76 88
77static void unselect_row(uint8_t row) { setPinInputHigh(row_pins[row]); } 89static void unselect_row(uint8_t row) {
90 setPinInputHigh_atomic(row_pins[row]);
91}
78 92
79static void unselect_rows(void) { 93static void unselect_rows(void) {
80 for (uint8_t x = 0; x < MATRIX_ROWS; x++) { 94 for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
81 setPinInputHigh(row_pins[x]); 95 setPinInputHigh_atomic(row_pins[x]);
82 } 96 }
83} 97}
84 98
85static void init_pins(void) { 99static void init_pins(void) {
86 unselect_rows(); 100 unselect_rows();
87 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 101 for (uint8_t x = 0; x < MATRIX_COLS; x++) {
88 setPinInputHigh(col_pins[x]); 102 setPinInputHigh_atomic(col_pins[x]);
89 } 103 }
90} 104}
91 105
@@ -120,22 +134,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
120# elif (DIODE_DIRECTION == ROW2COL) 134# elif (DIODE_DIRECTION == ROW2COL)
121 135
122static void select_col(uint8_t col) { 136static void select_col(uint8_t col) {
123 setPinOutput(col_pins[col]); 137 setPinOutput_writeLow(col_pins[col]);
124 writePinLow(col_pins[col]);
125} 138}
126 139
127static void unselect_col(uint8_t col) { setPinInputHigh(col_pins[col]); } 140static void unselect_col(uint8_t col) {
141 setPinInputHigh_atomic(col_pins[col]);
142}
128 143
129static void unselect_cols(void) { 144static void unselect_cols(void) {
130 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 145 for (uint8_t x = 0; x < MATRIX_COLS; x++) {
131 setPinInputHigh(col_pins[x]); 146 setPinInputHigh_atomic(col_pins[x]);
132 } 147 }
133} 148}
134 149
135static void init_pins(void) { 150static void init_pins(void) {
136 unselect_cols(); 151 unselect_cols();
137 for (uint8_t x = 0; x < MATRIX_ROWS; x++) { 152 for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
138 setPinInputHigh(row_pins[x]); 153 setPinInputHigh_atomic(row_pins[x]);
139 } 154 }
140} 155}
141 156
diff --git a/quantum/mcu_selection.mk b/quantum/mcu_selection.mk
index 295dfd318..9518a6463 100644
--- a/quantum/mcu_selection.mk
+++ b/quantum/mcu_selection.mk
@@ -318,6 +318,9 @@ ifneq (,$(filter $(MCU),atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 a
318 ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes)) 318 ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes))
319 OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT 319 OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
320 endif 320 endif
321 ifneq (,$(filter $(MCU),atmega16u2 atmega32u2))
322 NO_I2C = yes
323 endif
321endif 324endif
322 325
323ifneq (,$(filter $(MCU),atmega32a)) 326ifneq (,$(filter $(MCU),atmega32a))
diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c
index b1267922c..a2d315408 100644
--- a/quantum/process_keycode/process_auto_shift.c
+++ b/quantum/process_keycode/process_auto_shift.c
@@ -16,48 +16,149 @@
16 16
17#ifdef AUTO_SHIFT_ENABLE 17#ifdef AUTO_SHIFT_ENABLE
18 18
19# include <stdbool.h>
19# include <stdio.h> 20# include <stdio.h>
20 21
21# include "process_auto_shift.h" 22# include "process_auto_shift.h"
22 23
23static bool autoshift_enabled = true;
24static uint16_t autoshift_time = 0; 24static uint16_t autoshift_time = 0;
25static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT; 25static uint16_t autoshift_timeout = AUTO_SHIFT_TIMEOUT;
26static uint16_t autoshift_lastkey = KC_NO; 26static uint16_t autoshift_lastkey = KC_NO;
27static struct {
28 // Whether autoshift is enabled.
29 bool enabled : 1;
30 // Whether the last auto-shifted key was released after the timeout. This
31 // is used to replicate the last key for a tap-then-hold.
32 bool lastshifted : 1;
33 // Whether an auto-shiftable key has been pressed but not processed.
34 bool in_progress : 1;
35 // Whether the auto-shifted keypress has been registered.
36 bool holding_shift : 1;
37} autoshift_flags = {true, false, false, false};
38
39/** \brief Record the press of an autoshiftable key
40 *
41 * \return Whether the record should be further processed.
42 */
43static bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) {
44 if (!autoshift_flags.enabled) {
45 return true;
46 }
47
48# ifndef AUTO_SHIFT_MODIFIERS
49 if (get_mods() & (~MOD_BIT(KC_LSFT))) {
50 return true;
51 }
52# endif
53# ifdef AUTO_SHIFT_REPEAT
54 const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
55# ifndef AUTO_SHIFT_NO_AUTO_REPEAT
56 if (!autoshift_flags.lastshifted) {
57# endif
58 if (elapsed < TAPPING_TERM && keycode == autoshift_lastkey) {
59 // Allow a tap-then-hold for keyrepeat.
60 if (!autoshift_flags.lastshifted) {
61 register_code(autoshift_lastkey);
62 } else {
63 // Simulate pressing the shift key.
64 add_weak_mods(MOD_BIT(KC_LSFT));
65 register_code(autoshift_lastkey);
66 }
67 return false;
68 }
69# ifndef AUTO_SHIFT_NO_AUTO_REPEAT
70 }
71# endif
72# endif
27 73
28void autoshift_flush(void) { 74 // Record the keycode so we can simulate it later.
29 if (autoshift_lastkey != KC_NO) { 75 autoshift_lastkey = keycode;
30 uint16_t elapsed = timer_elapsed(autoshift_time); 76 autoshift_time = now;
77 autoshift_flags.in_progress = true;
31 78
32 if (elapsed > autoshift_timeout) { 79# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
33 tap_code16(LSFT(autoshift_lastkey)); 80 clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
81# endif
82 return false;
83}
84
85/** \brief Registers an autoshiftable key under the right conditions
86 *
87 * If the autoshift delay has elapsed, register a shift and the key.
88 *
89 * If the autoshift key is released before the delay has elapsed, register the
90 * key without a shift.
91 */
92static void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger) {
93 // Called on key down with KC_NO, auto-shifted key up, and timeout.
94 if (autoshift_flags.in_progress) {
95 // Process the auto-shiftable key.
96 autoshift_flags.in_progress = false;
97
98 // Time since the initial press was recorded.
99 const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
100 if (elapsed < autoshift_timeout) {
101 register_code(autoshift_lastkey);
102 autoshift_flags.lastshifted = false;
34 } else { 103 } else {
35 tap_code(autoshift_lastkey); 104 // Simulate pressing the shift key.
105 add_weak_mods(MOD_BIT(KC_LSFT));
106 register_code(autoshift_lastkey);
107 autoshift_flags.lastshifted = true;
108# if defined(AUTO_SHIFT_REPEAT) && !defined(AUTO_SHIFT_NO_AUTO_REPEAT)
109 if (matrix_trigger) {
110 // Prevents release.
111 return;
112 }
113# endif
36 } 114 }
37 115
38 autoshift_time = 0; 116# if TAP_CODE_DELAY > 0
39 autoshift_lastkey = KC_NO; 117 wait_ms(TAP_CODE_DELAY);
118# endif
119 unregister_code(autoshift_lastkey);
120 del_weak_mods(MOD_BIT(KC_LSFT));
121 } else {
122 // Release after keyrepeat.
123 unregister_code(keycode);
124 if (keycode == autoshift_lastkey) {
125 // This will only fire when the key was the last auto-shiftable
126 // pressed. That prevents aaaaBBBB then releasing a from unshifting
127 // later Bs (if B wasn't auto-shiftable).
128 del_weak_mods(MOD_BIT(KC_LSFT));
129 }
40 } 130 }
131 send_keyboard_report(); // del_weak_mods doesn't send one.
132 // Roll the autoshift_time forward for detecting tap-and-hold.
133 autoshift_time = now;
41} 134}
42 135
43void autoshift_on(uint16_t keycode) { 136/** \brief Simulates auto-shifted key releases when timeout is hit
44 autoshift_time = timer_read(); 137 *
45 autoshift_lastkey = keycode; 138 * Can be called from \c matrix_scan_user so that auto-shifted keys are sent
139 * immediately after the timeout has expired, rather than waiting for the key
140 * to be released.
141 */
142void autoshift_matrix_scan(void) {
143 if (autoshift_flags.in_progress) {
144 const uint16_t now = timer_read();
145 const uint16_t elapsed = TIMER_DIFF_16(now, autoshift_time);
146 if (elapsed >= autoshift_timeout) {
147 autoshift_end(autoshift_lastkey, now, true);
148 }
149 }
46} 150}
47 151
48void autoshift_toggle(void) { 152void autoshift_toggle(void) {
49 if (autoshift_enabled) { 153 autoshift_flags.enabled = !autoshift_flags.enabled;
50 autoshift_enabled = false; 154 del_weak_mods(MOD_BIT(KC_LSFT));
51 autoshift_flush();
52 } else {
53 autoshift_enabled = true;
54 }
55} 155}
56 156
57void autoshift_enable(void) { autoshift_enabled = true; } 157void autoshift_enable(void) { autoshift_flags.enabled = true; }
158
58void autoshift_disable(void) { 159void autoshift_disable(void) {
59 autoshift_enabled = false; 160 autoshift_flags.enabled = false;
60 autoshift_flush(); 161 del_weak_mods(MOD_BIT(KC_LSFT));
61} 162}
62 163
63# ifndef AUTO_SHIFT_NO_SETUP 164# ifndef AUTO_SHIFT_NO_SETUP
@@ -70,19 +171,30 @@ void autoshift_timer_report(void) {
70} 171}
71# endif 172# endif
72 173
73bool get_autoshift_state(void) { return autoshift_enabled; } 174bool get_autoshift_state(void) { return autoshift_flags.enabled; }
74 175
75uint16_t get_autoshift_timeout(void) { return autoshift_timeout; } 176uint16_t get_autoshift_timeout(void) { return autoshift_timeout; }
76 177
77void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; } 178void set_autoshift_timeout(uint16_t timeout) { autoshift_timeout = timeout; }
78 179
79bool process_auto_shift(uint16_t keycode, keyrecord_t *record) { 180bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
181 // Note that record->event.time isn't reliable, see:
182 // https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550
183 const uint16_t now = timer_read();
184
80 if (record->event.pressed) { 185 if (record->event.pressed) {
186 if (autoshift_flags.in_progress) {
187 // Evaluate previous key if there is one. Doing this elsewhere is
188 // more complicated and easier to break.
189 autoshift_end(KC_NO, now, false);
190 }
191 // For pressing another key while keyrepeating shifted autoshift.
192 del_weak_mods(MOD_BIT(KC_LSFT));
193
81 switch (keycode) { 194 switch (keycode) {
82 case KC_ASTG: 195 case KC_ASTG:
83 autoshift_toggle(); 196 autoshift_toggle();
84 return true; 197 return true;
85
86 case KC_ASON: 198 case KC_ASON:
87 autoshift_enable(); 199 autoshift_enable();
88 return true; 200 return true;
@@ -102,41 +214,28 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
102 autoshift_timer_report(); 214 autoshift_timer_report();
103 return true; 215 return true;
104# endif 216# endif
217 }
218 }
219
220 switch (keycode) {
105# ifndef NO_AUTO_SHIFT_ALPHA 221# ifndef NO_AUTO_SHIFT_ALPHA
106 case KC_A ... KC_Z: 222 case KC_A ... KC_Z:
107# endif 223# endif
108# ifndef NO_AUTO_SHIFT_NUMERIC 224# ifndef NO_AUTO_SHIFT_NUMERIC
109 case KC_1 ... KC_0: 225 case KC_1 ... KC_0:
110# endif 226# endif
111# ifndef NO_AUTO_SHIFT_SPECIAL 227# ifndef NO_AUTO_SHIFT_SPECIAL
112 case KC_TAB: 228 case KC_TAB:
113 case KC_MINUS ... KC_SLASH: 229 case KC_MINUS ... KC_SLASH:
114 case KC_NONUS_BSLASH: 230 case KC_NONUS_BSLASH:
115# endif
116 autoshift_flush();
117 if (!autoshift_enabled) return true;
118
119# ifndef AUTO_SHIFT_MODIFIERS
120 if (get_mods()) {
121 return true;
122 }
123# endif
124 autoshift_on(keycode);
125
126 // We need some extra handling here for OSL edge cases
127# if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)
128 clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);
129# endif 231# endif
232 if (record->event.pressed) {
233 return autoshift_press(keycode, now, record);
234 } else {
235 autoshift_end(keycode, now, false);
130 return false; 236 return false;
131 237 }
132 default:
133 autoshift_flush();
134 return true;
135 }
136 } else {
137 autoshift_flush();
138 } 238 }
139
140 return true; 239 return true;
141} 240}
142 241
diff --git a/quantum/process_keycode/process_auto_shift.h b/quantum/process_keycode/process_auto_shift.h
index e86c4658e..5b2718f11 100644
--- a/quantum/process_keycode/process_auto_shift.h
+++ b/quantum/process_keycode/process_auto_shift.h
@@ -30,3 +30,4 @@ void autoshift_toggle(void);
30bool get_autoshift_state(void); 30bool get_autoshift_state(void);
31uint16_t get_autoshift_timeout(void); 31uint16_t get_autoshift_timeout(void);
32void set_autoshift_timeout(uint16_t timeout); 32void set_autoshift_timeout(uint16_t timeout);
33void autoshift_matrix_scan(void);
diff --git a/quantum/process_keycode/process_joystick.c b/quantum/process_keycode/process_joystick.c
index 5778a7434..3ffaf42bf 100644
--- a/quantum/process_keycode/process_joystick.c
+++ b/quantum/process_keycode/process_joystick.c
@@ -129,17 +129,17 @@ bool process_joystick_analogread_quantum() {
129 // test the converted value against the lower range 129 // test the converted value against the lower range
130 int32_t ref = joystick_axes[axis_index].mid_digit; 130 int32_t ref = joystick_axes[axis_index].mid_digit;
131 int32_t range = joystick_axes[axis_index].min_digit; 131 int32_t range = joystick_axes[axis_index].min_digit;
132 int32_t ranged_val = ((axis_val - ref) * -127) / (range - ref); 132 int32_t ranged_val = ((axis_val - ref) * -JOYSTICK_RESOLUTION) / (range - ref);
133 133
134 if (ranged_val > 0) { 134 if (ranged_val > 0) {
135 // the value is in the higher range 135 // the value is in the higher range
136 range = joystick_axes[axis_index].max_digit; 136 range = joystick_axes[axis_index].max_digit;
137 ranged_val = ((axis_val - ref) * 127) / (range - ref); 137 ranged_val = ((axis_val - ref) * JOYSTICK_RESOLUTION) / (range - ref);
138 } 138 }
139 139
140 // clamp the result in the valid range 140 // clamp the result in the valid range
141 ranged_val = ranged_val < -127 ? -127 : ranged_val; 141 ranged_val = ranged_val < -JOYSTICK_RESOLUTION ? -JOYSTICK_RESOLUTION : ranged_val;
142 ranged_val = ranged_val > 127 ? 127 : ranged_val; 142 ranged_val = ranged_val > JOYSTICK_RESOLUTION ? JOYSTICK_RESOLUTION : ranged_val;
143 143
144 if (ranged_val != joystick_status.axes[axis_index]) { 144 if (ranged_val != joystick_status.axes[axis_index]) {
145 joystick_status.axes[axis_index] = ranged_val; 145 joystick_status.axes[axis_index] = ranged_val;
diff --git a/quantum/process_keycode/process_midi.c b/quantum/process_keycode/process_midi.c
index e52577014..8e2fb955e 100644
--- a/quantum/process_keycode/process_midi.c
+++ b/quantum/process_keycode/process_midi.c
@@ -41,12 +41,12 @@ static int8_t midi_modulation_step;
41static uint16_t midi_modulation_timer; 41static uint16_t midi_modulation_timer;
42midi_config_t midi_config; 42midi_config_t midi_config;
43 43
44inline uint8_t compute_velocity(uint8_t setting) { return (setting + 1) * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN + 1)); } 44inline uint8_t compute_velocity(uint8_t setting) { return setting * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN)); }
45 45
46void midi_init(void) { 46void midi_init(void) {
47 midi_config.octave = MI_OCT_2 - MIDI_OCTAVE_MIN; 47 midi_config.octave = MI_OCT_2 - MIDI_OCTAVE_MIN;
48 midi_config.transpose = 0; 48 midi_config.transpose = 0;
49 midi_config.velocity = (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN); 49 midi_config.velocity = 127;
50 midi_config.channel = 0; 50 midi_config.channel = 0;
51 midi_config.modulation_interval = 8; 51 midi_config.modulation_interval = 8;
52 52
@@ -66,7 +66,7 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) {
66 case MIDI_TONE_MIN ... MIDI_TONE_MAX: { 66 case MIDI_TONE_MIN ... MIDI_TONE_MAX: {
67 uint8_t channel = midi_config.channel; 67 uint8_t channel = midi_config.channel;
68 uint8_t tone = keycode - MIDI_TONE_MIN; 68 uint8_t tone = keycode - MIDI_TONE_MIN;
69 uint8_t velocity = compute_velocity(midi_config.velocity); 69 uint8_t velocity = midi_config.velocity;
70 if (record->event.pressed) { 70 if (record->event.pressed) {
71 if (tone_status[tone] == MIDI_INVALID_NOTE) { 71 if (tone_status[tone] == MIDI_INVALID_NOTE) {
72 uint8_t note = midi_compute_note(keycode); 72 uint8_t note = midi_compute_note(keycode);
@@ -124,19 +124,30 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) {
124 return false; 124 return false;
125 case MIDI_VELOCITY_MIN ... MIDI_VELOCITY_MAX: 125 case MIDI_VELOCITY_MIN ... MIDI_VELOCITY_MAX:
126 if (record->event.pressed) { 126 if (record->event.pressed) {
127 midi_config.velocity = keycode - MIDI_VELOCITY_MIN; 127 midi_config.velocity = compute_velocity(keycode - MIDI_VELOCITY_MIN);
128 dprintf("midi velocity %d\n", midi_config.velocity); 128 dprintf("midi velocity %d\n", midi_config.velocity);
129 } 129 }
130 return false; 130 return false;
131 case MI_VELD: 131 case MI_VELD:
132 if (record->event.pressed && midi_config.velocity > 0) { 132 if (record->event.pressed && midi_config.velocity > 0) {
133 midi_config.velocity--; 133 if (midi_config.velocity == 127) {
134 midi_config.velocity -= 10;
135 } else if (midi_config.velocity > 12) {
136 midi_config.velocity -= 13;
137 } else {
138 midi_config.velocity = 0;
139 }
140
134 dprintf("midi velocity %d\n", midi_config.velocity); 141 dprintf("midi velocity %d\n", midi_config.velocity);
135 } 142 }
136 return false; 143 return false;
137 case MI_VELU: 144 case MI_VELU:
138 if (record->event.pressed) { 145 if (record->event.pressed && midi_config.velocity < 127) {
139 midi_config.velocity++; 146 if (midi_config.velocity < 115) {
147 midi_config.velocity += 13;
148 } else {
149 midi_config.velocity = 127;
150 }
140 dprintf("midi velocity %d\n", midi_config.velocity); 151 dprintf("midi velocity %d\n", midi_config.velocity);
141 } 152 }
142 return false; 153 return false;
diff --git a/quantum/process_keycode/process_midi.h b/quantum/process_keycode/process_midi.h
index 0007b3ed2..ef5661dd4 100644
--- a/quantum/process_keycode/process_midi.h
+++ b/quantum/process_keycode/process_midi.h
@@ -35,7 +35,7 @@ typedef union {
35 struct { 35 struct {
36 uint8_t octave : 4; 36 uint8_t octave : 4;
37 int8_t transpose : 4; 37 int8_t transpose : 4;
38 uint8_t velocity : 4; 38 uint8_t velocity : 7;
39 uint8_t channel : 4; 39 uint8_t channel : 4;
40 uint8_t modulation_interval : 4; 40 uint8_t modulation_interval : 4;
41 }; 41 };
diff --git a/quantum/process_keycode/process_sequencer.c b/quantum/process_keycode/process_sequencer.c
new file mode 100644
index 000000000..334b4c009
--- /dev/null
+++ b/quantum/process_keycode/process_sequencer.c
@@ -0,0 +1,62 @@
1/* Copyright 2020 Rodolphe Belouin
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "process_sequencer.h"
18
19bool process_sequencer(uint16_t keycode, keyrecord_t *record) {
20 if (record->event.pressed) {
21 switch (keycode) {
22 case SQ_ON:
23 sequencer_on();
24 return false;
25 case SQ_OFF:
26 sequencer_off();
27 return false;
28 case SQ_TOG:
29 sequencer_toggle();
30 return false;
31 case SQ_TMPD:
32 sequencer_decrease_tempo();
33 return false;
34 case SQ_TMPU:
35 sequencer_increase_tempo();
36 return false;
37 case SEQUENCER_RESOLUTION_MIN ... SEQUENCER_RESOLUTION_MAX:
38 sequencer_set_resolution(keycode - SEQUENCER_RESOLUTION_MIN);
39 return false;
40 case SQ_RESD:
41 sequencer_decrease_resolution();
42 return false;
43 case SQ_RESU:
44 sequencer_increase_resolution();
45 return false;
46 case SQ_SALL:
47 sequencer_set_all_steps_on();
48 return false;
49 case SQ_SCLR:
50 sequencer_set_all_steps_off();
51 return false;
52 case SEQUENCER_STEP_MIN ... SEQUENCER_STEP_MAX:
53 sequencer_toggle_step(keycode - SEQUENCER_STEP_MIN);
54 return false;
55 case SEQUENCER_TRACK_MIN ... SEQUENCER_TRACK_MAX:
56 sequencer_toggle_single_active_track(keycode - SEQUENCER_TRACK_MIN);
57 return false;
58 }
59 }
60
61 return true;
62}
diff --git a/quantum/process_keycode/process_sequencer.h b/quantum/process_keycode/process_sequencer.h
new file mode 100644
index 000000000..2b85f2429
--- /dev/null
+++ b/quantum/process_keycode/process_sequencer.h
@@ -0,0 +1,21 @@
1/* Copyright 2020 Rodolphe Belouin
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "quantum.h"
20
21bool process_sequencer(uint16_t keycode, keyrecord_t *record);
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 0b2f98762..3ac0ed871 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -58,6 +58,10 @@ float bell_song[][2] = SONG(TERMINAL_SOUND);
58# endif 58# endif
59#endif 59#endif
60 60
61#ifdef AUTO_SHIFT_ENABLE
62# include "process_auto_shift.h"
63#endif
64
61static void do_code16(uint16_t code, void (*f)(uint8_t)) { 65static void do_code16(uint16_t code, void (*f)(uint8_t)) {
62 switch (code) { 66 switch (code) {
63 case QK_MODS ... QK_MODS_MAX: 67 case QK_MODS ... QK_MODS_MAX:
@@ -228,6 +232,9 @@ bool process_record_quantum(keyrecord_t *record) {
228 process_record_via(keycode, record) && 232 process_record_via(keycode, record) &&
229#endif 233#endif
230 process_record_kb(keycode, record) && 234 process_record_kb(keycode, record) &&
235#if defined(SEQUENCER_ENABLE)
236 process_sequencer(keycode, record) &&
237#endif
231#if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED) 238#if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)
232 process_midi(keycode, record) && 239 process_midi(keycode, record) &&
233#endif 240#endif
@@ -636,6 +643,10 @@ void matrix_scan_quantum() {
636 matrix_scan_music(); 643 matrix_scan_music();
637#endif 644#endif
638 645
646#ifdef SEQUENCER_ENABLE
647 matrix_scan_sequencer();
648#endif
649
639#ifdef TAP_DANCE_ENABLE 650#ifdef TAP_DANCE_ENABLE
640 matrix_scan_tap_dance(); 651 matrix_scan_tap_dance();
641#endif 652#endif
@@ -664,6 +675,10 @@ void matrix_scan_quantum() {
664 dip_switch_read(false); 675 dip_switch_read(false);
665#endif 676#endif
666 677
678#ifdef AUTO_SHIFT_ENABLE
679 autoshift_matrix_scan();
680#endif
681
667 matrix_scan_kb(); 682 matrix_scan_kb();
668} 683}
669 684
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 0e452a062..cb0af306a 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -31,7 +31,7 @@
31 31
32#ifdef BACKLIGHT_ENABLE 32#ifdef BACKLIGHT_ENABLE
33# ifdef LED_MATRIX_ENABLE 33# ifdef LED_MATRIX_ENABLE
34# include "ledmatrix.h" 34# include "led_matrix.h"
35# else 35# else
36# include "backlight.h" 36# include "backlight.h"
37# endif 37# endif
@@ -68,6 +68,11 @@ extern layer_state_t default_layer_state;
68extern layer_state_t layer_state; 68extern layer_state_t layer_state;
69#endif 69#endif
70 70
71#if defined(SEQUENCER_ENABLE)
72# include "sequencer.h"
73# include "process_sequencer.h"
74#endif
75
71#if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED) 76#if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)
72# include "process_midi.h" 77# include "process_midi.h"
73#endif 78#endif
@@ -220,6 +225,61 @@ typedef ioline_t pin_t;
220# define togglePin(pin) palToggleLine(pin) 225# define togglePin(pin) palToggleLine(pin)
221#endif 226#endif
222 227
228// Atomic macro to help make GPIO and other controls atomic.
229#ifdef IGNORE_ATOMIC_BLOCK
230/* do nothing atomic macro */
231# define ATOMIC_BLOCK for (uint8_t __ToDo = 1; __ToDo; __ToDo = 0)
232# define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK
233# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK
234
235#elif defined(__AVR__)
236/* atomic macro for AVR */
237# include <util/atomic.h>
238
239# define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
240# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON)
241
242#elif defined(PROTOCOL_CHIBIOS) || defined(PROTOCOL_ARM_ATSAM)
243/* atomic macro for ChibiOS / ARM ATSAM */
244# if defined(PROTOCOL_ARM_ATSAM)
245# include "arm_atsam_protocol.h"
246# endif
247
248static __inline__ uint8_t __interrupt_disable__(void) {
249# if defined(PROTOCOL_CHIBIOS)
250 chSysLock();
251# endif
252# if defined(PROTOCOL_ARM_ATSAM)
253 __disable_irq();
254# endif
255 return 1;
256}
257
258static __inline__ void __interrupt_enable__(const uint8_t *__s) {
259# if defined(PROTOCOL_CHIBIOS)
260 chSysUnlock();
261# endif
262# if defined(PROTOCOL_ARM_ATSAM)
263 __enable_irq();
264# endif
265 __asm__ volatile("" ::: "memory");
266 (void)__s;
267}
268
269# define ATOMIC_BLOCK(type) for (type, __ToDo = __interrupt_disable__(); __ToDo; __ToDo = 0)
270# define ATOMIC_FORCEON uint8_t sreg_save __attribute__((__cleanup__(__interrupt_enable__))) = 0
271
272# define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE dose not implement")
273# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON)
274
275/* Other platform */
276#else
277
278# define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE dose not implement")
279# define ATOMIC_BLOCK_FORCEON _Static_assert(0, "ATOMIC_BLOCK_FORCEON dose not implement")
280
281#endif
282
223#define SEND_STRING(string) send_string_P(PSTR(string)) 283#define SEND_STRING(string) send_string_P(PSTR(string))
224#define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval) 284#define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval)
225 285
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index a0a7bc340..a2cc7b38d 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -16,6 +16,10 @@
16#ifndef QUANTUM_KEYCODES_H 16#ifndef QUANTUM_KEYCODES_H
17#define QUANTUM_KEYCODES_H 17#define QUANTUM_KEYCODES_H
18 18
19#if defined(SEQUENCER_ENABLE)
20# include "sequencer.h"
21#endif
22
19#ifndef MIDI_ENABLE_STRICT 23#ifndef MIDI_ENABLE_STRICT
20# define MIDI_ENABLE_STRICT 0 24# define MIDI_ENABLE_STRICT 0
21#endif 25#endif
@@ -343,7 +347,8 @@ enum quantum_keycodes {
343 MI_TRNSU, // transpose up 347 MI_TRNSU, // transpose up
344 348
345 MIDI_VELOCITY_MIN, 349 MIDI_VELOCITY_MIN,
346 MI_VEL_1 = MIDI_VELOCITY_MIN, 350 MI_VEL_0 = MIDI_VELOCITY_MIN,
351 MI_VEL_1,
347 MI_VEL_2, 352 MI_VEL_2,
348 MI_VEL_3, 353 MI_VEL_3,
349 MI_VEL_4, 354 MI_VEL_4,
@@ -549,6 +554,37 @@ enum quantum_keycodes {
549 JS_BUTTON31, 554 JS_BUTTON31,
550 JS_BUTTON_MAX = JS_BUTTON31, 555 JS_BUTTON_MAX = JS_BUTTON31,
551 556
557#if defined(SEQUENCER_ENABLE)
558 SQ_ON,
559 SQ_OFF,
560 SQ_TOG,
561
562 SQ_TMPD, // Decrease tempo
563 SQ_TMPU, // Increase tempo
564
565 SEQUENCER_RESOLUTION_MIN,
566 SEQUENCER_RESOLUTION_MAX = SEQUENCER_RESOLUTION_MIN + SEQUENCER_RESOLUTIONS,
567 SQ_RESD, // Decrease resolution
568 SQ_RESU, // Increase resolution
569
570 SQ_SALL, // All steps on
571 SQ_SCLR, // All steps off
572 SEQUENCER_STEP_MIN,
573 SEQUENCER_STEP_MAX = SEQUENCER_STEP_MIN + SEQUENCER_STEPS,
574
575 SEQUENCER_TRACK_MIN,
576 SEQUENCER_TRACK_MAX = SEQUENCER_TRACK_MIN + SEQUENCER_TRACKS,
577
578/**
579 * Helpers to assign a keycode to a step, a resolution, or a track.
580 * Falls back to NOOP if n is out of range.
581 */
582# define SQ_S(n) (n < SEQUENCER_STEPS ? SEQUENCER_STEP_MIN + n : XXXXXXX)
583# define SQ_R(n) (n < SEQUENCER_RESOLUTIONS ? SEQUENCER_RESOLUTION_MIN + n : XXXXXXX)
584# define SQ_T(n) (n < SEQUENCER_TRACKS ? SEQUENCER_TRACK_MIN + n : XXXXXXX)
585
586#endif
587
552 // always leave at the end 588 // always leave at the end
553 SAFE_RANGE 589 SAFE_RANGE
554}; 590};
diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c
index 802c5afce..f239bd582 100644
--- a/quantum/rgb_matrix.c
+++ b/quantum/rgb_matrix.c
@@ -31,6 +31,8 @@ const point_t k_rgb_matrix_center = {112, 32};
31const point_t k_rgb_matrix_center = RGB_MATRIX_CENTER; 31const point_t k_rgb_matrix_center = RGB_MATRIX_CENTER;
32#endif 32#endif
33 33
34__attribute__((weak)) RGB rgb_matrix_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); }
35
34// Generic effect runners 36// Generic effect runners
35#include "rgb_matrix_runners/effect_runner_dx_dy_dist.h" 37#include "rgb_matrix_runners/effect_runner_dx_dy_dist.h"
36#include "rgb_matrix_runners/effect_runner_dx_dy.h" 38#include "rgb_matrix_runners/effect_runner_dx_dy.h"
@@ -401,6 +403,10 @@ void rgb_matrix_task(void) {
401 break; 403 break;
402 case RENDERING: 404 case RENDERING:
403 rgb_task_render(effect); 405 rgb_task_render(effect);
406 if (!suspend_backlight) {
407 rgb_matrix_indicators();
408 rgb_matrix_indicators_advanced(&rgb_effect_params);
409 }
404 break; 410 break;
405 case FLUSHING: 411 case FLUSHING:
406 rgb_task_flush(effect); 412 rgb_task_flush(effect);
@@ -409,10 +415,6 @@ void rgb_matrix_task(void) {
409 rgb_task_sync(); 415 rgb_task_sync();
410 break; 416 break;
411 } 417 }
412
413 if (!suspend_backlight) {
414 rgb_matrix_indicators();
415 }
416} 418}
417 419
418void rgb_matrix_indicators(void) { 420void rgb_matrix_indicators(void) {
@@ -424,6 +426,28 @@ __attribute__((weak)) void rgb_matrix_indicators_kb(void) {}
424 426
425__attribute__((weak)) void rgb_matrix_indicators_user(void) {} 427__attribute__((weak)) void rgb_matrix_indicators_user(void) {}
426 428
429void rgb_matrix_indicators_advanced(effect_params_t *params) {
430 /* special handling is needed for "params->iter", since it's already been incremented.
431 * Could move the invocations to rgb_task_render, but then it's missing a few checks
432 * and not sure which would be better. Otherwise, this should be called from
433 * rgb_task_render, right before the iter++ line.
434 */
435#if defined(RGB_MATRIX_LED_PROCESS_LIMIT) && RGB_MATRIX_LED_PROCESS_LIMIT > 0 && RGB_MATRIX_LED_PROCESS_LIMIT < DRIVER_LED_TOTAL
436 uint8_t min = RGB_MATRIX_LED_PROCESS_LIMIT * (params->iter - 1);
437 uint8_t max = min + RGB_MATRIX_LED_PROCESS_LIMIT;
438 if (max > DRIVER_LED_TOTAL) max = DRIVER_LED_TOTAL;
439#else
440 uint8_t min = 0;
441 uint8_t max = DRIVER_LED_TOTAL;
442#endif
443 rgb_matrix_indicators_advanced_kb(min, max);
444 rgb_matrix_indicators_advanced_user(min, max);
445}
446
447__attribute__((weak)) void rgb_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max) {}
448
449__attribute__((weak)) void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {}
450
427void rgb_matrix_init(void) { 451void rgb_matrix_init(void) {
428 rgb_matrix_driver.init(); 452 rgb_matrix_driver.init();
429 453
diff --git a/quantum/rgb_matrix.h b/quantum/rgb_matrix.h
index 733333349..771a1fcd3 100644
--- a/quantum/rgb_matrix.h
+++ b/quantum/rgb_matrix.h
@@ -57,6 +57,11 @@
57 uint8_t max = DRIVER_LED_TOTAL; 57 uint8_t max = DRIVER_LED_TOTAL;
58#endif 58#endif
59 59
60#define RGB_MATRIX_INDICATOR_SET_COLOR(i, r, g, b) \
61 if (i >= led_min && i <= led_max) { \
62 rgb_matrix_set_color(i, r, g, b); \
63 }
64
60#define RGB_MATRIX_TEST_LED_FLAGS() \ 65#define RGB_MATRIX_TEST_LED_FLAGS() \
61 if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) continue 66 if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) continue
62 67
@@ -103,6 +108,10 @@ void rgb_matrix_indicators(void);
103void rgb_matrix_indicators_kb(void); 108void rgb_matrix_indicators_kb(void);
104void rgb_matrix_indicators_user(void); 109void rgb_matrix_indicators_user(void);
105 110
111void rgb_matrix_indicators_advanced(effect_params_t *params);
112void rgb_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max);
113void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max);
114
106void rgb_matrix_init(void); 115void rgb_matrix_init(void);
107 116
108void rgb_matrix_set_suspend_state(bool state); 117void rgb_matrix_set_suspend_state(bool state);
diff --git a/quantum/rgb_matrix_animations/alpha_mods_anim.h b/quantum/rgb_matrix_animations/alpha_mods_anim.h
index 0778ab209..426d88ef3 100644
--- a/quantum/rgb_matrix_animations/alpha_mods_anim.h
+++ b/quantum/rgb_matrix_animations/alpha_mods_anim.h
@@ -7,9 +7,9 @@ bool ALPHAS_MODS(effect_params_t* params) {
7 RGB_MATRIX_USE_LIMITS(led_min, led_max); 7 RGB_MATRIX_USE_LIMITS(led_min, led_max);
8 8
9 HSV hsv = rgb_matrix_config.hsv; 9 HSV hsv = rgb_matrix_config.hsv;
10 RGB rgb1 = hsv_to_rgb(hsv); 10 RGB rgb1 = rgb_matrix_hsv_to_rgb(hsv);
11 hsv.h += rgb_matrix_config.speed; 11 hsv.h += rgb_matrix_config.speed;
12 RGB rgb2 = hsv_to_rgb(hsv); 12 RGB rgb2 = rgb_matrix_hsv_to_rgb(hsv);
13 13
14 for (uint8_t i = led_min; i < led_max; i++) { 14 for (uint8_t i = led_min; i < led_max; i++) {
15 RGB_MATRIX_TEST_LED_FLAGS(); 15 RGB_MATRIX_TEST_LED_FLAGS();
diff --git a/quantum/rgb_matrix_animations/breathing_anim.h b/quantum/rgb_matrix_animations/breathing_anim.h
index 887425f9d..340bd93e5 100644
--- a/quantum/rgb_matrix_animations/breathing_anim.h
+++ b/quantum/rgb_matrix_animations/breathing_anim.h
@@ -8,7 +8,7 @@ bool BREATHING(effect_params_t* params) {
8 HSV hsv = rgb_matrix_config.hsv; 8 HSV hsv = rgb_matrix_config.hsv;
9 uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8); 9 uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8);
10 hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v); 10 hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v);
11 RGB rgb = hsv_to_rgb(hsv); 11 RGB rgb = rgb_matrix_hsv_to_rgb(hsv);
12 for (uint8_t i = led_min; i < led_max; i++) { 12 for (uint8_t i = led_min; i < led_max; i++) {
13 RGB_MATRIX_TEST_LED_FLAGS(); 13 RGB_MATRIX_TEST_LED_FLAGS();
14 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 14 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
diff --git a/quantum/rgb_matrix_animations/gradient_left_right_anim.h b/quantum/rgb_matrix_animations/gradient_left_right_anim.h
index 2eab2eb75..53dfd04e2 100644
--- a/quantum/rgb_matrix_animations/gradient_left_right_anim.h
+++ b/quantum/rgb_matrix_animations/gradient_left_right_anim.h
@@ -12,7 +12,7 @@ bool GRADIENT_LEFT_RIGHT(effect_params_t* params) {
12 // The x range will be 0..224, map this to 0..7 12 // The x range will be 0..224, map this to 0..7
13 // Relies on hue being 8-bit and wrapping 13 // Relies on hue being 8-bit and wrapping
14 hsv.h = rgb_matrix_config.hsv.h + (scale * g_led_config.point[i].x >> 5); 14 hsv.h = rgb_matrix_config.hsv.h + (scale * g_led_config.point[i].x >> 5);
15 RGB rgb = hsv_to_rgb(hsv); 15 RGB rgb = rgb_matrix_hsv_to_rgb(hsv);
16 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 16 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
17 } 17 }
18 return led_max < DRIVER_LED_TOTAL; 18 return led_max < DRIVER_LED_TOTAL;
diff --git a/quantum/rgb_matrix_animations/gradient_up_down_anim.h b/quantum/rgb_matrix_animations/gradient_up_down_anim.h
index 0f1f8e23c..7e0d2898c 100644
--- a/quantum/rgb_matrix_animations/gradient_up_down_anim.h
+++ b/quantum/rgb_matrix_animations/gradient_up_down_anim.h
@@ -12,7 +12,7 @@ bool GRADIENT_UP_DOWN(effect_params_t* params) {
12 // The y range will be 0..64, map this to 0..4 12 // The y range will be 0..64, map this to 0..4
13 // Relies on hue being 8-bit and wrapping 13 // Relies on hue being 8-bit and wrapping
14 hsv.h = rgb_matrix_config.hsv.h + scale * (g_led_config.point[i].y >> 4); 14 hsv.h = rgb_matrix_config.hsv.h + scale * (g_led_config.point[i].y >> 4);
15 RGB rgb = hsv_to_rgb(hsv); 15 RGB rgb = rgb_matrix_hsv_to_rgb(hsv);
16 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 16 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
17 } 17 }
18 return led_max < DRIVER_LED_TOTAL; 18 return led_max < DRIVER_LED_TOTAL;
diff --git a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h b/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h
index ef2d1500b..9493b3850 100644
--- a/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h
+++ b/quantum/rgb_matrix_animations/jellybean_raindrops_anim.h
@@ -5,7 +5,7 @@ RGB_MATRIX_EFFECT(JELLYBEAN_RAINDROPS)
5static void jellybean_raindrops_set_color(int i, effect_params_t* params) { 5static void jellybean_raindrops_set_color(int i, effect_params_t* params) {
6 if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return; 6 if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return;
7 HSV hsv = {rand() & 0xFF, rand() & 0xFF, rgb_matrix_config.hsv.v}; 7 HSV hsv = {rand() & 0xFF, rand() & 0xFF, rgb_matrix_config.hsv.v};
8 RGB rgb = hsv_to_rgb(hsv); 8 RGB rgb = rgb_matrix_hsv_to_rgb(hsv);
9 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 9 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
10} 10}
11 11
diff --git a/quantum/rgb_matrix_animations/raindrops_anim.h b/quantum/rgb_matrix_animations/raindrops_anim.h
index 6e1b5acb0..38359cdca 100644
--- a/quantum/rgb_matrix_animations/raindrops_anim.h
+++ b/quantum/rgb_matrix_animations/raindrops_anim.h
@@ -15,7 +15,7 @@ static void raindrops_set_color(int i, effect_params_t* params) {
15 } 15 }
16 16
17 hsv.h = rgb_matrix_config.hsv.h + (deltaH * (rand() & 0x03)); 17 hsv.h = rgb_matrix_config.hsv.h + (deltaH * (rand() & 0x03));
18 RGB rgb = hsv_to_rgb(hsv); 18 RGB rgb = rgb_matrix_hsv_to_rgb(hsv);
19 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 19 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
20} 20}
21 21
diff --git a/quantum/rgb_matrix_animations/solid_color_anim.h b/quantum/rgb_matrix_animations/solid_color_anim.h
index c8f5e70e7..79d63cf13 100644
--- a/quantum/rgb_matrix_animations/solid_color_anim.h
+++ b/quantum/rgb_matrix_animations/solid_color_anim.h
@@ -4,7 +4,7 @@ RGB_MATRIX_EFFECT(SOLID_COLOR)
4bool SOLID_COLOR(effect_params_t* params) { 4bool SOLID_COLOR(effect_params_t* params) {
5 RGB_MATRIX_USE_LIMITS(led_min, led_max); 5 RGB_MATRIX_USE_LIMITS(led_min, led_max);
6 6
7 RGB rgb = hsv_to_rgb(rgb_matrix_config.hsv); 7 RGB rgb = rgb_matrix_hsv_to_rgb(rgb_matrix_config.hsv);
8 for (uint8_t i = led_min; i < led_max; i++) { 8 for (uint8_t i = led_min; i < led_max; i++) {
9 RGB_MATRIX_TEST_LED_FLAGS(); 9 RGB_MATRIX_TEST_LED_FLAGS();
10 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 10 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
diff --git a/quantum/rgb_matrix_animations/typing_heatmap_anim.h b/quantum/rgb_matrix_animations/typing_heatmap_anim.h
index e82c1b49e..b855fdc19 100644
--- a/quantum/rgb_matrix_animations/typing_heatmap_anim.h
+++ b/quantum/rgb_matrix_animations/typing_heatmap_anim.h
@@ -51,7 +51,7 @@ bool TYPING_HEATMAP(effect_params_t* params) {
51 if (!HAS_ANY_FLAGS(g_led_config.flags[led[j]], params->flags)) continue; 51 if (!HAS_ANY_FLAGS(g_led_config.flags[led[j]], params->flags)) continue;
52 52
53 HSV hsv = {170 - qsub8(val, 85), rgb_matrix_config.hsv.s, scale8((qadd8(170, val) - 170) * 3, rgb_matrix_config.hsv.v)}; 53 HSV hsv = {170 - qsub8(val, 85), rgb_matrix_config.hsv.s, scale8((qadd8(170, val) - 170) * 3, rgb_matrix_config.hsv.v)};
54 RGB rgb = hsv_to_rgb(hsv); 54 RGB rgb = rgb_matrix_hsv_to_rgb(hsv);
55 rgb_matrix_set_color(led[j], rgb.r, rgb.g, rgb.b); 55 rgb_matrix_set_color(led[j], rgb.r, rgb.g, rgb.b);
56 } 56 }
57 57
diff --git a/quantum/rgb_matrix_runners/effect_runner_dx_dy.h b/quantum/rgb_matrix_runners/effect_runner_dx_dy.h
index 9d0c9fab1..4867609c8 100644
--- a/quantum/rgb_matrix_runners/effect_runner_dx_dy.h
+++ b/quantum/rgb_matrix_runners/effect_runner_dx_dy.h
@@ -10,7 +10,7 @@ bool effect_runner_dx_dy(effect_params_t* params, dx_dy_f effect_func) {
10 RGB_MATRIX_TEST_LED_FLAGS(); 10 RGB_MATRIX_TEST_LED_FLAGS();
11 int16_t dx = g_led_config.point[i].x - k_rgb_matrix_center.x; 11 int16_t dx = g_led_config.point[i].x - k_rgb_matrix_center.x;
12 int16_t dy = g_led_config.point[i].y - k_rgb_matrix_center.y; 12 int16_t dy = g_led_config.point[i].y - k_rgb_matrix_center.y;
13 RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, time)); 13 RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, time));
14 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 14 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
15 } 15 }
16 return led_max < DRIVER_LED_TOTAL; 16 return led_max < DRIVER_LED_TOTAL;
diff --git a/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h b/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h
index 2824c8252..9545b418d 100644
--- a/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h
+++ b/quantum/rgb_matrix_runners/effect_runner_dx_dy_dist.h
@@ -11,7 +11,7 @@ bool effect_runner_dx_dy_dist(effect_params_t* params, dx_dy_dist_f effect_func)
11 int16_t dx = g_led_config.point[i].x - k_rgb_matrix_center.x; 11 int16_t dx = g_led_config.point[i].x - k_rgb_matrix_center.x;
12 int16_t dy = g_led_config.point[i].y - k_rgb_matrix_center.y; 12 int16_t dy = g_led_config.point[i].y - k_rgb_matrix_center.y;
13 uint8_t dist = sqrt16(dx * dx + dy * dy); 13 uint8_t dist = sqrt16(dx * dx + dy * dy);
14 RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, dist, time)); 14 RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, dist, time));
15 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 15 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
16 } 16 }
17 return led_max < DRIVER_LED_TOTAL; 17 return led_max < DRIVER_LED_TOTAL;
diff --git a/quantum/rgb_matrix_runners/effect_runner_i.h b/quantum/rgb_matrix_runners/effect_runner_i.h
index 5e6bf5daa..95bfe8b39 100644
--- a/quantum/rgb_matrix_runners/effect_runner_i.h
+++ b/quantum/rgb_matrix_runners/effect_runner_i.h
@@ -8,7 +8,7 @@ bool effect_runner_i(effect_params_t* params, i_f effect_func) {
8 uint8_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 4); 8 uint8_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 4);
9 for (uint8_t i = led_min; i < led_max; i++) { 9 for (uint8_t i = led_min; i < led_max; i++) {
10 RGB_MATRIX_TEST_LED_FLAGS(); 10 RGB_MATRIX_TEST_LED_FLAGS();
11 RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time)); 11 RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time));
12 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 12 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
13 } 13 }
14 return led_max < DRIVER_LED_TOTAL; 14 return led_max < DRIVER_LED_TOTAL;
diff --git a/quantum/rgb_matrix_runners/effect_runner_reactive.h b/quantum/rgb_matrix_runners/effect_runner_reactive.h
index 53e77e3fb..8485b61f3 100644
--- a/quantum/rgb_matrix_runners/effect_runner_reactive.h
+++ b/quantum/rgb_matrix_runners/effect_runner_reactive.h
@@ -20,7 +20,7 @@ bool effect_runner_reactive(effect_params_t* params, reactive_f effect_func) {
20 } 20 }
21 21
22 uint16_t offset = scale16by8(tick, rgb_matrix_config.speed); 22 uint16_t offset = scale16by8(tick, rgb_matrix_config.speed);
23 RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, offset)); 23 RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, offset));
24 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 24 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
25 } 25 }
26 return led_max < DRIVER_LED_TOTAL; 26 return led_max < DRIVER_LED_TOTAL;
diff --git a/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h b/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h
index b5d284a40..5c69d0fbb 100644
--- a/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h
+++ b/quantum/rgb_matrix_runners/effect_runner_reactive_splash.h
@@ -20,7 +20,7 @@ bool effect_runner_reactive_splash(uint8_t start, effect_params_t* params, react
20 hsv = effect_func(hsv, dx, dy, dist, tick); 20 hsv = effect_func(hsv, dx, dy, dist, tick);
21 } 21 }
22 hsv.v = scale8(hsv.v, rgb_matrix_config.hsv.v); 22 hsv.v = scale8(hsv.v, rgb_matrix_config.hsv.v);
23 RGB rgb = hsv_to_rgb(hsv); 23 RGB rgb = rgb_matrix_hsv_to_rgb(hsv);
24 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 24 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
25 } 25 }
26 return led_max < DRIVER_LED_TOTAL; 26 return led_max < DRIVER_LED_TOTAL;
diff --git a/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h b/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h
index 3fb7d4805..02351de51 100644
--- a/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h
+++ b/quantum/rgb_matrix_runners/effect_runner_sin_cos_i.h
@@ -10,7 +10,7 @@ bool effect_runner_sin_cos_i(effect_params_t* params, sin_cos_i_f effect_func) {
10 int8_t sin_value = sin8(time) - 128; 10 int8_t sin_value = sin8(time) - 128;
11 for (uint8_t i = led_min; i < led_max; i++) { 11 for (uint8_t i = led_min; i < led_max; i++) {
12 RGB_MATRIX_TEST_LED_FLAGS(); 12 RGB_MATRIX_TEST_LED_FLAGS();
13 RGB rgb = hsv_to_rgb(effect_func(rgb_matrix_config.hsv, cos_value, sin_value, i, time)); 13 RGB rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, cos_value, sin_value, i, time));
14 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b); 14 rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
15 } 15 }
16 return led_max < DRIVER_LED_TOTAL; 16 return led_max < DRIVER_LED_TOTAL;
diff --git a/quantum/rgblight.c b/quantum/rgblight.c
index 76bb6eb8c..7f9e330d3 100644
--- a/quantum/rgblight.c
+++ b/quantum/rgblight.c
@@ -123,9 +123,11 @@ void rgblight_set_effect_range(uint8_t start_pos, uint8_t num_leds) {
123 rgblight_ranges.effect_num_leds = num_leds; 123 rgblight_ranges.effect_num_leds = num_leds;
124} 124}
125 125
126__attribute__((weak)) RGB rgblight_hsv_to_rgb(HSV hsv) { return hsv_to_rgb(hsv); }
127
126void sethsv_raw(uint8_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) { 128void sethsv_raw(uint8_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) {
127 HSV hsv = {hue, sat, val}; 129 HSV hsv = {hue, sat, val};
128 RGB rgb = hsv_to_rgb(hsv); 130 RGB rgb = rgblight_hsv_to_rgb(hsv);
129 setrgb(rgb.r, rgb.g, rgb.b, led1); 131 setrgb(rgb.r, rgb.g, rgb.b, led1);
130} 132}
131 133
diff --git a/quantum/sequencer/sequencer.c b/quantum/sequencer/sequencer.c
new file mode 100644
index 000000000..0eaf3a17a
--- /dev/null
+++ b/quantum/sequencer/sequencer.c
@@ -0,0 +1,275 @@
1/* Copyright 2020 Rodolphe Belouin
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "sequencer.h"
18
19#ifdef MIDI_ENABLE
20# include "process_midi.h"
21#endif
22
23#ifdef MIDI_MOCKED
24# include "tests/midi_mock.h"
25#endif
26
27sequencer_config_t sequencer_config = {
28 false, // enabled
29 {false}, // steps
30 {0}, // track notes
31 60, // tempo
32 SQ_RES_4, // resolution
33};
34
35sequencer_state_t sequencer_internal_state = {0, 0, 0, 0, SEQUENCER_PHASE_ATTACK};
36
37bool is_sequencer_on(void) { return sequencer_config.enabled; }
38
39void sequencer_on(void) {
40 dprintln("sequencer on");
41 sequencer_config.enabled = true;
42 sequencer_internal_state.current_track = 0;
43 sequencer_internal_state.current_step = 0;
44 sequencer_internal_state.timer = timer_read();
45 sequencer_internal_state.phase = SEQUENCER_PHASE_ATTACK;
46}
47
48void sequencer_off(void) {
49 dprintln("sequencer off");
50 sequencer_config.enabled = false;
51 sequencer_internal_state.current_step = 0;
52}
53
54void sequencer_toggle(void) {
55 if (is_sequencer_on()) {
56 sequencer_off();
57 } else {
58 sequencer_on();
59 }
60}
61
62void sequencer_set_track_notes(const uint16_t track_notes[SEQUENCER_TRACKS]) {
63 for (uint8_t i = 0; i < SEQUENCER_TRACKS; i++) {
64 sequencer_config.track_notes[i] = track_notes[i];
65 }
66}
67
68bool is_sequencer_track_active(uint8_t track) { return (sequencer_internal_state.active_tracks >> track) & true; }
69
70void sequencer_set_track_activation(uint8_t track, bool value) {
71 if (value) {
72 sequencer_internal_state.active_tracks |= (1 << track);
73 } else {
74 sequencer_internal_state.active_tracks &= ~(1 << track);
75 }
76 dprintf("sequencer: track %d is %s\n", track, value ? "active" : "inactive");
77}
78
79void sequencer_toggle_track_activation(uint8_t track) { sequencer_set_track_activation(track, !is_sequencer_track_active(track)); }
80
81void sequencer_toggle_single_active_track(uint8_t track) {
82 if (is_sequencer_track_active(track)) {
83 sequencer_internal_state.active_tracks = 0;
84 } else {
85 sequencer_internal_state.active_tracks = 1 << track;
86 }
87}
88
89bool is_sequencer_step_on(uint8_t step) { return step < SEQUENCER_STEPS && (sequencer_config.steps[step] & sequencer_internal_state.active_tracks) > 0; }
90
91bool is_sequencer_step_on_for_track(uint8_t step, uint8_t track) { return step < SEQUENCER_STEPS && (sequencer_config.steps[step] >> track) & true; }
92
93void sequencer_set_step(uint8_t step, bool value) {
94 if (step < SEQUENCER_STEPS) {
95 if (value) {
96 sequencer_config.steps[step] |= sequencer_internal_state.active_tracks;
97 } else {
98 sequencer_config.steps[step] &= ~sequencer_internal_state.active_tracks;
99 }
100 dprintf("sequencer: step %d is %s\n", step, value ? "on" : "off");
101 } else {
102 dprintf("sequencer: step %d is out of range\n", step);
103 }
104}
105
106void sequencer_toggle_step(uint8_t step) {
107 if (is_sequencer_step_on(step)) {
108 sequencer_set_step_off(step);
109 } else {
110 sequencer_set_step_on(step);
111 }
112}
113
114void sequencer_set_all_steps(bool value) {
115 for (uint8_t step = 0; step < SEQUENCER_STEPS; step++) {
116 if (value) {
117 sequencer_config.steps[step] |= sequencer_internal_state.active_tracks;
118 } else {
119 sequencer_config.steps[step] &= ~sequencer_internal_state.active_tracks;
120 }
121 }
122 dprintf("sequencer: all steps are %s\n", value ? "on" : "off");
123}
124
125uint8_t sequencer_get_tempo(void) { return sequencer_config.tempo; }
126
127void sequencer_set_tempo(uint8_t tempo) {
128 if (tempo > 0) {
129 sequencer_config.tempo = tempo;
130 dprintf("sequencer: tempo set to %d bpm\n", tempo);
131 } else {
132 dprintln("sequencer: cannot set tempo to 0");
133 }
134}
135
136void sequencer_increase_tempo(void) {
137 // Handling potential uint8_t overflow
138 if (sequencer_config.tempo < UINT8_MAX) {
139 sequencer_set_tempo(sequencer_config.tempo + 1);
140 } else {
141 dprintf("sequencer: cannot set tempo above %d\n", UINT8_MAX);
142 }
143}
144
145void sequencer_decrease_tempo(void) { sequencer_set_tempo(sequencer_config.tempo - 1); }
146
147sequencer_resolution_t sequencer_get_resolution(void) { return sequencer_config.resolution; }
148
149void sequencer_set_resolution(sequencer_resolution_t resolution) {
150 if (resolution >= 0 && resolution < SEQUENCER_RESOLUTIONS) {
151 sequencer_config.resolution = resolution;
152 dprintf("sequencer: resolution set to %d\n", resolution);
153 } else {
154 dprintf("sequencer: resolution %d is out of range\n", resolution);
155 }
156}
157
158void sequencer_increase_resolution(void) { sequencer_set_resolution(sequencer_config.resolution + 1); }
159
160void sequencer_decrease_resolution(void) { sequencer_set_resolution(sequencer_config.resolution - 1); }
161
162uint8_t sequencer_get_current_step(void) { return sequencer_internal_state.current_step; }
163
164void sequencer_phase_attack(void) {
165 dprintf("sequencer: step %d\n", sequencer_internal_state.current_step);
166 dprintf("sequencer: time %d\n", timer_read());
167
168 if (sequencer_internal_state.current_track == 0) {
169 sequencer_internal_state.timer = timer_read();
170 }
171
172 if (timer_elapsed(sequencer_internal_state.timer) < sequencer_internal_state.current_track * SEQUENCER_TRACK_THROTTLE) {
173 return;
174 }
175
176#if defined(MIDI_ENABLE) || defined(MIDI_MOCKED)
177 if (is_sequencer_step_on_for_track(sequencer_internal_state.current_step, sequencer_internal_state.current_track)) {
178 process_midi_basic_noteon(midi_compute_note(sequencer_config.track_notes[sequencer_internal_state.current_track]));
179 }
180#endif
181
182 if (sequencer_internal_state.current_track < SEQUENCER_TRACKS - 1) {
183 sequencer_internal_state.current_track++;
184 } else {
185 sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE;
186 }
187}
188
189void sequencer_phase_release(void) {
190 if (timer_elapsed(sequencer_internal_state.timer) < SEQUENCER_PHASE_RELEASE_TIMEOUT + sequencer_internal_state.current_track * SEQUENCER_TRACK_THROTTLE) {
191 return;
192 }
193#if defined(MIDI_ENABLE) || defined(MIDI_MOCKED)
194 if (is_sequencer_step_on_for_track(sequencer_internal_state.current_step, sequencer_internal_state.current_track)) {
195 process_midi_basic_noteoff(midi_compute_note(sequencer_config.track_notes[sequencer_internal_state.current_track]));
196 }
197#endif
198 if (sequencer_internal_state.current_track > 0) {
199 sequencer_internal_state.current_track--;
200 } else {
201 sequencer_internal_state.phase = SEQUENCER_PHASE_PAUSE;
202 }
203}
204
205void sequencer_phase_pause(void) {
206 if (timer_elapsed(sequencer_internal_state.timer) < sequencer_get_step_duration()) {
207 return;
208 }
209
210 sequencer_internal_state.current_step = (sequencer_internal_state.current_step + 1) % SEQUENCER_STEPS;
211 sequencer_internal_state.phase = SEQUENCER_PHASE_ATTACK;
212}
213
214void matrix_scan_sequencer(void) {
215 if (!sequencer_config.enabled) {
216 return;
217 }
218
219 if (sequencer_internal_state.phase == SEQUENCER_PHASE_PAUSE) {
220 sequencer_phase_pause();
221 }
222
223 if (sequencer_internal_state.phase == SEQUENCER_PHASE_RELEASE) {
224 sequencer_phase_release();
225 }
226
227 if (sequencer_internal_state.phase == SEQUENCER_PHASE_ATTACK) {
228 sequencer_phase_attack();
229 }
230}
231
232uint16_t sequencer_get_beat_duration(void) { return get_beat_duration(sequencer_config.tempo); }
233
234uint16_t sequencer_get_step_duration(void) { return get_step_duration(sequencer_config.tempo, sequencer_config.resolution); }
235
236uint16_t get_beat_duration(uint8_t tempo) {
237 // Don’t crash in the unlikely case where the given tempo is 0
238 if (tempo == 0) {
239 return get_beat_duration(60);
240 }
241
242 /**
243 * Given
244 * t = tempo and d = duration, both strictly greater than 0
245 * When
246 * t beats / minute = 1 beat / d ms
247 * Then
248 * t beats / 60000ms = 1 beat / d ms
249 * d ms = 60000ms / t
250 */
251 return 60000 / tempo;
252}
253
254uint16_t get_step_duration(uint8_t tempo, sequencer_resolution_t resolution) {
255 /**
256 * Resolution cheatsheet:
257 * 1/2 => 2 steps per 4 beats
258 * 1/2T => 3 steps per 4 beats
259 * 1/4 => 4 steps per 4 beats
260 * 1/4T => 6 steps per 4 beats
261 * 1/8 => 8 steps per 4 beats
262 * 1/8T => 12 steps per 4 beats
263 * 1/16 => 16 steps per 4 beats
264 * 1/16T => 24 steps per 4 beats
265 * 1/32 => 32 steps per 4 beats
266 *
267 * The number of steps for binary resolutions follows the powers of 2.
268 * The ternary variants are simply 1.5x faster.
269 */
270 bool is_binary = resolution % 2 == 0;
271 uint8_t binary_steps = 2 << (resolution / 2);
272 uint16_t binary_step_duration = get_beat_duration(tempo) * 4 / binary_steps;
273
274 return is_binary ? binary_step_duration : 2 * binary_step_duration / 3;
275}
diff --git a/quantum/sequencer/sequencer.h b/quantum/sequencer/sequencer.h
new file mode 100644
index 000000000..aeca7a1e9
--- /dev/null
+++ b/quantum/sequencer/sequencer.h
@@ -0,0 +1,122 @@
1/* Copyright 2020 Rodolphe Belouin
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdbool.h>
20#include "debug.h"
21#include "timer.h"
22
23// Maximum number of steps: 256
24#ifndef SEQUENCER_STEPS
25# define SEQUENCER_STEPS 16
26#endif
27
28// Maximum number of tracks: 8
29#ifndef SEQUENCER_TRACKS
30# define SEQUENCER_TRACKS 8
31#endif
32
33#ifndef SEQUENCER_TRACK_THROTTLE
34# define SEQUENCER_TRACK_THROTTLE 3
35#endif
36
37#ifndef SEQUENCER_PHASE_RELEASE_TIMEOUT
38# define SEQUENCER_PHASE_RELEASE_TIMEOUT 30
39#endif
40
41/**
42 * Make sure that the items of this enumeration follow the powers of 2, separated by a ternary variant.
43 * Check the implementation of `get_step_duration` for further explanation.
44 */
45typedef enum { SQ_RES_2, SQ_RES_2T, SQ_RES_4, SQ_RES_4T, SQ_RES_8, SQ_RES_8T, SQ_RES_16, SQ_RES_16T, SQ_RES_32, SEQUENCER_RESOLUTIONS } sequencer_resolution_t;
46
47typedef struct {
48 bool enabled;
49 uint8_t steps[SEQUENCER_STEPS];
50 uint16_t track_notes[SEQUENCER_TRACKS];
51 uint8_t tempo; // Is a maximum tempo of 255 reasonable?
52 sequencer_resolution_t resolution;
53} sequencer_config_t;
54
55/**
56 * Because Digital Audio Workstations get overwhelmed when too many MIDI signals are sent concurrently,
57 * We use a "phase" state machine to delay some of the events.
58 */
59typedef enum sequencer_phase_t {
60 SEQUENCER_PHASE_ATTACK, // t=0ms, send the MIDI note on signal
61 SEQUENCER_PHASE_RELEASE, // t=SEQUENCER_PHASE_RELEASE_TIMEOUT ms, send the MIDI note off signal
62 SEQUENCER_PHASE_PAUSE // t=step duration ms, loop
63} sequencer_phase_t;
64
65typedef struct {
66 uint8_t active_tracks;
67 uint8_t current_track;
68 uint8_t current_step;
69 uint16_t timer;
70 sequencer_phase_t phase;
71} sequencer_state_t;
72
73extern sequencer_config_t sequencer_config;
74
75// We expose the internal state to make the feature more "unit-testable"
76extern sequencer_state_t sequencer_internal_state;
77
78bool is_sequencer_on(void);
79void sequencer_toggle(void);
80void sequencer_on(void);
81void sequencer_off(void);
82
83void sequencer_set_track_notes(const uint16_t track_notes[SEQUENCER_TRACKS]);
84
85bool is_sequencer_track_active(uint8_t track);
86void sequencer_set_track_activation(uint8_t track, bool value);
87void sequencer_toggle_track_activation(uint8_t track);
88void sequencer_toggle_single_active_track(uint8_t track);
89
90#define sequencer_activate_track(track) sequencer_set_track_activation(track, true)
91#define sequencer_deactivate_track(track) sequencer_set_track_activation(track, false)
92
93bool is_sequencer_step_on(uint8_t step);
94bool is_sequencer_step_on_for_track(uint8_t step, uint8_t track);
95void sequencer_set_step(uint8_t step, bool value);
96void sequencer_toggle_step(uint8_t step);
97void sequencer_set_all_steps(bool value);
98
99#define sequencer_set_step_on(step) sequencer_set_step(step, true)
100#define sequencer_set_step_off(step) sequencer_set_step(step, false)
101#define sequencer_set_all_steps_on() sequencer_set_all_steps(true)
102#define sequencer_set_all_steps_off() sequencer_set_all_steps(false)
103
104uint8_t sequencer_get_tempo(void);
105void sequencer_set_tempo(uint8_t tempo);
106void sequencer_increase_tempo(void);
107void sequencer_decrease_tempo(void);
108
109sequencer_resolution_t sequencer_get_resolution(void);
110void sequencer_set_resolution(sequencer_resolution_t resolution);
111void sequencer_increase_resolution(void);
112void sequencer_decrease_resolution(void);
113
114uint8_t sequencer_get_current_step(void);
115
116uint16_t sequencer_get_beat_duration(void);
117uint16_t sequencer_get_step_duration(void);
118
119uint16_t get_beat_duration(uint8_t tempo);
120uint16_t get_step_duration(uint8_t tempo, sequencer_resolution_t resolution);
121
122void matrix_scan_sequencer(void);
diff --git a/quantum/sequencer/tests/midi_mock.c b/quantum/sequencer/tests/midi_mock.c
new file mode 100644
index 000000000..236e16f9d
--- /dev/null
+++ b/quantum/sequencer/tests/midi_mock.c
@@ -0,0 +1,26 @@
1/* Copyright 2020 Rodolphe Belouin
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "midi_mock.h"
18
19uint16_t last_noteon = 0;
20uint16_t last_noteoff = 0;
21
22uint16_t midi_compute_note(uint16_t keycode) { return keycode; }
23
24void process_midi_basic_noteon(uint16_t note) { last_noteon = note; }
25
26void process_midi_basic_noteoff(uint16_t note) { last_noteoff = note; }
diff --git a/quantum/sequencer/tests/midi_mock.h b/quantum/sequencer/tests/midi_mock.h
new file mode 100644
index 000000000..4d8c2eb30
--- /dev/null
+++ b/quantum/sequencer/tests/midi_mock.h
@@ -0,0 +1,26 @@
1/* Copyright 2020 Rodolphe Belouin
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdint.h>
20
21extern uint16_t last_noteon;
22extern uint16_t last_noteoff;
23
24uint16_t midi_compute_note(uint16_t keycode);
25void process_midi_basic_noteon(uint16_t note);
26void process_midi_basic_noteoff(uint16_t note);
diff --git a/quantum/sequencer/tests/rules.mk b/quantum/sequencer/tests/rules.mk
new file mode 100644
index 000000000..76c221cf9
--- /dev/null
+++ b/quantum/sequencer/tests/rules.mk
@@ -0,0 +1,11 @@
1# The letter case of these variables might seem odd. However:
2# - it is consistent with the serial_link example that is used as a reference in the Unit Testing article (https://docs.qmk.fm/#/unit_testing?id=adding-tests-for-new-or-existing-features)
3# - Neither `make test:sequencer` or `make test:SEQUENCER` work when using SCREAMING_SNAKE_CASE
4
5sequencer_DEFS := -DNO_DEBUG -DMIDI_MOCKED
6
7sequencer_SRC := \
8 $(QUANTUM_PATH)/sequencer/tests/midi_mock.c \
9 $(QUANTUM_PATH)/sequencer/tests/sequencer_tests.cpp \
10 $(QUANTUM_PATH)/sequencer/sequencer.c \
11 $(TMK_PATH)/common/test/timer.c
diff --git a/quantum/sequencer/tests/sequencer_tests.cpp b/quantum/sequencer/tests/sequencer_tests.cpp
new file mode 100644
index 000000000..e81984e5b
--- /dev/null
+++ b/quantum/sequencer/tests/sequencer_tests.cpp
@@ -0,0 +1,590 @@
1/* Copyright 2020 Rodolphe Belouin
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "gtest/gtest.h"
18
19extern "C" {
20#include "sequencer.h"
21#include "midi_mock.h"
22#include "quantum/quantum_keycodes.h"
23}
24
25extern "C" {
26void set_time(uint32_t t);
27void advance_time(uint32_t ms);
28}
29
30class SequencerTest : public ::testing::Test {
31 protected:
32 void SetUp() override {
33 config_copy.enabled = sequencer_config.enabled;
34
35 for (int i = 0; i < SEQUENCER_STEPS; i++) {
36 config_copy.steps[i] = sequencer_config.steps[i];
37 }
38
39 for (int i = 0; i < SEQUENCER_TRACKS; i++) {
40 config_copy.track_notes[i] = sequencer_config.track_notes[i];
41 }
42
43 config_copy.tempo = sequencer_config.tempo;
44 config_copy.resolution = sequencer_config.resolution;
45
46 state_copy.active_tracks = sequencer_internal_state.active_tracks;
47 state_copy.current_track = sequencer_internal_state.current_track;
48 state_copy.current_step = sequencer_internal_state.current_step;
49 state_copy.timer = sequencer_internal_state.timer;
50
51 last_noteon = 0;
52 last_noteoff = 0;
53
54 set_time(0);
55 }
56
57 void TearDown() override {
58 sequencer_config.enabled = config_copy.enabled;
59
60 for (int i = 0; i < SEQUENCER_STEPS; i++) {
61 sequencer_config.steps[i] = config_copy.steps[i];
62 }
63
64 for (int i = 0; i < SEQUENCER_TRACKS; i++) {
65 sequencer_config.track_notes[i] = config_copy.track_notes[i];
66 }
67
68 sequencer_config.tempo = config_copy.tempo;
69 sequencer_config.resolution = config_copy.resolution;
70
71 sequencer_internal_state.active_tracks = state_copy.active_tracks;
72 sequencer_internal_state.current_track = state_copy.current_track;
73 sequencer_internal_state.current_step = state_copy.current_step;
74 sequencer_internal_state.timer = state_copy.timer;
75 }
76
77 sequencer_config_t config_copy;
78 sequencer_state_t state_copy;
79};
80
81TEST_F(SequencerTest, TestOffByDefault) { EXPECT_EQ(is_sequencer_on(), false); }
82
83TEST_F(SequencerTest, TestOn) {
84 sequencer_config.enabled = false;
85
86 sequencer_on();
87 EXPECT_EQ(is_sequencer_on(), true);
88
89 // sequencer_on is idempotent
90 sequencer_on();
91 EXPECT_EQ(is_sequencer_on(), true);
92}
93
94TEST_F(SequencerTest, TestOff) {
95 sequencer_config.enabled = true;
96
97 sequencer_off();
98 EXPECT_EQ(is_sequencer_on(), false);
99
100 // sequencer_off is idempotent
101 sequencer_off();
102 EXPECT_EQ(is_sequencer_on(), false);
103}
104
105TEST_F(SequencerTest, TestToggle) {
106 sequencer_config.enabled = false;
107
108 sequencer_toggle();
109 EXPECT_EQ(is_sequencer_on(), true);
110
111 sequencer_toggle();
112 EXPECT_EQ(is_sequencer_on(), false);
113}
114
115TEST_F(SequencerTest, TestNoActiveTrackByDefault) {
116 for (int i = 0; i < SEQUENCER_TRACKS; i++) {
117 EXPECT_EQ(is_sequencer_track_active(i), false);
118 }
119}
120
121TEST_F(SequencerTest, TestGetActiveTracks) {
122 sequencer_internal_state.active_tracks = (1 << 7) + (1 << 6) + (1 << 3) + (1 << 1) + (1 << 0);
123
124 EXPECT_EQ(is_sequencer_track_active(0), true);
125 EXPECT_EQ(is_sequencer_track_active(1), true);
126 EXPECT_EQ(is_sequencer_track_active(2), false);
127 EXPECT_EQ(is_sequencer_track_active(3), true);
128 EXPECT_EQ(is_sequencer_track_active(4), false);
129 EXPECT_EQ(is_sequencer_track_active(5), false);
130 EXPECT_EQ(is_sequencer_track_active(6), true);
131 EXPECT_EQ(is_sequencer_track_active(7), true);
132}
133
134TEST_F(SequencerTest, TestGetActiveTracksOutOfBound) {
135 sequencer_set_track_activation(-1, true);
136 sequencer_set_track_activation(8, true);
137
138 EXPECT_EQ(is_sequencer_track_active(-1), false);
139 EXPECT_EQ(is_sequencer_track_active(8), false);
140}
141
142TEST_F(SequencerTest, TestToggleTrackActivation) {
143 sequencer_internal_state.active_tracks = (1 << 7) + (1 << 6) + (1 << 3) + (1 << 1) + (1 << 0);
144
145 sequencer_toggle_track_activation(6);
146
147 EXPECT_EQ(is_sequencer_track_active(0), true);
148 EXPECT_EQ(is_sequencer_track_active(1), true);
149 EXPECT_EQ(is_sequencer_track_active(2), false);
150 EXPECT_EQ(is_sequencer_track_active(3), true);
151 EXPECT_EQ(is_sequencer_track_active(4), false);
152 EXPECT_EQ(is_sequencer_track_active(5), false);
153 EXPECT_EQ(is_sequencer_track_active(6), false);
154 EXPECT_EQ(is_sequencer_track_active(7), true);
155}
156
157TEST_F(SequencerTest, TestToggleSingleTrackActivation) {
158 sequencer_internal_state.active_tracks = (1 << 7) + (1 << 6) + (1 << 3) + (1 << 1) + (1 << 0);
159
160 sequencer_toggle_single_active_track(2);
161
162 EXPECT_EQ(is_sequencer_track_active(0), false);
163 EXPECT_EQ(is_sequencer_track_active(1), false);
164 EXPECT_EQ(is_sequencer_track_active(2), true);
165 EXPECT_EQ(is_sequencer_track_active(3), false);
166 EXPECT_EQ(is_sequencer_track_active(4), false);
167 EXPECT_EQ(is_sequencer_track_active(5), false);
168 EXPECT_EQ(is_sequencer_track_active(6), false);
169 EXPECT_EQ(is_sequencer_track_active(7), false);
170}
171
172TEST_F(SequencerTest, TestStepOffByDefault) {
173 for (int i = 0; i < SEQUENCER_STEPS; i++) {
174 EXPECT_EQ(is_sequencer_step_on(i), false);
175 }
176}
177
178TEST_F(SequencerTest, TestIsStepOffWithNoActiveTracks) {
179 sequencer_config.steps[3] = 0xFF;
180 EXPECT_EQ(is_sequencer_step_on(3), false);
181}
182
183TEST_F(SequencerTest, TestIsStepOffWithGivenActiveTracks) {
184 sequencer_set_track_activation(2, true);
185 sequencer_set_track_activation(3, true);
186
187 sequencer_config.steps[3] = (1 << 0) + (1 << 1);
188
189 // No active tracks have the step enabled, so it is off
190 EXPECT_EQ(is_sequencer_step_on(3), false);
191}
192
193TEST_F(SequencerTest, TestIsStepOnWithGivenActiveTracks) {
194 sequencer_set_track_activation(2, true);
195 sequencer_set_track_activation(3, true);
196
197 sequencer_config.steps[3] = (1 << 2);
198
199 // Track 2 has the step enabled, so it is on
200 EXPECT_EQ(is_sequencer_step_on(3), true);
201}
202
203TEST_F(SequencerTest, TestIsStepOffForGivenTrack) {
204 sequencer_config.steps[3] = 0x00;
205 EXPECT_EQ(is_sequencer_step_on_for_track(3, 5), false);
206}
207
208TEST_F(SequencerTest, TestIsStepOnForGivenTrack) {
209 sequencer_config.steps[3] = (1 << 5);
210 EXPECT_EQ(is_sequencer_step_on_for_track(3, 5), true);
211}
212
213TEST_F(SequencerTest, TestSetStepOn) {
214 sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);
215 sequencer_config.steps[2] = (1 << 5) + (1 << 2);
216
217 sequencer_set_step(2, true);
218
219 EXPECT_EQ(sequencer_config.steps[2], (1 << 6) + (1 << 5) + (1 << 3) + (1 << 2));
220}
221
222TEST_F(SequencerTest, TestSetStepOff) {
223 sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);
224 sequencer_config.steps[2] = (1 << 5) + (1 << 2);
225
226 sequencer_set_step(2, false);
227
228 EXPECT_EQ(sequencer_config.steps[2], (1 << 5));
229}
230
231TEST_F(SequencerTest, TestToggleStepOff) {
232 sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);
233 sequencer_config.steps[2] = (1 << 5) + (1 << 2);
234
235 sequencer_toggle_step(2);
236
237 EXPECT_EQ(sequencer_config.steps[2], (1 << 5));
238}
239
240TEST_F(SequencerTest, TestToggleStepOn) {
241 sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);
242 sequencer_config.steps[2] = 0;
243
244 sequencer_toggle_step(2);
245
246 EXPECT_EQ(sequencer_config.steps[2], (1 << 6) + (1 << 3) + (1 << 2));
247}
248
249TEST_F(SequencerTest, TestSetAllStepsOn) {
250 sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);
251 sequencer_config.steps[2] = (1 << 7) + (1 << 6);
252 sequencer_config.steps[4] = (1 << 3) + (1 << 1);
253
254 sequencer_set_all_steps(true);
255
256 EXPECT_EQ(sequencer_config.steps[2], (1 << 7) + (1 << 6) + (1 << 3) + (1 << 2));
257 EXPECT_EQ(sequencer_config.steps[4], (1 << 6) + (1 << 3) + (1 << 2) + (1 << 1));
258}
259
260TEST_F(SequencerTest, TestSetAllStepsOff) {
261 sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);
262 sequencer_config.steps[2] = (1 << 7) + (1 << 6);
263 sequencer_config.steps[4] = (1 << 3) + (1 << 1);
264
265 sequencer_set_all_steps(false);
266
267 EXPECT_EQ(sequencer_config.steps[2], (1 << 7));
268 EXPECT_EQ(sequencer_config.steps[4], (1 << 1));
269}
270
271TEST_F(SequencerTest, TestSetTempoZero) {
272 sequencer_config.tempo = 123;
273
274 sequencer_set_tempo(0);
275
276 EXPECT_EQ(sequencer_config.tempo, 123);
277}
278
279TEST_F(SequencerTest, TestIncreaseTempoMax) {
280 sequencer_config.tempo = UINT8_MAX;
281
282 sequencer_increase_tempo();
283
284 EXPECT_EQ(sequencer_config.tempo, UINT8_MAX);
285}
286
287TEST_F(SequencerTest, TestSetResolutionLowerBound) {
288 sequencer_config.resolution = SQ_RES_4;
289
290 sequencer_set_resolution((sequencer_resolution_t)-1);
291
292 EXPECT_EQ(sequencer_config.resolution, SQ_RES_4);
293}
294
295TEST_F(SequencerTest, TestSetResolutionUpperBound) {
296 sequencer_config.resolution = SQ_RES_4;
297
298 sequencer_set_resolution(SEQUENCER_RESOLUTIONS);
299
300 EXPECT_EQ(sequencer_config.resolution, SQ_RES_4);
301}
302
303TEST_F(SequencerTest, TestGetBeatDuration) {
304 EXPECT_EQ(get_beat_duration(60), 1000);
305 EXPECT_EQ(get_beat_duration(120), 500);
306 EXPECT_EQ(get_beat_duration(240), 250);
307 EXPECT_EQ(get_beat_duration(0), 1000);
308}
309
310TEST_F(SequencerTest, TestGetStepDuration60) {
311 /**
312 * Resolution cheatsheet:
313 * 1/2 => 2 steps per 4 beats
314 * 1/2T => 3 steps per 4 beats
315 * 1/4 => 4 steps per 4 beats
316 * 1/4T => 6 steps per 4 beats
317 * 1/8 => 8 steps per 4 beats
318 * 1/8T => 12 steps per 4 beats
319 * 1/16 => 16 steps per 4 beats
320 * 1/16T => 24 steps per 4 beats
321 * 1/32 => 32 steps per 4 beats
322 *
323 * The number of steps for binary resolutions follows the powers of 2.
324 * The ternary variants are simply 1.5x faster.
325 */
326 EXPECT_EQ(get_step_duration(60, SQ_RES_2), 2000);
327 EXPECT_EQ(get_step_duration(60, SQ_RES_4), 1000);
328 EXPECT_EQ(get_step_duration(60, SQ_RES_8), 500);
329 EXPECT_EQ(get_step_duration(60, SQ_RES_16), 250);
330 EXPECT_EQ(get_step_duration(60, SQ_RES_32), 125);
331
332 EXPECT_EQ(get_step_duration(60, SQ_RES_2T), 1333);
333 EXPECT_EQ(get_step_duration(60, SQ_RES_4T), 666);
334 EXPECT_EQ(get_step_duration(60, SQ_RES_8T), 333);
335 EXPECT_EQ(get_step_duration(60, SQ_RES_16T), 166);
336}
337
338TEST_F(SequencerTest, TestGetStepDuration120) {
339 /**
340 * Resolution cheatsheet:
341 * 1/2 => 2 steps per 4 beats
342 * 1/2T => 3 steps per 4 beats
343 * 1/4 => 4 steps per 4 beats
344 * 1/4T => 6 steps per 4 beats
345 * 1/8 => 8 steps per 4 beats
346 * 1/8T => 12 steps per 4 beats
347 * 1/16 => 16 steps per 4 beats
348 * 1/16T => 24 steps per 4 beats
349 * 1/32 => 32 steps per 4 beats
350 *
351 * The number of steps for binary resolutions follows the powers of 2.
352 * The ternary variants are simply 1.5x faster.
353 */
354 EXPECT_EQ(get_step_duration(30, SQ_RES_2), 4000);
355 EXPECT_EQ(get_step_duration(30, SQ_RES_4), 2000);
356 EXPECT_EQ(get_step_duration(30, SQ_RES_8), 1000);
357 EXPECT_EQ(get_step_duration(30, SQ_RES_16), 500);
358 EXPECT_EQ(get_step_duration(30, SQ_RES_32), 250);
359
360 EXPECT_EQ(get_step_duration(30, SQ_RES_2T), 2666);
361 EXPECT_EQ(get_step_duration(30, SQ_RES_4T), 1333);
362 EXPECT_EQ(get_step_duration(30, SQ_RES_8T), 666);
363 EXPECT_EQ(get_step_duration(30, SQ_RES_16T), 333);
364}
365
366void setUpMatrixScanSequencerTest(void) {
367 sequencer_config.enabled = true;
368 sequencer_config.tempo = 120;
369 sequencer_config.resolution = SQ_RES_16;
370
371 // Configure the notes for each track
372 sequencer_config.track_notes[0] = MI_C;
373 sequencer_config.track_notes[1] = MI_D;
374 sequencer_config.track_notes[2] = MI_E;
375 sequencer_config.track_notes[3] = MI_F;
376 sequencer_config.track_notes[4] = MI_G;
377 sequencer_config.track_notes[5] = MI_A;
378 sequencer_config.track_notes[6] = MI_B;
379 sequencer_config.track_notes[7] = MI_C;
380
381 // Turn on some steps
382 sequencer_config.steps[0] = (1 << 0);
383 sequencer_config.steps[2] = (1 << 1) + (1 << 0);
384}
385
386TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackFirstTrackOfFirstStep) {
387 setUpMatrixScanSequencerTest();
388
389 matrix_scan_sequencer();
390 EXPECT_EQ(last_noteon, MI_C);
391 EXPECT_EQ(last_noteoff, 0);
392}
393
394TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackSecondTrackAfterFirstTrackOfFirstStep) {
395 setUpMatrixScanSequencerTest();
396
397 matrix_scan_sequencer();
398 EXPECT_EQ(sequencer_internal_state.current_step, 0);
399 EXPECT_EQ(sequencer_internal_state.current_track, 1);
400 EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);
401}
402
403TEST_F(SequencerTest, TestMatrixScanSequencerShouldNotAttackInactiveTrackFirstStep) {
404 setUpMatrixScanSequencerTest();
405
406 sequencer_internal_state.current_step = 0;
407 sequencer_internal_state.current_track = 1;
408
409 // Wait some time after the first track has been attacked
410 advance_time(SEQUENCER_TRACK_THROTTLE);
411
412 matrix_scan_sequencer();
413 EXPECT_EQ(last_noteon, 0);
414 EXPECT_EQ(last_noteoff, 0);
415}
416
417TEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackThirdTrackAfterSecondTrackOfFirstStep) {
418 setUpMatrixScanSequencerTest();
419
420 sequencer_internal_state.current_step = 0;
421 sequencer_internal_state.current_track = 1;
422
423 // Wait some time after the second track has been attacked
424 advance_time(2 * SEQUENCER_TRACK_THROTTLE);
425
426 matrix_scan_sequencer();
427 EXPECT_EQ(sequencer_internal_state.current_step, 0);
428 EXPECT_EQ(sequencer_internal_state.current_track, 2);
429 EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);
430}
431
432TEST_F(SequencerTest, TestMatrixScanSequencerShouldEnterReleasePhaseAfterLastTrackHasBeenProcessedFirstStep) {
433 setUpMatrixScanSequencerTest();
434
435 sequencer_internal_state.current_step = 0;
436 sequencer_internal_state.current_track = SEQUENCER_TRACKS - 1;
437
438 // Wait until all notes have been attacked
439 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
440
441 matrix_scan_sequencer();
442 EXPECT_EQ(last_noteon, 0);
443 EXPECT_EQ(last_noteoff, 0);
444 EXPECT_EQ(sequencer_internal_state.current_step, 0);
445 EXPECT_EQ(sequencer_internal_state.current_track, SEQUENCER_TRACKS - 1);
446 EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_RELEASE);
447}
448
449TEST_F(SequencerTest, TestMatrixScanSequencerShouldReleaseBackwards) {
450 setUpMatrixScanSequencerTest();
451
452 sequencer_internal_state.current_step = 0;
453 sequencer_internal_state.current_track = SEQUENCER_TRACKS - 1;
454 sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE;
455
456 // Wait until all notes have been attacked
457 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
458 // + the release timeout
459 advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);
460
461 matrix_scan_sequencer();
462 EXPECT_EQ(sequencer_internal_state.current_step, 0);
463 EXPECT_EQ(sequencer_internal_state.current_track, SEQUENCER_TRACKS - 2);
464 EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_RELEASE);
465}
466
467TEST_F(SequencerTest, TestMatrixScanSequencerShouldNotReleaseInactiveTrackFirstStep) {
468 setUpMatrixScanSequencerTest();
469
470 sequencer_internal_state.current_step = 0;
471 sequencer_internal_state.current_track = SEQUENCER_TRACKS - 1;
472 sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE;
473
474 // Wait until all notes have been attacked
475 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
476 // + the release timeout
477 advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);
478
479 matrix_scan_sequencer();
480 EXPECT_EQ(last_noteon, 0);
481 EXPECT_EQ(last_noteoff, 0);
482}
483
484TEST_F(SequencerTest, TestMatrixScanSequencerShouldReleaseFirstTrackFirstStep) {
485 setUpMatrixScanSequencerTest();
486
487 sequencer_internal_state.current_step = 0;
488 sequencer_internal_state.current_track = 0;
489 sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE;
490
491 // Wait until all notes have been attacked
492 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
493 // + the release timeout
494 advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);
495 // + all the other notes have been released
496 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
497
498 matrix_scan_sequencer();
499 EXPECT_EQ(last_noteon, 0);
500 EXPECT_EQ(last_noteoff, MI_C);
501}
502
503TEST_F(SequencerTest, TestMatrixScanSequencerShouldEnterPausePhaseAfterRelease) {
504 setUpMatrixScanSequencerTest();
505
506 sequencer_internal_state.current_step = 0;
507 sequencer_internal_state.current_track = 0;
508 sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE;
509
510 // Wait until all notes have been attacked
511 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
512 // + the release timeout
513 advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);
514 // + all the other notes have been released
515 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
516
517 matrix_scan_sequencer();
518 EXPECT_EQ(sequencer_internal_state.current_step, 0);
519 EXPECT_EQ(sequencer_internal_state.current_track, 0);
520 EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_PAUSE);
521}
522
523TEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessFirstTrackOfSecondStepAfterPause) {
524 setUpMatrixScanSequencerTest();
525
526 sequencer_internal_state.current_step = 0;
527 sequencer_internal_state.current_track = 0;
528 sequencer_internal_state.phase = SEQUENCER_PHASE_PAUSE;
529
530 // Wait until all notes have been attacked
531 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
532 // + the release timeout
533 advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);
534 // + all the other notes have been released
535 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
536 // + the step duration (one 16th at tempo=120 lasts 125ms)
537 advance_time(125);
538
539 matrix_scan_sequencer();
540 EXPECT_EQ(sequencer_internal_state.current_step, 1);
541 EXPECT_EQ(sequencer_internal_state.current_track, 1);
542 EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);
543}
544
545TEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessSecondTrackTooEarly) {
546 setUpMatrixScanSequencerTest();
547
548 sequencer_internal_state.current_step = 2;
549 sequencer_internal_state.current_track = 1;
550
551 matrix_scan_sequencer();
552 EXPECT_EQ(last_noteon, 0);
553 EXPECT_EQ(last_noteoff, 0);
554}
555
556TEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessSecondTrackOnTime) {
557 setUpMatrixScanSequencerTest();
558
559 sequencer_internal_state.current_step = 2;
560 sequencer_internal_state.current_track = 1;
561
562 // Wait until first track has been attacked
563 advance_time(SEQUENCER_TRACK_THROTTLE);
564
565 matrix_scan_sequencer();
566 EXPECT_EQ(last_noteon, MI_D);
567 EXPECT_EQ(last_noteoff, 0);
568}
569
570TEST_F(SequencerTest, TestMatrixScanSequencerShouldLoopOnceSequenceIsOver) {
571 setUpMatrixScanSequencerTest();
572
573 sequencer_internal_state.current_step = SEQUENCER_STEPS - 1;
574 sequencer_internal_state.current_track = 0;
575 sequencer_internal_state.phase = SEQUENCER_PHASE_PAUSE;
576
577 // Wait until all notes have been attacked
578 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
579 // + the release timeout
580 advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);
581 // + all the other notes have been released
582 advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);
583 // + the step duration (one 16th at tempo=120 lasts 125ms)
584 advance_time(125);
585
586 matrix_scan_sequencer();
587 EXPECT_EQ(sequencer_internal_state.current_step, 0);
588 EXPECT_EQ(sequencer_internal_state.current_track, 1);
589 EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);
590}
diff --git a/quantum/sequencer/tests/testlist.mk b/quantum/sequencer/tests/testlist.mk
new file mode 100644
index 000000000..bb3899110
--- /dev/null
+++ b/quantum/sequencer/tests/testlist.mk
@@ -0,0 +1 @@
TEST_LIST += sequencer
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c
index 5bad9db08..cd5a024c3 100644
--- a/quantum/split_common/matrix.c
+++ b/quantum/split_common/matrix.c
@@ -45,6 +45,19 @@ uint8_t thisHand, thatHand;
45// user-defined overridable functions 45// user-defined overridable functions
46__attribute__((weak)) void matrix_slave_scan_user(void) {} 46__attribute__((weak)) void matrix_slave_scan_user(void) {}
47 47
48static inline void setPinOutput_writeLow(pin_t pin) {
49 ATOMIC_BLOCK_FORCEON {
50 setPinOutput(pin);
51 writePinLow(pin);
52 }
53}
54
55static inline void setPinInputHigh_atomic(pin_t pin) {
56 ATOMIC_BLOCK_FORCEON {
57 setPinInputHigh(pin);
58 }
59}
60
48// matrix code 61// matrix code
49 62
50#ifdef DIRECT_PINS 63#ifdef DIRECT_PINS
@@ -83,22 +96,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
83# if (DIODE_DIRECTION == COL2ROW) 96# if (DIODE_DIRECTION == COL2ROW)
84 97
85static void select_row(uint8_t row) { 98static void select_row(uint8_t row) {
86 setPinOutput(row_pins[row]); 99 setPinOutput_writeLow(row_pins[row]);
87 writePinLow(row_pins[row]);
88} 100}
89 101
90static void unselect_row(uint8_t row) { setPinInputHigh(row_pins[row]); } 102static void unselect_row(uint8_t row) {
103 setPinInputHigh_atomic(row_pins[row]);
104}
91 105
92static void unselect_rows(void) { 106static void unselect_rows(void) {
93 for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { 107 for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
94 setPinInputHigh(row_pins[x]); 108 setPinInputHigh_atomic(row_pins[x]);
95 } 109 }
96} 110}
97 111
98static void init_pins(void) { 112static void init_pins(void) {
99 unselect_rows(); 113 unselect_rows();
100 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 114 for (uint8_t x = 0; x < MATRIX_COLS; x++) {
101 setPinInputHigh(col_pins[x]); 115 setPinInputHigh_atomic(col_pins[x]);
102 } 116 }
103} 117}
104 118
@@ -133,22 +147,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
133# elif (DIODE_DIRECTION == ROW2COL) 147# elif (DIODE_DIRECTION == ROW2COL)
134 148
135static void select_col(uint8_t col) { 149static void select_col(uint8_t col) {
136 setPinOutput(col_pins[col]); 150 setPinOutput_writeLow(col_pins[col]);
137 writePinLow(col_pins[col]);
138} 151}
139 152
140static void unselect_col(uint8_t col) { setPinInputHigh(col_pins[col]); } 153static void unselect_col(uint8_t col) {
154 setPinInputHigh_atomic(col_pins[col]);
155}
141 156
142static void unselect_cols(void) { 157static void unselect_cols(void) {
143 for (uint8_t x = 0; x < MATRIX_COLS; x++) { 158 for (uint8_t x = 0; x < MATRIX_COLS; x++) {
144 setPinInputHigh(col_pins[x]); 159 setPinInputHigh_atomic(col_pins[x]);
145 } 160 }
146} 161}
147 162
148static void init_pins(void) { 163static void init_pins(void) {
149 unselect_cols(); 164 unselect_cols();
150 for (uint8_t x = 0; x < ROWS_PER_HAND; x++) { 165 for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
151 setPinInputHigh(row_pins[x]); 166 setPinInputHigh_atomic(row_pins[x]);
152 } 167 }
153} 168}
154 169