tttapa / Control-Surface

Arduino library for creating MIDI controllers and other MIDI devices.
GNU General Public License v3.0
1.21k stars 137 forks source link

Pitch Bend Doesn't Send Zero At Center (Sends +8) #1059

Open nelsonii opened 1 month ago

nelsonii commented 1 month ago

First off, thank you for this extensive library. I'm using it on a number of MIDI Controllers (mainly PB and CC devices) and it's been extremely helpful. The world needs more Pitch Bend Wheels. :-)

What I'm seeing is that the last value sent for Pitch Bend -- when it's centered -- is +8 or -9. I see this in the most basic "hello world" PB example and in my more complicated code (using mapping, to correct for joystick deadzones, etc).

My customer is using my controller MainStage on a Mac Pro Powerbook osX 14.5. I see it in Pocket MIDI and MIDI-OX (I use those to test / debug messages).

The "last value of 8" is detuning their DAW. Is there any way to get it to send zero? I use mapping, so I can force a pitch bend send when "centered" if need be -- but I'm not sure what that code would be.

12:34:51.026 From Seeed XIAO M0 Pitch Wheel 1 -1354 12:34:51.026 From Seeed XIAO M0 Pitch Wheel 1 -1082 12:34:51.026 From Seeed XIAO M0 Pitch Wheel 1 -921 12:34:51.026 From Seeed XIAO M0 Pitch Wheel 1 -809 12:34:51.026 From Seeed XIAO M0 Pitch Wheel 1 -745 12:34:51.026 From Seeed XIAO M0 Pitch Wheel 1 -697 12:34:51.026 From Seeed XIAO M0 Pitch Wheel 1 -665 12:34:51.026 From Seeed XIAO M0 Pitch Wheel 1 8 12:34:52.015 From Seeed XIAO M0 Pitch Wheel 1 -313 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 -297 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 -265 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 -233 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 -201 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 -153 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 -121 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 -89 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 -57 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 -25 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 8 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 56 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 88 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 120 12:34:52.041 From Seeed XIAO M0 Pitch Wheel 1 168

Here are some of the features I'm using -- just snippits as there is a lot of code. (Again, same behavior using the most basic PB example found in the library.)

global

Bankable::PBPotentiometer potPB {{bankChannel, BankType::ChangeChannel}, pinPB, CHANNEL_1}; // pitch bend

FilteredAnalog<14, 10, uint32_t, uint32_t> filterPB = pinPB;

setup

  FilteredAnalog<>::setupADC();
  filterPB.resetToCurrentValue();

    potPB.map(mappingFunctionPB_INV);

mapping function

analog_t mappingFunctionPB_INV(analog_t raw) {

    //INVERTING
    raw = constrain(raw, PBminimumValue, PBmaximumValue);
    if (raw<(PBcenter-PBdeadzone) || raw>(PBcenter+PBdeadzone)) { 
      raw = map(raw, PBminimumValue, PBmaximumValue, 16383, 0);
    } else { raw = 8192; }
    return raw;

}
tttapa commented 1 month ago

You could try something like this: (untested)

#include <Control_Surface.h>

template <uint8_t InputPrecisionBits>
struct PitchBendSenderMiddle {
    static void send(uint16_t value, MIDIAddress address) {
        value = AH::increaseBitDepthMiddle<14, precision(), uint16_t>(value);
        Control_Surface.sendPitchBend(address.getChannelCable(), value);
    }

    constexpr static uint8_t precision() {
        static_assert(InputPrecisionBits <= 14, "");
        return InputPrecisionBits;
    }
};

struct PBPotentiometerMiddle : MIDIFilteredAnalog<PitchBendSenderMiddle<10>> {
    PBPotentiometerMiddle(pin_t analogPin, MIDIChannelCable address = Channel_1)
        : MIDIFilteredAnalog(analogPin, address, {}) {}
};

USBMIDI_Interface midi;
PBPotentiometerMiddle pot {A0, Channel_1};

void setup() { Control_Surface.begin(); }
void loop() { Control_Surface.loop(); }

See this page for details: https://tttapa.github.io/Control-Surface-doc/Doxygen/dd/d5e/group__AH__Math.html#ga274ecbe8f2a095423f25beef281cb9de

increase-bit-depth

nelsonii commented 1 month ago

I found that PBPotentiometer getValue() is showing a good "512" (center) 10 bit value. So, I went with a hack like this:

In the main Loop, after Control_Surface.loop();

  //Force centering on zero
  if (potPB.getValue()==512 && PBdirty) { 
    Control_Surface.sendPitchBend(Channel(defaultSetting.channelShift) , (uint16_t) 8192); 
    PBdirty=false;
  }

(Note: defaultSetting.channelShift is a variable I use to keep track of the current MIDI Channel, as I use banks and allow the user to change channels at will.)

Where PBdirty is a boolean. I set it to "true" in my PB mapping code:

analog_t map_PB_INV(analog_t raw) {

    //INVERTING
    raw = constrain(raw, PBminimumValue, PBmaximumValue);
    if (raw<(PBcenter-PBdeadzone) || raw>(PBcenter+PBdeadzone)) { 
      raw = map(raw, PBminimumValue, PBmaximumValue, 16383, 0);
      PBdirty = true;
    } else { raw = 8192; }
    return raw;
}

It's not elegant, but now I get a "zero" on pitch bend when centered. (Centered, according to my map, as my physical design does not allow a full "0 to max" throw -- it's a much smaller range. I do a calibration on the device to get the low/high range values that map uses. )

tttapa commented 1 month ago

Glad to hear that you got it to work. Did the approach above not work for you?

nelsonii commented 1 month ago

I was rushing to get a fix for a user, and didn't see a clear way to make it Bankable (I use banks to allow the user to change MIDI Channels). More of a "me not understanding it well enough" than an issue with the code. It's still on my list of things to work on. :-)

I'm also using the work-around for an issue one user was seeing with his DAW -- it wanted (not sure why) PB to keep sending the min or max values when at the limits. (Not just send "8192" once.) Like continuous send. This could very well be a DAW config issue -- but they are hard to debug with so many DAWs out there.