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