OpenShot Library | libopenshot-audio  0.2.0
juce_MidiBuffer.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
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* const d) noexcept
29  {
30  return readUnaligned<int32> (d);
31  }
32 
33  inline uint16 getEventDataSize (const void* const d) noexcept
34  {
35  return readUnaligned<uint16> (static_cast<const char*> (d) + sizeof (int32));
36  }
37 
38  inline uint16 getEventTotalSize (const void* const d) noexcept
39  {
40  return (uint16) (getEventDataSize (d) + sizeof (int32) + sizeof (uint16));
41  }
42 
43  static int findActualEventLength (const uint8* const data, const int maxBytes) noexcept
44  {
45  unsigned int byte = (unsigned int) *data;
46  int size = 0;
47 
48  if (byte == 0xf0 || byte == 0xf7)
49  {
50  const uint8* d = data + 1;
51 
52  while (d < data + maxBytes)
53  if (*d++ == 0xf7)
54  break;
55 
56  size = (int) (d - data);
57  }
58  else if (byte == 0xff)
59  {
60  if (maxBytes == 1)
61  {
62  size = 1;
63  }
64  else
65  {
66  int n;
67  const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n);
68  size = jmin (maxBytes, n + 2 + bytesLeft);
69  }
70  }
71  else if (byte >= 0x80)
72  {
73  size = jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte));
74  }
75 
76  return size;
77  }
78 
79  static uint8* findEventAfter (uint8* d, uint8* endData, const int samplePosition) noexcept
80  {
81  while (d < endData && getEventTime (d) <= samplePosition)
82  d += getEventTotalSize (d);
83 
84  return d;
85  }
86 }
87 
88 //==============================================================================
91 
92 MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept : data (other.data) {}
93 
95 {
96  data = other.data;
97  return *this;
98 }
99 
100 MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept
101 {
102  addEvent (message, 0);
103 }
104 
105 void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); }
106 void MidiBuffer::clear() noexcept { data.clearQuick(); }
107 void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); }
108 bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; }
109 
110 void MidiBuffer::clear (const int startSample, const int numSamples)
111 {
112  uint8* const start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1);
113  uint8* const end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1);
114 
115  data.removeRange ((int) (start - data.begin()), (int) (end - data.begin()));
116 }
117 
118 void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber)
119 {
120  addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber);
121 }
122 
123 void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const int sampleNumber)
124 {
125  const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast<const uint8*> (newData), maxBytes);
126 
127  if (numBytes > 0)
128  {
129  const size_t newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16);
130  const int offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin());
131 
132  data.insertMultiple (offset, 0, (int) newItemSize);
133 
134  uint8* const d = data.begin() + offset;
135  writeUnaligned<int32> (d, sampleNumber);
136  writeUnaligned<uint16> (d + 4, static_cast<uint16> (numBytes));
137  memcpy (d + 6, newData, (size_t) numBytes);
138  }
139 }
140 
141 void MidiBuffer::addEvents (const MidiBuffer& otherBuffer,
142  const int startSample,
143  const int numSamples,
144  const int sampleDeltaToAdd)
145 {
146  Iterator i (otherBuffer);
147  i.setNextSamplePosition (startSample);
148 
149  const uint8* eventData;
150  int eventSize, position;
151 
152  while (i.getNextEvent (eventData, eventSize, position)
153  && (position < startSample + numSamples || numSamples < 0))
154  {
155  addEvent (eventData, eventSize, position + sampleDeltaToAdd);
156  }
157 }
158 
159 int MidiBuffer::getNumEvents() const noexcept
160 {
161  int n = 0;
162  const uint8* const end = data.end();
163 
164  for (const uint8* d = data.begin(); d < end; ++n)
165  d += MidiBufferHelpers::getEventTotalSize (d);
166 
167  return n;
168 }
169 
170 int MidiBuffer::getFirstEventTime() const noexcept
171 {
172  return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0;
173 }
174 
175 int MidiBuffer::getLastEventTime() const noexcept
176 {
177  if (data.size() == 0)
178  return 0;
179 
180  const uint8* const endData = data.end();
181 
182  for (const uint8* d = data.begin();;)
183  {
184  const uint8* const nextOne = d + MidiBufferHelpers::getEventTotalSize (d);
185 
186  if (nextOne >= endData)
187  return MidiBufferHelpers::getEventTime (d);
188 
189  d = nextOne;
190  }
191 }
192 
193 //==============================================================================
195  : buffer (b), data (b.data.begin())
196 {
197 }
198 
200 
201 void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept
202 {
203  data = buffer.data.begin();
204  const uint8* const dataEnd = buffer.data.end();
205 
206  while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition)
207  data += MidiBufferHelpers::getEventTotalSize (data);
208 }
209 
210 bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes, int& samplePosition) noexcept
211 {
212  if (data >= buffer.data.end())
213  return false;
214 
215  samplePosition = MidiBufferHelpers::getEventTime (data);
216  const int itemSize = MidiBufferHelpers::getEventDataSize (data);
217  numBytes = itemSize;
218  midiData = data + sizeof (int32) + sizeof (uint16);
219  data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
220 
221  return true;
222 }
223 
224 bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept
225 {
226  if (data >= buffer.data.end())
227  return false;
228 
229  samplePosition = MidiBufferHelpers::getEventTime (data);
230  const int itemSize = MidiBufferHelpers::getEventDataSize (data);
231  result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition);
232  data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize;
233 
234  return true;
235 }
236 
237 } // namespace juce
MidiBuffer & operator=(const MidiBuffer &) noexcept
Makes a copy of another MidiBuffer.
void setNextSamplePosition(int samplePosition) noexcept
Repositions the iterator so that the next event retrieved will be the first one whose sample position...
void swapWith(MidiBuffer &) noexcept
Exchanges the contents of this buffer with another one.
Encapsulates a MIDI message.
bool getNextEvent(MidiMessage &result, int &samplePosition) noexcept
Retrieves a copy of the next event from the buffer.
~Iterator() noexcept
Destructor.
int getFirstEventTime() const noexcept
Returns the sample number of the first event in the buffer.
int getRawDataSize() const noexcept
Returns the number of bytes of data in the message.
Iterator(const MidiBuffer &) noexcept
Creates an Iterator for this MidiBuffer.
bool isEmpty() const noexcept
Returns true if the buffer is empty.
MidiBuffer() noexcept
Creates an empty MidiBuffer.
int getNumEvents() const noexcept
Counts the number of events in the buffer.
~MidiBuffer()
Destructor.
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...
Holds a sequence of time-stamped midi events.
static int readVariableLengthVal(const uint8 *data, int &numBytesUsed) noexcept
Reads a midi variable-length integer.
const uint8 * getRawData() const noexcept
Returns a pointer to the raw midi data.
Array< uint8 > data
The raw data holding this buffer.
int getLastEventTime() const noexcept
Returns the sample number of the last event in the buffer.
void addEvent(const MidiMessage &midiMessage, int sampleNumber)
Adds an event to the buffer.
Used to iterate through the events in a MidiBuffer.
void addEvents(const MidiBuffer &otherBuffer, int startSample, int numSamples, int sampleDeltaToAdd)
Adds some events from another buffer to this one.
void clear() noexcept
Removes all events from the buffer.
void ensureSize(size_t minimumNumBytes)
Preallocates some memory for the buffer to use.