Can there be negative control values in MIDI?

Status
Not open for further replies.

zalterman

Well-known member
Hi - I am using my teensy 3.1 to turn sensor data into MIDI which is sent to my computer over usb and used in MAX MSP. I would like have MAXMSP receive data that is positive in one direction in negative in the other. Ideally, I'd like to be able to use usbMIDI.sendControlChange() to send values -127 to 127, but this seems not possible. Any ideas for an alternative? usbMIDI.sendPitchBend() seems like it fits the description perfectly, but I can't seem to get it to output anything besides a 0 or 1.

here is more specificity if helpful
_________________________________

I am using a postion sensitive touchstrip
https://www.sparkfun.com/products/8680
That I would like to control the frequency of a sine wave in MAXMSP. it is easy enough to use the absolute position of the touchstrip to do this and just map the values of finger position to frequency. However, I would like to use the strip only for relative position. So when i press my finger down, there is no output. Just negative output as I travel down the strip, away from the start point. and Positive when I travel up. I finally got code working so that it outputs (over Serial) a postive, increasing number from 0 when i travel up from the startpoint, and a negative, decreasing number from 0 when i travel downwards (as well as the moment of contact and release). I would like to be able to do this in MIDI, because it is much easier to deal with in MAX MSP.

ANY IDEAS?

here is my code

#include <QueueArray.h>

boolean pressed = false;
int delayStart = 0;
int initValue = 0;
int currentValue=0;
int lastValue=10;
int s = 0;

QueueArray <int> history;


void setup(){
Serial.begin(57600);
pinMode(A0,INPUT);
history.setPrinter (Serial);
}

void loop(){

s = (analogRead(A0)/8);

if (!pressed){
if(s>2){
if(delayStart==0){
delayStart = millis();
//usbMIDI.sendNoteOn(1,127,1);
Serial.println("CONTACT");
}
if ((millis()-delayStart)>30){
pressed = true;
initValue = s;
lastValue = initValue;
delayStart = 0;
for(int i = 0; i < 700; i++) { //100 values is actually only 100*20microseconds =2ms lets give it like 5ms thats not too much lag. so 250 in queue?
history.enqueue(initValue);
}
//usbMIDI.sendNoteOn(2,127,1);
Serial.println("PRESSED");
}
} else {
if (delayStart==!0){
Serial.println("bad zero");
}

//delayStart = 0; //so this is whats causing the double CONTACT hiccup. the sensor sometimes drops down then back up again very quickly when pressed. i want to totally ignore this - but commenting it out makes values fucked. why?
}
} else{
currentValue = history.dequeue();
history.enqueue(s);

if(abs(currentValue-lastValue)>1){ //only output changes - ??what if you press down at beggingin at 10. then you wont output until you move?
int outty = (currentValue-initValue);
usbMIDI.sendControlChange(20,outty,1);
/*if(outty>=0){
usbMIDI.sendControlChange(13,outty,1);
}
if(outty<0){
outty = (outty*-1);
usbMIDI.sendControlChange(14,outty,1);
}
*/
lastValue=currentValue;
}

if (s<2){ //*** maybe i should also have some sort of protection against very very short jumps to zero. like anything less than 10ms i discard
//***i want to put a millis() on every line but is that too fast. should i be using something besides arduino serial monitor for th
pressed=false;
while(history.count() > 0) {
history.dequeue(); //clear queue
}

//usbMIDI.sendNoteOn(3,127,1);
Serial.println("RELEASED");
}
}
}
 
I haven't looked at your code (please encapsulate it in CODE tags, it's very hard to read otherwise). MIDI doesn't do negative numbers natively - it generally (except pitchbend) only deals in 7-bit number: 0-127. You can do tricksy things with that, though, such as taking a higher bit number and splitting it into two 7-bit numbers (IE, split a 14 bit number into two 7 bit numbers, by taking the 'upper' and 'lower' 7 bits of it and spitting them out on two CC#'s).

Now, you could also take 0-127, and just say to yourself '64 is my zero' - IE, when you send a "0", have Max interpret that as "-64" and when you send 127 Max should interpret that as "+63." Or, use one CC# for positive values, and another for negative values.

Also, look into the EllapsedMillis library (Paul wrote it, there's information available on the Arduino site and lurking around here, but it's built into Teensyduino so you shouldn't need to install anything). It's much easier to use than "if (mills()-delayStart > 30)".
 
Hey MuShoo, thanks for the reply

You can do tricksy things with that, though, such as taking a higher bit number and splitting it into two 7-bit numbers (IE, split a 14 bit number into two 7 bit numbers, by taking the 'upper' and 'lower' 7 bits of it and spitting them out on two CC#'s).
yeah i tried something similar where i said if the current value (which is the immediate reading at the A0 minus the first stored position when finger makes contact) is greater than zero, then do a control change from one cc# and if less than zero, do a control change on a different cc#. It was workingish in max but i was getting all sorts of odd jumps in value and such - the problem was mostly on my MAX end of things, but thats why I wanted to see if there was a solution without having to deal with this in MAX.


Now, you could also take 0-127, and just say to yourself '64 is my zero' - IE, when you send a "0", have Max interpret that as "-64" and when you send 127 Max should interpret that as "+63." Or, use one CC# for positive values, and another for negative values.
I tried this as well but also got complicated on the MAX side of things.
maybe something wrong here on teensy? here is part of code with 2 CC's. thanks for tip on CODE tags, I didn't know about them.

_____________
Code:
  int outty = (currentValue-initValue);
     
            if(outty>0){
              outty = map(outty, 1,127,64,127);
            }
            if(outty<0){
              outty = map(outty,-1,-127,62,0);
            }
            if(outty==0){
              outty = outty+63;
            }
        usbMIDI.sendControlChange(20,outty,1);   //and in MAX i subtract 63
__________________


You're making me think maybe I should really just do one of these solutions^ and deal with it in MAX.

Also, look into the EllapsedMillis library (Paul wrote it, there's information available on the Arduino site and lurking around here, but it's built into Teensyduino so you shouldn't need to install anything). It's much easier to use than "if (mills()-delayStart > 30)".
great to know that exists


So yes - i'll look further into solutions on MAX side of things, I'm just being impatient since i was having those initial problems with value jumps on contact and release. And an alternative is to not use MIDI at all, do it all through a custom serial protocol, which would definitely make things more flexible. But might also take more time than I have right now.

z
 
I'm not completely certain, but I'm relatively sure that Map() doesn't like negative numbers at all - do an absolute on your negative numbers to remap them. It also doesn't like going 'backwards,' as I recall.

You'd have to do something like (pesudocode):

if(outty<0){
outty = outty * -1;
map(outty, 1, 127, 0, 62);
}

For your purposes, you may be able to skip the abs() step (I omitted it above). Essentially, you can just invert it back to positive values and then map those correctly.
 
As to the jumps... It's a lot more complicated, but whenever you take a reading, keep your previous reading as well (you'll need two variables). That way, you can then do something like this (again, pseudocode):

prevValue = value;
value = (prevValue * 0.1) + (analogRead(whateverThatPinWas) * 0.9);

That will, in effect, smooth your values a little bit (make sure the two decimal values ALWAYS add up to 1.0!) You can change the decimal values to change the amount of smoothing - higher amounts of prevValue will make for a slower response to changes, but smoother values.
 
This mainly depends on what your receiving software can handle. You could send 14bit data one of several ways (pitchbend * 16 channels, paired MSB/LSB CCs, NRPNs) and interpret them the same way that pitchbend is encoded (+/- 8k range).
 
Status
Not open for further replies.
Back
Top