1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

AudioDeviceManager: Always try to pick an initial device type that has some connected devices

This commit is contained in:
reuk 2021-08-25 12:19:08 +01:00
parent 10a26b7584
commit 34bda5d75b
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11
2 changed files with 402 additions and 6 deletions

View file

@ -109,11 +109,29 @@ void AudioDeviceManager::createDeviceTypesIfNeeded()
types.clear (false);
if (auto* first = availableDeviceTypes.getFirst())
currentDeviceType = first->getTypeName();
for (auto* type : availableDeviceTypes)
type->scanForDevices();
pickCurrentDeviceTypeWithDevices();
}
}
void AudioDeviceManager::pickCurrentDeviceTypeWithDevices()
{
const auto deviceTypeHasDevices = [] (const AudioIODeviceType* ptr)
{
return ! ptr->getDeviceNames (true) .isEmpty()
|| ! ptr->getDeviceNames (false).isEmpty();
};
const auto iter = std::find_if (availableDeviceTypes.begin(),
availableDeviceTypes.end(),
deviceTypeHasDevices);
if (iter != availableDeviceTypes.end())
currentDeviceType = (*iter)->getTypeName();
}
const OwnedArray<AudioIODeviceType>& AudioDeviceManager::getAvailableDeviceTypes()
{
scanDevicesIfNeeded();
@ -244,6 +262,7 @@ String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
const AudioDeviceSetup* preferredSetupOptions)
{
scanDevicesIfNeeded();
pickCurrentDeviceTypeWithDevices();
numInputChansNeeded = numInputChannelsNeeded;
numOutputChansNeeded = numOutputChannelsNeeded;
@ -1182,4 +1201,375 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& name)
}
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class AudioDeviceManagerTests : public UnitTest
{
public:
AudioDeviceManagerTests() : UnitTest ("AudioDeviceManager", UnitTestCategories::audio) {}
void runTest() override
{
beginTest ("When the AudioDeviceSetup has non-empty device names, initialise uses the requested devices");
{
AudioDeviceManager manager;
initialiseManager (manager);
expectEquals (manager.getAvailableDeviceTypes().size(), 2);
AudioDeviceManager::AudioDeviceSetup setup;
setup.outputDeviceName = "z";
setup.inputDeviceName = "c";
expect (manager.initialise (2, 2, nullptr, true, String{}, &setup).isEmpty());
const auto& newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
}
beginTest ("When the AudioDeviceSetup has empty device names, initialise picks suitable default devices");
{
AudioDeviceManager manager;
initialiseManager (manager);
AudioDeviceManager::AudioDeviceSetup setup;
expect (manager.initialise (2, 2, nullptr, true, String{}, &setup).isEmpty());
const auto& newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.outputDeviceName, String ("x"));
expectEquals (newSetup.inputDeviceName, String ("a"));
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
}
beginTest ("When a default device name is used, a suitable device is chosen");
{
AudioDeviceManager manager;
initialiseManager (manager);
expect (manager.initialise (2, 2, nullptr, true, "y").isEmpty());
const auto& newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.outputDeviceName, String ("y"));
expectEquals (newSetup.inputDeviceName, String ("a"));
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
}
beginTest ("When first device type has no devices, a device type with devices is used instead");
{
AudioDeviceManager manager;
initialiseManagerWithEmptyDeviceType (manager);
AudioDeviceManager::AudioDeviceSetup setup;
expect (manager.initialise (2, 2, nullptr, true, {}, &setup).isEmpty());
const auto& newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.outputDeviceName, String ("x"));
expectEquals (newSetup.inputDeviceName, String ("a"));
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
}
beginTest ("Carry out a long sequence of configuration changes");
{
AudioDeviceManager manager;
initialiseManagerWithEmptyDeviceType (manager);
initialiseWithDefaultDevices (manager);
disableInputChannelsButLeaveDeviceOpen (manager);
selectANewInputDevice (manager);
disableInputDevice (manager);
reenableInputDeviceWithNoChannels (manager);
enableInputChannels (manager);
disableInputChannelsButLeaveDeviceOpen (manager);
switchDeviceType (manager);
enableInputChannels (manager);
closeDeviceByRequestingEmptyNames (manager);
}
}
private:
void initialiseWithDefaultDevices (AudioDeviceManager& manager)
{
manager.initialiseWithDefaultDevices (2, 2);
const auto& setup = manager.getAudioDeviceSetup();
expectEquals (setup.inputChannels.countNumberOfSetBits(), 2);
expectEquals (setup.outputChannels.countNumberOfSetBits(), 2);
expect (setup.useDefaultInputChannels);
expect (setup.useDefaultOutputChannels);
expect (manager.getCurrentAudioDevice() != nullptr);
}
void disableInputChannelsButLeaveDeviceOpen (AudioDeviceManager& manager)
{
auto setup = manager.getAudioDeviceSetup();
setup.inputChannels.clear();
setup.useDefaultInputChannels = false;
expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
const auto newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expect (! newSetup.useDefaultInputChannels);
expect (newSetup.useDefaultOutputChannels);
expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
expect (manager.getCurrentAudioDevice() != nullptr);
}
void selectANewInputDevice (AudioDeviceManager& manager)
{
auto setup = manager.getAudioDeviceSetup();
setup.inputDeviceName = "b";
expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
const auto newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expect (! newSetup.useDefaultInputChannels);
expect (newSetup.useDefaultOutputChannels);
expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
expect (manager.getCurrentAudioDevice() != nullptr);
}
void disableInputDevice (AudioDeviceManager& manager)
{
auto setup = manager.getAudioDeviceSetup();
setup.inputDeviceName = "";
expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
const auto newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expect (! newSetup.useDefaultInputChannels);
expect (newSetup.useDefaultOutputChannels);
expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
expect (manager.getCurrentAudioDevice() != nullptr);
}
void reenableInputDeviceWithNoChannels (AudioDeviceManager& manager)
{
auto setup = manager.getAudioDeviceSetup();
setup.inputDeviceName = "a";
expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
const auto newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expect (! newSetup.useDefaultInputChannels);
expect (newSetup.useDefaultOutputChannels);
expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
expect (manager.getCurrentAudioDevice() != nullptr);
}
void enableInputChannels (AudioDeviceManager& manager)
{
auto setup = manager.getAudioDeviceSetup();
setup.inputDeviceName = manager.getCurrentDeviceTypeObject()->getDeviceNames (true)[0];
setup.inputChannels = 3;
setup.useDefaultInputChannels = false;
expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
const auto newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expect (! newSetup.useDefaultInputChannels);
expect (newSetup.useDefaultOutputChannels);
expectEquals (newSetup.inputDeviceName, setup.inputDeviceName);
expectEquals (newSetup.outputDeviceName, setup.outputDeviceName);
expect (manager.getCurrentAudioDevice() != nullptr);
}
void switchDeviceType (AudioDeviceManager& manager)
{
const auto oldSetup = manager.getAudioDeviceSetup();
expectEquals (manager.getCurrentAudioDeviceType(), String (mockAName));
manager.setCurrentAudioDeviceType (mockBName, true);
expectEquals (manager.getCurrentAudioDeviceType(), String (mockBName));
const auto newSetup = manager.getAudioDeviceSetup();
expect (newSetup.outputDeviceName.isNotEmpty());
// We had no channels enabled, which means we don't need to open a new input device
expect (newSetup.inputDeviceName.isEmpty());
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 0);
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expect (manager.getCurrentAudioDevice() != nullptr);
}
void closeDeviceByRequestingEmptyNames (AudioDeviceManager& manager)
{
auto setup = manager.getAudioDeviceSetup();
setup.inputDeviceName = "";
setup.outputDeviceName = "";
expect (manager.setAudioDeviceSetup (setup, true).isEmpty());
const auto newSetup = manager.getAudioDeviceSetup();
expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2);
expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2);
expect (newSetup.inputDeviceName.isEmpty());
expect (newSetup.outputDeviceName.isEmpty());
expect (manager.getCurrentAudioDevice() == nullptr);
}
const String mockAName = "mockA";
const String mockBName = "mockB";
const String emptyName = "empty";
class MockDevice : public AudioIODevice
{
public:
MockDevice (String typeNameIn, String outNameIn, String inNameIn)
: AudioIODevice ("mock", typeNameIn), outName (outNameIn), inName (inNameIn) {}
StringArray getOutputChannelNames() override { return { "o1", "o2", "o3" }; }
StringArray getInputChannelNames() override { return { "i1", "i2", "i3" }; }
Array<double> getAvailableSampleRates() override { return { 44100.0, 48000.0 }; }
Array<int> getAvailableBufferSizes() override { return { 128, 256 }; }
int getDefaultBufferSize() override { return 128; }
String open (const BigInteger& inputs, const BigInteger& outputs, double sr, int bs) override
{
inChannels = inputs;
outChannels = outputs;
sampleRate = sr;
blockSize = bs;
on = true;
return {};
}
void close() override { on = false; }
bool isOpen() override { return on; }
void start (AudioIODeviceCallback*) override { playing = true; }
void stop() override { playing = false; }
bool isPlaying() override { return playing; }
String getLastError() override { return {}; }
int getCurrentBufferSizeSamples() override { return blockSize; }
double getCurrentSampleRate() override { return sampleRate; }
int getCurrentBitDepth() override { return 16; }
BigInteger getActiveOutputChannels() const override { return outChannels; }
BigInteger getActiveInputChannels() const override { return inChannels; }
int getOutputLatencyInSamples() override { return 0; }
int getInputLatencyInSamples() override { return 0; }
private:
String outName, inName;
BigInteger outChannels, inChannels;
double sampleRate = 0.0;
int blockSize = 0;
bool on = false, playing = false;
};
class MockDeviceType : public AudioIODeviceType
{
public:
explicit MockDeviceType (String kind)
: MockDeviceType (std::move (kind), { "a", "b", "c" }, { "x", "y", "z" }) {}
MockDeviceType (String kind, StringArray inputNames, StringArray outputNames)
: AudioIODeviceType (std::move (kind)),
inNames (std::move (inputNames)),
outNames (std::move (outputNames)) {}
void scanForDevices() override {}
StringArray getDeviceNames (bool isInput) const override
{
return getNames (isInput);
}
int getDefaultDeviceIndex (bool) const override { return 0; }
int getIndexOfDevice (AudioIODevice* device, bool isInput) const override
{
return getNames (isInput).indexOf (device->getName());
}
bool hasSeparateInputsAndOutputs() const override { return true; }
AudioIODevice* createDevice (const String& outputName, const String& inputName) override
{
if (inNames.contains (inputName) || outNames.contains (outputName))
return new MockDevice (getTypeName(), outputName, inputName);
return nullptr;
}
private:
const StringArray& getNames (bool isInput) const { return isInput ? inNames : outNames; }
const StringArray inNames, outNames;
};
void initialiseManager (AudioDeviceManager& manager)
{
manager.addAudioDeviceType (std::make_unique<MockDeviceType> (mockAName));
manager.addAudioDeviceType (std::make_unique<MockDeviceType> (mockBName));
}
void initialiseManagerWithEmptyDeviceType (AudioDeviceManager& manager)
{
manager.addAudioDeviceType (std::make_unique<MockDeviceType> (emptyName, StringArray{}, StringArray{}));
initialiseManager (manager);
}
};
static AudioDeviceManagerTests audioDeviceManagerTests;
#endif
} // namespace juce