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