| @@ -33,79 +33,68 @@ namespace juce | |||||
| class MidiDataConcatenator | class MidiDataConcatenator | ||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | |||||
| MidiDataConcatenator (int initialBufferSize) | MidiDataConcatenator (int initialBufferSize) | ||||
| : pendingData ((size_t) initialBufferSize) | |||||
| : pendingSysexData ((size_t) initialBufferSize) | |||||
| { | { | ||||
| } | } | ||||
| void reset() | void reset() | ||||
| { | { | ||||
| pendingBytes = 0; | |||||
| runningStatus = 0; | |||||
| pendingDataTime = 0; | |||||
| currentMessageLen = 0; | |||||
| pendingSysexSize = 0; | |||||
| pendingSysexTime = 0; | |||||
| } | } | ||||
| template <typename UserDataType, typename CallbackType> | template <typename UserDataType, typename CallbackType> | ||||
| void pushMidiData (const void* inputData, int numBytes, double time, | void pushMidiData (const void* inputData, int numBytes, double time, | ||||
| UserDataType* input, CallbackType& callback) | UserDataType* input, CallbackType& callback) | ||||
| { | { | ||||
| const uint8* d = static_cast<const uint8*> (inputData); | |||||
| auto d = static_cast<const uint8*> (inputData); | |||||
| while (numBytes > 0) | while (numBytes > 0) | ||||
| { | { | ||||
| if (pendingBytes > 0 || d[0] == 0xf0) | |||||
| auto nextByte = *d; | |||||
| if (pendingSysexSize != 0 || nextByte == 0xf0) | |||||
| { | { | ||||
| processSysex (d, numBytes, time, input, callback); | processSysex (d, numBytes, time, input, callback); | ||||
| runningStatus = 0; | |||||
| currentMessageLen = 0; | |||||
| continue; | |||||
| } | } | ||||
| else | |||||
| { | |||||
| int len = 0; | |||||
| uint8 data[3]; | |||||
| while (numBytes > 0) | |||||
| { | |||||
| // If there's a realtime message embedded in the middle of | |||||
| // the normal message, handle it now.. | |||||
| if (*d >= 0xf8 && *d <= 0xfe) | |||||
| { | |||||
| callback.handleIncomingMidiMessage (input, MidiMessage (*d++, time)); | |||||
| --numBytes; | |||||
| } | |||||
| else | |||||
| { | |||||
| if (len == 0 && *d < 0x80 && runningStatus >= 0x80) | |||||
| data[len++] = runningStatus; | |||||
| data[len++] = *d++; | |||||
| --numBytes; | |||||
| const uint8 firstByte = data[0]; | |||||
| ++d; | |||||
| --numBytes; | |||||
| if (firstByte < 0x80 || firstByte == 0xf7) | |||||
| { | |||||
| len = 0; | |||||
| break; // ignore this malformed MIDI message.. | |||||
| } | |||||
| if (nextByte >= 0xf8 && nextByte <= 0xfe) // realtime message | |||||
| { | |||||
| callback.handleIncomingMidiMessage (input, MidiMessage (nextByte, time)); | |||||
| // These can be embedded in the middle of a normal message, so we won't | |||||
| // reset the currentMessageLen here. | |||||
| continue; | |||||
| } | |||||
| if (len >= MidiMessage::getMessageLengthFromFirstByte (firstByte)) | |||||
| break; | |||||
| } | |||||
| if (nextByte < 0x80) | |||||
| { | |||||
| if (currentMessageLen == 3) | |||||
| { | |||||
| currentMessageLen = 0; // message is too long - abandon it and start again with the next byte | |||||
| continue; | |||||
| } | } | ||||
| if (len > 0) | |||||
| { | |||||
| int used = 0; | |||||
| const MidiMessage m (data, len, used, 0, time); | |||||
| currentMessage[currentMessageLen++] = nextByte; | |||||
| } | |||||
| else | |||||
| { | |||||
| currentMessage[0] = nextByte; | |||||
| currentMessageLen = 1; | |||||
| } | |||||
| if (used <= 0) | |||||
| break; // malformed message.. | |||||
| auto expectedLength = MidiMessage::getMessageLengthFromFirstByte (currentMessage[0]); | |||||
| jassert (used == len); | |||||
| callback.handleIncomingMidiMessage (input, m); | |||||
| runningStatus = data[0]; | |||||
| } | |||||
| if (expectedLength == currentMessageLen) | |||||
| { | |||||
| callback.handleIncomingMidiMessage (input, MidiMessage (currentMessage, expectedLength)); | |||||
| currentMessageLen = 1; // reset, but leave the first byte to use as the running status byte | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -117,22 +106,22 @@ private: | |||||
| { | { | ||||
| if (*d == 0xf0) | if (*d == 0xf0) | ||||
| { | { | ||||
| pendingBytes = 0; | |||||
| pendingDataTime = time; | |||||
| pendingSysexSize = 0; | |||||
| pendingSysexTime = time; | |||||
| } | } | ||||
| pendingData.ensureSize ((size_t) (pendingBytes + numBytes), false); | |||||
| uint8* totalMessage = static_cast<uint8*> (pendingData.getData()); | |||||
| uint8* dest = totalMessage + pendingBytes; | |||||
| pendingSysexData.ensureSize ((size_t) (pendingSysexSize + numBytes), false); | |||||
| auto totalMessage = static_cast<uint8*> (pendingSysexData.getData()); | |||||
| auto dest = totalMessage + pendingSysexSize; | |||||
| do | do | ||||
| { | { | ||||
| if (pendingBytes > 0 && *d >= 0x80) | |||||
| if (pendingSysexSize > 0 && *d >= 0x80) | |||||
| { | { | ||||
| if (*d == 0xf7) | if (*d == 0xf7) | ||||
| { | { | ||||
| *dest++ = *d++; | *dest++ = *d++; | ||||
| ++pendingBytes; | |||||
| ++pendingSysexSize; | |||||
| --numBytes; | --numBytes; | ||||
| break; | break; | ||||
| } | } | ||||
| @@ -145,7 +134,7 @@ private: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| pendingBytes = 0; | |||||
| pendingSysexSize = 0; | |||||
| int used = 0; | int used = 0; | ||||
| const MidiMessage m (d, numBytes, used, 0, time); | const MidiMessage m (d, numBytes, used, 0, time); | ||||
| @@ -162,30 +151,32 @@ private: | |||||
| else | else | ||||
| { | { | ||||
| *dest++ = *d++; | *dest++ = *d++; | ||||
| ++pendingBytes; | |||||
| ++pendingSysexSize; | |||||
| --numBytes; | --numBytes; | ||||
| } | } | ||||
| } | } | ||||
| while (numBytes > 0); | while (numBytes > 0); | ||||
| if (pendingBytes > 0) | |||||
| if (pendingSysexSize > 0) | |||||
| { | { | ||||
| if (totalMessage [pendingBytes - 1] == 0xf7) | |||||
| if (totalMessage [pendingSysexSize - 1] == 0xf7) | |||||
| { | { | ||||
| callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime)); | |||||
| pendingBytes = 0; | |||||
| callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingSysexSize, pendingSysexTime)); | |||||
| pendingSysexSize = 0; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| callback.handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime); | |||||
| callback.handlePartialSysexMessage (input, totalMessage, pendingSysexSize, pendingSysexTime); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| MemoryBlock pendingData; | |||||
| double pendingDataTime = 0; | |||||
| int pendingBytes = 0; | |||||
| uint8 runningStatus = 0; | |||||
| uint8 currentMessage[3] = {}; | |||||
| int currentMessageLen = 0; | |||||
| MemoryBlock pendingSysexData; | |||||
| double pendingSysexTime = 0; | |||||
| int pendingSysexSize = 0; | |||||
| JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator) | JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator) | ||||
| }; | }; | ||||