Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
134 lines (116 sloc) 4.71 KB
/* Copyright October 2017 - havencking@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Plastic Pitch MIDI for TeensyLC provides pitchbend per note name.
*
* Receives MIDI from any channel, and sends MIDI on the first 12 channels.
*
*/
#define NUM_MIDI_BYTES 50 // number of MIDI bytes to try reading per single pitchbend update
// standard MIDI values
#define NOTE_OFF_CMD 0x80 // first channel command
#define NOTE_ON_CMD 0x90 // first channel command
#define PITCHBEND_CMD 0xe0 // first channel command
// used by noteORvelocity in handleMIDIio()
#define NOTE 0
#define VELOCITY 1
int curPot; // potentiometers 0-11, pins A0-A11, and MIDI channels 1-12 correspond to notes (C, C#, D, D#, ... , B)
long curPitchbend; // pitchbend value (0-16384) that corresponds to curPot
long pitchbend[12]; // store pitchbend LSB values (0-128) for notes (C, C#, D, D#, ... , B)
long prev1Pitchbend[12];
long prev2Pitchbend[12];
//int pitchbendMSB[12]; // store pitchbend MSB values (0-128) for notes (C, C#, D, D#, ... , B)
int curCommand; // the current MIDI command
int curNote; // the received MIDI note
int curVelocity; // the received MIDI velocity
bool noteORvelocity; // toggle between note and velocity when reading incoming MIDI bytes from 0-127
void setup() {
Serial1.begin(31250); // open the serial port for MIDI
curPot = 0;
curPitchbend = 8192;
for (int i=0; i<36; i++) {
updatePitchbend();
}
// initialize MIDI parameters
curCommand = 0xFF;
curNote = 60;
curVelocity = 0;
noteORvelocity = NOTE;
}
void loop() {
// handle some MIDI I/O
for (int i=0; i<NUM_MIDI_BYTES; i++) {
handleMIDIio();
}
// read some pitchbend values, and send MIDI pitchbend if they changed
updatePitchbend();
}
// read a potentiometer, convert to pitchbend value (0-16384)
// analogRead returns 0-1023, use 1024 so that zero pitchbend (8192) is possible
// send MIDI pitchbend if the value changed, and increment to next potentiometer
void updatePitchbend() {
pitchbend[curPot] = 0.5 * (analogRead(curPot) * 16384 / 1024) + 0.3 * prev1Pitchbend[curPot] + 0.2 * prev2Pitchbend[curPot];
prev2Pitchbend[curPot] = prev1Pitchbend[curPot];
prev1Pitchbend[curPot] = pitchbend[curPot];
curPot = (curPot + 1) % 12;
}
// read one byte of incoming MIDI if available, respond to NOTE events, ignore the rest
void handleMIDIio() {
if (Serial1.available()) {
int data = Serial1.read();
if (data > 0xF7) {
// system realtime message, ignore the data, don't change curCommand
}
else if (data > 0x9F) { // commands other than noteON or noteOFF
curCommand = data;
}
else if (data > NOTE_ON_CMD - 1) { // && data < NOTE_ON_CMD + 16) { // a noteON command
curCommand = NOTE_ON_CMD; // note ON command first channel
}
else if (data > NOTE_OFF_CMD - 1) { // && data < NOTE_OFF_CMD + 16) { // a noteOFF command
curCommand = NOTE_OFF_CMD; // note OFF command first channel
}
else if ((curCommand == NOTE_ON_CMD || curCommand == NOTE_OFF_CMD) && noteORvelocity == NOTE) { // note data (0-127)
curNote = data;
noteORvelocity = VELOCITY;
}
else if ((curCommand == NOTE_ON_CMD || curCommand == NOTE_OFF_CMD) && noteORvelocity == VELOCITY) { // velocity data (0-127)
curVelocity = data;
noteORvelocity = NOTE;
// Send a noteON or noteOFF command now that note and velocity are read.
if (curCommand == NOTE_ON_CMD && curVelocity > 0) { // note ON
sendMIDI(PITCHBEND_CMD + curNote % 12, pitchbend[curNote % 12] & 127, pitchbend[curNote % 12] >> 7);
sendNoteOnCmd(curNote, curVelocity);
}
else { // note OFF
sendNoteOffCmd(curNote);
}
}
}
}
// send three bytes of MIDI out, e.g., note on, note number, velocity
void sendMIDI(int command, int byte1, int byte2) {
Serial1.write(command);
Serial1.write(byte1);
Serial1.write(byte2);
}
// turn a note on
void sendNoteOnCmd(int note, int velocity) {
sendMIDI(NOTE_ON_CMD + (note % 12), note, velocity); // send the MIDI note
}
// turn a note off
void sendNoteOffCmd(int note) {
sendMIDI(NOTE_OFF_CMD + (note % 12), note, 0);
}