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.

621 lines
20KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2016 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license/
  7. Permission to use, copy, modify, and/or distribute this software for any
  8. purpose with or without fee is hereby granted, provided that the above
  9. copyright notice and this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
  11. TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  12. FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  13. OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  14. USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  15. TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  16. OF THIS SOFTWARE.
  17. -----------------------------------------------------------------------------
  18. To release a closed-source product which uses other parts of JUCE not
  19. licensed under the ISC terms, commercial licenses are available: visit
  20. www.juce.com for more information.
  21. ==============================================================================
  22. */
  23. #if JUCE_ALSA
  24. // You can define these strings in your app if you want to override the default names:
  25. #ifndef JUCE_ALSA_MIDI_NAME
  26. #define JUCE_ALSA_MIDI_NAME JUCEApplicationBase::getInstance()->getApplicationName().toUTF8()
  27. #endif
  28. //==============================================================================
  29. namespace
  30. {
  31. //==============================================================================
  32. class AlsaClient : public ReferenceCountedObject
  33. {
  34. public:
  35. typedef ReferenceCountedObjectPtr<AlsaClient> Ptr;
  36. //==============================================================================
  37. // represents an input or output port of the supplied AlsaClient
  38. class Port
  39. {
  40. public:
  41. Port (AlsaClient& c, bool forInput) noexcept
  42. : portId (-1),
  43. callbackEnabled (false),
  44. client (c),
  45. isInput (forInput),
  46. callback (nullptr),
  47. maxEventSize (4 * 1024),
  48. midiInput (nullptr)
  49. {}
  50. ~Port()
  51. {
  52. if (isValid())
  53. {
  54. if (isInput)
  55. enableCallback (false);
  56. else
  57. snd_midi_event_free (midiParser);
  58. snd_seq_delete_simple_port (client.get(), portId);
  59. }
  60. }
  61. void connectWith (int sourceClient, int sourcePort) const noexcept
  62. {
  63. if (isInput)
  64. snd_seq_connect_from (client.get(), portId, sourceClient, sourcePort);
  65. else
  66. snd_seq_connect_to (client.get(), portId, sourceClient, sourcePort);
  67. }
  68. bool isValid() const noexcept
  69. {
  70. return client.get() != nullptr && portId >= 0;
  71. }
  72. void setupInput(MidiInput* input, MidiInputCallback* cb)
  73. {
  74. jassert (cb && input);
  75. callback = cb;
  76. midiInput = input;
  77. }
  78. void setupOutput()
  79. {
  80. jassert (! isInput);
  81. snd_midi_event_new ((size_t) maxEventSize, &midiParser);
  82. }
  83. void enableCallback (bool enable)
  84. {
  85. if (callbackEnabled != enable)
  86. {
  87. callbackEnabled = enable;
  88. if (enable)
  89. client.registerCallback();
  90. else
  91. client.unregisterCallback();
  92. }
  93. }
  94. bool sendMessageNow (const MidiMessage& message)
  95. {
  96. if (message.getRawDataSize() > maxEventSize)
  97. {
  98. maxEventSize = message.getRawDataSize();
  99. snd_midi_event_free (midiParser);
  100. snd_midi_event_new ((size_t) maxEventSize, &midiParser);
  101. }
  102. snd_seq_event_t event;
  103. snd_seq_ev_clear (&event);
  104. long numBytes = (long) message.getRawDataSize();
  105. const uint8* data = message.getRawData();
  106. snd_seq_t* seqHandle = client.get();
  107. bool success = true;
  108. while (numBytes > 0)
  109. {
  110. const long numSent = snd_midi_event_encode (midiParser, data, numBytes, &event);
  111. if (numSent <= 0)
  112. {
  113. success = numSent == 0;
  114. break;
  115. }
  116. numBytes -= numSent;
  117. data += numSent;
  118. snd_seq_ev_set_source (&event, portId);
  119. snd_seq_ev_set_subs (&event);
  120. snd_seq_ev_set_direct (&event);
  121. if (snd_seq_event_output_direct (seqHandle, &event) < 0)
  122. {
  123. success = false;
  124. break;
  125. }
  126. }
  127. snd_midi_event_reset_encode (midiParser);
  128. return success;
  129. }
  130. bool operator== (const Port& lhs) const noexcept
  131. {
  132. return portId != -1 && portId == lhs.portId;
  133. }
  134. int portId;
  135. bool callbackEnabled;
  136. private:
  137. friend class AlsaClient;
  138. AlsaClient& client;
  139. bool isInput;
  140. MidiInputCallback* callback;
  141. snd_midi_event_t* midiParser;
  142. int maxEventSize;
  143. MidiInput* midiInput;
  144. void createPort (const String& name, bool enableSubscription)
  145. {
  146. if (snd_seq_t* seqHandle = client.get())
  147. {
  148. const unsigned int caps =
  149. isInput
  150. ? (SND_SEQ_PORT_CAP_WRITE | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_WRITE : 0))
  151. : (SND_SEQ_PORT_CAP_WRITE | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_READ : 0));
  152. portId = snd_seq_create_simple_port (seqHandle, name.toUTF8(), caps,
  153. SND_SEQ_PORT_TYPE_MIDI_GENERIC |
  154. SND_SEQ_PORT_TYPE_APPLICATION);
  155. }
  156. }
  157. void handleIncomingMidiMessage (const MidiMessage& message) const
  158. {
  159. callback->handleIncomingMidiMessage (midiInput, message);
  160. }
  161. void handlePartialSysexMessage (const uint8* messageData, int numBytesSoFar, double timeStamp)
  162. {
  163. callback->handlePartialSysexMessage (midiInput, messageData, numBytesSoFar, timeStamp);
  164. }
  165. };
  166. static Ptr getInstance()
  167. {
  168. if (instance == nullptr)
  169. instance = new AlsaClient();
  170. return instance;
  171. }
  172. void registerCallback ()
  173. {
  174. if (inputThread == nullptr)
  175. inputThread = new MidiInputThread (*this);
  176. if (++activeCallbacks - 1 == 0)
  177. inputThread->startThread();
  178. }
  179. void unregisterCallback ()
  180. {
  181. jassert (activeCallbacks.get() > 0);
  182. if (--activeCallbacks == 0 && inputThread->isThreadRunning())
  183. inputThread->signalThreadShouldExit();
  184. }
  185. void handleIncomingMidiMessage (snd_seq_event* event, const MidiMessage& message)
  186. {
  187. if (event->dest.port < ports.size()
  188. && ports[event->dest.port]->callbackEnabled)
  189. ports[event->dest.port]->handleIncomingMidiMessage (message);
  190. }
  191. void handlePartialSysexMessage (snd_seq_event* event, const uint8* messageData, int numBytesSoFar, double timeStamp)
  192. {
  193. if (event->dest.port < ports.size()
  194. && ports[event->dest.port]->callbackEnabled)
  195. ports[event->dest.port]->handlePartialSysexMessage (messageData, numBytesSoFar, timeStamp);
  196. }
  197. snd_seq_t* get() const noexcept { return handle; }
  198. int getId() const noexcept { return clientId; }
  199. Port* createPort (const String& name, bool forInput, bool enableSubscription)
  200. {
  201. Port* port = new Port (*this, forInput);
  202. port->createPort (name, enableSubscription);
  203. ports.set (port->portId, port);
  204. incReferenceCount();
  205. return port;
  206. }
  207. void deletePort (Port* port)
  208. {
  209. ports.remove (port->portId);
  210. decReferenceCount();
  211. }
  212. private:
  213. snd_seq_t* handle;
  214. int clientId;
  215. OwnedArray<Port> ports;
  216. Atomic<int> activeCallbacks;
  217. CriticalSection callbackLock;
  218. static AlsaClient* instance;
  219. //==============================================================================
  220. friend class ReferenceCountedObjectPtr<AlsaClient>;
  221. friend struct ContainerDeletePolicy<AlsaClient>;
  222. AlsaClient ()
  223. : handle (nullptr),
  224. inputThread (nullptr)
  225. {
  226. jassert (instance == nullptr);
  227. snd_seq_open (&handle, "default", SND_SEQ_OPEN_DUPLEX, 0);
  228. snd_seq_nonblock (handle, SND_SEQ_NONBLOCK);
  229. snd_seq_set_client_name (handle, JUCE_ALSA_MIDI_NAME);
  230. clientId = snd_seq_client_id(handle);
  231. // It's good idea to pre-allocate a good number of elements
  232. ports.ensureStorageAllocated (32);
  233. }
  234. ~AlsaClient()
  235. {
  236. jassert (instance != nullptr);
  237. instance = nullptr;
  238. if (handle != nullptr)
  239. snd_seq_close (handle);
  240. jassert (activeCallbacks.get() == 0);
  241. if (inputThread)
  242. inputThread->stopThread (3000);
  243. }
  244. //==============================================================================
  245. class MidiInputThread : public Thread
  246. {
  247. public:
  248. MidiInputThread (AlsaClient& c)
  249. : Thread ("Juce MIDI Input"), client (c), concatenator (2048)
  250. {
  251. jassert (client.get() != nullptr);
  252. }
  253. void run() override
  254. {
  255. const int maxEventSize = 16 * 1024;
  256. snd_midi_event_t* midiParser;
  257. snd_seq_t* seqHandle = client.get();
  258. if (snd_midi_event_new (maxEventSize, &midiParser) >= 0)
  259. {
  260. const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN);
  261. HeapBlock<pollfd> pfd ((size_t) numPfds);
  262. snd_seq_poll_descriptors (seqHandle, pfd, (unsigned int) numPfds, POLLIN);
  263. HeapBlock<uint8> buffer (maxEventSize);
  264. while (! threadShouldExit())
  265. {
  266. if (poll (pfd, (nfds_t) numPfds, 100) > 0) // there was a "500" here which is a bit long when we exit the program and have to wait for a timeout on this poll call
  267. {
  268. if (threadShouldExit())
  269. break;
  270. do
  271. {
  272. snd_seq_event_t* inputEvent = nullptr;
  273. if (snd_seq_event_input (seqHandle, &inputEvent) >= 0)
  274. {
  275. // xxx what about SYSEXes that are too big for the buffer?
  276. const long numBytes = snd_midi_event_decode (midiParser, buffer,
  277. maxEventSize, inputEvent);
  278. snd_midi_event_reset_decode (midiParser);
  279. concatenator.pushMidiData (buffer, (int) numBytes,
  280. Time::getMillisecondCounter() * 0.001,
  281. inputEvent, client);
  282. snd_seq_free_event (inputEvent);
  283. }
  284. }
  285. while (snd_seq_event_input_pending (seqHandle, 0) > 0);
  286. }
  287. }
  288. snd_midi_event_free (midiParser);
  289. }
  290. };
  291. private:
  292. AlsaClient& client;
  293. MidiDataConcatenator concatenator;
  294. };
  295. ScopedPointer<MidiInputThread> inputThread;
  296. };
  297. AlsaClient* AlsaClient::instance = nullptr;
  298. //==============================================================================
  299. static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client,
  300. snd_seq_client_info_t* clientInfo,
  301. const bool forInput,
  302. StringArray& deviceNamesFound,
  303. const int deviceIndexToOpen)
  304. {
  305. AlsaClient::Port* port = nullptr;
  306. snd_seq_t* seqHandle = client->get();
  307. snd_seq_port_info_t* portInfo = nullptr;
  308. snd_seq_port_info_alloca (&portInfo);
  309. jassert (portInfo);
  310. int numPorts = snd_seq_client_info_get_num_ports (clientInfo);
  311. const int sourceClient = snd_seq_client_info_get_client (clientInfo);
  312. snd_seq_port_info_set_client (portInfo, sourceClient);
  313. snd_seq_port_info_set_port (portInfo, -1);
  314. while (--numPorts >= 0)
  315. {
  316. if (snd_seq_query_next_port (seqHandle, portInfo) == 0
  317. && (snd_seq_port_info_get_capability (portInfo)
  318. & (forInput ? SND_SEQ_PORT_CAP_SUBS_WRITE : SND_SEQ_PORT_CAP_SUBS_READ)) != 0)
  319. {
  320. const String portName = snd_seq_port_info_get_name(portInfo);
  321. deviceNamesFound.add (portName);
  322. if (deviceNamesFound.size() == deviceIndexToOpen + 1)
  323. {
  324. const int sourcePort = snd_seq_port_info_get_port (portInfo);
  325. if (sourcePort != -1)
  326. {
  327. port = client->createPort (portName, forInput, false);
  328. jassert (port->isValid());
  329. port->connectWith (sourceClient, sourcePort);
  330. break;
  331. }
  332. }
  333. }
  334. }
  335. return port;
  336. }
  337. static AlsaClient::Port* iterateMidiDevices (const bool forInput,
  338. StringArray& deviceNamesFound,
  339. const int deviceIndexToOpen)
  340. {
  341. AlsaClient::Port* port = nullptr;
  342. const AlsaClient::Ptr client (AlsaClient::getInstance());
  343. if (snd_seq_t* const seqHandle = client->get())
  344. {
  345. snd_seq_system_info_t* systemInfo = nullptr;
  346. snd_seq_client_info_t* clientInfo = nullptr;
  347. snd_seq_system_info_alloca (&systemInfo);
  348. jassert(systemInfo);
  349. if (snd_seq_system_info (seqHandle, systemInfo) == 0)
  350. {
  351. snd_seq_client_info_alloca (&clientInfo);
  352. jassert(clientInfo);
  353. int numClients = snd_seq_system_info_get_cur_clients (systemInfo);
  354. while (--numClients >= 0)
  355. {
  356. if (snd_seq_query_next_client (seqHandle, clientInfo) == 0)
  357. {
  358. const int sourceClient = snd_seq_client_info_get_client (clientInfo);
  359. if (sourceClient != client->getId()
  360. && sourceClient != SND_SEQ_CLIENT_SYSTEM)
  361. {
  362. port = iterateMidiClient (client, clientInfo, forInput,
  363. deviceNamesFound, deviceIndexToOpen);
  364. if (port)
  365. break;
  366. }
  367. }
  368. }
  369. }
  370. }
  371. deviceNamesFound.appendNumbersToDuplicates (true, true);
  372. return port;
  373. }
  374. } // namespace
  375. StringArray MidiOutput::getDevices()
  376. {
  377. StringArray devices;
  378. iterateMidiDevices (false, devices, -1);
  379. return devices;
  380. }
  381. int MidiOutput::getDefaultDeviceIndex()
  382. {
  383. return 0;
  384. }
  385. MidiOutput* MidiOutput::openDevice (int deviceIndex)
  386. {
  387. MidiOutput* newDevice = nullptr;
  388. StringArray devices;
  389. AlsaClient::Port* port = iterateMidiDevices (false, devices, deviceIndex);
  390. if (port == nullptr)
  391. return nullptr;
  392. jassert (port->isValid());
  393. newDevice = new MidiOutput (devices [deviceIndex]);
  394. port->setupOutput();
  395. newDevice->internal = port;
  396. return newDevice;
  397. }
  398. MidiOutput* MidiOutput::createNewDevice (const String& deviceName)
  399. {
  400. MidiOutput* newDevice = nullptr;
  401. const AlsaClient::Ptr client (AlsaClient::getInstance());
  402. AlsaClient::Port* port = client->createPort (deviceName, false, true);
  403. jassert (port->isValid());
  404. newDevice = new MidiOutput (deviceName);
  405. port->setupOutput();
  406. newDevice->internal = port;
  407. return newDevice;
  408. }
  409. MidiOutput::~MidiOutput()
  410. {
  411. stopBackgroundThread();
  412. AlsaClient::Ptr client (AlsaClient::getInstance());
  413. client->deletePort (static_cast<AlsaClient::Port*> (internal));
  414. }
  415. void MidiOutput::sendMessageNow (const MidiMessage& message)
  416. {
  417. static_cast<AlsaClient::Port*> (internal)->sendMessageNow (message);
  418. }
  419. //==============================================================================
  420. MidiInput::MidiInput (const String& nm)
  421. : name (nm), internal (nullptr)
  422. {
  423. }
  424. MidiInput::~MidiInput()
  425. {
  426. stop();
  427. AlsaClient::Ptr client (AlsaClient::getInstance());
  428. client->deletePort (static_cast<AlsaClient::Port*> (internal));
  429. }
  430. void MidiInput::start()
  431. {
  432. static_cast<AlsaClient::Port*> (internal)->enableCallback (true);
  433. }
  434. void MidiInput::stop()
  435. {
  436. static_cast<AlsaClient::Port*> (internal)->enableCallback (false);
  437. }
  438. int MidiInput::getDefaultDeviceIndex()
  439. {
  440. return 0;
  441. }
  442. StringArray MidiInput::getDevices()
  443. {
  444. StringArray devices;
  445. iterateMidiDevices (true, devices, -1);
  446. return devices;
  447. }
  448. MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback)
  449. {
  450. MidiInput* newDevice = nullptr;
  451. StringArray devices;
  452. AlsaClient::Port* port = iterateMidiDevices (true, devices, deviceIndex);
  453. if (port == nullptr)
  454. return nullptr;
  455. jassert (port->isValid());
  456. newDevice = new MidiInput (devices [deviceIndex]);
  457. port->setupInput (newDevice, callback);
  458. newDevice->internal = port;
  459. return newDevice;
  460. }
  461. MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
  462. {
  463. MidiInput* newDevice = nullptr;
  464. AlsaClient::Ptr client (AlsaClient::getInstance());
  465. AlsaClient::Port* port = client->createPort (deviceName, true, true);
  466. jassert (port->isValid());
  467. newDevice = new MidiInput (deviceName);
  468. port->setupInput (newDevice, callback);
  469. newDevice->internal = port;
  470. return newDevice;
  471. }
  472. //==============================================================================
  473. #else
  474. // (These are just stub functions if ALSA is unavailable...)
  475. StringArray MidiOutput::getDevices() { return StringArray(); }
  476. int MidiOutput::getDefaultDeviceIndex() { return 0; }
  477. MidiOutput* MidiOutput::openDevice (int) { return nullptr; }
  478. MidiOutput* MidiOutput::createNewDevice (const String&) { return nullptr; }
  479. MidiOutput::~MidiOutput() {}
  480. void MidiOutput::sendMessageNow (const MidiMessage&) {}
  481. MidiInput::MidiInput (const String& nm) : name (nm), internal (nullptr) {}
  482. MidiInput::~MidiInput() {}
  483. void MidiInput::start() {}
  484. void MidiInput::stop() {}
  485. int MidiInput::getDefaultDeviceIndex() { return 0; }
  486. StringArray MidiInput::getDevices() { return StringArray(); }
  487. MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return nullptr; }
  488. MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return nullptr; }
  489. #endif