MIDI Problems

Status
Not open for further replies.
Hi all.......I have a program that runs fine on a Mega and it also works on the Teensy 2.0 in serial mode (using Hairless) but will not work in MIDI mode. Haven't found any joy on the net. It's for a device to send MIDI pitch bend

Here's the code:

// Vibe Bar Main Program
//
// Sees the movement of the Vibe Bar
// Between full left and dead spot
// Between full right and dead spot
// Between full forward and dead spot
// Between full back and dead spot
// and maps to MIDI output.
//
// Claude Woodward 17/06/15
//

#include <Vibe_Bar_Configuration.h>
#include <EEPROM.h>

// MIDI Libraries
#include <MIDI.h>
#include <midi_Defs.h>
#include <midi_Message.h>
#include <midi_Namespace.h>
#include <midi_Settings.h>

// MIDI Constants
int pitchBEND = 224;

// These Analog pins are defined in ~/Documents/Arduino/libraries/Vibe_Bar_Configuration/Vibe_Bar_Configuration.cpp
// These lines tell the Arduino where the Hall Effect sensors are connected to
extern const int SENSOR_LEFT_RIGHT;
extern const int SENSOR_FWD_BACK;

// These Analog pins are defined in ~/Documents/Arduino/libraries/Vibe_Bar_Configuration/Vibe_Bar_Configuration.cpp
// This defines where the Arduino built-in LED is
extern const int LED_PIN;

// These are text names for non-volatile memory addresses
// These are defined in ~/Documents/Arduino/libraries/Vibe_Bar_Configuration/Vibe_Bar_Configuration.cpp
extern const int VIBE_LR_MIN1;
extern const int VIBE_LR_MIN2;
extern const int VIBE_LR_MAX1;
extern const int VIBE_LR_MAX2;
extern const int VIBE_LR_DEAD_MIN1;
extern const int VIBE_LR_DEAD_MIN2;
extern const int VIBE_LR_DEAD_MAX1;
extern const int VIBE_LR_DEAD_MAX2;
extern const int VIBE_FB_MIN1;
extern const int VIBE_FB_MIN2;
extern const int VIBE_FB_MAX1;
extern const int VIBE_FB_MAX2;
extern const int VIBE_FB_DEAD_MIN1;
extern const int VIBE_FB_DEAD_MIN2;
extern const int VIBE_FB_DEAD_MAX1;
extern const int VIBE_FB_DEAD_MAX2;

// This is called a 'class' it contains functions and data that alre all carried around together
// This particular class contains the graph mapping the hall effect sensor reading to a MIDI output
class CustomCalibration {
public:
void setup( int minVal, int maxVal, int deadMin, int deadMax, int outMin, int outMax ) {
_minVal = minVal;
_maxVal = maxVal;
_deadMin = deadMin;
_deadMax = deadMax;
_outMin = outMin;
_outMax = outMax;
_outMid = ( outMin + outMax ) / 2;
if ( minVal > deadMin || deadMin > deadMax || deadMax > maxVal || outMin > outMax )
{
pinMode(LED_PIN,OUTPUT);
Serial.begin(19200);
Serial.println("EEPROM Calibration Value Check Failed: Possible cause = bad calibration");
while (true){
digitalWrite(LED_PIN,HIGH);
delay(500);
digitalWrite(LED_PIN,LOW);
delay(500);
Serial.print(" minVal = ");
Serial.print(_minVal);
Serial.print(" maxVal = ");
Serial.print(_maxVal);
Serial.print(" deadMin = ");
Serial.print(_deadMin);
Serial.print(" deadMax = ");
Serial.print(_deadMax);
Serial.print(" outMin = ");
Serial.print(_outMin);
Serial.print(" outMax = ");
Serial.print(_outMax);
Serial.println();
}
}
}

int Map(int value) {
if( value < _minVal ) {
return _outMin;
} else if ( value < _deadMin ) {
return map( value, _minVal, _deadMin, _outMin, _outMid );
} else if ( value < _deadMax ) {
return _outMid;
} else if ( value < _maxVal ) {
return map( value, _deadMax, _maxVal, _outMid, _outMax );
} else {
return _outMax;
}
}
private:
int _minVal, _maxVal, _deadMin, _deadMax, _outMin, _outMax, _outMid;
};

CustomCalibration leftRightCalibration, fwdBackCalibration;

void setup() {
// MIDI Range constants
int midiMin = 0;
int midiMax = 127;

// Initialise the Serial port (MIDI is sent over Serial)
Serial.begin(31250);//(19200);//

// Create custom mapping objects (they map the Hall Effect sensor values to the MIDI range - and incorporate a 'dead spot')
leftRightCalibration = CustomCalibration();
fwdBackCalibration = CustomCalibration();

// Load calibration points from the EEPROM (these are stored during the calibration routine)
leftRightCalibration.setup( EEPROM.read(VIBE_LR_MIN1) + EEPROM.read(VIBE_LR_MIN2)*256, EEPROM.read(VIBE_LR_MAX1) + EEPROM.read(VIBE_LR_MAX2)*256,
EEPROM.read(VIBE_LR_DEAD_MIN1) + EEPROM.read(VIBE_LR_DEAD_MIN2)*256, EEPROM.read(VIBE_LR_DEAD_MAX1) + EEPROM.read(VIBE_LR_DEAD_MAX2)*256, midiMin, ( ( midiMax * 2 ) / 3 ) + 1 );
fwdBackCalibration.setup( EEPROM.read(VIBE_FB_MIN1) + EEPROM.read(VIBE_FB_MIN2)*256, EEPROM.read(VIBE_FB_MAX1) + EEPROM.read(VIBE_FB_MAX2)*256,
EEPROM.read(VIBE_FB_DEAD_MIN1) + EEPROM.read(VIBE_FB_DEAD_MIN2)*256, EEPROM.read(VIBE_FB_DEAD_MAX1) + EEPROM.read(VIBE_FB_DEAD_MAX2)*256, midiMin, ( midiMax * 1 ) / 3 );
}

void loop() {
// Read Sensors
int leftRightRawValue = analogRead( SENSOR_LEFT_RIGHT );
int fwdBackRawValue = analogRead( SENSOR_FWD_BACK );
// Map to MIDI range
int leftRightValue = leftRightCalibration.Map( leftRightRawValue );
int fwdBackValue = fwdBackCalibration.Map( fwdBackRawValue );
// Combine Left-Right and Forward-Back into a single bend measurement
int bendVal = leftRightValue + fwdBackValue;
// Send as MIDI command
MIDImessage(pitchBEND, 0, bendVal);

}

void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
Serial.write(command);
Serial.write(MIDInote);
Serial.write(MIDIvelocity);
delay(20);
}

Any ideas?

sonicmanipulator
 
Hi all.......I have a program that runs fine on a Mega and it also works on the Teensy 2.0 in serial mode (using Hairless) but will not work in MIDI mode. Haven't found any joy on the net. It's for a device to send MIDI pitch bend...
Do you mean you want MIDI through the USB interface? You will have to convert to usbMIDI send command.

You need something like this:
usbMIDI.sendPitchBend(value, channel)

in place of this:
MIDImessage(pitchBEND, 0, bendVal);

Documentation page: https://www.pjrc.com/teensy/td_midi.html

Does the calibration stuff all work?
 
As the MIDImessage() call contains a delay(20) command you may need to add a delay in after calling the usbMIDI send to limit output.
 
Midi Problems

Do you mean you want MIDI through the USB interface? You will have to convert to usbMIDI send command.

You need something like this:
usbMIDI.sendPitchBend(value, channel)

in place of this:
MIDImessage(pitchBEND, 0, bendVal);

Documentation page: https://www.pjrc.com/teensy/td_midi.html

Does the calibration stuff all work?

Thanks for getting back so quick. I did this:


void loop() {
// Read Sensors
int leftRightRawValue = analogRead( SENSOR_LEFT_RIGHT );
int fwdBackRawValue = analogRead( SENSOR_FWD_BACK );
// Map to MIDI range
int leftRightValue = leftRightCalibration.Map( leftRightRawValue );
int fwdBackValue = fwdBackCalibration.Map( fwdBackRawValue );
// Combine Left-Right and Forward-Back into a single bend measurement
int bendVal = leftRightValue + fwdBackValue;
// Send as MIDI command
usbMIDI.sendPitchBend( 0, bendVal);
delay(20);
}

void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
Serial.write(command);
Serial.write(MIDInote);
Serial.write(MIDIvelocity);
delay(20);
}

with the extra delay. I now get MIDI but the PB values are turning into channel numbers. I should probably know this (I'm not a native coder and a friend wrote the code for me. I tried swapping the order around but no joy. The cal routine works as it's before the MIDI part. Any suggestions?

sonicmanipulator
 
.... I tried swapping the order around but no joy...
Joy or no, that's how they go. Value then channel.
If the calibration is not working that would explain why reversing the parameters didn't sort it. I don't understand it sufficiently. Are there error messages in the serial print window?
 
Joy or no, that's how they go. Value then channel.
If the calibration is not working that would explain why reversing the parameters didn't sort it. I don't understand it sufficiently. Are there error messages in the serial print window?

Ok, I did it again an now I get the channel first, then Value then BP command. Should be BP then Value......mmmmmm

usbMIDI.sendPitchBend( bendVal,1);
delay(20);
}

void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
Serial.write(command);
Serial.write(MIDInote);
Serial.write(MIDIvelocity);
delay(20);
}
 
now I get the channel first, then Value then BP command
What program are you using that displays these values?
I wrote a sketch to send a "usbMIDI.sendPitchBend( 43,1);" message every half second to the PC and used MIDI-Ox to monitor it. MidiOx receives and displays the correct message - "E0 2B".

Pete
 
What program are you using that displays these values?
I wrote a sketch to send a "usbMIDI.sendPitchBend( 43,1);" message every half second to the PC and used MIDI-Ox to monitor it. MidiOx receives and displays the correct message - "E0 2B".

Pete

Interesting. I'm on a Mac using Logic Pro x. I found the MIDI message part at the end is redundant but for some reason it's still sending those 2 bytes ass about (to use a technical term). So close!! Can you put your code up and I'll see if it works on mine.

If you're interested in the controllers I'm using, here's a video:

https://www.youtube.com/watch?v=-bYc_AINC3I

sonicmanipulator
 
The last six lines should not just be redundant they should never fire... you should be able to delete them without impact on how the sketch runs.

If deleting them changes things then I'm not understanding what the code is doing.

I would forget about the midi output until you are sure bendVal is tracking as you would expect.

I would put in a serial.print(bendVal) next to the midi send and make sure you're seeing sensible values that reflect the movements detected by the hall sensors. (Maybe up the delay to 500 to slow down the print so you can see the values as you move the bar slowly.)
 
Last edited:
oh.. and without the configuration file no one can really know what's going on with the calibration routine which is far-and-away the most complex thing going on here.
 
The exchange of comments is confusing, and its very hard to give advice to the OP.

How is the Teensy connected to the host (Mac) USB MIDI or Serial Midi and a MIDI dongle, or perhaps both ?

What is the expected response behaviour of the program, what data do you expect to receive and what is the data that is actually is received ?

I found the MIDI message part at the end is redundant but for some reason it's still sending those 2 bytes ass about (to use a technical term).

What bytes are received through what channel ? How do you see the offending bytes ?

Without precisely describing the current setup, the expected results and then the observed results it is almost impossible to give much help.
 
it's still sending those 2 bytes ass about
All I have to go on is that you say the message is received backwards but you still haven't explained precisely how you know that those two bytes are the wrong way round.
MidiOx on a PC shows that it correctly receives the pitchbend message from my sketch on a Teensy. So the Teensy is sending the correct message.
You aren't receiving it correctly.


Pete
 
The exchange of comments is confusing, and its very hard to give advice to the OP.
True...
How is the Teensy connected to the host (Mac) USB MIDI or Serial Midi and a MIDI dongle, or perhaps both ?
If it was not via USB MIDI it would not compile with usbMIDI.sendPitchBend() in the code.

What is the expected response behaviour of the program, what data do you expect to receive and what is the data that is actually is received ?
I'm not sure of the mapping... as I said above... but there are apparently two hall-effect sensors that produce voltages based on their motion from a stable mid position. So there is effectively a two-dimensional mapping to a single pitchbend value. By avoiding the midi side at first and using serial.print() to output the bendVal the OP might be able to see what's not working correctly.

If it was failing outright I would expect the OP would have seen error messages in the serial monitor from this line returning 'true' and printing troubleshooting info:
if ( minVal > deadMin || deadMin > deadMax || deadMax > maxVal || outMin > outMax )

What bytes are received through what channel ? How do you see the offending bytes ?
Yes... OP needs to be more precise when describing observed behaviour -- especially since no one is likely going to whip-up the hardware on their end just to test.

Without precisely describing the current setup, the expected results and then the observed results it is almost impossible to give much help.
I've assumed the hardware is two hall-effect analog output sensors that do not vary across the full voltage range and therefore need to be calibrated before being mapped. Without the configuration details it's impossible to say how.

It's common for MIDI projects to attract coding neophytes and it's just as common to not know where to start and to try too difficult a project for a first go... in this case the example project being altered is less simple than most midi controllers because of the remapping issue. If the OP can sort out that part they might have a chance at porting this to usbMIDI but it looks like it might be tricky. In the absence of the missing file I can't say more.
 
Last edited:
Since the code supposedly works on a Teensy in serial mode, I assume the issues is in transitioning from serial MIDI to USB MIDI, rather than the sensors themselves.

Wrongly calibrated sensors will create weird behaviour, but not communication bytes in 'wrong' places
 
...Wrongly calibrated sensors will create weird behaviour, but not communication bytes in 'wrong' places
Of course...

The old code is using the second byte not as midi channel but as the second byte of data for the pitchbend which it's leaving as zero.

usbMIDI.sendPitchBend() needs to have a 14 bit integer range not 7 so the line would be:

usbMIDI.sendPitchBend(bendVal<<7,1);
 
I suspect the data-byte order issue is related to that as the low values sent as the bend amount may have looked like channel 0.

bendVal<<7 is a fancy way have multiplying by 128 to bring the bit levels up from 7 to 14 bits.

I'd still like to see the config file as it's doing something interesting and I'd like to know how.
 
Last edited:
Just to clarify: MidiOx actually reports "E0 2B 00". The low order 7 bits of a pitchbend value are sent first. The value of 43 that my code sends, is sent as 43 (= 0x2B) in the low order (first) 7 bits and the remaining bits are zero.

usbMIDI.sendPitchBend() needs to have a 14 bit integer range not 7 so the line would be:

usbMIDI.sendPitchBend(bendVal<<7,1);

No. You don't need to shift the value up. The MIDI library pitchbend routine accepts an integer and sends the command byte, then the low order 7 bits of the pitchbend value followed by the next higher order 7 bits.

Pete
 
...No. You don't need to shift the value up.

usbMIDI.sendPitchBend(value, channel)
The function takes two parameters, value and channel. The two data bytes (that contain 14 bits of pitchbend resolution data) are extracted from the integer value such that:
  • 0 is full bend down
  • 16383 is full up
  • 8192 is no bend

If you have only 7 bits of data you would pad the LSB with zero -- which is what the code at the bottom was doing with the original MIDI call. To do the same thing in the usbMIDI library you need to shift the data up 7 bits.

I did some testing... I'm pretty sure.:D
 
I checked out my SY77 synth and 61es keyboard and you are correct. They both change only the high order 7 bits.

Pete
 
True...

If it was not via USB MIDI it would not compile with usbMIDI.sendPitchBend() in the code.

I'm not sure of the mapping... as I said above... but there are apparently two hall-effect sensors that produce voltages based on their motion from a stable mid position. So there is effectively a two-dimensional mapping to a single pitchbend value. By avoiding the midi side at first and using serial.print() to output the bendVal the OP might be able to see what's not working correctly.

If it was failing outright I would expect the OP would have seen error messages in the serial monitor from this line returning 'true' and printing troubleshooting info:
if ( minVal > deadMin || deadMin > deadMax || deadMax > maxVal || outMin > outMax )

Yes... OP needs to be more precise when describing observed behaviour -- especially since no one is likely going to whip-up the hardware on their end just to test.

I've assumed the hardware is two hall-effect analog output sensors that do not vary across the full voltage range and therefore need to be calibrated before being mapped. Without the configuration details it's impossible to say how.

It's common for MIDI projects to attract coding neophytes and it's just as common to not know where to start and to try too difficult a project for a first go... in this case the example project being altered is less simple than most midi controllers because of the remapping issue. If the OP can sort out that part they might have a chance at porting this to usbMIDI but it looks like it might be tricky. In the absence of the missing file I can't say more.



Thanks for all your comments.

So to be clear, I'm using the usbMIDI going into a Mac running Logic X. The environment input viewer displays channel, command, value.
Using the code below I get channel, value, command. The values display 0 - 127 when I move the bar suggesting the program works. When I use the non usbMIDI code running on a MEGA, I connect the TX (MIDI) out to a MIDI to USB circuit and it sends everything fine. And I know if the cal routine doesn't add up, the program won't run. So it must be the way the Teensy deals with MIDI. It wouldn't be a library thing would it?

A very vexing problem


void loop() {
// Read Sensors
int leftRightRawValue = analogRead( SENSOR_LEFT_RIGHT );
int fwdBackRawValue = analogRead( SENSOR_FWD_BACK );
// Map to MIDI range
int leftRightValue = leftRightCalibration.Map( leftRightRawValue );
int fwdBackValue = fwdBackCalibration.Map( fwdBackRawValue );
// Combine Left-Right and Forward-Back into a single bend measurement
int bendVal = leftRightValue + fwdBackValue;
// Send as MIDI command
//MIDImessage(pitchBEND, 0, bendVal);
usbMIDI.sendPitchBend( bendVal,1);
delay(20);
}
/*void MIDImessage(int command, int MIDInote, int MIDIvelocity) {
Serial.write(command);
Serial.write(MIDInote);
Serial.write(MIDIvelocity);
delay(20);
}*/
 
True...

If it was not via USB MIDI it would not compile with usbMIDI.sendPitchBend() in the code.

I'm not sure of the mapping... as I said above... but there are apparently two hall-effect sensors that produce voltages based on their motion from a stable mid position. So there is effectively a two-dimensional mapping to a single pitchbend value. By avoiding the midi side at first and using serial.print() to output the bendVal the OP might be able to see what's not working correctly.

If it was failing outright I would expect the OP would have seen error messages in the serial monitor from this line returning 'true' and printing troubleshooting info:
if ( minVal > deadMin || deadMin > deadMax || deadMax > maxVal || outMin > outMax )

Yes... OP needs to be more precise when describing observed behaviour -- especially since no one is likely going to whip-up the hardware on their end just to test.

I've assumed the hardware is two hall-effect analog output sensors that do not vary across the full voltage range and therefore need to be calibrated before being mapped. Without the configuration details it's impossible to say how.

It's common for MIDI projects to attract coding neophytes and it's just as common to not know where to start and to try too difficult a project for a first go... in this case the example project being altered is less simple than most midi controllers because of the remapping issue. If the OP can sort out that part they might have a chance at porting this to usbMIDI but it looks like it might be tricky. In the absence of the missing file I can't say more.

Here's a possible clue - I swapped the usbMIDI for CC and sends everything in the right order:

// Send as MIDI command
usbMIDI.sendControlChange(7, bendVal, 1);
//usbMIDI.sendPitchBend( bendVal,1);
delay(20);
}
 
Control change works because usbMIDI.sendControlChange() is expecting a 7-bit value as it's data parameter.

As I've said above (#16, 17 and 19) usbMIDI.sendPitchBend is looking for 14 bits of range and so to get a sweep of the fullest range you need to multiply your 7-bit value by 128 (or shift the bits up by 7 places in its binary form which is the same thing).

Did you try

usbMIDI.sendPitchBend( bendVal<<7,1);

This should give you pitchbend output across the full* range
 
Control change works because usbMIDI.sendControlChange() is expecting a 7-bit value as it's data parameter.

As I've said above (#16, 17 and 19) usbMIDI.sendPitchBend is looking for 14 bits of range and so to get a sweep of the fullest range you need to multiply your 7-bit value by 128 (or shift the bits up by 7 places in its binary form which is the same thing).

Did you try

usbMIDI.sendPitchBend( bendVal<<7,1);

This should give you pitchbend output across the full* range

YES YES YES!!! you've done it - praise him with great praise!
Thanks so much.......it's been doing my brain in. Much relieved.

sonicmanipulator
 
That only leaves the mystery of how this code was supposed to work on a Mega and Teensy2 but needs a modification to work with USBSerial when AFAICT all the drivers involved handle the pitchbend value in the same way.

Pete
 
Status
Not open for further replies.
Back
Top