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.

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