void AudioDeviceManager::setCurrentAudioDeviceType (const String& type,
                                                    const bool treatAsChosenDevice)
{
    for (int i = 0; i < availableDeviceTypes.size(); ++i)
    {
        if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type
             && currentDeviceType != type)
        {
            if (currentAudioDevice != nullptr)
            {
                closeAudioDevice();
                Thread::sleep (1500); // allow a moment for OS devices to sort themselves out, to help
                                      // avoid things like DirectSound/ASIO clashes
            }

            currentDeviceType = type;

            AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i));
            insertDefaultDeviceNames (s);

            setAudioDeviceSetup (s, treatAsChosenDevice);

            sendChangeMessage();
            break;
        }
    }
}
void AudioDeviceManager::restartLastAudioDevice()
{
    if (currentAudioDevice == nullptr)
    {
        if (currentSetup.inputDeviceName.isEmpty()
              && currentSetup.outputDeviceName.isEmpty())
        {
            // This method will only reload the last device that was running
            // before closeAudioDevice() was called - you need to actually open
            // one first, with setAudioDevice().
            jassertfalse;
            return;
        }

        AudioDeviceSetup s (currentSetup);
        setAudioDeviceSetup (s, false);
    }
}
String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName,
                                              const AudioDeviceSetup* preferredSetupOptions)
{
    AudioDeviceSetup setup;

    if (preferredSetupOptions != nullptr)
    {
        setup = *preferredSetupOptions;
    }
    else if (preferredDefaultDeviceName.isNotEmpty())
    {
        for (int j = availableDeviceTypes.size(); --j >= 0;)
        {
            AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j);

            const StringArray outs (type->getDeviceNames (false));

            for (int i = 0; i < outs.size(); ++i)
            {
                if (outs[i].matchesWildcard (preferredDefaultDeviceName, true))
                {
                    setup.outputDeviceName = outs[i];
                    break;
                }
            }

            const StringArray ins (type->getDeviceNames (true));

            for (int i = 0; i < ins.size(); ++i)
            {
                if (ins[i].matchesWildcard (preferredDefaultDeviceName, true))
                {
                    setup.inputDeviceName = ins[i];
                    break;
                }
            }
        }
    }

    insertDefaultDeviceNames (setup);
    return setAudioDeviceSetup (setup, false);
}
void AudioDeviceManager::setCurrentAudioDeviceType (const String& type,
                                                    const bool treatAsChosenDevice)
{
    for (int i = 0; i < availableDeviceTypes.size(); ++i)
    {
        if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type
             && currentDeviceType != type)
        {
            currentDeviceType = type;

            AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i));
            insertDefaultDeviceNames (s);

            setAudioDeviceSetup (s, treatAsChosenDevice);

            sendChangeMessage();
            break;
        }
    }
}
String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName,
                                              const AudioDeviceSetup* preferredSetupOptions)
{
    AudioDeviceSetup setup;

    if (preferredSetupOptions != nullptr)
    {
        setup = *preferredSetupOptions;
    }
    else if (preferredDefaultDeviceName.isNotEmpty())
    {
        for (auto* type : availableDeviceTypes)
        {
            for (auto& out : type->getDeviceNames (false))
            {
                if (out.matchesWildcard (preferredDefaultDeviceName, true))
                {
                    setup.outputDeviceName = out;
                    break;
                }
            }

            for (auto& in : type->getDeviceNames (true))
            {
                if (in.matchesWildcard (preferredDefaultDeviceName, true))
                {
                    setup.inputDeviceName = in;
                    break;
                }
            }
        }
    }

    insertDefaultDeviceNames (setup);
    return setAudioDeviceSetup (setup, false);
}
//==============================================================================
String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
                                       const int numOutputChannelsNeeded,
                                       const XmlElement* const e,
                                       const bool selectDefaultDeviceOnFailure,
                                       const String& preferredDefaultDeviceName,
                                       const AudioDeviceSetup* preferredSetupOptions)
{
    scanDevicesIfNeeded();

    numInputChansNeeded = numInputChannelsNeeded;
    numOutputChansNeeded = numOutputChannelsNeeded;

    if (e != nullptr && e->hasTagName ("DEVICESETUP"))
    {
        lastExplicitSettings = new XmlElement (*e);

        String error;
        AudioDeviceSetup setup;

        if (preferredSetupOptions != nullptr)
            setup = *preferredSetupOptions;

        if (e->getStringAttribute ("audioDeviceName").isNotEmpty())
        {
            setup.inputDeviceName = setup.outputDeviceName
                = e->getStringAttribute ("audioDeviceName");
        }
        else
        {
            setup.inputDeviceName = e->getStringAttribute ("audioInputDeviceName");
            setup.outputDeviceName = e->getStringAttribute ("audioOutputDeviceName");
        }

        currentDeviceType = e->getStringAttribute ("deviceType");

        if (findType (currentDeviceType) == nullptr)
        {
            AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName);

            if (type != nullptr)
                currentDeviceType = type->getTypeName();
            else if (availableDeviceTypes.size() > 0)
                currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName();
        }

        setup.bufferSize = e->getIntAttribute ("audioDeviceBufferSize");
        setup.sampleRate = e->getDoubleAttribute ("audioDeviceRate");

        setup.inputChannels .parseString (e->getStringAttribute ("audioDeviceInChans",  "11"), 2);
        setup.outputChannels.parseString (e->getStringAttribute ("audioDeviceOutChans", "11"), 2);

        setup.useDefaultInputChannels  = ! e->hasAttribute ("audioDeviceInChans");
        setup.useDefaultOutputChannels = ! e->hasAttribute ("audioDeviceOutChans");

        error = setAudioDeviceSetup (setup, true);

        midiInsFromXml.clear();
        forEachXmlChildElementWithTagName (*e, c, "MIDIINPUT")
            midiInsFromXml.add (c->getStringAttribute ("name"));

        const StringArray allMidiIns (MidiInput::getDevices());

        for (int i = allMidiIns.size(); --i >= 0;)
            setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i]));

        if (error.isNotEmpty() && selectDefaultDeviceOnFailure)
            error = initialise (numInputChannelsNeeded, numOutputChannelsNeeded, 0,
                                false, preferredDefaultDeviceName);

        setDefaultMidiOutput (e->getStringAttribute ("defaultMidiOutput"));

        return error;
    }
    else
    {
        AudioDeviceSetup setup;

        if (preferredSetupOptions != nullptr)
        {
            setup = *preferredSetupOptions;
        }
        else if (preferredDefaultDeviceName.isNotEmpty())
        {
            for (int j = availableDeviceTypes.size(); --j >= 0;)
            {
                AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j);

                StringArray outs (type->getDeviceNames (false));

                int i;
                for (i = 0; i < outs.size(); ++i)
                {
                    if (outs[i].matchesWildcard (preferredDefaultDeviceName, true))
                    {
                        setup.outputDeviceName = outs[i];
                        break;
                    }
                }

                StringArray ins (type->getDeviceNames (true));

                for (i = 0; i < ins.size(); ++i)
                {
                    if (ins[i].matchesWildcard (preferredDefaultDeviceName, true))
                    {
                        setup.inputDeviceName = ins[i];
                        break;
                    }
                }
            }
        }

        insertDefaultDeviceNames (setup);
        return setAudioDeviceSetup (setup, false);
    }
}
String AudioDeviceManager::initialiseFromXML (const XmlElement& xml,
                                              const bool selectDefaultDeviceOnFailure,
                                              const String& preferredDefaultDeviceName,
                                              const AudioDeviceSetup* preferredSetupOptions)
{
    lastExplicitSettings = new XmlElement (xml);

    String error;
    AudioDeviceSetup setup;

    if (preferredSetupOptions != nullptr)
        setup = *preferredSetupOptions;

    if (xml.getStringAttribute ("audioDeviceName").isNotEmpty())
    {
        setup.inputDeviceName = setup.outputDeviceName
            = xml.getStringAttribute ("audioDeviceName");
    }
    else
    {
        setup.inputDeviceName  = xml.getStringAttribute ("audioInputDeviceName");
        setup.outputDeviceName = xml.getStringAttribute ("audioOutputDeviceName");
    }

    currentDeviceType = xml.getStringAttribute ("deviceType");

    if (findType (currentDeviceType) == nullptr)
    {
        if (AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName))
            currentDeviceType = type->getTypeName();
        else if (availableDeviceTypes.size() > 0)
            currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName();
    }

    setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize", setup.bufferSize);
    setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate", setup.sampleRate);

    setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans",  "11"), 2);
    setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2);

    setup.useDefaultInputChannels  = ! xml.hasAttribute ("audioDeviceInChans");
    setup.useDefaultOutputChannels = ! xml.hasAttribute ("audioDeviceOutChans");

    error = setAudioDeviceSetup (setup, true);

    midiInsFromXml.clear();

    forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT")
        midiInsFromXml.add (c->getStringAttribute ("name"));

    const StringArray allMidiIns (MidiInput::getDevices());

    for (int i = allMidiIns.size(); --i >= 0;)
        setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i]));

    if (error.isNotEmpty() && selectDefaultDeviceOnFailure)
        error = initialise (numInputChansNeeded, numOutputChansNeeded,
                            nullptr, false, preferredDefaultDeviceName);

    setDefaultMidiOutput (xml.getStringAttribute ("defaultMidiOutput"));

    return error;
}