Audio plugin host https://kx.studio/carla
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.

673 lines
25KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. The code included in this file is provided under the terms of the ISC license
  8. http://www.isc.org/downloads/software-support-policy/isc-license. Permission
  9. To use, copy, modify, and/or distribute this software for any purpose with or
  10. without fee is hereby granted provided that the above copyright notice and
  11. this permission notice appear in all copies.
  12. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  13. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  14. DISCLAIMED.
  15. ==============================================================================
  16. */
  17. namespace juce
  18. {
  19. static void* juce_libjackHandle = nullptr;
  20. static void* juce_loadJackFunction (const char* const name)
  21. {
  22. if (juce_libjackHandle == nullptr)
  23. return nullptr;
  24. return dlsym (juce_libjackHandle, name);
  25. }
  26. #define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \
  27. return_type fn_name argument_types \
  28. { \
  29. using ReturnType = return_type; \
  30. typedef return_type (*fn_type) argument_types; \
  31. static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \
  32. jassert (fn != nullptr); \
  33. return (fn != nullptr) ? ((*fn) arguments) : ReturnType(); \
  34. }
  35. #define JUCE_DECL_VOID_JACK_FUNCTION(fn_name, argument_types, arguments) \
  36. void fn_name argument_types \
  37. { \
  38. typedef void (*fn_type) argument_types; \
  39. static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \
  40. jassert (fn != nullptr); \
  41. if (fn != nullptr) (*fn) arguments; \
  42. }
  43. //==============================================================================
  44. JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status, ...), (client_name, options, status))
  45. JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client))
  46. JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client))
  47. JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client))
  48. JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client))
  49. JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client))
  50. JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function)(void* arg), void* arg), (client, function, arg))
  51. JUCE_DECL_VOID_JACK_FUNCTION (jack_on_info_shutdown, (jack_client_t* client, JackInfoShutdownCallback function, void* arg), (client, function, arg))
  52. JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes))
  53. JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port))
  54. JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size))
  55. JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func))
  56. JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg))
  57. JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags))
  58. JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port))
  59. JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port))
  60. JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg))
  61. JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id))
  62. JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port))
  63. JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name))
  64. JUCE_DECL_JACK_FUNCTION (int, jack_set_xrun_callback, (jack_client_t* client, JackXRunCallback xrun_callback, void* arg), (client, xrun_callback, arg))
  65. JUCE_DECL_JACK_FUNCTION (int, jack_port_flags, (const jack_port_t* port), (port))
  66. JUCE_DECL_JACK_FUNCTION (jack_port_t*, jack_port_by_name, (jack_client_t* client, const char* name), (client, name))
  67. JUCE_DECL_VOID_JACK_FUNCTION (jack_free, (void* ptr), (ptr))
  68. #if JUCE_DEBUG
  69. #define JACK_LOGGING_ENABLED 1
  70. #endif
  71. #if JACK_LOGGING_ENABLED
  72. namespace
  73. {
  74. void jack_Log (const String& s)
  75. {
  76. std::cerr << s << std::endl;
  77. }
  78. const char* getJackErrorMessage (const jack_status_t status)
  79. {
  80. if (status & JackServerFailed
  81. || status & JackServerError) return "Unable to connect to JACK server";
  82. if (status & JackVersionError) return "Client's protocol version does not match";
  83. if (status & JackInvalidOption) return "The operation contained an invalid or unsupported option";
  84. if (status & JackNameNotUnique) return "The desired client name was not unique";
  85. if (status & JackNoSuchClient) return "Requested client does not exist";
  86. if (status & JackInitFailure) return "Unable to initialize client";
  87. return nullptr;
  88. }
  89. }
  90. #define JUCE_JACK_LOG_STATUS(x) { if (const char* m = getJackErrorMessage (x)) jack_Log (m); }
  91. #define JUCE_JACK_LOG(x) jack_Log(x)
  92. #else
  93. #define JUCE_JACK_LOG_STATUS(x) {}
  94. #define JUCE_JACK_LOG(x) {}
  95. #endif
  96. //==============================================================================
  97. #ifndef JUCE_JACK_CLIENT_NAME
  98. #ifdef JucePlugin_Name
  99. #define JUCE_JACK_CLIENT_NAME JucePlugin_Name
  100. #else
  101. #define JUCE_JACK_CLIENT_NAME "JUCEJack"
  102. #endif
  103. #endif
  104. struct JackPortIterator
  105. {
  106. JackPortIterator (jack_client_t* const client, const bool forInput)
  107. {
  108. if (client != nullptr)
  109. ports.reset (juce::jack_get_ports (client, nullptr, nullptr,
  110. forInput ? JackPortIsInput : JackPortIsOutput));
  111. }
  112. bool next()
  113. {
  114. if (ports == nullptr || ports.get()[index + 1] == nullptr)
  115. return false;
  116. name = CharPointer_UTF8 (ports.get()[++index]);
  117. return true;
  118. }
  119. String getClientName() const
  120. {
  121. return name.upToFirstOccurrenceOf (":", false, false);
  122. }
  123. String getChannelName() const
  124. {
  125. return name.fromFirstOccurrenceOf (":", false, false);
  126. }
  127. struct Free
  128. {
  129. void operator() (const char** ptr) const noexcept { juce::jack_free (ptr); }
  130. };
  131. std::unique_ptr<const char*, Free> ports;
  132. int index = -1;
  133. String name;
  134. };
  135. //==============================================================================
  136. class JackAudioIODevice : public AudioIODevice
  137. {
  138. public:
  139. JackAudioIODevice (const String& inName,
  140. const String& outName,
  141. std::function<void()> notifyIn)
  142. : AudioIODevice (outName.isEmpty() ? inName : outName, "JACK"),
  143. inputName (inName),
  144. outputName (outName),
  145. notifyChannelsChanged (std::move (notifyIn))
  146. {
  147. jassert (outName.isNotEmpty() || inName.isNotEmpty());
  148. jack_status_t status = {};
  149. client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status);
  150. if (client == nullptr)
  151. {
  152. JUCE_JACK_LOG_STATUS (status);
  153. }
  154. else
  155. {
  156. juce::jack_set_error_function (errorCallback);
  157. // open input ports
  158. const StringArray inputChannels (getInputChannelNames());
  159. for (int i = 0; i < inputChannels.size(); ++i)
  160. {
  161. String inputChannelName;
  162. inputChannelName << "in_" << ++totalNumberOfInputChannels;
  163. inputPorts.add (juce::jack_port_register (client, inputChannelName.toUTF8(),
  164. JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0));
  165. }
  166. // open output ports
  167. const StringArray outputChannels (getOutputChannelNames());
  168. for (int i = 0; i < outputChannels.size(); ++i)
  169. {
  170. String outputChannelName;
  171. outputChannelName << "out_" << ++totalNumberOfOutputChannels;
  172. outputPorts.add (juce::jack_port_register (client, outputChannelName.toUTF8(),
  173. JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0));
  174. }
  175. inChans.calloc (totalNumberOfInputChannels + 2);
  176. outChans.calloc (totalNumberOfOutputChannels + 2);
  177. }
  178. }
  179. ~JackAudioIODevice() override
  180. {
  181. close();
  182. if (client != nullptr)
  183. {
  184. juce::jack_client_close (client);
  185. client = nullptr;
  186. }
  187. }
  188. StringArray getChannelNames (const String& clientName, bool forInput) const
  189. {
  190. StringArray names;
  191. for (JackPortIterator i (client, forInput); i.next();)
  192. if (i.getClientName() == clientName)
  193. names.add (i.getChannelName());
  194. return names;
  195. }
  196. StringArray getOutputChannelNames() override { return getChannelNames (outputName, true); }
  197. StringArray getInputChannelNames() override { return getChannelNames (inputName, false); }
  198. Array<double> getAvailableSampleRates() override
  199. {
  200. Array<double> rates;
  201. if (client != nullptr)
  202. rates.add (juce::jack_get_sample_rate (client));
  203. return rates;
  204. }
  205. Array<int> getAvailableBufferSizes() override
  206. {
  207. Array<int> sizes;
  208. if (client != nullptr)
  209. sizes.add (static_cast<int> (juce::jack_get_buffer_size (client)));
  210. return sizes;
  211. }
  212. int getDefaultBufferSize() override { return getCurrentBufferSizeSamples(); }
  213. int getCurrentBufferSizeSamples() override { return client != nullptr ? static_cast<int> (juce::jack_get_buffer_size (client)) : 0; }
  214. double getCurrentSampleRate() override { return client != nullptr ? static_cast<int> (juce::jack_get_sample_rate (client)) : 0; }
  215. template <typename Fn>
  216. void forEachClientChannel (const String& clientName, bool isInput, Fn&& fn)
  217. {
  218. auto index = 0;
  219. for (JackPortIterator i (client, isInput); i.next();)
  220. {
  221. if (i.getClientName() != clientName)
  222. continue;
  223. fn (i.ports.get()[i.index], index);
  224. index += 1;
  225. }
  226. }
  227. String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
  228. double /* sampleRate */, int /* bufferSizeSamples */) override
  229. {
  230. if (client == nullptr)
  231. {
  232. lastError = "No JACK client running";
  233. return lastError;
  234. }
  235. lastError.clear();
  236. close();
  237. xruns.store (0, std::memory_order_relaxed);
  238. juce::jack_set_process_callback (client, processCallback, this);
  239. juce::jack_set_port_connect_callback (client, portConnectCallback, this);
  240. juce::jack_on_shutdown (client, shutdownCallback, this);
  241. juce::jack_on_info_shutdown (client, infoShutdownCallback, this);
  242. juce::jack_set_xrun_callback (client, xrunCallback, this);
  243. juce::jack_activate (client);
  244. deviceIsOpen = true;
  245. if (! inputChannels.isZero())
  246. {
  247. forEachClientChannel (inputName, false, [&] (const char* portName, int index)
  248. {
  249. if (! inputChannels[index])
  250. return;
  251. jassert (index < inputPorts.size());
  252. const auto* source = portName;
  253. const auto* inputPort = inputPorts[index];
  254. jassert (juce::jack_port_flags (juce::jack_port_by_name (client, source)) & JackPortIsOutput);
  255. jassert (juce::jack_port_flags (inputPort) & JackPortIsInput);
  256. auto error = juce::jack_connect (client, source, juce::jack_port_name (inputPort));
  257. if (error != 0)
  258. JUCE_JACK_LOG ("Cannot connect input port " + String (index) + " (" + portName + "), error " + String (error));
  259. });
  260. }
  261. if (! outputChannels.isZero())
  262. {
  263. forEachClientChannel (outputName, true, [&] (const char* portName, int index)
  264. {
  265. if (! outputChannels[index])
  266. return;
  267. jassert (index < outputPorts.size());
  268. const auto* outputPort = outputPorts[index];
  269. const auto* destination = portName;
  270. jassert (juce::jack_port_flags (outputPort) & JackPortIsOutput);
  271. jassert (juce::jack_port_flags (juce::jack_port_by_name (client, destination)) & JackPortIsInput);
  272. auto error = juce::jack_connect (client, juce::jack_port_name (outputPort), destination);
  273. if (error != 0)
  274. JUCE_JACK_LOG ("Cannot connect output port " + String (index) + " (" + portName + "), error " + String (error));
  275. });
  276. }
  277. updateActivePorts();
  278. return lastError;
  279. }
  280. void close() override
  281. {
  282. stop();
  283. if (client != nullptr)
  284. {
  285. const auto result = juce::jack_deactivate (client);
  286. jassertquiet (result == 0);
  287. juce::jack_set_xrun_callback (client, xrunCallback, nullptr);
  288. juce::jack_set_process_callback (client, processCallback, nullptr);
  289. juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr);
  290. juce::jack_on_shutdown (client, shutdownCallback, nullptr);
  291. juce::jack_on_info_shutdown (client, infoShutdownCallback, nullptr);
  292. }
  293. deviceIsOpen = false;
  294. }
  295. void start (AudioIODeviceCallback* newCallback) override
  296. {
  297. if (deviceIsOpen && newCallback != callback)
  298. {
  299. if (newCallback != nullptr)
  300. newCallback->audioDeviceAboutToStart (this);
  301. AudioIODeviceCallback* const oldCallback = callback;
  302. {
  303. const ScopedLock sl (callbackLock);
  304. callback = newCallback;
  305. }
  306. if (oldCallback != nullptr)
  307. oldCallback->audioDeviceStopped();
  308. }
  309. }
  310. void stop() override
  311. {
  312. start (nullptr);
  313. }
  314. bool isOpen() override { return deviceIsOpen; }
  315. bool isPlaying() override { return callback != nullptr; }
  316. int getCurrentBitDepth() override { return 32; }
  317. String getLastError() override { return lastError; }
  318. int getXRunCount() const noexcept override { return xruns.load (std::memory_order_relaxed); }
  319. BigInteger getActiveOutputChannels() const override { return activeOutputChannels; }
  320. BigInteger getActiveInputChannels() const override { return activeInputChannels; }
  321. int getOutputLatencyInSamples() override
  322. {
  323. int latency = 0;
  324. for (int i = 0; i < outputPorts.size(); i++)
  325. latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, outputPorts[i]));
  326. return latency;
  327. }
  328. int getInputLatencyInSamples() override
  329. {
  330. int latency = 0;
  331. for (int i = 0; i < inputPorts.size(); i++)
  332. latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, inputPorts[i]));
  333. return latency;
  334. }
  335. String inputName, outputName;
  336. private:
  337. //==============================================================================
  338. class MainThreadDispatcher : private AsyncUpdater
  339. {
  340. public:
  341. explicit MainThreadDispatcher (JackAudioIODevice& device) : ref (device) {}
  342. ~MainThreadDispatcher() override { cancelPendingUpdate(); }
  343. void updateActivePorts()
  344. {
  345. if (MessageManager::getInstance()->isThisTheMessageThread())
  346. handleAsyncUpdate();
  347. else
  348. triggerAsyncUpdate();
  349. }
  350. private:
  351. void handleAsyncUpdate() override { ref.updateActivePorts(); }
  352. JackAudioIODevice& ref;
  353. };
  354. //==============================================================================
  355. void process (const int numSamples)
  356. {
  357. int numActiveInChans = 0, numActiveOutChans = 0;
  358. for (int i = 0; i < totalNumberOfInputChannels; ++i)
  359. {
  360. if (activeInputChannels[i])
  361. if (auto* in = (jack_default_audio_sample_t*) juce::jack_port_get_buffer (inputPorts.getUnchecked (i),
  362. static_cast<jack_nframes_t> (numSamples)))
  363. inChans[numActiveInChans++] = (float*) in;
  364. }
  365. for (int i = 0; i < totalNumberOfOutputChannels; ++i)
  366. {
  367. if (activeOutputChannels[i])
  368. if (auto* out = (jack_default_audio_sample_t*) juce::jack_port_get_buffer (outputPorts.getUnchecked (i),
  369. static_cast<jack_nframes_t> (numSamples)))
  370. outChans[numActiveOutChans++] = (float*) out;
  371. }
  372. const ScopedLock sl (callbackLock);
  373. if (callback != nullptr)
  374. {
  375. if ((numActiveInChans + numActiveOutChans) > 0)
  376. callback->audioDeviceIOCallbackWithContext (const_cast<const float**> (inChans.getData()),
  377. numActiveInChans,
  378. outChans,
  379. numActiveOutChans,
  380. numSamples,
  381. {});
  382. }
  383. else
  384. {
  385. for (int i = 0; i < numActiveOutChans; ++i)
  386. zeromem (outChans[i], static_cast<size_t> (numSamples) * sizeof (float));
  387. }
  388. }
  389. static int processCallback (jack_nframes_t nframes, void* callbackArgument)
  390. {
  391. if (callbackArgument != nullptr)
  392. ((JackAudioIODevice*) callbackArgument)->process (static_cast<int> (nframes));
  393. return 0;
  394. }
  395. static int xrunCallback (void* callbackArgument)
  396. {
  397. if (callbackArgument != nullptr)
  398. ((JackAudioIODevice*) callbackArgument)->xruns++;
  399. return 0;
  400. }
  401. void updateActivePorts()
  402. {
  403. BigInteger newOutputChannels, newInputChannels;
  404. for (int i = 0; i < outputPorts.size(); ++i)
  405. if (juce::jack_port_connected (outputPorts.getUnchecked (i)))
  406. newOutputChannels.setBit (i);
  407. for (int i = 0; i < inputPorts.size(); ++i)
  408. if (juce::jack_port_connected (inputPorts.getUnchecked (i)))
  409. newInputChannels.setBit (i);
  410. if (newOutputChannels != activeOutputChannels
  411. || newInputChannels != activeInputChannels)
  412. {
  413. AudioIODeviceCallback* const oldCallback = callback;
  414. stop();
  415. activeOutputChannels = newOutputChannels;
  416. activeInputChannels = newInputChannels;
  417. if (oldCallback != nullptr)
  418. start (oldCallback);
  419. if (notifyChannelsChanged != nullptr)
  420. notifyChannelsChanged();
  421. }
  422. }
  423. static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg)
  424. {
  425. if (JackAudioIODevice* device = static_cast<JackAudioIODevice*> (arg))
  426. device->mainThreadDispatcher.updateActivePorts();
  427. }
  428. static void threadInitCallback (void* /* callbackArgument */)
  429. {
  430. JUCE_JACK_LOG ("JackAudioIODevice::initialise");
  431. }
  432. static void shutdownCallback (void* callbackArgument)
  433. {
  434. JUCE_JACK_LOG ("JackAudioIODevice::shutdown");
  435. if (JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument)
  436. {
  437. device->client = nullptr;
  438. device->close();
  439. }
  440. }
  441. static void infoShutdownCallback (jack_status_t code, const char* reason, void* arg)
  442. {
  443. jassertquiet (code == 0);
  444. JUCE_JACK_LOG ("Shutting down with message:");
  445. JUCE_JACK_LOG (reason);
  446. ignoreUnused (reason);
  447. shutdownCallback (arg);
  448. }
  449. static void errorCallback (const char* msg)
  450. {
  451. JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (msg));
  452. ignoreUnused (msg);
  453. }
  454. bool deviceIsOpen = false;
  455. jack_client_t* client = nullptr;
  456. String lastError;
  457. AudioIODeviceCallback* callback = nullptr;
  458. CriticalSection callbackLock;
  459. HeapBlock<float*> inChans, outChans;
  460. int totalNumberOfInputChannels = 0;
  461. int totalNumberOfOutputChannels = 0;
  462. Array<jack_port_t*> inputPorts, outputPorts;
  463. BigInteger activeInputChannels, activeOutputChannels;
  464. std::atomic<int> xruns { 0 };
  465. std::function<void()> notifyChannelsChanged;
  466. MainThreadDispatcher mainThreadDispatcher { *this };
  467. };
  468. //==============================================================================
  469. class JackAudioIODeviceType;
  470. class JackAudioIODeviceType : public AudioIODeviceType
  471. {
  472. public:
  473. JackAudioIODeviceType()
  474. : AudioIODeviceType ("JACK")
  475. {}
  476. void scanForDevices()
  477. {
  478. hasScanned = true;
  479. inputNames.clear();
  480. outputNames.clear();
  481. if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so.0", RTLD_LAZY);
  482. if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY);
  483. if (juce_libjackHandle == nullptr) return;
  484. jack_status_t status = {};
  485. // open a dummy client
  486. if (auto* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status))
  487. {
  488. // scan for output devices
  489. for (JackPortIterator i (client, false); i.next();)
  490. if (i.getClientName() != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.getClientName()))
  491. inputNames.add (i.getClientName());
  492. // scan for input devices
  493. for (JackPortIterator i (client, true); i.next();)
  494. if (i.getClientName() != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.getClientName()))
  495. outputNames.add (i.getClientName());
  496. juce::jack_client_close (client);
  497. }
  498. else
  499. {
  500. JUCE_JACK_LOG_STATUS (status);
  501. }
  502. }
  503. StringArray getDeviceNames (bool wantInputNames) const
  504. {
  505. jassert (hasScanned); // need to call scanForDevices() before doing this
  506. return wantInputNames ? inputNames : outputNames;
  507. }
  508. int getDefaultDeviceIndex (bool /* forInput */) const
  509. {
  510. jassert (hasScanned); // need to call scanForDevices() before doing this
  511. return 0;
  512. }
  513. bool hasSeparateInputsAndOutputs() const { return true; }
  514. int getIndexOfDevice (AudioIODevice* device, bool asInput) const
  515. {
  516. jassert (hasScanned); // need to call scanForDevices() before doing this
  517. if (JackAudioIODevice* d = dynamic_cast<JackAudioIODevice*> (device))
  518. return asInput ? inputNames.indexOf (d->inputName)
  519. : outputNames.indexOf (d->outputName);
  520. return -1;
  521. }
  522. AudioIODevice* createDevice (const String& outputDeviceName,
  523. const String& inputDeviceName)
  524. {
  525. jassert (hasScanned); // need to call scanForDevices() before doing this
  526. const int inputIndex = inputNames.indexOf (inputDeviceName);
  527. const int outputIndex = outputNames.indexOf (outputDeviceName);
  528. if (inputIndex >= 0 || outputIndex >= 0)
  529. return new JackAudioIODevice (inputDeviceName, outputDeviceName,
  530. [this] { callDeviceChangeListeners(); });
  531. return nullptr;
  532. }
  533. private:
  534. StringArray inputNames, outputNames;
  535. bool hasScanned = false;
  536. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType)
  537. };
  538. } // namespace juce