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.

496 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. class MidiInCollector
  19. {
  20. public:
  21. //==============================================================================
  22. MidiInCollector (MidiInput* const input_,
  23. MidiInputCallback& callback_)
  24. : deviceHandle (0),
  25. input (input_),
  26. callback (callback_),
  27. concatenator (4096),
  28. isStarted (false),
  29. startTime (0)
  30. {
  31. }
  32. ~MidiInCollector()
  33. {
  34. stop();
  35. if (deviceHandle != 0)
  36. {
  37. int count = 5;
  38. while (--count >= 0)
  39. {
  40. if (midiInClose (deviceHandle) == MMSYSERR_NOERROR)
  41. break;
  42. Sleep (20);
  43. }
  44. }
  45. }
  46. //==============================================================================
  47. void handleMessage (const uint32 message, const uint32 timeStamp)
  48. {
  49. if ((message & 0xff) >= 0x80 && isStarted)
  50. {
  51. concatenator.pushMidiData (&message, 3, convertTimeStamp (timeStamp), input, callback);
  52. writeFinishedBlocks();
  53. }
  54. }
  55. void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp)
  56. {
  57. if (isStarted && hdr->dwBytesRecorded > 0)
  58. {
  59. concatenator.pushMidiData (hdr->lpData, (int) hdr->dwBytesRecorded, convertTimeStamp (timeStamp), input, callback);
  60. writeFinishedBlocks();
  61. }
  62. }
  63. void start()
  64. {
  65. jassert (deviceHandle != 0);
  66. if (deviceHandle != 0 && ! isStarted)
  67. {
  68. activeMidiCollectors.addIfNotAlreadyThere (this);
  69. for (int i = 0; i < (int) numHeaders; ++i)
  70. headers[i].write (deviceHandle);
  71. startTime = Time::getMillisecondCounter();
  72. MMRESULT res = midiInStart (deviceHandle);
  73. if (res == MMSYSERR_NOERROR)
  74. {
  75. concatenator.reset();
  76. isStarted = true;
  77. }
  78. else
  79. {
  80. unprepareAllHeaders();
  81. }
  82. }
  83. }
  84. void stop()
  85. {
  86. if (isStarted)
  87. {
  88. isStarted = false;
  89. midiInReset (deviceHandle);
  90. midiInStop (deviceHandle);
  91. activeMidiCollectors.removeValue (this);
  92. unprepareAllHeaders();
  93. concatenator.reset();
  94. }
  95. }
  96. static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR midiMessage, DWORD_PTR timeStamp)
  97. {
  98. MidiInCollector* const collector = reinterpret_cast <MidiInCollector*> (dwInstance);
  99. if (activeMidiCollectors.contains (collector))
  100. {
  101. if (uMsg == MIM_DATA)
  102. collector->handleMessage ((uint32) midiMessage, (uint32) timeStamp);
  103. else if (uMsg == MIM_LONGDATA)
  104. collector->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp);
  105. }
  106. }
  107. HMIDIIN deviceHandle;
  108. private:
  109. static Array <MidiInCollector*, CriticalSection> activeMidiCollectors;
  110. MidiInput* input;
  111. MidiInputCallback& callback;
  112. MidiDataConcatenator concatenator;
  113. bool volatile isStarted;
  114. uint32 startTime;
  115. class MidiHeader
  116. {
  117. public:
  118. MidiHeader()
  119. {
  120. zerostruct (hdr);
  121. hdr.lpData = data;
  122. hdr.dwBufferLength = (DWORD) numElementsInArray (data);
  123. }
  124. void write (HMIDIIN deviceHandle)
  125. {
  126. hdr.dwBytesRecorded = 0;
  127. MMRESULT res = midiInPrepareHeader (deviceHandle, &hdr, sizeof (hdr));
  128. res = midiInAddBuffer (deviceHandle, &hdr, sizeof (hdr));
  129. }
  130. void writeIfFinished (HMIDIIN deviceHandle)
  131. {
  132. if ((hdr.dwFlags & WHDR_DONE) != 0)
  133. {
  134. MMRESULT res = midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr));
  135. (void) res;
  136. write (deviceHandle);
  137. }
  138. }
  139. void unprepare (HMIDIIN deviceHandle)
  140. {
  141. if ((hdr.dwFlags & WHDR_DONE) != 0)
  142. {
  143. int c = 10;
  144. while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr)) == MIDIERR_STILLPLAYING)
  145. Thread::sleep (20);
  146. jassert (c >= 0);
  147. }
  148. }
  149. private:
  150. MIDIHDR hdr;
  151. char data [256];
  152. JUCE_DECLARE_NON_COPYABLE (MidiHeader);
  153. };
  154. enum { numHeaders = 32 };
  155. MidiHeader headers [numHeaders];
  156. void writeFinishedBlocks()
  157. {
  158. for (int i = 0; i < (int) numHeaders; ++i)
  159. headers[i].writeIfFinished (deviceHandle);
  160. }
  161. void unprepareAllHeaders()
  162. {
  163. for (int i = 0; i < (int) numHeaders; ++i)
  164. headers[i].unprepare (deviceHandle);
  165. }
  166. double convertTimeStamp (uint32 timeStamp)
  167. {
  168. timeStamp += startTime;
  169. const uint32 now = Time::getMillisecondCounter();
  170. if (timeStamp > now)
  171. {
  172. if (timeStamp > now + 2)
  173. --startTime;
  174. timeStamp = now;
  175. }
  176. return timeStamp * 0.001;
  177. }
  178. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInCollector);
  179. };
  180. Array <MidiInCollector*, CriticalSection> MidiInCollector::activeMidiCollectors;
  181. //==============================================================================
  182. StringArray MidiInput::getDevices()
  183. {
  184. StringArray s;
  185. const UINT num = midiInGetNumDevs();
  186. for (UINT i = 0; i < num; ++i)
  187. {
  188. MIDIINCAPS mc = { 0 };
  189. if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  190. s.add (String (mc.szPname, sizeof (mc.szPname)));
  191. }
  192. return s;
  193. }
  194. int MidiInput::getDefaultDeviceIndex()
  195. {
  196. return 0;
  197. }
  198. MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const callback)
  199. {
  200. if (callback == nullptr)
  201. return nullptr;
  202. UINT deviceId = MIDI_MAPPER;
  203. int n = 0;
  204. String name;
  205. const UINT num = midiInGetNumDevs();
  206. for (UINT i = 0; i < num; ++i)
  207. {
  208. MIDIINCAPS mc = { 0 };
  209. if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  210. {
  211. if (index == n)
  212. {
  213. deviceId = i;
  214. name = String (mc.szPname, (size_t) numElementsInArray (mc.szPname));
  215. break;
  216. }
  217. ++n;
  218. }
  219. }
  220. ScopedPointer <MidiInput> in (new MidiInput (name));
  221. ScopedPointer <MidiInCollector> collector (new MidiInCollector (in, *callback));
  222. HMIDIIN h;
  223. MMRESULT err = midiInOpen (&h, deviceId,
  224. (DWORD_PTR) &MidiInCollector::midiInCallback,
  225. (DWORD_PTR) (MidiInCollector*) collector,
  226. CALLBACK_FUNCTION);
  227. if (err == MMSYSERR_NOERROR)
  228. {
  229. collector->deviceHandle = h;
  230. in->internal = collector.release();
  231. return in.release();
  232. }
  233. return nullptr;
  234. }
  235. MidiInput::MidiInput (const String& name_)
  236. : name (name_),
  237. internal (0)
  238. {
  239. }
  240. MidiInput::~MidiInput()
  241. {
  242. delete static_cast <MidiInCollector*> (internal);
  243. }
  244. void MidiInput::start()
  245. {
  246. static_cast <MidiInCollector*> (internal)->start();
  247. }
  248. void MidiInput::stop()
  249. {
  250. static_cast <MidiInCollector*> (internal)->stop();
  251. }
  252. //==============================================================================
  253. struct MidiOutHandle
  254. {
  255. int refCount;
  256. UINT deviceId;
  257. HMIDIOUT handle;
  258. static Array<MidiOutHandle*> activeHandles;
  259. private:
  260. JUCE_LEAK_DETECTOR (MidiOutHandle);
  261. };
  262. Array<MidiOutHandle*> MidiOutHandle::activeHandles;
  263. //==============================================================================
  264. StringArray MidiOutput::getDevices()
  265. {
  266. StringArray s;
  267. const UINT num = midiOutGetNumDevs();
  268. for (UINT i = 0; i < num; ++i)
  269. {
  270. MIDIOUTCAPS mc = { 0 };
  271. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  272. s.add (String (mc.szPname, sizeof (mc.szPname)));
  273. }
  274. return s;
  275. }
  276. int MidiOutput::getDefaultDeviceIndex()
  277. {
  278. const UINT num = midiOutGetNumDevs();
  279. int n = 0;
  280. for (UINT i = 0; i < num; ++i)
  281. {
  282. MIDIOUTCAPS mc = { 0 };
  283. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  284. {
  285. if ((mc.wTechnology & MOD_MAPPER) != 0)
  286. return n;
  287. ++n;
  288. }
  289. }
  290. return 0;
  291. }
  292. MidiOutput* MidiOutput::openDevice (int index)
  293. {
  294. UINT deviceId = MIDI_MAPPER;
  295. const UINT num = midiOutGetNumDevs();
  296. int n = 0;
  297. {
  298. for (UINT i = 0; i < num; ++i)
  299. {
  300. MIDIOUTCAPS mc = { 0 };
  301. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  302. {
  303. // use the microsoft sw synth as a default - best not to allow deviceId
  304. // to be MIDI_MAPPER, or else device sharing breaks
  305. if (String (mc.szPname, sizeof (mc.szPname)).containsIgnoreCase ("microsoft"))
  306. deviceId = i;
  307. if (index == n)
  308. {
  309. deviceId = i;
  310. break;
  311. }
  312. ++n;
  313. }
  314. }
  315. }
  316. int i;
  317. for (i = MidiOutHandle::activeHandles.size(); --i >= 0;)
  318. {
  319. MidiOutHandle* const han = MidiOutHandle::activeHandles.getUnchecked(i);
  320. if (han->deviceId == deviceId)
  321. {
  322. han->refCount++;
  323. MidiOutput* const out = new MidiOutput();
  324. out->internal = han;
  325. return out;
  326. }
  327. }
  328. for (i = 4; --i >= 0;)
  329. {
  330. HMIDIOUT h = 0;
  331. MMRESULT res = midiOutOpen (&h, deviceId, 0, 0, CALLBACK_NULL);
  332. if (res == MMSYSERR_NOERROR)
  333. {
  334. MidiOutHandle* const han = new MidiOutHandle();
  335. han->deviceId = deviceId;
  336. han->refCount = 1;
  337. han->handle = h;
  338. MidiOutHandle::activeHandles.add (han);
  339. MidiOutput* const out = new MidiOutput();
  340. out->internal = han;
  341. return out;
  342. }
  343. else if (res == MMSYSERR_ALLOCATED)
  344. {
  345. Sleep (100);
  346. }
  347. else
  348. {
  349. break;
  350. }
  351. }
  352. return nullptr;
  353. }
  354. MidiOutput::~MidiOutput()
  355. {
  356. stopBackgroundThread();
  357. MidiOutHandle* const h = static_cast <MidiOutHandle*> (internal);
  358. if (MidiOutHandle::activeHandles.contains (h) && --(h->refCount) == 0)
  359. {
  360. midiOutClose (h->handle);
  361. MidiOutHandle::activeHandles.removeValue (h);
  362. delete h;
  363. }
  364. }
  365. void MidiOutput::sendMessageNow (const MidiMessage& message)
  366. {
  367. const MidiOutHandle* const handle = static_cast <const MidiOutHandle*> (internal);
  368. if (message.getRawDataSize() > 3
  369. || message.isSysEx())
  370. {
  371. MIDIHDR h = { 0 };
  372. h.lpData = (char*) message.getRawData();
  373. h.dwBufferLength = (DWORD) message.getRawDataSize();
  374. h.dwBytesRecorded = (DWORD) message.getRawDataSize();
  375. if (midiOutPrepareHeader (handle->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR)
  376. {
  377. MMRESULT res = midiOutLongMsg (handle->handle, &h, sizeof (MIDIHDR));
  378. if (res == MMSYSERR_NOERROR)
  379. {
  380. while ((h.dwFlags & MHDR_DONE) == 0)
  381. Sleep (1);
  382. int count = 500; // 1 sec timeout
  383. while (--count >= 0)
  384. {
  385. res = midiOutUnprepareHeader (handle->handle, &h, sizeof (MIDIHDR));
  386. if (res == MIDIERR_STILLPLAYING)
  387. Sleep (2);
  388. else
  389. break;
  390. }
  391. }
  392. }
  393. }
  394. else
  395. {
  396. midiOutShortMsg (handle->handle,
  397. *(unsigned int*) message.getRawData());
  398. }
  399. }