Hi,
when trying to get a MIDI shield to play Axel F, I ran into a strange bug and even after trying to isolate it as far down as I can, I can't find it... it's either very subtle or I'm blind.
So maybe someone else can shed a light on it:
The code follows, although it has been simplified down to the very essentials for reproducing the bug.
#include <NewSoftSerial.h>
#include <string.h>
NewSoftSerial mySerial(2, 3); //Soft TX on 3, we don't use RX in this code
struct Place {
const char* note;
int durationDenominator;
int durationNominator;
};
struct Place sheet[][200] = {
{{NULL}, {"G4"}, {"EOF"}},
{{NULL}, {"E4"}, {"EOF"}},
{{NULL}, {NULL}, {"EOF"}},
{{"E4"}, {NULL}, {"EOF"}},
};
#define SHEET_CHANNEL_COUNT 4
//(sizeof(sheet) / sizeof(sheet[0]))
static int placeIndexes[SHEET_CHANNEL_COUNT] = {-1, -1, -1, -1};
static int placeDurations[SHEET_CHANNEL_COUNT] = {0, 0, 0, 0}; /* front, that is */
byte resetMIDI = 4; //Tied to VS1053 Reset line
byte ledPin = 13; //MIDI traffic indicator
// Play a MIDI note. Doesn't check to see that cmd is greater than 127, or that data values are less than 127
void talkMIDI(byte cmd, byte data1, byte data2) {
digitalWrite(ledPin, HIGH);
mySerial.print(cmd, BYTE);
mySerial.print(data1, BYTE);
//Some commands only have one data byte. All cmds less than 0xBn have 2 data bytes
//(sort of: http://253.ccarh.org/handout/midiprotocol/)
if( (cmd & 0xF0) <= 0xB0)
mySerial.print(data2, BYTE);
mySerial.flush();
digitalWrite(ledPin, LOW);
}
// channel ranges from 0-15
void noteOn(byte channel, byte note, byte attack_velocity) {
talkMIDI( (0x90 | channel), note, attack_velocity);
}
void setup() {
Serial.begin(57600);
// Setup soft serial for MIDI control
mySerial.begin(31250);
// Reset the VS1053
pinMode(resetMIDI, OUTPUT);
digitalWrite(resetMIDI, LOW);
delay(100);
digitalWrite(resetMIDI, HIGH);
delay(100);
talkMIDI(0xB0, 0x07, 120); //0xB0 is channel message, set channel volume to near max
talkMIDI(0xB0, 0, 0x00); //Default bank GM1
talkMIDI(0xC0, 2/*instrument*/, 0); //Set instrument number. 0xC0 is a 1 data byte command
}
int channelDoneP(int channel) {
Serial.println(placeIndexes[channel], DEC);
if(placeIndexes[channel] == -1)
return(0);
if(sheet[0][0].note) {
/*if(sheet[0][0].note[0] == 'E')
if(sheet[0][0].note[1] == 'O')
if(sheet[0][0].note[2] == 'F')*/
return(1); // XXX
}
return(0);
}
void loop() {
for(int channel = 0; channel < SHEET_CHANNEL_COUNT; ++channel) {
placeDurations[channel] = 0;
placeIndexes[channel] = -1;
placeMidiNotes[channel] = 31;
}
while(1) {
int minDuration = 99999;
int allFinished = 1;
for(int channel = 0; channel < SHEET_CHANNEL_COUNT; ++channel) {
if(!channelDoneP(channel)) {
if(placeDurations[channel] < minDuration)
minDuration = placeDurations[channel];
allFinished = 0;
}
}
if(allFinished)
break;
noteOn(3, 70, 60); // ****
delay(500);
continue;
}
}
So what happens is this:
I compile and upload the code using Arduino 0022 to an Arduino Uno.
Then the line marked with // **** is never reached.
If I change the line marked with // XXX to return(0), the line marked with // **** is reached.
Note that I checked whether the line is reached or not by listening whether a MIDI note is playing... (I can try with a LED later...)
I also checked whether it works when I do
noteOn(3, 70, 60); // ****
delay(500);
at the beginning of loop(): yes, it does.
I also ported the code to the PC (replacing noteOn by dummy code) and there // **** is reached regardless.
Why?