OpenShot Audio Library | OpenShotAudio  0.6.0
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 
23 namespace juce
24 {
25 
26 class MidiDeviceListConnectionBroadcaster final : private AsyncUpdater
27 {
28 public:
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 
70 private:
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 //==============================================================================
94 MidiDeviceListConnection::~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 //==============================================================================
107 MidiOutput::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 {
172 }
173 
175 {
176  stopThread (5000);
177 }
178 
179 void 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
static MessageManager * getInstance()
virtual void handlePartialSysexMessage(MidiInput *source, const uint8 *messageData, int numBytesSoFar, double timestamp)
static Array< MidiDeviceInfo > getAvailableDevices()
void sendBlockOfMessagesNow(const MidiBuffer &buffer)
static Array< MidiDeviceInfo > getAvailableDevices()
void sendMessageNow(const MidiMessage &message)
void sendBlockOfMessages(const MidiBuffer &buffer, double millisecondCounterToStartAt, double samplesPerSecondForBuffer)
bool wait(double timeOutMilliseconds) const
bool startThread()
bool threadShouldExit() const
bool stopThread(int timeOutMilliseconds)
void notify() const
bool isThreadRunning() const
static void waitForMillisecondCounter(uint32 targetTime) noexcept
Definition: juce_Time.cpp:267
static uint32 getMillisecondCounter() noexcept
Definition: juce_Time.cpp:241