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.

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