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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_MidiList.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
11namespace tracktion { inline namespace engine
12{
13
17{
18public:
19 MidiList();
21 ~MidiList();
22
23 static juce::ValueTree createMidiList();
24
26 void copyFrom (const MidiList&, juce::UndoManager*);
27
29 void addFrom (const MidiList&, juce::UndoManager*);
30
31 //==============================================================================
33 {
34 none,
36 };
37
38 //==============================================================================
39 const juce::Array<MidiNote*>& getNotes() const;
40 const juce::Array<MidiControllerEvent*>& getControllerEvents() const;
41 const juce::Array<MidiSysexEvent*>& getSysexEvents() const;
42
43 //==============================================================================
44 bool isAttachedToClip() const noexcept { return ! state.getParent().hasType (IDs::NA); }
45
46 void setCompList (bool shouldBeComp) noexcept { isComp = shouldBeComp; }
47 bool isCompList() const noexcept { return isComp; }
48
49 //==============================================================================
51 MidiChannel getMidiChannel() const { return midiChannel; }
52
54 void setMidiChannel (MidiChannel chanNum);
55
57 juce::String getImportedMidiTrackName() const noexcept { return importedName; }
58
60 juce::String getImportedFileName() const noexcept { return importedFileName; }
61 void setImportedFileName (const juce::String& n) { importedFileName = n; }
62
63 //==============================================================================
64 bool isEmpty() const noexcept { return state.getNumChildren() == 0; }
65
66 void clear (juce::UndoManager*);
67 void trimOutside (BeatPosition firstBeat, BeatPosition lastBeat, juce::UndoManager*);
68 void moveAllBeatPositions (BeatDuration deltaBeats, juce::UndoManager*);
69 void rescale (double factor, juce::UndoManager*);
70
71 //==============================================================================
72 int getNumNotes() const { return getNotes().size(); }
73 MidiNote* getNote (int index) const { return getNotes()[index]; }
74 MidiNote* getNoteFor (const juce::ValueTree&);
75
76 juce::Range<int> getNoteNumberRange() const;
77
79 BeatPosition getFirstBeatNumber() const;
80
82 BeatPosition getLastBeatNumber() const;
83
84 MidiNote* addNote (const MidiNote&, juce::UndoManager*);
85 MidiNote* addNote (int pitch, BeatPosition startBeat, BeatDuration lengthInBeats, int velocity, int colourIndex, juce::UndoManager*);
86 void removeNote (MidiNote&, juce::UndoManager*);
87 void removeAllNotes (juce::UndoManager*);
88
89 //==============================================================================
90 int getNumControllerEvents() const { return getControllerEvents().size(); }
91
92 MidiControllerEvent* getControllerEvent (int index) const { return getControllerEvents()[index]; }
93 MidiControllerEvent* getControllerEventAt (BeatPosition, int controllerType) const;
94
95 MidiControllerEvent* addControllerEvent (const MidiControllerEvent&, juce::UndoManager*);
96 MidiControllerEvent* addControllerEvent (BeatPosition, int controllerType, int controllerValue, juce::UndoManager*);
97 MidiControllerEvent* addControllerEvent (BeatPosition, int controllerType, int controllerValue, int metadata, juce::UndoManager*);
98
99 void removeControllerEvent (MidiControllerEvent&, juce::UndoManager*);
100 void removeAllControllers (juce::UndoManager*);
101
103 bool containsController (int controllerType) const;
104
105 void setControllerValueAt (int controllerType, BeatPosition beatNumber, int newValue, juce::UndoManager*);
106 void removeControllersBetween (int controllerType, BeatPosition beatNumberStart, BeatPosition beatNumberEnd, juce::UndoManager*);
107
109 void insertRepeatedControllerValue (int type, int startVal, int endVal,
110 BeatRange rangeBeats,
111 BeatDuration intervalBeats, juce::UndoManager*);
112
113 //==============================================================================
114 int getNumSysExEvents() const { return getSysexEvents().size(); }
115
116 MidiSysexEvent* getSysexEvent (int index) const { return getSysexEvents()[index]; }
117 MidiSysexEvent* getSysexEventUnchecked (int index) const { return getSysexEvents().getUnchecked (index); }
118 MidiSysexEvent* getSysexEventFor (const juce::ValueTree&) const;
119
120 MidiSysexEvent& addSysExEvent (const juce::MidiMessage&, BeatPosition, juce::UndoManager*);
121
122 void removeSysExEvent (const MidiSysexEvent&, juce::UndoManager*);
123 void removeAllSysexes (juce::UndoManager*);
124
125 //==============================================================================
130 TimePosition editTimeOfListTimeZero, juce::UndoManager*);
131
134 TimePosition editTimeOfListTimeZero, juce::UndoManager*);
135
137 enum class TimeBase
138 {
139 seconds,
140 beats,
141 beatsRaw
142 };
143
151
154
155 //==============================================================================
156 static bool looksLikeMPEData (const juce::File&);
157
158 static constexpr const double defaultInitialTimbreValue = 0.5;
159 static constexpr const double defaultInitialPitchBendValue = 0;
160 static constexpr const double defaultInitialPressureValue = 0;
161
162 static bool fileHasTempoChanges (const juce::File&);
163
164 static bool readSeparateTracksFromFile (const juce::File&,
166 juce::Array<BeatPosition>& tempoChangeBeatNumbers,
168 juce::Array<int>& numerators,
169 juce::Array<int>& denominators,
170 BeatDuration& songLength,
171 bool importAsNoteExpression);
172
173 //==============================================================================
174 template <typename Type>
175 static void sortMidiEventsByTime (juce::Array<Type>& notes)
176 {
177 std::sort (notes.begin(), notes.end(),
178 [] (const Type& a, const Type& b) { return a->getBeatPosition() < b->getBeatPosition(); });
179 }
180
181 template <typename Type>
182 static void sortMidiEventsByNoteNumber (juce::Array<Type>& notes)
183 {
184 std::sort (notes.begin(), notes.end(),
185 [] (const Type& a, const Type& b) { return a->getNoteNumber() < b->getNoteNumber(); });
186 }
187
188 //==============================================================================
189 juce::ValueTree state;
190
191private:
192 //==============================================================================
195
196 juce::String importedFileName;
197 juce::String importedName;
198
199 void initialise (juce::UndoManager*);
200
201 template<typename EventType>
202 struct EventDelegate
203 {
204 static bool isSuitableType (const juce::ValueTree&);
206 static bool updateObject (EventType&, const juce::Identifier&);
207 static void removeFromSelection (EventType*);
208 };
209
210 template<typename EventType>
211 struct EventList : public ValueTreeObjectList<EventType>
212 {
213 EventList (const juce::ValueTree& v)
214 : ValueTreeObjectList<EventType> (v)
215 {
217 }
218
219 ~EventList() override
220 {
222 }
223
224 EventType* getEventFor (const juce::ValueTree& v)
225 {
226 for (auto m : ValueTreeObjectList<EventType>::objects)
227 if (m->state == v)
228 return m;
229
230 return {};
231 }
232
233 bool isSuitableType (const juce::ValueTree& v) const override { return EventDelegate<EventType>::isSuitableType (v); }
234 EventType* createNewObject (const juce::ValueTree& v) override { return new EventType (v); }
235 void deleteObject (EventType* m) override { delete m; }
236 void newObjectAdded (EventType*) override { triggerSort(); }
237 void objectRemoved (EventType* m) override { EventDelegate<EventType>::removeFromSelection (m); triggerSort(); }
238 void objectOrderChanged() override { triggerSort(); }
239
240 void valueTreePropertyChanged (juce::ValueTree& v, const juce::Identifier& i) override
241 {
242 if (auto e = getEventFor (v))
243 if (EventDelegate<EventType>::updateObject (*e, i))
244 triggerSort();
245 }
246
247 void triggerSort()
248 {
249 const juce::ScopedLock sl (lock);
250 needsSorting = true;
251 }
252
253 const juce::Array<EventType*>& getSortedList()
254 {
255 TRACKTION_ASSERT_MESSAGE_THREAD
256
257 const juce::ScopedLock sl (lock);
258
259 if (needsSorting)
260 {
261 needsSorting = false;
263 sortMidiEventsByTime (sortedEvents);
264 }
265
266 return sortedEvents;
267 }
268
269 bool needsSorting = true;
270 juce::Array<EventType*> sortedEvents;
272
274 };
275
279
280 //==============================================================================
282};
283
284}} // namespace tracktion { inline namespace engine
ElementType * begin() noexcept
ElementType * end() noexcept
bool hasType(const Identifier &typeName) const noexcept
int getNumChildren() const noexcept
ValueTree getParent() const noexcept
void insertRepeatedControllerValue(int type, int startVal, int endVal, BeatRange rangeBeats, BeatDuration intervalBeats, juce::UndoManager *)
Adds controller values over a specified time, at an even interval.
void copyFrom(const MidiList &, juce::UndoManager *)
Clears the current list and copies the others contents and properties.
MidiChannel getMidiChannel() const
Gets the list's midi channel number.
void setMidiChannel(MidiChannel chanNum)
Gives the list a channel number that it'll use when generating real midi messages.
void addFrom(const MidiList &, juce::UndoManager *)
Adds copies of the events in another list to this one.
juce::MidiMessageSequence exportToPlaybackMidiSequence(MidiClip &, TimeBase, bool generateMPE) const
Creates a juce::MidiMessageSequence from the list in order to be played back The sequence will be in ...
void importMidiSequence(const juce::MidiMessageSequence &, Edit *, TimePosition editTimeOfListTimeZero, juce::UndoManager *)
Adds the contents of a MidiMessageSequence to this list.
BeatPosition getLastBeatNumber() const
Beat number of last event in the list.
juce::String getImportedFileName() const noexcept
Set the imported file name if you want it to appear on the clip.
bool containsController(int controllerType) const
True if there are any controller events of this type.
TimeBase
Determines MIDI event timing.
@ beats
Event times will be in seconds relative to the Edit timeline.
@ beatsRaw
Event times will be in beats relative to the Edit timeline.
@ none
No automation, add the sequence as plain MIDI with the channel of the clip.
@ expression
Add the automation as EXP assuming the source sequence is MPE MIDI.
BeatPosition getFirstBeatNumber() const
Beat number of first event in the list.
juce::String getImportedMidiTrackName() const noexcept
If the data was pulled from a midi file then this may have a useful name describing its purpose.
void importFromEditTimeSequenceWithNoteExpression(const juce::MidiMessageSequence &, Edit *, TimePosition editTimeOfListTimeZero, juce::UndoManager *)
Adds the contents of a MidiSequence to this list assigning MPE expression changes to EXP expression.
static juce::MidiMessageSequence createDefaultPlaybackMidiSequence(const MidiList &, MidiClip &, TimeBase, bool generateMPE)
Creates the default MIDI playback sequence.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
T lock(T... args)
T sort(T... args)
Represents a duration in beats.
Represents a MIDI channel 1-16, and also contains extra ID bits to encode info about the event's orig...