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 {
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 {
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 {
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//==============================================================================
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
113{
114 for (const auto metadata : buffer)
115 sendMessageNow (metadata.getMessage());
116}
117
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
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
170{
171 startThread (Priority::high);
172}
173
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 {
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
232}
233
234} // namespace juce
void cancelPendingUpdate() noexcept
static MessageManager * getInstance()
virtual void handlePartialSysexMessage(MidiInput *source, const uint8 *messageData, int numBytesSoFar, double timestamp)
static Array< MidiDeviceInfo > getAvailableDevices()
double getTimeStamp() const noexcept
void sendBlockOfMessagesNow(const MidiBuffer &buffer)
void sendMessageNow(const MidiMessage &message)
void sendBlockOfMessages(const MidiBuffer &buffer, double millisecondCounterToStartAt, double samplesPerSecondForBuffer)
static Array< MidiDeviceInfo > getAvailableDevices()
bool wait(double timeOutMilliseconds) const
bool threadShouldExit() const
bool stopThread(int timeOutMilliseconds)
void notify() const
bool isThreadRunning() const
static void waitForMillisecondCounter(uint32 targetTime) noexcept
static uint32 getMillisecondCounter() noexcept