OpenShot Audio Library | OpenShotAudio  0.6.0
juce_MidiBuffer.cpp
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 
23 namespace juce
24 {
25 
26 namespace 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)
68  return jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte));
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 //==============================================================================
104 MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept
105 {
106  addEvent (message, 0);
107 }
108 
109 void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); }
110 void MidiBuffer::clear() noexcept { data.clearQuick(); }
111 void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); }
112 bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; }
113 
114 void 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 
122 bool MidiBuffer::addEvent (const MidiMessage& m, int sampleNumber)
123 {
124  return addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber);
125 }
126 
127 bool MidiBuffer::addEvent (const void* newData, int maxBytes, int sampleNumber)
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;
146  writeUnaligned<int32> (d, sampleNumber);
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 
155 void MidiBuffer::addEvents (const MidiBuffer& otherBuffer,
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 
169 int MidiBuffer::getNumEvents() const noexcept
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 
180 int MidiBuffer::getFirstEventTime() const noexcept
181 {
182  return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0;
183 }
184 
185 int MidiBuffer::getLastEventTime() const noexcept
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 
203 MidiBufferIterator MidiBuffer::findNextSamplePosition (int samplePosition) const noexcept
204 {
205  return std::find_if (cbegin(), cend(), [&] (const MidiMessageMetadata& metadata) noexcept
206  {
207  return metadata.samplePosition >= samplePosition;
208  });
209 }
210 
211 //==============================================================================
212 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
213 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
214 
215 MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept
216  : buffer (b), iterator (b.data.begin())
217 {
218 }
219 
220 void MidiBuffer::Iterator::setNextSamplePosition (int samplePosition) noexcept
221 {
222  iterator = buffer.findNextSamplePosition (samplePosition);
223 }
224 
225 bool 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 
237 bool 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 
248 JUCE_END_IGNORE_WARNINGS_MSVC
249 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
250 
251 //==============================================================================
252 //==============================================================================
253 #if JUCE_UNIT_TESTS
254 
255 struct MidiBufferTest final : public UnitTest
256 {
257  MidiBufferTest()
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 
316 static MidiBufferTest midiBufferTest;
317 
318 #endif
319 
320 } // namespace juce
void ensureStorageAllocated(int minNumElements)
Definition: juce_Array.h:1040
void clearQuick()
Definition: juce_Array.h:198
int size() const noexcept
Definition: juce_Array.h:215
ElementType * end() noexcept
Definition: juce_Array.h:344
void removeRange(int startIndex, int numberToRemove)
Definition: juce_Array.h:894
ElementType * begin() noexcept
Definition: juce_Array.h:328
void insertMultiple(int indexToInsertAt, ParameterType newElement, int numberOfTimesToInsertIt)
Definition: juce_Array.h:480
MidiBufferIterator & operator++() noexcept
reference operator*() const noexcept
void setNextSamplePosition(int samplePosition) noexcept
bool getNextEvent(MidiMessage &result, int &samplePosition) noexcept
int getFirstEventTime() const noexcept
int getLastEventTime() const noexcept
void ensureSize(size_t minimumNumBytes)
int getNumEvents() const noexcept
MidiBufferIterator findNextSamplePosition(int samplePosition) const noexcept
MidiBufferIterator cend() const noexcept
bool isEmpty() const noexcept
void swapWith(MidiBuffer &) noexcept
bool addEvent(const MidiMessage &midiMessage, int sampleNumber)
MidiBuffer() noexcept=default
void clear() noexcept
void addEvents(const MidiBuffer &otherBuffer, int startSample, int numSamples, int sampleDeltaToAdd)
Array< uint8 > data
MidiBufferIterator end() const noexcept
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
static int getMessageLengthFromFirstByte(uint8 firstByte) noexcept
const uint8 * getRawData() const noexcept
static VariableLengthValue readVariableLengthValue(const uint8 *data, int maxBytesToUse) noexcept
int getRawDataSize() const noexcept