The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1319 lines
42KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. struct MidiServiceType
  20. {
  21. struct InputWrapper
  22. {
  23. virtual ~InputWrapper() {}
  24. virtual String getDeviceName() = 0;
  25. virtual void start() = 0;
  26. virtual void stop() = 0;
  27. };
  28. struct OutputWrapper
  29. {
  30. virtual ~OutputWrapper() {}
  31. virtual String getDeviceName() = 0;
  32. virtual void sendMessageNow (const MidiMessage&) = 0;
  33. };
  34. MidiServiceType() {}
  35. virtual ~MidiServiceType() {}
  36. virtual StringArray getDevices (bool) = 0;
  37. virtual int getDefaultDeviceIndex (bool) = 0;
  38. virtual InputWrapper* createInputWrapper (MidiInput&, int, MidiInputCallback&) = 0;
  39. virtual OutputWrapper* createOutputWrapper (int) = 0;
  40. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiServiceType)
  41. };
  42. //==============================================================================
  43. struct Win32MidiService : public MidiServiceType,
  44. private Timer
  45. {
  46. Win32MidiService() {}
  47. StringArray getDevices (bool isInput) override
  48. {
  49. return isInput ? Win32InputWrapper::getDevices()
  50. : Win32OutputWrapper::getDevices();
  51. }
  52. int getDefaultDeviceIndex (bool isInput) override
  53. {
  54. return isInput ? Win32InputWrapper::getDefaultDeviceIndex()
  55. : Win32OutputWrapper::getDefaultDeviceIndex();
  56. }
  57. InputWrapper* createInputWrapper (MidiInput& input, int index, MidiInputCallback& callback) override
  58. {
  59. return new Win32InputWrapper (*this, input, index, callback);
  60. }
  61. OutputWrapper* createOutputWrapper (int index) override
  62. {
  63. return new Win32OutputWrapper (*this, index);
  64. }
  65. private:
  66. struct Win32InputWrapper;
  67. //==============================================================================
  68. struct MidiInCollector : public ReferenceCountedObject
  69. {
  70. MidiInCollector (Win32MidiService& s, const String& name) : deviceName (name), midiService (s) {}
  71. ~MidiInCollector()
  72. {
  73. stop();
  74. if (deviceHandle != 0)
  75. {
  76. for (int count = 5; --count >= 0;)
  77. {
  78. if (midiInClose (deviceHandle) == MMSYSERR_NOERROR)
  79. break;
  80. Sleep (20);
  81. }
  82. }
  83. }
  84. using Ptr = ReferenceCountedObjectPtr<MidiInCollector>;
  85. void addClient (Win32InputWrapper* c)
  86. {
  87. const ScopedLock sl (clientLock);
  88. jassert (! clients.contains (c));
  89. clients.add (c);
  90. }
  91. void removeClient (Win32InputWrapper* c)
  92. {
  93. const ScopedLock sl (clientLock);
  94. clients.removeFirstMatchingValue (c);
  95. startOrStop();
  96. midiService.asyncCheckForUnusedCollectors();
  97. }
  98. void handleMessage (const uint8* bytes, uint32 timeStamp)
  99. {
  100. if (bytes[0] >= 0x80 && isStarted.load())
  101. {
  102. {
  103. auto len = MidiMessage::getMessageLengthFromFirstByte (bytes[0]);
  104. auto time = convertTimeStamp (timeStamp);
  105. const ScopedLock sl (clientLock);
  106. for (auto* c : clients)
  107. c->pushMidiData (bytes, len, time);
  108. }
  109. writeFinishedBlocks();
  110. }
  111. }
  112. void handleSysEx (MIDIHDR* hdr, uint32 timeStamp)
  113. {
  114. if (isStarted.load() && hdr->dwBytesRecorded > 0)
  115. {
  116. {
  117. auto time = convertTimeStamp (timeStamp);
  118. const ScopedLock sl (clientLock);
  119. for (auto* c : clients)
  120. c->pushMidiData (hdr->lpData, (int) hdr->dwBytesRecorded, time);
  121. }
  122. writeFinishedBlocks();
  123. }
  124. }
  125. void startOrStop()
  126. {
  127. const ScopedLock sl (clientLock);
  128. if (countRunningClients() == 0)
  129. stop();
  130. else
  131. start();
  132. }
  133. void start()
  134. {
  135. if (deviceHandle != 0 && ! isStarted.load())
  136. {
  137. activeMidiCollectors.addIfNotAlreadyThere (this);
  138. for (int i = 0; i < (int) numHeaders; ++i)
  139. {
  140. headers[i].prepare (deviceHandle);
  141. headers[i].write (deviceHandle);
  142. }
  143. startTime = Time::getMillisecondCounterHiRes();
  144. auto res = midiInStart (deviceHandle);
  145. if (res == MMSYSERR_NOERROR)
  146. isStarted = true;
  147. else
  148. unprepareAllHeaders();
  149. }
  150. }
  151. void stop()
  152. {
  153. if (isStarted.load())
  154. {
  155. isStarted = false;
  156. midiInReset (deviceHandle);
  157. midiInStop (deviceHandle);
  158. activeMidiCollectors.removeFirstMatchingValue (this);
  159. unprepareAllHeaders();
  160. }
  161. }
  162. static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance,
  163. DWORD_PTR midiMessage, DWORD_PTR timeStamp)
  164. {
  165. auto* collector = reinterpret_cast<MidiInCollector*> (dwInstance);
  166. // This is primarily a check for the collector being a dangling
  167. // pointer, as the callback can sometimes be delayed
  168. if (activeMidiCollectors.contains (collector))
  169. {
  170. if (uMsg == MIM_DATA)
  171. collector->handleMessage ((const uint8*) &midiMessage, (uint32) timeStamp);
  172. else if (uMsg == MIM_LONGDATA)
  173. collector->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp);
  174. }
  175. }
  176. String deviceName;
  177. HMIDIIN deviceHandle = 0;
  178. private:
  179. Win32MidiService& midiService;
  180. CriticalSection clientLock;
  181. Array<Win32InputWrapper*> clients;
  182. std::atomic<bool> isStarted { false };
  183. double startTime = 0;
  184. // This static array is used to prevent occasional callbacks to objects that are
  185. // in the process of being deleted
  186. static Array<MidiInCollector*, CriticalSection> activeMidiCollectors;
  187. int countRunningClients() const
  188. {
  189. int num = 0;
  190. for (auto* c : clients)
  191. if (c->started)
  192. ++num;
  193. return num;
  194. }
  195. struct MidiHeader
  196. {
  197. MidiHeader() {}
  198. void prepare (HMIDIIN device)
  199. {
  200. zerostruct (hdr);
  201. hdr.lpData = data;
  202. hdr.dwBufferLength = (DWORD) numElementsInArray (data);
  203. midiInPrepareHeader (device, &hdr, sizeof (hdr));
  204. }
  205. void unprepare (HMIDIIN device)
  206. {
  207. if ((hdr.dwFlags & WHDR_DONE) != 0)
  208. {
  209. int c = 10;
  210. while (--c >= 0 && midiInUnprepareHeader (device, &hdr, sizeof (hdr)) == MIDIERR_STILLPLAYING)
  211. Thread::sleep (20);
  212. jassert (c >= 0);
  213. }
  214. }
  215. void write (HMIDIIN device)
  216. {
  217. hdr.dwBytesRecorded = 0;
  218. midiInAddBuffer (device, &hdr, sizeof (hdr));
  219. }
  220. void writeIfFinished (HMIDIIN device)
  221. {
  222. if ((hdr.dwFlags & WHDR_DONE) != 0)
  223. write (device);
  224. }
  225. MIDIHDR hdr;
  226. char data[256];
  227. JUCE_DECLARE_NON_COPYABLE (MidiHeader)
  228. };
  229. enum { numHeaders = 32 };
  230. MidiHeader headers[numHeaders];
  231. void writeFinishedBlocks()
  232. {
  233. for (int i = 0; i < (int) numHeaders; ++i)
  234. headers[i].writeIfFinished (deviceHandle);
  235. }
  236. void unprepareAllHeaders()
  237. {
  238. for (int i = 0; i < (int) numHeaders; ++i)
  239. headers[i].unprepare (deviceHandle);
  240. }
  241. double convertTimeStamp (uint32 timeStamp)
  242. {
  243. auto t = startTime + timeStamp;
  244. auto now = Time::getMillisecondCounterHiRes();
  245. if (t > now)
  246. {
  247. if (t > now + 2.0)
  248. startTime -= 1.0;
  249. t = now;
  250. }
  251. return t * 0.001;
  252. }
  253. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInCollector)
  254. };
  255. //==============================================================================
  256. struct Win32InputWrapper : public InputWrapper
  257. {
  258. Win32InputWrapper (Win32MidiService& parentService,
  259. MidiInput& midiInput, int index, MidiInputCallback& c)
  260. : input (midiInput), callback (c)
  261. {
  262. collector = getOrCreateCollector (parentService, index);
  263. collector->addClient (this);
  264. }
  265. ~Win32InputWrapper()
  266. {
  267. collector->removeClient (this);
  268. }
  269. static MidiInCollector::Ptr getOrCreateCollector (Win32MidiService& parentService, int index)
  270. {
  271. auto names = getDevices();
  272. UINT deviceID = MIDI_MAPPER;
  273. String deviceName;
  274. if (isPositiveAndBelow (index, names.size()))
  275. {
  276. deviceName = names[index];
  277. deviceID = index;
  278. }
  279. const ScopedLock sl (parentService.activeCollectorLock);
  280. for (auto& c : parentService.activeCollectors)
  281. if (c->deviceName == deviceName)
  282. return c;
  283. MidiInCollector::Ptr c (new MidiInCollector (parentService, deviceName));
  284. HMIDIIN h;
  285. auto err = midiInOpen (&h, deviceID,
  286. (DWORD_PTR) &MidiInCollector::midiInCallback,
  287. (DWORD_PTR) (MidiInCollector*) c.get(),
  288. CALLBACK_FUNCTION);
  289. if (err != MMSYSERR_NOERROR)
  290. throw std::runtime_error ("Failed to create Windows input device wrapper");
  291. c->deviceHandle = h;
  292. parentService.activeCollectors.add (c);
  293. return c;
  294. }
  295. static StringArray getDevices()
  296. {
  297. StringArray s;
  298. auto num = midiInGetNumDevs();
  299. for (UINT i = 0; i < num; ++i)
  300. {
  301. MIDIINCAPS mc = { 0 };
  302. if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  303. s.add (String (mc.szPname, (size_t) numElementsInArray (mc.szPname)));
  304. }
  305. s.appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 (""));
  306. return s;
  307. }
  308. static int getDefaultDeviceIndex() { return 0; }
  309. void start() override { started = true; concatenator.reset(); collector->startOrStop(); }
  310. void stop() override { started = false; collector->startOrStop(); concatenator.reset(); }
  311. String getDeviceName() override { return collector->deviceName; }
  312. void pushMidiData (const void* inputData, int numBytes, double time)
  313. {
  314. concatenator.pushMidiData (inputData, numBytes, time, &input, callback);
  315. }
  316. MidiInput& input;
  317. MidiInputCallback& callback;
  318. MidiDataConcatenator concatenator { 4096 };
  319. MidiInCollector::Ptr collector;
  320. bool started = false;
  321. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32InputWrapper)
  322. };
  323. //==============================================================================
  324. struct MidiOutHandle : public ReferenceCountedObject
  325. {
  326. using Ptr = ReferenceCountedObjectPtr<MidiOutHandle>;
  327. MidiOutHandle (Win32MidiService& parent, const String& name, HMIDIOUT h)
  328. : owner (parent), deviceName (name), handle (h)
  329. {
  330. owner.activeOutputHandles.add (this);
  331. }
  332. ~MidiOutHandle()
  333. {
  334. if (handle != nullptr)
  335. midiOutClose (handle);
  336. owner.activeOutputHandles.removeFirstMatchingValue (this);
  337. }
  338. Win32MidiService& owner;
  339. String deviceName;
  340. HMIDIOUT handle;
  341. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutHandle)
  342. };
  343. //==============================================================================
  344. struct Win32OutputWrapper : public OutputWrapper
  345. {
  346. Win32OutputWrapper (Win32MidiService& p, int index) : parent (p)
  347. {
  348. auto names = getDevices();
  349. UINT deviceID = MIDI_MAPPER;
  350. if (isPositiveAndBelow (index, names.size()))
  351. {
  352. deviceName = names[index];
  353. deviceID = index;
  354. }
  355. if (deviceID == MIDI_MAPPER)
  356. {
  357. // use the microsoft sw synth as a default - best not to allow deviceID
  358. // to be MIDI_MAPPER, or else device sharing breaks
  359. for (int i = 0; i < names.size(); ++i)
  360. if (names[i].containsIgnoreCase ("microsoft"))
  361. deviceID = (UINT) i;
  362. }
  363. for (int i = parent.activeOutputHandles.size(); --i >= 0;)
  364. {
  365. auto* activeHandle = parent.activeOutputHandles.getUnchecked (i);
  366. if (activeHandle->deviceName == deviceName)
  367. {
  368. han = activeHandle;
  369. return;
  370. }
  371. }
  372. for (int i = 4; --i >= 0;)
  373. {
  374. HMIDIOUT h = 0;
  375. auto res = midiOutOpen (&h, deviceID, 0, 0, CALLBACK_NULL);
  376. if (res == MMSYSERR_NOERROR)
  377. {
  378. han = new MidiOutHandle (parent, deviceName, h);
  379. return;
  380. }
  381. if (res == MMSYSERR_ALLOCATED)
  382. Sleep (100);
  383. else
  384. break;
  385. }
  386. throw std::runtime_error ("Failed to create Windows output device wrapper");
  387. }
  388. void sendMessageNow (const MidiMessage& message) override
  389. {
  390. if (message.getRawDataSize() > 3 || message.isSysEx())
  391. {
  392. MIDIHDR h = { 0 };
  393. h.lpData = (char*) message.getRawData();
  394. h.dwBytesRecorded = h.dwBufferLength = (DWORD) message.getRawDataSize();
  395. if (midiOutPrepareHeader (han->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR)
  396. {
  397. auto res = midiOutLongMsg (han->handle, &h, sizeof (MIDIHDR));
  398. if (res == MMSYSERR_NOERROR)
  399. {
  400. while ((h.dwFlags & MHDR_DONE) == 0)
  401. Sleep (1);
  402. int count = 500; // 1 sec timeout
  403. while (--count >= 0)
  404. {
  405. res = midiOutUnprepareHeader (han->handle, &h, sizeof (MIDIHDR));
  406. if (res == MIDIERR_STILLPLAYING)
  407. Sleep (2);
  408. else
  409. break;
  410. }
  411. }
  412. }
  413. }
  414. else
  415. {
  416. for (int i = 0; i < 50; ++i)
  417. {
  418. if (midiOutShortMsg (han->handle, *(unsigned int*) message.getRawData()) != MIDIERR_NOTREADY)
  419. break;
  420. Sleep (1);
  421. }
  422. }
  423. }
  424. static Array<MIDIOUTCAPS> getDeviceCaps()
  425. {
  426. Array<MIDIOUTCAPS> devices;
  427. auto num = midiOutGetNumDevs();
  428. for (UINT i = 0; i < num; ++i)
  429. {
  430. MIDIOUTCAPS mc = { 0 };
  431. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  432. devices.add (mc);
  433. }
  434. return devices;
  435. }
  436. static StringArray getDevices()
  437. {
  438. StringArray s;
  439. for (auto& mc : getDeviceCaps())
  440. s.add (String (mc.szPname, (size_t) numElementsInArray (mc.szPname)));
  441. s.appendNumbersToDuplicates (false, false, CharPointer_UTF8 ("-"), CharPointer_UTF8 (""));
  442. return s;
  443. }
  444. static int getDefaultDeviceIndex()
  445. {
  446. int n = 0;
  447. for (auto& mc : getDeviceCaps())
  448. {
  449. if ((mc.wTechnology & MOD_MAPPER) != 0)
  450. return n;
  451. ++n;
  452. }
  453. return 0;
  454. }
  455. String getDeviceName() override { return deviceName; }
  456. Win32MidiService& parent;
  457. String deviceName;
  458. MidiOutHandle::Ptr han;
  459. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Win32OutputWrapper)
  460. };
  461. //==============================================================================
  462. void asyncCheckForUnusedCollectors()
  463. {
  464. startTimer (10);
  465. }
  466. void timerCallback() override
  467. {
  468. stopTimer();
  469. const ScopedLock sl (activeCollectorLock);
  470. for (int i = activeCollectors.size(); --i >= 0;)
  471. if (activeCollectors.getObjectPointer(i)->getReferenceCount() == 1)
  472. activeCollectors.remove (i);
  473. }
  474. CriticalSection activeCollectorLock;
  475. ReferenceCountedArray<MidiInCollector> activeCollectors;
  476. Array<MidiOutHandle*> activeOutputHandles;
  477. };
  478. Array<Win32MidiService::MidiInCollector*, CriticalSection> Win32MidiService::MidiInCollector::activeMidiCollectors;
  479. //==============================================================================
  480. //==============================================================================
  481. #if JUCE_USE_WINRT_MIDI
  482. using namespace Microsoft::WRL;
  483. using namespace ABI::Windows::Foundation;
  484. using namespace ABI::Windows::Devices::Midi;
  485. using namespace ABI::Windows::Devices::Enumeration;
  486. using namespace ABI::Windows::Storage::Streams;
  487. //==============================================================================
  488. class WinRTMidiService : public MidiServiceType
  489. {
  490. public:
  491. //==============================================================================
  492. WinRTMidiService()
  493. {
  494. if (! WinRTWrapper::getInstance()->isInitialised())
  495. throw std::runtime_error ("Failed to initialise the WinRT wrapper");
  496. midiInFactory = WinRTWrapper::getInstance()->getWRLFactory<IMidiInPortStatics> (&RuntimeClass_Windows_Devices_Midi_MidiInPort[0]);
  497. if (midiInFactory == nullptr)
  498. throw std::runtime_error ("Failed to create midi in factory");
  499. midiOutFactory = WinRTWrapper::getInstance()->getWRLFactory<IMidiOutPortStatics> (&RuntimeClass_Windows_Devices_Midi_MidiOutPort[0]);
  500. if (midiOutFactory == nullptr)
  501. throw std::runtime_error ("Failed to create midi out factory");
  502. inputDeviceWatcher.reset (new MidiIODeviceWatcher<IMidiInPortStatics> (midiInFactory));
  503. if (! inputDeviceWatcher->start())
  504. throw std::runtime_error ("Failed to start midi input device watcher");
  505. outputDeviceWatcher.reset (new MidiIODeviceWatcher<IMidiOutPortStatics> (midiOutFactory));
  506. if (! outputDeviceWatcher->start())
  507. throw std::runtime_error ("Failed to start midi output device watcher");
  508. }
  509. ~WinRTMidiService() {}
  510. StringArray getDevices (bool isInput) override
  511. {
  512. return isInput ? inputDeviceWatcher ->getDevices()
  513. : outputDeviceWatcher->getDevices();
  514. }
  515. int getDefaultDeviceIndex (bool isInput) override
  516. {
  517. return isInput ? inputDeviceWatcher ->getDefaultDeviceIndex()
  518. : outputDeviceWatcher->getDefaultDeviceIndex();
  519. }
  520. InputWrapper* createInputWrapper (MidiInput& input, int index, MidiInputCallback& callback) override
  521. {
  522. return new WinRTInputWrapper (*this, input, index, callback);
  523. }
  524. OutputWrapper* createOutputWrapper (int index) override
  525. {
  526. return new WinRTOutputWrapper (*this, index);
  527. }
  528. template <typename COMFactoryType>
  529. struct MidiIODeviceWatcher
  530. {
  531. struct DeviceInfo
  532. {
  533. String name, id;
  534. bool isDefault = false;
  535. };
  536. MidiIODeviceWatcher (ComSmartPtr<COMFactoryType>& comFactory) : factory (comFactory)
  537. {
  538. }
  539. ~MidiIODeviceWatcher()
  540. {
  541. stop();
  542. }
  543. bool start()
  544. {
  545. HSTRING deviceSelector;
  546. auto hr = factory->GetDeviceSelector (&deviceSelector);
  547. if (FAILED (hr))
  548. return false;
  549. auto deviceInformationFactory = WinRTWrapper::getInstance()->getWRLFactory<IDeviceInformationStatics> (&RuntimeClass_Windows_Devices_Enumeration_DeviceInformation[0]);
  550. if (deviceInformationFactory == nullptr)
  551. return false;
  552. hr = deviceInformationFactory->CreateWatcherAqsFilter (deviceSelector, watcher.resetAndGetPointerAddress());
  553. if (FAILED (hr))
  554. return false;
  555. struct DeviceEnumerationThread : public Thread
  556. {
  557. DeviceEnumerationThread (String threadName, MidiIODeviceWatcher<COMFactoryType>& p)
  558. : Thread (threadName), parent (p)
  559. {}
  560. void run() override
  561. {
  562. auto parentPtr = &parent;
  563. parent.watcher->add_Added (
  564. Callback<ITypedEventHandler<DeviceWatcher*, DeviceInformation*>> (
  565. [parentPtr](IDeviceWatcher*, IDeviceInformation* info) { return parentPtr->addDevice (info); }
  566. ).Get(),
  567. &parent.deviceAddedToken);
  568. parent.watcher->add_Removed (
  569. Callback<ITypedEventHandler<DeviceWatcher*, DeviceInformationUpdate*>> (
  570. [parentPtr](IDeviceWatcher*, IDeviceInformationUpdate* info) { return parentPtr->removeDevice (info); }
  571. ).Get(),
  572. &parent.deviceRemovedToken);
  573. EventRegistrationToken deviceEnumerationCompletedToken { 0 };
  574. parent.watcher->add_EnumerationCompleted (
  575. Callback<ITypedEventHandler<DeviceWatcher*, IInspectable*>> (
  576. [this](IDeviceWatcher*, IInspectable*) { enumerationCompleted.signal(); return S_OK; }
  577. ).Get(),
  578. &deviceEnumerationCompletedToken);
  579. parent.watcher->Start();
  580. enumerationCompleted.wait();
  581. if (deviceEnumerationCompletedToken.value != 0)
  582. parent.watcher->remove_EnumerationCompleted (deviceEnumerationCompletedToken);
  583. }
  584. MidiIODeviceWatcher<COMFactoryType>& parent;
  585. WaitableEvent enumerationCompleted;
  586. };
  587. DeviceEnumerationThread enumerationThread ("WinRT Device Enumeration Thread", *this);
  588. enumerationThread.startThread();
  589. enumerationThread.waitForThreadToExit (4000);
  590. return true;
  591. }
  592. bool stop()
  593. {
  594. if (watcher == nullptr)
  595. return true;
  596. if (deviceAddedToken.value != 0)
  597. {
  598. auto hr = watcher->remove_Added (deviceAddedToken);
  599. if (FAILED (hr))
  600. return false;
  601. deviceAddedToken.value = 0;
  602. }
  603. if (deviceRemovedToken.value != 0)
  604. {
  605. auto hr = watcher->remove_Removed (deviceRemovedToken);
  606. if (FAILED (hr))
  607. return false;
  608. deviceRemovedToken.value = 0;
  609. }
  610. auto hr = watcher->Stop();
  611. if (FAILED (hr))
  612. return false;
  613. watcher = nullptr;
  614. return true;
  615. }
  616. HRESULT addDevice (IDeviceInformation* addedDeviceInfo)
  617. {
  618. boolean isEnabled;
  619. auto hr = addedDeviceInfo->get_IsEnabled (&isEnabled);
  620. if (FAILED (hr))
  621. return S_OK;
  622. if (! isEnabled)
  623. return S_OK;
  624. const ScopedLock lock (deviceChanges);
  625. DeviceInfo info;
  626. HSTRING name;
  627. hr = addedDeviceInfo->get_Name (&name);
  628. if (FAILED (hr))
  629. return S_OK;
  630. info.name = WinRTWrapper::getInstance()->hStringToString (name);
  631. HSTRING id;
  632. hr = addedDeviceInfo->get_Id (&id);
  633. if (FAILED (hr))
  634. return S_OK;
  635. info.id = WinRTWrapper::getInstance()->hStringToString (id);
  636. boolean isDefault;
  637. hr = addedDeviceInfo->get_IsDefault (&isDefault);
  638. if (FAILED (hr))
  639. return S_OK;
  640. info.isDefault = isDefault != 0;
  641. connectedDevices.add (info);
  642. return S_OK;
  643. }
  644. HRESULT removeDevice (IDeviceInformationUpdate* removedDeviceInfo)
  645. {
  646. const ScopedLock lock (deviceChanges);
  647. HSTRING removedDeviceIdHstr;
  648. removedDeviceInfo->get_Id (&removedDeviceIdHstr);
  649. auto removedDeviceId = WinRTWrapper::getInstance()->hStringToString (removedDeviceIdHstr);
  650. for (int i = 0; i < connectedDevices.size(); ++i)
  651. {
  652. if (connectedDevices[i].id == removedDeviceId)
  653. {
  654. connectedDevices.remove (i);
  655. break;
  656. }
  657. }
  658. return S_OK;
  659. }
  660. StringArray getDevices()
  661. {
  662. {
  663. const ScopedLock lock (deviceChanges);
  664. lastQueriedConnectedDevices = connectedDevices;
  665. }
  666. StringArray result;
  667. for (auto info : lastQueriedConnectedDevices.get())
  668. result.add (info.name);
  669. return result;
  670. }
  671. int getDefaultDeviceIndex()
  672. {
  673. auto& lastDevices = lastQueriedConnectedDevices.get();
  674. for (int i = 0; i < lastDevices.size(); ++i)
  675. if (lastDevices[i].isDefault)
  676. return i;
  677. return 0;
  678. }
  679. String getDeviceNameFromIndex (int index)
  680. {
  681. if (isPositiveAndBelow (index, lastQueriedConnectedDevices.get().size()))
  682. return lastQueriedConnectedDevices.get()[index].name;
  683. return {};
  684. }
  685. String getDeviceID (const String& name)
  686. {
  687. const ScopedLock lock (deviceChanges);
  688. for (auto info : connectedDevices)
  689. if (info.name == name)
  690. return info.id;
  691. return {};
  692. }
  693. ComSmartPtr<COMFactoryType>& factory;
  694. EventRegistrationToken deviceAddedToken { 0 },
  695. deviceRemovedToken { 0 };
  696. ComSmartPtr<IDeviceWatcher> watcher;
  697. Array<DeviceInfo> connectedDevices;
  698. CriticalSection deviceChanges;
  699. ThreadLocalValue<Array<DeviceInfo>> lastQueriedConnectedDevices;
  700. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiIODeviceWatcher);
  701. };
  702. //==============================================================================
  703. template <typename COMFactoryType, typename COMInterfaceType, typename COMType>
  704. struct OpenMidiPortThread : public Thread
  705. {
  706. OpenMidiPortThread (String threadName, String midiDeviceID,
  707. ComSmartPtr<COMFactoryType>& comFactory,
  708. ComSmartPtr<COMInterfaceType>& comPort)
  709. : Thread (threadName),
  710. deviceID (midiDeviceID),
  711. factory (comFactory),
  712. port (comPort)
  713. {
  714. }
  715. ~OpenMidiPortThread()
  716. {
  717. stopThread (2000);
  718. }
  719. void run() override
  720. {
  721. WinRTWrapper::ScopedHString hDeviceId (deviceID);
  722. ComSmartPtr<IAsyncOperation<COMType*>> asyncOp;
  723. auto hr = factory->FromIdAsync (hDeviceId.get(), asyncOp.resetAndGetPointerAddress());
  724. if (FAILED (hr))
  725. return;
  726. hr = asyncOp->put_Completed (Callback<IAsyncOperationCompletedHandler<COMType*>> (
  727. [this] (IAsyncOperation<COMType*>* asyncOpPtr, AsyncStatus)
  728. {
  729. if (asyncOpPtr == nullptr)
  730. return E_ABORT;
  731. auto hr = asyncOpPtr->GetResults (port.resetAndGetPointerAddress());
  732. if (FAILED (hr))
  733. return hr;
  734. portOpened.signal();
  735. return S_OK;
  736. }
  737. ).Get());
  738. // When using Bluetooth the asynchronous port opening operation will occasionally
  739. // hang, so we use a timeout. We will be able to remove this when Microsoft
  740. // improves the Bluetooth MIDI stack.
  741. portOpened.wait (2000);
  742. }
  743. const String deviceID;
  744. ComSmartPtr<COMFactoryType>& factory;
  745. ComSmartPtr<COMInterfaceType>& port;
  746. WaitableEvent portOpened { true };
  747. };
  748. //==============================================================================
  749. struct WinRTInputWrapper : public InputWrapper
  750. {
  751. WinRTInputWrapper (WinRTMidiService& service, MidiInput& input, int index, MidiInputCallback& cb)
  752. : inputDevice (input),
  753. callback (cb)
  754. {
  755. const ScopedLock lock (service.inputDeviceWatcher->deviceChanges);
  756. deviceName = service.inputDeviceWatcher->getDeviceNameFromIndex (index);
  757. if (deviceName.isEmpty())
  758. throw std::runtime_error ("Invalid device index");
  759. auto deviceID = service.inputDeviceWatcher->getDeviceID (deviceName);
  760. if (deviceID.isEmpty())
  761. throw std::runtime_error ("Device unavailable");
  762. OpenMidiPortThread<IMidiInPortStatics, IMidiInPort, MidiInPort> portThread ("Open WinRT MIDI input port",
  763. deviceID,
  764. service.midiInFactory,
  765. midiInPort);
  766. portThread.startThread();
  767. portThread.waitForThreadToExit (-1);
  768. if (midiInPort == nullptr)
  769. throw std::runtime_error ("Timed out waiting for midi input port creation");
  770. startTime = Time::getMillisecondCounterHiRes();
  771. auto hr = midiInPort->add_MessageReceived (
  772. Callback<ITypedEventHandler<MidiInPort*, MidiMessageReceivedEventArgs*>> (
  773. [this] (IMidiInPort*, IMidiMessageReceivedEventArgs* args) { return midiInMessageReceived (args); }
  774. ).Get(),
  775. &midiInMessageToken);
  776. if (FAILED (hr))
  777. throw std::runtime_error ("Failed to set midi input callback");
  778. }
  779. ~WinRTInputWrapper()
  780. {
  781. if (midiInMessageToken.value != 0)
  782. midiInPort->remove_MessageReceived (midiInMessageToken);
  783. midiInPort = nullptr;
  784. }
  785. void start() override
  786. {
  787. if (! isStarted)
  788. {
  789. concatenator.reset();
  790. isStarted = true;
  791. }
  792. }
  793. void stop() override
  794. {
  795. if (isStarted)
  796. {
  797. isStarted = false;
  798. concatenator.reset();
  799. }
  800. }
  801. String getDeviceName() override { return deviceName; }
  802. HRESULT midiInMessageReceived (IMidiMessageReceivedEventArgs* args)
  803. {
  804. if (! isStarted)
  805. return S_OK;
  806. ComSmartPtr<IMidiMessage> message;
  807. auto hr = args->get_Message (message.resetAndGetPointerAddress());
  808. if (FAILED (hr))
  809. return hr;
  810. ComSmartPtr<IBuffer> buffer;
  811. hr = message->get_RawData (buffer.resetAndGetPointerAddress());
  812. if (FAILED (hr))
  813. return hr;
  814. ComSmartPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
  815. hr = buffer->QueryInterface (bufferByteAccess.resetAndGetPointerAddress());
  816. if (FAILED (hr))
  817. return hr;
  818. uint8_t* bufferData = nullptr;
  819. hr = bufferByteAccess->Buffer (&bufferData);
  820. if (FAILED (hr))
  821. return hr;
  822. uint32_t numBytes = 0;
  823. hr = buffer->get_Length (&numBytes);
  824. if (FAILED (hr))
  825. return hr;
  826. ABI::Windows::Foundation::TimeSpan timespan;
  827. hr = message->get_Timestamp (&timespan);
  828. if (FAILED (hr))
  829. return hr;
  830. concatenator.pushMidiData (bufferData, numBytes,
  831. convertTimeStamp (timespan.Duration),
  832. &inputDevice, callback);
  833. return S_OK;
  834. }
  835. double convertTimeStamp (int64 timestamp)
  836. {
  837. auto millisecondsSinceStart = static_cast<double> (timestamp) / 10000.0;
  838. auto t = startTime + millisecondsSinceStart;
  839. auto now = Time::getMillisecondCounterHiRes();
  840. if (t > now)
  841. {
  842. if (t > now + 2.0)
  843. startTime -= 1.0;
  844. t = now;
  845. }
  846. return t * 0.001;
  847. }
  848. MidiInput& inputDevice;
  849. MidiInputCallback& callback;
  850. String deviceName;
  851. MidiDataConcatenator concatenator { 4096 };
  852. ComSmartPtr<IMidiInPort> midiInPort;
  853. EventRegistrationToken midiInMessageToken { 0 };
  854. double startTime = 0;
  855. bool isStarted = false;
  856. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WinRTInputWrapper);
  857. };
  858. //==============================================================================
  859. struct WinRTOutputWrapper : public OutputWrapper
  860. {
  861. WinRTOutputWrapper (WinRTMidiService& service, int index)
  862. {
  863. const ScopedLock lock (service.outputDeviceWatcher->deviceChanges);
  864. deviceName = service.outputDeviceWatcher->getDeviceNameFromIndex (index);
  865. if (deviceName.isEmpty())
  866. throw std::runtime_error ("Invalid device index");
  867. auto deviceID = service.outputDeviceWatcher->getDeviceID (deviceName);
  868. if (deviceID.isEmpty())
  869. throw std::runtime_error ("Device unavailable");
  870. OpenMidiPortThread<IMidiOutPortStatics, IMidiOutPort, IMidiOutPort> portThread ("Open WinRT MIDI output port",
  871. deviceID,
  872. service.midiOutFactory,
  873. midiOutPort);
  874. portThread.startThread();
  875. portThread.waitForThreadToExit (-1);
  876. if (midiOutPort == nullptr)
  877. throw std::runtime_error ("Timed out waiting for midi output port creation");
  878. auto bufferFactory = WinRTWrapper::getInstance()->getWRLFactory<IBufferFactory> (&RuntimeClass_Windows_Storage_Streams_Buffer[0]);
  879. if (bufferFactory == nullptr)
  880. throw std::runtime_error ("Failed to create output buffer factory");
  881. auto hr = bufferFactory->Create (static_cast<UINT32> (65536), buffer.resetAndGetPointerAddress());
  882. if (FAILED (hr))
  883. throw std::runtime_error ("Failed to create output buffer");
  884. hr = buffer->QueryInterface (bufferByteAccess.resetAndGetPointerAddress());
  885. if (FAILED (hr))
  886. throw std::runtime_error ("Failed to get buffer byte access");
  887. hr = bufferByteAccess->Buffer (&bufferData);
  888. if (FAILED (hr))
  889. throw std::runtime_error ("Failed to get buffer data pointer");
  890. }
  891. ~WinRTOutputWrapper() {}
  892. void sendMessageNow (const MidiMessage& message) override
  893. {
  894. auto numBytes = message.getRawDataSize();
  895. auto hr = buffer->put_Length (numBytes);
  896. if (FAILED (hr))
  897. jassertfalse;
  898. memcpy_s (bufferData, numBytes, message.getRawData(), numBytes);
  899. midiOutPort->SendBuffer (buffer);
  900. }
  901. String getDeviceName() override { return deviceName; }
  902. String deviceName;
  903. ComSmartPtr<IMidiOutPort> midiOutPort;
  904. ComSmartPtr<IBuffer> buffer;
  905. ComSmartPtr<Windows::Storage::Streams::IBufferByteAccess> bufferByteAccess;
  906. uint8_t* bufferData = nullptr;
  907. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WinRTOutputWrapper);
  908. };
  909. ComSmartPtr<IMidiInPortStatics> midiInFactory;
  910. ComSmartPtr<IMidiOutPortStatics> midiOutFactory;
  911. std::unique_ptr<MidiIODeviceWatcher<IMidiInPortStatics>> inputDeviceWatcher;
  912. std::unique_ptr<MidiIODeviceWatcher<IMidiOutPortStatics>> outputDeviceWatcher;
  913. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WinRTMidiService)
  914. };
  915. #endif // JUCE_USE_WINRT_MIDI
  916. //==============================================================================
  917. struct MidiService : public DeletedAtShutdown
  918. {
  919. MidiService()
  920. {
  921. #if JUCE_USE_WINRT_MIDI
  922. try
  923. {
  924. internal.reset (new WinRTMidiService());
  925. return;
  926. }
  927. catch (std::runtime_error&) {}
  928. #endif
  929. internal.reset (new Win32MidiService());
  930. }
  931. ~MidiService()
  932. {
  933. clearSingletonInstance();
  934. }
  935. static MidiServiceType& getService()
  936. {
  937. jassert (getInstance()->internal != nullptr);
  938. return *getInstance()->internal.get();
  939. }
  940. JUCE_DECLARE_SINGLETON (MidiService, false)
  941. private:
  942. std::unique_ptr<MidiServiceType> internal;
  943. };
  944. JUCE_IMPLEMENT_SINGLETON (MidiService)
  945. //==============================================================================
  946. StringArray MidiInput::getDevices()
  947. {
  948. return MidiService::getService().getDevices (true);
  949. }
  950. int MidiInput::getDefaultDeviceIndex()
  951. {
  952. return MidiService::getService().getDefaultDeviceIndex (true);
  953. }
  954. MidiInput::MidiInput (const String& deviceName) : name (deviceName)
  955. {
  956. }
  957. MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
  958. {
  959. if (callback == nullptr)
  960. return nullptr;
  961. std::unique_ptr<MidiInput> in (new MidiInput (String()));
  962. std::unique_ptr<MidiServiceType::InputWrapper> wrapper;
  963. try
  964. {
  965. wrapper.reset (MidiService::getService().createInputWrapper (*in, index, *callback));
  966. }
  967. catch (std::runtime_error&)
  968. {
  969. return nullptr;
  970. }
  971. in->setName (wrapper->getDeviceName());
  972. in->internal = wrapper.release();
  973. return in.release();
  974. }
  975. MidiInput::~MidiInput()
  976. {
  977. delete static_cast<MidiServiceType::InputWrapper*> (internal);
  978. }
  979. void MidiInput::start() { static_cast<MidiServiceType::InputWrapper*> (internal)->start(); }
  980. void MidiInput::stop() { static_cast<MidiServiceType::InputWrapper*> (internal)->stop(); }
  981. //==============================================================================
  982. StringArray MidiOutput::getDevices()
  983. {
  984. return MidiService::getService().getDevices (false);
  985. }
  986. int MidiOutput::getDefaultDeviceIndex()
  987. {
  988. return MidiService::getService().getDefaultDeviceIndex (false);
  989. }
  990. MidiOutput* MidiOutput::openDevice (int index)
  991. {
  992. std::unique_ptr<MidiServiceType::OutputWrapper> wrapper;
  993. try
  994. {
  995. wrapper.reset (MidiService::getService().createOutputWrapper (index));
  996. }
  997. catch (std::runtime_error&)
  998. {
  999. return nullptr;
  1000. }
  1001. std::unique_ptr<MidiOutput> out (new MidiOutput (wrapper->getDeviceName()));
  1002. out->internal = wrapper.release();
  1003. return out.release();
  1004. }
  1005. MidiOutput::~MidiOutput()
  1006. {
  1007. stopBackgroundThread();
  1008. delete static_cast<MidiServiceType::OutputWrapper*> (internal);
  1009. }
  1010. void MidiOutput::sendMessageNow (const MidiMessage& message)
  1011. {
  1012. static_cast<MidiServiceType::OutputWrapper*> (internal)->sendMessageNow (message);
  1013. }
  1014. } // namespace juce