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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_MidiNoteDispatcher.cpp
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
14MidiNoteDispatcher::MidiNoteDispatcher()
15{
16}
17
18MidiNoteDispatcher::~MidiNoteDispatcher()
19{
20 stopTimer();
21}
22
23void MidiNoteDispatcher::dispatchPendingMessagesForDevices (TimePosition editTime)
24{
25 const std::shared_lock sl (deviceMutex);
26
27 for (auto state : devices)
28 dispatchPendingMessages (*state, editTime);
29}
30
31void MidiNoteDispatcher::masterTimeUpdate (TimePosition editTime)
32{
33 const std::scoped_lock s (timeLock);
34 masterTime = editTime;
35 hiResClockOfMasterTime = juce::Time::getMillisecondCounterHiRes();
36}
37
38void MidiNoteDispatcher::prepareToPlay (TimePosition editTime)
39{
40 masterTimeUpdate (editTime);
41}
42
43TimePosition MidiNoteDispatcher::getCurrentTime() const
44{
45 const std::scoped_lock s (timeLock);
46 return masterTime + TimeDuration::fromSeconds ((juce::Time::getMillisecondCounterHiRes() - hiResClockOfMasterTime) * 0.001);
47}
48
49void MidiNoteDispatcher::dispatchPendingMessages (DeviceState& state, TimePosition editTime)
50{
51 // N.B. This should only be called under a deviceLock
52 auto& pendingBuffer = state.device.getPendingMessages();
53 state.device.context.masterLevels.processMidi (pendingBuffer, nullptr);
54 const auto delay = state.device.getMidiOutput().getDeviceDelay();
55
56 if (! state.device.sendMessages (pendingBuffer, editTime - delay))
57 state.buffer.mergeFromAndClear (pendingBuffer);
58}
59
60void MidiNoteDispatcher::setMidiDeviceList (const juce::OwnedArray<MidiOutputDeviceInstance>& newList)
61{
64
65 for (auto d : newList)
66 newDevices.add (new DeviceState (*d));
67
68 if (newList.isEmpty())
69 stopTimer();
70
71 bool startTimerFlag = false;
72
73 {
74 const std::unique_lock sl (deviceMutex);
75 newDevices.swapWith (devices);
76 startTimerFlag = ! devices.isEmpty();
77 }
78
79 if (startTimerFlag)
80 startTimer (1);
81}
82
83void MidiNoteDispatcher::hiResTimerCallback()
84{
85 struct MessageToSend
86 {
87 MidiOutputDeviceInstance* device;
88 juce::MidiMessage message;
89 };
90
91 juce::Array<MessageToSend> messagesToSend;
92 messagesToSend.ensureStorageAllocated (32);
93
94 {
95 const std::shared_lock sl (deviceMutex);
96
97 for (auto d : devices)
98 {
99 auto& device = d->device;
100 auto& buffer = d->buffer;
101 auto& midiOut = device.getMidiOutput();
102
103 if (buffer.isAllNotesOff)
104 midiOut.sendNoteOffMessages();
105
106 while (buffer.isNotEmpty())
107 {
108 auto& message = buffer[0];
109
110 auto noteTime = TimePosition::fromSeconds (message.getTimeStamp());
111 auto currentTime = getCurrentTime();
112
113 if (noteTime > currentTime + TimeDuration::fromSeconds (0.25))
114 {
115 buffer.remove (0);
116 }
117 else if (noteTime <= currentTime)
118 {
119 messagesToSend.add ({ &device, message });
120 buffer.remove (0);
121 }
122 else
123 {
124 break;
125 }
126 }
127 }
128 }
129
130 for (auto& m : messagesToSend)
131 m.device->getMidiOutput().fireMessage (m.message);
132}
133
134}} // namespace tracktion { inline namespace engine
void ensureStorageAllocated(int minNumElements)
void add(const ElementType &newElement)
void startTimer(int intervalInMilliseconds)
bool isEmpty() const noexcept
static double getMillisecondCounterHiRes() noexcept
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.