diff options
Diffstat (limited to 'Midi.cpp')
| -rw-r--r-- | Midi.cpp | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/Midi.cpp b/Midi.cpp new file mode 100644 index 0000000..612b4bb --- /dev/null +++ b/Midi.cpp @@ -0,0 +1,482 @@ +/* Midi.cpp: Code for MIDI processing library, Arduino version + * + * (c) 2003-2008 Tymm Twillman <tymm@booyaka.com> + * + * This file is part of Tymm's Arduino Midi Library. + * + * Tymm's Arduino Midi Library is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either version 2.1 + * of the License, or (at your option) any later version. + * + * Tymm's Arduino Midi Library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Tymm's Arduino Midi Library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include "HardwareSerial.h" +#include "WConstants.h" +#include "Midi.h" + + +// This is used for tracking when we're processing a proprietary stream of data +// The assigned value is arbitrary; just for internal use. +static const int MODE_PROPRIETARY = 0xff; + + +// These are midi status message types as sent on the wire +static const int STATUS_EVENT_NOTE_OFF = 0x80; +static const int STATUS_EVENT_NOTE_ON = 0x90; +static const int STATUS_EVENT_VELOCITY_CHANGE = 0xA0; +static const int STATUS_EVENT_CONTROL_CHANGE = 0xB0; +static const int STATUS_EVENT_PROGRAM_CHANGE = 0xC0; +static const int STATUS_AFTER_TOUCH = 0xD0; +static const int STATUS_PITCH_CHANGE = 0xE0; +static const int STATUS_START_PROPRIETARY = 0xF0; +static const int STATUS_SONG_POSITION = 0xF2; +static const int STATUS_SONG_SELECT = 0xF3; +static const int STATUS_TUNE_REQUEST = 0xF6; +static const int STATUS_END_PROPRIETARY = 0xF7; +static const int STATUS_SYNC = 0xF8; +static const int STATUS_START = 0xFA; +static const int STATUS_CONTINUE = 0xFB; +static const int STATUS_STOP = 0xFC; +static const int STATUS_ACTIVE_SENSE = 0xFE; +static const int STATUS_RESET = 0xFF; + + + +// Handle decoding incoming MIDI traffic a byte at a time -- remembers +// what it needs to from one call to the next. +// +// This is a private function & not meant to be called from outside this class. +// It's used whenever data is available from the serial port. +// +void Midi::recvByte(int byte) +{ + int tmp; + int channel; + int bigval; /* temp 14-bit value for pitch, song pos */ + + + if (recvMode_ & MODE_PROPRIETARY + && byte != STATUS_END_PROPRIETARY) + { + /* If proprietary handling compiled in, just pass all data received + * after a START_PROPRIETARY event to proprietary_decode + * until get an END_PROPRIETARY event + */ + +#ifdef CONFIG_MIDI_PROPRIETARY + proprietaryDecode(byte); +#endif + + return; + } + + if (byte & 0x80) { + + /* All < 0xf0 events get at least 1 arg byte so + * it's ok to mask off the low 4 bits to figure + * out how to handle the event for < 0xf0 events. + */ + + tmp = byte; + + if (tmp < 0xf0) + tmp &= 0xf0; + + switch (tmp) { + /* These status events take 2 bytes as arguments */ + case STATUS_EVENT_NOTE_OFF: + case STATUS_EVENT_NOTE_ON: + case STATUS_EVENT_VELOCITY_CHANGE: + case STATUS_EVENT_CONTROL_CHANGE: + case STATUS_PITCH_CHANGE: + case STATUS_SONG_POSITION: + recvBytesNeeded_ = 2; + recvByteCount_ = 0; + recvEvent_ = byte; + break; + + /* 1 byte arguments */ + case STATUS_EVENT_PROGRAM_CHANGE: + case STATUS_AFTER_TOUCH: + case STATUS_SONG_SELECT: + recvBytesNeeded_ = 1; + recvByteCount_ = 0; + recvEvent_ = byte; + return; + + /* No arguments ( > 0xf0 events) */ + case STATUS_START_PROPRIETARY: + recvMode_ |= MODE_PROPRIETARY; + +#ifdef CONFIG_MIDI_PROPRIETARY + proprietaryDecodeStart(); +#endif + + break; + case STATUS_END_PROPRIETARY: + recvMode_ &= ~MODE_PROPRIETARY; + +#ifdef CONFIG_MIDI_PROPRIETARY + proprietaryDecodeEnd(); +#endif + + break; + case STATUS_TUNE_REQUEST: + handleTuneRequest(); + break; + case STATUS_SYNC: + handleSync(); + break; + case STATUS_START: + handleStart(); + break; + case STATUS_CONTINUE: + handleContinue(); + break; + case STATUS_STOP: + handleStop(); + break; + case STATUS_ACTIVE_SENSE: + handleActiveSense(); + break; + case STATUS_RESET: + handleReset(); + break; + } + + return; + } + + if (++recvByteCount_ == recvBytesNeeded_) { + /* Copy out the channel (if applicable; in some cases this will be meaningless, + * but in those cases the value will be ignored) + */ + channel = (recvEvent_ & 0x0f) + 1; + + tmp = recvEvent_; + if (tmp < 0xf0) { + tmp &= 0xf0; + } + + /* See if this event matches our MIDI channel + * (or we're accepting for all channels) + */ + if (!channelIn_ + || (channel == channelIn_) + || (tmp >= 0xf0)) + { + switch (tmp) { + case STATUS_EVENT_NOTE_ON: + /* If velocity is 0, it's actually a note off & should fall thru + * to the note off case + */ + if (byte) { + handleNoteOn(channel, recvArg0_, byte); + break; + } + + case STATUS_EVENT_NOTE_OFF: + handleNoteOff(channel, recvArg0_, byte); + break; + case STATUS_EVENT_VELOCITY_CHANGE: + handleVelocityChange(channel, recvArg0_, byte); + break; + case STATUS_EVENT_CONTROL_CHANGE: + handleControlChange(channel, recvArg0_, byte); + break; + case STATUS_EVENT_PROGRAM_CHANGE: + handleProgramChange(channel, byte); + break; + case STATUS_AFTER_TOUCH: + handleAfterTouch(channel, byte); + break; + case STATUS_PITCH_CHANGE: + bigval = (byte << 7) | recvArg0_; + handlePitchChange(bigval); + break; + case STATUS_SONG_POSITION: + bigval = (byte << 7) | recvArg0_; + handleSongPosition(bigval); + break; + case STATUS_SONG_SELECT: + handleSongSelect(byte); + break; + } + } + + /* Just reset the byte count; keep the same event -- might get more messages + trailing from current event. + */ + recvByteCount_ = 0; + } + + recvArg0_ = byte; +} + + +// Try to read data at serial port & pass anything read to processing function +void Midi::poll(void) +{ + int c; + + + // Just keep sucking data from serial port until it runs out, processing + // MIDI messages as we go + while((c = serial_.read()) != -1) { + recvByte(c); + } +} + + +// Send Midi NOTE OFF message to a given channel, with note 0-127 and velocity 0-127 +void Midi::sendNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) +{ + int status = STATUS_EVENT_NOTE_OFF | ((channel - 1) & 0x0f); + + + if (sendFullCommands_ || (lastStatusSent_ != status)) { + serial_.print(status, BYTE); + } + + serial_.print(note & 0x7f, BYTE); + serial_.print(velocity & 0x7f, BYTE); +} + + +// Send Midi NOTE ON message to a given channel, with note 0-127 and velocity 0-127 +void Midi::sendNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) +{ + int status = STATUS_EVENT_NOTE_ON | ((channel - 1) & 0x0f); + + + if (sendFullCommands_ || (lastStatusSent_ != status)) { + serial_.print(status, BYTE); + } + + serial_.print(note & 0x7f, BYTE); + serial_.print(velocity & 0x7f, BYTE); +} + + +// Send a Midi VELOCITY CHANGE message to a given channel, with given note 0-127, +// and new velocity 0-127 +void Midi::sendVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity) +{ + int status = STATUS_EVENT_VELOCITY_CHANGE | ((channel - 1) & 0x0f); + + + if (sendFullCommands_ || (lastStatusSent_ != status)) { + serial_.print(status, BYTE); + } + + serial_.print(note & 0x7f, BYTE); + serial_.print(velocity & 0x7f, BYTE); +} + + +// Send a Midi CC message to a given channel, as a given controller 0-127, with given +// value 0-127 +void Midi::sendControlChange(unsigned int channel, unsigned int controller, unsigned int value) +{ + int status = STATUS_EVENT_CONTROL_CHANGE | ((channel - 1) & 0x0f); + + + if (sendFullCommands_ || (lastStatusSent_ != status)) { + serial_.print(status, BYTE); + } + + serial_.print(controller & 0x7f, BYTE); + serial_.print(value & 0x7f, BYTE); +} + + +// Send a Midi PROGRAM CHANGE message to given channel, with program ID 0-127 +void Midi::sendProgramChange(unsigned int channel, unsigned int program) +{ + int status = STATUS_EVENT_PROGRAM_CHANGE | ((channel - 1) & 0x0f); + + + if (sendFullCommands_ || (lastStatusSent_ != status)) { + serial_.print(status, BYTE); + } + + serial_.print(program & 0x7f, BYTE); +} + + +// Send a Midi AFTER TOUCH message to given channel, with velocity 0-127 +void Midi::sendAfterTouch(unsigned int channel, unsigned int velocity) +{ + int status = STATUS_AFTER_TOUCH | ((channel - 1) & 0x0f); + + + if (sendFullCommands_ || (lastStatusSent_ != status)) { + serial_.print(status, BYTE); + } + + serial_.print(velocity & 0x7f, BYTE); +} + + +// Send a Midi PITCH CHANGE message, with a 14-bit pitch (always for all channels) +void Midi::sendPitchChange(unsigned int pitch) +{ + serial_.print(STATUS_PITCH_CHANGE, BYTE); + serial_.print(pitch & 0x7f, BYTE); + serial_.print((pitch >> 7) & 0x7f, BYTE); +} + + +// Send a Midi SONG POSITION message, with a 14-bit position (always for all channels) +void Midi::sendSongPosition(unsigned int position) +{ + serial_.print(STATUS_SONG_POSITION, BYTE); + serial_.print(position & 0x7f, BYTE); + serial_.print((position >> 7) & 0x7f, BYTE); +} + + +// Send a Midi SONG SELECT message, with a song ID of 0-127 (always for all channels) +void Midi::sendSongSelect(unsigned int song) +{ + serial_.print(STATUS_SONG_SELECT, BYTE); + serial_.print(song & 0x7f, BYTE); +} + + +// Send a Midi TUNE REQUEST message (TUNE REQUEST is always for all channels) +void Midi::sendTuneRequest(void) +{ + serial_.print(STATUS_TUNE_REQUEST, BYTE); +} + + +// Send a Midi SYNC message (SYNC is always for all channels) +void Midi::sendSync(void) +{ + serial_.print(STATUS_SYNC, BYTE); +} + + +// Send a Midi START message (START is always for all channels) +void Midi::sendStart(void) +{ + serial_.print(STATUS_START, BYTE); +} + + +// Send a Midi CONTINUE message (CONTINUE is always for all channels) +void Midi::sendContinue(void) +{ + serial_.print(STATUS_CONTINUE, BYTE); +} + + +// Send a Midi STOP message (STOP is always for all channels) +void Midi::sendStop(void) +{ + serial_.print(STATUS_STOP, BYTE); +} + + +// Send a Midi ACTIVE SENSE message (ACTIVE SENSE is always for all channels) +void Midi::sendActiveSense(void) +{ + serial_.print(STATUS_ACTIVE_SENSE, BYTE); +} + + +// Send a Midi RESET message (RESET is always for all channels) +void Midi::sendReset(void) +{ + serial_.print(STATUS_RESET, BYTE); +} + + +// Constructor -- set up defaults for variables, get ready for use (but don't +// take over serial port yet) +Midi::Midi(HardwareSerial &serial) : serial_(serial) +{ + /* Not in proprietary stream */ + recvMode_ = 0; + /* No bytes recevied */ + recvByteCount_ = 0; + /* Not processing an event */ + recvEvent_ = 0; + /* No arguments to the event we haven't received */ + recvArg0_ = 0; + /* Not waiting for bytes to complete a message */ + recvBytesNeeded_ = 0; + // There was no last event. + lastStatusSent_ = false; + // Don't send the extra bytes; just send deltas + sendFullCommands_ = false; + + /* Listening to all channels */ + channelIn_ = 0; +} + + +// Open the serial port and begin processing. +void Midi::begin(unsigned int channel, unsigned long baud) +{ + channelIn_ = channel; + serial_.begin(baud); +} + + +// Set (package-specific) parameters for the Midi instance +void Midi::setParam(unsigned int param, unsigned int val) +{ + if (param == PARAM_SEND_FULL_COMMANDS) { + if (val) { + sendFullCommands_ = true; + } else { + sendFullCommands_ = false; + } + } else if (param == PARAM_CHANNEL_IN) { + channelIn_ = val; + } +} + + +// Get (package-specific) parameters for the Midi instance +unsigned int Midi::getParam(unsigned int param) +{ + if (param == PARAM_SEND_FULL_COMMANDS) { + return sendFullCommands_; + } else if (param == PARAM_CHANNEL_IN) { + return channelIn_; + } + + return 0; +} + + +// Placeholders. You should subclass the Midi base class and define these to have your +// version called. +void Midi::handleNoteOff(unsigned int channel, unsigned int note, unsigned int velocity) {} +void Midi::handleNoteOn(unsigned int channel, unsigned int note, unsigned int velocity) {} +void Midi::handleVelocityChange(unsigned int channel, unsigned int note, unsigned int velocity) {} +void Midi::handleControlChange(unsigned int channel, unsigned int controller, unsigned int value) {} +void Midi::handleProgramChange(unsigned int channel, unsigned int program) {} +void Midi::handleAfterTouch(unsigned int channel, unsigned int velocity) {} +void Midi::handlePitchChange(unsigned int pitch) {} +void Midi::handleSongPosition(unsigned int position) {} +void Midi::handleSongSelect(unsigned int song) {} +void Midi::handleTuneRequest(void) {} +void Midi::handleSync(void) {} +void Midi::handleStart(void) {} +void Midi::handleContinue(void) {} +void Midi::handleStop(void) {} +void Midi::handleActiveSense(void) {} +void Midi::handleReset(void) {} |
