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.

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