| @@ -306,26 +306,67 @@ String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDevi | |||||
| } | } | ||||
| else if (preferredDefaultDeviceName.isNotEmpty()) | else if (preferredDefaultDeviceName.isNotEmpty()) | ||||
| { | { | ||||
| for (auto* type : availableDeviceTypes) | |||||
| const auto nameMatches = [&preferredDefaultDeviceName] (const String& name) | |||||
| { | |||||
| return name.matchesWildcard (preferredDefaultDeviceName, true); | |||||
| }; | |||||
| struct WildcardMatch | |||||
| { | |||||
| String value; | |||||
| bool successful; | |||||
| }; | |||||
| const auto getWildcardMatch = [&nameMatches] (const StringArray& names) | |||||
| { | |||||
| const auto iter = std::find_if (names.begin(), names.end(), nameMatches); | |||||
| return WildcardMatch { iter != names.end() ? *iter : String(), iter != names.end() }; | |||||
| }; | |||||
| struct WildcardMatches | |||||
| { | { | ||||
| for (auto& out : type->getDeviceNames (false)) | |||||
| WildcardMatch input, output; | |||||
| }; | |||||
| const auto getMatchesForType = [&getWildcardMatch] (const AudioIODeviceType* type) | |||||
| { | |||||
| return WildcardMatches { getWildcardMatch (type->getDeviceNames (true)), | |||||
| getWildcardMatch (type->getDeviceNames (false)) }; | |||||
| }; | |||||
| struct SearchResult | |||||
| { | |||||
| String type, input, output; | |||||
| }; | |||||
| const auto result = [&] | |||||
| { | |||||
| // First, look for a device type with an input and output which matches the preferred name | |||||
| for (auto* type : availableDeviceTypes) | |||||
| { | { | ||||
| if (out.matchesWildcard (preferredDefaultDeviceName, true)) | |||||
| { | |||||
| setup.outputDeviceName = out; | |||||
| break; | |||||
| } | |||||
| const auto matches = getMatchesForType (type); | |||||
| if (matches.input.successful && matches.output.successful) | |||||
| return SearchResult { type->getTypeName(), matches.input.value, matches.output.value }; | |||||
| } | } | ||||
| for (auto& in : type->getDeviceNames (true)) | |||||
| // No device type has matching ins and outs, so fall back to a device where either the | |||||
| // input or output match | |||||
| for (auto* type : availableDeviceTypes) | |||||
| { | { | ||||
| if (in.matchesWildcard (preferredDefaultDeviceName, true)) | |||||
| { | |||||
| setup.inputDeviceName = in; | |||||
| break; | |||||
| } | |||||
| const auto matches = getMatchesForType (type); | |||||
| if (matches.input.successful || matches.output.successful) | |||||
| return SearchResult { type->getTypeName(), matches.input.value, matches.output.value }; | |||||
| } | } | ||||
| } | |||||
| // No devices match the query, so just use the default devices from the current type | |||||
| return SearchResult { currentDeviceType, {}, {} }; | |||||
| }(); | |||||
| currentDeviceType = result.type; | |||||
| setup.inputDeviceName = result.input; | |||||
| setup.outputDeviceName = result.output; | |||||
| } | } | ||||
| insertDefaultDeviceNames (setup); | insertDefaultDeviceNames (setup); | ||||
| @@ -1279,20 +1320,64 @@ public: | |||||
| expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); | expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); | ||||
| } | } | ||||
| beginTest ("When a default device name is used, a suitable device is chosen"); | |||||
| beginTest ("When the preferred device name matches an input and an output on the same type, that type is used"); | |||||
| { | { | ||||
| AudioDeviceManager manager; | AudioDeviceManager manager; | ||||
| initialiseManager (manager); | |||||
| initialiseManagerWithDifferentDeviceNames (manager); | |||||
| expect (manager.initialise (2, 2, nullptr, true, "y").isEmpty()); | |||||
| expect (manager.initialise (2, 2, nullptr, true, "bar *").isEmpty()); | |||||
| expectEquals (manager.getCurrentAudioDeviceType(), String ("bar")); | |||||
| const auto& newSetup = manager.getAudioDeviceSetup(); | const auto& newSetup = manager.getAudioDeviceSetup(); | ||||
| expectEquals (newSetup.outputDeviceName, String ("y")); | |||||
| expectEquals (newSetup.inputDeviceName, String ("a")); | |||||
| expectEquals (newSetup.outputDeviceName, String ("bar out a")); | |||||
| expectEquals (newSetup.inputDeviceName, String ("bar in a")); | |||||
| expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); | expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); | ||||
| expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); | expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); | ||||
| expect (manager.getCurrentAudioDevice() != nullptr); | |||||
| } | |||||
| beginTest ("When the preferred device name matches either an input and an output, but not both, that type is used"); | |||||
| { | |||||
| AudioDeviceManager manager; | |||||
| initialiseManagerWithDifferentDeviceNames (manager); | |||||
| expect (manager.initialise (2, 2, nullptr, true, "bar out b").isEmpty()); | |||||
| expectEquals (manager.getCurrentAudioDeviceType(), String ("bar")); | |||||
| const auto& newSetup = manager.getAudioDeviceSetup(); | |||||
| expectEquals (newSetup.outputDeviceName, String ("bar out b")); | |||||
| expectEquals (newSetup.inputDeviceName, String ("bar in a")); | |||||
| expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); | |||||
| expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); | |||||
| expect (manager.getCurrentAudioDevice() != nullptr); | |||||
| } | |||||
| beginTest ("When the preferred device name does not match any inputs or outputs, defaults are used"); | |||||
| { | |||||
| AudioDeviceManager manager; | |||||
| initialiseManagerWithDifferentDeviceNames (manager); | |||||
| expect (manager.initialise (2, 2, nullptr, true, "unmatchable").isEmpty()); | |||||
| expectEquals (manager.getCurrentAudioDeviceType(), String ("foo")); | |||||
| const auto& newSetup = manager.getAudioDeviceSetup(); | |||||
| expectEquals (newSetup.outputDeviceName, String ("foo out a")); | |||||
| expectEquals (newSetup.inputDeviceName, String ("foo in a")); | |||||
| expectEquals (newSetup.outputChannels.countNumberOfSetBits(), 2); | |||||
| expectEquals (newSetup.inputChannels.countNumberOfSetBits(), 2); | |||||
| expect (manager.getCurrentAudioDevice() != nullptr); | |||||
| } | } | ||||
| beginTest ("When first device type has no devices, a device type with devices is used instead"); | beginTest ("When first device type has no devices, a device type with devices is used instead"); | ||||
| @@ -1593,6 +1678,17 @@ private: | |||||
| manager.addAudioDeviceType (std::make_unique<MockDeviceType> (emptyName, StringArray{}, StringArray{})); | manager.addAudioDeviceType (std::make_unique<MockDeviceType> (emptyName, StringArray{}, StringArray{})); | ||||
| initialiseManager (manager); | initialiseManager (manager); | ||||
| } | } | ||||
| void initialiseManagerWithDifferentDeviceNames (AudioDeviceManager& manager) | |||||
| { | |||||
| manager.addAudioDeviceType (std::make_unique<MockDeviceType> ("foo", | |||||
| StringArray { "foo in a", "foo in b" }, | |||||
| StringArray { "foo out a", "foo out b" })); | |||||
| manager.addAudioDeviceType (std::make_unique<MockDeviceType> ("bar", | |||||
| StringArray { "bar in a", "bar in b" }, | |||||
| StringArray { "bar out a", "bar out b" })); | |||||
| } | |||||
| }; | }; | ||||
| static AudioDeviceManagerTests audioDeviceManagerTests; | static AudioDeviceManagerTests audioDeviceManagerTests; | ||||