OpenShot Audio Library | OpenShotAudio 0.4.0
Loading...
Searching...
No Matches
juce_AudioDeviceManager.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - 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
23namespace juce
24{
25
26template <typename Setup>
27static auto getSetupInfo (Setup& s, bool isInput)
28{
29 struct SetupInfo
30 {
31 // double brackets so that we get the expression type, i.e. a (possibly const) reference
32 decltype ((s.inputDeviceName)) name;
33 decltype ((s.inputChannels)) channels;
34 decltype ((s.useDefaultInputChannels)) useDefault;
35 };
36
37 return isInput ? SetupInfo { s.inputDeviceName, s.inputChannels, s.useDefaultInputChannels }
38 : SetupInfo { s.outputDeviceName, s.outputChannels, s.useDefaultOutputChannels };
39}
40
41static auto tie (const AudioDeviceManager::AudioDeviceSetup& s)
42{
43 return std::tie (s.outputDeviceName,
44 s.inputDeviceName,
45 s.sampleRate,
46 s.bufferSize,
47 s.inputChannels,
48 s.useDefaultInputChannels,
49 s.outputChannels,
50 s.useDefaultOutputChannels);
51}
52
53bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const
54{
55 return tie (*this) == tie (other);
56}
57
58bool AudioDeviceManager::AudioDeviceSetup::operator!= (const AudioDeviceManager::AudioDeviceSetup& other) const
59{
60 return tie (*this) != tie (other);
61}
62
63//==============================================================================
64class AudioDeviceManager::CallbackHandler final : public AudioIODeviceCallback,
65 public MidiInputCallback,
66 public AudioIODeviceType::Listener
67{
68public:
69 CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {}
70
71private:
72 void audioDeviceIOCallbackWithContext (const float* const* ins,
73 int numIns,
74 float* const* outs,
75 int numOuts,
76 int numSamples,
77 const AudioIODeviceCallbackContext& context) override
78 {
79 owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples, context);
80 }
81
82 void audioDeviceAboutToStart (AudioIODevice* device) override
83 {
84 owner.audioDeviceAboutToStartInt (device);
85 }
86
87 void audioDeviceStopped() override
88 {
89 owner.audioDeviceStoppedInt();
90 }
91
92 void audioDeviceError (const String& message) override
93 {
94 owner.audioDeviceErrorInt (message);
95 }
96
97 void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override
98 {
99 owner.handleIncomingMidiMessageInt (source, message);
100 }
101
102 void audioDeviceListChanged() override
103 {
104 owner.audioDeviceListChanged();
105 }
106
107 AudioDeviceManager& owner;
108
109 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
110};
111
112//==============================================================================
114{
115 callbackHandler.reset (new CallbackHandler (*this));
116}
117
119{
120 currentAudioDevice.reset();
121 defaultMidiOutput.reset();
122}
123
124//==============================================================================
125void AudioDeviceManager::createDeviceTypesIfNeeded()
126{
127 if (availableDeviceTypes.size() == 0)
128 {
131
132 for (auto* t : types)
133 addAudioDeviceType (std::unique_ptr<AudioIODeviceType> (t));
134
135 types.clear (false);
136
137 for (auto* type : availableDeviceTypes)
138 type->scanForDevices();
139
140 pickCurrentDeviceTypeWithDevices();
141 }
142}
143
144void AudioDeviceManager::pickCurrentDeviceTypeWithDevices()
145{
146 const auto deviceTypeHasDevices = [] (const AudioIODeviceType* ptr)
147 {
148 return ! ptr->getDeviceNames (true) .isEmpty()
149 || ! ptr->getDeviceNames (false).isEmpty();
150 };
151
152 if (auto* type = findType (currentDeviceType))
153 if (deviceTypeHasDevices (type))
154 return;
155
156 const auto iter = std::find_if (availableDeviceTypes.begin(),
157 availableDeviceTypes.end(),
158 deviceTypeHasDevices);
159
160 if (iter != availableDeviceTypes.end())
161 currentDeviceType = (*iter)->getTypeName();
162}
163
165{
166 scanDevicesIfNeeded();
167 return availableDeviceTypes;
168}
169
170void AudioDeviceManager::updateCurrentSetup()
171{
172 if (currentAudioDevice != nullptr)
173 {
174 currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
175 currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
176 currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
177 currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
178 }
179}
180
181void AudioDeviceManager::audioDeviceListChanged()
182{
183 if (currentAudioDevice != nullptr)
184 {
185 auto currentDeviceStillAvailable = [&]
186 {
187 auto currentTypeName = currentAudioDevice->getTypeName();
188 auto currentDeviceName = currentAudioDevice->getName();
189
190 for (auto* deviceType : availableDeviceTypes)
191 {
192 if (currentTypeName == deviceType->getTypeName())
193 {
194 for (auto& deviceName : deviceType->getDeviceNames (true))
195 if (currentDeviceName == deviceName)
196 return true;
197
198 for (auto& deviceName : deviceType->getDeviceNames (false))
199 if (currentDeviceName == deviceName)
200 return true;
201 }
202 }
203
204 return false;
205 }();
206
207 if (! currentDeviceStillAvailable)
208 {
210
211 if (auto e = createStateXml())
212 initialiseFromXML (*e, true, preferredDeviceName, &currentSetup);
213 else
214 initialiseDefault (preferredDeviceName, &currentSetup);
215 }
216
217 updateCurrentSetup();
218 }
219
221}
222
223void AudioDeviceManager::midiDeviceListChanged()
224{
225 openLastRequestedMidiDevices (midiDeviceInfosFromXml, defaultMidiOutputDeviceInfo);
227}
228
229//==============================================================================
230static void addIfNotNull (OwnedArray<AudioIODeviceType>& list, AudioIODeviceType* const device)
231{
232 if (device != nullptr)
233 list.add (device);
234}
235
237{
238 addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::shared));
239 addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::exclusive));
240 addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::sharedLowLatency));
251}
252
253void AudioDeviceManager::addAudioDeviceType (std::unique_ptr<AudioIODeviceType> newDeviceType)
254{
255 if (newDeviceType != nullptr)
256 {
257 jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size());
258
259 availableDeviceTypes.add (newDeviceType.release());
260 lastDeviceTypeConfigs.add (new AudioDeviceSetup());
261
262 availableDeviceTypes.getLast()->addListener (callbackHandler.get());
263 }
264}
265
267{
268 if (deviceTypeToRemove != nullptr)
269 {
270 jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size());
271
272 auto index = availableDeviceTypes.indexOf (deviceTypeToRemove);
273
274 if (auto removed = std::unique_ptr<AudioIODeviceType> (availableDeviceTypes.removeAndReturn (index)))
275 {
276 removed->removeListener (callbackHandler.get());
277 lastDeviceTypeConfigs.remove (index, true);
278 }
279 }
280}
281
282static bool deviceListContains (AudioIODeviceType* type, bool isInput, const String& name)
283{
284 for (auto& deviceName : type->getDeviceNames (isInput))
285 if (deviceName.trim().equalsIgnoreCase (name.trim()))
286 return true;
287
288 return false;
289}
290
291//==============================================================================
292String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
293 const int numOutputChannelsNeeded,
294 const XmlElement* const xml,
295 const bool selectDefaultDeviceOnFailure,
296 const String& preferredDefaultDeviceName,
297 const AudioDeviceSetup* preferredSetupOptions)
298{
299 scanDevicesIfNeeded();
300 pickCurrentDeviceTypeWithDevices();
301
302 numInputChansNeeded = numInputChannelsNeeded;
303 numOutputChansNeeded = numOutputChannelsNeeded;
304 preferredDeviceName = preferredDefaultDeviceName;
305
306 if (xml != nullptr && xml->hasTagName ("DEVICESETUP"))
307 return initialiseFromXML (*xml, selectDefaultDeviceOnFailure,
308 preferredDeviceName, preferredSetupOptions);
309
310 return initialiseDefault (preferredDeviceName, preferredSetupOptions);
311}
312
313String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName,
314 const AudioDeviceSetup* preferredSetupOptions)
315{
316 AudioDeviceSetup setup;
317
318 if (preferredSetupOptions != nullptr)
319 {
320 setup = *preferredSetupOptions;
321 }
322 else if (preferredDefaultDeviceName.isNotEmpty())
323 {
324 const auto nameMatches = [&preferredDefaultDeviceName] (const String& name)
325 {
326 return name.matchesWildcard (preferredDefaultDeviceName, true);
327 };
328
329 struct WildcardMatch
330 {
331 String value;
332 bool successful;
333 };
334
335 const auto getWildcardMatch = [&nameMatches] (const StringArray& names)
336 {
337 const auto iter = std::find_if (names.begin(), names.end(), nameMatches);
338 return WildcardMatch { iter != names.end() ? *iter : String(), iter != names.end() };
339 };
340
341 struct WildcardMatches
342 {
343 WildcardMatch input, output;
344 };
345
346 const auto getMatchesForType = [&getWildcardMatch] (const AudioIODeviceType* type)
347 {
348 return WildcardMatches { getWildcardMatch (type->getDeviceNames (true)),
349 getWildcardMatch (type->getDeviceNames (false)) };
350 };
351
352 struct SearchResult
353 {
354 String type, input, output;
355 };
356
357 const auto result = [&]
358 {
359 // First, look for a device type with an input and output which matches the preferred name
360 for (auto* type : availableDeviceTypes)
361 {
362 const auto matches = getMatchesForType (type);
363
364 if (matches.input.successful && matches.output.successful)
365 return SearchResult { type->getTypeName(), matches.input.value, matches.output.value };
366 }
367
368 // No device type has matching ins and outs, so fall back to a device where either the
369 // input or output match
370 for (auto* type : availableDeviceTypes)
371 {
372 const auto matches = getMatchesForType (type);
373
374 if (matches.input.successful || matches.output.successful)
375 return SearchResult { type->getTypeName(), matches.input.value, matches.output.value };
376 }
377
378 // No devices match the query, so just use the default devices from the current type
379 return SearchResult { currentDeviceType, {}, {} };
380 }();
381
382 currentDeviceType = result.type;
383 setup.inputDeviceName = result.input;
384 setup.outputDeviceName = result.output;
385 }
386
387 insertDefaultDeviceNames (setup);
388 return setAudioDeviceSetup (setup, false);
389}
390
391String AudioDeviceManager::initialiseFromXML (const XmlElement& xml,
392 bool selectDefaultDeviceOnFailure,
393 const String& preferredDefaultDeviceName,
394 const AudioDeviceSetup* preferredSetupOptions)
395{
396 lastExplicitSettings.reset (new XmlElement (xml));
397
398 String error;
399 AudioDeviceSetup setup;
400
401 if (preferredSetupOptions != nullptr)
402 setup = *preferredSetupOptions;
403
404 if (xml.getStringAttribute ("audioDeviceName").isNotEmpty())
405 {
406 setup.inputDeviceName = setup.outputDeviceName
407 = xml.getStringAttribute ("audioDeviceName");
408 }
409 else
410 {
411 setup.inputDeviceName = xml.getStringAttribute ("audioInputDeviceName");
412 setup.outputDeviceName = xml.getStringAttribute ("audioOutputDeviceName");
413 }
414
415 currentDeviceType = xml.getStringAttribute ("deviceType");
416
417 if (findType (currentDeviceType) == nullptr)
418 {
419 if (auto* type = findType (setup.inputDeviceName, setup.outputDeviceName))
420 currentDeviceType = type->getTypeName();
421 else if (auto* firstType = availableDeviceTypes.getFirst())
422 currentDeviceType = firstType->getTypeName();
423 }
424
425 setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize", setup.bufferSize);
426 setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate", setup.sampleRate);
427
428 setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans", "11"), 2);
429 setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2);
430
431 setup.useDefaultInputChannels = ! xml.hasAttribute ("audioDeviceInChans");
432 setup.useDefaultOutputChannels = ! xml.hasAttribute ("audioDeviceOutChans");
433
434 error = setAudioDeviceSetup (setup, true);
435
436 if (error.isNotEmpty() && selectDefaultDeviceOnFailure)
437 error = initialise (numInputChansNeeded, numOutputChansNeeded, nullptr, false, preferredDefaultDeviceName);
438
439 enabledMidiInputs.clear();
440
441 const auto midiInputs = [&]
442 {
443 Array<MidiDeviceInfo> result;
444
445 for (auto* c : xml.getChildWithTagNameIterator ("MIDIINPUT"))
446 result.add ({ c->getStringAttribute ("name"), c->getStringAttribute ("identifier") });
447
448 return result;
449 }();
450
451 const MidiDeviceInfo defaultOutputDeviceInfo (xml.getStringAttribute ("defaultMidiOutput"),
452 xml.getStringAttribute ("defaultMidiOutputDevice"));
453
454 openLastRequestedMidiDevices (midiInputs, defaultOutputDeviceInfo);
455
456 return error;
457}
458
459void AudioDeviceManager::openLastRequestedMidiDevices (const Array<MidiDeviceInfo>& desiredInputs, const MidiDeviceInfo& defaultOutput)
460{
461 const auto openDeviceIfAvailable = [&] (const Array<MidiDeviceInfo>& devices,
462 const MidiDeviceInfo& deviceToOpen,
463 auto&& doOpen)
464 {
465 const auto iterWithMatchingIdentifier = std::find_if (devices.begin(), devices.end(), [&] (const auto& x)
466 {
467 return x.identifier == deviceToOpen.identifier;
468 });
469
470 if (iterWithMatchingIdentifier != devices.end())
471 {
472 doOpen (deviceToOpen.identifier);
473 return;
474 }
475
476 const auto iterWithMatchingName = std::find_if (devices.begin(), devices.end(), [&] (const auto& x)
477 {
478 return x.name == deviceToOpen.name;
479 });
480
481 if (iterWithMatchingName != devices.end())
482 doOpen (iterWithMatchingName->identifier);
483 };
484
485 midiDeviceInfosFromXml = desiredInputs;
486
487 const auto inputs = MidiInput::getAvailableDevices();
488
489 for (const auto& info : midiDeviceInfosFromXml)
490 openDeviceIfAvailable (inputs, info, [&] (const auto identifier) { setMidiInputDeviceEnabled (identifier, true); });
491
492 const auto outputs = MidiOutput::getAvailableDevices();
493
494 openDeviceIfAvailable (outputs, defaultOutput, [&] (const auto identifier) { setDefaultMidiOutputDevice (identifier); });
495}
496
498 int numOutputChannelsNeeded)
499{
500 lastExplicitSettings.reset();
501
502 return initialise (numInputChannelsNeeded, numOutputChannelsNeeded,
503 nullptr, false, {}, nullptr);
504}
505
506void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const
507{
508 enum class Direction { out, in };
509
510 if (auto* type = getCurrentDeviceTypeObject())
511 {
512 // We avoid selecting a device pair that doesn't share a matching sample rate, if possible.
513 // If not, other parts of the AudioDeviceManager and AudioIODevice classes should generate
514 // an appropriate error message when opening or starting these devices.
515 const auto getDevicesToTestForMatchingSampleRate = [&setup, type, this] (Direction dir)
516 {
517 const auto isInput = dir == Direction::in;
518 const auto info = getSetupInfo (setup, isInput);
519
520 if (! info.name.isEmpty())
521 return StringArray { info.name };
522
523 const auto numChannelsNeeded = isInput ? numInputChansNeeded : numOutputChansNeeded;
524 auto deviceNames = numChannelsNeeded > 0 ? type->getDeviceNames (isInput) : StringArray {};
525 deviceNames.move (type->getDefaultDeviceIndex (isInput), 0);
526
527 return deviceNames;
528 };
529
530 std::map<std::pair<Direction, String>, Array<double>> sampleRatesCache;
531
532 const auto getSupportedSampleRates = [&sampleRatesCache, type] (Direction dir, const String& deviceName)
533 {
534 const auto key = std::make_pair (dir, deviceName);
535
536 auto& entry = [&]() -> auto&
537 {
538 auto it = sampleRatesCache.find (key);
539
540 if (it != sampleRatesCache.end())
541 return it->second;
542
543 auto& elem = sampleRatesCache[key];
544 auto tempDevice = rawToUniquePtr (type->createDevice ((dir == Direction::in) ? "" : deviceName,
545 (dir == Direction::in) ? deviceName : ""));
546 if (tempDevice != nullptr)
547 elem = tempDevice->getAvailableSampleRates();
548
549 return elem;
550 }();
551
552 return entry;
553 };
554
555 const auto validate = [&getSupportedSampleRates] (const String& outputDeviceName, const String& inputDeviceName)
556 {
557 jassert (! outputDeviceName.isEmpty() && ! inputDeviceName.isEmpty());
558
559 const auto outputSampleRates = getSupportedSampleRates (Direction::out, outputDeviceName);
560 const auto inputSampleRates = getSupportedSampleRates (Direction::in, inputDeviceName);
561
562 return std::any_of (inputSampleRates.begin(),
563 inputSampleRates.end(),
564 [&] (auto inputSampleRate) { return outputSampleRates.contains (inputSampleRate); });
565 };
566
567 auto outputsToTest = getDevicesToTestForMatchingSampleRate (Direction::out);
568 auto inputsToTest = getDevicesToTestForMatchingSampleRate (Direction::in);
569
570 // We set default device names, so in case no in-out pair passes the validation, we still
571 // produce the same result as before
572 if (setup.outputDeviceName.isEmpty() && ! outputsToTest.isEmpty())
573 setup.outputDeviceName = outputsToTest[0];
574
575 if (setup.inputDeviceName.isEmpty() && ! inputsToTest.isEmpty())
576 setup.inputDeviceName = inputsToTest[0];
577
578 // We check all possible in-out pairs until the first validation pass. If no pair passes we
579 // leave the setup unchanged.
580 for (const auto& out : outputsToTest)
581 {
582 for (const auto& in : inputsToTest)
583 {
584 if (validate (out, in))
585 {
586 setup.outputDeviceName = out;
587 setup.inputDeviceName = in;
588
589 return;
590 }
591 }
592 }
593 }
594}
595
596std::unique_ptr<XmlElement> AudioDeviceManager::createStateXml() const
597{
598 if (lastExplicitSettings != nullptr)
599 return std::make_unique<XmlElement> (*lastExplicitSettings);
600
601 return {};
602}
603
604//==============================================================================
605void AudioDeviceManager::scanDevicesIfNeeded()
606{
607 if (listNeedsScanning)
608 {
609 listNeedsScanning = false;
610
611 createDeviceTypesIfNeeded();
612
613 for (auto* type : availableDeviceTypes)
614 type->scanForDevices();
615 }
616}
617
618AudioIODeviceType* AudioDeviceManager::findType (const String& typeName)
619{
620 scanDevicesIfNeeded();
621
622 for (auto* type : availableDeviceTypes)
623 if (type->getTypeName() == typeName)
624 return type;
625
626 return {};
627}
628
629AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const String& outputName)
630{
631 scanDevicesIfNeeded();
632
633 for (auto* type : availableDeviceTypes)
634 if ((inputName.isNotEmpty() && deviceListContains (type, true, inputName))
635 || (outputName.isNotEmpty() && deviceListContains (type, false, outputName)))
636 return type;
637
638 return {};
639}
640
645
647{
648 setup = currentSetup;
649}
650
651void AudioDeviceManager::deleteCurrentDevice()
652{
653 currentAudioDevice.reset();
654 currentSetup.inputDeviceName.clear();
655 currentSetup.outputDeviceName.clear();
656}
657
658void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, bool treatAsChosenDevice)
659{
660 for (int i = 0; i < availableDeviceTypes.size(); ++i)
661 {
662 if (availableDeviceTypes.getUnchecked (i)->getTypeName() == type
663 && currentDeviceType != type)
664 {
665 if (currentAudioDevice != nullptr)
666 {
668 Thread::sleep (1500); // allow a moment for OS devices to sort themselves out, to help
669 // avoid things like DirectSound/ASIO clashes
670 }
671
672 currentDeviceType = type;
673
674 AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked (i));
675 insertDefaultDeviceNames (s);
676
677 setAudioDeviceSetup (s, treatAsChosenDevice);
678
680 break;
681 }
682 }
683}
684
686{
687 return currentAudioDevice != nullptr ? currentAudioDevice->getWorkgroup() : AudioWorkgroup{};
688}
689
691{
692 for (auto* type : availableDeviceTypes)
693 if (type->getTypeName() == currentDeviceType)
694 return type;
695
696 return availableDeviceTypes.getFirst();
697}
698
699static void updateSetupChannels (AudioDeviceManager::AudioDeviceSetup& setup, int defaultNumIns, int defaultNumOuts)
700{
701 auto updateChannels = [] (const String& deviceName, BigInteger& channels, int defaultNumChannels)
702 {
703 if (deviceName.isEmpty())
704 {
705 channels.clear();
706 }
707 else if (defaultNumChannels != -1)
708 {
709 channels.clear();
710 channels.setRange (0, defaultNumChannels, true);
711 }
712 };
713
714 updateChannels (setup.inputDeviceName, setup.inputChannels, setup.useDefaultInputChannels ? defaultNumIns : -1);
715 updateChannels (setup.outputDeviceName, setup.outputChannels, setup.useDefaultOutputChannels ? defaultNumOuts : -1);
716}
717
719 bool treatAsChosenDevice)
720{
721 jassert (&newSetup != &currentSetup); // this will have no effect
722
723 if (newSetup != currentSetup)
725 else if (currentAudioDevice != nullptr)
726 return {};
727
728 stopDevice();
729
730 if (getCurrentDeviceTypeObject() == nullptr
731 || (newSetup.inputDeviceName.isEmpty() && newSetup.outputDeviceName.isEmpty()))
732 {
733 deleteCurrentDevice();
734
735 if (treatAsChosenDevice)
736 updateXml();
737
738 return {};
739 }
740
741 String error;
742
743 const auto needsNewDevice = currentSetup.inputDeviceName != newSetup.inputDeviceName
744 || currentSetup.outputDeviceName != newSetup.outputDeviceName
745 || currentAudioDevice == nullptr;
746
747 if (needsNewDevice)
748 {
749 deleteCurrentDevice();
750 scanDevicesIfNeeded();
751
752 auto* type = getCurrentDeviceTypeObject();
753
754 for (const auto isInput : { false, true })
755 {
756 const auto name = getSetupInfo (newSetup, isInput).name;
757
758 if (name.isNotEmpty() && ! deviceListContains (type, isInput, name))
759 return "No such device: " + name;
760 }
761
762 currentAudioDevice.reset (type->createDevice (newSetup.outputDeviceName, newSetup.inputDeviceName));
763
764 if (currentAudioDevice == nullptr)
765 error = "Can't open the audio device!\n\n"
766 "This may be because another application is currently using the same device - "
767 "if so, you should close any other applications and try again!";
768 else
769 error = currentAudioDevice->getLastError();
770
771 if (error.isNotEmpty())
772 {
773 deleteCurrentDevice();
774 return error;
775 }
776 }
777
778 currentSetup = newSetup;
779
780 if (! currentSetup.useDefaultInputChannels) numInputChansNeeded = currentSetup.inputChannels.countNumberOfSetBits();
781 if (! currentSetup.useDefaultOutputChannels) numOutputChansNeeded = currentSetup.outputChannels.countNumberOfSetBits();
782
783 updateSetupChannels (currentSetup, numInputChansNeeded, numOutputChansNeeded);
784
785 if (currentSetup.inputChannels.isZero() && currentSetup.outputChannels.isZero())
786 {
787 if (treatAsChosenDevice)
788 updateXml();
789
790 return {};
791 }
792
793 currentSetup.sampleRate = chooseBestSampleRate (currentSetup.sampleRate);
794 currentSetup.bufferSize = chooseBestBufferSize (currentSetup.bufferSize);
795
796 error = currentAudioDevice->open (currentSetup.inputChannels,
797 currentSetup.outputChannels,
798 currentSetup.sampleRate,
799 currentSetup.bufferSize);
800
801 if (error.isEmpty())
802 {
803 currentDeviceType = currentAudioDevice->getTypeName();
804
805 currentAudioDevice->start (callbackHandler.get());
806
807 error = currentAudioDevice->getLastError();
808 }
809
810 if (error.isEmpty())
811 {
812 updateCurrentSetup();
813
814 for (int i = 0; i < availableDeviceTypes.size(); ++i)
815 if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType)
816 *(lastDeviceTypeConfigs.getUnchecked (i)) = currentSetup;
817
818 if (treatAsChosenDevice)
819 updateXml();
820 }
821 else
822 {
823 deleteCurrentDevice();
824 }
825
826 return error;
827}
828
829double AudioDeviceManager::chooseBestSampleRate (double rate) const
830{
831 jassert (currentAudioDevice != nullptr);
832
833 auto rates = currentAudioDevice->getAvailableSampleRates();
834
835 if (rate > 0 && rates.contains (rate))
836 return rate;
837
838 rate = currentAudioDevice->getCurrentSampleRate();
839
840 if (rate > 0 && rates.contains (rate))
841 return rate;
842
843 double lowestAbove44 = 0.0;
844
845 for (int i = rates.size(); --i >= 0;)
846 {
847 auto sr = rates[i];
848
849 if (sr >= 44100.0 && (lowestAbove44 < 1.0 || sr < lowestAbove44))
850 lowestAbove44 = sr;
851 }
852
853 if (lowestAbove44 > 0.0)
854 return lowestAbove44;
855
856 return rates[0];
857}
858
859int AudioDeviceManager::chooseBestBufferSize (int bufferSize) const
860{
861 jassert (currentAudioDevice != nullptr);
862
863 if (bufferSize > 0 && currentAudioDevice->getAvailableBufferSizes().contains (bufferSize))
864 return bufferSize;
865
866 return currentAudioDevice->getDefaultBufferSize();
867}
868
869void AudioDeviceManager::stopDevice()
870{
871 if (currentAudioDevice != nullptr)
872 currentAudioDevice->stop();
873
874 testSound.reset();
875}
876
878{
879 stopDevice();
880 currentAudioDevice.reset();
881 loadMeasurer.reset();
882}
883
885{
886 if (currentAudioDevice == nullptr)
887 {
888 if (currentSetup.inputDeviceName.isEmpty()
889 && currentSetup.outputDeviceName.isEmpty())
890 {
891 // This method will only reload the last device that was running
892 // before closeAudioDevice() was called - you need to actually open
893 // one first, with setAudioDeviceSetup().
894 jassertfalse;
895 return;
896 }
897
898 AudioDeviceSetup s (currentSetup);
899 setAudioDeviceSetup (s, false);
900 }
901}
902
903void AudioDeviceManager::updateXml()
904{
905 lastExplicitSettings.reset (new XmlElement ("DEVICESETUP"));
906
907 lastExplicitSettings->setAttribute ("deviceType", currentDeviceType);
908 lastExplicitSettings->setAttribute ("audioOutputDeviceName", currentSetup.outputDeviceName);
909 lastExplicitSettings->setAttribute ("audioInputDeviceName", currentSetup.inputDeviceName);
910
911 if (currentAudioDevice != nullptr)
912 {
913 lastExplicitSettings->setAttribute ("audioDeviceRate", currentAudioDevice->getCurrentSampleRate());
914
915 if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples())
916 lastExplicitSettings->setAttribute ("audioDeviceBufferSize", currentAudioDevice->getCurrentBufferSizeSamples());
917
918 if (! currentSetup.useDefaultInputChannels)
919 lastExplicitSettings->setAttribute ("audioDeviceInChans", currentSetup.inputChannels.toString (2));
920
921 if (! currentSetup.useDefaultOutputChannels)
922 lastExplicitSettings->setAttribute ("audioDeviceOutChans", currentSetup.outputChannels.toString (2));
923 }
924
925 for (auto& input : enabledMidiInputs)
926 {
927 auto* child = lastExplicitSettings->createNewChildElement ("MIDIINPUT");
928
929 child->setAttribute ("name", input->getName());
930 child->setAttribute ("identifier", input->getIdentifier());
931 }
932
933 if (midiDeviceInfosFromXml.size() > 0)
934 {
935 // Add any midi devices that have been enabled before, but which aren't currently
936 // open because the device has been disconnected.
937 auto availableMidiDevices = MidiInput::getAvailableDevices();
938
939 for (auto& d : midiDeviceInfosFromXml)
940 {
941 if (! availableMidiDevices.contains (d))
942 {
943 auto* child = lastExplicitSettings->createNewChildElement ("MIDIINPUT");
944
945 child->setAttribute ("name", d.name);
946 child->setAttribute ("identifier", d.identifier);
947 }
948 }
949 }
950
951 if (defaultMidiOutputDeviceInfo != MidiDeviceInfo())
952 {
953 lastExplicitSettings->setAttribute ("defaultMidiOutput", defaultMidiOutputDeviceInfo.name);
954 lastExplicitSettings->setAttribute ("defaultMidiOutputDevice", defaultMidiOutputDeviceInfo.identifier);
955 }
956}
957
958//==============================================================================
960{
961 {
962 const ScopedLock sl (audioCallbackLock);
963
964 if (callbacks.contains (newCallback))
965 return;
966 }
967
968 if (currentAudioDevice != nullptr && newCallback != nullptr)
969 newCallback->audioDeviceAboutToStart (currentAudioDevice.get());
970
971 const ScopedLock sl (audioCallbackLock);
972 callbacks.add (newCallback);
973}
974
976{
977 if (callbackToRemove != nullptr)
978 {
979 bool needsDeinitialising = currentAudioDevice != nullptr;
980
981 {
982 const ScopedLock sl (audioCallbackLock);
983
984 needsDeinitialising = needsDeinitialising && callbacks.contains (callbackToRemove);
985 callbacks.removeFirstMatchingValue (callbackToRemove);
986 }
987
988 if (needsDeinitialising)
989 callbackToRemove->audioDeviceStopped();
990 }
991}
992
993void AudioDeviceManager::audioDeviceIOCallbackInt (const float* const* inputChannelData,
994 int numInputChannels,
995 float* const* outputChannelData,
996 int numOutputChannels,
997 int numSamples,
998 const AudioIODeviceCallbackContext& context)
999{
1000 const ScopedLock sl (audioCallbackLock);
1001
1002 inputLevelGetter->updateLevel (inputChannelData, numInputChannels, numSamples);
1003
1004 if (callbacks.size() > 0)
1005 {
1006 AudioProcessLoadMeasurer::ScopedTimer timer (loadMeasurer, numSamples);
1007
1008 tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true);
1009
1010 callbacks.getUnchecked (0)->audioDeviceIOCallbackWithContext (inputChannelData,
1011 numInputChannels,
1012 outputChannelData,
1013 numOutputChannels,
1014 numSamples,
1015 context);
1016
1017 auto* const* tempChans = tempBuffer.getArrayOfWritePointers();
1018
1019 for (int i = callbacks.size(); --i > 0;)
1020 {
1021 callbacks.getUnchecked (i)->audioDeviceIOCallbackWithContext (inputChannelData,
1022 numInputChannels,
1023 tempChans,
1024 numOutputChannels,
1025 numSamples,
1026 context);
1027
1028 for (int chan = 0; chan < numOutputChannels; ++chan)
1029 {
1030 if (auto* src = tempChans [chan])
1031 if (auto* dst = outputChannelData [chan])
1032 for (int j = 0; j < numSamples; ++j)
1033 dst[j] += src[j];
1034 }
1035 }
1036 }
1037 else
1038 {
1039 for (int i = 0; i < numOutputChannels; ++i)
1040 zeromem (outputChannelData[i], (size_t) numSamples * sizeof (float));
1041 }
1042
1043 if (testSound != nullptr)
1044 {
1045 auto numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition);
1046 auto* src = testSound->getReadPointer (0, testSoundPosition);
1047
1048 for (int i = 0; i < numOutputChannels; ++i)
1049 if (auto* dst = outputChannelData [i])
1050 for (int j = 0; j < numSamps; ++j)
1051 dst[j] += src[j];
1052
1053 testSoundPosition += numSamps;
1054
1055 if (testSoundPosition >= testSound->getNumSamples())
1056 testSound.reset();
1057 }
1058
1059 outputLevelGetter->updateLevel (outputChannelData, numOutputChannels, numSamples);
1060}
1061
1062void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device)
1063{
1064 loadMeasurer.reset (device->getCurrentSampleRate(),
1065 device->getCurrentBufferSizeSamples());
1066
1067 updateCurrentSetup();
1068
1069 {
1070 const ScopedLock sl (audioCallbackLock);
1071
1072 for (int i = callbacks.size(); --i >= 0;)
1073 callbacks.getUnchecked (i)->audioDeviceAboutToStart (device);
1074 }
1075
1077}
1078
1079void AudioDeviceManager::audioDeviceStoppedInt()
1080{
1082
1083 const ScopedLock sl (audioCallbackLock);
1084
1085 loadMeasurer.reset();
1086
1087 for (int i = callbacks.size(); --i >= 0;)
1088 callbacks.getUnchecked (i)->audioDeviceStopped();
1089}
1090
1091void AudioDeviceManager::audioDeviceErrorInt (const String& message)
1092{
1093 const ScopedLock sl (audioCallbackLock);
1094
1095 for (int i = callbacks.size(); --i >= 0;)
1096 callbacks.getUnchecked (i)->audioDeviceError (message);
1097}
1098
1100{
1101 return loadMeasurer.getLoadAsProportion();
1102}
1103
1104//==============================================================================
1105void AudioDeviceManager::setMidiInputDeviceEnabled (const String& identifier, bool enabled)
1106{
1107 if (enabled != isMidiInputDeviceEnabled (identifier))
1108 {
1109 if (enabled)
1110 {
1111 if (auto midiIn = MidiInput::openDevice (identifier, callbackHandler.get()))
1112 {
1113 enabledMidiInputs.push_back (std::move (midiIn));
1114 enabledMidiInputs.back()->start();
1115 }
1116 }
1117 else
1118 {
1119 auto removePredicate = [identifier] (const std::unique_ptr<MidiInput>& in) { return in->getIdentifier() == identifier; };
1120 enabledMidiInputs.erase (std::remove_if (std::begin (enabledMidiInputs), std::end (enabledMidiInputs), removePredicate),
1121 std::end (enabledMidiInputs));
1122 }
1123
1124 updateXml();
1126 }
1127}
1128
1130{
1131 for (auto& mi : enabledMidiInputs)
1132 if (mi->getIdentifier() == identifier)
1133 return true;
1134
1135 return false;
1136}
1137
1139{
1140 removeMidiInputDeviceCallback (identifier, callbackToAdd);
1141
1142 if (identifier.isEmpty() || isMidiInputDeviceEnabled (identifier))
1143 {
1144 const ScopedLock sl (midiCallbackLock);
1145 midiCallbacks.add ({ identifier, callbackToAdd });
1146 }
1147}
1148
1150{
1151 for (int i = midiCallbacks.size(); --i >= 0;)
1152 {
1153 auto& mc = midiCallbacks.getReference (i);
1154
1155 if (mc.callback == callbackToRemove && mc.deviceIdentifier == identifier)
1156 {
1157 const ScopedLock sl (midiCallbackLock);
1158 midiCallbacks.remove (i);
1159 }
1160 }
1161}
1162
1163void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const MidiMessage& message)
1164{
1165 if (! message.isActiveSense())
1166 {
1167 const ScopedLock sl (midiCallbackLock);
1168
1169 for (auto& mc : midiCallbacks)
1170 if (mc.deviceIdentifier.isEmpty() || mc.deviceIdentifier == source->getIdentifier())
1171 mc.callback->handleIncomingMidiMessage (source, message);
1172 }
1173}
1174
1175//==============================================================================
1177{
1178 if (defaultMidiOutputDeviceInfo.identifier != identifier)
1179 {
1180 std::unique_ptr<MidiOutput> oldMidiPort;
1181 Array<AudioIODeviceCallback*> oldCallbacks;
1182
1183 {
1184 const ScopedLock sl (audioCallbackLock);
1185 oldCallbacks.swapWith (callbacks);
1186 }
1187
1188 if (currentAudioDevice != nullptr)
1189 for (int i = oldCallbacks.size(); --i >= 0;)
1190 oldCallbacks.getUnchecked (i)->audioDeviceStopped();
1191
1192 std::swap (oldMidiPort, defaultMidiOutput);
1193
1194 if (identifier.isNotEmpty())
1195 defaultMidiOutput = MidiOutput::openDevice (identifier);
1196
1197 if (defaultMidiOutput != nullptr)
1198 defaultMidiOutputDeviceInfo = defaultMidiOutput->getDeviceInfo();
1199 else
1200 defaultMidiOutputDeviceInfo = {};
1201
1202 if (currentAudioDevice != nullptr)
1203 for (auto* c : oldCallbacks)
1204 c->audioDeviceAboutToStart (currentAudioDevice.get());
1205
1206 {
1207 const ScopedLock sl (audioCallbackLock);
1208 oldCallbacks.swapWith (callbacks);
1209 }
1210
1211 updateXml();
1213 }
1214}
1215
1216//==============================================================================
1217AudioDeviceManager::LevelMeter::LevelMeter() noexcept : level() {}
1218
1219void AudioDeviceManager::LevelMeter::updateLevel (const float* const* channelData, int numChannels, int numSamples) noexcept
1220{
1221 if (getReferenceCount() <= 1)
1222 return;
1223
1224 auto localLevel = level.get();
1225
1226 if (numChannels > 0)
1227 {
1228 for (int j = 0; j < numSamples; ++j)
1229 {
1230 float s = 0;
1231
1232 for (int i = 0; i < numChannels; ++i)
1233 s += std::abs (channelData[i][j]);
1234
1235 s /= (float) numChannels;
1236
1237 const float decayFactor = 0.99992f;
1238
1239 if (s > localLevel)
1240 localLevel = s;
1241 else if (localLevel > 0.001f)
1242 localLevel *= decayFactor;
1243 else
1244 localLevel = 0;
1245 }
1246 }
1247 else
1248 {
1249 localLevel = 0;
1250 }
1251
1252 level = localLevel;
1253}
1254
1255double AudioDeviceManager::LevelMeter::getCurrentLevel() const noexcept
1256{
1257 jassert (getReferenceCount() > 1);
1258 return level.get();
1259}
1260
1262{
1263 { // cunningly nested to swap, unlock and delete in that order.
1264 std::unique_ptr<AudioBuffer<float>> oldSound;
1265
1266 {
1267 const ScopedLock sl (audioCallbackLock);
1268 std::swap (oldSound, testSound);
1269 }
1270 }
1271
1272 testSoundPosition = 0;
1273
1274 if (currentAudioDevice != nullptr)
1275 {
1276 auto sampleRate = currentAudioDevice->getCurrentSampleRate();
1277 auto soundLength = (int) sampleRate;
1278
1279 double frequency = 440.0;
1280 float amplitude = 0.5f;
1281
1282 auto phasePerSample = MathConstants<double>::twoPi / (sampleRate / frequency);
1283
1284 std::unique_ptr<AudioBuffer<float>> newSound (new AudioBuffer<float> (1, soundLength));
1285
1286 for (int i = 0; i < soundLength; ++i)
1287 newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
1288
1289 newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
1290 newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
1291
1292 {
1293 const ScopedLock sl (audioCallbackLock);
1294 std::swap (testSound, newSound);
1295 }
1296 }
1297}
1298
1300{
1301 auto deviceXRuns = (currentAudioDevice != nullptr ? currentAudioDevice->getXRunCount() : -1);
1302 return jmax (0, deviceXRuns) + loadMeasurer.getXRunCount();
1303}
1304
1305//==============================================================================
1306// Deprecated
1307void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool enabled)
1308{
1309 for (auto& device : MidiInput::getAvailableDevices())
1310 {
1311 if (device.name == name)
1312 {
1313 setMidiInputDeviceEnabled (device.identifier, enabled);
1314 return;
1315 }
1316 }
1317}
1318
1319bool AudioDeviceManager::isMidiInputEnabled (const String& name) const
1320{
1321 for (auto& device : MidiInput::getAvailableDevices())
1322 if (device.name == name)
1323 return isMidiInputDeviceEnabled (device.identifier);
1324
1325 return false;
1326}
1327
1328void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCallback* callbackToAdd)
1329{
1330 if (name.isEmpty())
1331 {
1332 addMidiInputDeviceCallback ({}, callbackToAdd);
1333 }
1334 else
1335 {
1336 for (auto& device : MidiInput::getAvailableDevices())
1337 {
1338 if (device.name == name)
1339 {
1340 addMidiInputDeviceCallback (device.identifier, callbackToAdd);
1341 return;
1342 }
1343 }
1344 }
1345}
1346
1347void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputCallback* callbackToRemove)
1348{
1349 if (name.isEmpty())
1350 {
1351 removeMidiInputDeviceCallback ({}, callbackToRemove);
1352 }
1353 else
1354 {
1355 for (auto& device : MidiInput::getAvailableDevices())
1356 {
1357 if (device.name == name)
1358 {
1359 removeMidiInputDeviceCallback (device.identifier, callbackToRemove);
1360 return;
1361 }
1362 }
1363 }
1364}
1365
1366void AudioDeviceManager::setDefaultMidiOutput (const String& name)
1367{
1368 for (auto& device : MidiOutput::getAvailableDevices())
1369 {
1370 if (device.name == name)
1371 {
1372 setDefaultMidiOutputDevice (device.identifier);
1373 return;
1374 }
1375 }
1376}
1377
1378//==============================================================================
1379//==============================================================================
1380#if JUCE_UNIT_TESTS
1381
1382class AudioDeviceManagerTests final : public UnitTest
1383{
1384public:
1385 AudioDeviceManagerTests() : UnitTest ("AudioDeviceManager", UnitTestCategories::audio) {}
1386
1387 void runTest() override
1388 {
1389 beginTest ("When the AudioDeviceSetup has non-empty device names, initialise uses the requested devices");
1390 {
1391 AudioDeviceManager manager;
1392 initialiseManager (manager);
1393
1394 expectEquals (manager.getAvailableDeviceTypes().size(), 2);
1395
1396 AudioDeviceManager::AudioDeviceSetup setup;
1397 setup.outputDeviceName = "z";
1398 setup.inputDeviceName = "c";
1399
1400 expect (manager.initialise (2, 2, nullptr, true, String{}, &setup).isEmpty());
1401
1402 const auto& newSetup = manager.getAudioDeviceSetup();
1403
1404 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1405 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1406
1407 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1408 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1409 }
1410
1411 beginTest ("When the AudioDeviceSetup has empty device names, initialise picks suitable default devices");
1412 {
1413 AudioDeviceManager manager;
1414 initialiseManager (manager);
1415
1416 AudioDeviceManager::AudioDeviceSetup setup;
1417
1418 expect (manager.initialise (2, 2, nullptr, true, String{}, &setup).isEmpty());
1419
1420 const auto& newSetup = manager.getAudioDeviceSetup();
1421
1422 expectEquals (newSetup.outputDeviceName, String ("x"));
1423 expectEquals (newSetup.inputDeviceName, String ("a"));
1424
1425 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1426 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1427 }
1428
1429 beginTest ("When the preferred device name matches an input and an output on the same type, that type is used");
1430 {
1431 AudioDeviceManager manager;
1432 initialiseManagerWithDifferentDeviceNames (manager);
1433
1434 expect (manager.initialise (2, 2, nullptr, true, "bar *").isEmpty());
1435
1436 expectEquals (manager.getCurrentAudioDeviceType(), String ("bar"));
1437
1438 const auto& newSetup = manager.getAudioDeviceSetup();
1439
1440 expectEquals (newSetup.outputDeviceName, String ("bar out a"));
1441 expectEquals (newSetup.inputDeviceName, String ("bar in a"));
1442
1443 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1444 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1445
1446 expect (manager.getCurrentAudioDevice() != nullptr);
1447 }
1448
1449 beginTest ("When the preferred device name matches either an input and an output, but not both, that type is used");
1450 {
1451 AudioDeviceManager manager;
1452 initialiseManagerWithDifferentDeviceNames (manager);
1453
1454 expect (manager.initialise (2, 2, nullptr, true, "bar out b").isEmpty());
1455
1456 expectEquals (manager.getCurrentAudioDeviceType(), String ("bar"));
1457
1458 const auto& newSetup = manager.getAudioDeviceSetup();
1459
1460 expectEquals (newSetup.outputDeviceName, String ("bar out b"));
1461 expectEquals (newSetup.inputDeviceName, String ("bar in a"));
1462
1463 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1464 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1465
1466 expect (manager.getCurrentAudioDevice() != nullptr);
1467 }
1468
1469 beginTest ("When the preferred device name does not match any inputs or outputs, defaults are used");
1470 {
1471 AudioDeviceManager manager;
1472 initialiseManagerWithDifferentDeviceNames (manager);
1473
1474 expect (manager.initialise (2, 2, nullptr, true, "unmatchable").isEmpty());
1475
1476 expectEquals (manager.getCurrentAudioDeviceType(), String ("foo"));
1477
1478 const auto& newSetup = manager.getAudioDeviceSetup();
1479
1480 expectEquals (newSetup.outputDeviceName, String ("foo out a"));
1481 expectEquals (newSetup.inputDeviceName, String ("foo in a"));
1482
1483 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1484 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1485
1486 expect (manager.getCurrentAudioDevice() != nullptr);
1487 }
1488
1489 beginTest ("When first device type has no devices, a device type with devices is used instead");
1490 {
1491 AudioDeviceManager manager;
1492 initialiseManagerWithEmptyDeviceType (manager);
1493
1494 AudioDeviceManager::AudioDeviceSetup setup;
1495
1496 expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty());
1497
1498 const auto& newSetup = manager.getAudioDeviceSetup();
1499
1500 expectEquals (newSetup.outputDeviceName, String ("x"));
1501 expectEquals (newSetup.inputDeviceName, String ("a"));
1502
1503 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1504 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1505 }
1506
1507 beginTest ("If a device type has been explicitly set to a type with devices, "
1508 "initialisation should respect this choice");
1509 {
1510 AudioDeviceManager manager;
1511 initialiseManagerWithEmptyDeviceType (manager);
1512 manager.setCurrentAudioDeviceType (mockBName, true);
1513
1514 AudioDeviceManager::AudioDeviceSetup setup;
1515 expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty());
1516
1517 expectEquals (manager.getCurrentAudioDeviceType(), mockBName);
1518 }
1519
1520 beginTest ("If a device type has been explicitly set to a type without devices, "
1521 "initialisation should pick a type with devices instead");
1522 {
1523 AudioDeviceManager manager;
1524 initialiseManagerWithEmptyDeviceType (manager);
1525 manager.setCurrentAudioDeviceType (emptyName, true);
1526
1527 AudioDeviceManager::AudioDeviceSetup setup;
1528 expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty());
1529
1530 expectEquals (manager.getCurrentAudioDeviceType(), mockAName);
1531 }
1532
1533 beginTest ("Carry out a long sequence of configuration changes");
1534 {
1535 AudioDeviceManager manager;
1536 initialiseManagerWithEmptyDeviceType (manager);
1537 initialiseWithDefaultDevices (manager);
1538 disableInputChannelsButLeaveDeviceOpen (manager);
1539 selectANewInputDevice (manager);
1540 disableInputDevice (manager);
1541 reenableInputDeviceWithNoChannels (manager);
1542 enableInputChannels (manager);
1543 disableInputChannelsButLeaveDeviceOpen (manager);
1544 switchDeviceType (manager);
1545 enableInputChannels (manager);
1546 closeDeviceByRequestingEmptyNames (manager);
1547 }
1548
1549 beginTest ("AudioDeviceManager updates its current settings before notifying callbacks when device restarts itself");
1550 {
1551 AudioDeviceManager manager;
1552 auto deviceType = std::make_unique<MockDeviceType> ("foo",
1553 StringArray { "foo in a", "foo in b" },
1554 StringArray { "foo out a", "foo out b" });
1555 auto* ptr = deviceType.get();
1556 manager.addAudioDeviceType (std::move (deviceType));
1557
1558 AudioDeviceManager::AudioDeviceSetup setup;
1559 setup.sampleRate = 48000.0;
1560 setup.bufferSize = 256;
1561 setup.inputDeviceName = "foo in a";
1562 setup.outputDeviceName = "foo out a";
1563 setup.useDefaultInputChannels = true;
1564 setup.useDefaultOutputChannels = true;
1565 manager.setAudioDeviceSetup (setup, true);
1566
1567 const auto currentSetup = manager.getAudioDeviceSetup();
1568 expectEquals (currentSetup.sampleRate, setup.sampleRate);
1569 expectEquals (currentSetup.bufferSize, setup.bufferSize);
1570
1571 MockCallback callback;
1572 manager.addAudioCallback (&callback);
1573
1574 constexpr auto newSr = 10000.0;
1575 constexpr auto newBs = 1024;
1576 auto numCalls = 0;
1577
1578 // Compilers disagree about whether newSr and newBs need to be captured
1579 callback.aboutToStart = [&]
1580 {
1581 ++numCalls;
1582 const auto current = manager.getAudioDeviceSetup();
1583 expectEquals (current.sampleRate, newSr);
1584 expectEquals (current.bufferSize, newBs);
1585 };
1586
1587 ptr->restartDevices (newSr, newBs);
1588 expectEquals (numCalls, 1);
1589 }
1590 }
1591
1592private:
1593 void initialiseWithDefaultDevices (AudioDeviceManager& manager)
1594 {
1595 manager.initialiseWithDefaultDevices (2, 2);
1596 const auto& setup = manager.getAudioDeviceSetup();
1597
1598 expectEquals (setup.inputChannels.countNumberOfSetBits(), 2);
1599 expectEquals (setup.outputChannels.countNumberOfSetBits(), 2);
1600
1601 expect (setup.useDefaultInputChannels);
1602 expect (setup.useDefaultOutputChannels);
1603
1604 expect (manager.getCurrentAudioDevice() != nullptr);
1605 }
1606
1607 void disableInputChannelsButLeaveDeviceOpen (AudioDeviceManager& manager)
1608 {
1609 auto setup = manager.getAudioDeviceSetup();
1610 setup.inputChannels.clear();
1611 setup.useDefaultInputChannels = false;
1612
1613 expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
1614
1615 const auto newSetup = manager.getAudioDeviceSetup();
1616 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1617 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1618
1619 expect (! newSetup.useDefaultInputChannels);
1620 expect (newSetup.useDefaultOutputChannels);
1621
1622 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1623 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1624
1625 expect (manager.getCurrentAudioDevice() != nullptr);
1626 }
1627
1628 void selectANewInputDevice (AudioDeviceManager& manager)
1629 {
1630 auto setup = manager.getAudioDeviceSetup();
1631 setup.inputDeviceName = "b";
1632
1633 expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
1634
1635 const auto newSetup = manager.getAudioDeviceSetup();
1636 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1637 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1638
1639 expect (! newSetup.useDefaultInputChannels);
1640 expect (newSetup.useDefaultOutputChannels);
1641
1642 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1643 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1644
1645 expect (manager.getCurrentAudioDevice() != nullptr);
1646 }
1647
1648 void disableInputDevice (AudioDeviceManager& manager)
1649 {
1650 auto setup = manager.getAudioDeviceSetup();
1651 setup.inputDeviceName = "";
1652
1653 expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
1654
1655 const auto newSetup = manager.getAudioDeviceSetup();
1656 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1657 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1658
1659 expect (! newSetup.useDefaultInputChannels);
1660 expect (newSetup.useDefaultOutputChannels);
1661
1662 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1663 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1664
1665 expect (manager.getCurrentAudioDevice() != nullptr);
1666 }
1667
1668 void reenableInputDeviceWithNoChannels (AudioDeviceManager& manager)
1669 {
1670 auto setup = manager.getAudioDeviceSetup();
1671 setup.inputDeviceName = "a";
1672
1673 expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
1674
1675 const auto newSetup = manager.getAudioDeviceSetup();
1676 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1677 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1678
1679 expect (! newSetup.useDefaultInputChannels);
1680 expect (newSetup.useDefaultOutputChannels);
1681
1682 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1683 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1684
1685 expect (manager.getCurrentAudioDevice() != nullptr);
1686 }
1687
1688 void enableInputChannels (AudioDeviceManager& manager)
1689 {
1690 auto setup = manager.getAudioDeviceSetup();
1691 setup.inputDeviceName = manager.getCurrentDeviceTypeObject()->getDeviceNames (true)[0];
1692 setup.inputChannels = 3;
1693 setup.useDefaultInputChannels = false;
1694
1695 expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
1696
1697 const auto newSetup = manager.getAudioDeviceSetup();
1698 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1699 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1700
1701 expect (! newSetup.useDefaultInputChannels);
1702 expect (newSetup.useDefaultOutputChannels);
1703
1704 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1705 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1706
1707 expect (manager.getCurrentAudioDevice() != nullptr);
1708 }
1709
1710 void switchDeviceType (AudioDeviceManager& manager)
1711 {
1712 const auto oldSetup = manager.getAudioDeviceSetup();
1713
1714 expectEquals (manager.getCurrentAudioDeviceType(), String (mockAName));
1715
1716 manager.setCurrentAudioDeviceType (mockBName, true);
1717
1718 expectEquals (manager.getCurrentAudioDeviceType(), String (mockBName));
1719
1720 const auto newSetup = manager.getAudioDeviceSetup();
1721
1722 expect (newSetup.outputDeviceName.isNotEmpty());
1723 // We had no channels enabled, which means we don't need to open a new input device
1724 expect (newSetup.inputDeviceName.isEmpty());
1725
1726 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1727 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1728
1729 expect (manager.getCurrentAudioDevice() != nullptr);
1730 }
1731
1732 void closeDeviceByRequestingEmptyNames (AudioDeviceManager& manager)
1733 {
1734 auto setup = manager.getAudioDeviceSetup();
1735 setup.inputDeviceName = "";
1736 setup.outputDeviceName = "";
1737
1738 expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
1739
1740 const auto newSetup = manager.getAudioDeviceSetup();
1741 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1742 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1743
1744 expect (newSetup.inputDeviceName.isEmpty());
1745 expect (newSetup.outputDeviceName.isEmpty());
1746
1747 expect (manager.getCurrentAudioDevice() == nullptr);
1748 }
1749
1750 const String mockAName = "mockA";
1751 const String mockBName = "mockB";
1752 const String emptyName = "empty";
1753
1754 struct Restartable
1755 {
1756 virtual ~Restartable() = default;
1757 virtual void restart (double newSr, int newBs) = 0;
1758 };
1759
1760 class MockDevice final : public AudioIODevice,
1761 private Restartable
1762 {
1763 public:
1764 MockDevice (ListenerList<Restartable>& l, String typeNameIn, String outNameIn, String inNameIn)
1765 : AudioIODevice ("mock", typeNameIn), listeners (l), outName (outNameIn), inName (inNameIn)
1766 {
1767 listeners.add (this);
1768 }
1769
1770 ~MockDevice() override
1771 {
1772 listeners.remove (this);
1773 }
1774
1775 StringArray getOutputChannelNames() override { return { "o1", "o2", "o3" }; }
1776 StringArray getInputChannelNames() override { return { "i1", "i2", "i3" }; }
1777
1778 Array<double> getAvailableSampleRates() override { return { 44100.0, 48000.0 }; }
1779 Array<int> getAvailableBufferSizes() override { return { 128, 256 }; }
1780 int getDefaultBufferSize() override { return 128; }
1781
1782 String open (const BigInteger& inputs, const BigInteger& outputs, double sr, int bs) override
1783 {
1784 inChannels = inputs;
1785 outChannels = outputs;
1786 sampleRate = sr;
1787 blockSize = bs;
1788 on = true;
1789 return {};
1790 }
1791
1792 void close() override { on = false; }
1793 bool isOpen() override { return on; }
1794
1795 void start (AudioIODeviceCallback* c) override
1796 {
1797 callback = c;
1798 callback->audioDeviceAboutToStart (this);
1799 playing = true;
1800 }
1801
1802 void stop() override
1803 {
1804 playing = false;
1805 callback->audioDeviceStopped();
1806 }
1807
1808 bool isPlaying() override { return playing; }
1809
1810 String getLastError() override { return {}; }
1811 int getCurrentBufferSizeSamples() override { return blockSize; }
1812 double getCurrentSampleRate() override { return sampleRate; }
1813 int getCurrentBitDepth() override { return 16; }
1814
1815 BigInteger getActiveOutputChannels() const override { return outChannels; }
1816 BigInteger getActiveInputChannels() const override { return inChannels; }
1817
1818 int getOutputLatencyInSamples() override { return 0; }
1819 int getInputLatencyInSamples() override { return 0; }
1820
1821 private:
1822 void restart (double newSr, int newBs) override
1823 {
1824 stop();
1825 close();
1826 open (inChannels, outChannels, newSr, newBs);
1827 start (callback);
1828 }
1829
1830 ListenerList<Restartable>& listeners;
1831 AudioIODeviceCallback* callback = nullptr;
1832 String outName, inName;
1833 BigInteger outChannels, inChannels;
1834 double sampleRate = 0.0;
1835 int blockSize = 0;
1836 bool on = false, playing = false;
1837 };
1838
1839 class MockDeviceType final : public AudioIODeviceType
1840 {
1841 public:
1842 explicit MockDeviceType (String kind)
1843 : MockDeviceType (std::move (kind), { "a", "b", "c" }, { "x", "y", "z" }) {}
1844
1845 MockDeviceType (String kind, StringArray inputNames, StringArray outputNames)
1846 : AudioIODeviceType (std::move (kind)),
1847 inNames (std::move (inputNames)),
1848 outNames (std::move (outputNames)) {}
1849
1850 ~MockDeviceType() override
1851 {
1852 // A Device outlived its DeviceType!
1853 jassert (listeners.isEmpty());
1854 }
1855
1856 void scanForDevices() override {}
1857
1858 StringArray getDeviceNames (bool isInput) const override
1859 {
1860 return getNames (isInput);
1861 }
1862
1863 int getDefaultDeviceIndex (bool) const override { return 0; }
1864
1865 int getIndexOfDevice (AudioIODevice* device, bool isInput) const override
1866 {
1867 return getNames (isInput).indexOf (device->getName());
1868 }
1869
1870 bool hasSeparateInputsAndOutputs() const override { return true; }
1871
1872 AudioIODevice* createDevice (const String& outputName, const String& inputName) override
1873 {
1874 if (inNames.contains (inputName) || outNames.contains (outputName))
1875 return new MockDevice (listeners, getTypeName(), outputName, inputName);
1876
1877 return nullptr;
1878 }
1879
1880 // Call this to emulate the device restarting itself with new settings.
1881 // This might happen e.g. when a user changes the ASIO settings.
1882 void restartDevices (double newSr, int newBs)
1883 {
1884 listeners.call ([&] (auto& l) { return l.restart (newSr, newBs); });
1885 }
1886
1887 private:
1888 const StringArray& getNames (bool isInput) const { return isInput ? inNames : outNames; }
1889
1890 const StringArray inNames, outNames;
1891 ListenerList<Restartable> listeners;
1892 };
1893
1894 class MockCallback final : public AudioIODeviceCallback
1895 {
1896 public:
1897 std::function<void()> callback;
1898 std::function<void()> aboutToStart;
1899 std::function<void()> stopped;
1900 std::function<void()> error;
1901
1902 void audioDeviceIOCallbackWithContext (const float* const*,
1903 int,
1904 float* const*,
1905 int,
1906 int,
1907 const AudioIODeviceCallbackContext&) override
1908 {
1909 NullCheckedInvocation::invoke (callback);
1910 }
1911
1912 void audioDeviceAboutToStart (AudioIODevice*) override { NullCheckedInvocation::invoke (aboutToStart); }
1913 void audioDeviceStopped() override { NullCheckedInvocation::invoke (stopped); }
1914 void audioDeviceError (const String&) override { NullCheckedInvocation::invoke (error); }
1915 };
1916
1917 void initialiseManager (AudioDeviceManager& manager)
1918 {
1919 manager.addAudioDeviceType (std::make_unique<MockDeviceType> (mockAName));
1920 manager.addAudioDeviceType (std::make_unique<MockDeviceType> (mockBName));
1921 }
1922
1923 void initialiseManagerWithEmptyDeviceType (AudioDeviceManager& manager)
1924 {
1925 manager.addAudioDeviceType (std::make_unique<MockDeviceType> (emptyName, StringArray{}, StringArray{}));
1926 initialiseManager (manager);
1927 }
1928
1929 void initialiseManagerWithDifferentDeviceNames (AudioDeviceManager& manager)
1930 {
1931 manager.addAudioDeviceType (std::make_unique<MockDeviceType> ("foo",
1932 StringArray { "foo in a", "foo in b" },
1933 StringArray { "foo out a", "foo out b" }));
1934
1935 manager.addAudioDeviceType (std::make_unique<MockDeviceType> ("bar",
1936 StringArray { "bar in a", "bar in b" },
1937 StringArray { "bar out a", "bar out b" }));
1938 }
1939};
1940
1941static AudioDeviceManagerTests audioDeviceManagerTests;
1942
1943#endif
1944
1945} // namespace juce
void swapWith(OtherArrayType &otherArray) noexcept
Definition juce_Array.h:621
ElementType getUnchecked(int index) const
Definition juce_Array.h:252
int size() const noexcept
Definition juce_Array.h:215
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Type *const * getArrayOfWritePointers() noexcept
bool isMidiInputDeviceEnabled(const String &deviceIdentifier) const
void removeAudioDeviceType(AudioIODeviceType *deviceTypeToRemove)
AudioDeviceSetup getAudioDeviceSetup() const
void removeMidiInputDeviceCallback(const String &deviceIdentifier, MidiInputCallback *callback)
AudioIODeviceType * getCurrentDeviceTypeObject() const
String setAudioDeviceSetup(const AudioDeviceSetup &newSetup, bool treatAsChosenDevice)
virtual void createAudioDeviceTypes(OwnedArray< AudioIODeviceType > &types)
void setDefaultMidiOutputDevice(const String &deviceIdentifier)
void setMidiInputDeviceEnabled(const String &deviceIdentifier, bool enabled)
void addMidiInputDeviceCallback(const String &deviceIdentifier, MidiInputCallback *callback)
void setCurrentAudioDeviceType(const String &type, bool treatAsChosenDevice)
const OwnedArray< AudioIODeviceType > & getAvailableDeviceTypes()
String initialise(int numInputChannelsNeeded, int numOutputChannelsNeeded, const XmlElement *savedState, bool selectDefaultDeviceOnFailure, const String &preferredDefaultDeviceName=String(), const AudioDeviceSetup *preferredSetupOptions=nullptr)
void addAudioCallback(AudioIODeviceCallback *newCallback)
String initialiseWithDefaultDevices(int numInputChannelsNeeded, int numOutputChannelsNeeded)
void removeAudioCallback(AudioIODeviceCallback *callback)
void addAudioDeviceType(std::unique_ptr< AudioIODeviceType > newDeviceType)
AudioWorkgroup getDeviceAudioWorkgroup() const
std::unique_ptr< XmlElement > createStateXml() const
virtual void audioDeviceAboutToStart(AudioIODevice *device)=0
virtual void audioDeviceStopped()=0
static AudioIODeviceType * createAudioIODeviceType_WASAPI(WASAPIDeviceMode deviceMode)
static AudioIODeviceType * createAudioIODeviceType_ASIO()
static AudioIODeviceType * createAudioIODeviceType_Oboe()
static AudioIODeviceType * createAudioIODeviceType_JACK()
static AudioIODeviceType * createAudioIODeviceType_DirectSound()
static AudioIODeviceType * createAudioIODeviceType_Android()
static AudioIODeviceType * createAudioIODeviceType_OpenSLES()
static AudioIODeviceType * createAudioIODeviceType_CoreAudio()
virtual StringArray getDeviceNames(bool wantInputNames=false) const =0
const String & getTypeName() const noexcept
static AudioIODeviceType * createAudioIODeviceType_Bela()
static AudioIODeviceType * createAudioIODeviceType_ALSA()
static AudioIODeviceType * createAudioIODeviceType_iOSAudio()
String toString(int base, int minimumNumCharacters=1) const
int countNumberOfSetBits() const noexcept
static Array< MidiDeviceInfo > getAvailableDevices()
static std::unique_ptr< MidiInput > openDevice(const String &deviceIdentifier, MidiInputCallback *callback)
String getIdentifier() const noexcept
bool isActiveSense() const noexcept
static std::unique_ptr< MidiOutput > openDevice(const String &deviceIdentifier)
static Array< MidiDeviceInfo > getAvailableDevices()
String trim() const
bool isEmpty() const noexcept
bool matchesWildcard(StringRef wildcard, bool ignoreCase) const noexcept
void clear() noexcept
bool isNotEmpty() const noexcept
static void JUCE_CALLTYPE sleep(int milliseconds)
bool hasTagName(StringRef possibleTagName) const noexcept
static constexpr FloatType twoPi