Go Down

Topic: Sending multiple MIDI notes simultaneously (Read 175 times) previous topic - next topic

SoupBowlNinja

Hi,

Complete novice when it comes to Arduino and programming. I picked up an Uno last Friday with a view to making some weird MIDI instruments and so far I've managed to get it to switch a note on with one button and hold that note until I turn it off with another button.

What I've been trying to figure out how to do is get it to play three notes simultaneously with one button and then hold them until I switch those notes off again with the same button.

The next step from there would be to map those chords to a pot, then a joystick etc etc.

Here's the code I'm using. Any help would be massively massively appreciated because I have no knowledge of programming whatsoever and I'm finding the process really difficult and frustrating. Basically assume that I have the same level of intelligence as a 4 year old child and go from there!

Code: [Select]
#include <MIDI.h>
 int velocity = 100;//velocity of MIDI notes, must be between 0 and 127
 int noteON = 144;//144 = 10010000 in binary, note on command
 int noteOFF = 128;
 int buttonBpin = 8;
 int buttonApin = 9;

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
  pinMode(buttonBpin, INPUT_PULLUP);
  pinMode(buttonApin, INPUT_PULLUP);

}

void loop()


{
   if (digitalRead(buttonApin) == LOW)
  for (int note =50;note=50;note++) {//note 50
    MIDImessage(noteON, note, velocity);//turn note on
    delay (200); //wait 200ms
    break; }

   if (digitalRead(buttonBpin) == LOW)
   for (int note =50;note=50;note++) {//note 50
    MIDImessage(noteOFF, note, 0); //turn note off
    delay (200); //wait 200ms
  break;}
 
}

//send MIDI message
void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
  Serial.write(command);//send note on or note off command
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}



slipstick

Interesting code. Why do you include MIDI.h and then not use any of it's facilities but hand-code the MIDI writes over serial yourself?

Anyway, if you want 3 notes to sound just do 3 writes. MIDI is a serial protocol so the notes can't be technically simultaneous but they will be close enough (within a few thousandths of a second).

For the button do you mean to hold the button down to keep notes sounding and release to stop or press once to start the notes, release and then press again for off?

Steve

SoupBowlNinja

I think the best answer to your question Steve is that I have absolutely no idea what I'm doing so I've just been bodging bits of other people's code together and hoping for the best. I've posted my new code below, extraneous bollocks and all!

I would like to be able to press the button once to send noteON release and press again to send noteOFF.

Ultimately I'd like to be able to assign multiple chords to a range of values on a pot. So, say if the value from the pot is between 1 and 10 it would play chord X, with a value between 10 and 15 it would send the note off message, then between 15 and 25 it would play chord Y etc etc. Those wouldn't be the exact range values of course, it's MIDI so it'd be seven chords and seven dead zones over a range of 0-127. I'd also like to use push buttons to define the key of the chords but that's a few steps away yet obviously.
Long term goal is to map chords over an accellerometer/gyoscope/magnetometer and control them in 3d space using buttons to define the musical key and other parameters including modulation and pitch bend. House it in a ball with some nice LEDS to make it look pretty. I realise that's a bloody long way off though given that I'm starting from rock bottom.


Code: [Select]
#include <MIDI.h>
 int velocity = 100;//velocity of MIDI notes, must be between 0 and 127
 int noteON = 144;//144 = 10010000 in binary, note on command
 int noteOFF = 128;
 int buttonBpin = 8;
 int buttonApin = 9;

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
  pinMode(buttonBpin, INPUT_PULLUP);
  pinMode(buttonApin, INPUT_PULLUP);

}

void loop()


{
   if (digitalRead(buttonApin) == LOW) {
    MIDImessage(noteON, 50, velocity);//turn note on
    MIDImessage(noteON, 51, velocity);
    MIDImessage(noteON, 52, velocity);
    delay (200); //wait 200ms
  }

   if (digitalRead(buttonBpin) == LOW) {
    MIDImessage(noteOFF, 50, 0); //turn note off
    MIDImessage(noteOFF, 51, 0);
    MIDImessage(noteOFF, 52, 0);
    delay (200); //wait 200ms
  }


 
 
}

//send MIDI message
void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
  Serial.write(command);//send note on or note off command
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}

Grumpy_Mike

Quote
I would like to be able to press the button once to send noteON release and press again to send noteOFF.
First of all you need to detect a Chang in state of the button, that is when the button becomes pressed not that the button is pressed. Their is a change state example in the IDE in the examples section. Lots of other good stuff there as well.
Basically you compair the reading from the button with what you had last time round the loop. Combine this with what you get now.
Code: [Select]
  thisTime = digitalRead (buttonPin);
if(thisTime == LOW && lastTime == HIGH) {
// do stuff
}
lastTime == thisTime;


The && in the if statement is a logical AND coupling to two conditions.

Then set up a Boolean variable and call it notePlaying and set it to false. Then when you detect a push use this notePlaying variable in an if statement to run the turn on or turn off code. Include a statement in both sections of code to set the notePlaying variable to the correct state.

SoupBowlNinja

#4
Oct 09, 2017, 07:32 pm Last Edit: Oct 09, 2017, 08:14 pm by SoupBowlNinja
Thanks Mike. I've tried incorporating that into my code but can't figure out how. I've sort of gone off on a tangent and now have three buttons mapped to three chords. When the button(s) are pressed and held it sends 1 set of noteOns then when you release it sends 1 set of noteOFFs. Exactly the same as if one were to press and hold then release a chord on a piano. I would prefer to be able to toggle them though because that would work better with the chords mapped to a pot.

Here's my current code for the buttons:

Code: [Select]
#include <MIDI.h>
 int velocity = 100;//velocity of MIDI notes, must be between 0 and 127
 int noteON = 144;//144 = 10010000 in binary, note on command
 int noteOFF = 128;
 int buttonBpin = 8;
 int buttonApin = 9;
 int buttonCpin = 7;

// Variable for the button's current state.
// button_mode = 1 when the button is up, and 0 when the button is pressed.
// This variable is 'static' and persists because it is declared outside of a function.
int buttonAmode = 1;
int buttonBmode = 1;
int buttonCmode = 1;

void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
  pinMode(buttonBpin, INPUT_PULLUP);
  pinMode(buttonApin, INPUT_PULLUP);
  pinMode(buttonCpin, INPUT_PULLUP);

}

void loop()


{

if ((digitalRead(buttonApin) == LOW) && (buttonAmode == 1)){ //start of section
    buttonAmode = 0;
    MIDImessage(noteON, 50, velocity);//turn note on
    MIDImessage(noteON, 53, velocity);
    MIDImessage(noteON, 57, velocity);
    delay (200); //wait 200ms
}
  
  

else if ((digitalRead(buttonApin) == HIGH) && (buttonAmode == 0))
  {
  // Button was down before, but is released now. Set the button to
  // released and report that to the serial monitor.
    buttonAmode = 1; // Button is released.
    MIDImessage(noteOFF, 50, 0); //turn note off
    MIDImessage(noteOFF, 53, 0);
    MIDImessage(noteOFF, 57, 0);
    delay (200); //wait 200ms
  } //end of section

if ((digitalRead(buttonBpin) == LOW) && (buttonBmode == 1)){ //start of section
    buttonBmode = 0;
    MIDImessage(noteON, 48, velocity);//turn note on
    MIDImessage(noteON, 52, velocity);
    MIDImessage(noteON, 57, velocity);
    delay (200); //wait 200ms
}
  
  

else if ((digitalRead(buttonBpin) == HIGH) && (buttonBmode == 0))
  {
  // Button was down before, but is released now. Set the button to
  // released and report that to the serial monitor.
    buttonBmode = 1; // Button is released.
    MIDImessage(noteOFF, 48, 0); //turn note off
    MIDImessage(noteOFF, 52, 0);
    MIDImessage(noteOFF, 57, 0);
    delay (200); //wait 200ms
  } //end of section

if ((digitalRead(buttonCpin) == LOW) && (buttonCmode == 1)){ //start of section
    buttonCmode = 0;
    MIDImessage(noteON, 52, velocity);//turn note on
    MIDImessage(noteON, 55, velocity);
    MIDImessage(noteON, 59, velocity);
    delay (200); //wait 200ms
}
  
  

else if ((digitalRead(buttonCpin) == HIGH) && (buttonCmode == 0))
  {
  // Button was down before, but is released now. Set the button to
  // released and report that to the serial monitor.
    buttonCmode = 1; // Button is released.
    MIDImessage(noteOFF, 52, 0); //turn note off
    MIDImessage(noteOFF, 55, 0);
    MIDImessage(noteOFF, 59, 0);
    delay (200); //wait 200ms
  } //end of section



  
  
}



//send MIDI message
void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
  Serial.write(command);//send note on or note off command
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}




I've also been working on trying to map a chord to a pot so that it sends three noteOFF messages once when it's under 50, sends three noteON messages once between 50 and 500 and then sends three noteOFF messages once at above 500. Like the code above when you hold and release the button(s).
At the lower range it's fluctuating between noteON and noteOFF and sending them every 200ms. At the middle range it's only sending noteON but it's sending them every 200ms and the higher range is doing the same thing as the lower range.

Once I've got this working my next step is to reduce the ranges and then map seven different chords into it. Then I want to add buttons as a modifier to change the key. Then map it all again to a 4 axis joystick and so on.

Here's the code. There's probably loads of errors and stuff in there because as I said I really don't have any idea what I'm doing.

Code: [Select]
#include <MIDI.h>
 
 int velocity = 100;//velocity of MIDI notes, must be between 0 and 127
 int noteON = 144;//144 = 10010000 in binary, note on command
 int noteOFF = 128;
 int buttonBpin = 8;
 int buttonApin = 9;
 int potPin = A0;
 
void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
  pinMode(buttonBpin, INPUT_PULLUP);
  pinMode(buttonApin, INPUT_PULLUP);
  pinMode(potPin, INPUT);
}

void loop()



{  

 
  int potValue = analogRead (potPin); //check the value of the pot

  if (potValue < 50) {//if the value of the pot is under 50
    MIDImessage(noteOFF, 50, 0); //turn note off
    MIDImessage(noteOFF, 51, 0);
    MIDImessage(noteOFF, 52, 0);
    delay (200); //wait 200ms
  }


 
   if (potValue >= 50 && potValue < 500); {//if the value of the pot is between 50 and 500
    MIDImessage(noteON, 50, velocity);//turn notes on
    MIDImessage(noteON, 51, velocity);
    MIDImessage(noteON, 52, velocity);
    delay (200); //wait 200ms
  }

  if (potValue >= 500) { //if the value of the pot is greater or equal to 500
    MIDImessage(noteOFF, 50, 0); //turn note off
    MIDImessage(noteOFF, 51, 0);
    MIDImessage(noteOFF, 52, 0);
    delay (200); //wait 200ms
  }

 
 
}

//send MIDI message
void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
  Serial.write(command);//send note on or note off command
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}



I really appreciate the help so far. I'm a musician, not a coder and I need to pick this up as quickly as I can for my master's degree.

Grumpy_Mike

You have not done what I said. This is what I said, it compiles but not tested o it might not work exactly:-
Code: [Select]

#include <MIDI.h>
int velocity = 100;//velocity of MIDI notes, must be between 0 and 127
int noteON = 144;//144 = 10010000 in binary, note on command
int noteOFF = 128;
int buttonBpin = 8;
int buttonApin = 9;
int buttonCpin = 7;

// Variable for the button's current state.
// buttonState = the current state of the button
// lastButtonState = the state of the button when last read
// This variable is 'static' and persists because it is declared outside of a function.
byte butonStateA, butonStateB, butonStateC;
byte lastButonStateA, lastButonStateB, lastButonStateC;


void setup() {
  //  Set MIDI baud rate:
  Serial.begin(31250);
  pinMode(buttonBpin, INPUT_PULLUP);
  pinMode(buttonApin, INPUT_PULLUP);
  pinMode(buttonCpin, INPUT_PULLUP);
}

void loop(){
  butonStateA = digitalRead(buttonApin); // read the state only ONCE per loop
  butonStateB = digitalRead(buttonBpin);
  butonStateC = digitalRead(buttonCpin);
  if ((butonStateA == LOW) && (lastButonStateA == HIGH)) { //start of section
    MIDImessage(noteON, 50, velocity);//turn note on
    MIDImessage(noteON, 53, velocity);
    MIDImessage(noteON, 57, velocity);
    delay (200); //wait 200ms
  }
  if ((butonStateA == HIGH) && (lastButonStateA == LOW))
  {
    // Button was down before, but is released now. Set the button to
    MIDImessage(noteOFF, 50, 0); //turn note off
    MIDImessage(noteOFF, 53, 0);
    MIDImessage(noteOFF, 57, 0);
    delay (200); //wait 200ms
  } //end of section

  if ((butonStateB == LOW) && (lastButonStateB == HIGH)) { //start of section
    MIDImessage(noteON, 48, velocity);//turn note on
    MIDImessage(noteON, 52, velocity);
    MIDImessage(noteON, 57, velocity);
    delay (200); //wait 200ms
  }
 
  if ((butonStateB == HIGH) && (lastButonStateB == LOW))
  {
    // Button was down before, but is released now.
    MIDImessage(noteOFF, 48, 0); //turn note off
    MIDImessage(noteOFF, 52, 0);
    MIDImessage(noteOFF, 57, 0);
    delay (200); //wait 200ms
  } //end of section

  if ((butonStateC == LOW) && (lastButonStateC == HIGH)) { //start of section
    MIDImessage(noteON, 52, velocity);//turn note on
    MIDImessage(noteON, 55, velocity);
    MIDImessage(noteON, 59, velocity);
    delay (200); //wait 200ms
  }

if ((butonStateC == HIGH) && (lastButonStateC == LOW))
  {
    // Button was down before, but is released now.
    MIDImessage(noteOFF, 52, 0); //turn note off
    MIDImessage(noteOFF, 55, 0);
    MIDImessage(noteOFF, 59, 0);
    delay (200); //wait 200ms
  } //end of section

    lastButonStateA = butonStateA; // save for the next time round the loop
    lastButonStateB = butonStateB;
    lastButonStateC = butonStateC; 
}

//send MIDI message
void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
  Serial.write(command);//send note on or note off command
  Serial.write(MIDInote);//send pitch data
  Serial.write(MIDIvelocity);//send velocity data
}



Note it is written to match your style which is not very good. It should be written with a for loop and arrays, but that is for another day.

SoupBowlNinja

That's brilliant Mike, thank you for your help. I'll hook it up tomorrow and see how it goes. I realise that my 'style' is bad mate,  I think even using the word 'style' is a push to be honest!

Go Up