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.

491 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "../../../juce_Config.h"
  24. #if JUCE_BUILD_GUI_CLASSES
  25. #if JUCE_ALSA
  26. #include "../../../src/juce_core/basics/juce_StandardHeader.h"
  27. #include <alsa/asoundlib.h>
  28. BEGIN_JUCE_NAMESPACE
  29. #include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h"
  30. #include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h"
  31. #include "../../../src/juce_core/threads/juce_Thread.h"
  32. #include "../../../src/juce_core/basics/juce_Time.h"
  33. //==============================================================================
  34. static snd_seq_t* iterateDevices (const bool forInput,
  35. StringArray& deviceNamesFound,
  36. const int deviceIndexToOpen)
  37. {
  38. snd_seq_t* returnedHandle = 0;
  39. snd_seq_t* seqHandle;
  40. if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT
  41. : SND_SEQ_OPEN_OUTPUT, 0) == 0)
  42. {
  43. snd_seq_system_info_t* systemInfo;
  44. snd_seq_client_info_t* clientInfo;
  45. if (snd_seq_system_info_malloc (&systemInfo) == 0)
  46. {
  47. if (snd_seq_system_info (seqHandle, systemInfo) == 0
  48. && snd_seq_client_info_malloc (&clientInfo) == 0)
  49. {
  50. int numClients = snd_seq_system_info_get_cur_clients (systemInfo);
  51. while (--numClients >= 0 && returnedHandle == 0)
  52. {
  53. if (snd_seq_query_next_client (seqHandle, clientInfo) == 0)
  54. {
  55. snd_seq_port_info_t* portInfo;
  56. if (snd_seq_port_info_malloc (&portInfo) == 0)
  57. {
  58. int numPorts = snd_seq_client_info_get_num_ports (clientInfo);
  59. const int client = snd_seq_client_info_get_client (clientInfo);
  60. snd_seq_port_info_set_client (portInfo, client);
  61. snd_seq_port_info_set_port (portInfo, -1);
  62. while (--numPorts >= 0)
  63. {
  64. if (snd_seq_query_next_port (seqHandle, portInfo) == 0
  65. && (snd_seq_port_info_get_capability (portInfo)
  66. & (forInput ? SND_SEQ_PORT_CAP_READ
  67. : SND_SEQ_PORT_CAP_WRITE)) != 0)
  68. {
  69. deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo));
  70. if (deviceNamesFound.size() == deviceIndexToOpen + 1)
  71. {
  72. const int sourcePort = snd_seq_port_info_get_port (portInfo);
  73. const int sourceClient = snd_seq_client_info_get_client (clientInfo);
  74. if (sourcePort != -1)
  75. {
  76. snd_seq_set_client_name (seqHandle,
  77. forInput ? "Juce Midi Input"
  78. : "Juce Midi Output");
  79. const int portId
  80. = snd_seq_create_simple_port (seqHandle,
  81. forInput ? "Juce Midi In Port"
  82. : "Juce Midi Out Port",
  83. forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)
  84. : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ),
  85. SND_SEQ_PORT_TYPE_MIDI_GENERIC);
  86. snd_seq_connect_from (seqHandle, portId, sourceClient, sourcePort);
  87. returnedHandle = seqHandle;
  88. break;
  89. }
  90. }
  91. }
  92. }
  93. snd_seq_port_info_free (portInfo);
  94. }
  95. }
  96. }
  97. snd_seq_client_info_free (clientInfo);
  98. }
  99. snd_seq_system_info_free (systemInfo);
  100. }
  101. if (returnedHandle == 0)
  102. snd_seq_close (seqHandle);
  103. }
  104. deviceNamesFound.appendNumbersToDuplicates (true, true);
  105. return returnedHandle;
  106. }
  107. static snd_seq_t* createDevice (const bool forInput,
  108. const String& deviceNameToOpen)
  109. {
  110. snd_seq_t* seqHandle = 0;
  111. if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT
  112. : SND_SEQ_OPEN_OUTPUT, 0) == 0)
  113. {
  114. snd_seq_set_client_name (seqHandle,
  115. (const char*) (forInput ? (deviceNameToOpen + T(" Input"))
  116. : (deviceNameToOpen + T(" Output"))));
  117. const int portId
  118. = snd_seq_create_simple_port (seqHandle,
  119. forInput ? "in"
  120. : "out",
  121. forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)
  122. : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ),
  123. forInput ? SND_SEQ_PORT_TYPE_APPLICATION
  124. : SND_SEQ_PORT_TYPE_MIDI_GENERIC);
  125. if (portId < 0)
  126. {
  127. snd_seq_close (seqHandle);
  128. seqHandle = 0;
  129. }
  130. }
  131. return seqHandle;
  132. }
  133. //==============================================================================
  134. class MidiOutputDevice
  135. {
  136. public:
  137. MidiOutputDevice (MidiOutput* const midiOutput_,
  138. snd_seq_t* const seqHandle_)
  139. :
  140. midiOutput (midiOutput_),
  141. seqHandle (seqHandle_),
  142. maxEventSize (16 * 1024)
  143. {
  144. jassert (seqHandle != 0 && midiOutput != 0);
  145. snd_midi_event_new (maxEventSize, &midiParser);
  146. }
  147. ~MidiOutputDevice()
  148. {
  149. snd_midi_event_free (midiParser);
  150. snd_seq_close (seqHandle);
  151. }
  152. void sendMessageNow (const MidiMessage& message)
  153. {
  154. if (message.getRawDataSize() > maxEventSize)
  155. {
  156. maxEventSize = message.getRawDataSize();
  157. snd_midi_event_free (midiParser);
  158. snd_midi_event_new (maxEventSize, &midiParser);
  159. }
  160. snd_seq_event_t event;
  161. snd_seq_ev_clear (&event);
  162. snd_midi_event_encode (midiParser,
  163. message.getRawData(),
  164. message.getRawDataSize(),
  165. &event);
  166. snd_midi_event_reset_encode (midiParser);
  167. snd_seq_ev_set_source (&event, 0);
  168. snd_seq_ev_set_subs (&event);
  169. snd_seq_ev_set_direct (&event);
  170. snd_seq_event_output_direct (seqHandle, &event);
  171. }
  172. juce_UseDebuggingNewOperator
  173. private:
  174. MidiOutput* const midiOutput;
  175. snd_seq_t* const seqHandle;
  176. snd_midi_event_t* midiParser;
  177. int maxEventSize;
  178. };
  179. const StringArray MidiOutput::getDevices()
  180. {
  181. StringArray devices;
  182. iterateDevices (false, devices, -1);
  183. return devices;
  184. }
  185. int MidiOutput::getDefaultDeviceIndex()
  186. {
  187. return 0;
  188. }
  189. MidiOutput* MidiOutput::openDevice (int deviceIndex)
  190. {
  191. MidiOutput* newDevice = 0;
  192. StringArray devices;
  193. snd_seq_t* const handle = iterateDevices (false, devices, deviceIndex);
  194. if (handle != 0)
  195. {
  196. newDevice = new MidiOutput();
  197. newDevice->internal = new MidiOutputDevice (newDevice, handle);
  198. }
  199. return newDevice;
  200. }
  201. MidiOutput* MidiOutput::createNewDevice (const String& deviceName)
  202. {
  203. MidiOutput* newDevice = 0;
  204. snd_seq_t* const handle = createDevice (false, deviceName);
  205. if (handle != 0)
  206. {
  207. newDevice = new MidiOutput();
  208. newDevice->internal = new MidiOutputDevice (newDevice, handle);
  209. }
  210. return newDevice;
  211. }
  212. MidiOutput::~MidiOutput()
  213. {
  214. MidiOutputDevice* const device = (MidiOutputDevice*) internal;
  215. delete device;
  216. }
  217. void MidiOutput::reset()
  218. {
  219. }
  220. bool MidiOutput::getVolume (float& leftVol, float& rightVol)
  221. {
  222. return false;
  223. }
  224. void MidiOutput::setVolume (float leftVol, float rightVol)
  225. {
  226. }
  227. void MidiOutput::sendMessageNow (const MidiMessage& message)
  228. {
  229. ((MidiOutputDevice*) internal)->sendMessageNow (message);
  230. }
  231. //==============================================================================
  232. class MidiInputThread : public Thread
  233. {
  234. public:
  235. MidiInputThread (MidiInput* const midiInput_,
  236. snd_seq_t* const seqHandle_,
  237. MidiInputCallback* const callback_)
  238. : Thread (T("Juce MIDI Input")),
  239. midiInput (midiInput_),
  240. seqHandle (seqHandle_),
  241. callback (callback_)
  242. {
  243. jassert (seqHandle != 0 && callback != 0 && midiInput != 0);
  244. }
  245. ~MidiInputThread()
  246. {
  247. snd_seq_close (seqHandle);
  248. }
  249. void run()
  250. {
  251. const int maxEventSize = 16 * 1024;
  252. snd_midi_event_t* midiParser;
  253. if (snd_midi_event_new (maxEventSize, &midiParser) >= 0)
  254. {
  255. uint8* const buffer = (uint8*) juce_malloc (maxEventSize);
  256. const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN);
  257. struct pollfd* const pfd = (struct pollfd*) alloca (numPfds * sizeof (struct pollfd));
  258. snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN);
  259. while (! threadShouldExit())
  260. {
  261. if (poll (pfd, numPfds, 500) > 0)
  262. {
  263. snd_seq_event_t* inputEvent = 0;
  264. snd_seq_nonblock (seqHandle, 1);
  265. do
  266. {
  267. if (snd_seq_event_input (seqHandle, &inputEvent) >= 0)
  268. {
  269. // xxx what about SYSEXes that are too big for the buffer?
  270. const int numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent);
  271. snd_midi_event_reset_decode (midiParser);
  272. if (numBytes > 0)
  273. {
  274. const MidiMessage message ((const uint8*) buffer,
  275. numBytes,
  276. Time::getMillisecondCounter() * 0.001);
  277. callback->handleIncomingMidiMessage (midiInput, message);
  278. }
  279. snd_seq_free_event (inputEvent);
  280. }
  281. }
  282. while (snd_seq_event_input_pending (seqHandle, 0) > 0);
  283. snd_seq_free_event (inputEvent);
  284. }
  285. }
  286. snd_midi_event_free (midiParser);
  287. juce_free (buffer);
  288. }
  289. };
  290. juce_UseDebuggingNewOperator
  291. private:
  292. MidiInput* const midiInput;
  293. snd_seq_t* const seqHandle;
  294. MidiInputCallback* const callback;
  295. };
  296. //==============================================================================
  297. MidiInput::MidiInput (const String& name_)
  298. : name (name_),
  299. internal (0)
  300. {
  301. }
  302. MidiInput::~MidiInput()
  303. {
  304. stop();
  305. MidiInputThread* const thread = (MidiInputThread*) internal;
  306. delete thread;
  307. }
  308. void MidiInput::start()
  309. {
  310. ((MidiInputThread*) internal)->startThread();
  311. }
  312. void MidiInput::stop()
  313. {
  314. ((MidiInputThread*) internal)->stopThread (3000);
  315. }
  316. int MidiInput::getDefaultDeviceIndex()
  317. {
  318. return 0;
  319. }
  320. const StringArray MidiInput::getDevices()
  321. {
  322. StringArray devices;
  323. iterateDevices (true, devices, -1);
  324. return devices;
  325. }
  326. MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback)
  327. {
  328. MidiInput* newDevice = 0;
  329. StringArray devices;
  330. snd_seq_t* const handle = iterateDevices (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 = 0;
  341. snd_seq_t* const handle = createDevice (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. END_JUCE_NAMESPACE
  350. //==============================================================================
  351. #else
  352. //==============================================================================
  353. // (These are just stub functions if ALSA is unavailable...)
  354. #include "../../../src/juce_core/basics/juce_StandardHeader.h"
  355. BEGIN_JUCE_NAMESPACE
  356. #include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h"
  357. #include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h"
  358. //==============================================================================
  359. const StringArray MidiOutput::getDevices() { return StringArray(); }
  360. int MidiOutput::getDefaultDeviceIndex() { return 0; }
  361. MidiOutput* MidiOutput::openDevice (int) { return 0; }
  362. MidiOutput* MidiOutput::createNewDevice (const String&) { return 0; }
  363. MidiOutput::~MidiOutput() {}
  364. void MidiOutput::reset() {}
  365. bool MidiOutput::getVolume (float&, float&) { return false; }
  366. void MidiOutput::setVolume (float, float) {}
  367. void MidiOutput::sendMessageNow (const MidiMessage&) {}
  368. MidiInput::MidiInput (const String& name_)
  369. : name (name_),
  370. internal (0)
  371. {}
  372. MidiInput::~MidiInput() {}
  373. void MidiInput::start() {}
  374. void MidiInput::stop() {}
  375. int MidiInput::getDefaultDeviceIndex() { return 0; }
  376. const StringArray MidiInput::getDevices() { return StringArray(); }
  377. MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return 0; }
  378. MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return 0; }
  379. END_JUCE_NAMESPACE
  380. #endif
  381. #endif