From 335cad374dbaf220ba869273000c20e7cb0ef694 Mon Sep 17 00:00:00 2001 From: RobertoHE Date: Thu, 5 Aug 2021 21:30:44 +0200 Subject: [PATCH] Update parser Parser is more simple now. It not evaluate the size of package twice. Parser not check if a message is well constructed or not except first message of the package. Parser can transform runningStatus messages to Full Midi messages. If #define RUNNING_ENABLE is commented, parser will add data like full midi messages. FortySevenEffects MIDI serial parser respond identically to both method (runningStatus and Full), fortunately. --- src/BLEMIDI_Transport.h | 153 +++++++++++++++++++++++++++------------- 1 file changed, 104 insertions(+), 49 deletions(-) diff --git a/src/BLEMIDI_Transport.h b/src/BLEMIDI_Transport.h index a26f9b8..6f6c269 100644 --- a/src/BLEMIDI_Transport.h +++ b/src/BLEMIDI_Transport.h @@ -10,8 +10,6 @@ #include "BLEMIDI_Defs.h" #include "BLEMIDI_Namespace.h" -// #define CHECK_BIT(var, pos) (!!((var) & (1 << (pos)))) - BEGIN_BLEMIDI_NAMESPACE template @@ -49,7 +47,7 @@ public: { mBleClass.begin(mDeviceName, this); } - + void end() { mBleClass.end(); @@ -77,7 +75,7 @@ public: void endTransmission() { - if (mTxBuffer[mTxIndex - 1] == MIDI_NAMESPACE::MidiType::SystemExclusiveEnd) + if (mTxBuffer[mTxIndex - 1] == 0xF7) { if (mTxIndex >= sizeof(mTxBuffer)) { @@ -90,7 +88,7 @@ public: { mTxBuffer[mTxIndex - 1] = mTimestampLow; // or generate new ? } - mTxBuffer[mTxIndex++] = MIDI_NAMESPACE::MidiType::SystemExclusiveEnd; + mTxBuffer[mTxIndex++] = 0xF7; } mBleClass.write(mTxBuffer, mTxIndex); @@ -110,7 +108,6 @@ public: return mRxIndex; mRxBuffer[mRxIndex++] = byte; - return mRxIndex; } @@ -221,6 +218,14 @@ public: MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved from other messages – except for System Exclusive messages. */ + + /** + * If #define RUNNING_ENABLE is commented, it will transform all incoming runningStatus messages in full midi messages. + * Else, it will put in the buffer the same info that it had received (runningStatus will be not transformated). + * It recommend not use runningStatus by default. Only use if parser accepts runningStatus and your application has a so high transmission rate. + */ + //#define RUNNING_ENABLE + void receive(byte *buffer, size_t length) { // Pointers used to search through payload. @@ -229,108 +234,158 @@ public: // lastStatus used to capture runningStatus byte lastStatus; + // previousStatus used to continue a runningStatus interrupted by a timeStamp or a System Message. + byte previousStatus = 0x00; byte headerByte = buffer[lPtr++]; -// auto signatureIs1 = CHECK_BIT(headerByte, 7 - 1); // must be 1 -// auto reservedIs0 = !CHECK_BIT(headerByte, 6 - 1); // must be 0 + auto timestampHigh = 0x3f & headerByte; byte timestampByte = buffer[lPtr++]; uint16_t timestamp = 0; bool sysExContinuation = false; + bool runningStatusContinuation = false; if (timestampByte >= 80) // if bit 7 is 1, it's a timestampByte { - timestamp = setMidiTimestamp(headerByte, timestampByte); + auto timestampLow = 0x7f & timestampByte; + timestamp = timestampLow + (timestampHigh << 7); } else // if bit 7 is 0, it's the Continuation of a previous SysEx { sysExContinuation = true; - lPtr--; + lPtr--; // the second byte is part of the SysEx } - // While statement contains incrementing pointers and breaks when buffer size exceeded. + //While statement contains incrementing pointers and breaks when buffer size exceeded. while (true) { lastStatus = buffer[lPtr]; - if ((buffer[lPtr] < 0x80) && !sysExContinuation) - return; // Status message not present, bail + if (previousStatus == 0x00) + { + if ((lastStatus < 0x80) && !sysExContinuation) + return; // Status message not present and it is not a runningStatus continuation, bail + } + else if (lastStatus < 0x80) + { + lastStatus = previousStatus; + runningStatusContinuation = true; + } // Point to next non-data byte rPtr = lPtr; while ((buffer[rPtr + 1] < 0x80) && (rPtr < (length - 1))) rPtr++; - // look at l and r pointers and decode by size. - if (rPtr - lPtr < 1) + if (!runningStatusContinuation) { - // Time code or system - mBleClass.add(lastStatus); - } - else if (rPtr - lPtr < 2) - { - mBleClass.add(lastStatus); - mBleClass.add(buffer[lPtr + 1]); - } - else if (rPtr - lPtr < 3) - { - mBleClass.add(lastStatus); - mBleClass.add(buffer[lPtr + 1]); - mBleClass.add(buffer[lPtr + 2]); - } - else - { - - auto midiType = buffer[lPtr] & 0xF0; - if (sysExContinuation) - midiType = MIDI_NAMESPACE::MidiType::SystemExclusive; - - // Too much data // If not System Common or System Real-Time, send it as running status + + auto midiType = lastStatus & 0xF0; + if (sysExContinuation) + midiType = 0xF0; + switch (midiType) { - case MIDI_NAMESPACE::MidiType::NoteOff: - case MIDI_NAMESPACE::MidiType::NoteOn: - case MIDI_NAMESPACE::MidiType::AfterTouchPoly: - case MIDI_NAMESPACE::MidiType::ControlChange: - case MIDI_NAMESPACE::MidiType::PitchBend: + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + case 0xE0: +#ifdef RUNNING_ENABLE + mBleClass.add(lastStatus); +#endif for (auto i = lPtr; i < rPtr; i = i + 2) { +#ifndef RUNNING_ENABLE mBleClass.add(lastStatus); +#endif mBleClass.add(buffer[i + 1]); mBleClass.add(buffer[i + 2]); } break; - case MIDI_NAMESPACE::MidiType::ProgramChange: - case MIDI_NAMESPACE::MidiType::AfterTouchChannel: + case 0xC0: + case 0xD0: +#ifdef RUNNING_ENABLE + mBleClass.add(lastStatus); +#endif for (auto i = lPtr; i < rPtr; i = i + 1) { +#ifndef RUNNING_ENABLE mBleClass.add(lastStatus); +#endif mBleClass.add(buffer[i + 1]); } break; - case MIDI_NAMESPACE::MidiType::SystemExclusive: - - mBleClass.add(buffer[lPtr]); + case 0xF0: + mBleClass.add(lastStatus); for (auto i = lPtr; i < rPtr; i++) mBleClass.add(buffer[i + 1]); break; + default: break; } } + else + { +#ifndef RUNNING_ENABLE + auto midiType = lastStatus & 0xF0; + if (sysExContinuation) + midiType = 0xF0; + + switch (midiType) + { + case 0x80: + case 0x90: + case 0xA0: + case 0xB0: + case 0xE0: + //3 bytes full Midi -> 2 bytes runningStatus + for (auto i = lPtr; i <= rPtr; i = i + 2) + { + mBleClass.add(lastStatus); + mBleClass.add(buffer[i]); + mBleClass.add(buffer[i + 1]); + } + break; + case 0xC0: + case 0xD0: + //2 bytes full Midi -> 1 byte runningStatus + for (auto i = lPtr; i <= rPtr; i = i + 1) + { + mBleClass.add(lastStatus); + mBleClass.add(buffer[i]); + } + break; + + default: + break; + } +#else + mBleClass.add(lastStatus); + for (auto i = lPtr; i <= rPtr; i++) + mBleClass.add(buffer[i]); +#endif + runningStatusContinuation = false; + } if (++rPtr >= length) return; // end of packet + if (lastStatus < 0xf0) //exclude System Message. They must not be RunningStatus + { + previousStatus = lastStatus; + } + timestampByte = buffer[rPtr++]; if (timestampByte >= 80) // is bit 7 set? { - timestamp = setMidiTimestamp(headerByte, timestampByte); - // what do to with the timestamp? + auto timestampLow = 0x7f & timestampByte; + timestamp = timestampLow + (timestampHigh << 7); } // Point to next status