26template <
typename Setup>
27static auto getSetupInfo (Setup& s,
bool isInput)
32 decltype ((s.inputDeviceName)) name;
33 decltype ((s.inputChannels)) channels;
34 decltype ((s.useDefaultInputChannels)) useDefault;
37 return isInput ? SetupInfo { s.inputDeviceName, s.inputChannels, s.useDefaultInputChannels }
38 : SetupInfo { s.outputDeviceName, s.outputChannels, s.useDefaultOutputChannels };
41static auto tie (
const AudioDeviceManager::AudioDeviceSetup& s)
43 return std::tie (s.outputDeviceName,
48 s.useDefaultInputChannels,
50 s.useDefaultOutputChannels);
53bool AudioDeviceManager::AudioDeviceSetup::operator== (
const AudioDeviceManager::AudioDeviceSetup& other)
const
55 return tie (*
this) == tie (other);
58bool AudioDeviceManager::AudioDeviceSetup::operator!= (
const AudioDeviceManager::AudioDeviceSetup& other)
const
60 return tie (*
this) != tie (other);
64class AudioDeviceManager::CallbackHandler final :
public AudioIODeviceCallback,
65 public MidiInputCallback,
66 public AudioIODeviceType::Listener
69 CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {}
72 void audioDeviceIOCallbackWithContext (
const float*
const* ins,
77 const AudioIODeviceCallbackContext& context)
override
79 owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples, context);
82 void audioDeviceAboutToStart (AudioIODevice* device)
override
84 owner.audioDeviceAboutToStartInt (device);
87 void audioDeviceStopped()
override
89 owner.audioDeviceStoppedInt();
92 void audioDeviceError (
const String& message)
override
94 owner.audioDeviceErrorInt (message);
97 void handleIncomingMidiMessage (MidiInput* source,
const MidiMessage& message)
override
99 owner.handleIncomingMidiMessageInt (source, message);
102 void audioDeviceListChanged()
override
104 owner.audioDeviceListChanged();
107 AudioDeviceManager& owner;
109 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
115 callbackHandler.reset (
new CallbackHandler (*
this));
120 currentAudioDevice.reset();
121 defaultMidiOutput.reset();
125void AudioDeviceManager::createDeviceTypesIfNeeded()
127 if (availableDeviceTypes.size() == 0)
132 for (
auto*
t : types)
137 for (
auto* type : availableDeviceTypes)
138 type->scanForDevices();
140 pickCurrentDeviceTypeWithDevices();
144void AudioDeviceManager::pickCurrentDeviceTypeWithDevices()
146 const auto deviceTypeHasDevices = [] (
const AudioIODeviceType* ptr)
148 return ! ptr->getDeviceNames (
true) .isEmpty()
149 || ! ptr->getDeviceNames (
false).isEmpty();
152 if (
auto* type = findType (currentDeviceType))
153 if (deviceTypeHasDevices (type))
156 const auto iter = std::find_if (availableDeviceTypes.begin(),
157 availableDeviceTypes.end(),
158 deviceTypeHasDevices);
160 if (iter != availableDeviceTypes.end())
161 currentDeviceType = (*iter)->getTypeName();
166 scanDevicesIfNeeded();
167 return availableDeviceTypes;
170void AudioDeviceManager::updateCurrentSetup()
172 if (currentAudioDevice !=
nullptr)
174 currentSetup.
sampleRate = currentAudioDevice->getCurrentSampleRate();
175 currentSetup.
bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
176 currentSetup.
inputChannels = currentAudioDevice->getActiveInputChannels();
177 currentSetup.
outputChannels = currentAudioDevice->getActiveOutputChannels();
181void AudioDeviceManager::audioDeviceListChanged()
183 if (currentAudioDevice !=
nullptr)
185 auto currentDeviceStillAvailable = [&]
187 auto currentTypeName = currentAudioDevice->getTypeName();
188 auto currentDeviceName = currentAudioDevice->getName();
190 for (
auto* deviceType : availableDeviceTypes)
192 if (currentTypeName == deviceType->getTypeName())
194 for (
auto& deviceName : deviceType->getDeviceNames (true))
195 if (currentDeviceName == deviceName)
198 for (
auto& deviceName : deviceType->getDeviceNames (false))
199 if (currentDeviceName == deviceName)
207 if (! currentDeviceStillAvailable)
212 initialiseFromXML (*e,
true, preferredDeviceName, ¤tSetup);
214 initialiseDefault (preferredDeviceName, ¤tSetup);
217 updateCurrentSetup();
223void AudioDeviceManager::midiDeviceListChanged()
225 openLastRequestedMidiDevices (midiDeviceInfosFromXml, defaultMidiOutputDeviceInfo);
230static void addIfNotNull (OwnedArray<AudioIODeviceType>& list, AudioIODeviceType*
const device)
232 if (device !=
nullptr)
257 jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size());
262 availableDeviceTypes.getLast()->addListener (callbackHandler.get());
270 jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size());
274 if (
auto removed = std::unique_ptr<AudioIODeviceType> (availableDeviceTypes.removeAndReturn (index)))
276 removed->removeListener (callbackHandler.get());
277 lastDeviceTypeConfigs.remove (index,
true);
299 scanDevicesIfNeeded();
300 pickCurrentDeviceTypeWithDevices();
306 if (xml !=
nullptr && xml->
hasTagName (
"DEVICESETUP"))
316 AudioDeviceSetup
setup;
322 else if (preferredDefaultDeviceName.
isNotEmpty())
324 const auto nameMatches = [&preferredDefaultDeviceName] (
const String& name)
335 const auto getWildcardMatch = [&nameMatches] (
const StringArray& names)
337 const auto iter = std::find_if (names.begin(), names.end(), nameMatches);
338 return WildcardMatch { iter != names.end() ? *iter : String(), iter != names.end() };
341 struct WildcardMatches
343 WildcardMatch input, output;
346 const auto getMatchesForType = [&getWildcardMatch] (
const AudioIODeviceType* type)
348 return WildcardMatches { getWildcardMatch (type->
getDeviceNames (
true)),
354 String type, input, output;
357 const auto result = [&]
360 for (
auto* type : availableDeviceTypes)
362 const auto matches = getMatchesForType (type);
364 if (matches.input.successful && matches.output.successful)
365 return SearchResult { type->
getTypeName(), matches.input.value, matches.output.value };
370 for (
auto* type : availableDeviceTypes)
372 const auto matches = getMatchesForType (type);
374 if (matches.input.successful || matches.output.successful)
375 return SearchResult { type->
getTypeName(), matches.input.value, matches.output.value };
379 return SearchResult { currentDeviceType, {}, {} };
382 currentDeviceType = result.type;
383 setup.inputDeviceName = result.input;
384 setup.outputDeviceName = result.output;
387 insertDefaultDeviceNames (setup);
391String AudioDeviceManager::initialiseFromXML (
const XmlElement& xml,
392 bool selectDefaultDeviceOnFailure,
393 const String& preferredDefaultDeviceName,
394 const AudioDeviceSetup* preferredSetupOptions)
396 lastExplicitSettings.reset (
new XmlElement (xml));
399 AudioDeviceSetup setup;
401 if (preferredSetupOptions !=
nullptr)
402 setup = *preferredSetupOptions;
404 if (xml.getStringAttribute (
"audioDeviceName").isNotEmpty())
406 setup.inputDeviceName = setup.outputDeviceName
407 = xml.getStringAttribute (
"audioDeviceName");
411 setup.inputDeviceName = xml.getStringAttribute (
"audioInputDeviceName");
412 setup.outputDeviceName = xml.getStringAttribute (
"audioOutputDeviceName");
415 currentDeviceType = xml.getStringAttribute (
"deviceType");
417 if (findType (currentDeviceType) ==
nullptr)
419 if (
auto* type = findType (setup.inputDeviceName, setup.outputDeviceName))
421 else if (
auto* firstType = availableDeviceTypes.getFirst())
422 currentDeviceType = firstType->getTypeName();
425 setup.bufferSize = xml.getIntAttribute (
"audioDeviceBufferSize", setup.bufferSize);
426 setup.sampleRate = xml.getDoubleAttribute (
"audioDeviceRate", setup.sampleRate);
428 setup.inputChannels .parseString (xml.getStringAttribute (
"audioDeviceInChans",
"11"), 2);
429 setup.outputChannels.parseString (xml.getStringAttribute (
"audioDeviceOutChans",
"11"), 2);
431 setup.useDefaultInputChannels = ! xml.hasAttribute (
"audioDeviceInChans");
432 setup.useDefaultOutputChannels = ! xml.hasAttribute (
"audioDeviceOutChans");
436 if (error.isNotEmpty() && selectDefaultDeviceOnFailure)
437 error =
initialise (numInputChansNeeded, numOutputChansNeeded,
nullptr,
false, preferredDefaultDeviceName);
439 enabledMidiInputs.clear();
441 const auto midiInputs = [&]
443 Array<MidiDeviceInfo> result;
445 for (
auto* c : xml.getChildWithTagNameIterator (
"MIDIINPUT"))
446 result.add ({ c->getStringAttribute (
"name"), c->getStringAttribute (
"identifier") });
451 const MidiDeviceInfo defaultOutputDeviceInfo (xml.getStringAttribute (
"defaultMidiOutput"),
452 xml.getStringAttribute (
"defaultMidiOutputDevice"));
454 openLastRequestedMidiDevices (midiInputs, defaultOutputDeviceInfo);
459void AudioDeviceManager::openLastRequestedMidiDevices (
const Array<MidiDeviceInfo>& desiredInputs,
const MidiDeviceInfo& defaultOutput)
461 const auto openDeviceIfAvailable = [&] (
const Array<MidiDeviceInfo>& devices,
462 const MidiDeviceInfo& deviceToOpen,
465 const auto iterWithMatchingIdentifier = std::find_if (devices.begin(), devices.end(), [&] (
const auto& x)
467 return x.identifier == deviceToOpen.identifier;
470 if (iterWithMatchingIdentifier != devices.end())
472 doOpen (deviceToOpen.identifier);
476 const auto iterWithMatchingName = std::find_if (devices.begin(), devices.end(), [&] (
const auto& x)
478 return x.name == deviceToOpen.name;
481 if (iterWithMatchingName != devices.end())
482 doOpen (iterWithMatchingName->identifier);
485 midiDeviceInfosFromXml = desiredInputs;
489 for (
const auto& info : midiDeviceInfosFromXml)
500 lastExplicitSettings.reset();
503 nullptr,
false, {},
nullptr);
506void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup&
setup)
const
515 const auto getDevicesToTestForMatchingSampleRate = [&setup, type,
this] (Direction dir)
517 const auto isInput = dir == Direction::in;
518 const auto info = getSetupInfo (setup, isInput);
520 if (! info.name.isEmpty())
521 return StringArray { info.name };
523 const auto numChannelsNeeded = isInput ? numInputChansNeeded : numOutputChansNeeded;
524 auto deviceNames = numChannelsNeeded > 0 ? type->getDeviceNames (isInput) : StringArray {};
525 deviceNames.move (type->getDefaultDeviceIndex (isInput), 0);
530 std::map<std::pair<Direction, String>, Array<double>> sampleRatesCache;
532 const auto getSupportedSampleRates = [&sampleRatesCache, type] (Direction dir,
const String& deviceName)
534 const auto key = std::make_pair (dir, deviceName);
536 auto& entry = [&]() ->
auto&
538 auto it = sampleRatesCache.find (key);
540 if (it != sampleRatesCache.end())
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();
555 const auto validate = [&getSupportedSampleRates] (
const String& outputDeviceName,
const String& inputDeviceName)
557 jassert (! outputDeviceName.isEmpty() && ! inputDeviceName.isEmpty());
559 const auto outputSampleRates = getSupportedSampleRates (Direction::out, outputDeviceName);
560 const auto inputSampleRates = getSupportedSampleRates (Direction::in, inputDeviceName);
562 return std::any_of (inputSampleRates.begin(),
563 inputSampleRates.end(),
564 [&] (
auto inputSampleRate) { return outputSampleRates.contains (inputSampleRate); });
567 auto outputsToTest = getDevicesToTestForMatchingSampleRate (Direction::out);
568 auto inputsToTest = getDevicesToTestForMatchingSampleRate (Direction::in);
572 if (setup.outputDeviceName.isEmpty() && ! outputsToTest.isEmpty())
573 setup.outputDeviceName = outputsToTest[0];
575 if (setup.inputDeviceName.isEmpty() && ! inputsToTest.isEmpty())
576 setup.inputDeviceName = inputsToTest[0];
580 for (
const auto& out : outputsToTest)
582 for (
const auto& in : inputsToTest)
584 if (validate (out, in))
586 setup.outputDeviceName = out;
587 setup.inputDeviceName = in;
598 if (lastExplicitSettings !=
nullptr)
599 return std::make_unique<XmlElement> (*lastExplicitSettings);
605void AudioDeviceManager::scanDevicesIfNeeded()
607 if (listNeedsScanning)
609 listNeedsScanning =
false;
611 createDeviceTypesIfNeeded();
613 for (
auto* type : availableDeviceTypes)
614 type->scanForDevices();
618AudioIODeviceType* AudioDeviceManager::findType (
const String& typeName)
620 scanDevicesIfNeeded();
622 for (
auto* type : availableDeviceTypes)
623 if (type->getTypeName() == typeName)
629AudioIODeviceType* AudioDeviceManager::findType (
const String& inputName,
const String& outputName)
631 scanDevicesIfNeeded();
633 for (
auto* type : availableDeviceTypes)
634 if ((inputName.isNotEmpty() && deviceListContains (type, true, inputName))
635 || (outputName.isNotEmpty() && deviceListContains (type, false, outputName)))
648 setup = currentSetup;
651void AudioDeviceManager::deleteCurrentDevice()
653 currentAudioDevice.reset();
660 for (
int i = 0; i < availableDeviceTypes.size(); ++i)
662 if (availableDeviceTypes.getUnchecked (i)->getTypeName() == type
663 && currentDeviceType != type)
665 if (currentAudioDevice !=
nullptr)
672 currentDeviceType = type;
675 insertDefaultDeviceNames (s);
687 return currentAudioDevice !=
nullptr ? currentAudioDevice->getWorkgroup() :
AudioWorkgroup{};
692 for (
auto* type : availableDeviceTypes)
693 if (type->getTypeName() == currentDeviceType)
696 return availableDeviceTypes.getFirst();
707 else if (defaultNumChannels != -1)
710 channels.setRange (0, defaultNumChannels,
true);
721 jassert (&
newSetup != ¤tSetup);
725 else if (currentAudioDevice !=
nullptr)
731 || (
newSetup.inputDeviceName.isEmpty() &&
newSetup.outputDeviceName.isEmpty()))
733 deleteCurrentDevice();
745 || currentAudioDevice ==
nullptr;
749 deleteCurrentDevice();
750 scanDevicesIfNeeded();
754 for (
const auto isInput : {
false,
true })
759 return "No such device: " + name;
762 currentAudioDevice.reset (type->createDevice (
newSetup.outputDeviceName,
newSetup.inputDeviceName));
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!";
769 error = currentAudioDevice->getLastError();
773 deleteCurrentDevice();
783 updateSetupChannels (currentSetup, numInputChansNeeded, numOutputChansNeeded);
796 error = currentAudioDevice->open (currentSetup.
inputChannels,
803 currentDeviceType = currentAudioDevice->getTypeName();
805 currentAudioDevice->start (callbackHandler.get());
807 error = currentAudioDevice->getLastError();
812 updateCurrentSetup();
814 for (
int i = 0; i < availableDeviceTypes.size(); ++i)
815 if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType)
816 *(lastDeviceTypeConfigs.getUnchecked (i)) = currentSetup;
823 deleteCurrentDevice();
829double AudioDeviceManager::chooseBestSampleRate (
double rate)
const
831 jassert (currentAudioDevice !=
nullptr);
833 auto rates = currentAudioDevice->getAvailableSampleRates();
835 if (rate > 0 &&
rates.contains (rate))
838 rate = currentAudioDevice->getCurrentSampleRate();
840 if (rate > 0 &&
rates.contains (rate))
845 for (
int i =
rates.size(); --i >= 0;)
853 if (lowestAbove44 > 0.0)
854 return lowestAbove44;
859int AudioDeviceManager::chooseBestBufferSize (
int bufferSize)
const
861 jassert (currentAudioDevice !=
nullptr);
863 if (bufferSize > 0 && currentAudioDevice->getAvailableBufferSizes().contains (bufferSize))
866 return currentAudioDevice->getDefaultBufferSize();
869void AudioDeviceManager::stopDevice()
871 if (currentAudioDevice !=
nullptr)
872 currentAudioDevice->stop();
880 currentAudioDevice.reset();
881 loadMeasurer.
reset();
886 if (currentAudioDevice ==
nullptr)
903void AudioDeviceManager::updateXml()
905 lastExplicitSettings.reset (
new XmlElement (
"DEVICESETUP"));
907 lastExplicitSettings->setAttribute (
"deviceType", currentDeviceType);
908 lastExplicitSettings->setAttribute (
"audioOutputDeviceName", currentSetup.
outputDeviceName);
909 lastExplicitSettings->setAttribute (
"audioInputDeviceName", currentSetup.
inputDeviceName);
911 if (currentAudioDevice !=
nullptr)
913 lastExplicitSettings->setAttribute (
"audioDeviceRate", currentAudioDevice->getCurrentSampleRate());
915 if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples())
916 lastExplicitSettings->setAttribute (
"audioDeviceBufferSize", currentAudioDevice->getCurrentBufferSizeSamples());
925 for (
auto& input : enabledMidiInputs)
927 auto* child = lastExplicitSettings->createNewChildElement (
"MIDIINPUT");
929 child->setAttribute (
"name", input->getName());
930 child->setAttribute (
"identifier", input->getIdentifier());
933 if (midiDeviceInfosFromXml.size() > 0)
939 for (
auto& d : midiDeviceInfosFromXml)
941 if (! availableMidiDevices.contains (d))
943 auto* child = lastExplicitSettings->createNewChildElement (
"MIDIINPUT");
945 child->setAttribute (
"name", d.name);
946 child->setAttribute (
"identifier", d.identifier);
951 if (defaultMidiOutputDeviceInfo != MidiDeviceInfo())
953 lastExplicitSettings->setAttribute (
"defaultMidiOutput", defaultMidiOutputDeviceInfo.
name);
954 lastExplicitSettings->setAttribute (
"defaultMidiOutputDevice", defaultMidiOutputDeviceInfo.
identifier);
968 if (currentAudioDevice !=
nullptr &&
newCallback !=
nullptr)
969 newCallback->audioDeviceAboutToStart (currentAudioDevice.get());
993void AudioDeviceManager::audioDeviceIOCallbackInt (
const float*
const*
inputChannelData,
1004 if (callbacks.size() > 0)
1010 callbacks.getUnchecked (0)->audioDeviceIOCallbackWithContext (
inputChannelData,
1019 for (
int i = callbacks.size(); --i > 0;)
1021 callbacks.getUnchecked (i)->audioDeviceIOCallbackWithContext (
inputChannelData,
1032 for (
int j = 0;
j < numSamples; ++
j)
1039 for (
int i = 0; i < numOutputChannels; ++i)
1040 zeromem (outputChannelData[i], (
size_t) numSamples *
sizeof (float));
1043 if (testSound !=
nullptr)
1045 auto numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition);
1046 auto* src = testSound->getReadPointer (0, testSoundPosition);
1048 for (
int i = 0; i < numOutputChannels; ++i)
1049 if (
auto* dst = outputChannelData [i])
1050 for (
int j = 0; j < numSamps; ++j)
1053 testSoundPosition += numSamps;
1055 if (testSoundPosition >= testSound->getNumSamples())
1059 outputLevelGetter->updateLevel (outputChannelData, numOutputChannels, numSamples);
1062void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice*
const device)
1064 loadMeasurer.
reset (device->getCurrentSampleRate(),
1065 device->getCurrentBufferSizeSamples());
1067 updateCurrentSetup();
1070 const ScopedLock sl (audioCallbackLock);
1072 for (
int i = callbacks.size(); --i >= 0;)
1073 callbacks.getUnchecked (i)->audioDeviceAboutToStart (device);
1079void AudioDeviceManager::audioDeviceStoppedInt()
1083 const ScopedLock sl (audioCallbackLock);
1085 loadMeasurer.
reset();
1087 for (
int i = callbacks.size(); --i >= 0;)
1088 callbacks.getUnchecked (i)->audioDeviceStopped();
1091void AudioDeviceManager::audioDeviceErrorInt (
const String& message)
1093 const ScopedLock sl (audioCallbackLock);
1095 for (
int i = callbacks.size(); --i >= 0;)
1096 callbacks.getUnchecked (i)->audioDeviceError (message);
1113 enabledMidiInputs.push_back (std::move (
midiIn));
1114 enabledMidiInputs.back()->start();
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));
1131 for (
auto&
mi : enabledMidiInputs)
1132 if (
mi->getIdentifier() == identifier)
1151 for (
int i = midiCallbacks.
size(); --i >= 0;)
1158 midiCallbacks.
remove (i);
1163void AudioDeviceManager::handleIncomingMidiMessageInt (
MidiInput* source,
const MidiMessage& message)
1169 for (
auto&
mc : midiCallbacks)
1170 if (
mc.deviceIdentifier.isEmpty() ||
mc.deviceIdentifier == source->getIdentifier())
1171 mc.callback->handleIncomingMidiMessage (source, message);
1178 if (defaultMidiOutputDeviceInfo.
identifier != identifier)
1188 if (currentAudioDevice !=
nullptr)
1197 if (defaultMidiOutput !=
nullptr)
1198 defaultMidiOutputDeviceInfo = defaultMidiOutput->getDeviceInfo();
1200 defaultMidiOutputDeviceInfo = {};
1202 if (currentAudioDevice !=
nullptr)
1204 c->audioDeviceAboutToStart (currentAudioDevice.get());
1217AudioDeviceManager::LevelMeter::LevelMeter()
noexcept : level() {}
1219void AudioDeviceManager::LevelMeter::updateLevel (
const float*
const* channelData,
int numChannels,
int numSamples)
noexcept
1221 if (getReferenceCount() <= 1)
1224 auto localLevel = level.get();
1226 if (numChannels > 0)
1228 for (
int j = 0; j < numSamples; ++j)
1232 for (
int i = 0; i < numChannels; ++i)
1233 s += std::abs (channelData[i][j]);
1235 s /= (float) numChannels;
1237 const float decayFactor = 0.99992f;
1241 else if (localLevel > 0.001f)
1242 localLevel *= decayFactor;
1255double AudioDeviceManager::LevelMeter::getCurrentLevel() const noexcept
1257 jassert (getReferenceCount() > 1);
1264 std::unique_ptr<AudioBuffer<float>>
oldSound;
1272 testSoundPosition = 0;
1274 if (currentAudioDevice !=
nullptr)
1276 auto sampleRate = currentAudioDevice->getCurrentSampleRate();
1279 double frequency = 440.0;
1280 float amplitude = 0.5f;
1287 newSound->setSample (0, i, amplitude * (
float) std::sin (i * phasePerSample));
1301 auto deviceXRuns = (currentAudioDevice !=
nullptr ? currentAudioDevice->getXRunCount() : -1);
1307void AudioDeviceManager::setMidiInputEnabled (
const String& name,
const bool enabled)
1319bool AudioDeviceManager::isMidiInputEnabled (
const String& name)
const
1321 for (
auto& device : MidiInput::getAvailableDevices())
1322 if (device.name == name)
1328void AudioDeviceManager::addMidiInputCallback (
const String& name, MidiInputCallback* callbackToAdd)
1336 for (
auto& device : MidiInput::getAvailableDevices())
1338 if (device.name == name)
1347void AudioDeviceManager::removeMidiInputCallback (
const String& name, MidiInputCallback* callbackToRemove)
1355 for (
auto& device : MidiInput::getAvailableDevices())
1357 if (device.name == name)
1366void AudioDeviceManager::setDefaultMidiOutput (
const String& name)
1368 for (
auto& device : MidiOutput::getAvailableDevices())
1370 if (device.name == name)
1382class AudioDeviceManagerTests final :
public UnitTest
1385 AudioDeviceManagerTests() : UnitTest (
"AudioDeviceManager", UnitTestCategories::audio) {}
1387 void runTest()
override
1389 beginTest (
"When the AudioDeviceSetup has non-empty device names, initialise uses the requested devices");
1391 AudioDeviceManager manager;
1392 initialiseManager (manager);
1394 expectEquals (manager.getAvailableDeviceTypes().size(), 2);
1396 AudioDeviceManager::AudioDeviceSetup setup;
1398 setup.inputDeviceName =
"c";
1400 expect (manager.initialise (2, 2,
nullptr,
true, String{}, &setup).isEmpty());
1402 const auto& newSetup = manager.getAudioDeviceSetup();
1404 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1405 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1407 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1408 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1411 beginTest (
"When the AudioDeviceSetup has empty device names, initialise picks suitable default devices");
1413 AudioDeviceManager manager;
1414 initialiseManager (manager);
1416 AudioDeviceManager::AudioDeviceSetup setup;
1418 expect (manager.initialise (2, 2,
nullptr,
true, String{}, &setup).isEmpty());
1420 const auto& newSetup = manager.getAudioDeviceSetup();
1422 expectEquals (newSetup.outputDeviceName, String (
"x"));
1423 expectEquals (newSetup.inputDeviceName, String (
"a"));
1425 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1426 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1429 beginTest (
"When the preferred device name matches an input and an output on the same type, that type is used");
1431 AudioDeviceManager manager;
1432 initialiseManagerWithDifferentDeviceNames (manager);
1434 expect (manager.initialise (2, 2,
nullptr,
true,
"bar *").isEmpty());
1436 expectEquals (manager.getCurrentAudioDeviceType(), String (
"bar"));
1438 const auto& newSetup = manager.getAudioDeviceSetup();
1440 expectEquals (newSetup.outputDeviceName, String (
"bar out a"));
1441 expectEquals (newSetup.inputDeviceName, String (
"bar in a"));
1443 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1444 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1446 expect (manager.getCurrentAudioDevice() !=
nullptr);
1449 beginTest (
"When the preferred device name matches either an input and an output, but not both, that type is used");
1451 AudioDeviceManager manager;
1452 initialiseManagerWithDifferentDeviceNames (manager);
1454 expect (manager.initialise (2, 2,
nullptr,
true,
"bar out b").isEmpty());
1456 expectEquals (manager.getCurrentAudioDeviceType(), String (
"bar"));
1458 const auto& newSetup = manager.getAudioDeviceSetup();
1460 expectEquals (newSetup.outputDeviceName, String (
"bar out b"));
1461 expectEquals (newSetup.inputDeviceName, String (
"bar in a"));
1463 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1464 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1466 expect (manager.getCurrentAudioDevice() !=
nullptr);
1469 beginTest (
"When the preferred device name does not match any inputs or outputs, defaults are used");
1471 AudioDeviceManager manager;
1472 initialiseManagerWithDifferentDeviceNames (manager);
1474 expect (manager.initialise (2, 2,
nullptr,
true,
"unmatchable").isEmpty());
1476 expectEquals (manager.getCurrentAudioDeviceType(), String (
"foo"));
1478 const auto& newSetup = manager.getAudioDeviceSetup();
1480 expectEquals (newSetup.outputDeviceName, String (
"foo out a"));
1481 expectEquals (newSetup.inputDeviceName, String (
"foo in a"));
1483 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1484 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1486 expect (manager.getCurrentAudioDevice() !=
nullptr);
1489 beginTest (
"When first device type has no devices, a device type with devices is used instead");
1491 AudioDeviceManager manager;
1492 initialiseManagerWithEmptyDeviceType (manager);
1494 AudioDeviceManager::AudioDeviceSetup setup;
1496 expect (manager.initialise (2, 2,
nullptr,
true, {}, &setup).isEmpty());
1498 const auto& newSetup = manager.getAudioDeviceSetup();
1500 expectEquals (newSetup.outputDeviceName, String (
"x"));
1501 expectEquals (newSetup.inputDeviceName, String (
"a"));
1503 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1504 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1507 beginTest (
"If a device type has been explicitly set to a type with devices, "
1508 "initialisation should respect this choice");
1510 AudioDeviceManager manager;
1511 initialiseManagerWithEmptyDeviceType (manager);
1512 manager.setCurrentAudioDeviceType (mockBName,
true);
1514 AudioDeviceManager::AudioDeviceSetup setup;
1515 expect (manager.initialise (2, 2,
nullptr,
true, {}, &setup).isEmpty());
1517 expectEquals (manager.getCurrentAudioDeviceType(), mockBName);
1520 beginTest (
"If a device type has been explicitly set to a type without devices, "
1521 "initialisation should pick a type with devices instead");
1523 AudioDeviceManager manager;
1524 initialiseManagerWithEmptyDeviceType (manager);
1525 manager.setCurrentAudioDeviceType (emptyName,
true);
1527 AudioDeviceManager::AudioDeviceSetup setup;
1528 expect (manager.initialise (2, 2,
nullptr,
true, {}, &setup).isEmpty());
1530 expectEquals (manager.getCurrentAudioDeviceType(), mockAName);
1533 beginTest (
"Carry out a long sequence of configuration changes");
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);
1549 beginTest (
"AudioDeviceManager updates its current settings before notifying callbacks when device restarts itself");
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));
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);
1567 const auto currentSetup = manager.getAudioDeviceSetup();
1568 expectEquals (currentSetup.sampleRate, setup.sampleRate);
1569 expectEquals (currentSetup.bufferSize, setup.bufferSize);
1571 MockCallback callback;
1572 manager.addAudioCallback (&callback);
1574 constexpr auto newSr = 10000.0;
1575 constexpr auto newBs = 1024;
1579 callback.aboutToStart = [&]
1582 const auto current = manager.getAudioDeviceSetup();
1583 expectEquals (current.sampleRate, newSr);
1584 expectEquals (current.bufferSize, newBs);
1587 ptr->restartDevices (newSr, newBs);
1588 expectEquals (numCalls, 1);
1593 void initialiseWithDefaultDevices (AudioDeviceManager& manager)
1595 manager.initialiseWithDefaultDevices (2, 2);
1596 const auto& setup = manager.getAudioDeviceSetup();
1598 expectEquals (setup.inputChannels.countNumberOfSetBits(), 2);
1599 expectEquals (setup.outputChannels.countNumberOfSetBits(), 2);
1601 expect (setup.useDefaultInputChannels);
1602 expect (setup.useDefaultOutputChannels);
1604 expect (manager.getCurrentAudioDevice() !=
nullptr);
1607 void disableInputChannelsButLeaveDeviceOpen (AudioDeviceManager& manager)
1609 auto setup = manager.getAudioDeviceSetup();
1610 setup.inputChannels.clear();
1611 setup.useDefaultInputChannels =
false;
1613 expect (manager.setAudioDeviceSetup (setup,
true).isEmpty());
1615 const auto newSetup = manager.getAudioDeviceSetup();
1616 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1617 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1619 expect (! newSetup.useDefaultInputChannels);
1620 expect (newSetup.useDefaultOutputChannels);
1622 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1623 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1625 expect (manager.getCurrentAudioDevice() !=
nullptr);
1628 void selectANewInputDevice (AudioDeviceManager& manager)
1630 auto setup = manager.getAudioDeviceSetup();
1631 setup.inputDeviceName =
"b";
1633 expect (manager.setAudioDeviceSetup (setup,
true).isEmpty());
1635 const auto newSetup = manager.getAudioDeviceSetup();
1636 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1637 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1639 expect (! newSetup.useDefaultInputChannels);
1640 expect (newSetup.useDefaultOutputChannels);
1642 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1643 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1645 expect (manager.getCurrentAudioDevice() !=
nullptr);
1648 void disableInputDevice (AudioDeviceManager& manager)
1650 auto setup = manager.getAudioDeviceSetup();
1651 setup.inputDeviceName =
"";
1653 expect (manager.setAudioDeviceSetup (setup,
true).isEmpty());
1655 const auto newSetup = manager.getAudioDeviceSetup();
1656 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1657 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1659 expect (! newSetup.useDefaultInputChannels);
1660 expect (newSetup.useDefaultOutputChannels);
1662 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1663 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1665 expect (manager.getCurrentAudioDevice() !=
nullptr);
1668 void reenableInputDeviceWithNoChannels (AudioDeviceManager& manager)
1670 auto setup = manager.getAudioDeviceSetup();
1671 setup.inputDeviceName =
"a";
1673 expect (manager.setAudioDeviceSetup (setup,
true).isEmpty());
1675 const auto newSetup = manager.getAudioDeviceSetup();
1676 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1677 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1679 expect (! newSetup.useDefaultInputChannels);
1680 expect (newSetup.useDefaultOutputChannels);
1682 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1683 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1685 expect (manager.getCurrentAudioDevice() !=
nullptr);
1688 void enableInputChannels (AudioDeviceManager& manager)
1690 auto setup = manager.getAudioDeviceSetup();
1691 setup.inputDeviceName = manager.getCurrentDeviceTypeObject()->getDeviceNames (
true)[0];
1692 setup.inputChannels = 3;
1693 setup.useDefaultInputChannels =
false;
1695 expect (manager.setAudioDeviceSetup (setup,
true).isEmpty());
1697 const auto newSetup = manager.getAudioDeviceSetup();
1698 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1699 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1701 expect (! newSetup.useDefaultInputChannels);
1702 expect (newSetup.useDefaultOutputChannels);
1704 expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
1705 expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
1707 expect (manager.getCurrentAudioDevice() !=
nullptr);
1710 void switchDeviceType (AudioDeviceManager& manager)
1712 const auto oldSetup = manager.getAudioDeviceSetup();
1714 expectEquals (manager.getCurrentAudioDeviceType(), String (mockAName));
1716 manager.setCurrentAudioDeviceType (mockBName,
true);
1718 expectEquals (manager.getCurrentAudioDeviceType(), String (mockBName));
1720 const auto newSetup = manager.getAudioDeviceSetup();
1722 expect (newSetup.outputDeviceName.isNotEmpty());
1724 expect (newSetup.inputDeviceName.isEmpty());
1726 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
1727 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1729 expect (manager.getCurrentAudioDevice() !=
nullptr);
1732 void closeDeviceByRequestingEmptyNames (AudioDeviceManager& manager)
1734 auto setup = manager.getAudioDeviceSetup();
1735 setup.inputDeviceName =
"";
1736 setup.outputDeviceName =
"";
1738 expect (manager.setAudioDeviceSetup (setup,
true).isEmpty());
1740 const auto newSetup = manager.getAudioDeviceSetup();
1741 expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
1742 expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
1744 expect (newSetup.inputDeviceName.isEmpty());
1745 expect (newSetup.outputDeviceName.isEmpty());
1747 expect (manager.getCurrentAudioDevice() ==
nullptr);
1750 const String mockAName =
"mockA";
1751 const String mockBName =
"mockB";
1752 const String emptyName =
"empty";
1756 virtual ~Restartable() =
default;
1757 virtual void restart (
double newSr,
int newBs) = 0;
1760 class MockDevice final :
public AudioIODevice,
1764 MockDevice (ListenerList<Restartable>& l, String typeNameIn, String outNameIn, String inNameIn)
1765 : AudioIODevice (
"mock", typeNameIn), listeners (l), outName (outNameIn), inName (inNameIn)
1767 listeners.add (
this);
1770 ~MockDevice()
override
1772 listeners.remove (
this);
1775 StringArray getOutputChannelNames()
override {
return {
"o1",
"o2",
"o3" }; }
1776 StringArray getInputChannelNames()
override {
return {
"i1",
"i2",
"i3" }; }
1778 Array<double> getAvailableSampleRates()
override {
return { 44100.0, 48000.0 }; }
1779 Array<int> getAvailableBufferSizes()
override {
return { 128, 256 }; }
1780 int getDefaultBufferSize()
override {
return 128; }
1782 String open (
const BigInteger& inputs,
const BigInteger& outputs,
double sr,
int bs)
override
1784 inChannels = inputs;
1785 outChannels = outputs;
1792 void close()
override { on =
false; }
1793 bool isOpen()
override {
return on; }
1795 void start (AudioIODeviceCallback* c)
override
1798 callback->audioDeviceAboutToStart (
this);
1802 void stop()
override
1805 callback->audioDeviceStopped();
1808 bool isPlaying()
override {
return playing; }
1810 String getLastError()
override {
return {}; }
1811 int getCurrentBufferSizeSamples()
override {
return blockSize; }
1812 double getCurrentSampleRate()
override {
return sampleRate; }
1813 int getCurrentBitDepth()
override {
return 16; }
1815 BigInteger getActiveOutputChannels()
const override {
return outChannels; }
1816 BigInteger getActiveInputChannels()
const override {
return inChannels; }
1818 int getOutputLatencyInSamples()
override {
return 0; }
1819 int getInputLatencyInSamples()
override {
return 0; }
1822 void restart (
double newSr,
int newBs)
override
1826 open (inChannels, outChannels, newSr, newBs);
1830 ListenerList<Restartable>& listeners;
1831 AudioIODeviceCallback* callback =
nullptr;
1832 String outName, inName;
1833 BigInteger outChannels, inChannels;
1834 double sampleRate = 0.0;
1836 bool on =
false, playing =
false;
1839 class MockDeviceType final :
public AudioIODeviceType
1842 explicit MockDeviceType (String kind)
1843 : MockDeviceType (std::move (kind), {
"a",
"b",
"c" }, {
"x",
"y",
"z" }) {}
1845 MockDeviceType (String kind, StringArray inputNames, StringArray outputNames)
1846 : AudioIODeviceType (std::move (kind)),
1847 inNames (std::move (inputNames)),
1848 outNames (std::move (outputNames)) {}
1850 ~MockDeviceType()
override
1853 jassert (listeners.isEmpty());
1856 void scanForDevices()
override {}
1858 StringArray getDeviceNames (
bool isInput)
const override
1860 return getNames (isInput);
1863 int getDefaultDeviceIndex (
bool)
const override {
return 0; }
1865 int getIndexOfDevice (AudioIODevice* device,
bool isInput)
const override
1867 return getNames (isInput).indexOf (device->getName());
1870 bool hasSeparateInputsAndOutputs()
const override {
return true; }
1872 AudioIODevice* createDevice (
const String& outputName,
const String& inputName)
override
1874 if (inNames.contains (inputName) || outNames.contains (outputName))
1875 return new MockDevice (listeners, getTypeName(), outputName, inputName);
1882 void restartDevices (
double newSr,
int newBs)
1884 listeners.call ([&] (
auto& l) {
return l.restart (newSr, newBs); });
1888 const StringArray& getNames (
bool isInput)
const {
return isInput ? inNames : outNames; }
1890 const StringArray inNames, outNames;
1891 ListenerList<Restartable> listeners;
1894 class MockCallback final :
public AudioIODeviceCallback
1897 std::function<void()> callback;
1898 std::function<void()> aboutToStart;
1899 std::function<void()> stopped;
1900 std::function<void()> error;
1902 void audioDeviceIOCallbackWithContext (
const float*
const*,
1907 const AudioIODeviceCallbackContext&)
override
1909 NullCheckedInvocation::invoke (callback);
1912 void audioDeviceAboutToStart (AudioIODevice*)
override { NullCheckedInvocation::invoke (aboutToStart); }
1913 void audioDeviceStopped()
override { NullCheckedInvocation::invoke (stopped); }
1914 void audioDeviceError (
const String&)
override { NullCheckedInvocation::invoke (error); }
1917 void initialiseManager (AudioDeviceManager& manager)
1919 manager.addAudioDeviceType (std::make_unique<MockDeviceType> (mockAName));
1920 manager.addAudioDeviceType (std::make_unique<MockDeviceType> (mockBName));
1923 void initialiseManagerWithEmptyDeviceType (AudioDeviceManager& manager)
1925 manager.addAudioDeviceType (std::make_unique<MockDeviceType> (emptyName, StringArray{}, StringArray{}));
1926 initialiseManager (manager);
1929 void initialiseManagerWithDifferentDeviceNames (AudioDeviceManager& manager)
1931 manager.addAudioDeviceType (std::make_unique<MockDeviceType> (
"foo",
1932 StringArray {
"foo in a",
"foo in b" },
1933 StringArray {
"foo out a",
"foo out b" }));
1935 manager.addAudioDeviceType (std::make_unique<MockDeviceType> (
"bar",
1936 StringArray {
"bar in a",
"bar in b" },
1937 StringArray {
"bar out a",
"bar out b" }));
1941static AudioDeviceManagerTests audioDeviceManagerTests;
int size() const noexcept
void remove(int indexToRemove)
void add(const ElementType &newElement)
ElementType & getReference(int index) noexcept
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)
double getCpuUsage() const
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)
int getXRunCount() const noexcept
void addMidiInputDeviceCallback(const String &deviceIdentifier, MidiInputCallback *callback)
void setCurrentAudioDeviceType(const String &type, bool treatAsChosenDevice)
~AudioDeviceManager() override
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 restartLastAudioDevice()
void removeAudioCallback(AudioIODeviceCallback *callback)
void addAudioDeviceType(std::unique_ptr< AudioIODeviceType > newDeviceType)
AudioWorkgroup getDeviceAudioWorkgroup() const
std::unique_ptr< XmlElement > createStateXml() const
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()
double getLoadAsProportion() const
String toString(int base, int minimumNumCharacters=1) const
bool isZero() const noexcept
int countNumberOfSetBits() const noexcept
void sendSynchronousChangeMessage()
bool isActiveSense() const noexcept
static std::unique_ptr< MidiOutput > openDevice(const String &deviceIdentifier)
static Array< MidiDeviceInfo > getAvailableDevices()
bool isEmpty() const noexcept
bool matchesWildcard(StringRef wildcard, bool ignoreCase) const noexcept
bool isNotEmpty() const noexcept
static void JUCE_CALLTYPE sleep(int milliseconds)
bool hasTagName(StringRef possibleTagName) const noexcept
bool useDefaultInputChannels
BigInteger outputChannels
bool useDefaultOutputChannels