summaryrefslogtreecommitdiffstats
path: root/quantum/audio
diff options
context:
space:
mode:
authorRyan Ascheman <rascheman@groupon.com>2016-10-18 12:42:02 -0700
committerRyan Ascheman <rascheman@groupon.com>2016-10-18 12:42:02 -0700
commit55b8b8477cc6aee82dfe6792eea4e589cac433d5 (patch)
treece5bfbd1b0ee59dbffdc2044bcf90c89614392ed /quantum/audio
parentd1c70328f8d8ded6ce1e5422b468fc41ef315e7d (diff)
parent04df74f6360464661bcc1e6794e9fd3549084390 (diff)
Merge remote-tracking branch 'upstream/master'
* upstream/master: (1239 commits) Update ez.c removes planck/rev3 temporarily Move hand_swap_config to ez.c, removes error for infinity Update Makefile ergodox: Update algernon's keymap to v1.9 Added VS Code dir to .gitignore Support the Pegasus Hoof controller. [Jack & Erez] Simplifies and documents TO add readme use wait_ms instead of _delay_ms add messenger init keymap Add example keymap Adding whiskey_tango_foxtrot_capslock ergodox keymap Unicode map framework. Allow unicode up to 0xFFFFF using separate mapping table CIE 1931 dim curve Apply the dim curve to the RGB output Update the Cluecard readme files Tune snake and knight intervals for Cluecard Tunable RGB light intervals ...
Diffstat (limited to 'quantum/audio')
-rw-r--r--quantum/audio/audio.c477
-rw-r--r--quantum/audio/audio.h91
-rw-r--r--quantum/audio/audio_pwm.c643
-rw-r--r--quantum/audio/luts.c382
-rw-r--r--quantum/audio/luts.h15
-rw-r--r--quantum/audio/musical_notes.h217
-rw-r--r--quantum/audio/song_list.h125
-rw-r--r--quantum/audio/voices.c165
-rw-r--r--quantum/audio/voices.h31
-rw-r--r--quantum/audio/wave.h265
10 files changed, 2411 insertions, 0 deletions
diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c
new file mode 100644
index 0000000000..ead5fbf3e9
--- /dev/null
+++ b/quantum/audio/audio.c
@@ -0,0 +1,477 @@
+#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 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));
+
+// Fast PWM Mode Controls
+#define TIMER_3_PERIOD ICR3
+#define TIMER_3_DUTY_CYCLE OCR3A
+
+// -----------------------------------------------------------------------------
+
+
+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;
+
+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;
+
+uint8_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();
+
+ // 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);
+
+ audio_initialized = true;
+}
+
+void stop_all_notes()
+{
+ if (!audio_initialized) {
+ audio_init();
+ }
+ voices = 0;
+
+ DISABLE_AUDIO_COUNTER_3_ISR;
+ DISABLE_AUDIO_COUNTER_3_OUTPUT;
+
+ 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();
+ }
+ 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) {
+ DISABLE_AUDIO_COUNTER_3_ISR;
+ DISABLE_AUDIO_COUNTER_3_OUTPUT;
+ 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)
+{
+ float freq;
+
+ 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 (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 {
+ 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) {
+ end_of_note = (note_position >= (note_length / TIMER_3_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 {
+ DISABLE_AUDIO_COUNTER_3_ISR;
+ DISABLE_AUDIO_COUNTER_3_OUTPUT;
+ 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;
+ 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) {
+
+ 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;
+
+ if (freq > 0) {
+ frequencies[voices] = freq;
+ volumes[voices] = vol;
+ voices++;
+ }
+
+ ENABLE_AUDIO_COUNTER_3_ISR;
+ ENABLE_AUDIO_COUNTER_3_OUTPUT;
+ }
+
+}
+
+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;
+
+ note_frequency = (*notes_pointer)[current_note][0];
+ note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100);
+ note_position = 0;
+
+
+ ENABLE_AUDIO_COUNTER_3_ISR;
+ ENABLE_AUDIO_COUNTER_3_OUTPUT;
+ }
+
+}
+
+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) {
+ 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.h b/quantum/audio/audio.h
new file mode 100644
index 0000000000..47f326ea0a
--- /dev/null
+++ b/quantum/audio/audio.h
@@ -0,0 +1,91 @@
+#ifndef AUDIO_H
+#define AUDIO_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include "musical_notes.h"
+#include "song_list.h"
+#include "voices.h"
+#include "quantum.h"
+
+// Largely untested PWM audio mode (doesn't sound as good)
+// #define PWM_AUDIO
+
+// #define VIBRATO_ENABLE
+
+// Enable vibrato strength/amplitude - slows down ISR too much
+// #define VIBRATO_STRENGTH_ENABLE
+
+typedef union {
+ uint8_t raw;
+ struct {
+ bool enable :1;
+ uint8_t level :7;
+ };
+} audio_config_t;
+
+bool is_audio_on(void);
+void audio_toggle(void);
+void audio_on(void);
+void audio_off(void);
+
+// Vibrato rate functions
+
+#ifdef VIBRATO_ENABLE
+
+void set_vibrato_rate(float rate);
+void increase_vibrato_rate(float change);
+void decrease_vibrato_rate(float change);
+
+#ifdef VIBRATO_STRENGTH_ENABLE
+
+void set_vibrato_strength(float strength);
+void increase_vibrato_strength(float change);
+void decrease_vibrato_strength(float change);
+
+#endif
+
+#endif
+
+// Polyphony 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 set_timbre(float timbre);
+void set_tempo(uint8_t tempo);
+
+void increase_tempo(uint8_t tempo_change);
+void decrease_tempo(uint8_t tempo_change);
+
+void audio_init(void);
+
+#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, float n_rest);
+
+#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), }
+
+// 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_NOTE_ARRAY(note_array, note_repeat, note_rest_style) play_notes(&note_array, NOTE_ARRAY_SIZE((note_array)), (note_repeat), (note_rest_style));
+
+
+bool is_playing_notes(void);
+
+#endif \ No newline at end of file
diff --git a/quantum/audio/audio_pwm.c b/quantum/audio/audio_pwm.c
new file mode 100644
index 0000000000..f820eec1be
--- /dev/null
+++ b/quantum/audio/audio_pwm.c
@@ -0,0 +1,643 @@
+#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;
+
+uint8_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
+ }
+
+}