```cpp
// * MIDIsustainPitchbendv5
// This probably needs to be on MIDI ch 1 only to match JCS module in poly mode.
//#include <Wire.h>// maybe not needed
//#include <WireIMXRT.h>// maybe not needed
//#include <WireKinetis.h>// maybe not needed
//#include <antplusdefs.h>// maybe not needed// it compiles without them fine
#include <USBHost_t36.h>
// maybe add PWM frequency ? now running at 500hz could be much higher
//File myFile;
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
MIDIDevice midi1(myusb);
byte mc=1;// MIDI channel // 0 is omni mode //global // Saved in EEPROM
// mc probably needs to be on ch 1 to match JCS module
byte spp=0;// sustain pedal polarity 0 or 1// global // can be Saved in EEPROM
byte hv=0;// use to set pedal polarity
byte nhv=127;// to use other polarity swap values of hv and nhv.
byte mds=false;// modulation switch state
//byte EEPmode = false; // save to eeprom mode
byte sustainp = false;// sustain or hold mode
byte onegate = false; // gate 1 input
byte twogate = false;
byte threegate = false;
byte fourgate = false;
byte fivegate = false;
byte sixgate = false;
byte gateout1 = false; //gate 1 output
byte gateout2 = false;
byte gateout3 = false;
byte gateout4 = false;
byte gateout5 = false;
byte gateout6 = false;
int bndp;// bender right or positive
int bndn;// bender left or negative
byte sph1;//send retrigger gate pulse on next main loop
byte sph2;
byte sph3;
byte sph4;
byte sph5;
byte sph6;
unsigned long p1;// retigger pulse duration and timing
unsigned long p2;
unsigned long p3;
unsigned long p4;
unsigned long p5;
unsigned long p6;
byte onelht = false; // low to high transition of gate
byte twolht = false;
byte threelht = false;
byte fourlht = false;
byte fivelht = false;
byte sixlht = false;
byte setlht = false;// flag set to detect low to high gate transition in main loop.
void setup() {
Serial.begin(115200);
// maybe add PWM frequency ? now running at 500hz could be much higher
analogWriteResolution(12);// maybee add write frequency and frequencytimer2 library
pinMode(0, INPUT_PULLUP); // Sustain pedal polarity switch
//pinMode(13, INPUT_PULLUP); // maybe parellel sustain pedal input ?
pinMode(1, INPUT);
pinMode(2, INPUT);
pinMode(3, INPUT);
pinMode(4, INPUT);
pinMode(5, INPUT);
pinMode(6, INPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
pinMode(12, OUTPUT);
pinMode(22, OUTPUT);
pinMode(23, OUTPUT);
delay(300);
myusb.begin();
midi1.setHandleNoteOn(myNoteOn);
midi1.setHandleNoteOff(myNoteOff);
//midi1.setHandleAfterTouchPoly(myAfterTouchPoly);
midi1.setHandleControlChange(myControlChange);
//midi1.setHandleProgramChange(myProgramChange);
//midi1.setHandleAfterTouchChannel(myAfterTouchChannel);
midi1.setHandlePitchChange(myPitchChange);
// Only one of these System Exclusive handlers will actually be
// used. See the comments below for the difference between them.
//midi1.setHandleSystemExclusive(mySystemExclusiveChunk);
//midi1.setHandleSystemExclusive(mySystemExclusive);
//midi1.setHandleTimeCodeQuarterFrame(myTimeCodeQuarterFrame);
//midi1.setHandleSongPosition(mySongPosition);
//midi1.setHandleSongSelect(mySongSelect);
//midi1.setHandleTuneRequest(myTuneRequest);
//midi1.setHandleClock(myClock);
//midi1.setHandleStart(myStart);
//midi1.setHandleContinue(myContinue);
//midi1.setHandleStop(myStop);
//midi1.setHandleActiveSensing(myActiveSensing);
//midi1.setHandleSystemReset(mySystemReset);
// This generic System Real Time handler is only used if the
// more specific ones are not set.
//midi1.setHandleRealTimeSystem(myRealTimeSystem);
Serial.println("MIDI Sustain and Pitch Bend v6");
Serial.println("Firmware File: MIDIsustainPitchbendv6");
Serial.println("Teensy 4.0 board with MIDI host.");
//analogReadResolution(12); // maybee not needed // no analogRead()
PedPol();
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);
digitalWrite(10, LOW);
digitalWrite(11, LOW);
digitalWrite(12, LOW);
}// setup
void loop() {
// The handler functions are called when midi1 reads data. They
// will not be called automatically. You must call midi1.read()
// regularly from loop() for midi1 to actually read incoming
// data and run the handler functions as messages arrive.
//AudioNoInterrupts();
myusb.Task();
midi1.read();
spp = digitalRead(0);
//if (setlht == 0) {// made sustain not work at all and crashed.
sixgate = digitalRead(6);
fivegate = digitalRead(5);
fourgate = digitalRead(4);
threegate = digitalRead(3);
twogate = digitalRead(2);
onegate = digitalRead(1);// this can only do a digitalRead Here.
// these digital reads are for when sustain or hold is not on
///}
// problem is program can not tell what gate was retriggered.
// with hold on most of the retriggers heard are for the wrong gate// fixed
// JCS Gate 1 making MIDI box gate 2 go on.// fixed
// Problems mostly fixed by moving low to high transition detection into main loop in lhtSet() routine and not in myNoteOn() routine.
if ( onegate ){
if ( sustainp ){ //why is this only working if a note is held down ?// fixed by moving lht to main loop// gate/ triggers still sometimes get mixed up in sustain mode
if (setlht) lhtSet();// set low to high detection
if (onelht) { // gate 1 retrigger set low and start 1ms timer
gateout1 = false;
digitalWrite(7, gateout1);// maybe use real port address // it is faster
p1 = micros() + 900;// 1ms
sph1 = true; // flag to set gate 1 high again after 1ms
onelht = false;// reset low to high transistion flag to low
//Serial.println( "gate 1 retrigger flag");
} // lht1
if ((p1 < micros())&& (sph1)){ // gate 1 retrigger, 1ms has past , set gate 1 high again
gateout1 = true;
digitalWrite(7, gateout1);// maybe use real port address // it is faster
sph1 = false;// reset flag low because Gate 1 has been set high again
onelht= false;// reset flag low for next low to high transition of gate 1
/*
Serial.println( "gate 1 retriggered");
Serial.print("RT Gate , 1 to 6 =");
//Serial.print(digitalRead(1));
Serial.print(onegate);//reading wrong gate here // JCS gate 2 is triggering onegate variable
Serial.print(twogate);
Serial.print(threegate);
Serial.print(fourgate);
Serial.print(fivegate);
Serial.println(sixgate);
*/
}// micros
// Retrigger here once
// setup pulse timer 1ms ? low using loop
}// if sustinp
else { // gate 1 on without hold// reads correctly here but recycles continuisly // seems to be no problem for digitalWrite() to recycle
gateout1 = true;
//Serial.println( gatein1 );
//Serial.print(digitalRead(1));
//Serial.print("ON Gate IN , 1 to 6 =");
//Serial.print(onegate);
//Serial.print(twogate);
//Serial.print(threegate);
//Serial.print(fourgate);
//Serial.print(fivegate);
//Serial.println(sixgate);
digitalWrite(7, gateout1);
} // else sustain
} // if gate
else {
if ( sustainp == false ){// gate 1 off without hold
gateout1 = false;
digitalWrite(7, gateout1);
//Serial.println( gatein1 );
}// if sustainp
}// else
if ( twogate ){
if ( sustainp ){
if (setlht) lhtSet();
if (twolht) {
gateout2 = false;
digitalWrite(8, gateout2);// maybe use real port address // it is faster
p2 = micros() + 900;// 1ms
sph2 = true;
twolht = false;
//Serial.println( "gate 2 retrigger flag");
} // lht1
if ((p2 < micros())&& (sph2)){
gateout2 = true;
digitalWrite(8, gateout2);// maybe use real port address // it is faster
sph2 = false;
twolht= false;
//Serial.println( "gate 2 retriggered");
// critical, time-sensitive code here
//noInterrupts();
//Serial.println(digitalRead(2));
//Serial.println(digitalRead(3));
//delay(3);
//Serial.println(digitalRead(2));// gets stuck for 7.5 seconds then comes back with the correct readings for the last two and shows all four ?
//Serial.println(digitalRead(3));
//interrupts();
}// micros
// Retrigger here once
// setup pulse timer 1ms ? low using loop
}// if sustinp
else {
gateout2 = true;
digitalWrite(8, gateout2);
} // else sustain
}// if gate
else {
if ( sustainp == false ){
gateout2 = false;
digitalWrite(8, gateout2);
}// if sustainp
}// else
if ( threegate ){
if ( sustainp ){
if (setlht) lhtSet();
if (threelht) {
gateout3 = false;
digitalWrite(9, gateout3);// maybe use real port address // it is faster
p3 = micros() + 900;// 1ms
sph3 = true;
threelht = false;
} // lht1
if ((p3 < micros())&& (sph3)){
gateout3 = true;
digitalWrite(9, gateout3);// maybe use real port address // it is faster
sph3 = false;
threelht= false;
}// micros
// Retrigger here once
// setup pulse timer 1ms ? low using loop
}// if sustinp
else {
gateout3 = true;
digitalWrite(9, gateout3);
} // else sustain
}// if gate
else {
if ( sustainp == false ){
gateout3 = false;
digitalWrite(9, gateout3);
}// if sustainp
}// else
if ( fourgate ){
if ( sustainp ){
if (fourlht) {
gateout4 = false;
digitalWrite(10, gateout4);// maybe use real port address // it is faster
p4 = micros() + 900;// 1ms
sph4 = true;
fourlht = false;
} // lht1
if ((p4 < micros())&& (sph4)){
gateout4 = true;
digitalWrite(10, gateout4);// maybe use real port address // it is faster
sph4 = false;
fourlht= false;
}// micros
// Retrigger here once
// setup pulse timer 1ms ? low using loop
}// if sustinp
else {
gateout4 = true;
digitalWrite(10, gateout4);
} // else sustain
}// if gate
else {
if ( sustainp == false ){
gateout4 = false;
digitalWrite(10, gateout4);
}// if sustainp
}// else
if ( fivegate ){
if ( sustainp ){
if (setlht) lhtSet();
if (fivelht) {
gateout5 = false;
digitalWrite(11, gateout5);// maybe use real port address // it is faster // was coming out of 6 ?
p5 = micros() + 900;// 1ms
sph5 = true;
fivelht = false;
} // lht1
if ((p5 < micros())&& (sph5)){
gateout5 = true;
digitalWrite(11, gateout5);// maybe use real port address // it is faster
sph5 = false;
fivelht= false;
}// micros
}// if sustinp
else {
gateout5 = true;
digitalWrite(11, gateout5);
} // else sustain
}// if gate
else {
if ( sustainp == false ){
gateout5 = false;
digitalWrite(11, gateout5);
}// if sustainp
}// else
if ( sixgate ){
if ( sustainp ){
if (setlht) lhtSet();
if (sixlht) {
gateout6 = false;
digitalWrite(12, gateout6);// maybe use real port address // it is faster // was coming out of 5 ?
p6 = micros() + 900;// 1ms
sph6 = true;
sixlht = false;
} // lht1
if ((p6 < micros())&& (sph6)){
gateout6 = true;
digitalWrite(12, gateout6);// maybe use real port address // it is faster
sph6 = false;
sixlht= false;
}// micros
}// if sustinp
else {
gateout6 = true;
digitalWrite(12, gateout6);
} // else sustain
}// if gate
else {
if ( sustainp == false ){
gateout6 = false;
digitalWrite(12, gateout6);
}// if sustainp
}// else
}// Main loop
void myNoteOn(byte channel, byte note, byte velocity) {// will only retrigger with hold on if at least one note is held down.// problem fixxed
// you cannot do a digitalRead relilibly within This myNoteOn Routine
if (( mc == channel ) || ( mc == 0 )) {// problem is no voice related gate alicaton is possible
if ( sustainp ){// adding this keeps it from retriggering as sustain or hold is turned on
//sustainp = false; // did not work caused no sustain at all
setlht = true;//sets to detect low to high gate transitions in main loop// low to high transition detection will be done in main loop and needs to be.
// myNoteOn() has strainge problems// vaiables do not get set correctly sometimes and digitalRead() also has problems in this.
}// if sustinp
//Serial.print(", note=");
//Serial.print(note, DEC);
//Serial.print(", velocity=");
//Serial.println(velocity, DEC);
}//mc
}//my
void myNoteOff(byte channel, byte note, byte velocity) {// adding note off seem to change things the most// sometimes will not sound until note is release in sustain mode// fixed
if (( mc == channel ) || ( mc == 0 )) {
if ( onegate == 0) {// detect gate off
onelht = false;// reset gate 1 low to high detaction flag
sph1 = false;// reset send retrigger pulse on flag
}
if ( twogate == 0) {
twolht = false;
sph2 = false;
}
if ( threegate == 0) {
threelht = false;
sph3 = false;
}
if ( fourgate == 0) {
fivelht = false;
sph4 = false;
}
if ( sixgate == 0) {
sixlht = false;// not sure how this works but swapping 5 and 6 it fixed it for the most part // probably strainge compiler error // more unique varable names could help// error seems to be gone now
sph6 = false;
}
if ( fivegate == 0) {
fivelht = false;
sph5 = false;
}
// */
//Serial.print("Note Off, ch=");
//Serial.print(channel, DEC);
//Serial.print(", note=");
//Serial.print(note, DEC);
//Serial.print(", velocity=");
//Serial.println(velocity, DEC);
}//mc
}//my
/*
//void myAfterTouchPoly(byte channel, byte note, byte velocity) {// Nothing on the S-10
// Serial.print("AfterTouch Change, ch=");
// Serial.print(channel, DEC);
// Serial.print(", note=");
//Serial.print(note, DEC);
//Serial.print(", velocity=");
//Serial.println(velocity, DEC);
//}
*/
void myControlChange(byte channel, byte control, byte value) { //This works With the S-10 joystick switch as control #1 value on #127 off #0
//Serial.print("Control Change, ch=");// control #123 may be all notes off ?
//Serial.print(channel, DEC); // bender will need to take value, do math with bender range, and recall newsineshigh.
//Serial.print(", control=");
//Serial.print(control, DEC);
//Serial.print(", value=");
//Serial.println(value, DEC); // need to add sustain pedal function, control 64, value off 0, on 127// will need to give polarity option.
if (( mc == channel ) || ( mc == 0 )) {//read gates here also
if ( control == 64 ){ // Maybe even get strainge and invert the fuction touching notes to turn them off ? just an Idea.
PedPol(); // pedal works perfect with no changes needed.
if ( value == hv ){// had to use reverse polarity pedal/0 = hold or hv/ 127= not hold or nhv.
sustainp = true;
}//value==hv
if ( value == nhv ){
sustainp = false;
}//value==nhv
}//control==64
if ( control == 1 ){
if ( value == 127 ){// could be dynamic on some keyboard controllers // check it // Triton LE it is dynamic
//Serial.println("Modulation Switch Full On 127");
mds=true;
}//value=127
else{//else of value==127
//Serial.print(value);
//Serial.println(" Modulation Switch Off");
mds=false;
}//else of value==127
}//control
}//mc
}//my
/*
void myProgramChange(byte channel, byte program) {
//Serial.print("Program Change, ch=");// Programs start on 0 // channel starts on 1
//Serial.print(channel, DEC);
//Serial.print(", program=");
//Serial.println(program, DEC);
if (( mc == channel ) || ( mc == 0 )) {// ch 0 is omni mode
if (mp){ // Only act if MIDI program change is set to ON
}//mp
}//mc
}//my
//void myAfterTouchChannel(byte channel, byte pressure) { // Nothing on the S-10
// Serial.print("After Touch, ch=");
// Serial.print(channel, DEC);
// Serial.print(", pressure=");
// Serial.println(pressure, DEC);
//}
*/
void myPitchChange(byte channel, int pitch) { // This works with the joystick on the S-10
//Serial.print("Pitch Change, ch=");
//Serial.print(channel, DEC);
//Serial.print(", pitch=");
//Serial.println(pitch, DEC);
if (( mc == channel ) || ( mc == 0 )) {// pitch has a range of +/-8192 then converted to +/- 100 and multiplied by bender range
if ( pitch > -1 ) {
//Serial.println(abs(pitch));
//Serial.println(pitch);
bndp = pitch / 2;// bender right side or +
bndn = 0;
//Serial.println(bndp);
analogWrite(22,bndp);// maybe add PWM frequency ? now running at 500hz could be much higher
analogWrite(23,bndn);// seems to work ok without PWM frequency// there are two stages of 10uf filtering and it is not too slow.
}// if pitch
else{
bndn = ( abs( pitch ) / 2 ) - 1;// bender left side or -
bndp = 0;
//Serial.println(abs(pitch));
//Serial.println(bndn);
analogWrite(23,bndn);
analogWrite(22,bndp);
}// else
}//mc
}//my
/*
// This 3-input System Exclusive function is more complex, but allows you to
// process very large messages which do not fully fit within the midi1's
// internal buffer. Large messages are given to you in chunks, with the
// 3rd parameter to tell you which is the last chunk. This function is
// a Teensy extension, not available in the Arduino MIDI library.
//
//void mySystemExclusiveChunk(const byte *data, uint16_t length, bool last) {
//Serial.print("SysEx Message: ");
//printBytes(data, length);
// if (last) {
// Serial.println(" (end)");
//} else {
// Serial.println(" (to be continued)");
//}
//}
// This simpler 2-input System Exclusive function can only receive messages
// up to the size of the internal buffer. Larger messages are truncated, with
// no way to receive the data which did not fit in the buffer. If both types
// of SysEx functions are set, the 3-input version will be called by midi1.
//
//void mySystemExclusive(byte *data, unsigned int length) {
//Serial.print("SysEx Message: ");
//printBytes(data, length);
//Serial.println();
//}
//void myTimeCodeQuarterFrame(byte data) {
//static char SMPTE[8]={'0','0','0','0','0','0','0','0'};
//static byte fps=0;
//byte index = data >> 4;
//byte number = data & 15;
//if (index == 7) {
// fps = (number >> 1) & 3;
// number = number & 1;
// }
//if (index < 8 || number < 10) {
// SMPTE[index] = number + '0';
// Serial.print("TimeCode: "); // perhaps only print when index == 7
//Serial.print(SMPTE[7]);
// Serial.print(SMPTE[6]);
// Serial.print(':');
// Serial.print(SMPTE[5]);
// Serial.print(SMPTE[4]);
// Serial.print(':');
// Serial.print(SMPTE[3]);
// Serial.print(SMPTE[2]);
//Serial.print('.');
//Serial.print(SMPTE[1]); // perhaps add 2 to compensate for MIDI latency?
//Serial.print(SMPTE[0]);
// switch (fps) {
// case 0: Serial.println(" 24 fps"); break;
// case 1: Serial.println(" 25 fps"); break;
// case 2: Serial.println(" 29.97 fps"); break;
// case 3: Serial.println(" 30 fps"); break;
// }
// } else {
// Serial.print("TimeCode: invalid data = ");
// Serial.println(data, HEX);
// }
//}
//void mySongPosition(uint16_t beats) {
// Serial.print("Song Position, beat=");
// Serial.println(beats);
//}
//void mySongSelect(byte songNumber) {
// Serial.print("Song Select, song=");
// Serial.println(songNumber, DEC);
//}
//void myTuneRequest() { // nothing on the S-10
// Serial.println("Tune Request");
//}
//void myClock() {
// Serial.println("Clock");
//}
//void myStart() {// nothing on the S-10
// Serial.println("Start");
//}
//void myContinue() {
// Serial.println("Continue");
//}
//void myStop() {
// Serial.println("Stop");
//}
//void myActiveSensing() {
// Serial.println("Actvice Sensing");
//}
//void mySystemReset() {
// Serial.println("System Reset");
//}
//void myRealTimeSystem(uint8_t realtimebyte) {//nothing on the S-10
// Serial.print("Real Time Message, code=");
// Serial.println(realtimebyte, HEX);
//}
//void printBytes(const byte *data, unsigned int size) {
// while (size > 0) {
// byte b = *data++;
// if (b < 16) Serial.print('0');
// Serial.print(b, HEX);
// if (size > 1) Serial.print(' ');
// size = size - 1;
//}
//}
*/
void PedPol(void){
if ( spp == 0) {
hv=0; // could be pure math hv=spp*127; with no if
nhv=127; // could be pure math nhv= ~spp*127; with no if
}//spp==0
if ( spp == 1) {
hv=127;
nhv=0;
}//spp==1
}//PedPol
void lhtSet(void){// this is starting to work but now retriggers when hold is activated // Retrigger problem fixed by adding if(sustainp) to myNoteOn()
onegate = digitalRead(1);// this can only do a digitalRead Here, not in myNoteOn().
twogate = digitalRead(2);// lhtSet is used when hold or sustain is activated
threegate = digitalRead(3);// detect low to high transition
fourgate = digitalRead(4);// make this only if the lht variable is not allready set
fivegate = digitalRead(5);
sixgate = digitalRead(6);// maybe try removing these digitalRead()'s
if ( onegate ) onelht = true;// sets that a low to high transition has happend
if ( twogate ) twolht = true;// this still sounds better even if it retriggers every note being held down
if ( threegate ) threelht = true;
if ( fourgate ) fourlht = true;
if ( fivegate ) fivelht = true;
if ( sixgate ) sixlht = true;
setlht = false; //reset read low to high detections flag for next Note on.
/*
Serial.print("lhtSet Gate IN , 1 to 6 =");// only active with sustain on
Serial.print(onegate);//reading wrong gate here // JCS gate 2 is triggering onegate variable
Serial.print(twogate);
Serial.print(threegate);
Serial.print(fourgate);
Serial.print(fivegate);
Serial.println(sixgate);
*/
}// lhtSet()
// is it still mixing up variables ? if so fix that first. gate 1 ID correct gate 3 IDed as gate 2 ? // not recycling
// digitalRead() is miss IDing gate 3 as gate 2, proven// try disable interups ? // did not help
// also reading gate 4 as gate 3 at this point.//
// maybe try to make a single 6 bit number to track what notes are being held down (gates on) and not to rertrigger them
// make new and old versions of that variable to detect changes.
// another idea is to do hold down stream in the Decay of the Hexsynator. R70 or R76 is the decay discharge resistor and the cap is C24 and is 1uf.
// maybe use a BS170 MOSFET.
// if (( sixgate ) && ( sixlht == 0 )) {
// if (( fivegate ) && ( fivelht == 0 )) {
// if (( fourgate ) && ( fourlht == 0 )) {
// if (( threegate ) && ( threelht == 0 )) {
// if (( twogate ) && ( twolht == 0 )) {
// if (( onegate ) && ( onelht == 0 )) {
/*
if ( sixgate ) { // got worse with sustain on // not knowing what gate to retrigger
sixlht = true; // sets that a low to high transition has happend
setlht = false; //reset read low to high detections flag for next Note on.
return;
} //6
if ( fivegate ) {
fivelht = true;
setlht = false; //reset read low to high detections flag for next Note on.
return;
}//5
if ( fourgate ) {
fourlht = true;
setlht = false; //reset read low to high detections flag for next Note on.
return;
}//4
if ( threegate ) {
threelht = true;
setlht = false; //reset read low to high detections flag for next Note on.
return;
}//3
if ( twogate ) {
twolht = true;
setlht = false; //reset read low to high detections flag for next Note on.
return;
}//2
if ( onegate ) {
onelht = true; // sets that a low to high transition has happend
setlht = false; //reset read low to high detections flag for next Note on.
return;
}//1
*/
```