|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- //==============================================================================
- /**
- Helper class that takes chunks of incoming midi bytes, packages them into
- messages, and dispatches them to a midi callback.
-
- @tags{Audio}
- */
- class MidiDataConcatenator
- {
- public:
- MidiDataConcatenator (int initialBufferSize)
- : pendingSysexData ((size_t) initialBufferSize)
- {
- }
-
- void reset()
- {
- currentMessageLen = 0;
- pendingSysexSize = 0;
- pendingSysexTime = 0;
- }
-
- template <typename UserDataType, typename CallbackType>
- void pushMidiData (const void* inputData, int numBytes, double time,
- UserDataType* input, CallbackType& callback)
- {
- auto d = static_cast<const uint8*> (inputData);
-
- while (numBytes > 0)
- {
- auto nextByte = *d;
-
- if (pendingSysexSize != 0 || nextByte == 0xf0)
- {
- processSysex (d, numBytes, time, input, callback);
- currentMessageLen = 0;
- continue;
- }
-
- ++d;
- --numBytes;
-
- if (isRealtimeMessage (nextByte))
- {
- 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 (isInitialByte (nextByte))
- {
- currentMessage[0] = nextByte;
- currentMessageLen = 1;
- }
- else if (currentMessageLen > 0 && currentMessageLen < 3)
- {
- currentMessage[currentMessageLen++] = nextByte;
- }
- else
- {
- // message is too long or invalid MIDI - abandon it and start again with the next byte
- currentMessageLen = 0;
- continue;
- }
-
- auto expectedLength = MidiMessage::getMessageLengthFromFirstByte (currentMessage[0]);
-
- if (expectedLength == currentMessageLen)
- {
- callback.handleIncomingMidiMessage (input, MidiMessage (currentMessage, expectedLength, time));
- currentMessageLen = 1; // reset, but leave the first byte to use as the running status byte
- }
- }
- }
-
- private:
- template <typename UserDataType, typename CallbackType>
- void processSysex (const uint8*& d, int& numBytes, double time,
- UserDataType* input, CallbackType& callback)
- {
- if (*d == 0xf0)
- {
- pendingSysexSize = 0;
- pendingSysexTime = time;
- }
-
- pendingSysexData.ensureSize ((size_t) (pendingSysexSize + numBytes), false);
- auto totalMessage = static_cast<uint8*> (pendingSysexData.getData());
- auto dest = totalMessage + pendingSysexSize;
-
- do
- {
- if (pendingSysexSize > 0 && isStatusByte (*d))
- {
- if (*d == 0xf7)
- {
- *dest++ = *d++;
- ++pendingSysexSize;
- --numBytes;
- break;
- }
-
- if (*d >= 0xfa || *d == 0xf8)
- {
- callback.handleIncomingMidiMessage (input, MidiMessage (*d, time));
- ++d;
- --numBytes;
- }
- else
- {
- pendingSysexSize = 0;
- int used = 0;
- const MidiMessage m (d, numBytes, used, 0, time);
-
- if (used > 0)
- {
- callback.handleIncomingMidiMessage (input, m);
- numBytes -= used;
- d += used;
- }
-
- break;
- }
- }
- else
- {
- *dest++ = *d++;
- ++pendingSysexSize;
- --numBytes;
- }
- }
- while (numBytes > 0);
-
- if (pendingSysexSize > 0)
- {
- if (totalMessage [pendingSysexSize - 1] == 0xf7)
- {
- callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingSysexSize, pendingSysexTime));
- pendingSysexSize = 0;
- }
- else
- {
- callback.handlePartialSysexMessage (input, totalMessage, pendingSysexSize, pendingSysexTime);
- }
- }
- }
-
- static bool isRealtimeMessage (uint8 byte) { return byte >= 0xf8 && byte <= 0xfe; }
- static bool isStatusByte (uint8 byte) { return byte >= 0x80; }
- static bool isInitialByte (uint8 byte) { return isStatusByte (byte) && byte != 0xf7; }
-
- uint8 currentMessage[3];
- int currentMessageLen = 0;
-
- MemoryBlock pendingSysexData;
- double pendingSysexTime = 0;
- int pendingSysexSize = 0;
-
- JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator)
- };
-
- } // namespace juce
|