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)
48 s.useDefaultInputChannels,
50 s.useDefaultOutputChannels);
53bool AudioDeviceManager::AudioDeviceSetup::operator== (
const AudioDeviceManager::AudioDeviceSetup&
other)
const
58bool AudioDeviceManager::AudioDeviceSetup::operator!= (
const AudioDeviceManager::AudioDeviceSetup&
other)
const
72 void audioDeviceIOCallbackWithContext (
const float*
const* ins,
79 owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples, context);
84 owner.audioDeviceAboutToStartInt (device);
87 void audioDeviceStopped()
override
89 owner.audioDeviceStoppedInt();
92 void audioDeviceError (
const String& message)
override
94 owner.audioDeviceErrorInt (message);
99 owner.handleIncomingMidiMessageInt (source, message);
102 void audioDeviceListChanged()
override
104 owner.audioDeviceListChanged();
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()
148 return ! ptr->getDeviceNames (
true) .isEmpty()
149 || ! ptr->getDeviceNames (
false).isEmpty();
152 if (
auto* type = findType (currentDeviceType))
156 const auto iter =
std::find_if (availableDeviceTypes.begin(),
157 availableDeviceTypes.end(),
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)
212 initialiseFromXML (*e,
true, preferredDeviceName, ¤tSetup);
214 initialiseDefault (preferredDeviceName, ¤tSetup);
217 updateCurrentSetup();
223void AudioDeviceManager::midiDeviceListChanged()
225 openLastRequestedMidiDevices (midiDeviceInfosFromXml, defaultMidiOutputDeviceInfo);
232 if (device !=
nullptr)
257 jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size());
262 availableDeviceTypes.getLast()->addListener (callbackHandler.get());
270 jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size());
276 removed->removeListener (callbackHandler.get());
277 lastDeviceTypeConfigs.remove (index,
true);
284 for (
auto&
deviceName : type->getDeviceNames (isInput))
299 scanDevicesIfNeeded();
300 pickCurrentDeviceTypeWithDevices();
306 if (xml !=
nullptr && xml->
hasTagName (
"DEVICESETUP"))
316 AudioDeviceSetup setup;
338 return WildcardMatch { iter != names.end() ? *iter : String(), iter != names.end() };
343 WildcardMatch input, output;
354 String type, input, output;
357 const auto result = [&]
360 for (
auto* type : availableDeviceTypes)
364 if (matches.input.successful && matches.output.successful)
370 for (
auto* type : availableDeviceTypes)
374 if (matches.input.successful || matches.output.successful)
382 currentDeviceType = result.type;
383 setup.inputDeviceName = result.input;
384 setup.outputDeviceName = result.output;
387 insertDefaultDeviceNames (setup);
391String AudioDeviceManager::initialiseFromXML (
const XmlElement& xml,
396 lastExplicitSettings.reset (
new XmlElement (xml));
399 AudioDeviceSetup setup;
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");
439 enabledMidiInputs.clear();
445 for (
auto* c : xml.getChildWithTagNameIterator (
"MIDIINPUT"))
446 result.add ({ c->getStringAttribute (
"name"), c->getStringAttribute (
"identifier") });
452 xml.getStringAttribute (
"defaultMidiOutputDevice"));
467 return x.identifier == deviceToOpen.identifier;
478 return x.name == deviceToOpen.name;
489 for (
const auto& info : midiDeviceInfosFromXml)
500 lastExplicitSettings.reset();
503 nullptr,
false, {},
nullptr);
506void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup)
const
508 enum class Direction { out, in };
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;
525 deviceNames.move (type->getDefaultDeviceIndex (isInput), 0);
536 auto& entry = [&]() ->
auto&
557 jassert (! outputDeviceName.isEmpty() && ! inputDeviceName.isEmpty());
564 [&] (
auto inputSampleRate) { return outputSampleRates.contains (inputSampleRate); });
572 if (setup.outputDeviceName.isEmpty() && !
outputsToTest.isEmpty())
575 if (setup.inputDeviceName.isEmpty() && !
inputsToTest.isEmpty())
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)
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();
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 })
756 const auto name = getSetupInfo (
newSetup, isInput).name;
758 if (name.
isNotEmpty() && ! deviceListContains (type, isInput, name))
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;)
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)
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,
994 int numInputChannels,
996 int numOutputChannels,
1002 inputLevelGetter->updateLevel (
inputChannelData, numInputChannels, numSamples);
1004 if (callbacks.size() > 0)
1008 tempBuffer.
setSize (
jmax (1, numOutputChannels),
jmax (1, numSamples),
false,
false,
true);
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)
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)
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();
1072 for (
int i = callbacks.size(); --i >= 0;)
1073 callbacks.getUnchecked (i)->audioDeviceAboutToStart (device);
1079void AudioDeviceManager::audioDeviceStoppedInt()
1085 loadMeasurer.
reset();
1087 for (
int i = callbacks.size(); --i >= 0;)
1088 callbacks.getUnchecked (i)->audioDeviceStopped();
1091void AudioDeviceManager::audioDeviceErrorInt (
const String& message)
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();
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)
1226 if (numChannels > 0)
1228 for (
int j = 0;
j < numSamples; ++
j)
1232 for (
int i = 0; i < numChannels; ++i)
1235 s /= (
float) numChannels;
1255double AudioDeviceManager::LevelMeter::getCurrentLevel()
const noexcept
1257 jassert (getReferenceCount() > 1);
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)
1309 for (
auto& device :
MidiInput::getAvailableDevices())
1311 if (device.name == name)
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)
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
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" }));
Holds a resizable array of primitive or copy-by-value objects.
int size() const noexcept
Returns the current number of elements in the array.
void remove(int indexToRemove)
Removes an element from the array.
void add(const ElementType &newElement)
Appends a new element at the end of the array.
ElementType & getReference(int index) noexcept
Returns a direct reference to one of the elements in the array, without checking the index passed in.
A multi-channel buffer containing floating point audio samples.
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
Changes the buffer's size or number of channels.
Type *const * getArrayOfWritePointers() noexcept
Returns an array of pointers to the channels in the buffer.
Manages the state of some audio and midi i/o devices.
bool isMidiInputDeviceEnabled(const String &deviceIdentifier) const
Returns true if a given midi input device is being used.
AudioDeviceManager()
Creates a default AudioDeviceManager.
void removeAudioDeviceType(AudioIODeviceType *deviceTypeToRemove)
Removes a previously added device type from the manager.
AudioDeviceSetup getAudioDeviceSetup() const
Returns the current device properties that are in use.
void removeMidiInputDeviceCallback(const String &deviceIdentifier, MidiInputCallback *callback)
Removes a listener that was previously registered with addMidiInputDeviceCallback().
double getCpuUsage() const
Returns the average proportion of available CPU being spent inside the audio callbacks.
AudioIODeviceType * getCurrentDeviceTypeObject() const
Returns the currently active audio device type object.
String setAudioDeviceSetup(const AudioDeviceSetup &newSetup, bool treatAsChosenDevice)
Changes the current device or its settings.
virtual void createAudioDeviceTypes(OwnedArray< AudioIODeviceType > &types)
Creates a list of available types.
void setDefaultMidiOutputDevice(const String &deviceIdentifier)
Sets a midi output device to use as the default.
void setMidiInputDeviceEnabled(const String &deviceIdentifier, bool enabled)
Enables or disables a midi input device.
int getXRunCount() const noexcept
Returns the number of under- or over runs reported.
void addMidiInputDeviceCallback(const String &deviceIdentifier, MidiInputCallback *callback)
Registers a listener for callbacks when midi events arrive from a midi input.
void setCurrentAudioDeviceType(const String &type, bool treatAsChosenDevice)
Changes the class of audio device being used.
~AudioDeviceManager() override
Destructor.
const OwnedArray< AudioIODeviceType > & getAvailableDeviceTypes()
Returns a list of the types of device supported.
String initialise(int numInputChannelsNeeded, int numOutputChannelsNeeded, const XmlElement *savedState, bool selectDefaultDeviceOnFailure, const String &preferredDefaultDeviceName=String(), const AudioDeviceSetup *preferredSetupOptions=nullptr)
Opens a set of audio devices ready for use.
void addAudioCallback(AudioIODeviceCallback *newCallback)
Registers an audio callback to be used.
String initialiseWithDefaultDevices(int numInputChannelsNeeded, int numOutputChannelsNeeded)
Resets everything to a default device setup, clearing any stored settings.
void restartLastAudioDevice()
Tries to reload the last audio device that was running.
void removeAudioCallback(AudioIODeviceCallback *callback)
Deregisters a previously added callback.
void playTestSound()
Plays a beep through the current audio device.
void addAudioDeviceType(std::unique_ptr< AudioIODeviceType > newDeviceType)
Adds a new device type to the list of types.
AudioWorkgroup getDeviceAudioWorkgroup() const
Returns the current audio device workgroup, if supported.
void closeAudioDevice()
Closes the currently-open device.
std::unique_ptr< XmlElement > createStateXml() const
Returns some XML representing the current state of the manager.
One of these is passed to an AudioIODevice object to stream the audio data in and out.
A class for receiving events when audio devices are inserted or removed.
Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc.
static AudioIODeviceType * createAudioIODeviceType_WASAPI(WASAPIDeviceMode deviceMode)
Creates a WASAPI device type in the specified mode if it's available on this platform,...
static AudioIODeviceType * createAudioIODeviceType_ASIO()
Creates an ASIO device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_Oboe()
Creates an Oboe device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_JACK()
Creates a JACK device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_DirectSound()
Creates a DirectSound device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_Android()
Creates an Android device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_OpenSLES()
Creates an Android OpenSLES device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_CoreAudio()
Creates a CoreAudio device type if it's available on this platform, or returns null.
virtual StringArray getDeviceNames(bool wantInputNames=false) const =0
Returns the list of available devices of this type.
const String & getTypeName() const noexcept
Returns the name of this type of driver that this object manages.
static AudioIODeviceType * createAudioIODeviceType_Bela()
Creates a Bela device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_ALSA()
Creates an ALSA device type if it's available on this platform, or returns null.
static AudioIODeviceType * createAudioIODeviceType_iOSAudio()
Creates an iOS device type if it's available on this platform, or returns null.
Base class for an audio device with synchronised input and output channels.
int getXRunCount() const
Returns the number of over- (or under-) runs recorded since the state was reset.
void reset()
Resets the state.
double getLoadAsProportion() const
Returns the current load as a proportion 0 to 1.0.
A handle to an audio workgroup, which is a collection of realtime threads working together to produce...
An arbitrarily large integer class.
String toString(int base, int minimumNumCharacters=1) const
Converts the number to a string.
bool isZero() const noexcept
Returns true if no bits are set.
int countNumberOfSetBits() const noexcept
Returns the total number of set bits in the value.
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners.
void sendSynchronousChangeMessage()
Sends a synchronous change message to all the registered listeners.
Automatically locks and unlocks a mutex object.
Encapsulates a MIDI message.
bool isActiveSense() const noexcept
Returns true if this is an active-sense message.
static std::unique_ptr< MidiOutput > openDevice(const String &deviceIdentifier)
Tries to open one of the midi output devices.
static Array< MidiDeviceInfo > getAvailableDevices()
Returns a list of the available midi output devices.
An array designed for holding objects.
void clear(bool deleteObjects=true)
Clears the array, optionally deleting the objects inside it first.
bool isEmpty() const noexcept
Returns true if the string contains no characters.
bool matchesWildcard(StringRef wildcard, bool ignoreCase) const noexcept
Returns true if the string matches this simple wildcard expression.
void clear() noexcept
Resets this string to be empty.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
static void JUCE_CALLTYPE sleep(int milliseconds)
Suspends the execution of the current thread until the specified timeout period has elapsed (note tha...
Used to build a tree of elements representing an XML document.
bool hasTagName(StringRef possibleTagName) const noexcept
Tests whether this element has a particular tag name.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
std::unique_ptr< T > rawToUniquePtr(T *ptr)
Converts an owning raw pointer into a unique_ptr, deriving the type of the unique_ptr automatically.
void zeromem(void *memory, size_t numBytes) noexcept
Fills a block of memory with zeros.
Additional information that may be passed to the AudioIODeviceCallback.
This structure holds a set of properties describing the current audio setup.
String outputDeviceName
The name of the audio device used for output.
bool useDefaultInputChannels
If this is true, it indicates that the inputChannels array should be ignored, and instead,...
String inputDeviceName
The name of the audio device used for input.
BigInteger outputChannels
The set of active output channels.
double sampleRate
The current sample rate.
BigInteger inputChannels
The set of active input channels.
int bufferSize
The buffer size, in samples.
bool useDefaultOutputChannels
If this is true, it indicates that the outputChannels array should be ignored, and instead,...
This class measures the time between its construction and destruction and adds it to an AudioProcessL...
Commonly used mathematical constants.
String name
The name of this device.
String identifier
The identifier for this device.