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.

483 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. MidiInCollector (MidiInput* const input_,
  22. MidiInputCallback& callback_)
  23. : deviceHandle (0),
  24. input (input_),
  25. callback (callback_),
  26. concatenator (4096),
  27. isStarted (false),
  28. startTime (0)
  29. {
  30. }
  31. ~MidiInCollector()
  32. {
  33. stop();
  34. if (deviceHandle != 0)
  35. {
  36. for (int count = 5; --count >= 0;)
  37. {
  38. if (midiInClose (deviceHandle) == MMSYSERR_NOERROR)
  39. break;
  40. Sleep (20);
  41. }
  42. }
  43. }
  44. //==============================================================================
  45. void handleMessage (const uint8* bytes, const uint32 timeStamp)
  46. {
  47. if (bytes[0] >= 0x80 && isStarted)
  48. {
  49. concatenator.pushMidiData (bytes, MidiMessage::getMessageLengthFromFirstByte (bytes[0]),
  50. convertTimeStamp (timeStamp), input, callback);
  51. writeFinishedBlocks();
  52. }
  53. }
  54. void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp)
  55. {
  56. if (isStarted && hdr->dwBytesRecorded > 0)
  57. {
  58. concatenator.pushMidiData (hdr->lpData, (int) hdr->dwBytesRecorded,
  59. convertTimeStamp (timeStamp), input, callback);
  60. writeFinishedBlocks();
  61. }
  62. }
  63. void start()
  64. {
  65. if (deviceHandle != 0 && ! isStarted)
  66. {
  67. activeMidiCollectors.addIfNotAlreadyThere (this);
  68. for (int i = 0; i < (int) numHeaders; ++i)
  69. headers[i].write (deviceHandle);
  70. startTime = Time::getMillisecondCounterHiRes();
  71. MMRESULT res = midiInStart (deviceHandle);
  72. if (res == MMSYSERR_NOERROR)
  73. {
  74. concatenator.reset();
  75. isStarted = true;
  76. }
  77. else
  78. {
  79. unprepareAllHeaders();
  80. }
  81. }
  82. }
  83. void stop()
  84. {
  85. if (isStarted)
  86. {
  87. isStarted = false;
  88. midiInReset (deviceHandle);
  89. midiInStop (deviceHandle);
  90. activeMidiCollectors.removeFirstMatchingValue (this);
  91. unprepareAllHeaders();
  92. concatenator.reset();
  93. }
  94. }
  95. static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance,
  96. 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 ((const uint8*) &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. double 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. double t = startTime + timeStamp;
  169. const double now = Time::getMillisecondCounterHiRes();
  170. if (t > now)
  171. {
  172. if (t > now + 2.0)
  173. startTime -= 1.0;
  174. t = now;
  175. }
  176. return t * 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() { static_cast <MidiInCollector*> (internal)->start(); }
  245. void MidiInput::stop() { static_cast <MidiInCollector*> (internal)->stop(); }
  246. //==============================================================================
  247. struct MidiOutHandle
  248. {
  249. int refCount;
  250. UINT deviceId;
  251. HMIDIOUT handle;
  252. static Array<MidiOutHandle*> activeHandles;
  253. private:
  254. JUCE_LEAK_DETECTOR (MidiOutHandle);
  255. };
  256. Array<MidiOutHandle*> MidiOutHandle::activeHandles;
  257. //==============================================================================
  258. StringArray MidiOutput::getDevices()
  259. {
  260. StringArray s;
  261. const UINT num = midiOutGetNumDevs();
  262. for (UINT i = 0; i < num; ++i)
  263. {
  264. MIDIOUTCAPS mc = { 0 };
  265. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  266. s.add (String (mc.szPname, sizeof (mc.szPname)));
  267. }
  268. return s;
  269. }
  270. int MidiOutput::getDefaultDeviceIndex()
  271. {
  272. const UINT num = midiOutGetNumDevs();
  273. int n = 0;
  274. for (UINT i = 0; i < num; ++i)
  275. {
  276. MIDIOUTCAPS mc = { 0 };
  277. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  278. {
  279. if ((mc.wTechnology & MOD_MAPPER) != 0)
  280. return n;
  281. ++n;
  282. }
  283. }
  284. return 0;
  285. }
  286. MidiOutput* MidiOutput::openDevice (int index)
  287. {
  288. UINT deviceId = MIDI_MAPPER;
  289. const UINT num = midiOutGetNumDevs();
  290. int n = 0;
  291. for (UINT i = 0; i < num; ++i)
  292. {
  293. MIDIOUTCAPS mc = { 0 };
  294. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  295. {
  296. // use the microsoft sw synth as a default - best not to allow deviceId
  297. // to be MIDI_MAPPER, or else device sharing breaks
  298. if (String (mc.szPname, sizeof (mc.szPname)).containsIgnoreCase ("microsoft"))
  299. deviceId = i;
  300. if (index == n)
  301. {
  302. deviceId = i;
  303. break;
  304. }
  305. ++n;
  306. }
  307. }
  308. for (int i = MidiOutHandle::activeHandles.size(); --i >= 0;)
  309. {
  310. MidiOutHandle* const han = MidiOutHandle::activeHandles.getUnchecked(i);
  311. if (han->deviceId == deviceId)
  312. {
  313. han->refCount++;
  314. MidiOutput* const out = new MidiOutput();
  315. out->internal = han;
  316. return out;
  317. }
  318. }
  319. for (int i = 4; --i >= 0;)
  320. {
  321. HMIDIOUT h = 0;
  322. MMRESULT res = midiOutOpen (&h, deviceId, 0, 0, CALLBACK_NULL);
  323. if (res == MMSYSERR_NOERROR)
  324. {
  325. MidiOutHandle* const han = new MidiOutHandle();
  326. han->deviceId = deviceId;
  327. han->refCount = 1;
  328. han->handle = h;
  329. MidiOutHandle::activeHandles.add (han);
  330. MidiOutput* const out = new MidiOutput();
  331. out->internal = han;
  332. return out;
  333. }
  334. else if (res == MMSYSERR_ALLOCATED)
  335. {
  336. Sleep (100);
  337. }
  338. else
  339. {
  340. break;
  341. }
  342. }
  343. return nullptr;
  344. }
  345. MidiOutput::~MidiOutput()
  346. {
  347. stopBackgroundThread();
  348. MidiOutHandle* const h = static_cast <MidiOutHandle*> (internal);
  349. if (MidiOutHandle::activeHandles.contains (h) && --(h->refCount) == 0)
  350. {
  351. midiOutClose (h->handle);
  352. MidiOutHandle::activeHandles.removeFirstMatchingValue (h);
  353. delete h;
  354. }
  355. }
  356. void MidiOutput::sendMessageNow (const MidiMessage& message)
  357. {
  358. const MidiOutHandle* const handle = static_cast <const MidiOutHandle*> (internal);
  359. if (message.getRawDataSize() > 3 || message.isSysEx())
  360. {
  361. MIDIHDR h = { 0 };
  362. h.lpData = (char*) message.getRawData();
  363. h.dwBytesRecorded = h.dwBufferLength = (DWORD) message.getRawDataSize();
  364. if (midiOutPrepareHeader (handle->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR)
  365. {
  366. MMRESULT res = midiOutLongMsg (handle->handle, &h, sizeof (MIDIHDR));
  367. if (res == MMSYSERR_NOERROR)
  368. {
  369. while ((h.dwFlags & MHDR_DONE) == 0)
  370. Sleep (1);
  371. int count = 500; // 1 sec timeout
  372. while (--count >= 0)
  373. {
  374. res = midiOutUnprepareHeader (handle->handle, &h, sizeof (MIDIHDR));
  375. if (res == MIDIERR_STILLPLAYING)
  376. Sleep (2);
  377. else
  378. break;
  379. }
  380. }
  381. }
  382. }
  383. else
  384. {
  385. midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData());
  386. }
  387. }