JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_MidiBuffer.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26namespace MidiBufferHelpers
27{
28 inline int getEventTime (const void* d) noexcept
29 {
30 return readUnaligned<int32> (d);
31 }
32
33 inline uint16 getEventDataSize (const void* d) noexcept
34 {
35 return readUnaligned<uint16> (static_cast<const char*> (d) + sizeof (int32));
36 }
37
38 inline uint16 getEventTotalSize (const void* d) noexcept
39 {
40 return (uint16) (getEventDataSize (d) + sizeof (int32) + sizeof (uint16));
41 }
42
43 static int findActualEventLength (const uint8* data, int maxBytes) noexcept
44 {
45 auto byte = (unsigned int) *data;
46
47 if (byte == 0xf0 || byte == 0xf7)
48 {
49 int i = 1;
50
51 while (i < maxBytes)
52 if (data[i++] == 0xf7)
53 break;
54
55 return i;
56 }
57
58 if (byte == 0xff)
59 {
60 if (maxBytes == 1)
61 return 1;
62
63 const auto var = MidiMessage::readVariableLengthValue (data + 1, maxBytes - 1);
64 return jmin (maxBytes, var.value + 2 + var.bytesUsed);
65 }
66
67 if (byte >= 0x80)
69
70 return 0;
71 }
72
73 static uint8* findEventAfter (uint8* d, uint8* endData, int samplePosition) noexcept
74 {
75 while (d < endData && getEventTime (d) <= samplePosition)
76 d += getEventTotalSize (d);
77
78 return d;
79 }
80}
81
82//==============================================================================
84{
85 data += sizeof (int32) + sizeof (uint16) + size_t (MidiBufferHelpers::getEventDataSize (data));
86 return *this;
87}
88
90{
91 auto copy = *this;
92 ++(*this);
93 return copy;
94}
95
97{
98 return { data + sizeof (int32) + sizeof (uint16),
99 MidiBufferHelpers::getEventDataSize (data),
100 MidiBufferHelpers::getEventTime (data) };
101}
102
103//==============================================================================
104MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept
105{
106 addEvent (message, 0);
107}
108
109void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); }
110void MidiBuffer::clear() noexcept { data.clearQuick(); }
112bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; }
113
114void MidiBuffer::clear (int startSample, int numSamples)
115{
116 auto start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1);
117 auto end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1);
118
119 data.removeRange ((int) (start - data.begin()), (int) (end - start));
120}
121
123{
125}
126
128{
129 auto numBytes = MidiBufferHelpers::findActualEventLength (static_cast<const uint8*> (newData), maxBytes);
130
131 if (numBytes <= 0)
132 return true;
133
134 if (std::numeric_limits<uint16>::max() < numBytes)
135 {
136 // This method only supports messages smaller than (1 << 16) bytes
137 return false;
138 }
139
140 auto newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16);
141 auto offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin());
142
143 data.insertMultiple (offset, 0, (int) newItemSize);
144
145 auto* d = data.begin() + offset;
147 d += sizeof (int32);
148 writeUnaligned<uint16> (d, static_cast<uint16> (numBytes));
149 d += sizeof (uint16);
150 memcpy (d, newData, (size_t) numBytes);
151
152 return true;
153}
154
156 int startSample, int numSamples, int sampleDeltaToAdd)
157{
158 for (auto i = otherBuffer.findNextSamplePosition (startSample); i != otherBuffer.cend(); ++i)
159 {
160 const auto metadata = *i;
161
162 if (metadata.samplePosition >= startSample + numSamples && numSamples >= 0)
163 break;
164
165 addEvent (metadata.data, metadata.numBytes, metadata.samplePosition + sampleDeltaToAdd);
166 }
167}
168
170{
171 int n = 0;
172 auto end = data.end();
173
174 for (auto d = data.begin(); d < end; ++n)
175 d += MidiBufferHelpers::getEventTotalSize (d);
176
177 return n;
178}
179
181{
182 return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0;
183}
184
186{
187 if (data.size() == 0)
188 return 0;
189
190 auto endData = data.end();
191
192 for (auto d = data.begin();;)
193 {
194 auto nextOne = d + MidiBufferHelpers::getEventTotalSize (d);
195
196 if (nextOne >= endData)
197 return MidiBufferHelpers::getEventTime (d);
198
199 d = nextOne;
200 }
201}
202
204{
205 return std::find_if (cbegin(), cend(), [&] (const MidiMessageMetadata& metadata) noexcept
206 {
207 return metadata.samplePosition >= samplePosition;
208 });
209}
210
211//==============================================================================
212JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
213JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
214
215MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept
216 : buffer (b), iterator (b.data.begin())
217{
218}
219
220void MidiBuffer::Iterator::setNextSamplePosition (int samplePosition) noexcept
221{
222 iterator = buffer.findNextSamplePosition (samplePosition);
223}
224
225bool MidiBuffer::Iterator::getNextEvent (const uint8*& midiData, int& numBytes, int& samplePosition) noexcept
226{
227 if (iterator == buffer.cend())
228 return false;
229
230 const auto metadata = *iterator++;
231 midiData = metadata.data;
232 numBytes = metadata.numBytes;
233 samplePosition = metadata.samplePosition;
234 return true;
235}
236
237bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept
238{
239 if (iterator == buffer.cend())
240 return false;
241
242 const auto metadata = *iterator++;
243 result = metadata.getMessage();
244 samplePosition = metadata.samplePosition;
245 return true;
246}
247
248JUCE_END_IGNORE_WARNINGS_MSVC
249JUCE_END_IGNORE_WARNINGS_GCC_LIKE
250
251//==============================================================================
252//==============================================================================
253#if JUCE_UNIT_TESTS
254
255struct MidiBufferTest final : public UnitTest
256{
258 : UnitTest ("MidiBuffer", UnitTestCategories::midi)
259 {}
260
261 void runTest() override
262 {
263 beginTest ("Clear messages");
264 {
265 const auto message = MidiMessage::noteOn (1, 64, 0.5f);
266
267 const auto testBuffer = [&]
268 {
269 MidiBuffer buffer;
270 buffer.addEvent (message, 0);
271 buffer.addEvent (message, 10);
272 buffer.addEvent (message, 20);
273 buffer.addEvent (message, 30);
274 return buffer;
275 }();
276
277 {
278 auto buffer = testBuffer;
279 buffer.clear (10, 0);
280 expectEquals (buffer.getNumEvents(), 4);
281 }
282
283 {
284 auto buffer = testBuffer;
285 buffer.clear (10, 1);
286 expectEquals (buffer.getNumEvents(), 3);
287 }
288
289 {
290 auto buffer = testBuffer;
291 buffer.clear (10, 10);
292 expectEquals (buffer.getNumEvents(), 3);
293 }
294
295 {
296 auto buffer = testBuffer;
297 buffer.clear (10, 20);
298 expectEquals (buffer.getNumEvents(), 2);
299 }
300
301 {
302 auto buffer = testBuffer;
303 buffer.clear (10, 30);
304 expectEquals (buffer.getNumEvents(), 1);
305 }
306
307 {
308 auto buffer = testBuffer;
309 buffer.clear (10, 300);
310 expectEquals (buffer.getNumEvents(), 1);
311 }
312 }
313 }
314};
315
317
318#endif
319
320} // namespace juce
void ensureStorageAllocated(int minNumElements)
Increases the array's internal storage to hold a minimum number of elements.
void clearQuick()
Removes all elements from the array without freeing the array's allocated storage.
Definition juce_Array.h:198
int size() const noexcept
Returns the current number of elements in the array.
Definition juce_Array.h:215
void removeRange(int startIndex, int numberToRemove)
Removes a range of elements from the array.
Definition juce_Array.h:894
ElementType * begin() noexcept
Returns a pointer to the first element in the array.
Definition juce_Array.h:328
ElementType * end() noexcept
Returns a pointer to the element which follows the last element in the array.
Definition juce_Array.h:344
void insertMultiple(int indexToInsertAt, ParameterType newElement, int numberOfTimesToInsertIt)
Inserts multiple copies of an element into the array at a given position.
Definition juce_Array.h:480
An iterator to move over contiguous raw MIDI data, which Allows iterating over a MidiBuffer using C++...
MidiBufferIterator & operator++() noexcept
Make this iterator point to the next message in the buffer.
reference operator*() const noexcept
Return an instance of MidiMessageMetadata which describes the message to which the iterator is curren...
Holds a sequence of time-stamped midi events.
int getFirstEventTime() const noexcept
Returns the sample number of the first event in the buffer.
int getLastEventTime() const noexcept
Returns the sample number of the last event in the buffer.
void ensureSize(size_t minimumNumBytes)
Preallocates some memory for the buffer to use.
int getNumEvents() const noexcept
Counts the number of events in the buffer.
MidiBufferIterator findNextSamplePosition(int samplePosition) const noexcept
Get an iterator pointing to the first event with a timestamp greater-than or equal-to samplePosition.
bool isEmpty() const noexcept
Returns true if the buffer is empty.
void swapWith(MidiBuffer &) noexcept
Exchanges the contents of this buffer with another one.
bool addEvent(const MidiMessage &midiMessage, int sampleNumber)
Adds an event to the buffer.
MidiBuffer() noexcept=default
Creates an empty MidiBuffer.
void clear() noexcept
Removes all events from the buffer.
void addEvents(const MidiBuffer &otherBuffer, int startSample, int numSamples, int sampleDeltaToAdd)
Adds some events from another buffer to this one.
Array< uint8 > data
The raw data holding this buffer.
MidiBufferIterator end() const noexcept
Get a read-only iterator pointing one past the end of this buffer.
Encapsulates a MIDI message.
const uint8 * getRawData() const noexcept
Returns a pointer to the raw midi data.
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
Creates a key-down message (using a floating-point velocity).
static int getMessageLengthFromFirstByte(uint8 firstByte) noexcept
Based on the first byte of a short midi message, this uses a lookup table to return the message lengt...
static VariableLengthValue readVariableLengthValue(const uint8 *data, int maxBytesToUse) noexcept
Reads a midi variable-length integer, with protection against buffer overflow.
int getRawDataSize() const noexcept
Returns the number of bytes of data in the message.
T find_if(T... args)
typedef int
memcpy
JUCE Namespace.
unsigned short uint16
A platform-independent 16-bit unsigned integer type.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
signed int int32
A platform-independent 32-bit signed integer type.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
RangedDirectoryIterator begin(const RangedDirectoryIterator &it)
Returns the iterator that was passed in.
A view of MIDI message data stored in a contiguous buffer.
typedef size_t