]> 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 5edcccdbe10784813b88ceb8183a9ffdc9f5a93a..c92cb537319dcc3c301b999b4a7e24d8fb4993e2 100644 (file)
@@ -4,7 +4,7 @@
 #include <avr/pgmspace.h>
 #include <avr/interrupt.h>
 #include <avr/io.h>
-
+#include "print.h"
 #include "audio.h"
 #include "keymap_common.h"
 
@@ -12,6 +12,8 @@
 
 #define PI 3.14159265
 
+#define CPU_PRESCALER 8
+
 // #define PWM_AUDIO
 
 #ifdef PWM_AUDIO
@@ -32,8 +34,6 @@ int voice_place = 0;
 double frequency = 0;
 int volume = 0;
 long position = 0;
-int duty_place = 1;
-int duty_counter = 0;
 
 double frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0};
 int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
@@ -49,17 +49,28 @@ 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;
 
@@ -119,6 +130,9 @@ void stop_note(double freq) {
         voices--;
         if (voices < 0)
             voices = 0;
+        if (voice_place >= voices) {
+            voice_place = 0;
+        }
         if (voices == 0) {
             #ifdef PWM_AUDIO
                 TIMSK3 &= ~_BV(OCIE3A);
@@ -129,25 +143,6 @@ void stop_note(double freq) {
             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;
-                }
-                sliding = false;
-            }
-            frequency = freq;
-            volume = vol;
         }
     }
 }
@@ -175,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
@@ -189,6 +184,11 @@ 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) {
@@ -196,14 +196,14 @@ ISR(TIMER3_COMPA_vect) {
             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;
 
@@ -240,22 +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
-                voice_place %= voices;
-                if (place > (frequencies[voice_place] / 500)) {
-                    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 * duty_place; // Set compare to half the period
-                place++;
-                // if (duty_counter > (frequencies[voice_place] / 500)) {
-                //     duty_place = (duty_place % 3) + 1;
-                //     duty_counter = 0;
-                // }
-                // duty_counter++;
+                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
     }
@@ -281,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;
@@ -292,13 +320,13 @@ ISR(TIMER3_COMPA_vect) {
 
         note_position++;
         bool end_of_note = false;
-        if (ICR3 > 0) 
+        if (ICR3 > 0)
             end_of_note = (note_position >= (note_length / ICR3 * 0xFFFF));
-        else 
+        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 {
@@ -312,13 +340,21 @@ 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;
         }
 
@@ -330,26 +366,28 @@ ISR(TIMER3_COMPA_vect) {
     }
 }
 
-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;
 
@@ -360,6 +398,7 @@ if (audio_config.enable) {
         TIMSK3 |= _BV(OCIE3A);
         TCCR3A |= _BV(COM3A1);
     #endif
+
 }
 
 }
@@ -387,6 +426,7 @@ 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;
@@ -394,23 +434,8 @@ if (audio_config.enable && voices < 8) {
         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++;
     }
 
@@ -423,4 +448,45 @@ if (audio_config.enable && voices < 8) {
 
 }
 
-}
\ 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()
+{
+}
+//------------------------------------------------------------------------------