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

AudioDeviceManager: Take sample rate compatibility into account during default device selection

This commit is contained in:
attila 2022-08-19 19:59:45 +02:00
parent 5ec536f13f
commit 3323c68f96

View file

@ -502,15 +502,90 @@ String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNee
void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const
{
enum class Direction { out, in };
if (auto* type = getCurrentDeviceTypeObject())
{
for (const auto isInput : { false, true })
// We avoid selecting a device pair that doesn't share a matching sample rate, if possible.
// If not, other parts of the AudioDeviceManager and AudioIODevice classes should generate
// an appropriate error message when opening or starting these devices.
const auto getDevicesToTestForMatchingSampleRate = [&setup, type, this] (Direction dir)
{
const auto numChannelsNeeded = isInput ? numInputChansNeeded : numOutputChansNeeded;
const auto isInput = dir == Direction::in;
const auto info = getSetupInfo (setup, isInput);
if (numChannelsNeeded > 0 && info.name.isEmpty())
info.name = type->getDeviceNames (isInput) [type->getDefaultDeviceIndex (isInput)];
if (! info.name.isEmpty())
return StringArray { info.name };
const auto numChannelsNeeded = isInput ? numInputChansNeeded : numOutputChansNeeded;
auto deviceNames = numChannelsNeeded > 0 ? type->getDeviceNames (isInput) : StringArray {};
deviceNames.move (type->getDefaultDeviceIndex (isInput), 0);
return deviceNames;
};
std::map<std::pair<Direction, String>, Array<double>> sampleRatesCache;
const auto getSupportedSampleRates = [&sampleRatesCache, type] (Direction dir, const String& deviceName)
{
const auto key = std::make_pair (dir, deviceName);
auto& entry = [&]() -> auto&
{
auto it = sampleRatesCache.find (key);
if (it != sampleRatesCache.end())
return it->second;
auto& elem = sampleRatesCache[key];
auto tempDevice = rawToUniquePtr (type->createDevice ((dir == Direction::in) ? "" : deviceName,
(dir == Direction::in) ? deviceName : ""));
if (tempDevice != nullptr)
elem = tempDevice->getAvailableSampleRates();
return elem;
}();
return entry;
};
const auto validate = [&getSupportedSampleRates] (const String& outputDeviceName, const String& inputDeviceName)
{
jassert (! outputDeviceName.isEmpty() && ! inputDeviceName.isEmpty());
const auto outputSampleRates = getSupportedSampleRates (Direction::out, outputDeviceName);
const auto inputSampleRates = getSupportedSampleRates (Direction::in, inputDeviceName);
return std::any_of (inputSampleRates.begin(),
inputSampleRates.end(),
[&] (auto inputSampleRate) { return outputSampleRates.contains (inputSampleRate); });
};
auto outputsToTest = getDevicesToTestForMatchingSampleRate (Direction::out);
auto inputsToTest = getDevicesToTestForMatchingSampleRate (Direction::in);
// We set default device names, so in case no in-out pair passes the validation, we still
// produce the same result as before
if (setup.outputDeviceName.isEmpty() && ! outputsToTest.isEmpty())
setup.outputDeviceName = outputsToTest[0];
if (setup.inputDeviceName.isEmpty() && ! inputsToTest.isEmpty())
setup.inputDeviceName = inputsToTest[0];
// We check all possible in-out pairs until the first validation pass. If no pair passes we
// leave the setup unchanged.
for (const auto& out : outputsToTest)
{
for (const auto& in : inputsToTest)
{
if (validate (out, in))
{
setup.outputDeviceName = out;
setup.inputDeviceName = in;
return;
}
}
}
}
}