Go Down

Topic: midi and arduino (Read 2377 times) previous topic - next topic

Wessie3000

I have started a project, but cant figure out the code for my arduino one. So im asking a little bit of help.

I have an old organ foot pedal with 13 switches. All the switches make contact when in rest, so thats different than normal but it should be possible, right?
When I step on a pedal, and close one switch (break contact), i would like to have my arduino send a midi noteOn message, say C1. When i release the pedal (let the switch connect again) the arduino should send a noteOff message.

Of course all 13 pedals should send a different note (complete octave) and i would like to have some sort of up and down octave buttons and midi channel buttons. If thats possible with an uno.

I see a lot of examples on internet but i just dont know where to start, because im only a beginner and way in over my head.


Wessie3000

#1
Feb 06, 2017, 01:58 pm Last Edit: Feb 06, 2017, 02:51 pm by Wessie3000
I have searched for "one octave keyboard arduino" and found this. Might be usefull for me.
If i change char notes[12] (and the other 12s) into char notes[13] with an extra pin (a5?) and change buttonstates into LOW. Or am i missing something??
 

Code: [Select]


//midi controller template   https://www.youtube.com/watch?v=9J_tGVLD7UQ
//7-21-2014 update: changed MIDI CONTROL values to hexadecimal from base-10
//                  added comments


char notes[12] = {10, 9, 8, 7, 6, 5, 4, 3, 2, A0, A1, A2};      //pin setup for 12 buttons (keyboard)
boolean noteOn[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};      //button (keyboard) state
boolean noteLast[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};    //last button (keyboard) state
char noteCount = 12;

char changePins[3] = {12, 13, 11};                              //pin setup for 3 "modifier" keys
char octaveChanges[3] = {12, -12, 0};                           //numbers for octave changing
char singleChanges[3] = {1, -1, 0};                             //numbers for half step changing
boolean changeButton[3] = {0, 0, 0};                            //modifier button state
boolean changeButtonLast[3] = {0, 0, 0};                        //last modifier button state

int keysBase = 0x3C;                                            //base for keyboard (C4)
int keysLast = keysBase;                                        //last keyboard base state
int changekeys(int a, int b){                                   //function to change key base
  keysLast = a + b;
  return(keysLast);
}

int pots[2] = {A3, A4};                                         //pin setup for potentiometers
int potState[2] = {0, 0};                                       //pot state
int potLast[2] = {0, 0};                                        //last pot state
int potCount = 2;


void setup(){
  Serial.begin(31250);                                          //serial speed for MIDI
}

                                                                //MIDI protocol - http://www.midi.org/techspecs/midimessages.php
                                                                //note to MIDI number - http://www.phys.unsw.edu.au/jw/graphics/notes.GIF
int midi(int midiStatus, int midiData1, int midiData2){         //MIDI send function
  Serial.write(midiStatus);
  Serial.write(midiData1);
  Serial.write(midiData2);
}



void loop(){
  constrain(keysLast, 0x18, 0x60);                              //limit keyboard from C1 to C7
  for(int i = 0; i < 3; i++){                                   //loop for modifier keys
    changeButtonLast[i] = changeButton[i];                      //update modifier key state
    changeButton[i] = digitalRead(changePins[i]);               //read state of modifier keys
    if(changeButton[2] && changeButton[i] == HIGH && changeButton[i] != changeButtonLast[i]){    //if modifier buttons 1 or 2 are pressed while modifier button 3 is held down
      changekeys(singleChanges[i], keysLast);                   //change keyboard base up or down in half steps
    }
    if(changeButton[0] && changeButton[1] == HIGH){             //if modifier buttons 1 and 2 are pressed together simultaneously
      keysLast = keysBase;                                      //revert keyboard base to C4
    }
    if(changeButton[i] == HIGH && changeButtonLast[i] != changeButton[i] && changeButton[2] == LOW){    //if modifier buttons 1 or 2 are pressed while modifier button 3 is released
      changekeys(octaveChanges[i], keysLast);                   //change keyboard base up or down in octaves
    }
  }
  for(char n = 0; n < noteCount; n++){                          //loop for the 12 button keyboard
    noteOn[n] = digitalRead(notes[n]);                          //read state of keyboard
    if(noteOn[n] == HIGH && noteLast[n] != noteOn[n]){          //if a key(s) is pressed
      midi(0x90, keysLast + n, 0x7F);                           //channel 1 MIDI note(s) ON with static 127 note velocity
      noteLast[n] = noteOn[n];                                  //update keyboard state
    }
    if(noteLast[n] != noteOn[n]){                               //if last keyboard state changes
      midi(0x80, keysLast + n, 0x7F);                           //channel 1 MIDI note(s) OFF
      noteLast[n] = noteOn[n];                                  //update keyboard state
    }
  }
  for(char n = 0; n < potCount; n++){                           //loop for potentiometers
    potState[n] = (analogRead(pots[n])/4);                      //read pot state
    if(abs(potState[n] - potLast[n]) > 1){                      //if potstate changes
      midi(0xB0, 0x0C + n, ((potState[n])/2));                  //channel 1 MIDI control/mode change
      potLast[n] = potState[n];                                 //update pot state
    }
  }
}

PieterP

#2
Feb 06, 2017, 05:13 pm Last Edit: Feb 06, 2017, 07:08 pm by PieterP
I'm currently working on a MIDI controller library for Arduino and Teensy. It's not finished yet, but you can get it on my GitHub (edit: old url). I just added an invert option and an example to go with it.
(I didn't edit the Wiki yet, I'll do that when I merge this branch to the master, but you can see how it's done in the examples, adding buttons is really easy.)
If you want MIDI over USB support, you'll have to flash the HIDUINO firmware to your Uno. It's not that hard, it's explained in the readme (well, I'm working on it  :) )

If you want to learn more about building MIDI devices using Arduino, I explained it here. The parts about the Leonardo are a bit outdated, but everything I said about the Uno should still be correct. I covered the basics of the MIDI protocol, with basic example programs (without using any extra libraries) with a lot of comments, and also about connecting things like switches to your Arduino.

Hope this helps!
Pieter

Edit: Merged development branch to master and updated documentation. I also added Windows Flip instructions.
New url: https://github.com/tttapa/MIDI_controller

Grumpy_Mike

#3
Feb 07, 2017, 06:49 pm Last Edit: Feb 07, 2017, 06:52 pm by Grumpy_Mike
Quote
If i change char notes[12] (and the other 12s) into char notes[13] with an extra pin (a5?) and change buttonstates into LOW.
I don't see why that would not work. But it looks like that code was not written for a keyboard set up like you have.

If you want to learn all about MIDI, then I have written a book about it:- Arduino Music Projects

Wessie3000

i have changed the code a bit, but the change channel loop doesnt seem to work (beginning with
   constrain(NoteAan, 0x90, 0x9F);
   constrain(NoteUit, 0x80, 0x8F);  )

It goes from 1 to 16 without steps in between. But i dont see where its wrong....




Code: [Select]
//midi controller template   https://www.youtube.com/watch?v=9J_tGVLD7UQ
//7-21-2014 update: changed MIDI CONTROL values to hexadecimal from base-10
//                  added comments


char notes[13] = {10, 9, 8, 7, 6, 5, 4, 3, 2, A0, A1, A2, A3};      //pin setup for 13 buttons (keyboard)
boolean noteOn[13] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};      //button (keyboard) state
boolean noteLast[13] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};    //last button (keyboard) state
char noteCount = 13;

char changePins[3] = {12, 13, 11};                              //pin setup for 3 "modifier" keys
char octaveChanges[3] = {12, -12, 0};                           //numbers for octave changing
char singleChanges[3] = {1, -1, 0};                             //numbers for half step changing
boolean changeButton[3] = {0, 0, 0};                            //modifier button state
boolean changeButtonLast[3] = {0, 0, 0};        //last modifier button state

char channelPins[2] = {A4, A5};
boolean changechannel[2] = {0, 0};     
boolean changechannellast[2] = {0, 0};             

int keysBase = 0x24;                                            //base for keyboard (C2)
int keysLast = keysBase;                                        //last keyboard base state
int NoteAan = 0x90;
int NoteUit = 0x80;
int changekeys(int a, int b){                                   //function to change key base
  keysLast = a + b;
  return(keysLast);
}



void setup(){
  Serial.begin(9600);                                          //serial speed for MIDI usb
}

                                                                //MIDI protocol - http://www.midi.org/techspecs/midimessages.php
                                                                //note to MIDI number - http://www.phys.unsw.edu.au/jw/graphics/notes.GIF
int midi(int midiStatus, int midiData1, int midiData2){         //MIDI send function
  Serial.write(midiStatus);
  Serial.write(midiData1);
  Serial.write(midiData2);
}



void loop(){
  constrain(keysLast, 0x18, 0x60);                              //limit keyboard from C1 to C7
  for(int i = 0; i < 3; i++){                                   //loop for modifier keys
    changeButtonLast[i] = changeButton[i];                      //update modifier key state
    changeButton[i] = digitalRead(changePins[i]);               //read state of modifier keys
    if(changeButton[2] && changeButton[i] == HIGH && changeButton[i] != changeButtonLast[i]){    //if modifier buttons 1 or 2 are pressed while modifier button 3 is held down
      changekeys(singleChanges[i], keysLast);                   //change keyboard base up or down in half steps
    }
    if(changeButton[0] && changeButton[1] == HIGH){             //if modifier buttons 1 and 2 are pressed together simultaneously
      keysLast = keysBase;                                      //revert keyboard base to C4
    }
    if(changeButton[i] == HIGH && changeButtonLast[i] != changeButton[i] && changeButton[2] == LOW){    //if modifier buttons 1 or 2 are pressed while modifier button 3 is released
      changekeys(octaveChanges[i], keysLast);                   //change keyboard base up or down in octaves
    }
  }



   constrain(NoteAan, 0x90, 0x9F);
   constrain(NoteUit, 0x80, 0x8F); 
   for(int p = 0; p < 2; p++){
    changechannellast[p] = changechannel[p];                      //update modifier key state
    changechannel[p] = digitalRead(channelPins[p]);                //read state of channel keys
    if(changechannel[1] && changechannel[p] != changechannellast[p]){   
      NoteAan = 0x90 + 1;                    //change midichannel base up 
      NoteUit = 0x80 + 1;
    }
    if(changechannel[0] && changechannel[1] == HIGH){              //if modifier buttons 1 and 2 are pressed together simultaneously
      NoteAan = 0x90;                                      //revert channel to 1
      NoteUit = 0x80;
    }
    if(changechannel[0] && changechannel[p] != changechannellast[p]){   
      NoteAan = 0x90 - 1;                    //change midichannel base down
      NoteUit = 0x80 - 1;
    }

  }


  for(char n = 0; n < noteCount; n++){                          //loop for the 13 button keyboard
    noteOn[n] = digitalRead(notes[n]);                          //read state of keyboard
    if(noteOn[n] == LOW && noteLast[n] != noteOn[n]){          //if a key(s) is pressed
      midi(NoteAan, keysLast + n, 0x7F);                           //channel 1 MIDI note(s) ON with static 127 note velocity
      noteLast[n] = noteOn[n];                                  //update keyboard state
    }
    if(noteLast[n] != noteOn[n]){                               //if last keyboard state changes
      midi(NoteUit, keysLast + n, 0x7F);                           //channel 1 MIDI note(s) OFF
      noteLast[n] = noteOn[n];                                  //update keyboard state
    }
  }

  }

Grumpy_Mike

These two lines
Code: [Select]

   constrain(NoteAan, 0x90, 0x9F);
   constrain(NoteUit, 0x80, 0x8F);

Just make sure that the MIDI note on message and the MIDI note off message are constrained to the MIDI channels 0 to 15. The code is poor in that the lines should be after any code that could change these values and it is actually before.
Move them to before:-
Code: [Select]

  for(char n = 0; n < noteCount; n++){                          //loop for the 13 button keyboard


You said that you were going to:-
Quote
and change buttonstates into LOW.
I can't see any evidence of you doing that.
None of the inputs seem to be declared with the internal pull ups enabled which you will need if you are having a switch which is connected between ground and the input. There should be no external pull down resistor.

Quote
It goes from 1 to 16 without steps in between.
That will be because the logic of edge detection or state change of the change buttons is not working, for reasons sited above, is not working and it is incrementing or decrementing the values while the button is being held not when it changes.

Wessie3000

#6
Feb 09, 2017, 10:00 am Last Edit: Feb 09, 2017, 10:10 am by Wessie3000
The buttonstates LOW is only for the note switches 2 till 10 and A0 till A3


I have changed the code into this. Hopefully its working now. There were some dumb mistakes in the last one but im learning:)
I did not change the constrain lines because it was like that in the original code, to change octave and that works.

Code: [Select]

char notes[13] = {10, 9, 8, 7, 6, 5, 4, 3, 2, A0, A1, A2, A3};     
boolean noteOn[13] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};     
boolean noteLast[13] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};   
char noteCount = 13;

char changePins[3] = {12, 13, 11};                             
char octaveChanges[3] = {12, -12, 0};                       
char singleChanges[3] = {1, -1, 0};                         
boolean changeButton[3] = {0, 0, 0};                         
boolean changeButtonLast[3] = {0, 0, 0};       

char channelPins[2] = {A4, A5};
char channelchange[2] = {-1, 1};
boolean changechannel[2] = {0, 0};     
boolean changechannellast[2] = {0, 0};             

int keysBase = 0x24;                                         
int keysLast = keysBase;                                     
int NoteAanBase = 0x90;
int NoteUitBase = 0x80;
int NoteAanLast = NoteAanBase;
int NoteUitLast = NoteUitBase;

int changechannelaan(int a, int b){                                 
  NoteAanLast = a + b;
  return(NoteAanLast);
}
int changechanneluit(int a, int b){                               
  NoteUitLast = a + b;
  return(NoteUitLast);
}
int changekeys(int a, int b){                                   
  keysLast = a + b;
  return(keysLast);
}

void setup(){
  Serial.begin(9600);                                       
}

                                                               
                                                               
int midi(int midiStatus, int midiData1, int midiData2){       
  Serial.write(midiStatus);
  Serial.write(midiData1);
  Serial.write(midiData2);
}



void loop(){
  constrain(keysLast, 0x18, 0x60);                             
  for(int i = 0; i < 3; i++){                                 
    changeButtonLast[i] = changeButton[i];                     
    changeButton[i] = digitalRead(changePins[i]);             
    if(changeButton[2] && changeButton[i] == HIGH && changeButton[i] != changeButtonLast[i]){   
      changekeys(singleChanges[i], keysLast);                 
    }
    if(changeButton[0] && changeButton[1] == HIGH){             
      keysLast = keysBase;                                     
    }
    if(changeButton[i] == HIGH && changeButtonLast[i] != changeButton[i] && changeButton[2] == LOW){   
      changekeys(octaveChanges[i], keysLast);                 
    }
  }


   constrain(NoteAanLast, 0x90, 0x9F);
   constrain(NoteUitLast, 0x80, 0x8F); 
   for(int p = 0; p < 2; p++){
    changechannellast[p] = changechannel[p];                     
    changechannel[p] = digitalRead(channelPins[p]);               
    if(changechannel[1] == HIGH && changechannel[p] != changechannellast[p]){   
      changechannelaan(channelchange[p], NoteAanLast);                   
      changechanneluit(channelchange[p], NoteUitLast);   

    }
    if(changechannel[0] && changechannel[1] == HIGH){             
      NoteAanLast = NoteAanBase;                                     
      NoteUitLast = NoteUitBase;
    }
    if(changechannel[0] == HIGH && changechannel[p] != changechannellast[p]){   
      changechannelaan(channelchange[p], NoteAanLast);                   
      changechanneluit(channelchange[p], NoteUitLast);   

    }

  }

  for(char n = 0; n < noteCount; n++){                         
    noteOn[n] = digitalRead(notes[n]);                         
    if(noteOn[n] == LOW && noteLast[n] != noteOn[n]){       
      midi(NoteAanLast, keysLast + n, 0x7F);                           
      noteLast[n] = noteOn[n];                                 
    }
    if(noteLast[n] != noteOn[n]){                               
      midi(NoteUitLast, keysLast + n, 0x7F);                           
      noteLast[n] = noteOn[n];                                 
    }
  }

  }

Grumpy_Mike

Quote
I did not change the constrain lines because it was like that in the original code, to change octave and that works.
No it doesn't work. The only reason you think it does is because you have not tested it enough. Under the right ( or wrong ) circumstances it can transform a note off message on channel 16 into a note on message in channel 1. The fact that the original writer of he code made a mistake is no reason why you should propagate it, especially when you have been told about it and it is so easy to fix.

I answer these questions in a effort to try and teach you something, so you have a little understanding of what you are doing, so hopefully one day you will understand what you are doing and maybe help others.

Wessie3000

I understand... I will make the changes you suggest. And I have seen some weird midi notes that could be explained by that mistake.

Thank you for your time. Ive already learned much and will pass that on (my son wants to be a math professor, he is 7 years old:))

As for me becoming a professor wil be a bit difficult. But last week i was still blinking leds and now im sending midi notes, so thats good.

PieterP

IMHO, it would be better to start from scratch instead of using a program written by someone else, you'll understand things much faster and better.
Start with DigitalReadSerial for example, then try sending a MIDI note (without reading any buttons, just send a message every second or so). Once you get that working, try to send a MIDI note only when the button is pressed. Then you can expand that to all buttons. If it works, great, try some for-loops to clean up your code. Finally you can implement a system to switch the channels etc. or some other ideas.

This way, you'll gain a much better understanding of what is going on. Once you have some basic knowledge of how all the parts work together, you can come back to the code you found online. Either to get some inspiration to improve your own code, or to find out that the online code had some bugs.

Hope this helps!
Pieter

Grumpy_Mike

Quote
As for me becoming a professor wil be a bit difficult.
I think you misunderstood. When I said "and maybe help others" I meant in situations like this on forums.

I would agree with the above that you would be better starting from scratch, but at least you should try and understand what is going on where. I would also recommend declaring the input pins as inputs, I know they default to being inputs but there is nothing like a declaration to make it clear what pins are being used.

Wessie3000

Allright. I have been starting over as you guys suggested. And i understand the code better now.
I am busy rewriting the script and it goes pretty well.

Only have one question for now....

Do i need to debounce the buttons? I dont know really how that works (in a script)

But before i have finished the script and put a lot of work in it . Might be better to do that immediatly.
So how do i fit it in my existing script (if needed)

Grumpy_Mike

Quote
Do i need to debounce the buttons?
I would only do it at the end when you know you need to. Very often their is sufficient delay withing the program loop so that debounce happens naturally.

Wessie3000

Got it working!

I have a code which allows my 13 keys keyboard to send midi messages. Also I have managed (pff..it took me a while) to have a mode button. In mode 1 the keyboard sends midi notes. In mode 2 i can choose a channel (1-13) and it switch back to mode 1 again.

Thank you all for your help. I learned a lot...

Heres the code, maybe you have some tips still...

Code: [Select]


int notes[13] = {3, 4, 5, 6, 7, 8, 9, 10, 14, 15, 16, 17, 18};     
boolean noteOn[13] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};     
boolean noteLast[13] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};   
int noteCount = 13;

boolean shiftOn = 0;
boolean shiftOnLast = 0;
boolean  mode = 0;

int keysBase = 0x24;                                         
int keysLast = keysBase;                                     
int NoteAanBase = 0x90;
int NoteUitBase = 0x80;
int NoteAanLast = NoteAanBase;
int NoteUitLast = NoteUitBase;




void setup(){
  Serial.begin(9600);                                       

int inMin = 3;
int inMax = 11;
for(int i=inMin; i<=inMax; i++){
  pinMode(i, INPUT);}


pinMode(13, OUTPUT);

int inMinB = 14;
int inMaxB = 18;
for(int j=inMinB; j<=inMaxB; j++){
  pinMode(j, INPUT);}
}                                                               
                                                               
int midi(int midiStatus, int midiData1, int midiData2){       
  Serial.write(midiStatus);
  Serial.write(midiData1);
  Serial.write(midiData2);
}



void loop(){
shiftOn = digitalRead(11);



if(shiftOn == HIGH && shiftOnLast!= shiftOn ){   
  if (mode ==  1){
  mode = 0;}
   
  if (mode ==  0){
  mode = 1;}

    //shiftOnLast = shiftOn;
                                             }





if( mode  == 1){
     digitalWrite (13, HIGH);
   
  for(char n = 0; n < noteCount; n++){
      noteOn[n] = digitalRead(notes[n]);
                       
        if(noteOn[n] == LOW && noteLast[n] != noteOn[n]){       
                       
          noteLast[n] = noteOn[n];}


 
        if(noteLast[n] != noteOn[n]){     
          mode = 0;
    noteLast[n] = noteOn[n];
           NoteAanLast =  0x90+n; 
          NoteUitLast =  0x80+n;   
                       }
  }
      }

if( mode  == 0){
  digitalWrite (13, LOW);
 
  for(char n = 0; n < noteCount; n++){                         
      noteOn[n] = digitalRead(notes[n]);
                           
        if(noteOn[n] == LOW && noteLast[n] != noteOn[n]){       
          midi(NoteAanLast, keysLast + n, 0x7F);                           
          noteLast[n] = noteOn[n];}
   


    if(noteLast[n] != noteOn[n]){                               
          midi(NoteUitLast, keysLast + n, 0x7F);                           
          noteLast[n] = noteOn[n];}


             }
      }






Grumpy_Mike

#14
Feb 15, 2017, 12:43 pm Last Edit: Feb 15, 2017, 12:44 pm by Grumpy_Mike
A bit minor but putting a closing brace on a line with other stuff makes it hard to read so instead of
Code: [Select]
noteLast[n] = noteOn[n];}
I would use
Code: [Select]
       noteLast[n] = noteOn[n];
      }

Alternative you "select all" from the Edit menu and then "Auto Format" from the Tools menu.

Go Up