OpenShot Audio Library | OpenShotAudio: juce_MidiDevices.cpp Source File

OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_MidiDevices.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26class MidiDeviceListConnectionBroadcaster final : private AsyncUpdater
27{
28public:
29 ~MidiDeviceListConnectionBroadcaster() override
30 {
31 cancelPendingUpdate();
32 }
33
34 MidiDeviceListConnection::Key add (std::function<void()> callback)
35 {
36 JUCE_ASSERT_MESSAGE_THREAD
37 return callbacks.emplace (key++, std::move (callback)).first->first;
38 }
39
40 void remove (const MidiDeviceListConnection::Key k)
41 {
42 JUCE_ASSERT_MESSAGE_THREAD
43 callbacks.erase (k);
44 }
45
46 void notify()
47 {
48 if (MessageManager::getInstance()->isThisTheMessageThread())
49 {
50 cancelPendingUpdate();
51
52 const State newState;
53
54 if (std::exchange (lastNotifiedState, newState) != newState)
55 for (auto it = callbacks.begin(); it != callbacks.end();)
56 NullCheckedInvocation::invoke ((it++)->second);
57 }
58 else
59 {
60 triggerAsyncUpdate();
61 }
62 }
63
64 static auto& get()
65 {
66 static MidiDeviceListConnectionBroadcaster result;
67 return result;
68 }
69
70private:
71 MidiDeviceListConnectionBroadcaster() = default;
72
73 class State
74 {
75 Array<MidiDeviceInfo> ins = MidiInput::getAvailableDevices(), outs = MidiOutput::getAvailableDevices();
76 auto tie() const { return std::tie (ins, outs); }
77
78 public:
79 bool operator== (const State& other) const { return tie() == other.tie(); }
80 bool operator!= (const State& other) const { return tie() != other.tie(); }
81 };
82
83 void handleAsyncUpdate() override
84 {
85 notify();
86 }
87
88 std::map<MidiDeviceListConnection::Key, std::function<void()>> callbacks;
89 State lastNotifiedState;
90 MidiDeviceListConnection::Key key = 0;
91};
92
93//==============================================================================
94MidiDeviceListConnection::~MidiDeviceListConnection() noexcept
95{
96 if (broadcaster != nullptr)
97 broadcaster->remove (key);
98}
99
100//==============================================================================
101 void MidiInputCallback::handlePartialSysexMessage ([[maybe_unused]] MidiInput* source,
102 [[maybe_unused]] const uint8* messageData,
103 [[maybe_unused]] int numBytesSoFar,
104 [[maybe_unused]] double timestamp) {}
105
106//==============================================================================
107MidiOutput::MidiOutput (const String& deviceName, const String& deviceIdentifier)
108 : Thread ("midi out"), deviceInfo (deviceName, deviceIdentifier)
109{
110}
111
112 void MidiOutput::sendBlockOfMessagesNow (const MidiBuffer& buffer)
113{
114 for (const auto metadata : buffer)
115 sendMessageNow (metadata.getMessage());
116}
117
118 void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer,
119 double millisecondCounterToStartAt,
120 double samplesPerSecondForBuffer)
121{
122 // You've got to call startBackgroundThread() for this to actually work..
123 jassert (isThreadRunning());
124
125 // this needs to be a value in the future - RTFM for this method!
126 jassert (millisecondCounterToStartAt > 0);
127
128 auto timeScaleFactor = 1000.0 / samplesPerSecondForBuffer;
129
130 for (const auto metadata : buffer)
131 {
132 auto eventTime = millisecondCounterToStartAt + timeScaleFactor * metadata.samplePosition;
133 auto* m = new PendingMessage (metadata.data, metadata.numBytes, eventTime);
134
135 const ScopedLock sl (lock);
136
137 if (firstMessage == nullptr || firstMessage->message.getTimeStamp() > eventTime)
138 {
139 m->next = firstMessage;
140 firstMessage = m;
141 }
142 else
143 {
144 auto* mm = firstMessage;
145
146 while (mm->next != nullptr && mm->next->message.getTimeStamp() <= eventTime)
147 mm = mm->next;
148
149 m->next = mm->next;
150 mm->next = m;
151 }
152 }
153
154 notify();
155}
156
157 void MidiOutput::clearAllPendingMessages()
158{
159 const ScopedLock sl (lock);
160
161 while (firstMessage != nullptr)
162 {
163 auto* m = firstMessage;
164 firstMessage = firstMessage->next;
165 delete m;
166 }
167}
168
169 void MidiOutput::startBackgroundThread()
170{
171 startThread (Priority::high);
172}
173
174 void MidiOutput::stopBackgroundThread()
175{
176 stopThread (5000);
177}
178
179void MidiOutput::run()
180{
181 while (! threadShouldExit())
182 {
183 auto now = Time::getMillisecondCounter();
184 uint32 eventTime = 0;
185 uint32 timeToWait = 500;
186
187 PendingMessage* message;
188
189 {
190 const ScopedLock sl (lock);
191 message = firstMessage;
192
193 if (message != nullptr)
194 {
195 eventTime = (uint32) roundToInt (message->message.getTimeStamp());
196
197 if (eventTime > now + 20)
198 {
199 timeToWait = eventTime - (now + 20);
200 message = nullptr;
201 }
202 else
203 {
204 firstMessage = message->next;
205 }
206 }
207 }
208
209 if (message != nullptr)
210 {
211 std::unique_ptr<PendingMessage> messageDeleter (message);
212
213 if (eventTime > now)
214 {
215 Time::waitForMillisecondCounter (eventTime);
216
217 if (threadShouldExit())
218 break;
219 }
220
221 if (eventTime > now - 200)
222 sendMessageNow (message->message);
223 }
224 else
225 {
226 jassert (timeToWait < 1000 * 30);
227 wait ((int) timeToWait);
228 }
229 }
230
231 clearAllPendingMessages();
232}
233
234} // namespace juce
juce::AsyncUpdater::triggerAsyncUpdate
void triggerAsyncUpdate()
Definition juce_AsyncUpdater.cpp:62
juce::AsyncUpdater::cancelPendingUpdate
void cancelPendingUpdate() noexcept
Definition juce_AsyncUpdater.cpp:74
juce::GenericScopedLock
Definition juce_ScopedLock.h:55
juce::MessageManager::getInstance
static MessageManager * getInstance()
Definition juce_MessageManager.cpp:47
juce::MidiBuffer
Definition juce_MidiBuffer.h:145
juce::MidiInputCallback::handlePartialSysexMessage
virtual void handlePartialSysexMessage(MidiInput *source, const uint8 *messageData, int numBytesSoFar, double timestamp)
Definition juce_MidiDevices.cpp:101
juce::MidiInput
Definition juce_MidiDevices.h:164
juce::MidiInput::getAvailableDevices
static Array< MidiDeviceInfo > getAvailableDevices()
juce::MidiMessage::getTimeStamp
double getTimeStamp() const noexcept
Definition juce_MidiMessage.h:164
juce::MidiOutput::sendBlockOfMessagesNow
void sendBlockOfMessagesNow(const MidiBuffer &buffer)
Definition juce_MidiDevices.cpp:112
juce::MidiOutput::stopBackgroundThread
void stopBackgroundThread()
Definition juce_MidiDevices.cpp:174
juce::MidiOutput::clearAllPendingMessages
void clearAllPendingMessages()
Definition juce_MidiDevices.cpp:157
juce::MidiOutput::startBackgroundThread
void startBackgroundThread()
Definition juce_MidiDevices.cpp:169
juce::MidiOutput::sendMessageNow
void sendMessageNow(const MidiMessage &message)
juce::MidiOutput::sendBlockOfMessages
void sendBlockOfMessages(const MidiBuffer &buffer, double millisecondCounterToStartAt, double samplesPerSecondForBuffer)
Definition juce_MidiDevices.cpp:118
juce::MidiOutput::getAvailableDevices
static Array< MidiDeviceInfo > getAvailableDevices()
juce::String
Definition juce_String.h:53
juce::Thread
Definition juce_Thread.h:43
juce::Thread::wait
bool wait(double timeOutMilliseconds) const
Definition juce_Thread.cpp:288
juce::Thread::startThread
bool startThread()
Definition juce_Thread.cpp:145
juce::Thread::threadShouldExit
bool threadShouldExit() const
Definition juce_Thread.cpp:202
juce::Thread::stopThread
bool stopThread(int timeOutMilliseconds)
Definition juce_Thread.cpp:233
juce::Thread::notify
void notify() const
Definition juce_Thread.cpp:293
juce::Thread::isThreadRunning
bool isThreadRunning() const
Definition juce_Thread.cpp:180
juce::Time::waitForMillisecondCounter
static void waitForMillisecondCounter(uint32 targetTime) noexcept
Definition juce_Time.cpp:267
juce::Time::getMillisecondCounter
static uint32 getMillisecondCounter() noexcept
Definition juce_Time.cpp:241