219 lines
6.4 KiB
C++
219 lines
6.4 KiB
C++
#include "MidiBuffer.h"
|
|
|
|
//==============================================================================
|
|
/** A handy function to read un-aligned memory without a performance penalty or bus-error. */
|
|
template <typename Type>
|
|
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 <typename Type>
|
|
inline void writeUnaligned (void* dstPtr, Type value) noexcept
|
|
{
|
|
memcpy(dstPtr, &value, sizeof (Type));
|
|
}
|
|
|
|
namespace MidiBufferHelpers
|
|
{
|
|
inline int getEventTime (const void* d) noexcept
|
|
{
|
|
return readUnaligned<int32>(d);
|
|
}
|
|
|
|
inline uint16 getEventDataSize (const void* d) noexcept
|
|
{
|
|
return readUnaligned<uint16> (static_cast<const char*> (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<int>(start - data.GetData()), static_cast<int>(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<const uint8*> (newData), maxBytes);
|
|
|
|
if (numBytes <= 0)
|
|
return true;
|
|
|
|
if (std::numeric_limits<uint16>::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<int32> (d, sampleNumber);
|
|
d += sizeof (int32);
|
|
writeUnaligned<uint16> (d, static_cast<uint16> (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;
|
|
});
|
|
}
|