#include "MidiBuffer.h" //============================================================================== /** A handy function to read un-aligned memory without a performance penalty or bus-error. */ template inline Type readUnaligned (const void* srcPtr) noexcept { Type value; memcpy(&value, srcPtr, sizeof(Type)); return value; } /** A handy function to write un-aligned memory without a performance penalty or bus-error. */ template inline void writeUnaligned (void* dstPtr, Type value) noexcept { memcpy(dstPtr, &value, sizeof (Type)); } namespace MidiBufferHelpers { inline int getEventTime (const void* d) noexcept { return readUnaligned(d); } inline uint16 getEventDataSize (const void* d) noexcept { return readUnaligned (static_cast (d) + sizeof (int32)); } inline uint16 getEventTotalSize (const void* d) noexcept { return (uint16) (getEventDataSize (d) + sizeof (int32) + sizeof (uint16)); } static int findActualEventLength (const uint8* data, int maxBytes) noexcept { auto byte = (unsigned int)*data; if (byte == 0xf0 || byte == 0xf7) { int i = 1; while (i < maxBytes) if (data[i++] == 0xf7) break; return i; } if (byte == 0xff) { if (maxBytes == 1) return 1; const auto var = FMidiMessage::readVariableLengthValue (data + 1, maxBytes - 1); return FMath::Min(maxBytes, var.value + 2 + var.bytesUsed); } if (byte >= 0x80) return FMath::Min(maxBytes, FMidiMessage::getMessageLengthFromFirstByte ((uint8) byte)); return 0; } static uint8* findEventAfter (uint8* d, uint8* endData, int samplePosition) noexcept { while (d < endData && getEventTime(d) <= samplePosition) d += getEventTotalSize(d); return d; } } //============================================================================== MidiBufferIterator& MidiBufferIterator::operator++() noexcept { data += sizeof (int32) + sizeof (uint16) + size_t (MidiBufferHelpers::getEventDataSize (data)); return *this; } MidiBufferIterator MidiBufferIterator::operator++ (int) noexcept { auto copy = *this; ++(*this); return copy; } MidiBufferIterator::reference MidiBufferIterator::operator*() const noexcept { return { data + sizeof (int32) + sizeof (uint16), MidiBufferHelpers::getEventDataSize (data), MidiBufferHelpers::getEventTime (data) }; } //============================================================================== FMidiBuffer::FMidiBuffer(const FMidiMessage& message) noexcept { addEvent(message, 0); } void FMidiBuffer::swapWith (FMidiBuffer& other) noexcept { Swap(data, other.data); } void FMidiBuffer::clear() noexcept { data.Reset(); } void FMidiBuffer::ensureSize (size_t minimumNumBytes) { ensure(data.GetAllocatedSize() >= (int) minimumNumBytes); } bool FMidiBuffer::isEmpty() const noexcept { return data.IsEmpty(); } void FMidiBuffer::clear (int startSample, int numSamples) { auto start = MidiBufferHelpers::findEventAfter (data.GetData(), data.GetData() + data.Num(), startSample - 1); auto end = MidiBufferHelpers::findEventAfter (start, data.GetData() + data.Num(), startSample + numSamples - 1); data.RemoveAt(static_cast(start - data.GetData()), static_cast(end - start)); } bool FMidiBuffer::addEvent (const FMidiMessage& m, int sampleNumber) { return addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber); } bool FMidiBuffer::addEvent (const void* newData, int maxBytes, int sampleNumber) { auto numBytes = MidiBufferHelpers::findActualEventLength (static_cast (newData), maxBytes); if (numBytes <= 0) return true; if (std::numeric_limits::max() < numBytes) { // This method only supports messages smaller than (1 << 16) bytes return false; } auto newItemSize = (size_t)numBytes + sizeof (int32) + sizeof (uint16); auto offset = (int)(MidiBufferHelpers::findEventAfter(data.GetData(), data.GetData() + data.Num(), sampleNumber) - data.GetData()); // data.insertMultiple(offset, 0, (int)newItemSize); for (int i = 0; i < (int)newItemSize; ++i) data.Insert(0, offset); auto* d = data.GetData() + offset; writeUnaligned (d, sampleNumber); d += sizeof (int32); writeUnaligned (d, static_cast (numBytes)); d += sizeof (uint16); memcpy (d, newData, (size_t) numBytes); return true; } void FMidiBuffer::addEvents (const FMidiBuffer& otherBuffer, int startSample, int numSamples, int sampleDeltaToAdd) { for (auto i = otherBuffer.findNextSamplePosition (startSample); i != otherBuffer.cend(); ++i) { const auto metadata = *i; if (metadata.samplePosition >= startSample + numSamples && numSamples >= 0) break; addEvent (metadata.data, metadata.numBytes, metadata.samplePosition + sampleDeltaToAdd); } } int FMidiBuffer::getNumEvents() noexcept { int n = 0; for (int i = 0; i < data.Num(); ++i, ++n) { i += MidiBufferHelpers::getEventTotalSize(&data[i]); } return n; } int FMidiBuffer::getFirstEventTime() const noexcept { return data.Num() > 0 ? MidiBufferHelpers::getEventTime(data.GetData()) : 0; } int FMidiBuffer::getLastEventTime() const noexcept { if (data.Num() == 0) return 0; // auto endData = data.end(); // for (auto d = data.begin();;) // { // auto nextOne = d + MidiBufferHelpers::getEventTotalSize(d); // // if (nextOne >= endData) // return MidiBufferHelpers::getEventTime (d); // // d = nextOne; // } for (int i = 0; i < data.Num(); ++i) { const int32 nextOne = i + MidiBufferHelpers::getEventTotalSize(&data[i]); if (nextOne >= data.Num()) return MidiBufferHelpers::getEventTime(&data[i]); i = nextOne; } return 0; } MidiBufferIterator FMidiBuffer::findNextSamplePosition (int samplePosition) const noexcept { return std::find_if (cbegin(), cend(), [&] (const MidiMessageMetadata& metadata) noexcept { return metadata.samplePosition >= samplePosition; }); }