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.

484 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. (void) res;
  130. }
  131. void writeIfFinished (HMIDIIN deviceHandle)
  132. {
  133. if ((hdr.dwFlags & WHDR_DONE) != 0)
  134. {
  135. MMRESULT res = midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr));
  136. (void) res;
  137. write (deviceHandle);
  138. }
  139. }
  140. void unprepare (HMIDIIN deviceHandle)
  141. {
  142. if ((hdr.dwFlags & WHDR_DONE) != 0)
  143. {
  144. int c = 10;
  145. while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr)) == MIDIERR_STILLPLAYING)
  146. Thread::sleep (20);
  147. jassert (c >= 0);
  148. }
  149. }
  150. private:
  151. MIDIHDR hdr;
  152. char data [256];
  153. JUCE_DECLARE_NON_COPYABLE (MidiHeader)
  154. };
  155. enum { numHeaders = 32 };
  156. MidiHeader headers [numHeaders];
  157. void writeFinishedBlocks()
  158. {
  159. for (int i = 0; i < (int) numHeaders; ++i)
  160. headers[i].writeIfFinished (deviceHandle);
  161. }
  162. void unprepareAllHeaders()
  163. {
  164. for (int i = 0; i < (int) numHeaders; ++i)
  165. headers[i].unprepare (deviceHandle);
  166. }
  167. double convertTimeStamp (uint32 timeStamp)
  168. {
  169. double t = startTime + timeStamp;
  170. const double now = Time::getMillisecondCounterHiRes();
  171. if (t > now)
  172. {
  173. if (t > now + 2.0)
  174. startTime -= 1.0;
  175. t = now;
  176. }
  177. return t * 0.001;
  178. }
  179. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInCollector)
  180. };
  181. Array <MidiInCollector*, CriticalSection> MidiInCollector::activeMidiCollectors;
  182. //==============================================================================
  183. StringArray MidiInput::getDevices()
  184. {
  185. StringArray s;
  186. const UINT num = midiInGetNumDevs();
  187. for (UINT i = 0; i < num; ++i)
  188. {
  189. MIDIINCAPS mc = { 0 };
  190. if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  191. s.add (String (mc.szPname, sizeof (mc.szPname)));
  192. }
  193. return s;
  194. }
  195. int MidiInput::getDefaultDeviceIndex()
  196. {
  197. return 0;
  198. }
  199. MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const callback)
  200. {
  201. if (callback == nullptr)
  202. return nullptr;
  203. UINT deviceId = MIDI_MAPPER;
  204. int n = 0;
  205. String name;
  206. const UINT num = midiInGetNumDevs();
  207. for (UINT i = 0; i < num; ++i)
  208. {
  209. MIDIINCAPS mc = { 0 };
  210. if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  211. {
  212. if (index == n)
  213. {
  214. deviceId = i;
  215. name = String (mc.szPname, (size_t) numElementsInArray (mc.szPname));
  216. break;
  217. }
  218. ++n;
  219. }
  220. }
  221. ScopedPointer <MidiInput> in (new MidiInput (name));
  222. ScopedPointer <MidiInCollector> collector (new MidiInCollector (in, *callback));
  223. HMIDIIN h;
  224. MMRESULT err = midiInOpen (&h, deviceId,
  225. (DWORD_PTR) &MidiInCollector::midiInCallback,
  226. (DWORD_PTR) (MidiInCollector*) collector,
  227. CALLBACK_FUNCTION);
  228. if (err == MMSYSERR_NOERROR)
  229. {
  230. collector->deviceHandle = h;
  231. in->internal = collector.release();
  232. return in.release();
  233. }
  234. return nullptr;
  235. }
  236. MidiInput::MidiInput (const String& name_)
  237. : name (name_),
  238. internal (0)
  239. {
  240. }
  241. MidiInput::~MidiInput()
  242. {
  243. delete static_cast <MidiInCollector*> (internal);
  244. }
  245. void MidiInput::start() { static_cast <MidiInCollector*> (internal)->start(); }
  246. void MidiInput::stop() { static_cast <MidiInCollector*> (internal)->stop(); }
  247. //==============================================================================
  248. struct MidiOutHandle
  249. {
  250. int refCount;
  251. UINT deviceId;
  252. HMIDIOUT handle;
  253. static Array<MidiOutHandle*> activeHandles;
  254. private:
  255. JUCE_LEAK_DETECTOR (MidiOutHandle)
  256. };
  257. Array<MidiOutHandle*> MidiOutHandle::activeHandles;
  258. //==============================================================================
  259. StringArray MidiOutput::getDevices()
  260. {
  261. StringArray s;
  262. const UINT num = midiOutGetNumDevs();
  263. for (UINT i = 0; i < num; ++i)
  264. {
  265. MIDIOUTCAPS mc = { 0 };
  266. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  267. s.add (String (mc.szPname, sizeof (mc.szPname)));
  268. }
  269. return s;
  270. }
  271. int MidiOutput::getDefaultDeviceIndex()
  272. {
  273. const UINT num = midiOutGetNumDevs();
  274. int n = 0;
  275. for (UINT i = 0; i < num; ++i)
  276. {
  277. MIDIOUTCAPS mc = { 0 };
  278. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  279. {
  280. if ((mc.wTechnology & MOD_MAPPER) != 0)
  281. return n;
  282. ++n;
  283. }
  284. }
  285. return 0;
  286. }
  287. MidiOutput* MidiOutput::openDevice (int index)
  288. {
  289. UINT deviceId = MIDI_MAPPER;
  290. const UINT num = midiOutGetNumDevs();
  291. int n = 0;
  292. for (UINT i = 0; i < num; ++i)
  293. {
  294. MIDIOUTCAPS mc = { 0 };
  295. if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR)
  296. {
  297. // use the microsoft sw synth as a default - best not to allow deviceId
  298. // to be MIDI_MAPPER, or else device sharing breaks
  299. if (String (mc.szPname, sizeof (mc.szPname)).containsIgnoreCase ("microsoft"))
  300. deviceId = i;
  301. if (index == n)
  302. {
  303. deviceId = i;
  304. break;
  305. }
  306. ++n;
  307. }
  308. }
  309. for (int i = MidiOutHandle::activeHandles.size(); --i >= 0;)
  310. {
  311. MidiOutHandle* const han = MidiOutHandle::activeHandles.getUnchecked(i);
  312. if (han->deviceId == deviceId)
  313. {
  314. han->refCount++;
  315. MidiOutput* const out = new MidiOutput();
  316. out->internal = han;
  317. return out;
  318. }
  319. }
  320. for (int i = 4; --i >= 0;)
  321. {
  322. HMIDIOUT h = 0;
  323. MMRESULT res = midiOutOpen (&h, deviceId, 0, 0, CALLBACK_NULL);
  324. if (res == MMSYSERR_NOERROR)
  325. {
  326. MidiOutHandle* const han = new MidiOutHandle();
  327. han->deviceId = deviceId;
  328. han->refCount = 1;
  329. han->handle = h;
  330. MidiOutHandle::activeHandles.add (han);
  331. MidiOutput* const out = new MidiOutput();
  332. out->internal = han;
  333. return out;
  334. }
  335. else if (res == MMSYSERR_ALLOCATED)
  336. {
  337. Sleep (100);
  338. }
  339. else
  340. {
  341. break;
  342. }
  343. }
  344. return nullptr;
  345. }
  346. MidiOutput::~MidiOutput()
  347. {
  348. stopBackgroundThread();
  349. MidiOutHandle* const h = static_cast <MidiOutHandle*> (internal);
  350. if (MidiOutHandle::activeHandles.contains (h) && --(h->refCount) == 0)
  351. {
  352. midiOutClose (h->handle);
  353. MidiOutHandle::activeHandles.removeFirstMatchingValue (h);
  354. delete h;
  355. }
  356. }
  357. void MidiOutput::sendMessageNow (const MidiMessage& message)
  358. {
  359. const MidiOutHandle* const handle = static_cast <const MidiOutHandle*> (internal);
  360. if (message.getRawDataSize() > 3 || message.isSysEx())
  361. {
  362. MIDIHDR h = { 0 };
  363. h.lpData = (char*) message.getRawData();
  364. h.dwBytesRecorded = h.dwBufferLength = (DWORD) message.getRawDataSize();
  365. if (midiOutPrepareHeader (handle->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR)
  366. {
  367. MMRESULT res = midiOutLongMsg (handle->handle, &h, sizeof (MIDIHDR));
  368. if (res == MMSYSERR_NOERROR)
  369. {
  370. while ((h.dwFlags & MHDR_DONE) == 0)
  371. Sleep (1);
  372. int count = 500; // 1 sec timeout
  373. while (--count >= 0)
  374. {
  375. res = midiOutUnprepareHeader (handle->handle, &h, sizeof (MIDIHDR));
  376. if (res == MIDIERR_STILLPLAYING)
  377. Sleep (2);
  378. else
  379. break;
  380. }
  381. }
  382. }
  383. }
  384. else
  385. {
  386. midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData());
  387. }
  388. }