Commit fd77bf5c authored by Dean Camera's avatar Dean Camera
Browse files

Make the incomplete MIDIToneGenerator project work with up to three notes,...

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.
parent 857a8267
......@@ -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);
}
......
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment