Merge branch 'avr_core' into Arduino

This commit is contained in:
Francois Best 2012-07-03 08:10:45 +02:00
commit efd72f9df6
2 changed files with 410 additions and 414 deletions

View File

@ -33,8 +33,7 @@ MIDI_Class MIDI;
/*! \brief Default constructor for MIDI_Class. */ /*! \brief Default constructor for MIDI_Class. */
MIDI_Class::MIDI_Class() MIDI_Class::MIDI_Class()
{ {
#if COMPILE_MIDI_IN && USE_CALLBACKS
#if USE_CALLBACKS
// Initialise callbacks to NULL pointer // Initialise callbacks to NULL pointer
mNoteOffCallback = NULL; mNoteOffCallback = NULL;
@ -57,7 +56,6 @@ MIDI_Class::MIDI_Class()
mSystemResetCallback = NULL; mSystemResetCallback = NULL;
#endif #endif
} }
@ -79,20 +77,14 @@ MIDI_Class::~MIDI_Class()
*/ */
void MIDI_Class::begin(const byte inChannel) void MIDI_Class::begin(const byte inChannel)
{ {
// Initialise the Serial port // Initialise the Serial port
MIDI_SERIAL_PORT.begin(MIDI_BAUDRATE); MIDI_SERIAL_PORT.begin(MIDI_BAUDRATE);
#if COMPILE_MIDI_OUT && USE_RUNNING_STATUS
#if COMPILE_MIDI_OUT
#if USE_RUNNING_STATUS
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
#endif // USE_RUNNING_STATUS #endif // COMPILE_MIDI_OUT && USE_RUNNING_STATUS
#endif // COMPILE_MIDI_OUT
#if COMPILE_MIDI_IN #if COMPILE_MIDI_IN
@ -131,9 +123,7 @@ void MIDI_Class::begin(const byte inChannel)
const byte MIDI_Class::genstatus(const kMIDIType inType, const byte MIDI_Class::genstatus(const kMIDIType inType,
const byte inChannel) const const byte inChannel) const
{ {
return ((byte)inType | ((inChannel-1) & 0x0F)); return ((byte)inType | ((inChannel-1) & 0x0F));
} }
@ -153,9 +143,11 @@ void MIDI_Class::send(kMIDIType type,
byte data2, byte data2,
byte channel) byte channel)
{ {
// Then test if channel is valid // Then test if channel is valid
if (channel >= MIDI_CHANNEL_OFF || channel == MIDI_CHANNEL_OMNI || type < NoteOff) { if (channel >= MIDI_CHANNEL_OFF ||
channel == MIDI_CHANNEL_OMNI ||
type < NoteOff)
{
#if USE_RUNNING_STATUS #if USE_RUNNING_STATUS
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
@ -164,9 +156,8 @@ void MIDI_Class::send(kMIDIType type,
return; // Don't send anything return; // Don't send anything
} }
if (type <= PitchBend) { if (type <= PitchBend) // Channel messages
// Channel messages {
// Protection: remove MSBs on data // Protection: remove MSBs on data
data1 &= 0x7F; data1 &= 0x7F;
data2 &= 0x7F; data2 &= 0x7F;
@ -175,7 +166,8 @@ void MIDI_Class::send(kMIDIType type,
#if USE_RUNNING_STATUS #if USE_RUNNING_STATUS
// Check Running Status // Check Running Status
if (mRunningStatus_TX != statusbyte) { if (mRunningStatus_TX != statusbyte)
{
// New message, memorise and send header // New message, memorise and send header
mRunningStatus_TX = statusbyte; mRunningStatus_TX = statusbyte;
MIDI_SERIAL_PORT.write(mRunningStatus_TX); MIDI_SERIAL_PORT.write(mRunningStatus_TX);
@ -187,16 +179,13 @@ void MIDI_Class::send(kMIDIType type,
// Then send data // Then send data
MIDI_SERIAL_PORT.write(data1); MIDI_SERIAL_PORT.write(data1);
if (type != ProgramChange && type != AfterTouchChannel) { if (type != ProgramChange && type != AfterTouchChannel)
MIDI_SERIAL_PORT.write(data2); MIDI_SERIAL_PORT.write(data2);
}
return; return;
} }
if (type >= TuneRequest && type <= SystemReset) { if (type >= TuneRequest && type <= SystemReset)
// System Real-time and 1 byte. sendRealTime(type); // System Real-time and 1 byte.
sendRealTime(type);
}
} }
@ -213,17 +202,18 @@ void MIDI_Class::sendNoteOn(byte NoteNumber,
byte Velocity, byte Velocity,
byte Channel) byte Channel)
{ {
send(NoteOn,NoteNumber,Velocity,Channel); send(NoteOn,NoteNumber,Velocity,Channel);
} }
/*! \brief Send a Note Off message (a real Note Off, not a Note On with null velocity) /*! \brief Send a Note Off message
\param NoteNumber Pitch value in the MIDI format (0 to 127). \param NoteNumber Pitch value in the MIDI format (0 to 127).
\param Velocity Release velocity (0 to 127). \param Velocity Release velocity (0 to 127).
\param Channel The channel on which the message will be sent (1 to 16). \param Channel The channel on which the message will be sent (1 to 16).
Note: you can send NoteOn with zero velocity to make a NoteOff, this is based
on the Running Status principle, to avoid sending status messages and thus
sending only NoteOn data. This method will always send a real NoteOff message.
Take a look at the values, names and frequencies of notes here: Take a look at the values, names and frequencies of notes here:
http://www.phys.unsw.edu.au/jw/notes.html http://www.phys.unsw.edu.au/jw/notes.html
*/ */
@ -231,9 +221,7 @@ void MIDI_Class::sendNoteOff(byte NoteNumber,
byte Velocity, byte Velocity,
byte Channel) byte Channel)
{ {
send(NoteOff,NoteNumber,Velocity,Channel); send(NoteOff,NoteNumber,Velocity,Channel);
} }
@ -244,9 +232,7 @@ void MIDI_Class::sendNoteOff(byte NoteNumber,
void MIDI_Class::sendProgramChange(byte ProgramNumber, void MIDI_Class::sendProgramChange(byte ProgramNumber,
byte Channel) byte Channel)
{ {
send(ProgramChange,ProgramNumber,0,Channel); send(ProgramChange,ProgramNumber,0,Channel);
} }
@ -262,13 +248,11 @@ void MIDI_Class::sendControlChange(byte ControlNumber,
byte ControlValue, byte ControlValue,
byte Channel) byte Channel)
{ {
send(ControlChange,ControlNumber,ControlValue,Channel); send(ControlChange,ControlNumber,ControlValue,Channel);
} }
/*! \brief Send a Polyphonic AfterTouch message (applies to only one specified note) /*! \brief Send a Polyphonic AfterTouch message (applies to a specified note)
\param NoteNumber The note to apply AfterTouch to (0 to 127). \param NoteNumber The note to apply AfterTouch to (0 to 127).
\param Pressure The amount of AfterTouch to apply (0 to 127). \param Pressure The amount of AfterTouch to apply (0 to 127).
\param Channel The channel on which the message will be sent (1 to 16). \param Channel The channel on which the message will be sent (1 to 16).
@ -277,9 +261,7 @@ void MIDI_Class::sendPolyPressure(byte NoteNumber,
byte Pressure, byte Pressure,
byte Channel) byte Channel)
{ {
send(AfterTouchPoly,NoteNumber,Pressure,Channel); send(AfterTouchPoly,NoteNumber,Pressure,Channel);
} }
@ -290,9 +272,7 @@ void MIDI_Class::sendPolyPressure(byte NoteNumber,
void MIDI_Class::sendAfterTouch(byte Pressure, void MIDI_Class::sendAfterTouch(byte Pressure,
byte Channel) byte Channel)
{ {
send(AfterTouchChannel,Pressure,0,Channel); send(AfterTouchChannel,Pressure,0,Channel);
} }
@ -305,11 +285,8 @@ void MIDI_Class::sendAfterTouch(byte Pressure,
void MIDI_Class::sendPitchBend(int PitchValue, void MIDI_Class::sendPitchBend(int PitchValue,
byte Channel) byte Channel)
{ {
const unsigned int bend = PitchValue - MIDI_PITCHBEND_MIN;
unsigned int bend = PitchValue - MIDI_PITCHBEND_MIN;
send(PitchBend,(bend & 0x7F),(bend >> 7) & 0x7F,Channel); send(PitchBend,(bend & 0x7F),(bend >> 7) & 0x7F,Channel);
} }
@ -322,10 +299,8 @@ void MIDI_Class::sendPitchBend(int PitchValue,
void MIDI_Class::sendPitchBend(double PitchValue, void MIDI_Class::sendPitchBend(double PitchValue,
byte Channel) byte Channel)
{ {
const int pitchval = PitchValue * MIDI_PITCHBEND_MAX;
int pitchval = PitchValue * MIDI_PITCHBEND_MAX;
sendPitchBend(pitchval,Channel); sendPitchBend(pitchval,Channel);
} }
@ -342,34 +317,24 @@ void MIDI_Class::sendSysEx(int length,
const byte *const array, const byte *const array,
bool ArrayContainsBoundaries) bool ArrayContainsBoundaries)
{ {
if (ArrayContainsBoundaries == false)
if (ArrayContainsBoundaries == false) { {
MIDI_SERIAL_PORT.write(0xF0); MIDI_SERIAL_PORT.write(0xF0);
for (int i=0;i<length;++i) { for (int i=0;i<length;++i)
MIDI_SERIAL_PORT.write(array[i]); MIDI_SERIAL_PORT.write(array[i]);
}
MIDI_SERIAL_PORT.write(0xF7); MIDI_SERIAL_PORT.write(0xF7);
} }
else { else
{
for (int i=0;i<length;++i) { for (int i=0;i<length;++i)
MIDI_SERIAL_PORT.write(array[i]); MIDI_SERIAL_PORT.write(array[i]);
}
} }
#if USE_RUNNING_STATUS #if USE_RUNNING_STATUS
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
#endif #endif
} }
@ -380,9 +345,7 @@ void MIDI_Class::sendSysEx(int length,
*/ */
void MIDI_Class::sendTuneRequest() void MIDI_Class::sendTuneRequest()
{ {
sendRealTime(TuneRequest); sendRealTime(TuneRequest);
} }
@ -394,29 +357,25 @@ void MIDI_Class::sendTuneRequest()
*/ */
void MIDI_Class::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble) void MIDI_Class::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble)
{ {
const byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) );
byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) );
sendTimeCodeQuarterFrame(data); sendTimeCodeQuarterFrame(data);
} }
/*! \brief Send a MIDI Time Code Quarter Frame. /*! \brief Send a MIDI Time Code Quarter Frame.
See MIDI Specification for more information.
\param data if you want to encode directly the nibbles in your program, \param data if you want to encode directly the nibbles in your program,
you can send the byte here. you can send the byte here.
See MIDI Specification for more information.
*/ */
void MIDI_Class::sendTimeCodeQuarterFrame(byte data) void MIDI_Class::sendTimeCodeQuarterFrame(byte data)
{ {
MIDI_SERIAL_PORT.write((byte)TimeCodeQuarterFrame); MIDI_SERIAL_PORT.write((byte)TimeCodeQuarterFrame);
MIDI_SERIAL_PORT.write(data); MIDI_SERIAL_PORT.write(data);
#if USE_RUNNING_STATUS #if USE_RUNNING_STATUS
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
#endif #endif
} }
@ -425,7 +384,6 @@ void MIDI_Class::sendTimeCodeQuarterFrame(byte data)
*/ */
void MIDI_Class::sendSongPosition(unsigned int Beats) void MIDI_Class::sendSongPosition(unsigned int Beats)
{ {
MIDI_SERIAL_PORT.write((byte)SongPosition); MIDI_SERIAL_PORT.write((byte)SongPosition);
MIDI_SERIAL_PORT.write(Beats & 0x7F); MIDI_SERIAL_PORT.write(Beats & 0x7F);
MIDI_SERIAL_PORT.write((Beats >> 7) & 0x7F); MIDI_SERIAL_PORT.write((Beats >> 7) & 0x7F);
@ -433,21 +391,18 @@ void MIDI_Class::sendSongPosition(unsigned int Beats)
#if USE_RUNNING_STATUS #if USE_RUNNING_STATUS
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
#endif #endif
} }
/*! \brief Send a Song Select message */ /*! \brief Send a Song Select message */
void MIDI_Class::sendSongSelect(byte SongNumber) void MIDI_Class::sendSongSelect(byte SongNumber)
{ {
MIDI_SERIAL_PORT.write((byte)SongSelect); MIDI_SERIAL_PORT.write((byte)SongSelect);
MIDI_SERIAL_PORT.write(SongNumber & 0x7F); MIDI_SERIAL_PORT.write(SongNumber & 0x7F);
#if USE_RUNNING_STATUS #if USE_RUNNING_STATUS
mRunningStatus_TX = InvalidType; mRunningStatus_TX = InvalidType;
#endif #endif
} }
@ -460,7 +415,8 @@ void MIDI_Class::sendSongSelect(byte SongNumber)
*/ */
void MIDI_Class::sendRealTime(kMIDIType Type) void MIDI_Class::sendRealTime(kMIDIType Type)
{ {
switch (Type) { switch (Type)
{
case TuneRequest: // Not really real-time, but one byte anyway. case TuneRequest: // Not really real-time, but one byte anyway.
case Clock: case Clock:
case Start: case Start:
@ -475,12 +431,12 @@ void MIDI_Class::sendRealTime(kMIDIType Type)
break; break;
} }
// Do not cancel Running Status for real-time messages as they can be interleaved within any message. // Do not cancel Running Status for real-time messages as they can be
// Though, TuneRequest can be sent here, and as it is a System Common message, it must reset Running Status. // interleaved within any message. Though, TuneRequest can be sent here,
// and as it is a System Common message, it must reset Running Status.
#if USE_RUNNING_STATUS #if USE_RUNNING_STATUS
if (Type == TuneRequest) mRunningStatus_TX = InvalidType; if (Type == TuneRequest) mRunningStatus_TX = InvalidType;
#endif #endif
} }
#endif // COMPILE_MIDI_OUT #endif // COMPILE_MIDI_OUT
@ -502,9 +458,7 @@ void MIDI_Class::sendRealTime(kMIDIType Type)
*/ */
bool MIDI_Class::read() bool MIDI_Class::read()
{ {
return read(mInputChannel); return read(mInputChannel);
} }
@ -513,12 +467,13 @@ bool MIDI_Class::read()
*/ */
bool MIDI_Class::read(const byte inChannel) bool MIDI_Class::read(const byte inChannel)
{ {
if (inChannel >= MIDI_CHANNEL_OFF)
return false; // MIDI Input disabled.
if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled. if (parse(inChannel))
{
if (parse(inChannel)) { if (input_filter(inChannel))
{
if (input_filter(inChannel)) {
#if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) #if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU)
thru_filter(inChannel); thru_filter(inChannel);
@ -527,50 +482,44 @@ bool MIDI_Class::read(const byte inChannel)
#if USE_CALLBACKS #if USE_CALLBACKS
launchCallback(); launchCallback();
#endif #endif
return true; return true;
} }
} }
return false; return false;
} }
// Private method: MIDI parser // Private method: MIDI parser
bool MIDI_Class::parse(byte inChannel) bool MIDI_Class::parse(byte inChannel)
{ {
const byte bytes_available = MIDI_SERIAL_PORT.available();
const int bytes_available = MIDI_SERIAL_PORT.available(); if (bytes_available == 0)
if (bytes_available <= 0) {
// No data available. // No data available.
return false; return false;
}
// If the buffer is full -> Don't Panic! Call the Vogons to destroy it.
if (bytes_available == 128) {
MIDI_SERIAL_PORT.flush();
}
else {
/* Parsing algorithm: /* Parsing algorithm:
Get a byte from the serial buffer. Get a byte from the serial buffer.
* If there is no pending message to be recomposed, start a new one. * If there is no pending message to be recomposed, start a new one.
- Find type and channel (if pertinent) - Find type and channel (if pertinent)
- Look for other bytes in buffer, call parser recursively, until the message is assembled or the buffer is empty. - Look for other bytes in buffer, call parser recursively,
* Else, add the extracted byte to the pending message, and check validity. When the message is done, store it. until the message is assembled or the buffer is empty.
* Else, add the extracted byte to the pending message, and check validity.
When the message is done, store it.
*/ */
const byte extracted = MIDI_SERIAL_PORT.read(); const byte extracted = MIDI_SERIAL_PORT.read();
if (mPendingMessageIndex == 0) { // Start a new pending message if (mPendingMessageIndex == 0)
{
// Start a new pending message
mPendingMessage[0] = extracted; mPendingMessage[0] = extracted;
// Check for running status first // Check for running status first
switch (getTypeFromStatusByte(mRunningStatus_RX)) { switch (getTypeFromStatusByte(mRunningStatus_RX))
{
// Only these types allow Running Status: // Only these types allow Running Status:
case NoteOff: case NoteOff:
case NoteOn: case NoteOn:
@ -580,13 +529,16 @@ bool MIDI_Class::parse(byte inChannel)
case AfterTouchChannel: case AfterTouchChannel:
case PitchBend: case PitchBend:
// If the status byte is not received, prepend it to the pending message // If the status byte is not received, prepend it
if (extracted < 0x80) { // to the pending message
if (extracted < 0x80)
{
mPendingMessage[0] = mRunningStatus_RX; mPendingMessage[0] = mRunningStatus_RX;
mPendingMessage[1] = extracted; mPendingMessage[1] = extracted;
mPendingMessageIndex = 1; mPendingMessageIndex = 1;
} }
// Else: well, we received another status byte, so the running status does not apply here. // Else: well, we received another status byte,
// so the running status does not apply here.
// It will be updated upon completion of this message. // It will be updated upon completion of this message.
break; break;
@ -597,8 +549,8 @@ bool MIDI_Class::parse(byte inChannel)
} }
switch (getTypeFromStatusByte(mPendingMessage[0])) { switch (getTypeFromStatusByte(mPendingMessage[0]))
{
// 1 byte messages // 1 byte messages
case Start: case Start:
case Continue: case Continue:
@ -644,7 +596,9 @@ bool MIDI_Class::parse(byte inChannel)
break; break;
case SystemExclusive: case SystemExclusive:
mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // As the message can be any lenght between 3 and MIDI_SYSEX_ARRAY_SIZE bytes // The message can be any lenght
// between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE;
mRunningStatus_RX = InvalidType; mRunningStatus_RX = InvalidType;
break; break;
@ -669,14 +623,15 @@ bool MIDI_Class::parse(byte inChannel)
#endif #endif
} }
else { else
{
// First, test if this is a status byte // First, test if this is a status byte
if (extracted >= 0x80) { if (extracted >= 0x80)
{
// Reception of status bytes in the middle of an uncompleted message // Reception of status bytes in the middle of an uncompleted message
// are allowed only for interleaved Real Time message or EOX // are allowed only for interleaved Real Time message or EOX
switch (extracted) { switch (extracted)
{
case Clock: case Clock:
case Start: case Start:
case Continue: case Continue:
@ -684,15 +639,12 @@ bool MIDI_Class::parse(byte inChannel)
case ActiveSensing: case ActiveSensing:
case SystemReset: case SystemReset:
/* // Here we will have to extract the one-byte message,
This is tricky. Here we will have to extract the one-byte message, // pass it to the structure for being read outside
pass it to the structure for being read outside the MIDI class, // the MIDI class, and recompose the message it was
and recompose the message it was interleaved into. // interleaved into. Oh, and without killing the running status..
// This is done by leaving the pending message as is,
Oh, and without killing the running status.. // it will be completed on next calls.
This is done by leaving the pending message as is, it will be completed on next calls.
*/
mMessage.type = (kMIDIType)extracted; mMessage.type = (kMIDIType)extracted;
mMessage.data1 = 0; mMessage.data1 = 0;
@ -705,27 +657,25 @@ bool MIDI_Class::parse(byte inChannel)
// End of Exclusive // End of Exclusive
case 0xF7: case 0xF7:
if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive)
{
// Store System Exclusive array in midimsg structure // Store System Exclusive array in midimsg structure
for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++) { for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++)
mMessage.sysex_array[i] = mPendingMessage[i]; mMessage.sysex_array[i] = mPendingMessage[i];
}
mMessage.type = SystemExclusive; mMessage.type = SystemExclusive;
// Get length // Get length
mMessage.data1 = (mPendingMessageIndex+1) & 0xFF; mMessage.data1 = (mPendingMessageIndex+1) & 0xFF;
mMessage.data2 = (mPendingMessageIndex+1) >> 8; mMessage.data2 = (mPendingMessageIndex+1) >> 8;
mMessage.channel = 0; mMessage.channel = 0;
mMessage.valid = true; mMessage.valid = true;
reset_input_attributes(); reset_input_attributes();
return true; return true;
} }
else { else
{
// Well well well.. error. // Well well well.. error.
reset_input_attributes(); reset_input_attributes();
return false; return false;
@ -735,36 +685,34 @@ bool MIDI_Class::parse(byte inChannel)
default: default:
break; break;
} }
} }
// Add extracted data byte to pending message // Add extracted data byte to pending message
mPendingMessage[mPendingMessageIndex] = extracted; mPendingMessage[mPendingMessageIndex] = extracted;
// Now we are going to check if we have reached the end of the message // Now we are going to check if we have reached the end of the message
if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) { if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1))
{
// "FML" case: fall down here with an overflown SysEx.. // "FML" case: fall down here with an overflown SysEx..
// This means we received the last possible data byte that can fit the buffer. // This means we received the last possible data byte that can fit
// If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE. // the buffer. If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE.
if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive)
{
reset_input_attributes(); reset_input_attributes();
return false; return false;
} }
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
mMessage.channel = (mPendingMessage[0] & 0x0F)+1; // Don't check if it is a Channel Message // Don't check if it is a Channel Message
mMessage.channel = (mPendingMessage[0] & 0x0F)+1;
mMessage.data1 = mPendingMessage[1]; mMessage.data1 = mPendingMessage[1];
// Save data2 only if applicable // Save data2 only if applicable
if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2]; if (mPendingMessageExpectedLenght == 3)
else mMessage.data2 = 0; mMessage.data2 = mPendingMessage[2];
else
mMessage.data2 = 0;
// Reset local variables // Reset local variables
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
@ -773,7 +721,8 @@ bool MIDI_Class::parse(byte inChannel)
mMessage.valid = true; mMessage.valid = true;
// Activate running status (if enabled for the received type) // Activate running status (if enabled for the received type)
switch (mMessage.type) { switch (mMessage.type)
{
case NoteOff: case NoteOff:
case NoteOn: case NoteOn:
case AfterTouchPoly: case AfterTouchPoly:
@ -792,7 +741,8 @@ bool MIDI_Class::parse(byte inChannel)
} }
return true; return true;
} }
else { else
{
// Then update the index of the pending message. // Then update the index of the pending message.
mPendingMessageIndex++; mPendingMessageIndex++;
@ -804,11 +754,7 @@ bool MIDI_Class::parse(byte inChannel)
// to parse the rest of the message. // to parse the rest of the message.
return parse(inChannel); return parse(inChannel);
#endif #endif
} }
}
} }
// What are our chances to fall here? // What are our chances to fall here?
@ -819,45 +765,41 @@ bool MIDI_Class::parse(byte inChannel)
// Private method: check if the received message is on the listened channel // Private method: check if the received message is on the listened channel
bool MIDI_Class::input_filter(byte inChannel) bool MIDI_Class::input_filter(byte inChannel)
{ {
// This method handles recognition of channel
// (to know if the message is destinated to the Arduino)
if (mMessage.type == InvalidType)
// This method handles recognition of channel (to know if the message is destinated to the Arduino) return false;
if (mMessage.type == InvalidType) return false;
// First, check if the received message is Channel // First, check if the received message is Channel
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
{
// Then we need to know if we listen to it // Then we need to know if we listen to it
if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) { if ((mMessage.channel == mInputChannel) ||
(mInputChannel == MIDI_CHANNEL_OMNI))
{
return true; return true;
} }
else { else
{
// We don't listen to this channel // We don't listen to this channel
return false; return false;
} }
} }
else { else
{
// System messages are always received // System messages are always received
return true; return true;
} }
} }
// Private method: reset input attributes // Private method: reset input attributes
void MIDI_Class::reset_input_attributes() void MIDI_Class::reset_input_attributes()
{ {
mPendingMessageIndex = 0; mPendingMessageIndex = 0;
mPendingMessageExpectedLenght = 0; mPendingMessageExpectedLenght = 0;
mRunningStatus_RX = InvalidType; mRunningStatus_RX = InvalidType;
} }
@ -868,9 +810,7 @@ void MIDI_Class::reset_input_attributes()
*/ */
kMIDIType MIDI_Class::getType() const kMIDIType MIDI_Class::getType() const
{ {
return mMessage.type; return mMessage.type;
} }
@ -881,27 +821,21 @@ kMIDIType MIDI_Class::getType() const
*/ */
byte MIDI_Class::getChannel() const byte MIDI_Class::getChannel() const
{ {
return mMessage.channel; return mMessage.channel;
} }
/*! \brief Get the first data byte of the last received message. */ /*! \brief Get the first data byte of the last received message. */
byte MIDI_Class::getData1() const byte MIDI_Class::getData1() const
{ {
return mMessage.data1; return mMessage.data1;
} }
/*! \brief Get the second data byte of the last received message. */ /*! \brief Get the second data byte of the last received message. */
byte MIDI_Class::getData2() const byte MIDI_Class::getData2() const
{ {
return mMessage.data2; return mMessage.data2;
} }
@ -911,9 +845,7 @@ byte MIDI_Class::getData2() const
*/ */
const byte * MIDI_Class::getSysExArray() const const byte * MIDI_Class::getSysExArray() const
{ {
return mMessage.sysex_array; return mMessage.sysex_array;
} }
/*! \brief Get the lenght of the System Exclusive array. /*! \brief Get the lenght of the System Exclusive array.
@ -923,20 +855,15 @@ const byte * MIDI_Class::getSysExArray() const
*/ */
unsigned int MIDI_Class::getSysExArrayLength() const unsigned int MIDI_Class::getSysExArrayLength() const
{ {
const unsigned int size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1;
unsigned int coded_size = ((unsigned int)(mMessage.data2) << 8) | mMessage.data1; return (size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : size;
return (coded_size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : coded_size;
} }
/*! \brief Check if a valid message is stored in the structure. */ /*! \brief Check if a valid message is stored in the structure. */
bool MIDI_Class::check() const bool MIDI_Class::check() const
{ {
return mMessage.valid; return mMessage.valid;
} }
@ -948,9 +875,7 @@ bool MIDI_Class::check() const
*/ */
void MIDI_Class::setInputChannel(const byte Channel) void MIDI_Class::setInputChannel(const byte Channel)
{ {
mInputChannel = Channel; mInputChannel = Channel;
} }
@ -984,8 +909,8 @@ void MIDI_Class::setHandleSystemReset(void (*fptr)(void))
*/ */
void MIDI_Class::disconnectCallbackFromType(kMIDIType Type) void MIDI_Class::disconnectCallbackFromType(kMIDIType Type)
{ {
switch (Type)
switch (Type) { {
case NoteOff: mNoteOffCallback = NULL; break; case NoteOff: mNoteOffCallback = NULL; break;
case NoteOn: mNoteOnCallback = NULL; break; case NoteOn: mNoteOnCallback = NULL; break;
case AfterTouchPoly: mAfterTouchPolyCallback = NULL; break; case AfterTouchPoly: mAfterTouchPolyCallback = NULL; break;
@ -1007,17 +932,15 @@ void MIDI_Class::disconnectCallbackFromType(kMIDIType Type)
default: default:
break; break;
} }
} }
// Private - launch callback function based on received type. // Private - launch callback function based on received type.
void MIDI_Class::launchCallback() void MIDI_Class::launchCallback()
{ {
// The order is mixed to allow frequent messages to trigger their callback faster. // The order is mixed to allow frequent messages to trigger their callback faster.
switch (mMessage.type)
switch (mMessage.type) { {
// Notes // Notes
case NoteOff: if (mNoteOffCallback != NULL) mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; case NoteOff: if (mNoteOffCallback != NULL) mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
case NoteOn: if (mNoteOnCallback != NULL) mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; case NoteOn: if (mNoteOnCallback != NULL) mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
@ -1049,7 +972,6 @@ void MIDI_Class::launchCallback()
default: default:
break; break;
} }
} }
@ -1072,31 +994,27 @@ void MIDI_Class::launchCallback()
*/ */
void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode) void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode)
{ {
mThruFilterMode = inThruFilterMode; mThruFilterMode = inThruFilterMode;
if (mThruFilterMode != Off) mThruActivated = true; if (mThruFilterMode != Off)
else mThruActivated = false; mThruActivated = true;
else
mThruActivated = false;
} }
/*! \brief Setter method: turn message mirroring on. */ /*! \brief Setter method: turn message mirroring on. */
void MIDI_Class::turnThruOn(kThruFilterMode inThruFilterMode) void MIDI_Class::turnThruOn(kThruFilterMode inThruFilterMode)
{ {
mThruActivated = true; mThruActivated = true;
mThruFilterMode = inThruFilterMode; mThruFilterMode = inThruFilterMode;
} }
/*! \brief Setter method: turn message mirroring off. */ /*! \brief Setter method: turn message mirroring off. */
void MIDI_Class::turnThruOff() void MIDI_Class::turnThruOff()
{ {
mThruActivated = false; mThruActivated = false;
mThruFilterMode = Off; mThruFilterMode = Off;
} }
@ -1115,45 +1033,60 @@ void MIDI_Class::thru_filter(byte inChannel)
*/ */
// If the feature is disabled, don't do anything. // If the feature is disabled, don't do anything.
if (!mThruActivated || (mThruFilterMode == Off)) return; if (!mThruActivated || (mThruFilterMode == Off))
return;
// First, check if the received message is Channel // First, check if the received message is Channel
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
{
const bool filter_condition = ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)); const bool filter_condition = ((mMessage.channel == mInputChannel) ||
(mInputChannel == MIDI_CHANNEL_OMNI));
// Now let's pass it to the output // Now let's pass it to the output
switch (mThruFilterMode) { switch (mThruFilterMode)
{
case Full: case Full:
send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); send(mMessage.type,
mMessage.data1,
mMessage.data2,
mMessage.channel);
return; return;
break; break;
case SameChannel: case SameChannel:
if (filter_condition) { if (filter_condition)
send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); {
send(mMessage.type,
mMessage.data1,
mMessage.data2,
mMessage.channel);
return; return;
} }
break; break;
case DifferentChannel: case DifferentChannel:
if (!filter_condition) { if (!filter_condition)
send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); {
send(mMessage.type,
mMessage.data1,
mMessage.data2,
mMessage.channel);
return; return;
} }
break; break;
case Off: case Off:
// Do nothing. // Do nothing.
// Technically it's impossible to get there because the case was already tested earlier. // Technically it's impossible to get there because
// the case was already tested earlier.
break; break;
default: default:
break; break;
} }
} }
else { else
{
// Send the message to the output // Send the message to the output
switch (mMessage.type) { switch (mMessage.type)
{
// Real Time and 1 byte // Real Time and 1 byte
case Clock: case Clock:
case Start: case Start:
@ -1188,14 +1121,8 @@ void MIDI_Class::thru_filter(byte inChannel)
break; break;
default: default:
break; break;
} }
} }
} }
#endif // Thru #endif // Thru

View File

@ -56,6 +56,7 @@
// END OF CONFIGURATION AREA // END OF CONFIGURATION AREA
// (do not modify anything under this line unless you know what you are doing) // (do not modify anything under this line unless you know what you are doing)
#define MIDI_BAUDRATE 31250 #define MIDI_BAUDRATE 31250
#define MIDI_CHANNEL_OMNI 0 #define MIDI_CHANNEL_OMNI 0
@ -76,7 +77,8 @@ typedef uint16_t word;
/*! Enumeration of MIDI types */ /*! Enumeration of MIDI types */
enum kMIDIType { enum kMIDIType
{
NoteOff = 0x80, ///< Note Off NoteOff = 0x80, ///< Note Off
NoteOn = 0x90, ///< Note On NoteOn = 0x90, ///< Note On
AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch
@ -98,7 +100,6 @@ enum kMIDIType {
InvalidType = 0x00 ///< For notifying errors InvalidType = 0x00 ///< For notifying errors
}; };
/*! Enumeration of Thru filter modes */ /*! Enumeration of Thru filter modes */
enum kThruFilterMode { enum kThruFilterMode {
Off = 0, ///< Thru disabled (nothing passes through). Off = 0, ///< Thru disabled (nothing passes through).
@ -108,6 +109,74 @@ enum kThruFilterMode {
}; };
enum eMIDICCNumber
{
// High resolution Continuous Controllers MSB (+32 for LSB) ----------------
BankSelect = 0,
ModulationWheel = 1,
BreathController = 2,
// CC3 undefined
FootController = 4,
PortamentoTime = 5,
DataEntry = 6,
ChannelVolume = 7,
Balance = 8,
// CC9 undefined
Pan = 10,
ExpressionController = 11,
EffectControl1 = 12,
EffectControl2 = 13,
// CC14 undefined
// CC15 undefined
GeneralPurposeController1 = 16,
GeneralPurposeController2 = 17,
GeneralPurposeController3 = 18,
GeneralPurposeController4 = 19,
// Switches ----------------------------------------------------------------
Sustain = 64,
Portamento = 65,
Sostenuto = 66,
SoftPedal = 67,
Legato = 68,
Hold2 = 69,
// Low resolution continuous controllers -----------------------------------
SoundController1 = 70, ///< Synth: Sound Variation FX: Exciter On/Off
SoundController2 = 71, ///< Synth: Harmonic Content FX: Compressor On/Off
SoundController3 = 72, ///< Synth: Release Time FX: Distortion On/Off
SoundController4 = 73, ///< Synth: Attack Time FX: EQ On/Off
SoundController5 = 74, ///< Synth: Brightness FX: Expander On/Off
SoundController6 = 75, ///< Synth: Decay Time FX: Reverb On/Off
SoundController7 = 76, ///< Synth: Vibrato Rate FX: Delay On/Off
SoundController8 = 77, ///< Synth: Vibrato Depth FX: Pitch Transpose On/Off
SoundController9 = 78, ///< Synth: Vibrato Delay FX: Flange/Chorus On/Off
SoundController10 = 79, ///< Synth: Undefined FX: Special Effects On/Off
GeneralPurposeController5 = 80,
GeneralPurposeController6 = 81,
GeneralPurposeController7 = 82,
GeneralPurposeController8 = 83,
PortamentoControl = 84,
// CC85 to CC90 undefined
Effects1 = 91, ///< Reverb send level
Effects2 = 92, ///< Tremolo depth
Effects3 = 93, ///< Chorus send level
Effects4 = 94, ///< Celeste depth
Effects5 = 95, ///< Phaser depth
// Channel Mode messages ---------------------------------------------------
AllSoundOff = 120,
ResetAllControllers = 121,
LocalControl = 122,
AllNotesOff = 123,
OmniModeOff = 124,
OmniModeOn = 125,
MonoModeOn = 126,
PolyModeOn = 127
};
/*! The midimsg structure contains decoded data /*! The midimsg structure contains decoded data
of a MIDI message read from the serial port of a MIDI message read from the serial port
with read() or thru(). with read() or thru().
@ -263,9 +332,9 @@ private:
byte mRunningStatus_RX; byte mRunningStatus_RX;
byte mInputChannel; byte mInputChannel;
byte mPendingMessage[MIDI_SYSEX_ARRAY_SIZE]; byte mPendingMessage[3]; // SysEx are dumped into mMessage directly.
unsigned int mPendingMessageExpectedLenght; unsigned int mPendingMessageExpectedLenght;
unsigned int mPendingMessageIndex; // Extended to unsigned int for larger sysex payloads. unsigned int mPendingMessageIndex; // Extended to unsigned int for larger SysEx payloads.
midimsg mMessage; midimsg mMessage;