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