Make the incomplete MIDIToneGenerator project work with up to three notes, using...
authorDean Camera <dean@fourwalledcubicle.com>
Thu, 25 Nov 2010 02:46:35 +0000 (02:46 +0000)
committerDean Camera <dean@fourwalledcubicle.com>
Thu, 25 Nov 2010 02:46:35 +0000 (02:46 +0000)
Projects/Incomplete/MIDIToneGenerator/MIDIToneGenerator.c
Projects/Incomplete/MIDIToneGenerator/MIDIToneGenerator.h

index 7c5fc9a..c8e06be 100644 (file)
@@ -78,14 +78,7 @@ const uint8_t SineTable[256] =
 };\r
 \r
 /** Array of structures describing each note being generated */\r
 };\r
 \r
 /** Array of structures describing each note being generated */\r
-struct\r
-{\r
-       uint8_t  Pitch;\r
-       uint32_t TableIncrement;\r
-       uint32_t TablePosition;\r
-} NoteData[3];\r
-\r
-uint8_t PhaseCounter;\r
+DDSNoteData NoteData[MAX_SIMULTANEOUS_NOTES];\r
 \r
 /** Main program entry point. This routine contains the overall program flow, including initial\r
  *  setup of all components and the main program loop.\r
 \r
 /** Main program entry point. This routine contains the overall program flow, including initial\r
  *  setup of all components and the main program loop.\r
@@ -104,19 +97,34 @@ int main(void)
                {\r
                        if ((ReceivedMIDIEvent.Command == (MIDI_COMMAND_NOTE_ON >> 4)) && ((ReceivedMIDIEvent.Data1 & 0x0F) == 0))\r
                        {\r
                {\r
                        if ((ReceivedMIDIEvent.Command == (MIDI_COMMAND_NOTE_ON >> 4)) && ((ReceivedMIDIEvent.Data1 & 0x0F) == 0))\r
                        {\r
+                               DDSNoteData* LRUNoteStruct = &NoteData[0];\r
+                       \r
                                /* Find a free entry in the note table to use for the note being turned on */\r
                                for (uint8_t i = 0; i < MAX_SIMULTANEOUS_NOTES; i++)\r
                                {\r
                                /* Find a free entry in the note table to use for the note being turned on */\r
                                for (uint8_t i = 0; i < MAX_SIMULTANEOUS_NOTES; i++)\r
                                {\r
+                                       /* Check if the note is unused */\r
                                        if (!(NoteData[i].Pitch))\r
                                        {\r
                                        if (!(NoteData[i].Pitch))\r
                                        {\r
-                                               NoteData[i].Pitch          = ReceivedMIDIEvent.Data2;\r
-                                               NoteData[i].TableIncrement = (uint32_t)(BASE_INCREMENT * SCALE_FACTOR) +\r
-                                                                            ((uint32_t)(BASE_INCREMENT * NOTE_OCTIVE_RATIO * SCALE_FACTOR) *\r
-                                                                             (ReceivedMIDIEvent.Data2 - BASE_PITCH_INDEX));\r
-                                               NoteData[i].TablePosition  = 0;\r
+                                               /* If a note is unused, it's age is essentially infinite - always prefer unused not entries */\r
+                                               LRUNoteStruct = &NoteData[i];\r
                                                break;\r
                                        }\r
                                                break;\r
                                        }\r
+                                       else if (NoteData[i].LRUAge > LRUNoteStruct->LRUAge)\r
+                                       {\r
+                                               /* If an older entry that the current entry has been found, prefer overwriting that one */                                              \r
+                                               LRUNoteStruct = &NoteData[i];\r
+                                       }\r
+                                       \r
+                                       NoteData[i].LRUAge++;\r
                                }\r
                                }\r
+                               \r
+                               /* Update the oldest note entry with the new note data and reset its age */\r
+                               LRUNoteStruct->Pitch          = ReceivedMIDIEvent.Data2;\r
+                               LRUNoteStruct->TableIncrement = (uint32_t)(BASE_INCREMENT * SCALE_FACTOR) +\r
+                                                                        ((uint32_t)(BASE_INCREMENT * NOTE_OCTIVE_RATIO * SCALE_FACTOR) *\r
+                                                                         (ReceivedMIDIEvent.Data2 - BASE_PITCH_INDEX));\r
+                               LRUNoteStruct->TablePosition  = 0;\r
+                               LRUNoteStruct->LRUAge         = 0;\r
 \r
                                /* Turn on indicator LED to indicate note generation activity */\r
                                LEDs_SetAllLEDs(LEDS_LED1);\r
 \r
                                /* Turn on indicator LED to indicate note generation activity */\r
                                LEDs_SetAllLEDs(LEDS_LED1);\r
@@ -129,11 +137,9 @@ int main(void)
                                for (uint8_t i = 0; i < MAX_SIMULTANEOUS_NOTES; i++)\r
                                {\r
                                        if (NoteData[i].Pitch == ReceivedMIDIEvent.Data2)\r
                                for (uint8_t i = 0; i < MAX_SIMULTANEOUS_NOTES; i++)\r
                                {\r
                                        if (NoteData[i].Pitch == ReceivedMIDIEvent.Data2)\r
-                                       {\r
-                                               NoteData[i].Pitch = 0;\r
-                                               FoundActiveNote      = true;\r
-                                               break;\r
-                                       }\r
+                                         NoteData[i].Pitch = 0;\r
+                                       else if (NoteData[i].Pitch)\r
+                                         FoundActiveNote   = true;\r
                                }\r
                                \r
                                /* If all notes off, turn off the indicator LED */\r
                                }\r
                                \r
                                /* If all notes off, turn off the indicator LED */\r
@@ -182,15 +188,15 @@ void SetupHardware(void)
 \r
        /* Sample reload timer initialization */\r
        TIMSK0  = (1 << OCIE0A);\r
 \r
        /* Sample reload timer initialization */\r
        TIMSK0  = (1 << OCIE0A);\r
-       OCR0A   = 255;\r
+       OCR0A   = (VIRTUAL_SAMPLE_TABLE_SIZE / 8);\r
        TCCR0A  = (1 << WGM01);  // CTC mode\r
        TCCR0A  = (1 << WGM01);  // CTC mode\r
-       TCCR0B  = (1 << CS00);   // Fcpu speed\r
+       TCCR0B  = (1 << CS01);   // Fcpu/8 speed\r
 \r
        /* Set speaker as output */\r
        DDRC |= (1 << 6);\r
 \r
        /* PWM speaker timer initialization */\r
 \r
        /* Set speaker as output */\r
        DDRC |= (1 << 6);\r
 \r
        /* PWM speaker timer initialization */\r
-       TCCR3A  = ((1 << WGM30) | (1 << COM3A1) | (1 << COM3A0)); // Set on match, clear on TOP\r
+       TCCR3A  = ((1 << WGM31) | (1 << COM3A1) | (1 << COM3A0)); // Set on match, clear on TOP\r
        TCCR3B  = ((1 << WGM32) | (1 << CS30));  // Fast 8-Bit PWM, Fcpu speed\r
 }\r
 \r
        TCCR3B  = ((1 << WGM32) | (1 << CS30));  // Fast 8-Bit PWM, Fcpu speed\r
 }\r
 \r
@@ -208,6 +214,10 @@ void EVENT_USB_Device_Disconnect(void)
 {\r
        LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);\r
 \r
 {\r
        LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY);\r
 \r
+       /* Disable any notes currently being played */\r
+       for (uint8_t i = 0; i < MAX_SIMULTANEOUS_NOTES; i++)\r
+         NoteData[i].Pitch = 0;\r
+\r
        /* Set speaker as input to reduce current draw */\r
        DDRC &= ~(1 << 6);\r
 }\r
        /* Set speaker as input to reduce current draw */\r
        DDRC &= ~(1 << 6);\r
 }\r
index c064437..2936ca2 100644 (file)
                /** LED mask for the library LED driver, to indicate that an error has occurred in the USB interface. */\r
                #define LEDMASK_USB_ERROR        (LEDS_LED1 | LEDS_LED3)\r
                \r
                /** LED mask for the library LED driver, to indicate that an error has occurred in the USB interface. */\r
                #define LEDMASK_USB_ERROR        (LEDS_LED1 | LEDS_LED3)\r
                \r
-               #define SCALE_FACTOR             65536\r
-               #define BASE_FREQUENCY           27.5\r
-               #define NOTE_OCTIVE_RATIO        1.05946\r
-               #define BASE_PITCH_INDEX         21\r
-               #define MAX_SIMULTANEOUS_NOTES   3\r
+               /** Scale factor used to convert the floating point frequencies and ratios into a fixed point number */\r
+               #define SCALE_FACTOR               65536\r
                \r
                \r
-               #define BASE_INCREMENT           (((F_CPU / 255 / 2) / BASE_FREQUENCY))\r
+               /** Base (lowest) allowable MIDI note frequency */\r
+               #define BASE_FREQUENCY             27.5\r
                \r
                \r
+               /** Ratio between each note in an octave */\r
+               #define NOTE_OCTIVE_RATIO          1.05946\r
+               \r
+               /** Lowest valid MIDI pitch index */\r
+               #define BASE_PITCH_INDEX           21\r
+               \r
+               /** Maximum number of MIDI notes that can be played simultaneously */\r
+               #define MAX_SIMULTANEOUS_NOTES     3\r
+               \r
+               /** Number of samples in the virtual sample table (can be expanded to lower maximum frequency, but allow for\r
+                *  more simultaneous notes due to the reduced amount of processing time needed when the samples are spaced out)\r
+                */\r
+               #define VIRTUAL_SAMPLE_TABLE_SIZE  512\r
+               \r
+               /** Sample table increments per period for the base MIDI note frequency */\r
+               #define BASE_INCREMENT             (((F_CPU / VIRTUAL_SAMPLE_TABLE_SIZE / 2) / BASE_FREQUENCY))\r
+\r
+       /* Type Defines: */\r
+               typedef struct\r
+               {\r
+                       uint8_t  LRUAge;\r
+                       uint8_t  Pitch;\r
+                       uint32_t TableIncrement;\r
+                       uint32_t TablePosition;\r
+               } DDSNoteData;          \r
+\r
        /* Function Prototypes: */\r
                void SetupHardware(void);\r
                \r
        /* Function Prototypes: */\r
                void SetupHardware(void);\r
                \r