]> git.donarmstrong.com Git - qmk_firmware.git/blobdiff - quantum/audio.c
Merge branch 'master' of https://github.com/jackhumbert/tmk_keyboard
[qmk_firmware.git] / quantum / audio.c
index f29d941d7cce0be321a060b38ae2278806a9223c..c92cb537319dcc3c301b999b4a7e24d8fb4993e2 100644 (file)
@@ -4,12 +4,16 @@
 #include <avr/pgmspace.h>
 #include <avr/interrupt.h>
 #include <avr/io.h>
-
+#include "print.h"
 #include "audio.h"
 #include "keymap_common.h"
 
+#include "eeconfig.h"
+
 #define PI 3.14159265
 
+#define CPU_PRESCALER 8
+
 // #define PWM_AUDIO
 
 #ifdef PWM_AUDIO
@@ -45,17 +49,47 @@ uint16_t place_int = 0;
 bool repeat = true;
 uint8_t * sample;
 uint16_t sample_length = 0;
-
+double freq = 0;
 
 bool notes = false;
 bool note = false;
 float note_frequency = 0;
 float note_length = 0;
+float note_tempo = TEMPO_DEFAULT;
+float note_timbre = TIMBRE_DEFAULT;
 uint16_t note_position = 0;
 float (* notes_pointer)[][2];
-uint8_t notes_length;
+uint8_t notes_count;
 bool notes_repeat;
+float notes_rest;
+bool note_resting = false;
+
 uint8_t current_note = 0;
+uint8_t rest_counter = 0;
+
+uint8_t vibrato_counter = 0;
+float vibrato_strength = 0;
+
+float polyphony_rate = 0;
+
+audio_config_t audio_config;
+
+
+void audio_toggle(void) {
+    audio_config.enable ^= 1;
+    eeconfig_write_audio(audio_config.raw);
+}
+
+void audio_on(void) {
+    audio_config.enable = 1;
+    eeconfig_write_audio(audio_config.raw);
+}
+
+void audio_off(void) {
+    audio_config.enable = 0;
+    eeconfig_write_audio(audio_config.raw);
+}
+
 
 void stop_all_notes() {
     voices = 0;
@@ -77,58 +111,50 @@ void stop_all_notes() {
 }
 
 void stop_note(double freq) {
-    #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;
-            }
-        }
-    }
-    voices--;
-    if (voices < 0)
-        voices = 0;
-    if (voices == 0) {
+    if (note) {
         #ifdef PWM_AUDIO
-            TIMSK3 &= ~_BV(OCIE3A);
-        #else
-            TIMSK3 &= ~_BV(OCIE3A);
-            TCCR3A &= ~_BV(COM3A1);
+            freq = freq / SAMPLE_RATE;
         #endif
-        frequency = 0;
-        volume = 0;
-        note = false;
-    } else {
-        double freq = frequencies[voices - 1];
-        int vol = volumes[voices - 1];
-        double starting_f = frequency;
-        if (frequency < freq) {
-            sliding = true;
-            for (double f = starting_f; f <= freq; f += ((freq - starting_f) / 2000.0)) {
-                frequency = f;
-            }
-            sliding = false;
-        } else if (frequency > freq) {
-            sliding = true;
-            for (double f = starting_f; f >= freq; f -= ((starting_f - freq) / 2000.0)) {
-                frequency = f;
+        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;
+                }
             }
-            sliding = false;
         }
-        frequency = freq;
-        volume = vol;
+        voices--;
+        if (voices < 0)
+            voices = 0;
+        if (voice_place >= voices) {
+            voice_place = 0;
+        }
+        if (voices == 0) {
+            #ifdef PWM_AUDIO
+                TIMSK3 &= ~_BV(OCIE3A);
+            #else
+                TIMSK3 &= ~_BV(OCIE3A);
+                TCCR3A &= ~_BV(COM3A1);
+            #endif
+            frequency = 0;
+            volume = 0;
+            note = false;
+        }
     }
 }
 
 void init_notes() {
 
+    /* check signature */
+    if (!eeconfig_is_enabled()) {
+        eeconfig_init();
+    }
+    audio_config.raw = eeconfig_read_audio();
+
     #ifdef PWM_AUDIO
         PLLFRQ = _BV(PDIV2);
         PLLCSR = _BV(PLLE);
@@ -144,7 +170,7 @@ void init_notes() {
         DDRC |= _BV(PORTC6);
 
         TIMSK3 &= ~_BV(OCIE3A); // 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
@@ -158,22 +184,26 @@ void init_notes() {
     #endif
 }
 
+float mod(float a, int b)
+{
+    float r = fmod(a, b);
+    return r < 0 ? r + b : r;
+}
 
 ISR(TIMER3_COMPA_vect) {
-
     if (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;
 
@@ -210,16 +240,41 @@ ISR(TIMER3_COMPA_vect) {
                 OCR4A = sum;
             }
         #else
-            if (frequency > 0) {
-                // ICR3 = (int)(((double)F_CPU) / frequency); // Set max to the period
-                // OCR3A = (int)(((double)F_CPU) / frequency) >> 1; // Set compare to half the period
-                if (place > 10) {
-                    voice_place = (voice_place + 1) % voices;
-                    place = 0.0;
+            if (voices > 0) {
+                if (false && polyphony_rate > 0) {                
+                    if (voices > 1) {
+                        voice_place %= voices;
+                        if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER / voices)) {
+                            voice_place = (voice_place + 1) % voices;
+                            place = 0.0;
+                        }
+                    }
+                    if (vibrato_strength > 0) {
+                        freq = frequencies[voice_place] * pow(VIBRATO_LUT[(int)vibrato_counter], vibrato_strength);
+                        vibrato_counter = mod((vibrato_counter + 1), VIBRATO_LUT_LENGTH);
+                    } else {
+                        freq = frequencies[voice_place];
+                    } 
+                } else {
+                    if (frequency != 0) {
+                        if (frequency < frequencies[voices - 1]) {
+                            frequency = frequency * pow(2, 440/frequencies[voices - 1]/12/4);
+                        } else if (frequency > frequencies[voices - 1]) {
+                            frequency = frequency * pow(2, -440/frequencies[voices - 1]/12/4);
+                        }
+                    } else {
+                        frequency = frequencies[voices - 1];
+                    }
+
+                    if (false && vibrato_strength > 0) {
+                        freq = frequency * pow(VIBRATO_LUT[(int)vibrato_counter], vibrato_strength);
+                        vibrato_counter = mod((vibrato_counter + 1 + 440/frequencies[voices - 1]), VIBRATO_LUT_LENGTH);
+                    } else {
+                        freq = frequency;
+                    } 
                 }
-                ICR3 = (int)(((double)F_CPU) / frequencies[voice_place]); // Set max to the period
-                OCR3A = (int)(((double)F_CPU) / frequencies[voice_place]) >> 1; // Set compare to half the period
-                place++;
+                ICR3 = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period
+                OCR3A = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period
             }
         #endif
     }
@@ -245,8 +300,17 @@ ISR(TIMER3_COMPA_vect) {
                 place -= SINE_LENGTH;
         #else
             if (note_frequency > 0) {
-                ICR3 = (int)(((double)F_CPU) / note_frequency); // Set max to the period
-                OCR3A = (int)(((double)F_CPU) / note_frequency) >> 1; // Set compare to half the period
+                float freq;
+
+                if (false && vibrato_strength > 0) {
+                    freq = note_frequency * pow(VIBRATO_LUT[(int)vibrato_counter], vibrato_strength);
+                    vibrato_counter = mod((vibrato_counter + 1), VIBRATO_LUT_LENGTH);
+                } else {
+                    freq = note_frequency;
+                }
+
+                ICR3 = (int)(((double)F_CPU) / (freq * CPU_PRESCALER)); // Set max to the period
+                OCR3A = (int)((((double)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); // Set compare to half the period
             } else {
                 ICR3 = 0;
                 OCR3A = 0;
@@ -255,9 +319,14 @@ ISR(TIMER3_COMPA_vect) {
 
 
         note_position++;
-        if (note_position >= note_length) {
+        bool end_of_note = false;
+        if (ICR3 > 0)
+            end_of_note = (note_position >= (note_length / ICR3 * 0xFFFF));
+        else
+            end_of_note = (note_position >= (note_length * 0x7FF));
+        if (end_of_note) {
             current_note++;
-            if (current_note >= notes_length) {
+            if (current_note >= notes_count) {
                 if (notes_repeat) {
                     current_note = 0;
                 } else {
@@ -271,37 +340,54 @@ ISR(TIMER3_COMPA_vect) {
                     return;
                 }
             }
-            #ifdef PWM_AUDIO
-                note_frequency = (*notes_pointer)[current_note][0] / SAMPLE_RATE;
-                note_length = (*notes_pointer)[current_note][1];
-            #else
-                note_frequency = (*notes_pointer)[current_note][0];
-                note_length = (*notes_pointer)[current_note][1] / 4;
-            #endif
+            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] * (note_tempo / 100);
+                #else
+                    note_frequency = (*notes_pointer)[current_note][0];
+                    note_length = ((*notes_pointer)[current_note][1] / 4) * (note_tempo / 100);
+                #endif
+            }
             note_position = 0;
         }
 
     }
 
+    if (!audio_config.enable) {
+        notes = false;
+        note = false;
+    }
 }
 
-void play_notes(float (*np)[][2], uint8_t n_length, bool n_repeat) {
+void play_notes(float (*np)[][2], uint8_t n_count, bool n_repeat, float n_rest) {
+
+if (audio_config.enable) {
+
+       // Cancel note if a note is playing
     if (note)
         stop_all_notes();
     notes = true;
 
     notes_pointer = np;
-    notes_length = n_length;
+    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];
+        note_length = (*notes_pointer)[current_note][1] * (note_tempo / 100);
     #else
         note_frequency = (*notes_pointer)[current_note][0];
-        note_length = (*notes_pointer)[current_note][1] / 4;
+        note_length = ((*notes_pointer)[current_note][1] / 4) * (note_tempo / 100);
     #endif
     note_position = 0;
 
@@ -312,9 +398,15 @@ void play_notes(float (*np)[][2], uint8_t n_length, bool n_repeat) {
         TIMSK3 |= _BV(OCIE3A);
         TCCR3A |= _BV(COM3A1);
     #endif
+
+}
+
 }
 
 void play_sample(uint8_t * s, uint16_t l, bool r) {
+
+if (audio_config.enable) {
+
     stop_all_notes();
     place_int = 0;
     sample = s;
@@ -325,9 +417,16 @@ void play_sample(uint8_t * s, uint16_t l, bool r) {
         TIMSK3 |= _BV(OCIE3A);
     #else
     #endif
+
+}
+
 }
 
 void play_note(double freq, int vol) {
+
+if (audio_config.enable && voices < 8) {
+
+    // Cancel notes if notes are playing
     if (notes)
         stop_all_notes();
     note = true;
@@ -335,23 +434,8 @@ void play_note(double freq, int vol) {
         freq = freq / SAMPLE_RATE;
     #endif
     if (freq > 0) {
-        if (frequency != 0) {
-            double starting_f = frequency;
-            if (frequency < freq) {
-                for (double f = starting_f; f <= freq; f += ((freq - starting_f) / 2000.0)) {   
-                    frequency = f;
-                }
-            } else if (frequency > freq) {
-                for (double f = starting_f; f >= freq; f -= ((starting_f - freq) / 2000.0)) {
-                    frequency = f;
-                }
-            }
-        }
-        frequency = freq;
-        volume = vol;
-
-        frequencies[voices] = frequency;
-        volumes[voices] = volume;
+        frequencies[voices] = freq;
+        volumes[voices] = vol;
         voices++;
     }
 
@@ -362,4 +446,47 @@ void play_note(double freq, int vol) {
         TCCR3A |= _BV(COM3A1);
     #endif
 
-}
\ No newline at end of file
+}
+
+}
+
+void set_timbre(float timbre)
+{
+       note_timbre = timbre;
+}
+
+void set_tempo(float tempo)
+{
+       note_tempo = tempo;
+}
+
+void decrease_tempo(uint8_t tempo_change)
+{
+       note_tempo += (float) tempo_change;
+}
+
+void increase_tempo(uint8_t tempo_change)
+{
+       if (note_tempo - (float) tempo_change < 10)
+               {
+                       note_tempo = 10;
+               }
+       else
+               {
+               note_tempo -= (float) 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()
+{
+}
+//------------------------------------------------------------------------------