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.

469 lines
16KB

  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. #if JUCE_ALSA
  19. // You can define these strings in your app if you want to override the default names:
  20. #ifndef JUCE_ALSA_MIDI_INPUT_NAME
  21. #define JUCE_ALSA_MIDI_INPUT_NAME "Juce Midi Input"
  22. #endif
  23. #ifndef JUCE_ALSA_MIDI_OUTPUT_NAME
  24. #define JUCE_ALSA_MIDI_OUTPUT_NAME "Juce Midi Output"
  25. #endif
  26. //==============================================================================
  27. namespace
  28. {
  29. snd_seq_t* iterateMidiClient (snd_seq_t* seqHandle,
  30. snd_seq_client_info_t* clientInfo,
  31. const bool forInput,
  32. StringArray& deviceNamesFound,
  33. const int deviceIndexToOpen)
  34. {
  35. snd_seq_t* returnedHandle = nullptr;
  36. snd_seq_port_info_t* portInfo;
  37. if (snd_seq_port_info_malloc (&portInfo) == 0)
  38. {
  39. int numPorts = snd_seq_client_info_get_num_ports (clientInfo);
  40. const int client = snd_seq_client_info_get_client (clientInfo);
  41. snd_seq_port_info_set_client (portInfo, client);
  42. snd_seq_port_info_set_port (portInfo, -1);
  43. while (--numPorts >= 0)
  44. {
  45. if (snd_seq_query_next_port (seqHandle, portInfo) == 0
  46. && (snd_seq_port_info_get_capability (portInfo)
  47. & (forInput ? SND_SEQ_PORT_CAP_READ
  48. : SND_SEQ_PORT_CAP_WRITE)) != 0)
  49. {
  50. deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo));
  51. if (deviceNamesFound.size() == deviceIndexToOpen + 1)
  52. {
  53. const int sourcePort = snd_seq_port_info_get_port (portInfo);
  54. const int sourceClient = snd_seq_client_info_get_client (clientInfo);
  55. if (sourcePort != -1)
  56. {
  57. if (forInput)
  58. {
  59. snd_seq_set_client_name (seqHandle, JUCE_ALSA_MIDI_INPUT_NAME);
  60. const int portId = snd_seq_create_simple_port (seqHandle, "Juce Midi In Port",
  61. SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
  62. SND_SEQ_PORT_TYPE_MIDI_GENERIC);
  63. snd_seq_connect_from (seqHandle, portId, sourceClient, sourcePort);
  64. }
  65. else
  66. {
  67. snd_seq_set_client_name (seqHandle, JUCE_ALSA_MIDI_OUTPUT_NAME);
  68. const int portId = snd_seq_create_simple_port (seqHandle, "Juce Midi Out Port",
  69. SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ,
  70. SND_SEQ_PORT_TYPE_MIDI_GENERIC);
  71. snd_seq_connect_to (seqHandle, portId, sourceClient, sourcePort);
  72. }
  73. returnedHandle = seqHandle;
  74. }
  75. }
  76. }
  77. }
  78. snd_seq_port_info_free (portInfo);
  79. }
  80. return returnedHandle;
  81. }
  82. snd_seq_t* iterateMidiDevices (const bool forInput,
  83. StringArray& deviceNamesFound,
  84. const int deviceIndexToOpen)
  85. {
  86. snd_seq_t* returnedHandle = nullptr;
  87. snd_seq_t* seqHandle = nullptr;
  88. if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT
  89. : SND_SEQ_OPEN_OUTPUT, 0) == 0)
  90. {
  91. snd_seq_system_info_t* systemInfo = nullptr;
  92. snd_seq_client_info_t* clientInfo = nullptr;
  93. if (snd_seq_system_info_malloc (&systemInfo) == 0)
  94. {
  95. if (snd_seq_system_info (seqHandle, systemInfo) == 0
  96. && snd_seq_client_info_malloc (&clientInfo) == 0)
  97. {
  98. int numClients = snd_seq_system_info_get_cur_clients (systemInfo);
  99. while (--numClients >= 0 && returnedHandle == 0)
  100. if (snd_seq_query_next_client (seqHandle, clientInfo) == 0)
  101. returnedHandle = iterateMidiClient (seqHandle, clientInfo,
  102. forInput, deviceNamesFound,
  103. deviceIndexToOpen);
  104. snd_seq_client_info_free (clientInfo);
  105. }
  106. snd_seq_system_info_free (systemInfo);
  107. }
  108. if (returnedHandle == 0)
  109. snd_seq_close (seqHandle);
  110. }
  111. deviceNamesFound.appendNumbersToDuplicates (true, true);
  112. return returnedHandle;
  113. }
  114. snd_seq_t* createMidiDevice (const bool forInput, const String& deviceNameToOpen)
  115. {
  116. snd_seq_t* seqHandle = nullptr;
  117. if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT
  118. : SND_SEQ_OPEN_OUTPUT, 0) == 0)
  119. {
  120. snd_seq_set_client_name (seqHandle,
  121. (deviceNameToOpen + (forInput ? " Input" : " Output")).toUTF8());
  122. const int portId
  123. = snd_seq_create_simple_port (seqHandle,
  124. forInput ? "in"
  125. : "out",
  126. forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)
  127. : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ),
  128. forInput ? SND_SEQ_PORT_TYPE_APPLICATION
  129. : SND_SEQ_PORT_TYPE_MIDI_GENERIC);
  130. if (portId < 0)
  131. {
  132. snd_seq_close (seqHandle);
  133. seqHandle = nullptr;
  134. }
  135. }
  136. return seqHandle;
  137. }
  138. }
  139. //==============================================================================
  140. class MidiOutputDevice
  141. {
  142. public:
  143. MidiOutputDevice (MidiOutput* const midiOutput_,
  144. snd_seq_t* const seqHandle_)
  145. :
  146. midiOutput (midiOutput_),
  147. seqHandle (seqHandle_),
  148. maxEventSize (16 * 1024)
  149. {
  150. jassert (seqHandle != 0 && midiOutput != 0);
  151. snd_midi_event_new (maxEventSize, &midiParser);
  152. }
  153. ~MidiOutputDevice()
  154. {
  155. snd_midi_event_free (midiParser);
  156. snd_seq_close (seqHandle);
  157. }
  158. void sendMessageNow (const MidiMessage& message)
  159. {
  160. if (message.getRawDataSize() > maxEventSize)
  161. {
  162. maxEventSize = message.getRawDataSize();
  163. snd_midi_event_free (midiParser);
  164. snd_midi_event_new (maxEventSize, &midiParser);
  165. }
  166. snd_seq_event_t event;
  167. snd_seq_ev_clear (&event);
  168. long numBytes = (long) message.getRawDataSize();
  169. const uint8* data = message.getRawData();
  170. while (numBytes > 0)
  171. {
  172. const long numSent = snd_midi_event_encode (midiParser, data, numBytes, &event);
  173. if (numSent <= 0)
  174. break;
  175. numBytes -= numSent;
  176. data += numSent;
  177. snd_seq_ev_set_source (&event, 0);
  178. snd_seq_ev_set_subs (&event);
  179. snd_seq_ev_set_direct (&event);
  180. snd_seq_event_output (seqHandle, &event);
  181. }
  182. snd_seq_drain_output (seqHandle);
  183. snd_midi_event_reset_encode (midiParser);
  184. }
  185. private:
  186. MidiOutput* const midiOutput;
  187. snd_seq_t* const seqHandle;
  188. snd_midi_event_t* midiParser;
  189. int maxEventSize;
  190. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice);
  191. };
  192. StringArray MidiOutput::getDevices()
  193. {
  194. StringArray devices;
  195. iterateMidiDevices (false, devices, -1);
  196. return devices;
  197. }
  198. int MidiOutput::getDefaultDeviceIndex()
  199. {
  200. return 0;
  201. }
  202. MidiOutput* MidiOutput::openDevice (int deviceIndex)
  203. {
  204. MidiOutput* newDevice = nullptr;
  205. StringArray devices;
  206. snd_seq_t* const handle = iterateMidiDevices (false, devices, deviceIndex);
  207. if (handle != 0)
  208. {
  209. newDevice = new MidiOutput();
  210. newDevice->internal = new MidiOutputDevice (newDevice, handle);
  211. }
  212. return newDevice;
  213. }
  214. MidiOutput* MidiOutput::createNewDevice (const String& deviceName)
  215. {
  216. MidiOutput* newDevice = nullptr;
  217. snd_seq_t* const handle = createMidiDevice (false, deviceName);
  218. if (handle != 0)
  219. {
  220. newDevice = new MidiOutput();
  221. newDevice->internal = new MidiOutputDevice (newDevice, handle);
  222. }
  223. return newDevice;
  224. }
  225. MidiOutput::~MidiOutput()
  226. {
  227. delete static_cast <MidiOutputDevice*> (internal);
  228. }
  229. void MidiOutput::sendMessageNow (const MidiMessage& message)
  230. {
  231. static_cast <MidiOutputDevice*> (internal)->sendMessageNow (message);
  232. }
  233. //==============================================================================
  234. class MidiInputThread : public Thread
  235. {
  236. public:
  237. MidiInputThread (MidiInput* const midiInput_,
  238. snd_seq_t* const seqHandle_,
  239. MidiInputCallback* const callback_)
  240. : Thread ("Juce MIDI Input"),
  241. midiInput (midiInput_),
  242. seqHandle (seqHandle_),
  243. callback (callback_)
  244. {
  245. jassert (seqHandle != 0 && callback != 0 && midiInput != 0);
  246. }
  247. ~MidiInputThread()
  248. {
  249. snd_seq_close (seqHandle);
  250. }
  251. void run()
  252. {
  253. const int maxEventSize = 16 * 1024;
  254. snd_midi_event_t* midiParser;
  255. if (snd_midi_event_new (maxEventSize, &midiParser) >= 0)
  256. {
  257. HeapBlock <uint8> buffer (maxEventSize);
  258. const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN);
  259. struct pollfd* const pfd = (struct pollfd*) alloca (numPfds * sizeof (struct pollfd));
  260. snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN);
  261. while (! threadShouldExit())
  262. {
  263. if (poll (pfd, numPfds, 500) > 0)
  264. {
  265. snd_seq_event_t* inputEvent = nullptr;
  266. snd_seq_nonblock (seqHandle, 1);
  267. do
  268. {
  269. if (snd_seq_event_input (seqHandle, &inputEvent) >= 0)
  270. {
  271. // xxx what about SYSEXes that are too big for the buffer?
  272. const int numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent);
  273. snd_midi_event_reset_decode (midiParser);
  274. if (numBytes > 0)
  275. {
  276. const MidiMessage message ((const uint8*) buffer,
  277. numBytes,
  278. Time::getMillisecondCounter() * 0.001);
  279. callback->handleIncomingMidiMessage (midiInput, message);
  280. }
  281. snd_seq_free_event (inputEvent);
  282. }
  283. }
  284. while (snd_seq_event_input_pending (seqHandle, 0) > 0);
  285. snd_seq_free_event (inputEvent);
  286. }
  287. }
  288. snd_midi_event_free (midiParser);
  289. }
  290. };
  291. private:
  292. MidiInput* const midiInput;
  293. snd_seq_t* const seqHandle;
  294. MidiInputCallback* const callback;
  295. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInputThread);
  296. };
  297. //==============================================================================
  298. MidiInput::MidiInput (const String& name_)
  299. : name (name_),
  300. internal (0)
  301. {
  302. }
  303. MidiInput::~MidiInput()
  304. {
  305. stop();
  306. delete static_cast <MidiInputThread*> (internal);
  307. }
  308. void MidiInput::start()
  309. {
  310. static_cast <MidiInputThread*> (internal)->startThread();
  311. }
  312. void MidiInput::stop()
  313. {
  314. static_cast <MidiInputThread*> (internal)->stopThread (3000);
  315. }
  316. int MidiInput::getDefaultDeviceIndex()
  317. {
  318. return 0;
  319. }
  320. StringArray MidiInput::getDevices()
  321. {
  322. StringArray devices;
  323. iterateMidiDevices (true, devices, -1);
  324. return devices;
  325. }
  326. MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback)
  327. {
  328. MidiInput* newDevice = nullptr;
  329. StringArray devices;
  330. snd_seq_t* const handle = iterateMidiDevices (true, devices, deviceIndex);
  331. if (handle != 0)
  332. {
  333. newDevice = new MidiInput (devices [deviceIndex]);
  334. newDevice->internal = new MidiInputThread (newDevice, handle, callback);
  335. }
  336. return newDevice;
  337. }
  338. MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
  339. {
  340. MidiInput* newDevice = nullptr;
  341. snd_seq_t* const handle = createMidiDevice (true, deviceName);
  342. if (handle != 0)
  343. {
  344. newDevice = new MidiInput (deviceName);
  345. newDevice->internal = new MidiInputThread (newDevice, handle, callback);
  346. }
  347. return newDevice;
  348. }
  349. //==============================================================================
  350. #else
  351. // (These are just stub functions if ALSA is unavailable...)
  352. StringArray MidiOutput::getDevices() { return StringArray(); }
  353. int MidiOutput::getDefaultDeviceIndex() { return 0; }
  354. MidiOutput* MidiOutput::openDevice (int) { return nullptr; }
  355. MidiOutput* MidiOutput::createNewDevice (const String&) { return nullptr; }
  356. MidiOutput::~MidiOutput() {}
  357. void MidiOutput::sendMessageNow (const MidiMessage&) {}
  358. MidiInput::MidiInput (const String& name_) : name (name_), internal (0) {}
  359. MidiInput::~MidiInput() {}
  360. void MidiInput::start() {}
  361. void MidiInput::stop() {}
  362. int MidiInput::getDefaultDeviceIndex() { return 0; }
  363. StringArray MidiInput::getDevices() { return StringArray(); }
  364. MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return nullptr; }
  365. MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return nullptr; }
  366. #endif