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.

1235 lines
40KB

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