String comparison in array of structs does not work (or const initialization?)

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?

		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); // ****

you set allfinished to 1 ==> true in Arduinish

then you test if (allfinished) - which is true - and thus the code calls break which exits the while loop.

see it now?

Yes, but then I check whether the channel is done playing and if any is not, allFinished will end up being 0.

Found out why it didn't work: I ran out of RAM... sigh...

move some strings to progmem or recode the music strings.

Now you have a string "G4" which takes 3 bytes.
if you split a byte in 2 groups of 4 bits you can get 16 x 16 values => 16 different notes in 16 octaves.
[0123][4567] By coding in HExadecimal the first digit is the note and the second the octave, it becomes allmost readable

0x00 = C0
0x11 = C#1
0x22 = D2
0x75 = G5
etc

it even leaves open 0xC0 - 0xFF for special codes.

Hmmm... True, it would consume a lot less space if I just hex-encode it. Maybe just use the MIDI note number to begin with...

Thanks.

Maybe just use the MIDI note number to begin with...

Your project does MIDI so why not :slight_smile: