tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_MidiMessageArray.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11#pragma once
12
13namespace tracktion { inline namespace engine
14{
15
17{
18 using MPESourceID = uint32_t;
19
20 static MPESourceID createUniqueMPESourceID() noexcept
21 {
22 static MPESourceID i = 0;
23 return ++i;
24 }
25
26 static constexpr MPESourceID notMPE = 0;
27
29 {
30 MidiMessageWithSource (const juce::MidiMessage& m, MPESourceID source) : juce::MidiMessage (m), mpeSourceID (source) {}
31 MidiMessageWithSource (juce::MidiMessage&& m, MPESourceID source) : juce::MidiMessage (std::move (m)), mpeSourceID (source) {}
32
35 MidiMessageWithSource& operator= (const MidiMessageWithSource&) = default;
36 MidiMessageWithSource& operator= (MidiMessageWithSource&&) = default;
37
38 MPESourceID mpeSourceID = 0;
39 };
40
41 bool isEmpty() const noexcept { return messages.isEmpty(); }
42 bool isNotEmpty() const noexcept { return ! messages.isEmpty(); }
43
44 int size() const noexcept { return messages.size(); }
45 MidiMessageWithSource& operator[] (int i) { return messages.getReference (i); }
46 const MidiMessageWithSource& operator[] (int i) const { return messages.getReference (i); }
47
48 MidiMessageWithSource* begin() noexcept { return messages.begin(); }
49 const MidiMessageWithSource* begin() const noexcept { return messages.begin(); }
50 MidiMessageWithSource* end() noexcept { return messages.end(); }
51 const MidiMessageWithSource* end() const noexcept { return messages.end(); }
52
53 void remove (int index) { messages.remove (index); }
54
55 void swapWith (MidiMessageArray& other) noexcept
56 {
57 std::swap (isAllNotesOff, other.isAllNotesOff);
58 messages.swapWith (other.messages);
59 }
60
61 void clear() noexcept
62 {
63 isAllNotesOff = false;
64 messages.clearQuick();
65 }
66
67 void addMidiMessage (const juce::MidiMessage& m, MPESourceID mpeSourceID)
68 {
69 messages.add ({ m, mpeSourceID });
70 }
71
72 void addMidiMessage (juce::MidiMessage&& m, MPESourceID mpeSourceID)
73 {
74 messages.add ({ std::move (m), mpeSourceID });
75 }
76
77 void addMidiMessage (const juce::MidiMessage& m, double time, MPESourceID mpeSourceID)
78 {
79 messages.add ({ m, mpeSourceID });
80 messages.getReference (messages.size() - 1).setTimeStamp (time);
81 }
82
83 void addMidiMessage (juce::MidiMessage&& m, double time, MPESourceID mpeSourceID)
84 {
85 messages.add ({ std::move (m), mpeSourceID });
86 messages.getReference (messages.size() - 1).setTimeStamp (time);
87 }
88
89 void add (const MidiMessageWithSource& m)
90 {
91 messages.add (m);
92 }
93
94 void add (MidiMessageWithSource&& m)
95 {
96 messages.add (std::move (m));
97 }
98
99 void add (const MidiMessageWithSource& m, double time)
100 {
101 messages.add (m);
102 messages.getReference (messages.size() - 1).setTimeStamp (time);
103 }
104
105 void add (MidiMessageWithSource&& m, double time)
106 {
107 messages.add (std::move (m));
108 messages.getReference (messages.size() - 1).setTimeStamp (time);
109 }
110
111 void copyFrom (const MidiMessageArray& source)
112 {
113 clear();
114 mergeFrom (source);
115 }
116
117 void mergeFrom (const MidiMessageArray& source)
118 {
119 isAllNotesOff = isAllNotesOff || source.isAllNotesOff;
120
121 if (source.isEmpty())
122 return;
123
124 messages.ensureStorageAllocated (messages.size() + source.size());
125
126 for (auto& m : source)
127 messages.add (m);
128 }
129
130 void mergeFromWithOffset (const MidiMessageArray& source, double delta)
131 {
132 isAllNotesOff = isAllNotesOff || source.isAllNotesOff;
133
134 if (source.isEmpty())
135 return;
136
137 messages.ensureStorageAllocated (messages.size() + source.size());
138
139 for (const auto& m : source)
140 {
141 auto copy = MidiMessageWithSource (m);
142 copy.addToTimeStamp (delta);
143 messages.add (std::move (copy));
144 }
145 }
146
147 void mergeFromAndClear (MidiMessageArray& source)
148 {
149 isAllNotesOff = isAllNotesOff || source.isAllNotesOff;
150
151 if (isEmpty())
152 {
153 swapWith (source);
154 }
155 else
156 {
157 messages.ensureStorageAllocated (messages.size() + source.size());
158
159 for (auto& m : source)
160 messages.add (std::move (m));
161 }
162
163 source.clear();
164 }
165
166 void mergeFromAndClearWithOffset (MidiMessageArray& source, double delta)
167 {
168 isAllNotesOff = isAllNotesOff || source.isAllNotesOff;
169
170 if (isEmpty())
171 {
172 swapWith (source);
173 addToTimestamps (delta);
174 }
175 else
176 {
177 messages.ensureStorageAllocated (messages.size() + source.size());
178
179 for (auto& m : source)
180 {
181 messages.add (std::move (m));
182 messages.getReference (messages.size() - 1).addToTimeStamp (delta);
183 }
184 }
185
186 source.clear();
187 }
188
189 void mergeFromAndClearWithOffsetAndLimit (MidiMessageArray& source, double delta, int numItemsToTake)
190 {
191 if (numItemsToTake >= source.size())
192 return mergeFromAndClearWithOffset (source, delta);
193
194 isAllNotesOff = isAllNotesOff || source.isAllNotesOff;
195 messages.ensureStorageAllocated (messages.size() + numItemsToTake);
196
197 for (int i = 0; i < numItemsToTake; ++i)
198 {
199 messages.add (std::move (source.messages.getReference(i)));
200 messages.getReference (messages.size() - 1).addToTimeStamp (delta);
201 }
202
203 source.messages.removeRange (0, numItemsToTake);
204 }
205
206 void mergeFromAndClear (juce::Array<juce::MidiMessage>& source, MPESourceID mpeSourceID)
207 {
208 messages.ensureStorageAllocated (messages.size() + source.size());
209
210 for (auto& m : source)
211 addMidiMessage (m, mpeSourceID);
212
213 source.clear();
214 }
215
216 void removeNoteOnsAndOffs()
217 {
218 for (int i = messages.size(); --i >= 0;)
219 if (messages.getReference (i).isNoteOnOrOff())
220 messages.remove (i);
221 }
222
223 void addToTimestamps (double delta) noexcept
224 {
225 for (auto& m : messages)
226 m.addToTimeStamp (delta);
227 }
228
229 void addToNoteNumbers (int delta) noexcept
230 {
231 for (auto& m : messages)
232 m.setNoteNumber (m.getNoteNumber() + delta);
233 }
234
235 void multiplyVelocities (float factor) noexcept
236 {
237 for (auto& m : messages)
238 m.multiplyVelocity (factor);
239 }
240
241 void sortByTimestamp()
242 {
243 choc::sorting::stable_sort (messages.begin(), messages.end(), [] (const juce::MidiMessage& a, const juce::MidiMessage& b)
244 {
245 auto t1 = a.getTimeStamp();
246 auto t2 = b.getTimeStamp();
247
248 if (t1 == t2)
249 {
250 if (a.isNoteOff() && b.isNoteOn()) return true;
251 if (a.isNoteOn() && b.isNoteOff()) return false;
252 }
253 return t1 < t2;
254 });
255 }
256
257 void reserve (int size)
258 {
259 messages.ensureStorageAllocated (size);
260 }
261
262 bool isAllNotesOff = false;
263
264private:
265
267};
268
269}} // namespace tracktion
T move(T... args)
typedef uint32_t
T swap(T... args)