AronaSlate/Source/AronaCore/Midi/MidiBuffer.cpp
2024-01-25 11:21:15 +08:00

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;
});
}