Question about MIDI modwheel & pitch bend control wiring

Tim N.

Member
I'm currently resurrecting my Studiologic midi keyboard. Someone I loaned it to must have used an unsuitable power supply. So the electronics no longer work. I couldn't be bothered troubleshooting it, so I've opted to write a program to make the keyboard do what I want. Having a USB connection (with a custom name) is also a bonus. I'm using a Teensy 4.1. I've got all 76 keys working (sending correct Note On and Note Off messages for the correct key), so the keybed works perfectly well.

The issue I have is how the modwheel and pitch bend controls are wired. Here's a diagram of how the original is currently wired.

ModPitch.jpg

Seems kind of odd to me, like they are interacting in some way. The 4th terminal on the pots appears to be a tap point. I measured resistance between this 4th terminal and the two outer terminals (not the middle terminal). I got about 3k and 7k, so it's not a central tap. These values don't change if the control is rotated.

The resistance between the 4th terminal and the middle terminal (while rotating) varies between 0 at the UP position (on the pitch bend), and about 3k at the bottom (with the control wheels pulled towards you). The modwheel produces the same, but opposite values.

I also traced the connections on the original board. Looking at my diagram, the two connections from the center terminals go straight into the microprocessor chip, with a 100k resistor each, to ground. The others go to V+ and ground.

It's difficult to know how the original microprocessor has dealt with this connection. I'd prefer to use them in the usual way, with terminal 1 & 3 hooked up to the Teensy power rails, and the middle terminal going to an analog input.

Dealing with the change in voltage, and mapping the value to a 14-bit pitchbend message is another issue that I'm not very clear on. I'm using the usbMIDI code library, so it may well have a specific function that can deal with this more easily.

Thanks for any help.
 
@tim:

You should be able to use the PITCHBEND & MODWHEEL pots exactly as wired. PITCHBEND usually has some mechanical mechanism to return it to center when not activated, so I'll assume that this is the case in my functional description below.

Just to make sure that the PITCHBEND & MODWHEEL pots are operating as expected, execute the following:

1) make sure that no more than 3.3VDC is connected to the (V+) terminal in your schematic above (the T4.1 is *NOT* 5V tolerant)
2) connect the output from the MODWHEEL pot to pin 15 (analog input A1)
3) with the MODWHEEL turned all the way counter-clockwise, measure the voltage (you should get very nearly either 3.3VDC or 0VDC)
4) with the MODWHEEL turned all the way clockwise, measure the voltage (you should get very nearly either 0VDC or 3.3VDC)
5) disconnect the output from the MODWHEEL pot from pin 15 (analog input A1) & connect the output from the PITCHBEND pot to pin 15 (analog input A1)
6) with the PITCHBEND in the centered (inactivated) position, measure the voltage (you should get very nearly 1.65VDC)
7) with the PITCHBEND turned all the way counter-clockwise, measure the voltage (you should get very nearly either 3.3VDC or 0VDC)
8) with the PITCHBEND turned all the way clockwise, measure the voltage (you should get very nearly either 0VDC or 3.3VDC)

If/when you are satisfied that the PITCHBEND & MODWHEEL pots are operating as expected, then proceed with the following:

Program your T4.1 with the following:

Code:
void setup() {
   Serial.begin(9600);
   pinMode(15, INPUT_DISABLE);
}

void loop() {
   int adc_in = analogRead(15);
   Serial.print("A1 (pin 15) analog input = ");
   Serial.println(adc_in);
   delay(1000);
}

To determine the characteristics of the PITCHBEND & MODWHEEL pots that you will be monitoring, execute the following:

1) load & execute the sketch given above, while monitoring the output in the Arduino IDE Serial Monitor
2) connect the output from the MODWHEEL pot to pin 15 (analog input A1)
3) rotate the MODWHEEL from fully counter-clockwise to fully clockwise & note the values reported, while also noting in which direction (CW or CCW) that the minimum & maximum counts are reported - note the count at fully counter-clockwise as countModCCW (very nearly 0 or 1023), and note the count at fully clockwise as countModCW (very nearly 1023 or 0)
4) disconnect the output from the MODWHEEL pot from pin 15 (analog input A1) & connect the output from the PITCHBEND pot to pin 15 (analog input A1)
5) connect the output from the PITCHBEND pot to pin 15 (analog input A1)
6) mote the count at the PITCHBEND centered position as countPitchCenter (should be very nearly 512)
7) rotate the PITCHBEND from fully counter-clockwise to fully clockwise & note the values reported, while also noting in which direction (CW or CCW) that the minimum & maximum counts are reported - note the count at fully counter-clockwise as countpitchCCW (very nearly 0 or 1023), and note the count at fully clockwise as countpitchCW (very nearly 1023 or 0)

Now, for the real magic !! You will use the values determined above to be able to make use of the PITCHBEND & MODWHEEL pots in your sketch.

If your countModCCW is close to zero & your countModCW is close to 1023, then you have a MODWHEEL pot that increases in the clockwise direction. Let's say you want an actual MODWHEEL range from 0-100. If so, you would use the following command to make that translation directly & linearly:

Code:
MODpercent = map(adc_in, 0,1023, 0, 100);

If, however, your countModCCW is close to 1023 & your countModCW is close to 0, then you have a MODWHEEL pot that increases in the counter-clockwise direction. You still want an actual MODWHEEL range from 0-100. If so, you would use the following command to make that translation directly & linearly:

Code:
MODpercent = map(adc_in, 1023, 0, 0, 100);

PITCHBEND is a little more complicated. In my TeensyMIDIPolySynth, my PITCHBEND wheel sends a MIDI value between -8192 and +8192, so I'll use that range as an example. You'll need to decide what range you want your PITCHBEND pot to actually generate. I also like a small "deadband" in the middle of my PITCHBEND range so that a slight wobble of the PITCHBEND wheel doesn't cause any change. I'll use a "deadband" value of "5" (which gives an actual range of 10 counts on either side of the middle).

Your PITCHBEND pot should hover around a returned value of 512 when centered.

If your countPitchCCW is close to zero & your countPitchCW is close to 1023, then you have a PITCHBEND pot that increases in the clockwise direction. You want the calculated value of pitchbend to range from -8192 to +8192, with a center value of 0 (in this example). So, you would use the following to make the translation directly & linearly from input values to range values, with the desired "deadband" in the middle:

Code:
int countPitchCenter = 512;  // use your actual measured value
int countPitchCW = 1023;  // use your actual measured value
int countPitchCCW = 0;  // use your actual measured value

int pitchValue;
int deadband = 5;
int adc_in = analogRead(15);

if (adc_in < (countPitchCenter - deadband)) {
   pitchValue = map(adc_in, countPitchCCW, countPitchCenter, -8192, -1);
} else {
   if (adc_in > (countPitchCenter + deadband)) {
      pitchValue = map(adc_in, countPitchCenter, countPitchCW, 1, 8192);
   } else {
      pitchValue = 0;
   }
}

If, however, your countPitchCCW is close to 1023 & your countPitchCW is close to 0, then you have a PITCHBEND pot that increases in the counter-clockwise direction. You still want the calculated value of pitchbend to range from -8192 to +8192, with a center value of 0 (in this example). So, you would use the following to make that translation directly & linearly, with the desired "deadband" in the middle:

Code:
int countPitchCenter = 512;  // use your actual measured value
int countPitchCW = 0;  // use your actual measured value
int countPitchCCW = 1023;  // use your actual measured value

int pitchValue;
int deadband = 5;

int adc_in = analogRead(15);

if (adc_in > (countPitchCenter + deadband)) {
   pitchValue = map(adc_in, countPitchCW, countPitchCenter, -8192, -1);
} else {
   if (adc_in < (countPitchCenter - deadband)) {
      pitchValue = map(adc_in, countPitchCenter, countPitchCCW, 1, 8192);
   } else {
      pitchValue = 0;
   }
}

In the end, you'll need to wire the outputs from the two individual pots (PITCHBEND & MODWHEEL) to separate analog inputs & adjust your final sketch appropriately to read each individually, but I just wanted to give you some simple examples of how you might handle the specific voltages provided by the pots as they are actually wired. Hope this helps !!

This was all generated on-the-fly & has not been tested on actual hardware, so hopefully, no stupid mistakes have crept their way into this attempted description. Nothing worse than a non-working example to get someone frustrated !!

Mark J Culross
KD5RXT

NOTE: edited multiple times to correct mistakes & omissions !!
NOTE: edited again to correct INPUT_NONE to INPUT_DISABLE
NOTE: edited yet again to add missing parentheses & semicolons (geesh !!)
 
Last edited:
Totally awesome response, Mark. I get what you're saying, and so clearly. Thanks.

I was almost ready to order a couple of standard linear 10k pots. I'll give this a go later today. I'm assuming that in actual use, I'll need to keep track of the current value of each pot, and test for any change in value. Or, if either control is still in its' neutral position, not send any message. I'm also assuming that the usbMIDI.sendPitchChange function should be placed right after the Note On message is sent, and not to send the pitch change if there are no keys pressed. I'm also assuming that sendPitchChange is smart enough to take the analogRead value and construct the 14-bit value that the PitchChange MIDI message can use.

I'll let you know how it goes. Maybe I'm just over-thinking what I need to do.

Update Dec. 23, 2023:

Something I forgot to take into consideration: The knobs for the modwheel/pitchbend limit the control pot rotation to 100° (1/3 of the total 300°). So the resistance values/voltages are much less than one would expect. However, with mapping, I can still get it to work. Maybe I should use 50k linear pots...
 
Last edited:
Tim:

For reference, my USB MIDI keyboard sends MODWHEEL & PITCHBEND MIDI messages anytime those controls are moved, independent of whether any keys are pressed or not.

Your plan for monitoring for changes & sending appropriate MIDI messages for these only when changes are detected for these controls sounds good.

Good luck & have fun !!

Mark J Culross
KD5RXT
 
I'm also assuming that the usbMIDI.sendPitchChange function should be placed right after the Note On message is sent, and not to send the pitch change if there are no keys pressed.
No, because the Sustain pedal might be pressed (which delays acting on Note Off) and recently released keys may still be sounding due to the release phase of an ADSR envelope.
 
Ok. I got the modwheel and pitchbend controls to send out the correct messages (according to MIDI-ox). I'm finding the placement of the code for the modwheel and pitchbend responses to be fairly critical, as with the note responding. While I'd like to limit the number of MIDI messages that are sent, it still needs to work properly with any receiving MIDI device.

Still working on it.
 
Not sure that I didn't make an assumption behind my earlier posts that may not be as obvious as I has assumed: monitoring each of these two controls (MODWHEEL & PITCHBEND) works best if you sample each of their positions *every* time thru the loop() function. The corresponding MIDI message would then be sent anytime the current sample value for a given control changes from the previous sample value. Using this approach, a MIDI message is only sent out when the sampled position changes for a given control, and that MIDI message is sent as soon as any change is detected.

Hope that helps . . .

Mark J Culross
KD5RXT
 
works best if you sample each of their positions *every* time thru the loop() function. The corresponding MIDI message would then be sent anytime the current sample value for a given control changes from the previous sample value.
Yep, that's how I implemented my MIDI-controller as well.
One pitchbend fader shown in the basic example below, using the ResponsiveAnalogRead library.
C++:
#include <MIDI.h>
#include <ResponsiveAnalogRead.h>

ResponsiveAnalogRead analogZero(A0, true);

void setup() {
  pinMode(A0, INPUT_DISABLE);
}

void loop() {
  // read fader -----------------------------------------------------------------------------
  analogZero.update();
  if (analogZero.hasChanged()) {
    int value = constrain((map(analogZero.getValue(), 1, 1022, -8192, 8191)), -8192, 8191);
    usbMIDI.sendPitchBend(value, 1); // ChannelFader #1 at ch 1
    usbMIDI.send_now();
  }
  // discard incoming MIDI messages ---------------------------------------------------------
  while (usbMIDI.read()) {
  }
  delay(10);
}

Paul
 
Back
Top