OpenShot Audio Library | OpenShotAudio  0.6.0
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 
23 namespace juce
24 {
25 
26 template <typename Setup>
27 static 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 
41 static 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 
53 bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const
54 {
55  return tie (*this) == tie (other);
56 }
57 
58 bool AudioDeviceManager::AudioDeviceSetup::operator!= (const AudioDeviceManager::AudioDeviceSetup& other) const
59 {
60  return tie (*this) != tie (other);
61 }
62 
63 //==============================================================================
64 class AudioDeviceManager::CallbackHandler final : public AudioIODeviceCallback,
65  public MidiInputCallback,
66  public AudioIODeviceType::Listener
67 {
68 public:
69  CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {}
70 
71 private:
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 //==============================================================================
125 void AudioDeviceManager::createDeviceTypesIfNeeded()
126 {
127  if (availableDeviceTypes.size() == 0)
128  {
130  createAudioDeviceTypes (types);
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 
144 void 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 
170 void 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 
181 void 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 
223 void AudioDeviceManager::midiDeviceListChanged()
224 {
225  openLastRequestedMidiDevices (midiDeviceInfosFromXml, defaultMidiOutputDeviceInfo);
227 }
228 
229 //==============================================================================
230 static 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 
253 void 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 
282 static 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 //==============================================================================
292 String 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 
313 String 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 
391 String 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 
459 void 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 
506 void 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 
596 std::unique_ptr<XmlElement> AudioDeviceManager::createStateXml() const
597 {
598  if (lastExplicitSettings != nullptr)
599  return std::make_unique<XmlElement> (*lastExplicitSettings);
600 
601  return {};
602 }
603 
604 //==============================================================================
605 void 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 
618 AudioIODeviceType* 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 
629 AudioIODeviceType* 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 
642 {
643  return currentSetup;
644 }
645 
647 {
648  setup = currentSetup;
649 }
650 
651 void AudioDeviceManager::deleteCurrentDevice()
652 {
653  currentAudioDevice.reset();
654  currentSetup.inputDeviceName.clear();
655  currentSetup.outputDeviceName.clear();
656 }
657 
658 void 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 
699 static 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 
829 double 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 
859 int 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 
869 void 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 
903 void 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 
993 void 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 
1062 void 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 
1079 void 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 
1091 void 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 //==============================================================================
1105 void 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 
1163 void 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 //==============================================================================
1217 AudioDeviceManager::LevelMeter::LevelMeter() noexcept : level() {}
1218 
1219 void 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 
1255 double 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
1307 void 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 
1319 bool 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 
1328 void 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 
1347 void 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 
1366 void 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 
1382 class AudioDeviceManagerTests final : public UnitTest
1383 {
1384 public:
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 
1592 private:
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 
1941 static 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 remove(int indexToRemove)
Definition: juce_Array.h:742
void add(const ElementType &newElement)
Definition: juce_Array.h:418
ElementType & getReference(int index) noexcept
Definition: juce_Array.h:267
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
static AudioIODeviceType * createAudioIODeviceType_Bela()
const String & getTypeName() const noexcept
static AudioIODeviceType * createAudioIODeviceType_ALSA()
static AudioIODeviceType * createAudioIODeviceType_iOSAudio()
String toString(int base, int minimumNumCharacters=1) const
bool isZero() const noexcept
int countNumberOfSetBits() const noexcept
static std::unique_ptr< MidiInput > openDevice(const String &deviceIdentifier, MidiInputCallback *callback)
static Array< MidiDeviceInfo > getAvailableDevices()
String getIdentifier() const noexcept
bool isActiveSense() const noexcept
static Array< MidiDeviceInfo > getAvailableDevices()
static std::unique_ptr< MidiOutput > openDevice(const String &deviceIdentifier)
String trim() const
bool isEmpty() const noexcept
Definition: juce_String.h:310
bool matchesWildcard(StringRef wildcard, bool ignoreCase) const noexcept
void clear() noexcept
bool isNotEmpty() const noexcept
Definition: juce_String.h:316
static void JUCE_CALLTYPE sleep(int milliseconds)
bool hasTagName(StringRef possibleTagName) const noexcept