11namespace tracktion {
inline namespace engine
18 if (midi.hasType (IDs::NOTE))
20 convertPropertyToType<int> (midi, IDs::p);
21 convertPropertyToType<double> (midi, IDs::b);
22 convertPropertyToType<double> (midi, IDs::l);
23 convertPropertyToType<int> (midi, IDs::v);
24 convertPropertyToType<int> (midi, IDs::c);
26 else if (midi.hasType (IDs::CONTROL))
28 convertPropertyToType<double> (midi, IDs::b);
29 convertPropertyToType<int> (midi, IDs::type);
30 convertPropertyToType<int> (midi, IDs::val);
31 convertPropertyToType<int> (midi, IDs::metadata);
33 else if (MidiExpression::isExpression (midi.getType()))
35 convertPropertyToType<double> (midi, IDs::b);
36 convertPropertyToType<double> (midi, IDs::v);
40 convertMidiPropertiesFromStrings (v);
44template <
typename Type>
45static void removeMidiEventFromSelection (Type* event)
47 for (SelectionManager::Iterator sm; sm.next();)
48 if (
auto sme = sm->getFirstItemOfType<SelectedMidiEvents>())
49 sme->removeSelectedEvent (event);
64 : list (o), tempoSequence (ts), firstBeatNum (editBeatOfListTimeZero), undoManager (um)
72 currentEventBeat = tempoSequence !=
nullptr ? (tempoSequence->
toBeats (TimePosition::fromSeconds (message.
getTimeStamp())) - toDuration (firstBeatNum))
88 : startNote (n), startBeat (beat)
90 modulations.ensureStorageAllocated (50);
104 ActiveNote::ChangeType type;
107 bool containsModulation (ActiveNote::ChangeType t)
109 for (
const auto& mod : modulations)
126 for (
auto activeNote : activeNotes)
127 if (activeNote->startNote.noteID == note.noteID)
133 void addNoteToList (
juce::MPENote note, BeatPosition endBeat)
135 if (
auto an = getActiveNote (note))
137 const auto startBeat = an->startBeat;
138 auto n = list.addNote (an->startNote.initialNote, startBeat, toDuration (endBeat - toDuration (startBeat)),
139 note.noteOnVelocity.as7BitInt(), 0, undoManager);
140 auto noteState = n->state;
142 auto lift = note.noteOffVelocity.as7BitInt();
143 auto timb = an->startNote.timbre.asUnsignedFloat();
144 auto pres = an->startNote.pressure.asUnsignedFloat();
145 auto bend = (
float) an->startNote.totalPitchbendInSemitones;
148 noteState.setProperty (IDs::lift, lift, undoManager);
150 if (timb != MidiList::defaultInitialTimbreValue)
151 noteState.setProperty (IDs::timb, timb, undoManager);
153 if (pres != MidiList::defaultInitialPressureValue)
154 noteState.setProperty (IDs::pres, pres, undoManager);
156 if (bend != MidiList::defaultInitialPitchBendValue)
157 noteState.setProperty (IDs::bend, bend, undoManager);
161 MidiExpression::createAndAddExpressionToNote (noteState, IDs::PRESSURE, BeatPosition(), pres, undoManager);
162 MidiExpression::createAndAddExpressionToNote (noteState, IDs::PITCHBEND, BeatPosition(), bend, undoManager);
164 for (
auto& mod : an->modulations)
166 auto getType = [] (ActiveNote::ChangeType type)
170 case ActiveNote::pitchbend:
return IDs::PITCHBEND;
171 case ActiveNote::pressure:
return IDs::PRESSURE;
172 case ActiveNote::timbre:
return IDs::TIMBRE;
179 const auto relativeBeat = mod.beat - startBeat;
181 MidiExpression::createAndAddExpressionToNote (noteState, getType (mod.type),
toPosition (relativeBeat), mod.value, undoManager);
190 activeNotes.
add (
new ActiveNote (note, currentEventBeat));
196 if (
auto an = getActiveNote (note))
197 an->modulations.add (ActiveNote::Modulation { currentEventBeat, note.pressure.asUnsignedFloat(), ActiveNote::pressure });
202 if (
auto an = getActiveNote (note))
203 an->modulations.add (ActiveNote::Modulation { currentEventBeat,
float (note.totalPitchbendInSemitones), ActiveNote::pitchbend });
208 if (
auto an = getActiveNote (note))
209 an->modulations.add (ActiveNote::Modulation { currentEventBeat, note.timbre.asUnsignedFloat(), ActiveNote::timbre });
218 addNoteToList (note, currentEventBeat);
227 for (
auto note : midiList.getNotes())
228 if (noteHasExpression (note->state))
236 inline juce::MidiMessage createNoteOn (
int midiChannel,
int noteNumber,
float noteOnVelocity)
noexcept
239 const float noteOnVelocityToUse =
std::max (noteOnVelocity, 1.0f / 127.0f);
243 inline juce::MidiMessage createPitchbend (
int midiChannel,
float pitchbend,
float pitchbendRange)
noexcept
249 inline juce::MidiMessage createPressure (
int midiChannel,
float pressure)
noexcept
261 inline juce::MidiMessage createNoteOff (
int midiChannel,
int noteNumber,
float noteOffVelocity)
noexcept
269 using namespace NoteHelpers;
270 jassert (state.hasType (IDs::NOTE));
272 const float pitchbend = state.getProperty (IDs::bend, MidiList::defaultInitialPitchBendValue);
273 const float pressure = state.getProperty (IDs::pres, MidiList::defaultInitialPressureValue);
274 const float timbre = state.getProperty (IDs::timb, MidiList::defaultInitialTimbreValue);
276 seq.addEvent (
juce::MidiMessage (createPitchbend (midiChannel, pitchbend, 48.0f), noteOnTime));
277 seq.addEvent (
juce::MidiMessage (createPressure (midiChannel, pressure), noteOnTime));
278 seq.addEvent (
juce::MidiMessage (createTimbre (midiChannel, timbre), noteOnTime));
283 BeatPosition notePlaybackBeat, BeatPosition notePlaybackEndBeat)
noexcept
285 using namespace NoteHelpers;
287 auto& ts = clip.edit.tempoSequence;
288 const auto beat = notePlaybackBeat + BeatDuration::fromBeats (
static_cast<double> (state.getProperty (IDs::b)));
290 if (beat > notePlaybackEndBeat)
296 if (state.hasType (IDs::PITCHBEND))
297 seq.addEvent (
juce::MidiMessage (createPitchbend (midiChannel, state[IDs::v], 48.0f), time));
298 else if (state.hasType (IDs::PRESSURE))
299 seq.addEvent (
juce::MidiMessage (createPressure (midiChannel, state[IDs::v]), time));
300 else if (state.hasType (IDs::TIMBRE))
301 seq.addEvent (
juce::MidiMessage (createTimbre (midiChannel, state[IDs::v]), time));
308 if (note.isMute() || note.getLengthBeats() <= BeatDuration::fromBeats (0.00001))
311 const auto downTime = tb ==
MidiList::TimeBase::beats ? note.getPlaybackBeats (MidiNote::startEdge, clip, grooveTemplate).inBeats()
312 : note.getPlaybackTime (MidiNote::startEdge, clip, grooveTemplate).inSeconds();
313 const auto upTime = tb ==
MidiList::TimeBase::beats ? note.getPlaybackBeats (MidiNote::endEdge, clip, grooveTemplate).inBeats()
314 : note.getPlaybackTime (MidiNote::endEdge, clip, grooveTemplate).inSeconds();
316 if (upTime < downTime || upTime <= 0.0)
319 auto state = note.state;
321 : clip.edit.tempoSequence.
toBeats (TimePosition::fromSeconds (downTime));
323 : clip.edit.tempoSequence.
toBeats (TimePosition::fromSeconds (upTime));
326 addMidiNoteOnExpressionToSequence (seq, state, midiChannel, downTime);
329 using namespace NoteHelpers;
330 const auto noteOnVelocity =
juce::jlimit (0.0f, 1.0f, note.getVelocity() / 127.0f);
335 addMidiExpressionToSequence (seq, v, clip, tb, midiChannel, noteStartBeat, noteEndBeat);
338 const auto noteOffVelocity = state.hasProperty (IDs::lift) ?
int (state[IDs::lift]) / 127.0f : 0.0f;
344 const MidiNote& note,
int channelNumber,
bool addNoteUp,
345 const GrooveTemplate* grooveTemplate)
349 if (note.isMute() || note.getLengthBeats() <= BeatDuration::fromBeats (0.00001))
352 const auto downTime = [&]
358 case MidiList::TimeBase::seconds: [[ fallthrough ]];
359 default:
return note.getPlaybackTime (MidiNote::startEdge, clip, grooveTemplate).inSeconds();
363 auto velocity = (
uint8_t) note.getVelocity();
364 int noteNumber = note.getNoteNumber();
369 const auto upTime = [&]
375 case MidiList::TimeBase::seconds: [[ fallthrough ]];
376 default:
return note.getPlaybackTime (MidiNote::endEdge, clip, grooveTemplate).inSeconds();
380 if (upTime > downTime && upTime > 0.0)
386 else if (downTime >= 0.0)
393 const MidiControllerEvent& controller,
int channelNumber)
395 const auto time = [&]
401 case MidiList::TimeBase::seconds: [[ fallthrough ]];
402 default:
return std::max (0_tp, controller.getEditTime (clip) -
toDuration (clip.getPosition().getStart())).inSeconds();
406 const auto type = controller.getType();
407 const auto value = controller.getControllerValue();
417 case MidiControllerEvent::programChangeType:
418 if (clip.isSendingBankChanges())
420 auto id = clip.getAudioTrack()->getIdForBank (controller.getMetadata());
429 case MidiControllerEvent::aftertouchType:
433 case MidiControllerEvent::pitchWheelType:
437 case MidiControllerEvent::channelPressureType:
449 const auto time = [&]
455 case MidiList::TimeBase::seconds: [[ fallthrough ]];
456 default:
return std::max (0_tp, sysex.getEditTime (clip) -
toDuration (clip.getPosition().getStart())).inSeconds();
459 auto m = sysex.getMessage();
460 m.setTimeStamp (time);
474 : seq (s), clip (c), timeBase (tb), groove (g)
479 midiChannelBegin = mpeZone.getFirstMemberChannel() - 1;
480 midiChannelEnd = mpeZone.getLastMemberChannel();
481 midiChannelLastAssigned = midiChannelEnd - 1;
487 clearNotesEndingBefore (note.getPlaybackBeats (MidiNote::startEdge, clip, groove));
489 clearNotesEndingBefore (note.getPlaybackTime (MidiNote::startEdge, clip, groove));
491 auto midiChannel = findMidiChannelForNewNote (note.getNoteNumber());
492 midiChannels[midiChannel].notes.add (¬e);
493 midiChannels[midiChannel].lastNoteNumberPlayed = note.getNoteNumber();
495 addExpressiveNoteToSequence (seq, clip, timeBase, note, zoneLayout.
getLowerZone().getMasterChannel() + midiChannel, groove);
504 for (
auto note : notes)
505 if (note == ¬eToLookFor)
511 bool isFree() const noexcept
513 return notes.isEmpty();
517 int lastNoteNumberPlayed = -1;
520 int getNumMidiChannels() const noexcept
522 return midiChannelEnd - midiChannelBegin;
525 void clearNotesEndingBefore (TimePosition time)
528 for (
auto& midiChannel : midiChannels)
529 for (
int i = midiChannel.notes.
size(); --i >= 0;)
530 if (
auto n = midiChannel.notes.getUnchecked (i))
531 if (n->getPlaybackTime (MidiNote::endEdge, clip, groove) <
time)
535 void clearNotesEndingBefore (BeatPosition time)
538 for (
auto& midiChannel : midiChannels)
539 for (
int i = midiChannel.notes.
size(); --i >= 0;)
540 if (
auto n = midiChannel.notes.getUnchecked (i))
541 if (n->getPlaybackBeats (MidiNote::endEdge, clip, groove) <
time)
545 void stopNote (MidiNote& note)
547 for (
int ch = midiChannelBegin; ch < midiChannelEnd; ++ch)
549 if (midiChannels[ch].notes.contains (¬e))
551 midiChannels[ch].notes.removeFirstMatchingValue (¬e);
559 int findMidiChannelForNewNote (
int noteNumber)
noexcept
561 const int numMidiChannels = getNumMidiChannels();
565 if (numMidiChannels == 1)
566 return midiChannelBegin;
568 for (
int ch = midiChannelBegin; ch != midiChannelEnd; ++ch)
569 if (midiChannels[ch].isFree() && midiChannels[ch].lastNoteNumberPlayed == noteNumber)
573 for (
int ch = midiChannelLastAssigned + 1; ; ++ch)
575 if (ch == midiChannelEnd)
576 ch = midiChannelBegin;
578 if (midiChannels[ch].isFree())
580 midiChannelLastAssigned = ch;
584 if (ch == midiChannelLastAssigned)
588 midiChannelLastAssigned = findMidiChannelPlayingClosestNonequalNote (noteNumber);
590 return midiChannelLastAssigned;
593 int findMidiChannelPlayingClosestNonequalNote (
int noteNumber)
noexcept
595 int channelWithClosestNote = midiChannelBegin;
596 int closestNoteDistance = 127;
598 for (
int ch = midiChannelBegin; ch < midiChannelEnd; ++ch)
600 for (
auto note : midiChannels[ch].notes)
602 const int noteDistance = std::abs (note->getNoteNumber() - noteNumber);
604 if (noteDistance > 0 && noteDistance < closestNoteDistance)
606 closestNoteDistance = noteDistance;
607 channelWithClosestNote = ch;
612 return channelWithClosestNote;
617 const MidiClip& clip;
619 const GrooveTemplate* groove;
621 MidiChannel midiChannels[16];
622 int midiChannelBegin, midiChannelEnd, midiChannelLastAssigned;
626static juce::ValueTree createNoteValueTree (
int pitch, BeatPosition beat, BeatDuration length,
int vel,
int col)
628 return createValueTree (IDs::NOTE,
630 IDs::b, beat.inBeats(),
631 IDs::l,
std::max (0.0, length.inBeats()),
636juce::ValueTree MidiNote::createNote (
const MidiNote& n, BeatPosition newStart, BeatDuration newLength)
639 v.setProperty (IDs::b, newStart.inBeats(),
nullptr);
640 v.setProperty (IDs::l, newLength.inBeats(),
nullptr);
648 updatePropertiesFromState();
651void MidiNote::updatePropertiesFromState()
654 startBeat = BeatPosition::fromBeats (
static_cast<double> (state.
getProperty (IDs::b)));
655 lengthInBeats =
std::max (BeatDuration(), BeatDuration::fromBeats (
static_cast<double> (state.
getProperty (IDs::l))));
666 return v.hasType (IDs::NOTE);
671 m.updatePropertiesFromState();
675 static void removeFromSelection (
MidiNote* m)
677 removeMidiEventFromSelection (m);
684 return c.getQuantisation().roundBeatToNearest (startBeat + toDuration (c.getContentStartBeat()))
685 - toDuration (c.getContentStartBeat());
698 return c.getQuantisation().roundBeatToNearest (startBeat + toDuration (c.getContentStartBeat()))
699 + lengthInBeats - toDuration (c.getContentStartBeat());
702BeatPosition MidiNote::getQuantisedEndBeat (
const MidiClip*
const c)
const
705 return getQuantisedEndBeat (*c);
707 return startBeat + lengthInBeats;
710BeatDuration MidiNote::getQuantisedLengthBeats (
const MidiClip& c)
const
715BeatDuration MidiNote::getQuantisedLengthBeats (
const MidiClip*
const c)
const
723 const auto quantisedBeatInEdit = c.getQuantisation().roundBeatToNearest (startBeat + toDuration (c.getContentStartBeat()));
725 return c.edit.tempoSequence.toTime (quantisedBeatInEdit);
730 const auto quantisedBeatInEdit = c.getQuantisation().roundBeatToNearest (startBeat + toDuration (c.getContentStartBeat()))
733 return c.edit.tempoSequence.toTime (quantisedBeatInEdit);
736TimeRange MidiNote::getEditTimeRange (
const MidiClip& c)
const
738 const auto quantisedStartBeat = c.getQuantisation().roundBeatToNearest (startBeat - toDuration (c.getLoopStartBeats()) + toDuration (c.getContentStartBeat()));
740 return { c.edit.tempoSequence.toTime (quantisedStartBeat),
741 c.edit.tempoSequence.toTime (quantisedStartBeat + lengthInBeats) };
744TimeDuration MidiNote::getLengthSeconds (
const MidiClip& c)
const
746 return getEditTimeRange (c).getLength();
750void MidiNote::setStartAndLength (BeatPosition newStartBeat, BeatDuration newLengthInBeats,
juce::UndoManager* undoManager)
752 newStartBeat =
std::max (BeatPosition(), newStartBeat);
754 if (newLengthInBeats <= BeatDuration())
757 if (startBeat != newStartBeat)
759 state.
setProperty (IDs::b, newStartBeat.inBeats(), undoManager);
760 startBeat = newStartBeat;
763 if (lengthInBeats != newLengthInBeats)
765 state.
setProperty (IDs::l, newLengthInBeats.inBeats(), undoManager);
766 lengthInBeats = newLengthInBeats;
774 if (getNoteNumber() != newNoteNumber)
776 state.
setProperty (IDs::p, newNoteNumber, undoManager);
777 noteNumber = (
uint8_t) newNoteNumber;
785 if (getVelocity() != newVelocity)
787 state.
setProperty (IDs::v, newVelocity, undoManager);
788 velocity = (
uint8_t) newVelocity;
796 if (getColour() != newColourIndex)
799 colour = (
uint8_t) newColourIndex;
805 if (isMute() != shouldMute)
808 mute = shouldMute ? 1 : 0;
813TimePosition MidiNote::getPlaybackTime (NoteEdge edge,
const MidiClip& clip,
const GrooveTemplate*
const grooveTemplate)
const
815 auto pos = clip.getPosition();
819 :
std::
min (getEditEndTime (clip), pos.getEnd()) - TimeDuration::fromSeconds (0.0001);
821 if (grooveTemplate !=
nullptr)
822 time = grooveTemplate->editTimeToGroovyTime (time, clip.getGrooveStrength(), clip.edit);
827BeatPosition MidiNote::getPlaybackBeats (NoteEdge edge,
const MidiClip& clip,
const GrooveTemplate*
const grooveTemplate)
const
829 auto pos = clip.getPosition();
832 auto time = edge == startEdge ? clip.getQuantisation().roundBeatToNearest (startBeat + toDuration (clip.getContentStartBeat()))
833 :
std::min (clip.getQuantisation().roundBeatToNearest (startBeat +
toDuration (clip.getContentStartBeat()))
834 + lengthInBeats, clip.edit.tempoSequence.toBeats (pos.getEnd())) - 0.00001_bd;
836 if (grooveTemplate !=
nullptr)
837 time = grooveTemplate->beatsTimeToGroovyTime (time, clip.getGrooveStrength());
839 return time -
toDuration (clip.edit.tempoSequence.toBeats (pos.getStart()));
843juce::ValueTree MidiControllerEvent::createControllerEvent (
const MidiControllerEvent& e, BeatPosition beat)
846 v.setProperty (IDs::b, beat.inBeats(),
nullptr);
850juce::ValueTree MidiControllerEvent::createControllerEvent (BeatPosition beat,
int controllerType,
int controllerValue)
852 return createValueTree (IDs::CONTROL,
853 IDs::b, beat.inBeats(),
854 IDs::type, controllerType,
855 IDs::val, controllerValue);
858juce::ValueTree MidiControllerEvent::createControllerEvent (BeatPosition beat,
int controllerType,
int controllerValue,
int metadata)
860 auto v = createControllerEvent (beat, controllerType, controllerValue);
861 v.setProperty (IDs::metadata, metadata,
nullptr);
867 beatNumber (BeatPosition::fromBeats (static_cast<
double> (v.getProperty (IDs::b)))),
868 type (
int (v.getProperty (IDs::type))),
869 value (v.getProperty (IDs::val))
871 updatePropertiesFromState();
874void MidiControllerEvent::updatePropertiesFromState() noexcept
876 beatNumber = BeatPosition::fromBeats (
static_cast<double> (state.
getProperty (IDs::b)));
887 return v.hasType (IDs::CONTROL);
892 e.updatePropertiesFromState();
898 removeMidiEventFromSelection (e);
903juce::String MidiControllerEvent::getControllerTypeName (
int type)
noexcept
910 case programChangeType:
return TRANS(
"Program Change");
911 case noteVelocities:
return TRANS(
"Note Velocities");
912 case aftertouchType:
return TRANS(
"Aftertouch");
913 case pitchWheelType:
return TRANS(
"Pitch Wheel");
914 case sysExType:
return TRANS(
"SysEx Events");
915 case channelPressureType:
return TRANS(
"Channel Pressure");
919 return "(" +
TRANS(
"Unnamed") +
")";
924 return c.getQuantisation().roundBeatToNearest (beatNumber - toDuration (c.getLoopStartBeats()) + toDuration (c.getContentStartBeat()));
934 const int coarseValue = value >> 7;
936 if (type == programChangeType)
941 if (ownerClip !=
nullptr && ownerClip->
getTrack() !=
nullptr)
943 auto audioTrack = ownerClip->getAudioTrack();
944 bank = audioTrack->getNameForBank (metadata);
945 program += audioTrack->getNameForProgramNumber (coarseValue, metadata);
953 return bank +
": " + program;
956 if (type == pitchWheelType)
959 const int percent =
juce::jlimit (-100, 100, (
int)((value - 0x2000) * (100.5 / 0x2000)));
969 if (type == channelPressureType)
972 if (type == aftertouchType)
979 if (controllerName.isEmpty())
982 return controllerName +
" - " +
juce::String (coarseValue);
997void MidiControllerEvent::setBeatPosition (BeatPosition newBeatNumber,
juce::UndoManager* um)
999 newBeatNumber =
std::max (BeatPosition(), newBeatNumber);
1001 if (beatNumber != newBeatNumber)
1003 state.
setProperty (IDs::b, newBeatNumber.inBeats(), um);
1004 beatNumber = newBeatNumber;
1017void MidiControllerEvent::setControllerValue (
int newValue,
juce::UndoManager* um)
1019 if (value != newValue)
1026int MidiControllerEvent::getControllerDefautValue (
int type)
1030 case pitchWheelType:
return 8192;
1031 case 7:
return 99 << 7;
1032 case 10:
return 64 << 7;
1043juce::ValueTree MidiSysexEvent::createSysexEvent (
const MidiSysexEvent& e, BeatPosition time)
1046 v.setProperty (IDs::time,
time.inBeats(),
nullptr);
1052 return createValueTree (IDs::SYSEX,
1053 IDs::time,
time.inBeats(),
1054 IDs::data, midiToHex (m));
1062void MidiSysexEvent::updateMessage()
1075void MidiSysexEvent::updateTime()
1085 return v.hasType (IDs::SYSEX);
1104 removeMidiEventFromSelection (m);
1110 return c.edit.tempoSequence.toTime (getEditBeats (c));
1115 return c.getQuantisation().roundBeatToNearest (getBeatPosition() - toDuration (c.getLoopStartBeats()) + toDuration (c.getContentStartBeat()));
1123void MidiSysexEvent::setBeatPosition (BeatPosition newBeatNumber,
juce::UndoManager* um)
1131 return createValueTree (IDs::SEQUENCE,
1133 IDs::channelNumber, MidiChannel (1));
1136MidiList::MidiList() : state (IDs::SEQUENCE)
1138 state.setProperty (IDs::ver, 1,
nullptr);
1139 initialise (
nullptr);
1145 jassert (state.hasType (IDs::SEQUENCE));
1146 state.setProperty (IDs::ver, 1, um);
1147 convertMidiPropertiesFromStrings (state);
1152MidiList::~MidiList()
1160 midiChannel.referTo (state, IDs::channelNumber, um);
1161 isComp.
referTo (state, IDs::isComp, um,
false);
1193 midiChannel = newChannel;
1197template<
typename EventType>
1203 for (
auto* e : events)
1205 auto beat = e->getBeatPosition();
1216 jassert (noteList !=
nullptr);
1217 return getEventsChecked (noteList->getSortedList());
1222 jassert (controllerList !=
nullptr);
1223 return getEventsChecked (controllerList->getSortedList());
1228 jassert (sysexList !=
nullptr);
1229 return getEventsChecked (sysexList->getSortedList());
1235 if (delta != BeatDuration())
1237 for (
auto e : getNotes())
1238 e->setStartAndLength (e->getStartBeat() + delta, e->getLengthBeats(), um);
1240 for (
auto e : getControllerEvents())
1241 e->setBeatPosition (e->getBeatPosition() + delta, um);
1243 for (
auto e : getSysexEvents())
1244 e->setBeatPosition (e->getBeatPosition() + delta, um);
1250 if (factor != 1.0 && factor > 0.001 && factor < 1000.0)
1252 for (
auto e : getNotes())
1253 e->setStartAndLength (e->getStartBeat() * factor,
1254 e->getLengthBeats() * factor, um);
1256 for (
auto e : getControllerEvents())
1257 e->setBeatPosition (e->getBeatPosition() * factor, um);
1259 for (
auto e : getSysexEvents())
1260 e->setBeatPosition (e->getBeatPosition() * factor, um);
1264void MidiList::trimOutside (BeatPosition start, BeatPosition end,
juce::UndoManager* um)
1268 for (
auto n : getNotes())
1270 if (n->getStartBeat() >= (end - BeatDuration::fromBeats (0.0001)) || n->getEndBeat() <= (start + BeatDuration::fromBeats (0.0001)))
1272 itemsToRemove.
add (n->state);
1274 else if (n->getStartBeat() < start || n->getEndBeat() > end)
1276 auto newStart =
std::max (start, n->getStartBeat());
1277 auto newEnd =
std::min (end, n->getEndBeat());
1279 n->setStartAndLength (newStart, newEnd - newStart, um);
1283 for (
auto e : getControllerEvents())
1284 if (e->getBeatPosition() < start || e->getBeatPosition() >=
end)
1285 itemsToRemove.add (e->state);
1287 for (
auto s : getSysexEvents())
1288 if (s->getBeatPosition() < start || s->getBeatPosition() >=
end)
1289 itemsToRemove.add (s->state);
1291 for (
auto& v : itemsToRemove)
1292 state.removeChild (v, um);
1300 if (
auto first = getNotes().getFirst()) t =
std::min (t, first->getStartBeat());
1301 if (
auto first = getControllerEvents().getFirst()) t =
std::min (t, first->getBeatPosition());
1302 if (
auto first = getSysexEvents().getFirst()) t =
std::min (t, first->getBeatPosition());
1311 if (
auto last = getNotes().getLast()) t =
std::max (t, last->getEndBeat());
1312 if (
auto last = getControllerEvents().getLast()) t =
std::max (t, last->getBeatPosition());
1313 if (
auto last = getSysexEvents().getLast()) t =
std::max (t, last->getBeatPosition());
1320 for (
auto n : getNotes())
1329 if (getNumNotes() == 0)
1335 for (
auto n : getNotes())
1346 auto v = note.state.createCopy();
1348 return noteList->getEventFor (v);
1351MidiNote* MidiList::addNote (
int pitch, BeatPosition startBeat, BeatDuration lengthInBeats,
1354 auto v = createNoteValueTree (pitch, startBeat, lengthInBeats, velocity, colourIndex);
1356 return noteList->getEventFor (v);
1372MidiControllerEvent* MidiList::getControllerEventAt (BeatPosition beatNumber,
int controllerType)
const
1374 auto& controllerEvents = getControllerEvents();
1376 for (
int i = controllerEvents.size(); --i >= 0;)
1378 auto e = controllerEvents.getUnchecked (i);
1380 if (e->getBeatPosition() <= beatNumber && e->getType() == controllerType)
1381 return controllerEvents.getUnchecked (i);
1389 for (
auto e : getControllerEvents())
1390 if (e->getType() == controllerType)
1398 auto v =
event.state.createCopy();
1400 return controllerList->getEventFor (v);
1405 auto v = MidiControllerEvent::createControllerEvent (beat, controllerType, controllerValue);
1407 return controllerList->getEventFor (v);
1410MidiControllerEvent* MidiList::addControllerEvent (BeatPosition beat,
int controllerType,
int controllerValue,
int metadata,
juce::UndoManager* um)
1412 auto v = MidiControllerEvent::createControllerEvent (beat, controllerType, controllerValue, metadata);
1414 return controllerList->getEventFor (v);
1417void MidiList::removeControllerEvent (MidiControllerEvent& e,
juce::UndoManager* um)
1429void MidiList::setControllerValueAt (
int controllerType, BeatPosition beatNumber,
int newValue,
juce::UndoManager* um)
1431 beatNumber =
std::max (BeatPosition(), beatNumber);
1432 auto& controllerEvents = getControllerEvents();
1434 for (
int i = controllerEvents.size(); --i >= 0;)
1436 auto e = controllerEvents.getUnchecked (i);
1438 if (e->getType() == controllerType
1439 && e->getBeatPosition() <= beatNumber)
1441 e->setControllerValue (newValue, um);
1447static int interpolate (
int startValue,
int rangeValues, BeatPosition startBeat, BeatPosition beat, BeatDuration rangeBeats)
noexcept
1449 return juce::roundToInt (startValue + (rangeValues * (beat - startBeat).inBeats() / rangeBeats.inBeats()));
1455 auto rangeBeats =
beats.getLength();
1456 auto rangeValues = endVal - startVal;
1460 addControllerEvent (
beats.getStart(), type, startVal, um);
1461 addControllerEvent (
beats.getEnd(), type, endVal, um);
1465 auto maxNumSteps =
std::max (2, type == MidiControllerEvent::pitchWheelType ? std::abs (rangeValues)
1466 : std::abs (rangeValues) >> 7);
1468 auto limitedNumSteps =
juce::jlimit (2, maxNumSteps, numSteps);
1470 if (numSteps != limitedNumSteps)
1472 numSteps = limitedNumSteps;
1473 intervalBeats = BeatDuration::fromBeats (rangeBeats.inBeats() / numSteps);
1476 auto beat =
beats.getStart();
1478 for (
int i = 0; i < numSteps; ++i)
1480 const int value = interpolate (startVal, rangeValues,
beats.getStart(), beat, rangeBeats);
1481 addControllerEvent (beat, type, value, um);
1482 beat = beat + intervalBeats;
1485 if (beat - intervalBeats <
beats.getEnd())
1486 addControllerEvent (
beats.getEnd(), type, endVal, um);
1493 for (
auto e : getControllerEvents())
1494 if (e->getType() == controllerType && e->getBeatPosition() >= beatStart && e->getBeatPosition() < beatEnd)
1495 itemsToRemove.add (e->state);
1497 for (
auto& v : itemsToRemove)
1498 state.removeChild (v, um);
1504 for (
auto n : getSysexEvents())
1513 auto v = MidiSysexEvent::createSysexEvent (message, beat);
1515 sysexList->triggerSort();
1517 return *sysexList->getEventFor (v);
1520void MidiList::removeSysExEvent (
const MidiSysexEvent& event,
juce::UndoManager* um)
1533bool MidiList::fileHasTempoChanges (
const juce::File& f)
1540 if (! in.openedOk() || ! midiFile.
readFrom (in))
1551bool MidiList::looksLikeMPEData (
const juce::File& f)
1558 if (! in.openedOk() || ! midiFile.
readFrom (in))
1564 for (
int currentTrack = midiFile.
getNumTracks(); --currentTrack >= 0;)
1566 auto& trackSequence = *midiFile.
getTrack (currentTrack);
1568 for (
int eventIndex = trackSequence.getNumEvents(); --eventIndex >= 0;)
1570 if (
auto* event = trackSequence.getEventPointer (eventIndex))
1572 const auto& m =
event->message;
1575 noteChannelsUsed.
setBit (m.getChannel());
1576 else if (m.isController() || m.isPitchWheel() || m.isChannelPressure())
1577 controllerChannelsUsed.
setBit (m.getChannel());
1589bool MidiList::readSeparateTracksFromFile (
const juce::File& f,
1595 BeatDuration& songLength,
1596 bool importAsNoteExpression)
1598 songLength = BeatDuration();
1604 if (! in.openedOk() || ! midiFile.
readFrom (in))
1617 int numer = 4, denom = 4;
1618 double secsPerQuarterNote = 0.5;
1621 auto tickLen = 1.0 / (timeFormat > 0 ? timeFormat & 0x7fff
1622 : ((timeFormat & 0x7fff) >> 8) * (timeFormat & 0xff));
1628 if (msg.isTempoMetaEvent())
1629 secsPerQuarterNote = msg.getTempoSecondsPerQuarterNote();
1630 else if (msg.isTimeSignatureMetaEvent())
1631 msg.getTimeSignatureInfo (numer, denom);
1637 if (msg2.getTimeStamp() == msg.getTimeStamp())
1641 if (msg2.isTempoMetaEvent())
1642 secsPerQuarterNote = msg2.getTempoSecondsPerQuarterNote();
1643 else if (msg2.isTimeSignatureMetaEvent())
1644 msg2.getTimeSignatureInfo (numer, denom);
1652 tempoChangeBeatNumbers.
add (BeatPosition::fromBeats (tickLen * msg.getTimeStamp()));
1653 bpms.
add (4.0 * 60.0 / (denom * secsPerQuarterNote));
1654 numerators.
add (numer);
1655 denominators.
add (denom);
1658 if (importAsNoteExpression)
1662 for (
int currentTrack = 0; currentTrack < midiFile.
getNumTracks(); ++currentTrack)
1664 auto& trackSequence = *midiFile.
getTrack (currentTrack);
1666 if (trackSequence.getNumEvents() > 0)
1668 for (
int i = trackSequence.getNumEvents(); --i >= 0;)
1671 msg.setTimeStamp (tickLen * msg.getTimeStamp());
1674 for (
int channel = 0; channel <= 16; ++channel)
1677 trackSequence.extractSysExMessages (destSequence);
1679 trackSequence.extractMidiChannelMessages (channel, destSequence,
true);
1686 destSequence.
sort();
1692 midiList->importFromEditTimeSequenceWithNoteExpression (destSequence,
nullptr, TimePosition(),
nullptr);
1694 if (! midiList->isEmpty())
1695 lists.
add (midiList.release());
1700 for (
int currentTrack = 0; currentTrack < midiFile.
getNumTracks(); ++currentTrack)
1702 auto& trackSequence = *midiFile.
getTrack (currentTrack);
1704 if (trackSequence.getNumEvents() > 0)
1706 for (
int i = trackSequence.getNumEvents(); --i >= 0;)
1709 msg.setTimeStamp (tickLen * msg.getTimeStamp());
1712 for (
int channel = 0; channel <= 16; ++channel)
1715 MidiChannel midiChannel ((uint8_t)
std::max (1, channel));
1718 trackSequence.extractSysExMessages (channelSequence);
1720 trackSequence.extractMidiChannelMessages (channel, channelSequence,
true);
1724 channelSequence.
sort();
1727 songLength =
std::max (songLength, BeatDuration::fromBeats (channelSequence.
getEndTime()));
1730 midiList->setMidiChannel (midiChannel);
1732 midiList->importMidiSequence (channelSequence,
nullptr, TimePosition(),
nullptr);
1734 if (! midiList->isEmpty())
1735 lists.
add (midiList.release());
1750 auto firstBeatNum = ts !=
nullptr ? ts->
toBeats (editTimeOfListTimeZero) :
BeatPosition();
1753 for (
int i = 0; i < sequence.getNumEvents(); ++i)
1755 auto& m = sequence.getEventPointer (i)->message;
1757 auto beatTime = ts !=
nullptr ? ts->
toBeats (TimePosition::fromSeconds (m.getTimeStamp())) - toDuration (firstBeatNum)
1758 : BeatPosition::fromBeats (m.getTimeStamp());
1762 importedName =
TRANS(
"SysEx");
1763 addSysExEvent (m, beatTime, um);
1765 else if (m.isTrackNameEvent())
1767 importedName = m.getTextFromTextMetaEvent();
1769 else if (m.isForChannel (channelNumber))
1771 if (channelNumber == 10)
1772 importedName =
TRANS(
"Drums");
1776 const auto keyUpTime = TimePosition::fromSeconds (sequence.getTimeOfMatchingKeyUp (i));
1778 addNote (m.getNoteNumber(),
1780 ts !=
nullptr ? ((ts->
toBeats (keyUpTime) - firstBeatNum) - toDuration (beatTime))
1781 : (BeatDuration::fromBeats (keyUpTime.inSeconds()) - toDuration (beatTime)),
1784 else if (m.isAftertouch())
1786 addControllerEvent (beatTime, MidiControllerEvent::aftertouchType,
1787 m.getAfterTouchValue() << 7, m.getNoteNumber(), um);
1789 else if (m.isPitchWheel())
1791 addControllerEvent (beatTime, MidiControllerEvent::pitchWheelType, m.getPitchWheelValue(), um);
1793 else if (m.isController())
1795 addControllerEvent (beatTime, m.getControllerNumber(), m.getControllerValue() << 7, um);
1797 else if (m.isProgramChange())
1802 addControllerEvent (beatTime, MidiControllerEvent::programChangeType, m.getProgramChangeNumber() << 7, um);
1804 else if (m.isChannelPressure())
1806 addControllerEvent (beatTime, MidiControllerEvent::channelPressureType, m.getChannelPressureValue() << 7, um);
1819 auto firstBeatNum = ts !=
nullptr ? ts->
toBeats (editTimeOfListTimeZero) :
BeatPosition();
1823 layout.setLowerZone (15);
1826 for (
int i = 0; i < sequence.getNumEvents(); ++i)
1828 auto& m = sequence.getEventPointer (i)->message;
1829 const auto beatTime = ts !=
nullptr ? ts->
toBeats (TimePosition::fromSeconds (m.getTimeStamp())) - toDuration (firstBeatNum)
1830 : BeatPosition::fromBeats (m.getTimeStamp());
1834 importedName =
TRANS(
"SysEx");
1835 addSysExEvent (m, beatTime, um);
1837 else if (m.isTrackNameEvent())
1839 importedName = m.getTextFromTextMetaEvent();
1843 if (channelNumber == 10)
1844 importedName =
TRANS(
"Drums");
1846 if (m.isNoteOn() || m.isNoteOff() || m.isPitchWheel() || m.isChannelPressure()
1847 || (m.isController() && m.getControllerNumber() == 74))
1849 noteQueue.processMidiMessage (m);
1851 else if (m.isAftertouch())
1853 addControllerEvent (beatTime, MidiControllerEvent::aftertouchType, m.getAfterTouchValue() << 7, m.getNoteNumber(), um);
1855 else if (m.isPitchWheel())
1857 addControllerEvent (beatTime, MidiControllerEvent::pitchWheelType, m.getPitchWheelValue(), um);
1859 else if (m.isController())
1861 addControllerEvent (beatTime, m.getControllerNumber(), m.getControllerValue() << 7, um);
1863 else if (m.isProgramChange())
1868 addControllerEvent (beatTime, MidiControllerEvent::programChangeType, m.getProgramChangeNumber() << 7, um);
1870 else if (m.isChannelPressure())
1872 addControllerEvent (beatTime, MidiControllerEvent::channelPressureType, m.getChannelPressureValue() << 7, um);
1884 return clip.edit.engine.getEngineBehaviour().createPlaybackMidiSequence (*
this, clip, tb, generateMPE);
1891 auto& ts = clip.edit.tempoSequence;
1892 auto midiStartBeat = clip.getContentStartBeat();
1896 const auto overlapAllowance = 0.5_bd;
1898 : toPosition (ts.
toBeats (clip.getPosition().getStart()) - midiStartBeat - overlapAllowance);
1900 : toPosition (ts.
toBeats (clip.getPosition().getEnd()) - midiStartBeat + overlapAllowance);
1902 auto& notes = list.getNotes();
1903 const auto numNotes = notes.size();
1904 auto selectedEvents = clip.getSelectedEvents();
1906 auto grooveTemplate = clip.edit.engine.getGrooveTemplateManager().getTemplateByName (clip.getGrooveTemplate());
1908 if (grooveTemplate !=
nullptr && grooveTemplate->isEmpty())
1909 grooveTemplate =
nullptr;
1912 auto& controllerEvents = list.getControllerEvents();
1918 for (
auto e : controllerEvents)
1920 auto beat = e->getBeatPosition();
1922 if (beat < firstNoteBeat)
1924 if (! doneControllers.
contains (e->getType()))
1926 addToSequence (destSequence, clip, timeBase, *e, channelNumber);
1927 doneControllers.
add (e->getType());
1934 for (
auto e : controllerEvents)
1936 auto beat = e->getBeatPosition();
1938 if (beat >= firstNoteBeat && beat < lastNoteBeat)
1939 addToSequence (destSequence, clip, timeBase, *e, channelNumber);
1945 for (
int i = 0; i < numNotes; ++i)
1947 auto& note = *notes.getUnchecked (i);
1949 if (selectedEvents !=
nullptr && ! selectedEvents->isSelected (¬e))
1953 auto thisNoteStart = note.getStartBeat();
1955 if (thisNoteStart >= lastNoteBeat)
1958 auto thisNoteEnd = note.getEndBeat();
1959 auto noteNum = note.getNoteNumber();
1960 bool useNoteUp =
true;
1962 for (
int j = i + 1; j < numNotes; ++j)
1964 const auto& note2 = *notes.getUnchecked (j);
1965 const auto s = note2.getStartBeat();
1967 if (s >= lastNoteBeat || s >= thisNoteEnd)
1970 if (note2.getNoteNumber() == noteNum)
1977 if (thisNoteEnd > firstNoteBeat)
1978 addToSequence (destSequence, clip, timeBase, note, channelNumber, useNoteUp, grooveTemplate);
1985 for (
auto note : notes)
1987 if (selectedEvents !=
nullptr && ! selectedEvents->isSelected (note))
1990 assigner.addNote (*note);
1995 for (
auto e : list.getSysexEvents())
1997 auto beat = e->getBeatPosition();
1999 if (beat >= firstNoteBeat && beat < lastNoteBeat)
2000 addToSequence (destSequence, clip, timeBase, *e);
2003 return destSequence;
void add(const ElementType &newElement)
bool contains(ParameterType elementToLookFor) const
int countNumberOfSetBits() const noexcept
BigInteger & setBit(int bitNumber)
void referTo(ValueTree &tree, const Identifier &property, UndoManager *um)
String getFileName() const
void setZoneLayout(MPEZoneLayout newLayout)
virtual void processNextMidiEvent(const MidiMessage &message)
void addListener(Listener *listenerToAdd)
void setLowerZone(int numMemberChannels=0, int perNotePitchbendRange=48, int masterPitchbendRange=2) noexcept
MPEZone getLowerZone() const noexcept
void * getData() noexcept
size_t getSize() const noexcept
void loadFromHexString(StringRef sourceHexString)
int getNumTracks() const noexcept
short getTimeFormat() const noexcept
bool readFrom(InputStream &sourceStream, bool createMatchingNoteOffs=true, int *midiFileType=nullptr)
void findAllTimeSigEvents(MidiMessageSequence &timeSigEvents) const
void findAllTempoEvents(MidiMessageSequence &tempoChangeEvents) const
const MidiMessageSequence * getTrack(int index) const noexcept
MidiEventHolder * addEvent(const MidiMessage &newMessage, double timeAdjustment=0)
void updateMatchedPairs() noexcept
double getStartTime() const noexcept
double getEndTime() const noexcept
MidiEventHolder * getEventPointer(int index) const noexcept
int getNumEvents() const noexcept
static MidiMessage aftertouchChange(int channel, int noteNumber, int aftertouchAmount) noexcept
static MidiMessage pitchWheel(int channel, int position) noexcept
static MidiMessage noteOn(int channel, int noteNumber, float velocity) noexcept
static uint16 pitchbendToPitchwheelPos(float pitchbendInSemitones, float pitchbendRangeInSemitones) noexcept
double getTimeStamp() const noexcept
static const char * getGMInstrumentName(int midiInstrumentNumber)
static MidiMessage controllerEvent(int channel, int controllerType, int value) noexcept
static uint8 floatValueToMidiByte(float valueBetween0and1) noexcept
static MidiMessage channelPressureChange(int channel, int pressure) noexcept
static MidiMessage noteOff(int channel, int noteNumber, float velocity) noexcept
void setTimeStamp(double newTimestamp) noexcept
static MidiMessage programChange(int channel, int programNumber) noexcept
static const char * getControllerName(int controllerNumber)
int size() const noexcept
ObjectClass * add(ObjectClass *newObject)
void removeObject(const ObjectClass *objectToRemove, bool deleteObject=true)
bool isEmpty() const noexcept
static String toHexString(IntegerType number)
bool hasType(const Identifier &typeName) const noexcept
void removeChild(const ValueTree &child, UndoManager *undoManager)
ValueTree getChild(int index) const
int getNumChildren() const noexcept
void copyPropertiesFrom(const ValueTree &source, UndoManager *undoManager)
ValueTree & setProperty(const Identifier &name, const var &newValue, UndoManager *undoManager)
void removeAllChildren(UndoManager *undoManager)
void addChild(const ValueTree &child, int index, UndoManager *undoManager)
const var & getProperty(const Identifier &name) const noexcept
ValueTree createCopy() const
Track * getTrack() const override
Returns the parent Track this clip is on (if any).
The Tracktion Edit class!
static constexpr double maximumLength
The maximum length an Edit can be.
TempoSequence tempoSequence
The global TempoSequence of this Edit.
static const int ticksPerQuarterNote
The number of ticks per quarter note.
Engine & engine
A reference to the Engine.
EngineBehaviour & getEngineBehaviour() const
Returns the EngineBehaviour instance.
Determines the channels to assign to overlapping notes.
MPEChannelAssigner(juce::MidiMessageSequence &s, const MidiClip &c, MidiList::TimeBase tb, const GrooveTemplate *g)
Constructor.
Creates a MidiList with NoteExpression from a stream of MPE MIDI messages.
BeatPosition getEditBeats(const MidiClip &) const
This takes into account quantising, groove templates, clip offset, etc.
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.
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.
BeatPosition getFirstBeatNumber() const
Beat number of first event in the list.
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.
BeatPosition getQuantisedStartBeat(const MidiClip &) const
Returns the start, quantised according to the clip's settings.
TimePosition getEditStartTime(const MidiClip &) const
Gets the start of this note in terms of edit time, taking into account quantising,...
Holds a list of TempoSetting objects, to form a sequence of tempo changes.
TimePosition toTime(BeatPosition) const
Converts a number of beats a time.
BeatPosition toBeats(TimePosition) const
Converts a time to a number of beats.
#define TRANS(stringLiteral)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
int roundToInt(const FloatType value) noexcept
BeatPosition toBeats(TimePosition tp, const TempoSequence &ts)
Converts a TimePosition to a BeatPosition given a TempoSequence.
constexpr TimePosition toPosition(TimeDuration)
Converts a TimeDuration to a TimePosition.
constexpr TimeDuration toDuration(TimePosition)
Converts a TimePosition to a TimeDuration.
Represents a duration in beats.
constexpr double inBeats() const
Returns the position as a number of beats.
Represents a position in beats.
Represents a position in real-life time.
constexpr double inSeconds() const
Returns the TimePosition as a number of seconds.
Represents a MIDI channel 1-16, and also contains extra ID bits to encode info about the event's orig...
static bool listHasExpression(const MidiList &midiList) noexcept
Returns true if this MidiList is an expressive note.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.