};\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
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
+ /* Check if the note is unused */\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
+ 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
+ /* 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
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
/* 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
- 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
- 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
{\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
/** 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
- #define BASE_INCREMENT (((F_CPU / 255 / 2) / BASE_FREQUENCY))\r
+ /** Base (lowest) allowable MIDI note frequency */\r
+ #define BASE_FREQUENCY 27.5\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