There are, apparently, three ways to send polyphonic aftertouch using the Teensy MIDI libraries:
and
sendPolyPressure(note, pressure, channel) works with both DIN-5 serial MIDI and usb, but I get a deprecation warning when I compile.
sendAfterTouch(note, pressure, channel) is the non-deprecated version, but if I use this one, polyphonic aftertouch messages are sent when using serial MIDI but not when using USB.
sendAfterTouchPoly(note, pressure, channel) works fine for usb but isn't defined at all for serial MIDI.
In .arduino15/packages/teensy/hardware/avr/1.59.0/libraries/MIDI/src/MIDI.hpp, sendAfterTouch(...) and sendPolyPressure(...) with the same arguments do the exact same thing, so I was a bit confused why one would work and the other wouldn't, but then I found what are apparently a separate set of definitions specifically for usb over in .arduino15/packages/teensy/hardware/avr/1.59.0/libraries/USBHost_t36/USBHost_t36.h and in .arduino15/packages/teensy/hardware/avr/1.59.0/cores/teensy4/usb_midi.h. (That latter one seems to be what gets run when in USB midi mode.)
usb_midi.h only defines the non-polyphonic variant of sendAfterTouch (the one where we omit the note number and it affects the whole channel). If the polyphonic variant of sendAfterTouch isn't even defined for usb, I don't know why I don't just get a linker error when compiling. Instead it just seems to be silently doing nothing when the function is invoked at runtime.
I was able to get sendAfterTouch working the way I expect by adding the polyphonic variant to cores/teensy4/usb_midi.h:
Though, I had to remove the "cable=0" default argument from the non-polyphonic variant of sendAfterTouch because the compiler couldn't distinguish which version you meant if you supply three arguments. I'm not sure how important it is.
For what it's worth, this is my MIDI wrapper I use to let me use serial and usb MIDI at the same time:
I could work around this by calling sendAfterTouch(...) on serial MIDI and sendAfterTouchPoly(...) on usb or just use sendPolyPressure(...) on both and ignore the deprecation warning, but I thought I'd post it here just in case other people are running into the same thing.
(I've been using midisnoop under Xubuntu to monitor the MIDI stream.)
usbMIDI.sendAfterTouch(note, pressure, channel);
,usbMIDI.sendAfterTouchPoly(note, pressure, channel);
,and
usbMIDI.sendPolyPressure(note, pressure, channel);
.sendPolyPressure(note, pressure, channel) works with both DIN-5 serial MIDI and usb, but I get a deprecation warning when I compile.
sendAfterTouch(note, pressure, channel) is the non-deprecated version, but if I use this one, polyphonic aftertouch messages are sent when using serial MIDI but not when using USB.
sendAfterTouchPoly(note, pressure, channel) works fine for usb but isn't defined at all for serial MIDI.
In .arduino15/packages/teensy/hardware/avr/1.59.0/libraries/MIDI/src/MIDI.hpp, sendAfterTouch(...) and sendPolyPressure(...) with the same arguments do the exact same thing, so I was a bit confused why one would work and the other wouldn't, but then I found what are apparently a separate set of definitions specifically for usb over in .arduino15/packages/teensy/hardware/avr/1.59.0/libraries/USBHost_t36/USBHost_t36.h and in .arduino15/packages/teensy/hardware/avr/1.59.0/cores/teensy4/usb_midi.h. (That latter one seems to be what gets run when in USB midi mode.)
usb_midi.h only defines the non-polyphonic variant of sendAfterTouch (the one where we omit the note number and it affects the whole channel). If the polyphonic variant of sendAfterTouch isn't even defined for usb, I don't know why I don't just get a linker error when compiling. Instead it just seems to be silently doing nothing when the function is invoked at runtime.
I was able to get sendAfterTouch working the way I expect by adding the polyphonic variant to cores/teensy4/usb_midi.h:
void sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0xA0, note, pressure, channel, cable);
}
void sendAfterTouchPoly(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0xA0, note, pressure, channel, cable);
}
void sendAfterTouch(uint8_t note, uint8_t pressure, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) { // <----- NEW
send(0xA0, note, pressure, channel, cable);
}
void sendControlChange(uint8_t control, uint8_t value, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0xB0, control, value, channel, cable);
}
void sendProgramChange(uint8_t program, uint8_t channel, uint8_t cable=0) __attribute__((always_inline)) {
send(0xC0, program, 0, channel, cable);
}
void sendAfterTouch(uint8_t pressure, uint8_t channel) __attribute__((always_inline)) { // <---- MODIFIED
send(0xD0, pressure, 0, channel, 0);
}
Though, I had to remove the "cable=0" default argument from the non-polyphonic variant of sendAfterTouch because the compiler couldn't distinguish which version you meant if you supply three arguments. I'm not sure how important it is.
For what it's worth, this is my MIDI wrapper I use to let me use serial and usb MIDI at the same time:
bool useUsbMidi = true;
bool useDinMidi = false;
MIDI_CREATE_INSTANCE(HardwareSerial, Serial5, dinMidi);
uint64_t midiMsgsSent = 0;
uint64_t midiMsgsReceived = 0;
#define doMidi(func, ...) { \
midiMsgsSent++; \
if (useUsbMidi) { \
usbMIDI.func(__VA_ARGS__); \
} \
if (useDinMidi) { \
dinMidi.func(__VA_ARGS__); \
} \
}
void midiNoteOn(uint8_t note, uint8_t velocity, uint8_t channel) {
doMidi(sendNoteOn, note, velocity, channel);
}
void midiNoteOff(uint8_t note, uint8_t velocity, uint8_t channel) {
doMidi(sendNoteOff, note, velocity, channel);
}
void midiPitchBend(int16_t pb, uint8_t channel) {
doMidi(sendPitchBend, pb, channel);
}
void midiAfterTouch(uint8_t volume, uint8_t channel) {
doMidi(sendAfterTouch, volume, channel);
}
void midiPolyAfterTouch(uint8_t note, uint8_t pressure, uint8_t channel) {
doMidi(sendAfterTouch, note, pressure, channel); /* no message is sent on usb */
// doMidi(sendPolyPressure, note, pressure, channel); /* works on serial and usb, but causes deprecation warning */
}
void midiControlChange(uint8_t cc, uint8_t value, uint8_t channel) {
doMidi(sendControlChange, cc, value, channel);
}
void midiProgramChange(uint8_t bank, uint8_t channel) {
doMidi(sendProgramChange, bank, channel);
}
void midiRPN14Bit(uint8_t pmsb, uint8_t plsb, uint8_t vmsb, uint8_t vlsb, uint8_t channel) {
midiControlChange(0x65, pmsb, channel);
midiControlChange(0x64, plsb, channel);
midiControlChange(0x6, vmsb, channel);
midiControlChange(0x26, vlsb, channel);
midiControlChange(0x65, 127, channel);
midiControlChange(0x64, 127, channel);
}
void midiRPN(uint8_t pmsb, uint8_t plsb, uint8_t v, uint8_t channel) {
midiControlChange(0x65, pmsb, channel);
midiControlChange(0x64, plsb, channel);
midiControlChange(0x6, v, channel);
midiControlChange(0x65, 127, channel);
midiControlChange(0x64, 127, channel);
}
int midiBufferSize = 0;
void midiSetup(){
Serial.println("serial fifo size " + String(Serial5.availableForWrite()));
dinMidi.begin();
midiBufferSize = Serial5.availableForWrite();
Serial.println("midiBufferSize set to " + String(midiBufferSize)); /* the default size appears to be 39 bytes */
}
I could work around this by calling sendAfterTouch(...) on serial MIDI and sendAfterTouchPoly(...) on usb or just use sendPolyPressure(...) on both and ignore the deprecation warning, but I thought I'd post it here just in case other people are running into the same thing.
(I've been using midisnoop under Xubuntu to monitor the MIDI stream.)
Last edited: