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