Hi folks,
I'm working on a project using my Arduino Uno to read in MIDI via the serial connection and play a polyphonic tone that mimics the MIDI input. The problem I am having is this: It only seems to be able to play three notes (one after another), and then it stops playing altogether. The arduino is still receiving data (I see the RX LED on the board flashing) but it doesn't play any tones. Polyphonic sounds get very distorted and don't stop when I release the keys. Hoping someone can give me a hand here. I have excluded some of the code as it is irrelevant to the MIDI--only mode 2 (MIDI) is giving me problems.
#include <avr/pgmspace.h>
#include "Tone.h"
// Output for PWM
int PWMPin = 6;
// External PWM enable
int PWMPower = 5;
// 7-segment pins
int A = 13;
int B = 12;
int C = 11;
int D = 10;
int E = 9;
int F = 8;
int G = 7;
volatile int mode; // 0 = Output Off, 1 = Manual PWM, 2 = PC MIDI, 3 = External MIDI
//Audio output pins
//Using Analog pins simply becaues they were open
int ch0out = A0;
int ch1out = A1;
int ch2out = A2;
int ch3out = A3;
int ch4out = A4;
int ch5out = A5;
//MIDI notes
Tone ch0;
Tone ch1;
Tone ch2;
Tone ch3;
Tone ch4;
Tone ch5;
// Number of modes
#define modeNum 3
// Highest channel (0-15)
// Will eventually use an array to keep track
// of discrete channels we want to play
#define channelNum 5
// midi commands
#define MIDI_CMD_NOTE_OFF 0x80
#define MIDI_CMD_NOTE_ON 0x90
/* Probably don't need these for my project
#define MIDI_CMD_KEY_PRESSURE 0xA0
#define MIDI_CMD_CONTROLLER_CHANGE 0xB0
#define MIDI_CMD_PROGRAM_CHANGE 0xC0
#define MIDI_CMD_CHANNEL_PRESSURE 0xD0
#define MIDI_CMD_PITCH_BEND 0xE0
*/
// a dummy "ignore" state for commands which
// we wish to ignore.
#define MIDI_IGNORE 0x00
// midi "state" - which data byte we are receiving
#define MIDI_STATE_BYTE1 0x00
#define MIDI_STATE_BYTE2 0x01
// store note frequencies
uint16_t frequency[128] PROGMEM = {8, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 19, 21,
22, 23, 24, 26, 28, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52,
55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123,
131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262,
277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554,
587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109,
1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093,
2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
4186, 4435, 4699, 4978, 5274, 5588, 5920, 5920, 6645, 7040, 7459,
7902, 8372, 8870, 9397, 9956, 10548, 11175, 11840, 12544};
void setup() {
// Power on signal to external PWM circuitry
pinMode(PWMPower, OUTPUT);
// 7-segment display pins
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(E, OUTPUT);
pinMode(F, OUTPUT);
pinMode(G, OUTPUT);
// Interrupt to watch for mode-change button press
attachInterrupt(0, changeMode, RISING);
mode = 0;
displayMode();
// Make sure power signal to external PWM is off
digitalWrite(PWMPower, LOW);
}
void loop () {
if (mode == 0) {
}
else if (mode == 1) {
}
/*************************************************
** This is where the MIDI signal is processed **
*************************************************/
else if (mode == 2) {
static byte note; // Variable to store note number
static byte state; // Variable to store state (Byte 1, Byte 2)
static byte lastByte; // Variable to store previous byte value
static int cmd = MIDI_IGNORE; // Variable to store current command
static int channel; // Variable to store current channel number
while (Serial.available()) {
// read the incoming byte:
byte thisByte = Serial.read(); //Read incoming byte
if (thisByte & 0b10000000) { //Is this a command byte?
if (channel <= channelNum) { // Is this one of our channels?
cmd = thisByte & 0xF0; //Save the command
channel = thisByte & 0x0F; //Save the channel number
} else {
cmd = MIDI_IGNORE; // Otherwise, ignore message
}
state = MIDI_STATE_BYTE1; // Prepare for Byte 1 (note)
} else if ((state == MIDI_STATE_BYTE1) && (cmd != MIDI_IGNORE)) { // Is this data Byte 1?
if ( cmd==MIDI_CMD_NOTE_OFF ) { //If command is for NOTE OFF
state = MIDI_STATE_BYTE2; // expect to receive a velocity byte
} else if ( cmd == MIDI_CMD_NOTE_ON ) { // If command is for NOTE ON
if (channel == 0x00) ch0.begin(ch0out); // prepare to play note on specified channel
else if (channel == 0x01) ch1.begin(ch1out);
else if (channel == 0x02) ch2.begin(ch2out);
else if (channel == 0x03) ch3.begin(ch3out);
else if (channel == 0x04) ch4.begin(ch4out);
else if (channel == 0x05) ch5.begin(ch5out);
}
lastByte=thisByte; // save the current note
state = MIDI_STATE_BYTE2; // prepare for Byte2 (velocity)
} else if ((state == MIDI_STATE_BYTE2) && (cmd != MIDI_IGNORE)) { // Is this data for Byte 2 (velocity)?
if (cmd == MIDI_CMD_NOTE_OFF) {
if (channel == 0x00) ch0.stop(); // turn off note on current channel
else if (channel == 0x01) ch1.stop();
else if (channel == 0x02) ch2.stop();
else if (channel == 0x03) ch3.stop();
else if (channel == 0x04) ch4.stop();
else if (channel == 0x05) ch5.stop();
} else if (cmd == MIDI_CMD_NOTE_ON) {
if (thisByte != 0) { // if we're actually playing a note
note = lastByte; // get note number
if (channel == 0x00) ch0.play((unsigned int)pgm_read_word(&frequency[note])); // Play specified note on specified channel
else if (channel == 0x01) ch1.play((unsigned int)pgm_read_word(&frequency[note]));
else if (channel == 0x02) ch2.play((unsigned int)pgm_read_word(&frequency[note]));
else if (channel == 0x03) ch3.play((unsigned int)pgm_read_word(&frequency[note]));
else if (channel == 0x04) ch4.play((unsigned int)pgm_read_word(&frequency[note]));
else if (channel == 0x05) ch5.play((unsigned int)pgm_read_word(&frequency[note]));
}
}
state = MIDI_STATE_BYTE1; // message data complete
}
}
}
/**********************************
** End MIDI Processing Section **
**********************************/
}
void changeMode(){
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
// If interrupts come faster than 200ms, assume it's a bounce and ignore
if (interrupt_time - last_interrupt_time > 200)
{
Serial.end();
mode++;
if (mode > modeNum) {
mode = 0;
}
}
last_interrupt_time = interrupt_time;
if (mode == 0) {
stopAllChannels();
} else if (mode == 1) {
stopAllChannels();
digitalWrite(PWMPower, HIGH);
} else if (mode == 2) {
digitalWrite(PWMPower, LOW);
Serial.begin(57600);
} else if (mode == 3) {
stopAllChannels();
digitalWrite(PWMPower, LOW);
}
displayMode();
}
//For stopping all MIDI channels
void stopAllChannels() {
ch0.stop();
ch1.stop();
ch2.stop();
ch3.stop();
ch4.stop();
ch5.stop();
}
void displayMode(){
if (mode == 0) {
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, LOW);
}
if (mode == 1) {
digitalWrite(A, LOW);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, LOW);
}
else if (mode == 2) {
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, LOW);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
}
else if (mode == 3) {
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
}
}
If anyone sees any clear issues, please let me know!
Thanks,
Matt