qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

audio.h (10430B)


      1 /* Copyright 2016-2020 Jack Humbert
      2  * Copyright 2020 JohSchneider
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU General Public License as published by
      6  * the Free Software Foundation, either version 2 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  */
     17 #pragma once
     18 
     19 #include <stdint.h>
     20 #include <stdbool.h>
     21 
     22 #include "compiler_support.h"
     23 #include "musical_notes.h"
     24 #include "song_list.h"
     25 #include "voices.h"
     26 
     27 #if defined(AUDIO_DRIVER_PWM)
     28 #    include "audio_pwm.h"
     29 #elif defined(AUDIO_DRIVER_DAC)
     30 #    include "audio_dac.h"
     31 #endif
     32 
     33 typedef union audio_config_t {
     34     uint8_t raw;
     35     struct {
     36         bool    enable : 1;
     37         bool    clicky_enable : 1;
     38         bool    valid : 1;
     39         uint8_t reserved : 5;
     40     };
     41 } audio_config_t;
     42 
     43 STATIC_ASSERT(sizeof(audio_config_t) == sizeof(uint8_t), "Audio EECONFIG out of spec.");
     44 
     45 /*
     46  * a 'musical note' is represented by pitch and duration; a 'musical tone' adds intensity and timbre
     47  * https://en.wikipedia.org/wiki/Musical_tone
     48  * "A musical tone is characterized by its duration, pitch, intensity (or loudness), and timbre (or quality)"
     49  */
     50 typedef struct {
     51     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
     52     float    pitch;        // aka frequency, in Hz
     53     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
     54     // float intensity;    // aka volume [0,1] TODO: not used at the moment; pwm drivers can't handle it
     55     // uint8_t timbre;     // range: [0,100] TODO: this currently kept track of globally, should we do this per tone instead?
     56 } musical_tone_t;
     57 
     58 // public interface
     59 
     60 /**
     61  * @brief Save the current choices to the eeprom
     62  */
     63 void eeconfig_update_audio_current(void);
     64 
     65 /**
     66  * @brief one-time initialization called by quantum/quantum.c
     67  * @details usually done lazy, when some tones are to be played
     68  *
     69  * @post audio system (and hardware) initialized and ready to play tones
     70  */
     71 void audio_init(void);
     72 
     73 /**
     74  * \brief Handle various subsystem background tasks.
     75  */
     76 void audio_task(void);
     77 
     78 /**
     79  * @brief en-/disable audio output, save this choice to the eeprom
     80  */
     81 void audio_toggle(void);
     82 /**
     83  * @brief enable audio output, save this choice to the eeprom
     84  */
     85 void audio_on(void);
     86 /**
     87  * @brief disable audio output, save this choice to the eeprom
     88  */
     89 void audio_off(void);
     90 /**
     91  * @brief query the if audio output is enabled
     92  */
     93 bool audio_is_on(void);
     94 
     95 /**
     96  * @brief start playback of a tone with the given frequency and duration
     97  *
     98  * @details starts the playback of a given note, which is automatically stopped
     99  *          at the the end of its duration = fire&forget
    100  *
    101  * @param[in] pitch frequency of the tone be played
    102  * @param[in] duration in milliseconds, use 'audio_duration_to_ms' to convert
    103  *                     from the musical_notes.h unit to ms
    104  */
    105 void audio_play_note(float pitch, uint16_t duration);
    106 // TODO: audio_play_note(float pitch, uint16_t duration, float intensity, float timbre);
    107 // audio_play_note_with_instrument ifdef AUDIO_ENABLE_VOICES
    108 
    109 /**
    110  * @brief start playback of a tone with the given frequency
    111  *
    112  * @details the 'frequency' is put on-top the internal stack of active tones,
    113  *          as a new tone with indefinite duration. this tone is played by
    114  *          the hardware until a call to 'audio_stop_tone'.
    115  *          should a tone with that frequency already be active, its entry
    116  *          is put on the top of said internal stack - so no duplicate
    117  *          entries are kept.
    118  *          'hardware_start' is called upon the first note.
    119  *
    120  * @param[in] pitch frequency of the tone be played
    121  */
    122 void audio_play_tone(float pitch);
    123 
    124 /**
    125  * @brief stop a given tone/frequency
    126  *
    127  * @details removes a tone matching the given frequency from the internal
    128  *          playback stack
    129  *          the hardware is stopped in case this was the last/only frequency
    130  *          being played.
    131  *
    132  * @param[in] pitch tone/frequency to be stopped
    133  */
    134 void audio_stop_tone(float pitch);
    135 
    136 /**
    137  * @brief play a melody
    138  *
    139  * @details starts playback of a melody passed in from a SONG definition - an
    140  *          array of {pitch, duration} float-tuples
    141  *
    142  * @param[in] np note-pointer to the SONG array
    143  * @param[in] n_count number of MUSICAL_NOTES of the SONG
    144  * @param[in] n_repeat false for onetime, true for looped playback
    145  */
    146 void audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat);
    147 
    148 /**
    149  * @brief play a short tone of a specific frequency to emulate a 'click'
    150  *
    151  * @details constructs a two-note melody (one pause plus a note) and plays it through
    152  *          audio_play_melody. very short durations might not quite work due to
    153  *          hardware limitations (DAC: added pulses from zero-crossing feature;...)
    154  *
    155  * @param[in] delay in milliseconds, length for the pause before the pulses, can be zero
    156  * @param[in] pitch
    157  * @param[in] duration in milliseconds, length of the 'click'
    158  */
    159 void audio_play_click(uint16_t delay, float pitch, uint16_t duration);
    160 
    161 /**
    162  * @brief stops all playback
    163  *
    164  * @details stops playback of both a melody as well as single tones, resetting
    165  *          the internal state
    166  */
    167 void audio_stop_all(void);
    168 
    169 /**
    170  * @brief query if one/multiple tones are playing
    171  */
    172 bool audio_is_playing_note(void);
    173 
    174 /**
    175  * @brief query if a melody/SONG is playing
    176  */
    177 bool audio_is_playing_melody(void);
    178 
    179 // These macros are used to allow audio_play_melody to play an array of indeterminate
    180 // length. This works around the limitation of C's sizeof operation on pointers.
    181 // The global float array for the song must be used here.
    182 #define NOTE_ARRAY_SIZE(x) ((int16_t)(sizeof(x) / (sizeof(x[0]))))
    183 
    184 /**
    185  * @brief convenience macro, to play a melody/SONG once
    186  */
    187 #define PLAY_SONG(note_array) audio_play_melody(&note_array, NOTE_ARRAY_SIZE((note_array)), false)
    188 // TODO: a 'song' is a melody plus singing/vocals -> PLAY_MELODY
    189 /**
    190  * @brief convenience macro, to play a melody/SONG in a loop, until stopped by 'audio_stop_all'
    191  */
    192 #define PLAY_LOOP(note_array) audio_play_melody(&note_array, NOTE_ARRAY_SIZE((note_array)), true)
    193 
    194 // Tone-Multiplexing functions
    195 // this feature only makes sense for hardware setups which can't do proper
    196 // audio-wave synthesis = have no DAC and need to use PWM for tone generation
    197 #ifdef AUDIO_ENABLE_TONE_MULTIPLEXING
    198 #    ifndef AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT
    199 #        define AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT 0
    200 //       0=off, good starting value is 4; the lower the value the higher the cpu-load
    201 #    endif
    202 void audio_set_tone_multiplexing_rate(uint16_t rate);
    203 void audio_enable_tone_multiplexing(void);
    204 void audio_disable_tone_multiplexing(void);
    205 void audio_increase_tone_multiplexing_rate(uint16_t change);
    206 void audio_decrease_tone_multiplexing_rate(uint16_t change);
    207 #endif
    208 
    209 // Tempo functions
    210 
    211 void audio_set_tempo(uint8_t tempo);
    212 void audio_increase_tempo(uint8_t tempo_change);
    213 void audio_decrease_tempo(uint8_t tempo_change);
    214 
    215 // conversion macros, from 64parts-to-a-beat to milliseconds and back
    216 uint16_t audio_duration_to_ms(uint16_t duration_bpm);
    217 uint16_t audio_ms_to_duration(uint16_t duration_ms);
    218 
    219 void audio_startup(void);
    220 
    221 // hardware interface
    222 
    223 // implementation in the driver_avr/arm_* respective parts
    224 void audio_driver_initialize_impl(void);
    225 void audio_driver_start_impl(void);
    226 void audio_driver_stop_impl(void);
    227 
    228 /**
    229  * @brief get the number of currently active tones
    230  * @return number, 0=none active
    231  */
    232 uint8_t audio_get_number_of_active_tones(void);
    233 
    234 /**
    235  * @brief access to the raw/unprocessed frequency for a specific tone
    236  * @details each active tone has a frequency associated with it, which
    237  *          the internal state keeps track of, and is usually influenced
    238  *          by various effects
    239  * @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the
    240  *            first being the most recent and each increment yielding the next
    241  *            older one
    242  * @return a positive frequency, in Hz; or zero if the tone is a pause
    243  */
    244 float audio_get_frequency(uint8_t tone_index);
    245 
    246 /**
    247  * @brief calculate and return the frequency for the requested tone
    248  * @details effects like glissando, vibrato, ... are post-processed onto the
    249  *          each active tones 'base'-frequency; this function returns the
    250  *          post-processed result.
    251  * @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the
    252  *            first being the most recent and each increment yielding the next
    253  *            older one
    254  * @return a positive frequency, in Hz; or zero if the tone is a pause
    255  */
    256 float audio_get_processed_frequency(uint8_t tone_index);
    257 
    258 /**
    259  * @brief   update audio internal state: currently playing and active tones,...
    260  * @details This function is intended to be called by the audio-hardware
    261  *          specific implementation on a somewhat regular basis while a SONG
    262  *          or notes (pitch+duration) are playing to 'advance' the internal
    263  *          state (current playing notes, position in the melody, ...)
    264  *
    265  * @return true if something changed in the currently active tones, which the
    266  *         hardware might need to react to
    267  */
    268 bool audio_update_state(void);
    269 
    270 // legacy and back-warts compatibility stuff
    271 
    272 #define is_audio_on() audio_is_on()
    273 #define is_playing_notes() audio_is_playing_melody()
    274 #define is_playing_note() audio_is_playing_note()
    275 #define stop_all_notes() audio_stop_all()
    276 #define stop_note(f) audio_stop_tone(f)
    277 #define play_note(f, v) audio_play_tone(f)
    278 
    279 #define set_timbre(t) voice_set_timbre(t)
    280 #define set_tempo(t) audio_set_tempo(t)
    281 #define increase_tempo(t) audio_increase_tempo(t)
    282 #define decrease_tempo(t) audio_decrease_tempo(t)
    283 // vibrato functions are not used in any keyboards
    284 
    285 void audio_on_user(void);
    286 void audio_off_user(void);