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.

461 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. snd_midi_event_encode (midiParser,
  169. message.getRawData(),
  170. message.getRawDataSize(),
  171. &event);
  172. snd_midi_event_reset_encode (midiParser);
  173. snd_seq_ev_set_source (&event, 0);
  174. snd_seq_ev_set_subs (&event);
  175. snd_seq_ev_set_direct (&event);
  176. snd_seq_event_output (seqHandle, &event);
  177. snd_seq_drain_output (seqHandle);
  178. }
  179. private:
  180. MidiOutput* const midiOutput;
  181. snd_seq_t* const seqHandle;
  182. snd_midi_event_t* midiParser;
  183. int maxEventSize;
  184. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice);
  185. };
  186. StringArray MidiOutput::getDevices()
  187. {
  188. StringArray devices;
  189. iterateMidiDevices (false, devices, -1);
  190. return devices;
  191. }
  192. int MidiOutput::getDefaultDeviceIndex()
  193. {
  194. return 0;
  195. }
  196. MidiOutput* MidiOutput::openDevice (int deviceIndex)
  197. {
  198. MidiOutput* newDevice = nullptr;
  199. StringArray devices;
  200. snd_seq_t* const handle = iterateMidiDevices (false, devices, deviceIndex);
  201. if (handle != 0)
  202. {
  203. newDevice = new MidiOutput();
  204. newDevice->internal = new MidiOutputDevice (newDevice, handle);
  205. }
  206. return newDevice;
  207. }
  208. MidiOutput* MidiOutput::createNewDevice (const String& deviceName)
  209. {
  210. MidiOutput* newDevice = nullptr;
  211. snd_seq_t* const handle = createMidiDevice (false, deviceName);
  212. if (handle != 0)
  213. {
  214. newDevice = new MidiOutput();
  215. newDevice->internal = new MidiOutputDevice (newDevice, handle);
  216. }
  217. return newDevice;
  218. }
  219. MidiOutput::~MidiOutput()
  220. {
  221. delete static_cast <MidiOutputDevice*> (internal);
  222. }
  223. void MidiOutput::sendMessageNow (const MidiMessage& message)
  224. {
  225. static_cast <MidiOutputDevice*> (internal)->sendMessageNow (message);
  226. }
  227. //==============================================================================
  228. class MidiInputThread : public Thread
  229. {
  230. public:
  231. MidiInputThread (MidiInput* const midiInput_,
  232. snd_seq_t* const seqHandle_,
  233. MidiInputCallback* const callback_)
  234. : Thread ("Juce MIDI Input"),
  235. midiInput (midiInput_),
  236. seqHandle (seqHandle_),
  237. callback (callback_)
  238. {
  239. jassert (seqHandle != 0 && callback != 0 && midiInput != 0);
  240. }
  241. ~MidiInputThread()
  242. {
  243. snd_seq_close (seqHandle);
  244. }
  245. void run()
  246. {
  247. const int maxEventSize = 16 * 1024;
  248. snd_midi_event_t* midiParser;
  249. if (snd_midi_event_new (maxEventSize, &midiParser) >= 0)
  250. {
  251. HeapBlock <uint8> buffer (maxEventSize);
  252. const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN);
  253. struct pollfd* const pfd = (struct pollfd*) alloca (numPfds * sizeof (struct pollfd));
  254. snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN);
  255. while (! threadShouldExit())
  256. {
  257. if (poll (pfd, numPfds, 500) > 0)
  258. {
  259. snd_seq_event_t* inputEvent = nullptr;
  260. snd_seq_nonblock (seqHandle, 1);
  261. do
  262. {
  263. if (snd_seq_event_input (seqHandle, &inputEvent) >= 0)
  264. {
  265. // xxx what about SYSEXes that are too big for the buffer?
  266. const int numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent);
  267. snd_midi_event_reset_decode (midiParser);
  268. if (numBytes > 0)
  269. {
  270. const MidiMessage message ((const uint8*) buffer,
  271. numBytes,
  272. Time::getMillisecondCounter() * 0.001);
  273. callback->handleIncomingMidiMessage (midiInput, message);
  274. }
  275. snd_seq_free_event (inputEvent);
  276. }
  277. }
  278. while (snd_seq_event_input_pending (seqHandle, 0) > 0);
  279. snd_seq_free_event (inputEvent);
  280. }
  281. }
  282. snd_midi_event_free (midiParser);
  283. }
  284. };
  285. private:
  286. MidiInput* const midiInput;
  287. snd_seq_t* const seqHandle;
  288. MidiInputCallback* const callback;
  289. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInputThread);
  290. };
  291. //==============================================================================
  292. MidiInput::MidiInput (const String& name_)
  293. : name (name_),
  294. internal (0)
  295. {
  296. }
  297. MidiInput::~MidiInput()
  298. {
  299. stop();
  300. delete static_cast <MidiInputThread*> (internal);
  301. }
  302. void MidiInput::start()
  303. {
  304. static_cast <MidiInputThread*> (internal)->startThread();
  305. }
  306. void MidiInput::stop()
  307. {
  308. static_cast <MidiInputThread*> (internal)->stopThread (3000);
  309. }
  310. int MidiInput::getDefaultDeviceIndex()
  311. {
  312. return 0;
  313. }
  314. StringArray MidiInput::getDevices()
  315. {
  316. StringArray devices;
  317. iterateMidiDevices (true, devices, -1);
  318. return devices;
  319. }
  320. MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback)
  321. {
  322. MidiInput* newDevice = nullptr;
  323. StringArray devices;
  324. snd_seq_t* const handle = iterateMidiDevices (true, devices, deviceIndex);
  325. if (handle != 0)
  326. {
  327. newDevice = new MidiInput (devices [deviceIndex]);
  328. newDevice->internal = new MidiInputThread (newDevice, handle, callback);
  329. }
  330. return newDevice;
  331. }
  332. MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
  333. {
  334. MidiInput* newDevice = nullptr;
  335. snd_seq_t* const handle = createMidiDevice (true, deviceName);
  336. if (handle != 0)
  337. {
  338. newDevice = new MidiInput (deviceName);
  339. newDevice->internal = new MidiInputThread (newDevice, handle, callback);
  340. }
  341. return newDevice;
  342. }
  343. //==============================================================================
  344. #else
  345. // (These are just stub functions if ALSA is unavailable...)
  346. StringArray MidiOutput::getDevices() { return StringArray(); }
  347. int MidiOutput::getDefaultDeviceIndex() { return 0; }
  348. MidiOutput* MidiOutput::openDevice (int) { return nullptr; }
  349. MidiOutput* MidiOutput::createNewDevice (const String&) { return nullptr; }
  350. MidiOutput::~MidiOutput() {}
  351. void MidiOutput::sendMessageNow (const MidiMessage&) {}
  352. MidiInput::MidiInput (const String& name_) : name (name_), internal (0) {}
  353. MidiInput::~MidiInput() {}
  354. void MidiInput::start() {}
  355. void MidiInput::stop() {}
  356. int MidiInput::getDefaultDeviceIndex() { return 0; }
  357. StringArray MidiInput::getDevices() { return StringArray(); }
  358. MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return nullptr; }
  359. MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return nullptr; }
  360. #endif