Go Down

Topic: MIDI Xilophone (over serial) to Ableton (Read 32212 times) previous topic - next topic

Grumpy_Mike

Quote
Each time i press a button it send a message CONTINUOUSLY without stopping.

Yes that is what your code has been written to do.
By the way can you post code using the # icon not the quote one next to it. It will stop smillies appearing in your code.

What you have to do is to remember the state of your button push just before you exit the loop() function, in a variable, call it something like lastState. Then only send your MIDI when current state is LOW and the lastState was HIGH.
Do your digital reads into a variable rather than directly in the if statement, then this is easy.

boguz

ai, ai, ai...

i think i understand the idea...   but i can't really make it work!!!
i was trying it with the LED from pin13 in the arduino with just one button from pin8.

in the end i came to a code that seems to work but i am worried on how it would work with more buttons.
Please let me know what you think about this:
Code: [Select]
int lastState = HIGH;

void setup() {
  pinMode(13, OUTPUT);   
  pinMode(8, INPUT);     
  digitalWrite(8, HIGH); 
}

void loop() {
  bool buttonState = digitalRead(8);
  if (buttonState == LOW && buttonState != lastState) {
    digitalWrite(13, buttonState);
    delay(1000);
    lastState = LOW;
  }
  else {
    digitalWrite(13, buttonState);
    lastState = HIGH;
  }
}


where there is now the digitalWrite on pin 13 and the delay, i would then replace it with the MIDI_TX (    MIDI_TX(0xB0, 80, 127);   )   that i am using with the rest of the code.

Would this be an effective way to do it?
When i change for the MIDI messages what should i have inside the else? Just "lastState = HIGH;"?

Thank you

Grumpy_Mike

Quote
i have inside the else?

nothing.

Quote
bool lastState = HIGH, currentState;

void setup() {
  pinMode(13, OUTPUT);   
  pinMode(8, INPUT);     
  digitalWrite(8, HIGH); 
}

void loop() {
   currentState = digitalRead(8);
  if (currentState == LOW && currentState != lastState) {
    // do your MIDI stuff here
  }
lastState = currentState;
}

You need currentState and lastState variables for each button you want to monitor.

If you want to be grown up then make these into an array:-
http://www.thebox.myzen.co.uk/Tutorial/Arrays.html
but if it is too much then just repeat that code.

boguz

Quote
http://www.thebox.myzen.co.uk/Tutorial/Arrays.html

hey, that was cool reading!!! Of course understanding 100% what it means will take long time, but still i found it really interesting. THANKS!
(and, by the way, when i was looking at the other things there in the website, i found your Hexome. WOW!!! Amazing! i loved it!!! Congratulation)...

Well, after reading your article on arrays, i decided to give it a go.
i tried a couple of times, but in the end when i press the "Verify" button i always get some errors that i cannot fix.
Here is what i have at the moment:
Code: [Select]
bool lastState = HIGH, currentState;

void setup() {
  int buttonPins[4] = { 8, 9, 10, 11 };
  for (int i=0; i<4; i++) pinMode(buttonPins[i], INPUT);
  for (int i=0; i<4; i++) digitalWrite(buttonPins[i], HIGH);
 
}

void loop() {
  for (button=0; button<4; button++) {
    currentState[button] = digitalRead(buttonPins[button]);
  if(currentState[button] == LOW && currentState(button) != lastState(button)) {
    //STILL MISSES MY MIDI_TX HERE
  }
  lastState[button] = currentState[button];
  }
}


and i get the errors
Code: [Select]

sketch_apr07a.cpp: In function 'void loop()':
sketch_apr07a:12: error: invalid types 'bool[int]' for array subscript
sketch_apr07a:12: error: 'buttonPins' was not declared in this scope
sketch_apr07a:13: error: invalid types 'bool[int]' for array subscript
sketch_apr07a:13: error: 'currentState' cannot be used as a function
sketch_apr07a:13: error: 'lastState' cannot be used as a function
sketch_apr07a:16: error: invalid types 'bool[int]' for array subscript
sketch_apr07a:16: error: invalid types 'bool[int]' for array subscript


Well, if i am kind of close maybe i can still try to fix it, if not maybe i need to stay simple (at least for this project) and just repeat the code for the four button.

What do you think?

Grumpy_Mike

You have to declare the arrays outside of any function if you want to use them in more than one function. The one declared in setup() will not be valid in loop()

boguz

ahhhhhhhhhhhhh, i feel that i am almost there, but i can't understand what is wrong!!!

Code: [Select]
bool lastState = HIGH, currentState;
int buttonPins[4] = { 8, 9, 10, 11 };

void setup() {
  for (int i=0; i<4; i++) {
    pinMode(buttonPins[i], INPUT);
    digitalWrite(buttonPins[i], HIGH);
  }
  Serial.begin(9600);
}

void loop() {
  for (int button=0; button<4; button++) {
    currentState = digitalRead(buttonPins[button]);
    if (currentState != lastState) {
      if (currentState == LOW) {
        //REMEMBER TO CHANGE SERIAL WITH MIDI_TX!!!!!!!!!!
        Serial.println (button);
       }
       lastState = currentState;
     }
   }
}


When i press the buttons i get on the serial monitor "0, 1, 2 and 3", but i get A LOT OF THEM!!!
as long as i am pressing the button there are ALWAYS numbers being printed!

with this code, when i press a button shouldn't the "lastState" be set to "LOW"?
Wouldn't that mean that the next time, if my button is still down, the currentState would be equal to the lastState, and so do nothing...?

Grumpy_Mike

You only have one last state variable and you are over writing it every times round the for loop.
Make the last state and current state variables arrays and access then with the index of the for loop.

boguz

Yesterday when i read you message
Quote
Make the last state and current state variables arrays and access then with the index of the for loop.

i didn't understand what it meant.
Well, to be honest, i am not sure i already do!!! But, anyway, today when i woke up i had an idea of what it could mean.
So i changed the code like this:
Code: [Select]
bool currentState [4] = { HIGH, HIGH, HIGH, HIGH };
bool lastState [4] = { HIGH, HIGH, HIGH, HIGH };
int buttonPins[4] = { 8, 9, 10, 11 };

void setup() {
  for (int i=0; i<4; i++) {
    pinMode(buttonPins[i], INPUT);
    digitalWrite(buttonPins[i], HIGH);
  }
  Serial.begin(9600);
}

void loop() {
  for (int button=0; button<4; button++) {
    currentState[button] = digitalRead(buttonPins[button]);
      if (currentState[button] != lastState[button]) {
        if (currentState[button] == LOW) {
        //REMEMBER TO CHANGE SERIAL WITH MIDI_TX!!!!!!!!!!
        Serial.println (button);
      }
    }
    lastState[button] = currentState[button];
  }
}


It seems to work ok, but sometimes i still get 2 or 3 values for just 1 button push!
Is there still something missing from the code?
Should there be some kind of debouncing?

When i try to fit this code to the full Xylophone code i get the error:
Quote
Xilophone_with_Buttons.cpp: In function 'void setup()':
Xilophone_with_Buttons:57: error: a function-definition is not allowed here before '{' token
Xilophone_with_Buttons:186: error: expected `}' at end of input

I looked for { } missing somewhere but i can't find any. If it works when it is on it's own sketch, why doesn't it work when i copy and past it into to Xylophone sketch?

i can't find any duplicates, i have put each part of the code to it's right place, but still i get the same error...


HAPPY EASTER!!!  =)

Grumpy_Mike

It dosn't work because you are not doing it right.
The error message tells you where the compiler gets mixed up.
It sounds like the compiler thinks you are trying to define a function in the wrong place.
The number 57 is the line number where it got confused, the error is likely to be a line or two before this.
The line numberyou are at is shown in the bottom left corner of the window with your code in.
Tip - if you click so your cursor is just to one side of a { or a ( then the matching one will be highlited. Use this to work out where you are going wrong.

boguz

ai, ai, ai...  maybe i have been eating to much chocolate eggs!!! There was, of course, a missing { quite in the beginning.  :smiley-eek:

I found it and then everything worked fine.
i change a couple of lines so the signal from the buttons comes from the MIDI_TX, so now it looks like this:
Code: [Select]
void buttons() {
    for (int button=0; button<4; button++) {
    currentState[button] = digitalRead(buttonPins[button]);
      if (currentState[button] != lastState[button]) {
        if (currentState[button] == LOW) {
          MIDI_TX(0xB0, 80+button, 127);
        }
      }
      lastState[button] = currentState[button];
    }
  }


It seams to be working ok. When i check from the MIDI Monitor (running the serial <> midi converter also) i get this:
Code: [Select]
*** ZERO *** From Xilof OUT Control 1 General Purpose 5 127
*** ZERO *** From Xilof OUT Control 1 General Purpose 6 127
*** ZERO *** From Xilof OUT Control 1 General Purpose 7 127
*** ZERO *** From Xilof OUT Control 1 General Purpose 8 127

one General Purpose button for each of my 4 buttons.

I think it is time to go on to the next stage: adding the 4 potentiometers!
=)

Grumpy_Mike

Have you thought that while those buttons set the CC to 127 what resets this value?
Think about how you want to operate your buttons and what you want them to do.

boguz

i had thought that the buttons would be assignable to commands inside Ableton, like for instance Play, Stop or trigger a sample.
In this case the buttons would work fine because one button would be Play and another Stop.
But now that i think of it, if i wanted to assign it for instance to effect On/Off now that wouldn't work.
Hmmm...

i saw that there is a library that allows us to use clicks, double clicks, triple,...
Any experience with it? Do you think i would be able to use to send for instance 127 on a click and 0 on a double click?


Well, today i was trying to get the 4 pots to work. I tries to follow the advices you gave for the last few pieces of code.
I made them into array and i created a "if (potOutput[potnumber] != potLastOutput[potnumber])".
And i was then quite happy when it actually worked!!!  =)
But there is one problem...
i am mapping the 0-1023 from the pot to 0-127 of MIDI. I think sometimes the pot gets "stuck" in a place where the conversion "jumps" between two values. So in the MIDI monitor it happens (not so often) then i turn the knob and even after i stop turning it the value for that pot are changing.
I don't think this would be a big problem, but still, if there is a way to fix it it would be nicer to have things clean.
Do you think making some kind of average would fix it?

This is the code i have now...
Code: [Select]
for (int potnumber=0; potnumber<4; potnumber++) {
  potVal[potnumber] = analogRead(potPins[potnumber]);
  potOutput[potnumber] = map(potVal[potnumber], 0, 1023, 0, 127);
  if (potOutput[potnumber] != potLastOutput[potnumber]) {
    MIDI_TX(0xB0, 7+potnumber, potOutput[potnumber]);
    potLastOutput[potnumber] = potOutput[potnumber];
  }
}
}

Grumpy_Mike

Quote
In this case the buttons would work fine because one button would be Play and another Stop.

Are you sure?
Push play - it starts push stop - now play and stop are both on, how do you play again?
Sure it depends on how you system responds to it but is sounds to me like it will be on all the time.

Quote
And i was then quite happy when it actually worked

Well done.  :)

Quote
sometimes the pot gets "stuck" in a place where the conversion "jumps" between two values

Yes this is what happens with any A/D converter, it is called dithering and happens due to the natural noise in a system.
I think the best way round it is to test for any change on the raw value you read. But not only that only when a change is greater than say 4. To do this you would use a test like this:-
Code: [Select]
if(abs(oldValue - newVlaue) > 4) .....
In other words you subtract the two and remove any negative sign by using the abs() function, abs stands for absolute value.
Of course you would do it with arrays if you are doing four in one loop.
Only then map the newValue to the MIDI range and send it.

boguz

i was looking at the library ClickButton.
Maybe this would be a nice solution and with 4 buttons i could actually control thing better that with just one simple click per button.
i have never used a library before, but i think thing didn't go so bad.

i wrote something like this...
Quote
#include "ClickButton.h"

int buttonPin1=8;
int ledPin=13;

ClickButton button1(buttonPin1, LOW, CLICK_PULLUP);
int lastClickCode1=0;

void setup() {
  pinMode(buttonPin1, INPUT);
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  button1.Update();
  if(button1.click == CLICK_SINGLECLICKED && button1.click != lastClickCode1){
    Serial.println("1");}

  else if(button1.click == CLICK_SINGLEHOLD && button1.click != lastClickCode1){
   Serial.println("2");}
lastClickCode1 = button1.click;
}


Nothing too complicated. for each button two options: simple click or click+hold.
simple click i could then assign to cc and 127 and click+hold to 0.  Like this i could have On AND Off in just one button.

The problem is that when i try to get it done for the four buttons with arrays i get all mixed up!!!!  Maybe i eat too much easter chocolate eggs!!! :smiley-roll:
It starts already in the beginning with "ClickButton button1(buttonPin1, LOW, CLICK_PULLUP);"...


ai, ai, ai...

Grumpy_Mike

Quote
i was looking at the library ClickButton.

The problem with using libraries like this for simple functions is that you are not in control of what is happening and when it doesn't do what you want you are stuck.
What is wrong with sending the 127 value when you press the button and 0 when you release it?

Go Up