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.

1241 lines
40KB

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