Arduino + Musical Instrument Shield

Hello all!

I am using the Musical Instrument Shield from Sparkfun to build a piano that uses infrared emitter/detectors, and I haven't been able to make my project polyphonic. Since all 8 notes are on the same channel, when I play multiple notes they get arpeggiated, instead of ringing simultaneously. Does anyone know a way to fix this? I assume that I need to have each IR receiver on a different channel but I've tried several different things with no luck. Any suggestions are greatly appreciated!

Here's a simple version of my code:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(2, 3); //Soft TX on 3 (we don't use RX in this code?)

int irEmitters[] = {46,47,48,49,50,51,52,53}; // irEmitters: digital pins 46-52           
int inputPins[] = {A8,A9,A10,A11,A12,A13,A14,A15}; // irDetectors: analog pins 8-15
int notes[] = {60,62,64,65,67,69,71,72};          // notes: 60 = Middle C (C MAJOR SCALE)
int pinCount = 8;
int inputState[8];

int threshold = 500;



byte resetMIDI = 4; //Pin 4 is ties to VS1053 Reset line.


void setup() {
  
  //Setup soft serial for MIDI control
  mySerial.begin(31250);
  
  for (int i=0; i<pinCount;i++) {
    
    // Sets ir Emitters as OUTPUTS, and turns them on.
    pinMode(irEmitters[i], OUTPUT);
    digitalWrite(irEmitters[i], HIGH);
    
    
    pinMode(inputPins[i], OUTPUT); // pinMode(outputPins, OUTPUT) <- Turns inputs into outputs
    pinMode(inputPins[i], INPUT); 
  }
  

  //Reset the VS1053
  digitalWrite(resetMIDI, LOW);
  delay(100);
  digitalWrite(resetMIDI, HIGH);
  delay(100);
 
  talkMIDI(0xB0, 0x07, 126);          //0xB0 is channel message, set channel volume to near max (127)
  talkMIDI(0xB0, 0, 0x79);           //Default bank GM1
  talkMIDI(0xC0, 108, 0);           //INSTRUMENT 108
}

void loop() {

  for (int i=0; i<pinCount; i++) {
    
    inputState[i]= analogRead(inputPins[i]);
    delay(1);
    
    if (inputState[i] > threshold) {
      noteOn(0,notes[i],100);
      
      delay(200);
      
      if (inputState[i] > threshold) { 
      noteOff(0,notes[i],100);
      }
    }
  }
} 
  
  

//Send a MIDI note-on message.  Like Pressing a piano key
void noteOn(byte channel, byte note, byte attack_velocity) {
  talkMIDI( (0x90 | channel), note, attack_velocity);
}

//Send a MIDI note-off message.  Like releasing a piano key
void noteOff(byte channel, byte note, byte release_velocity) {
  talkMIDI( (0x80 | channel), note, release_velocity);
}

//Plays 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) {
  mySerial.write(cmd);
  mySerial.write(data1);

  //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 || (cmd & 0xF0) == 0xE0) // Wee bug seen in Sparkfun Comments page.
    mySerial.write(data2);
}

cooper_hu:
Since all 8 notes are on the same channel, when I play multiple notes they get arpeggiated, instead of ringing simultaneously.

Are you referring to SparkFun Music Instrument Shield - DEV-10587 - SparkFun Electronics

If yes, that page clearly says 'The shield is also capable of playing several tones simultaneously (maximum polyphony of up to 31 sounds!)'

When you say 'my code,' are you writing something from scratch, or editing the sample software? (I haven't looked at it, don't have the board, just trying to point you in the right directions to debug this.)

If scratch code then I'd go back to the demo code, make sure it works, and try to modify it again in the same way, but in smaller steps per change, to find out exactly where the poly stops working.

Unless it never worked, which is a different issue. If the sample software is -supposed- to poly, but doesn't, then you may have a version issue.

Did I ask enough questions to answer yours? :wink: :smiley:

Yupp that's the one I am referring to! (Should have included a link :-P)

I know it is capable of doing it but I haven't been able to!

The demo code only uses one MIDI channel, and when you run the sketch it only rings out a string of notes.
In other words, the demo code doesn't include any example that uses polyphonic notes.

Might be a good idea to post a diagram of your physical layout along with model no of the IR components.

In the meantime, have you screened the different IR emitter/receiver pairs from each other and ambient light?

As per the previous post, get the polyphonic stuff working standalone before introducing complexity with other things like IR.

Since all 8 notes are on the same channel, when I play multiple notes they get arpeggiated, instead of ringing simultaneously. Does anyone know a way to fix this?

Yes. Simply write your code without using the delay function.
Once a note is played your code can't play anything else until the delay is over.

Look for the input going off to generate the note off event, or use the technique of a state machine as shown in the blink without delay example, to generate your own note off event after a fixed interval. BUT do not use delay.

Yes. Simply write your code without using the delay function.
Once a note is played your code can't play anything else until the delay is over
Look for the input going off to generate the note off event, or use the technique of a state machine as shown in the blink without delay example, to generate your own note off event after a fixed interval. BUT do not use delay.

When I don't use the delay function the note continues to play as long as I have my finger blocking the infrared detector, and it's makes a weird distorted noise. But I understand what you mean by not using the delay.

Could u explain a little more clear how I would use the technique in blink without delay in my code? I appreciate the help all!

I believe I need to essentially use an if/then statement to say "Read the input (of the infrared detector) if the detector reads over the threshold, do the noteOn() function then do the noteOff() function. But I haven't been able to figure out how to do that.

So as of now the code works by: read the input, if the detector reads over the threshold, do noteOn(), delay(I set the amount of time on a pot that ranges from 50milliseconds to 750, re-read the input, if it's still over the threshold turn off.

Is there some sort of if/then equivalent in the Arduino programming language?

It's very easy.
It's called timeslice or slotting.

You have a counter that increments on each iteration in the loop and turn on or off the LED based on the counter value.

Could u explain a little more clear how I would use the technique in blink without delay in my code?

When a 'note on' occurs you make a note of the time when it is due to be turned off, this is given by the value in the millis() function plus the number of milliseconds you want the note to last.
Then at the start of each loop you compare the current value from millis with the note off time. If millis is greater, and your note off time is not zero, then you send a note off MIDI message and zero the note off time.

Write the code to do this for one note and get it working. Then extend it by using an array of note off times to handle the number of polyphonic notes you need.

It's very easy.
It's called timeslice or slotting

No it is not and it is not the way to tackle this problem.

Grumpy_Mike:

Could u explain a little more clear how I would use the technique in blink without delay in my code?

When a 'note on' occurs you make a note of the time when it is due to be turned off, this is given by the value in the millis() function plus the number of milliseconds you want the note to last.
Then at the start of each loop you compare the current value from millis with the note off time. If millis is greater, and your note off time is not zero, then you send a note off MIDI message and zero the note off time.

Write the code to do this for one note and get it working. Then extend it by using an array of note off times to handle the number of polyphonic notes you need.

It's very easy.
It's called timeslice or slotting

No it is not and it is not the way to tackle this problem.

Go ahead and give him the code.

That sure helps.

I don't know why the Arduino team put the delay function into the library in the first place?
Your not supposed to write blocking code.

All those CPU cycles wasted.

Go ahead and give him the code.
That sure helps.

Helps who?
Not him, if he doesn't write the code he will not learn anything and always be reliant on asking others. Implementing some one else's method in your code is just a step away from implementing your own. Which is a step closer to being an expert.

janost:
I don't know why the Arduino team put the delay function into the library in the first place?
Your not supposed to write blocking code.

All those CPU cycles wasted.

Delay can be a useful function if you know when to use it. However it is seductive for beginners who over use it and do not use it appropriately, or appreciate its limitations.

Grumpy_Mike:

Go ahead and give him the code.
That sure helps.

Helps who?
Not him, if he doesn't write the code he will not learn anything and always be reliant on asking others. Implementing some one else's method in your code is just a step away from implementing your own. Which is a step closer to being an expert.

That's what I meant.
Tell him how it should be done.

And leave the grumpiness off for a bit.

Helps who?
Not him, if he doesn't write the code he will not learn anything and always be reliant on asking others. Implementing some one else's method in your code is just a step away from implementing your own. Which is a step closer to being an expert.

I've only been using Arduino for about 3 months, and the only previous coding experience I have is html/css -- I'm having a hard time understanding what you're suggesting I do because I'm still not familiar with all the terms. If you have any spare time do you think you could show me the code you're talking about? I'd really appreciate it. Thus far, the way I've been understanding Arduino code is by dissecting other things I've found to make sense of them. I think it would help me a lot if you could share the code, but if you don't have time I understand.

You will learn a lot more if you write or at least start to write your own code. You then post the code you have and explain what it will do / will not do and we take it from there.

Thus far, the way I've been understanding Arduino code is by dissecting other things I've found to make sense of them

And I have said that the blink without delay example is a good starting point.
another good page to read about this technique is one I wrote:-
http://www.thebox.myzen.co.uk/Tutorial/State_Machine.html

because I'm still not familiar with all the terms.

That's fine, if you don't know then just ask, no extra charge.

but if you don't have time I understand.

It is not a matter of not having the time, this way is going to take me longer but it is a much better outcome for you.
If I just posted a lump of code then you would have to extract the relevant things from it and incorporate it into your own code. That is getting two steps removed.
The other thing is that the code I have written is for a book and I am sure the publishers are not going to want me to post that before it is published so it is embargoed for the time being. But that doesn't stop me teaching you how to write your own. :wink:

By nonblocking (no delay) I mean you have to find a way to delay (or not do anything) based on time.

That means one of 2 things, either you do your tasks based on time (a counter) or you do it with an ISR.

The first option works like you take action at intervals.
Or timestamped as Grumpy said.

Timeslice your main loop by checking events.
Either something received or time lapsed.

Don't block it by waiting.
If it's not time to do something, go ahead and do something else.

If you block your code by using a delay it cant check or do multiple tasks.
It's sort of multitasking, do all at once.

Awesome I'm going to read this now, thanks! I'll check back in a bit!

We who are more used to writing realtime code dont't use delays in our code.
We want all the CPU cycles we can get.

You do it like this:

You have 2 kind of events, either a key played or time lapsed.
Increment a counter, if time reached or event happened, do your stuff.

By nonblocking you just increment the counter and do any if count or event.

It's easy.

I am so confused!

I now understand how millis() is used to get the LED blinking on it's own cycle, and how it's the better way to go about it since delay() is a blocking function. But I still don't understand how to use it…

void loop {

noteOn(0,note1,100);
//millis() in here?
noteOff(0,note,100);

}

???

When you do note on you record a time, zero or anything and clear a counter.
When the counter reach a value you turn the note of.

This isn't easy to explain, give me some help any one?