11namespace tracktion {
inline namespace engine
14SelectedMidiEvents::SelectedMidiEvents (MidiClip& m) : clips ({ &m })
25 for (
int i = 1; i < m.size(); i++)
26 jassert (&m[0]->edit == &m[i]->edit);
30SelectedMidiEvents::~SelectedMidiEvents()
32 notifyListenersOfDeletion();
35Edit& SelectedMidiEvents::getEdit()
37 return clips[0]->edit;
41MidiClip* SelectedMidiEvents::clipForEvent (MidiNote* note)
const
44 if (c->getSequence().getNotes().contains (note))
52MidiClip* SelectedMidiEvents::clipForEvent (MidiSysexEvent* sysex)
const
55 if (c->getSequence().getSysexEvents().contains (sysex))
63MidiClip* SelectedMidiEvents::clipForEvent (MidiControllerEvent* controller)
const
66 if (c->getSequence().getControllerEvents().contains (controller))
75void SelectedMidiEvents::addSelectedEvent (MidiNote* note,
bool addToCurrent)
78 selectedNotes.clearQuick();
81 if (note !=
nullptr && clipForEvent (note) ==
nullptr)
84 if (note !=
nullptr && ! contains (*note))
85 selectedNotes.add (note);
87 sendSelectionChangedMessage (
nullptr);
89 if (selectedNotes.isEmpty())
92 SelectionManager::refreshAllPropertyPanelsShowing (*
this);
94 if (selectedSysexes.size() > 0)
96 selectedSysexes.clearQuick();
97 SelectionManager::refreshAllPropertyPanelsShowing (*
this);
100 if (selectedControllers.size() > 0)
102 selectedControllers.clearQuick();
103 SelectionManager::refreshAllPropertyPanelsShowing (*
this);
107void SelectedMidiEvents::addSelectedEvent (MidiSysexEvent* sysex,
bool addToCurrent)
110 selectedSysexes.clearQuick();
113 if (sysex !=
nullptr && clipForEvent (sysex) ==
nullptr)
116 if (sysex !=
nullptr && ! contains (*sysex))
117 selectedSysexes.add (sysex);
119 if (selectedSysexes.isEmpty())
122 sendSelectionChangedMessage (
nullptr);
124 if (selectedNotes.size() > 0)
126 selectedNotes.clearQuick();
127 SelectionManager::refreshAllPropertyPanelsShowing (*
this);
129 if (selectedControllers.size() > 0)
131 selectedControllers.clearQuick();
132 SelectionManager::refreshAllPropertyPanelsShowing (*
this);
136void SelectedMidiEvents::addSelectedEvent (MidiControllerEvent* controller,
bool addToCurrent)
139 selectedControllers.clearQuick();
142 if (controller !=
nullptr && clipForEvent (controller) ==
nullptr)
145 if (controller !=
nullptr && ! contains (*controller))
146 selectedControllers.add (controller);
148 if (selectedControllers.isEmpty())
151 sendSelectionChangedMessage (
nullptr);
153 if (selectedNotes.size() > 0)
155 selectedNotes.clearQuick();
156 SelectionManager::refreshAllPropertyPanelsShowing (*
this);
158 if (selectedSysexes.size() > 0)
160 selectedSysexes.clearQuick();
161 SelectionManager::refreshAllPropertyPanelsShowing (*
this);
165void SelectedMidiEvents::removeSelectedEvent (MidiNote* note)
169 selectedNotes.removeAllInstancesOf (note);
170 sendSelectionChangedMessage (
nullptr);
173 if (! anythingSelected())
177void SelectedMidiEvents::removeSelectedEvent (MidiSysexEvent* sysex)
179 if (sysex !=
nullptr)
181 selectedSysexes.removeAllInstancesOf (sysex);
182 sendSelectionChangedMessage (
nullptr);
185 if (! anythingSelected())
189void SelectedMidiEvents::removeSelectedEvent (MidiControllerEvent* controller)
191 if (controller !=
nullptr)
193 selectedControllers.removeAllInstancesOf (controller);
194 sendSelectionChangedMessage (
nullptr);
197 if (! anythingSelected())
201void SelectedMidiEvents::setSelected (SelectionManager& sm,
const juce::Array<MidiNote*>& notes,
bool addToSelection,
bool allowMixedSelection)
203 if (! addToSelection)
204 selectedNotes.clearQuick();
208 if (n !=
nullptr && clipForEvent (n) ==
nullptr)
211 if (n !=
nullptr && ! contains (*n))
212 selectedNotes.add (n);
215 sendSelectionChangedMessage (&sm);
217 if (selectedSysexes.size() > 0 && ! allowMixedSelection)
218 selectedSysexes.clearQuick();
220 if (selectedControllers.size() > 0 && ! allowMixedSelection)
221 selectedControllers.clearQuick();
223 if (getNumSelected() > 0)
224 sm.selectOnly (
this);
229void SelectedMidiEvents::setSelected (SelectionManager& sm,
const juce::Array<MidiSysexEvent*>& events,
bool addToSelection,
bool allowMixedSelection)
231 if (! addToSelection)
232 selectedSysexes.clearQuick();
234 for (
auto e : events)
236 if (e !=
nullptr && clipForEvent (e) ==
nullptr)
239 if (e !=
nullptr && ! contains (*e))
240 selectedSysexes.add (e);
243 sendSelectionChangedMessage (&sm);
245 if (selectedNotes.size() > 0 && ! allowMixedSelection)
246 selectedNotes.clearQuick();
248 if (selectedControllers.size() > 0 && ! allowMixedSelection)
249 selectedControllers.clearQuick();
251 if (getNumSelected() > 0)
252 sm.selectOnly (
this);
259 if (! addToSelection)
260 selectedControllers.clearQuick();
262 for (
auto e : events)
264 if (e !=
nullptr && clipForEvent (e) ==
nullptr)
267 if (e !=
nullptr && ! contains (*e))
268 selectedControllers.add (e);
271 sendSelectionChangedMessage (&sm);
273 if (selectedNotes.size() > 0 && ! allowMixedSelection)
274 selectedNotes.clearQuick();
276 if (selectedSysexes.size() > 0 && ! allowMixedSelection)
277 selectedSysexes.clearQuick();
279 if (getNumSelected() > 0)
280 sm.selectOnly (
this);
285bool SelectedMidiEvents::contains (
const MidiNote& note)
const noexcept
287 for (
auto n : selectedNotes)
288 if (n->state == note.state)
294bool SelectedMidiEvents::contains (
const MidiSysexEvent& event)
const noexcept
296 for (
auto s : selectedSysexes)
297 if (s->state == event.state)
303bool SelectedMidiEvents::contains (
const MidiControllerEvent& event)
const noexcept
305 for (
auto s : selectedControllers)
306 if (s->state == event.state)
312bool SelectedMidiEvents::isSelected (
const MidiNote* event)
const
314 return event !=
nullptr
315 && SelectionManager::findSelectionManagerContaining (
this) !=
nullptr
316 && contains (*event);
319bool SelectedMidiEvents::isSelected (
const MidiSysexEvent* event)
const
321 return event !=
nullptr
322 && SelectionManager::findSelectionManagerContaining (
this) !=
nullptr
323 && contains (*event);
326bool SelectedMidiEvents::isSelected (
const MidiControllerEvent* event)
const
328 return event !=
nullptr
329 && SelectionManager::findSelectionManagerContaining (
this) !=
nullptr
330 && contains (*event);
336 return TRANS(
"MIDI Events");
339void SelectedMidiEvents::selectionStatusChanged (
bool isNowSelected)
341 if (! anythingSelected())
346 selectedNotes.clearQuick();
347 selectedSysexes.clearQuick();
348 selectedControllers.clearQuick();
349 sendSelectionChangedMessage (
nullptr);
355 auto* undoManager = &getEdit().getUndoManager();
356 auto notes = selectedNotes;
365 for (
auto note : notes)
367 if (
auto clip = clipForEvent (note))
369 note->setNoteNumber (note->getNoteNumber() + deltaNote, undoManager);
371 if (shouldLockControllerToNotes && shouldLockControllerToNotes())
375 startTime =
std::min (startTime, note->getEditStartTime (*clip));
376 endTime =
std::max (endTime, note->getEditEndTime (*clip));
379 auto pos = note->getEditTimeRange (*clip);
380 auto newStartBeat = clip->getContentBeatAtTime (pos.getStart() + deltaStart) + toDuration (clip->getLoopStartBeats());
381 auto newEndBeat = clip->getContentBeatAtTime (pos.getEnd() + deltaStart + deltaLength) + toDuration (clip->getLoopStartBeats());
384 deltaBeat = newStartBeat - note->getBeatPosition();
386 note->setStartAndLength (newStartBeat, newEndBeat - newStartBeat, undoManager);
390 for (
auto sysexEvent : selectedSysexes)
392 if (
auto clip = clipForEvent (sysexEvent))
394 auto deltaTime = sysexEvent->getEditTime (*clip) + deltaStart;
395 sysexEvent->setBeatPosition (clip->getContentBeatAtTime (deltaTime) +
toDuration (clip->getLoopStartBeats()), undoManager);
399 if (shouldLockControllerToNotes && shouldLockControllerToNotes() && notes.
size() > 0)
401 moveControllerData (uniqueClips,
nullptr, *deltaBeat, startTime, endTime,
false);
405 for (
auto controllerEvent : selectedControllers)
407 auto& clip = *clipForEvent (controllerEvent);
411 startTime =
std::min (startTime, controllerEvent->getEditTime (clip));
412 endTime =
std::max (endTime, controllerEvent->getEditTime (clip));
414 auto start = controllerEvent->getEditTime (clip);
415 auto newStartBeat = clip.getContentBeatAtTime (start + deltaStart) +
toDuration (clip.getLoopStartBeats());
418 deltaBeat = newStartBeat - controllerEvent->getBeatPosition();
421 moveControllerData (uniqueClips, &selectedControllers, *deltaBeat,
422 startTime - TimeDuration::fromSeconds (0.001), endTime + TimeDuration::fromSeconds (0.001),
427void SelectedMidiEvents::setNoteLengths (BeatDuration newLength)
429 auto um = &getEdit().getUndoManager();
431 for (
auto note : selectedNotes)
432 note->setStartAndLength (note->getStartBeat(), newLength, um);
435void SelectedMidiEvents::setVelocities (
int newVelocity)
437 auto undoManager = &getEdit().getUndoManager();
439 for (
auto note : selectedNotes)
440 note->setVelocity (newVelocity, undoManager);
443void SelectedMidiEvents::changeColour (uint8_t newColour)
445 auto undoManager = &getEdit().getUndoManager();
447 for (
auto note : selectedNotes)
448 note->setColour (newColour, undoManager);
451void SelectedMidiEvents::nudge (TimecodeSnapType snapType,
int leftRight,
int upDown)
453 auto& ed = getEdit();
454 auto undoManager = &ed.getUndoManager();
457 for (
auto note : selectedNotes)
458 note->setNoteNumber (note->getNoteNumber() + upDown, undoManager);
462 if (
auto firstSelected = selectedNotes.getFirst())
464 if (
auto clip = clips[0])
466 auto start = firstSelected->getEditStartTime (*clip);
468 auto snapped = leftRight < 0
469 ? snapType.roundTimeDown (start - TimeDuration::fromSeconds (0.01), ed.tempoSequence)
470 : snapType.roundTimeUp (start + TimeDuration::fromSeconds (0.01), ed.tempoSequence);
472 auto delta = ed.tempoSequence.toBeats (snapped)
473 - ed.tempoSequence.toBeats (start);
479 for (
auto note : selectedNotes)
481 auto noteClip = clipForEvent (note);
484 startTime =
std::min (startTime, note->getEditStartTime (*noteClip));
485 endTime =
std::max (endTime, note->getEditEndTime (*noteClip));
487 note->setStartAndLength (note->getStartBeat() + delta,
488 note->getLengthBeats(),
492 if (shouldLockControllerToNotes && shouldLockControllerToNotes())
493 moveControllerData (uniqueClips,
nullptr, delta, startTime, endTime,
false);
499TimeRange SelectedMidiEvents::getSelectedRange()
const
501 bool doneFirst =
false;
504 for (
auto n : selectedNotes)
506 if (
auto clip = clipForEvent (n))
508 auto noteRange = n->getEditTimeRange (*clip);
517 time =
time.getUnionWith (noteRange);
524void SelectedMidiEvents::sendSelectionChangedMessage (SelectionManager* sm)
528 ++(sm->selectionChangeCount);
529 sm->sendChangeMessage();
545 for (
int i = selectedNotes.size(); --i >= 0;)
546 if (clipForEvent (selectedNotes[i]) ==
nullptr)
549 for (
int i = selectedSysexes.size(); --i >= 0;)
550 if (clipForEvent (selectedSysexes[i]) ==
nullptr)
551 selectedSysexes.remove (i);
553 for (
int i = selectedControllers.size(); --i >= 0;)
554 if (clipForEvent (selectedControllers[i]) ==
nullptr)
555 selectedControllers.remove (i);
565 auto& seq = c->getSequence();
569 for (
auto evt : seq.getControllerEvents())
571 if (evt->getEditTime (*c) >= startTime && evt->getEditTime (*c) < endTime)
574 seq.addControllerEvent (
MidiControllerEvent (evt->state.createCopy()), c->getUndoManager());
576 auto beat = evt->getBeatPosition() + deltaBeats;
577 evt->setBeatPosition (beat, c->getUndoManager());
578 movedEvents.
add (evt);
582 auto& ts = c->edit.tempoSequence;
584 const auto startTimeAfter = ts.toTime (ts.toBeats (startTime) + deltaBeats);
585 const auto endTimeAfter = ts.toTime (ts.toBeats (endTime) + deltaBeats);
587 for (
auto evt : seq.getControllerEvents())
588 if (onlyTheseEvents ==
nullptr || onlyTheseEvents->
contains (evt))
589 if (! movedEvents.
contains (evt) && evt->getEditTime (*c) >= startTimeAfter && evt->getEditTime (*c) <= endTimeAfter)
590 itemsToRemove.
add (evt->state);
592 for (
auto& v : itemsToRemove)
593 seq.state.removeChild (v, c->getUndoManager());
int size() const noexcept
void remove(int indexToRemove)
void add(const ElementType &newElement)
bool contains(ParameterType elementToLookFor) const
bool addIfNotAlreadyThere(ParameterType newElement)
#define TRANS(stringLiteral)
RangeType< TimePosition > TimeRange
A RangeType based on real time (i.e.
constexpr TimeDuration toDuration(TimePosition)
Converts a TimePosition to a TimeDuration.
Represents a duration in beats.
Represents a duration in real-life time.
Represents a position in real-life time.