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