aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Humbert <jack.humb@gmail.com>2017-06-27 16:57:19 -0400
committerGitHub <noreply@github.com>2017-06-27 16:57:19 -0400
commita52836e669cb41b39629076f4cd6f1d4d8a949b1 (patch)
tree915375855e8a8d667f2071391dc56a390db52854
parent4ba9438c3f71e6ea3433be4f9e1a28d36471d247 (diff)
parent7d28a417c035b66529d7f6d49479fe4c22737134 (diff)
downloadqmk_firmware-a52836e669cb41b39629076f4cd6f1d4d8a949b1.tar.gz
qmk_firmware-a52836e669cb41b39629076f4cd6f1d4d8a949b1.zip
Merge pull request #1445 from qmk/dual_audio
Allow for duophony (C6 and B5) and B5 audio
-rw-r--r--docs/modding_your_keyboard.md2
-rw-r--r--keyboards/atomic/keymaps/pvc/config.h2
-rw-r--r--keyboards/planck/config.h1
-rw-r--r--keyboards/preonic/config.h1
-rw-r--r--quantum/audio/audio.c317
-rw-r--r--quantum/audio/voices.c2
6 files changed, 303 insertions, 22 deletions
diff --git a/docs/modding_your_keyboard.md b/docs/modding_your_keyboard.md
index 2429570f5..44e6e6e72 100644
--- a/docs/modding_your_keyboard.md
+++ b/docs/modding_your_keyboard.md
@@ -1,7 +1,7 @@
1 1
2## Audio output from a speaker 2## Audio output from a speaker
3 3
4Your keyboard can make sounds! If you've got a Planck, Preonic, or basically any keyboard that allows access to the C6 port, you can hook up a simple speaker and make it beep. You can use those beeps to indicate layer transitions, modifiers, special keys, or just to play some funky 8bit tunes. 4Your keyboard can make sounds! If you've got a Planck, Preonic, or basically any keyboard that allows access to the C6 or B5 port (`#define C6_AUDIO` and `#define B5_AUDIO`), you can hook up a simple speaker and make it beep. You can use those beeps to indicate layer transitions, modifiers, special keys, or just to play some funky 8bit tunes.
5 5
6The audio code lives in [quantum/audio/audio.h](https://github.com/qmk/qmk_firmware/blob/master/quantum/audio/audio.h) and in the other files in the audio directory. It's enabled by default on the Planck [stock keymap](https://github.com/qmk/qmk_firmware/blob/master/keyboards/planck/keymaps/default/keymap.c). Here are the important bits: 6The audio code lives in [quantum/audio/audio.h](https://github.com/qmk/qmk_firmware/blob/master/quantum/audio/audio.h) and in the other files in the audio directory. It's enabled by default on the Planck [stock keymap](https://github.com/qmk/qmk_firmware/blob/master/keyboards/planck/keymaps/default/keymap.c). Here are the important bits:
7 7
diff --git a/keyboards/atomic/keymaps/pvc/config.h b/keyboards/atomic/keymaps/pvc/config.h
index 18a7253f2..ea5821ee7 100644
--- a/keyboards/atomic/keymaps/pvc/config.h
+++ b/keyboards/atomic/keymaps/pvc/config.h
@@ -49,6 +49,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
49#define BACKLIGHT_PIN B7 49#define BACKLIGHT_PIN B7
50#define BACKLIGHT_BREATHING 50#define BACKLIGHT_BREATHING
51 51
52#define C6_AUDIO
53
52/* COL2ROW or ROW2COL */ 54/* COL2ROW or ROW2COL */
53#define DIODE_DIRECTION COL2ROW 55#define DIODE_DIRECTION COL2ROW
54 56
diff --git a/keyboards/planck/config.h b/keyboards/planck/config.h
index 5cf96bb88..c86f8491e 100644
--- a/keyboards/planck/config.h
+++ b/keyboards/planck/config.h
@@ -37,6 +37,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
37#define UNUSED_PINS 37#define UNUSED_PINS
38 38
39#define AUDIO_VOICES 39#define AUDIO_VOICES
40#define C6_AUDIO
40 41
41#define BACKLIGHT_PIN B7 42#define BACKLIGHT_PIN B7
42 43
diff --git a/keyboards/preonic/config.h b/keyboards/preonic/config.h
index 239c29ebf..8aa88b7f0 100644
--- a/keyboards/preonic/config.h
+++ b/keyboards/preonic/config.h
@@ -38,6 +38,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
38#define UNUSED_PINS 38#define UNUSED_PINS
39 39
40#define AUDIO_VOICES 40#define AUDIO_VOICES
41#define C6_AUDIO
41 42
42#define BACKLIGHT_PIN B7 43#define BACKLIGHT_PIN B7
43 44
diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c
index 597073611..c924f2bd5 100644
--- a/quantum/audio/audio.c
+++ b/quantum/audio/audio.c
@@ -33,17 +33,41 @@
33 33
34// TIMSK3 - Timer/Counter #3 Interrupt Mask Register 34// TIMSK3 - Timer/Counter #3 Interrupt Mask Register
35// Turn on/off 3A interputs, stopping/enabling the ISR calls 35// Turn on/off 3A interputs, stopping/enabling the ISR calls
36#define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) 36#ifdef C6_AUDIO
37#define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) 37 #define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A)
38 #define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A)
39#endif
40
41#ifdef B5_AUDIO
42 #define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A)
43 #define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A)
44#endif
38 45
39// TCCR3A: Timer/Counter #3 Control Register 46// TCCR3A: Timer/Counter #3 Control Register
40// Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 47// Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6
41#define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); 48
42#define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); 49#ifdef C6_AUDIO
50 #define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1);
51 #define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0));
52#endif
53
54#ifdef B5_AUDIO
55 #define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1);
56 #define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0));
57#endif
43 58
44// Fast PWM Mode Controls 59// Fast PWM Mode Controls
45#define TIMER_3_PERIOD ICR3 60
46#define TIMER_3_DUTY_CYCLE OCR3A 61#ifdef C6_AUDIO
62 #define TIMER_3_PERIOD ICR3
63 #define TIMER_3_DUTY_CYCLE OCR3A
64#endif
65
66#ifdef B5_AUDIO
67 #define TIMER_1_PERIOD ICR1
68 #define TIMER_1_DUTY_CYCLE OCR1A
69#endif
70
47 71
48// ----------------------------------------------------------------------------- 72// -----------------------------------------------------------------------------
49 73
@@ -51,6 +75,7 @@
51int voices = 0; 75int voices = 0;
52int voice_place = 0; 76int voice_place = 0;
53float frequency = 0; 77float frequency = 0;
78float frequency_alt = 0;
54int volume = 0; 79int volume = 0;
55long position = 0; 80long position = 0;
56 81
@@ -105,16 +130,43 @@ void audio_init()
105 audio_config.raw = eeconfig_read_audio(); 130 audio_config.raw = eeconfig_read_audio();
106 131
107 // Set port PC6 (OC3A and /OC4A) as output 132 // Set port PC6 (OC3A and /OC4A) as output
108 DDRC |= _BV(PORTC6);
109 133
110 DISABLE_AUDIO_COUNTER_3_ISR; 134 #ifdef C6_AUDIO
135 DDRC |= _BV(PORTC6);
136 #else
137 DDRC |= _BV(PORTC6);
138 PORTC &= ~_BV(PORTC6);
139 #endif
140
141 #ifdef B5_AUDIO
142 DDRB |= _BV(PORTB5);
143 #else
144 DDRB |= _BV(PORTB5);
145 PORTB &= ~_BV(PORTB5);
146 #endif
147
148 #ifdef C6_AUDIO
149 DISABLE_AUDIO_COUNTER_3_ISR;
150 #endif
151
152 #ifdef B5_AUDIO
153 DISABLE_AUDIO_COUNTER_1_ISR;
154 #endif
111 155
112 // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers 156 // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers
113 // Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6 157 // Compare Output Mode (COM3An) = 0b00 = Normal port operation, OC3A disconnected from PC6
114 // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14 (Period = ICR3, Duty Cycle = OCR3A) 158 // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14 (Period = ICR3, Duty Cycle = OCR3A)
115 // Clock Select (CS3n) = 0b010 = Clock / 8 159 // Clock Select (CS3n) = 0b010 = Clock / 8
116 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); 160
117 TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); 161 #ifdef C6_AUDIO
162 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30);
163 TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30);
164 #endif
165
166 #ifdef B5_AUDIO
167 TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10);
168 TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
169 #endif
118 170
119 audio_initialized = true; 171 audio_initialized = true;
120} 172}
@@ -128,12 +180,21 @@ void stop_all_notes()
128 } 180 }
129 voices = 0; 181 voices = 0;
130 182
131 DISABLE_AUDIO_COUNTER_3_ISR; 183
132 DISABLE_AUDIO_COUNTER_3_OUTPUT; 184 #ifdef C6_AUDIO
185 DISABLE_AUDIO_COUNTER_3_ISR;
186 DISABLE_AUDIO_COUNTER_3_OUTPUT;
187 #endif
188
189 #ifdef B5_AUDIO
190 DISABLE_AUDIO_COUNTER_1_ISR;
191 DISABLE_AUDIO_COUNTER_1_OUTPUT;
192 #endif
133 193
134 playing_notes = false; 194 playing_notes = false;
135 playing_note = false; 195 playing_note = false;
136 frequency = 0; 196 frequency = 0;
197 frequency_alt = 0;
137 volume = 0; 198 volume = 0;
138 199
139 for (uint8_t i = 0; i < 8; i++) 200 for (uint8_t i = 0; i < 8; i++)
@@ -171,9 +232,16 @@ void stop_note(float freq)
171 voice_place = 0; 232 voice_place = 0;
172 } 233 }
173 if (voices == 0) { 234 if (voices == 0) {
174 DISABLE_AUDIO_COUNTER_3_ISR; 235 #ifdef C6_AUDIO
175 DISABLE_AUDIO_COUNTER_3_OUTPUT; 236 DISABLE_AUDIO_COUNTER_3_ISR;
237 DISABLE_AUDIO_COUNTER_3_OUTPUT;
238 #endif
239 #ifdef B5_AUDIO
240 DISABLE_AUDIO_COUNTER_1_ISR;
241 DISABLE_AUDIO_COUNTER_1_OUTPUT;
242 #endif
176 frequency = 0; 243 frequency = 0;
244 frequency_alt = 0;
177 volume = 0; 245 volume = 0;
178 playing_note = false; 246 playing_note = false;
179 } 247 }
@@ -200,12 +268,56 @@ float vibrato(float average_freq) {
200 268
201#endif 269#endif
202 270
271#ifdef C6_AUDIO
203ISR(TIMER3_COMPA_vect) 272ISR(TIMER3_COMPA_vect)
204{ 273{
205 float freq; 274 float freq;
206 275
207 if (playing_note) { 276 if (playing_note) {
208 if (voices > 0) { 277 if (voices > 0) {
278
279 #ifdef B5_AUDIO
280 float freq_alt = 0;
281 if (voices > 1) {
282 if (polyphony_rate == 0) {
283 if (glissando) {
284 if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440/frequencies[voices - 2]/12/2)) {
285 frequency_alt = frequency_alt * pow(2, 440/frequency_alt/12/2);
286 } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440/frequencies[voices - 2]/12/2)) {
287 frequency_alt = frequency_alt * pow(2, -440/frequency_alt/12/2);
288 } else {
289 frequency_alt = frequencies[voices - 2];
290 }
291 } else {
292 frequency_alt = frequencies[voices - 2];
293 }
294
295 #ifdef VIBRATO_ENABLE
296 if (vibrato_strength > 0) {
297 freq_alt = vibrato(frequency_alt);
298 } else {
299 freq_alt = frequency_alt;
300 }
301 #else
302 freq_alt = frequency_alt;
303 #endif
304 }
305
306 if (envelope_index < 65535) {
307 envelope_index++;
308 }
309
310 freq_alt = voice_envelope(freq_alt);
311
312 if (freq_alt < 30.517578125) {
313 freq_alt = 30.52;
314 }
315
316 TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq_alt * CPU_PRESCALER));
317 TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq_alt * CPU_PRESCALER)) * note_timbre);
318 }
319 #endif
320
209 if (polyphony_rate > 0) { 321 if (polyphony_rate > 0) {
210 if (voices > 1) { 322 if (voices > 1) {
211 voice_place %= voices; 323 voice_place %= voices;
@@ -328,6 +440,140 @@ ISR(TIMER3_COMPA_vect)
328 playing_note = false; 440 playing_note = false;
329 } 441 }
330} 442}
443#endif
444
445#ifdef B5_AUDIO
446ISR(TIMER1_COMPA_vect)
447{
448 #if defined(B5_AUDIO) && !defined(C6_AUDIO)
449 float freq = 0;
450
451 if (playing_note) {
452 if (voices > 0) {
453 if (polyphony_rate > 0) {
454 if (voices > 1) {
455 voice_place %= voices;
456 if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) {
457 voice_place = (voice_place + 1) % voices;
458 place = 0.0;
459 }
460 }
461
462 #ifdef VIBRATO_ENABLE
463 if (vibrato_strength > 0) {
464 freq = vibrato(frequencies[voice_place]);
465 } else {
466 freq = frequencies[voice_place];
467 }
468 #else
469 freq = frequencies[voice_place];
470 #endif
471 } else {
472 if (glissando) {
473 if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440/frequencies[voices - 1]/12/2)) {
474 frequency = frequency * pow(2, 440/frequency/12/2);
475 } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440/frequencies[voices - 1]/12/2)) {
476 frequency = frequency * pow(2, -440/frequency/12/2);
477 } else {
478 frequency = frequencies[voices - 1];
479 }
480 } else {
481 frequency = frequencies[voices - 1];
482 }
483
484 #ifdef VIBRATO_ENABLE
485 if (vibrato_strength > 0) {
486 freq = vibrato(frequency);
487 } else {
488 freq = frequency;
489 }
490 #else
491 freq = frequency;
492 #endif
493 }
494
495 if (envelope_index < 65535) {
496 envelope_index++;
497 }
498
499 freq = voice_envelope(freq);
500
501 if (freq < 30.517578125) {
502 freq = 30.52;
503 }
504
505 TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
506 TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre);
507 }
508 }
509
510 if (playing_notes) {
511 if (note_frequency > 0) {
512 #ifdef VIBRATO_ENABLE
513 if (vibrato_strength > 0) {
514 freq = vibrato(note_frequency);
515 } else {
516 freq = note_frequency;
517 }
518 #else
519 freq = note_frequency;
520 #endif
521
522 if (envelope_index < 65535) {
523 envelope_index++;
524 }
525 freq = voice_envelope(freq);
526
527 TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));
528 TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre);
529 } else {
530 TIMER_1_PERIOD = 0;
531 TIMER_1_DUTY_CYCLE = 0;
532 }
533
534 note_position++;
535 bool end_of_note = false;
536 if (TIMER_1_PERIOD > 0) {
537 end_of_note = (note_position >= (note_length / TIMER_1_PERIOD * 0xFFFF));
538 } else {
539 end_of_note = (note_position >= (note_length * 0x7FF));
540 }
541
542 if (end_of_note) {
543 current_note++;
544 if (current_note >= notes_count) {
545 if (notes_repeat) {
546 current_note = 0;
547 } else {
548 DISABLE_AUDIO_COUNTER_1_ISR;
549 DISABLE_AUDIO_COUNTER_1_OUTPUT;
550 playing_notes = false;
551 return;
552 }
553 }
554 if (!note_resting && (notes_rest > 0)) {
555 note_resting = true;
556 note_frequency = 0;
557 note_length = notes_rest;
558 current_note--;
559 } else {
560 note_resting = false;
561 envelope_index = 0;
562 note_frequency = (*notes_pointer)[current_note][0];
563 note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100);
564 }
565
566 note_position = 0;
567 }
568 }
569
570 if (!audio_config.enable) {
571 playing_notes = false;
572 playing_note = false;
573 }
574#endif
575}
576#endif
331 577
332void play_note(float freq, int vol) { 578void play_note(float freq, int vol) {
333 579
@@ -338,7 +584,12 @@ void play_note(float freq, int vol) {
338 } 584 }
339 585
340 if (audio_config.enable && voices < 8) { 586 if (audio_config.enable && voices < 8) {
341 DISABLE_AUDIO_COUNTER_3_ISR; 587 #ifdef C6_AUDIO
588 DISABLE_AUDIO_COUNTER_3_ISR;
589 #endif
590 #ifdef B5_AUDIO
591 DISABLE_AUDIO_COUNTER_1_ISR;
592 #endif
342 593
343 // Cancel notes if notes are playing 594 // Cancel notes if notes are playing
344 if (playing_notes) 595 if (playing_notes)
@@ -354,8 +605,21 @@ void play_note(float freq, int vol) {
354 voices++; 605 voices++;
355 } 606 }
356 607
357 ENABLE_AUDIO_COUNTER_3_ISR; 608 #ifdef C6_AUDIO
358 ENABLE_AUDIO_COUNTER_3_OUTPUT; 609 ENABLE_AUDIO_COUNTER_3_ISR;
610 ENABLE_AUDIO_COUNTER_3_OUTPUT;
611 #endif
612 #ifdef B5_AUDIO
613 #ifdef C6_AUDIO
614 if (voices > 1) {
615 ENABLE_AUDIO_COUNTER_1_ISR;
616 ENABLE_AUDIO_COUNTER_1_OUTPUT;
617 }
618 #else
619 ENABLE_AUDIO_COUNTER_1_ISR;
620 ENABLE_AUDIO_COUNTER_1_OUTPUT;
621 #endif
622 #endif
359 } 623 }
360 624
361} 625}
@@ -369,7 +633,12 @@ void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat, float n_rest)
369 633
370 if (audio_config.enable) { 634 if (audio_config.enable) {
371 635
372 DISABLE_AUDIO_COUNTER_3_ISR; 636 #ifdef C6_AUDIO
637 DISABLE_AUDIO_COUNTER_3_ISR;
638 #endif
639 #ifdef B5_AUDIO
640 DISABLE_AUDIO_COUNTER_1_ISR;
641 #endif
373 642
374 // Cancel note if a note is playing 643 // Cancel note if a note is playing
375 if (playing_note) 644 if (playing_note)
@@ -390,8 +659,16 @@ void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat, float n_rest)
390 note_position = 0; 659 note_position = 0;
391 660
392 661
393 ENABLE_AUDIO_COUNTER_3_ISR; 662 #ifdef C6_AUDIO
394 ENABLE_AUDIO_COUNTER_3_OUTPUT; 663 ENABLE_AUDIO_COUNTER_3_ISR;
664 ENABLE_AUDIO_COUNTER_3_OUTPUT;
665 #endif
666 #ifdef B5_AUDIO
667 #ifndef C6_AUDIO
668 ENABLE_AUDIO_COUNTER_1_ISR;
669 ENABLE_AUDIO_COUNTER_1_OUTPUT;
670 #endif
671 #endif
395 } 672 }
396 673
397} 674}
diff --git a/quantum/audio/voices.c b/quantum/audio/voices.c
index 54ebd423b..94147ccb6 100644
--- a/quantum/audio/voices.c
+++ b/quantum/audio/voices.c
@@ -44,7 +44,7 @@ float voice_envelope(float frequency) {
44 44
45 switch (voice) { 45 switch (voice) {
46 case default_voice: 46 case default_voice:
47 glissando = true; 47 glissando = false;
48 note_timbre = TIMBRE_50; 48 note_timbre = TIMBRE_50;
49 polyphony_rate = 0; 49 polyphony_rate = 0;
50 break; 50 break;