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:
parent
10a26b7584
commit
34bda5d75b
2 changed files with 402 additions and 6 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -93,13 +93,11 @@ public:
|
|||
The name has to be one of the ones listed by the AudioDeviceManager's currently
|
||||
selected device type.
|
||||
This may be the same as the input device.
|
||||
An empty string indicates the default device.
|
||||
*/
|
||||
String outputDeviceName;
|
||||
|
||||
/** The name of the audio device used for input.
|
||||
This may be the same as the output device.
|
||||
An empty string indicates the default device.
|
||||
*/
|
||||
String inputDeviceName;
|
||||
|
||||
|
|
@ -174,7 +172,10 @@ public:
|
|||
@param preferredSetupOptions if this is non-null, the structure will be used as the
|
||||
set of preferred settings when opening the device. If you
|
||||
use this parameter, the preferredDefaultDeviceName
|
||||
field will be ignored
|
||||
field will be ignored. If you set the outputDeviceName
|
||||
or inputDeviceName data members of the AudioDeviceSetup
|
||||
to empty strings, then a default device will be used.
|
||||
|
||||
|
||||
@returns an error message if anything went wrong, or an empty string if it worked ok.
|
||||
*/
|
||||
|
|
@ -219,7 +220,11 @@ public:
|
|||
settings, then tweak the appropriate fields in the AudioDeviceSetup structure,
|
||||
and pass it back into this method to apply the new settings.
|
||||
|
||||
@param newSetup the settings that you'd like to use
|
||||
@param newSetup the settings that you'd like to use.
|
||||
If you don't need an input or output device, set the
|
||||
inputDeviceName or outputDeviceName data members respectively
|
||||
to empty strings. Note that this behaviour differs from
|
||||
the behaviour of initialise().
|
||||
@param treatAsChosenDevice if this is true and if the device opens correctly, these new
|
||||
settings will be taken as having been explicitly chosen by the
|
||||
user, and the next time createStateXml() is called, these settings
|
||||
|
|
@ -546,6 +551,7 @@ private:
|
|||
|
||||
AudioIODeviceType* findType (const String& inputName, const String& outputName);
|
||||
AudioIODeviceType* findType (const String& typeName);
|
||||
void pickCurrentDeviceTypeWithDevices();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager)
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue