- initial version
git-svn-id: http://moon:8086/svn/software/trunk/projects/RBM@14 b431acfa-c32f-4a4a-93f1-934dc6c82436
This commit is contained in:
@@ -0,0 +1,987 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
AudioDeviceManager::AudioDeviceSetup::AudioDeviceSetup()
|
||||
: sampleRate (0),
|
||||
bufferSize (0),
|
||||
useDefaultInputChannels (true),
|
||||
useDefaultOutputChannels (true)
|
||||
{
|
||||
}
|
||||
|
||||
bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const
|
||||
{
|
||||
return outputDeviceName == other.outputDeviceName
|
||||
&& inputDeviceName == other.inputDeviceName
|
||||
&& sampleRate == other.sampleRate
|
||||
&& bufferSize == other.bufferSize
|
||||
&& inputChannels == other.inputChannels
|
||||
&& useDefaultInputChannels == other.useDefaultInputChannels
|
||||
&& outputChannels == other.outputChannels
|
||||
&& useDefaultOutputChannels == other.useDefaultOutputChannels;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class AudioDeviceManager::CallbackHandler : public AudioIODeviceCallback,
|
||||
public MidiInputCallback,
|
||||
public AudioIODeviceType::Listener
|
||||
{
|
||||
public:
|
||||
CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {}
|
||||
|
||||
private:
|
||||
void audioDeviceIOCallback (const float** ins, int numIns, float** outs, int numOuts, int numSamples) override
|
||||
{
|
||||
owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples);
|
||||
}
|
||||
|
||||
void audioDeviceAboutToStart (AudioIODevice* device) override
|
||||
{
|
||||
owner.audioDeviceAboutToStartInt (device);
|
||||
}
|
||||
|
||||
void audioDeviceStopped() override
|
||||
{
|
||||
owner.audioDeviceStoppedInt();
|
||||
}
|
||||
|
||||
void audioDeviceError (const String& message) override
|
||||
{
|
||||
owner.audioDeviceErrorInt (message);
|
||||
}
|
||||
|
||||
void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override
|
||||
{
|
||||
owner.handleIncomingMidiMessageInt (source, message);
|
||||
}
|
||||
|
||||
void audioDeviceListChanged() override
|
||||
{
|
||||
owner.audioDeviceListChanged();
|
||||
}
|
||||
|
||||
AudioDeviceManager& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
AudioDeviceManager::AudioDeviceManager()
|
||||
: numInputChansNeeded (0),
|
||||
numOutputChansNeeded (2),
|
||||
listNeedsScanning (true),
|
||||
useInputNames (false),
|
||||
inputLevel (0),
|
||||
testSoundPosition (0),
|
||||
cpuUsageMs (0),
|
||||
timeToCpuScale (0)
|
||||
{
|
||||
callbackHandler = new CallbackHandler (*this);
|
||||
}
|
||||
|
||||
AudioDeviceManager::~AudioDeviceManager()
|
||||
{
|
||||
currentAudioDevice = nullptr;
|
||||
defaultMidiOutput = nullptr;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void AudioDeviceManager::createDeviceTypesIfNeeded()
|
||||
{
|
||||
if (availableDeviceTypes.size() == 0)
|
||||
{
|
||||
OwnedArray<AudioIODeviceType> types;
|
||||
createAudioDeviceTypes (types);
|
||||
|
||||
for (int i = 0; i < types.size(); ++i)
|
||||
addAudioDeviceType (types.getUnchecked(i));
|
||||
|
||||
types.clear (false);
|
||||
|
||||
if (AudioIODeviceType* first = availableDeviceTypes.getFirst())
|
||||
currentDeviceType = first->getTypeName();
|
||||
}
|
||||
}
|
||||
|
||||
const OwnedArray<AudioIODeviceType>& AudioDeviceManager::getAvailableDeviceTypes()
|
||||
{
|
||||
scanDevicesIfNeeded();
|
||||
return availableDeviceTypes;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::audioDeviceListChanged()
|
||||
{
|
||||
if (currentAudioDevice != nullptr)
|
||||
{
|
||||
currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
|
||||
currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
|
||||
currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
|
||||
currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
|
||||
}
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static void addIfNotNull (OwnedArray<AudioIODeviceType>& list, AudioIODeviceType* const device)
|
||||
{
|
||||
if (device != nullptr)
|
||||
list.add (device);
|
||||
}
|
||||
|
||||
void AudioDeviceManager::createAudioDeviceTypes (OwnedArray<AudioIODeviceType>& list)
|
||||
{
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_iOSAudio());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ALSA());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_JACK());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_OpenSLES());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Android());
|
||||
}
|
||||
|
||||
void AudioDeviceManager::addAudioDeviceType (AudioIODeviceType* newDeviceType)
|
||||
{
|
||||
if (newDeviceType != nullptr)
|
||||
{
|
||||
jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size());
|
||||
availableDeviceTypes.add (newDeviceType);
|
||||
lastDeviceTypeConfigs.add (new AudioDeviceSetup());
|
||||
|
||||
newDeviceType->addListener (callbackHandler);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
|
||||
const int numOutputChannelsNeeded,
|
||||
const XmlElement* const xml,
|
||||
const bool selectDefaultDeviceOnFailure,
|
||||
const String& preferredDefaultDeviceName,
|
||||
const AudioDeviceSetup* preferredSetupOptions)
|
||||
{
|
||||
scanDevicesIfNeeded();
|
||||
|
||||
numInputChansNeeded = numInputChannelsNeeded;
|
||||
numOutputChansNeeded = numOutputChannelsNeeded;
|
||||
|
||||
if (xml != nullptr && xml->hasTagName ("DEVICESETUP"))
|
||||
return initialiseFromXML (*xml, selectDefaultDeviceOnFailure,
|
||||
preferredDefaultDeviceName, preferredSetupOptions);
|
||||
|
||||
return initialiseDefault (preferredDefaultDeviceName, preferredSetupOptions);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.sampleRate = xml.getDoubleAttribute ("audioDeviceRate");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNeeded,
|
||||
int numOutputChannelsNeeded)
|
||||
{
|
||||
lastExplicitSettings = nullptr;
|
||||
|
||||
return initialise (numInputChannelsNeeded, numOutputChannelsNeeded,
|
||||
nullptr, false, String(), nullptr);
|
||||
}
|
||||
|
||||
void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const
|
||||
{
|
||||
if (AudioIODeviceType* type = getCurrentDeviceTypeObject())
|
||||
{
|
||||
if (setup.outputDeviceName.isEmpty())
|
||||
setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)];
|
||||
|
||||
if (setup.inputDeviceName.isEmpty())
|
||||
setup.inputDeviceName = type->getDeviceNames (true) [type->getDefaultDeviceIndex (true)];
|
||||
}
|
||||
}
|
||||
|
||||
XmlElement* AudioDeviceManager::createStateXml() const
|
||||
{
|
||||
return lastExplicitSettings.createCopy();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDeviceManager::scanDevicesIfNeeded()
|
||||
{
|
||||
if (listNeedsScanning)
|
||||
{
|
||||
listNeedsScanning = false;
|
||||
|
||||
createDeviceTypesIfNeeded();
|
||||
|
||||
for (int i = availableDeviceTypes.size(); --i >= 0;)
|
||||
availableDeviceTypes.getUnchecked(i)->scanForDevices();
|
||||
}
|
||||
}
|
||||
|
||||
AudioIODeviceType* AudioDeviceManager::findType (const String& typeName)
|
||||
{
|
||||
scanDevicesIfNeeded();
|
||||
|
||||
for (int i = availableDeviceTypes.size(); --i >= 0;)
|
||||
if (availableDeviceTypes.getUnchecked(i)->getTypeName() == typeName)
|
||||
return availableDeviceTypes.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const String& outputName)
|
||||
{
|
||||
scanDevicesIfNeeded();
|
||||
|
||||
for (int i = availableDeviceTypes.size(); --i >= 0;)
|
||||
{
|
||||
AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(i);
|
||||
|
||||
if ((inputName.isNotEmpty() && type->getDeviceNames (true).contains (inputName, true))
|
||||
|| (outputName.isNotEmpty() && type->getDeviceNames (false).contains (outputName, true)))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup)
|
||||
{
|
||||
setup = currentSetup;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::deleteCurrentDevice()
|
||||
{
|
||||
currentAudioDevice = nullptr;
|
||||
currentSetup.inputDeviceName.clear();
|
||||
currentSetup.outputDeviceName.clear();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const
|
||||
{
|
||||
for (int i = 0; i < availableDeviceTypes.size(); ++i)
|
||||
if (availableDeviceTypes.getUnchecked(i)->getTypeName() == currentDeviceType)
|
||||
return availableDeviceTypes.getUnchecked(i);
|
||||
|
||||
return availableDeviceTypes[0];
|
||||
}
|
||||
|
||||
String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup,
|
||||
const bool treatAsChosenDevice)
|
||||
{
|
||||
jassert (&newSetup != ¤tSetup); // this will have no effect
|
||||
|
||||
if (newSetup == currentSetup && currentAudioDevice != nullptr)
|
||||
return String();
|
||||
|
||||
if (! (newSetup == currentSetup))
|
||||
sendChangeMessage();
|
||||
|
||||
stopDevice();
|
||||
|
||||
const String newInputDeviceName (numInputChansNeeded == 0 ? String() : newSetup.inputDeviceName);
|
||||
const String newOutputDeviceName (numOutputChansNeeded == 0 ? String() : newSetup.outputDeviceName);
|
||||
|
||||
String error;
|
||||
AudioIODeviceType* type = getCurrentDeviceTypeObject();
|
||||
|
||||
if (type == nullptr || (newInputDeviceName.isEmpty() && newOutputDeviceName.isEmpty()))
|
||||
{
|
||||
deleteCurrentDevice();
|
||||
|
||||
if (treatAsChosenDevice)
|
||||
updateXml();
|
||||
|
||||
return String();
|
||||
}
|
||||
|
||||
if (currentSetup.inputDeviceName != newInputDeviceName
|
||||
|| currentSetup.outputDeviceName != newOutputDeviceName
|
||||
|| currentAudioDevice == nullptr)
|
||||
{
|
||||
deleteCurrentDevice();
|
||||
scanDevicesIfNeeded();
|
||||
|
||||
if (newOutputDeviceName.isNotEmpty()
|
||||
&& ! type->getDeviceNames (false).contains (newOutputDeviceName))
|
||||
{
|
||||
return "No such device: " + newOutputDeviceName;
|
||||
}
|
||||
|
||||
if (newInputDeviceName.isNotEmpty()
|
||||
&& ! type->getDeviceNames (true).contains (newInputDeviceName))
|
||||
{
|
||||
return "No such device: " + newInputDeviceName;
|
||||
}
|
||||
|
||||
currentAudioDevice = type->createDevice (newOutputDeviceName, newInputDeviceName);
|
||||
|
||||
if (currentAudioDevice == nullptr)
|
||||
error = "Can't open the audio device!\n\n"
|
||||
"This may be because another application is currently using the same device - "
|
||||
"if so, you should close any other applications and try again!";
|
||||
else
|
||||
error = currentAudioDevice->getLastError();
|
||||
|
||||
if (error.isNotEmpty())
|
||||
{
|
||||
deleteCurrentDevice();
|
||||
return error;
|
||||
}
|
||||
|
||||
if (newSetup.useDefaultInputChannels)
|
||||
{
|
||||
inputChannels.clear();
|
||||
inputChannels.setRange (0, numInputChansNeeded, true);
|
||||
}
|
||||
|
||||
if (newSetup.useDefaultOutputChannels)
|
||||
{
|
||||
outputChannels.clear();
|
||||
outputChannels.setRange (0, numOutputChansNeeded, true);
|
||||
}
|
||||
|
||||
if (newInputDeviceName.isEmpty()) inputChannels.clear();
|
||||
if (newOutputDeviceName.isEmpty()) outputChannels.clear();
|
||||
}
|
||||
|
||||
if (! newSetup.useDefaultInputChannels) inputChannels = newSetup.inputChannels;
|
||||
if (! newSetup.useDefaultOutputChannels) outputChannels = newSetup.outputChannels;
|
||||
|
||||
currentSetup = newSetup;
|
||||
|
||||
currentSetup.sampleRate = chooseBestSampleRate (newSetup.sampleRate);
|
||||
currentSetup.bufferSize = chooseBestBufferSize (newSetup.bufferSize);
|
||||
|
||||
error = currentAudioDevice->open (inputChannels,
|
||||
outputChannels,
|
||||
currentSetup.sampleRate,
|
||||
currentSetup.bufferSize);
|
||||
|
||||
if (error.isEmpty())
|
||||
{
|
||||
currentDeviceType = currentAudioDevice->getTypeName();
|
||||
|
||||
currentAudioDevice->start (callbackHandler);
|
||||
|
||||
currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
|
||||
currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
|
||||
currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
|
||||
currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
|
||||
|
||||
for (int i = 0; i < availableDeviceTypes.size(); ++i)
|
||||
if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType)
|
||||
*(lastDeviceTypeConfigs.getUnchecked (i)) = currentSetup;
|
||||
|
||||
if (treatAsChosenDevice)
|
||||
updateXml();
|
||||
}
|
||||
else
|
||||
{
|
||||
deleteCurrentDevice();
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
double AudioDeviceManager::chooseBestSampleRate (double rate) const
|
||||
{
|
||||
jassert (currentAudioDevice != nullptr);
|
||||
|
||||
const Array<double> rates (currentAudioDevice->getAvailableSampleRates());
|
||||
|
||||
if (rate > 0 && rates.contains (rate))
|
||||
return rate;
|
||||
|
||||
double lowestAbove44 = 0.0;
|
||||
|
||||
for (int i = rates.size(); --i >= 0;)
|
||||
{
|
||||
const double sr = rates[i];
|
||||
|
||||
if (sr >= 44100.0 && (lowestAbove44 < 1.0 || sr < lowestAbove44))
|
||||
lowestAbove44 = sr;
|
||||
}
|
||||
|
||||
if (lowestAbove44 > 0.0)
|
||||
return lowestAbove44;
|
||||
|
||||
return rates[0];
|
||||
}
|
||||
|
||||
int AudioDeviceManager::chooseBestBufferSize (int bufferSize) const
|
||||
{
|
||||
jassert (currentAudioDevice != nullptr);
|
||||
|
||||
if (bufferSize > 0 && currentAudioDevice->getAvailableBufferSizes().contains (bufferSize))
|
||||
return bufferSize;
|
||||
|
||||
return currentAudioDevice->getDefaultBufferSize();
|
||||
}
|
||||
|
||||
void AudioDeviceManager::stopDevice()
|
||||
{
|
||||
if (currentAudioDevice != nullptr)
|
||||
currentAudioDevice->stop();
|
||||
|
||||
testSound = nullptr;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::closeAudioDevice()
|
||||
{
|
||||
stopDevice();
|
||||
currentAudioDevice = nullptr;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDeviceManager::updateXml()
|
||||
{
|
||||
lastExplicitSettings = new XmlElement ("DEVICESETUP");
|
||||
|
||||
lastExplicitSettings->setAttribute ("deviceType", currentDeviceType);
|
||||
lastExplicitSettings->setAttribute ("audioOutputDeviceName", currentSetup.outputDeviceName);
|
||||
lastExplicitSettings->setAttribute ("audioInputDeviceName", currentSetup.inputDeviceName);
|
||||
|
||||
if (currentAudioDevice != nullptr)
|
||||
{
|
||||
lastExplicitSettings->setAttribute ("audioDeviceRate", currentAudioDevice->getCurrentSampleRate());
|
||||
|
||||
if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples())
|
||||
lastExplicitSettings->setAttribute ("audioDeviceBufferSize", currentAudioDevice->getCurrentBufferSizeSamples());
|
||||
|
||||
if (! currentSetup.useDefaultInputChannels)
|
||||
lastExplicitSettings->setAttribute ("audioDeviceInChans", currentSetup.inputChannels.toString (2));
|
||||
|
||||
if (! currentSetup.useDefaultOutputChannels)
|
||||
lastExplicitSettings->setAttribute ("audioDeviceOutChans", currentSetup.outputChannels.toString (2));
|
||||
}
|
||||
|
||||
for (int i = 0; i < enabledMidiInputs.size(); ++i)
|
||||
lastExplicitSettings->createNewChildElement ("MIDIINPUT")
|
||||
->setAttribute ("name", enabledMidiInputs[i]->getName());
|
||||
|
||||
if (midiInsFromXml.size() > 0)
|
||||
{
|
||||
// Add any midi devices that have been enabled before, but which aren't currently
|
||||
// open because the device has been disconnected.
|
||||
const StringArray availableMidiDevices (MidiInput::getDevices());
|
||||
|
||||
for (int i = 0; i < midiInsFromXml.size(); ++i)
|
||||
if (! availableMidiDevices.contains (midiInsFromXml[i], true))
|
||||
lastExplicitSettings->createNewChildElement ("MIDIINPUT")
|
||||
->setAttribute ("name", midiInsFromXml[i]);
|
||||
}
|
||||
|
||||
if (defaultMidiOutputName.isNotEmpty())
|
||||
lastExplicitSettings->setAttribute ("defaultMidiOutput", defaultMidiOutputName);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDeviceManager::addAudioCallback (AudioIODeviceCallback* newCallback)
|
||||
{
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
if (callbacks.contains (newCallback))
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentAudioDevice != nullptr && newCallback != nullptr)
|
||||
newCallback->audioDeviceAboutToStart (currentAudioDevice);
|
||||
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
callbacks.add (newCallback);
|
||||
}
|
||||
|
||||
void AudioDeviceManager::removeAudioCallback (AudioIODeviceCallback* callbackToRemove)
|
||||
{
|
||||
if (callbackToRemove != nullptr)
|
||||
{
|
||||
bool needsDeinitialising = currentAudioDevice != nullptr;
|
||||
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
|
||||
needsDeinitialising = needsDeinitialising && callbacks.contains (callbackToRemove);
|
||||
callbacks.removeFirstMatchingValue (callbackToRemove);
|
||||
}
|
||||
|
||||
if (needsDeinitialising)
|
||||
callbackToRemove->audioDeviceStopped();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples)
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
|
||||
if (inputLevelMeasurementEnabledCount.get() > 0 && numInputChannels > 0)
|
||||
{
|
||||
for (int j = 0; j < numSamples; ++j)
|
||||
{
|
||||
float s = 0;
|
||||
|
||||
for (int i = 0; i < numInputChannels; ++i)
|
||||
s += std::abs (inputChannelData[i][j]);
|
||||
|
||||
s /= numInputChannels;
|
||||
|
||||
const double decayFactor = 0.99992;
|
||||
|
||||
if (s > inputLevel)
|
||||
inputLevel = s;
|
||||
else if (inputLevel > 0.001f)
|
||||
inputLevel *= decayFactor;
|
||||
else
|
||||
inputLevel = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inputLevel = 0;
|
||||
}
|
||||
|
||||
if (callbacks.size() > 0)
|
||||
{
|
||||
const double callbackStartTime = Time::getMillisecondCounterHiRes();
|
||||
|
||||
tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true);
|
||||
|
||||
callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels,
|
||||
outputChannelData, numOutputChannels, numSamples);
|
||||
|
||||
float** const tempChans = tempBuffer.getArrayOfWritePointers();
|
||||
|
||||
for (int i = callbacks.size(); --i > 0;)
|
||||
{
|
||||
callbacks.getUnchecked(i)->audioDeviceIOCallback (inputChannelData, numInputChannels,
|
||||
tempChans, numOutputChannels, numSamples);
|
||||
|
||||
for (int chan = 0; chan < numOutputChannels; ++chan)
|
||||
{
|
||||
if (const float* const src = tempChans [chan])
|
||||
if (float* const dst = outputChannelData [chan])
|
||||
for (int j = 0; j < numSamples; ++j)
|
||||
dst[j] += src[j];
|
||||
}
|
||||
}
|
||||
|
||||
const double msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime;
|
||||
const double filterAmount = 0.2;
|
||||
cpuUsageMs += filterAmount * (msTaken - cpuUsageMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < numOutputChannels; ++i)
|
||||
zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples);
|
||||
}
|
||||
|
||||
if (testSound != nullptr)
|
||||
{
|
||||
const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition);
|
||||
const float* const src = testSound->getReadPointer (0, testSoundPosition);
|
||||
|
||||
for (int i = 0; i < numOutputChannels; ++i)
|
||||
for (int j = 0; j < numSamps; ++j)
|
||||
outputChannelData [i][j] += src[j];
|
||||
|
||||
testSoundPosition += numSamps;
|
||||
if (testSoundPosition >= testSound->getNumSamples())
|
||||
testSound = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device)
|
||||
{
|
||||
cpuUsageMs = 0;
|
||||
|
||||
const double sampleRate = device->getCurrentSampleRate();
|
||||
const int blockSize = device->getCurrentBufferSizeSamples();
|
||||
|
||||
if (sampleRate > 0.0 && blockSize > 0)
|
||||
{
|
||||
const double msPerBlock = 1000.0 * blockSize / sampleRate;
|
||||
timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0;
|
||||
}
|
||||
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
for (int i = callbacks.size(); --i >= 0;)
|
||||
callbacks.getUnchecked(i)->audioDeviceAboutToStart (device);
|
||||
}
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
void AudioDeviceManager::audioDeviceStoppedInt()
|
||||
{
|
||||
cpuUsageMs = 0;
|
||||
timeToCpuScale = 0;
|
||||
sendChangeMessage();
|
||||
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
for (int i = callbacks.size(); --i >= 0;)
|
||||
callbacks.getUnchecked(i)->audioDeviceStopped();
|
||||
}
|
||||
|
||||
void AudioDeviceManager::audioDeviceErrorInt (const String& message)
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
for (int i = callbacks.size(); --i >= 0;)
|
||||
callbacks.getUnchecked(i)->audioDeviceError (message);
|
||||
}
|
||||
|
||||
double AudioDeviceManager::getCpuUsage() const
|
||||
{
|
||||
return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool enabled)
|
||||
{
|
||||
if (enabled != isMidiInputEnabled (name))
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
const int index = MidiInput::getDevices().indexOf (name);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
if (MidiInput* const midiIn = MidiInput::openDevice (index, callbackHandler))
|
||||
{
|
||||
enabledMidiInputs.add (midiIn);
|
||||
midiIn->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = enabledMidiInputs.size(); --i >= 0;)
|
||||
if (enabledMidiInputs[i]->getName() == name)
|
||||
enabledMidiInputs.remove (i);
|
||||
}
|
||||
|
||||
updateXml();
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioDeviceManager::isMidiInputEnabled (const String& name) const
|
||||
{
|
||||
for (int i = enabledMidiInputs.size(); --i >= 0;)
|
||||
if (enabledMidiInputs[i]->getName() == name)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCallback* callbackToAdd)
|
||||
{
|
||||
removeMidiInputCallback (name, callbackToAdd);
|
||||
|
||||
if (name.isEmpty() || isMidiInputEnabled (name))
|
||||
{
|
||||
const ScopedLock sl (midiCallbackLock);
|
||||
|
||||
MidiCallbackInfo mc;
|
||||
mc.deviceName = name;
|
||||
mc.callback = callbackToAdd;
|
||||
midiCallbacks.add (mc);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputCallback* callbackToRemove)
|
||||
{
|
||||
for (int i = midiCallbacks.size(); --i >= 0;)
|
||||
{
|
||||
const MidiCallbackInfo& mc = midiCallbacks.getReference(i);
|
||||
|
||||
if (mc.callback == callbackToRemove && mc.deviceName == name)
|
||||
{
|
||||
const ScopedLock sl (midiCallbackLock);
|
||||
midiCallbacks.remove (i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const MidiMessage& message)
|
||||
{
|
||||
if (! message.isActiveSense())
|
||||
{
|
||||
const ScopedLock sl (midiCallbackLock);
|
||||
|
||||
for (int i = 0; i < midiCallbacks.size(); ++i)
|
||||
{
|
||||
const MidiCallbackInfo& mc = midiCallbacks.getReference(i);
|
||||
|
||||
if (mc.deviceName.isEmpty() || mc.deviceName == source->getName())
|
||||
mc.callback->handleIncomingMidiMessage (source, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName)
|
||||
{
|
||||
if (defaultMidiOutputName != deviceName)
|
||||
{
|
||||
Array<AudioIODeviceCallback*> oldCallbacks;
|
||||
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
oldCallbacks.swapWith (callbacks);
|
||||
}
|
||||
|
||||
if (currentAudioDevice != nullptr)
|
||||
for (int i = oldCallbacks.size(); --i >= 0;)
|
||||
oldCallbacks.getUnchecked(i)->audioDeviceStopped();
|
||||
|
||||
defaultMidiOutput = nullptr;
|
||||
defaultMidiOutputName = deviceName;
|
||||
|
||||
if (deviceName.isNotEmpty())
|
||||
defaultMidiOutput = MidiOutput::openDevice (MidiOutput::getDevices().indexOf (deviceName));
|
||||
|
||||
if (currentAudioDevice != nullptr)
|
||||
for (int i = oldCallbacks.size(); --i >= 0;)
|
||||
oldCallbacks.getUnchecked(i)->audioDeviceAboutToStart (currentAudioDevice);
|
||||
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
oldCallbacks.swapWith (callbacks);
|
||||
}
|
||||
|
||||
updateXml();
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioDeviceManager::playTestSound()
|
||||
{
|
||||
{ // cunningly nested to swap, unlock and delete in that order.
|
||||
ScopedPointer<AudioSampleBuffer> oldSound;
|
||||
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
oldSound = testSound;
|
||||
}
|
||||
}
|
||||
|
||||
testSoundPosition = 0;
|
||||
|
||||
if (currentAudioDevice != nullptr)
|
||||
{
|
||||
const double sampleRate = currentAudioDevice->getCurrentSampleRate();
|
||||
const int soundLength = (int) sampleRate;
|
||||
|
||||
const double frequency = 440.0;
|
||||
const float amplitude = 0.5f;
|
||||
|
||||
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
|
||||
|
||||
AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength);
|
||||
|
||||
for (int i = 0; i < soundLength; ++i)
|
||||
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
|
||||
|
||||
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
|
||||
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
|
||||
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
testSound = newSound;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement)
|
||||
{
|
||||
if (enableMeasurement)
|
||||
++inputLevelMeasurementEnabledCount;
|
||||
else
|
||||
--inputLevelMeasurementEnabledCount;
|
||||
|
||||
inputLevel = 0;
|
||||
}
|
||||
|
||||
double AudioDeviceManager::getCurrentInputLevel() const
|
||||
{
|
||||
jassert (inputLevelMeasurementEnabledCount.get() > 0); // you need to call enableInputLevelMeasurement() before using this!
|
||||
return inputLevel;
|
||||
}
|
||||
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_AUDIODEVICEMANAGER_H_INCLUDED
|
||||
#define JUCE_AUDIODEVICEMANAGER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages the state of some audio and midi i/o devices.
|
||||
|
||||
This class keeps tracks of a currently-selected audio device, through
|
||||
with which it continuously streams data from an audio callback, as well as
|
||||
one or more midi inputs.
|
||||
|
||||
The idea is that your application will create one global instance of this object,
|
||||
and let it take care of creating and deleting specific types of audio devices
|
||||
internally. So when the device is changed, your callbacks will just keep running
|
||||
without having to worry about this.
|
||||
|
||||
The manager can save and reload all of its device settings as XML, which
|
||||
makes it very easy for you to save and reload the audio setup of your
|
||||
application.
|
||||
|
||||
And to make it easy to let the user change its settings, there's a component
|
||||
to do just that - the AudioDeviceSelectorComponent class, which contains a set of
|
||||
device selection/sample-rate/latency controls.
|
||||
|
||||
To use an AudioDeviceManager, create one, and use initialise() to set it up. Then
|
||||
call addAudioCallback() to register your audio callback with it, and use that to process
|
||||
your audio data.
|
||||
|
||||
The manager also acts as a handy hub for incoming midi messages, allowing a
|
||||
listener to register for messages from either a specific midi device, or from whatever
|
||||
the current default midi input device is. The listener then doesn't have to worry about
|
||||
re-registering with different midi devices if they are changed or deleted.
|
||||
|
||||
And yet another neat trick is that amount of CPU time being used is measured and
|
||||
available with the getCpuUsage() method.
|
||||
|
||||
The AudioDeviceManager is a ChangeBroadcaster, and will send a change message to
|
||||
listeners whenever one of its settings is changed.
|
||||
|
||||
@see AudioDeviceSelectorComponent, AudioIODevice, AudioIODeviceType
|
||||
*/
|
||||
class JUCE_API AudioDeviceManager : public ChangeBroadcaster
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a default AudioDeviceManager.
|
||||
|
||||
Initially no audio device will be selected. You should call the initialise() method
|
||||
and register an audio callback with setAudioCallback() before it'll be able to
|
||||
actually make any noise.
|
||||
*/
|
||||
AudioDeviceManager();
|
||||
|
||||
/** Destructor. */
|
||||
~AudioDeviceManager();
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This structure holds a set of properties describing the current audio setup.
|
||||
|
||||
An AudioDeviceManager uses this class to save/load its current settings, and to
|
||||
specify your preferred options when opening a device.
|
||||
|
||||
@see AudioDeviceManager::setAudioDeviceSetup(), AudioDeviceManager::initialise()
|
||||
*/
|
||||
struct JUCE_API AudioDeviceSetup
|
||||
{
|
||||
/** Creates an AudioDeviceSetup object.
|
||||
|
||||
The default constructor sets all the member variables to indicate default values.
|
||||
You can then fill-in any values you want to before passing the object to
|
||||
AudioDeviceManager::initialise().
|
||||
*/
|
||||
AudioDeviceSetup();
|
||||
|
||||
bool operator== (const AudioDeviceSetup& other) const;
|
||||
|
||||
/** The name of the audio device used for output.
|
||||
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;
|
||||
|
||||
/** The current sample rate.
|
||||
This rate is used for both the input and output devices.
|
||||
A value of 0 indicates that you don't care what rate is used, and the
|
||||
device will choose a sensible rate for you.
|
||||
*/
|
||||
double sampleRate;
|
||||
|
||||
/** The buffer size, in samples.
|
||||
This buffer size is used for both the input and output devices.
|
||||
A value of 0 indicates the default buffer size.
|
||||
*/
|
||||
int bufferSize;
|
||||
|
||||
/** The set of active input channels.
|
||||
The bits that are set in this array indicate the channels of the
|
||||
input device that are active.
|
||||
If useDefaultInputChannels is true, this value is ignored.
|
||||
*/
|
||||
BigInteger inputChannels;
|
||||
|
||||
/** If this is true, it indicates that the inputChannels array
|
||||
should be ignored, and instead, the device's default channels
|
||||
should be used.
|
||||
*/
|
||||
bool useDefaultInputChannels;
|
||||
|
||||
/** The set of active output channels.
|
||||
The bits that are set in this array indicate the channels of the
|
||||
input device that are active.
|
||||
If useDefaultOutputChannels is true, this value is ignored.
|
||||
*/
|
||||
BigInteger outputChannels;
|
||||
|
||||
/** If this is true, it indicates that the outputChannels array
|
||||
should be ignored, and instead, the device's default channels
|
||||
should be used.
|
||||
*/
|
||||
bool useDefaultOutputChannels;
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Opens a set of audio devices ready for use.
|
||||
|
||||
This will attempt to open either a default audio device, or one that was
|
||||
previously saved as XML.
|
||||
|
||||
@param numInputChannelsNeeded the maximum number of input channels your app would like to
|
||||
use (the actual number of channels opened may be less than
|
||||
the number requested)
|
||||
@param numOutputChannelsNeeded the maximum number of output channels your app would like to
|
||||
use (the actual number of channels opened may be less than
|
||||
the number requested)
|
||||
@param savedState either a previously-saved state that was produced
|
||||
by createStateXml(), or nullptr if you want the manager
|
||||
to choose the best device to open.
|
||||
@param selectDefaultDeviceOnFailure if true, then if the device specified in the XML
|
||||
fails to open, then a default device will be used
|
||||
instead. If false, then on failure, no device is
|
||||
opened.
|
||||
@param preferredDefaultDeviceName if this is not empty, and there's a device with this
|
||||
name, then that will be used as the default device
|
||||
(assuming that there wasn't one specified in the XML).
|
||||
The string can actually be a simple wildcard, containing "*"
|
||||
and "?" characters
|
||||
@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
|
||||
|
||||
@returns an error message if anything went wrong, or an empty string if it worked ok.
|
||||
*/
|
||||
String initialise (int numInputChannelsNeeded,
|
||||
int numOutputChannelsNeeded,
|
||||
const XmlElement* savedState,
|
||||
bool selectDefaultDeviceOnFailure,
|
||||
const String& preferredDefaultDeviceName = String(),
|
||||
const AudioDeviceSetup* preferredSetupOptions = nullptr);
|
||||
|
||||
/** Resets everything to a default device setup, clearing any stored settings. */
|
||||
String initialiseWithDefaultDevices (int numInputChannelsNeeded,
|
||||
int numOutputChannelsNeeded);
|
||||
|
||||
/** Returns some XML representing the current state of the manager.
|
||||
|
||||
This stores the current device, its samplerate, block size, etc, and
|
||||
can be restored later with initialise().
|
||||
|
||||
Note that this can return a null pointer if no settings have been explicitly changed
|
||||
(i.e. if the device manager has just been left in its default state).
|
||||
*/
|
||||
XmlElement* createStateXml() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current device properties that are in use.
|
||||
@see setAudioDeviceSetup
|
||||
*/
|
||||
void getAudioDeviceSetup (AudioDeviceSetup& result);
|
||||
|
||||
/** Changes the current device or its settings.
|
||||
|
||||
If you want to change a device property, like the current sample rate or
|
||||
block size, you can call getAudioDeviceSetup() to retrieve the current
|
||||
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 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
|
||||
will be returned. If it's false, then the device is treated as a
|
||||
temporary or default device, and a call to createStateXml() will
|
||||
return either the last settings that were made with treatAsChosenDevice
|
||||
as true, or the last XML settings that were passed into initialise().
|
||||
@returns an error message if anything went wrong, or an empty string if it worked ok.
|
||||
|
||||
@see getAudioDeviceSetup
|
||||
*/
|
||||
String setAudioDeviceSetup (const AudioDeviceSetup& newSetup,
|
||||
bool treatAsChosenDevice);
|
||||
|
||||
|
||||
/** Returns the currently-active audio device. */
|
||||
AudioIODevice* getCurrentAudioDevice() const noexcept { return currentAudioDevice; }
|
||||
|
||||
/** Returns the type of audio device currently in use.
|
||||
@see setCurrentAudioDeviceType
|
||||
*/
|
||||
String getCurrentAudioDeviceType() const { return currentDeviceType; }
|
||||
|
||||
/** Returns the currently active audio device type object.
|
||||
Don't keep a copy of this pointer - it's owned by the device manager and could
|
||||
change at any time.
|
||||
*/
|
||||
AudioIODeviceType* getCurrentDeviceTypeObject() const;
|
||||
|
||||
/** Changes the class of audio device being used.
|
||||
|
||||
This switches between, e.g. ASIO and DirectSound. On the Mac you probably won't ever call
|
||||
this because there's only one type: CoreAudio.
|
||||
|
||||
For a list of types, see getAvailableDeviceTypes().
|
||||
*/
|
||||
void setCurrentAudioDeviceType (const String& type,
|
||||
bool treatAsChosenDevice);
|
||||
|
||||
/** Closes the currently-open device.
|
||||
You can call restartLastAudioDevice() later to reopen it in the same state
|
||||
that it was just in.
|
||||
*/
|
||||
void closeAudioDevice();
|
||||
|
||||
/** Tries to reload the last audio device that was running.
|
||||
|
||||
Note that this only reloads the last device that was running before
|
||||
closeAudioDevice() was called - it doesn't reload any kind of saved-state,
|
||||
and can only be called after a device has been opened with SetAudioDevice().
|
||||
|
||||
If a device is already open, this call will do nothing.
|
||||
*/
|
||||
void restartLastAudioDevice();
|
||||
|
||||
//==============================================================================
|
||||
/** Registers an audio callback to be used.
|
||||
|
||||
The manager will redirect callbacks from whatever audio device is currently
|
||||
in use to all registered callback objects. If more than one callback is
|
||||
active, they will all be given the same input data, and their outputs will
|
||||
be summed.
|
||||
|
||||
If necessary, this method will invoke audioDeviceAboutToStart() on the callback
|
||||
object before returning.
|
||||
|
||||
To remove a callback, use removeAudioCallback().
|
||||
*/
|
||||
void addAudioCallback (AudioIODeviceCallback* newCallback);
|
||||
|
||||
/** Deregisters a previously added callback.
|
||||
|
||||
If necessary, this method will invoke audioDeviceStopped() on the callback
|
||||
object before returning.
|
||||
|
||||
@see addAudioCallback
|
||||
*/
|
||||
void removeAudioCallback (AudioIODeviceCallback* callback);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the average proportion of available CPU being spent inside the audio callbacks.
|
||||
@returns A value between 0 and 1.0 to indicate the approximate proportion of CPU
|
||||
time spent in the callbacks.
|
||||
*/
|
||||
double getCpuUsage() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Enables or disables a midi input device.
|
||||
|
||||
The list of devices can be obtained with the MidiInput::getDevices() method.
|
||||
|
||||
Any incoming messages from enabled input devices will be forwarded on to all the
|
||||
listeners that have been registered with the addMidiInputCallback() method. They
|
||||
can either register for messages from a particular device, or from just the
|
||||
"default" midi input.
|
||||
|
||||
Routing the midi input via an AudioDeviceManager means that when a listener
|
||||
registers for the default midi input, this default device can be changed by the
|
||||
manager without the listeners having to know about it or re-register.
|
||||
|
||||
It also means that a listener can stay registered for a midi input that is disabled
|
||||
or not present, so that when the input is re-enabled, the listener will start
|
||||
receiving messages again.
|
||||
|
||||
@see addMidiInputCallback, isMidiInputEnabled
|
||||
*/
|
||||
void setMidiInputEnabled (const String& midiInputDeviceName, bool enabled);
|
||||
|
||||
/** Returns true if a given midi input device is being used.
|
||||
@see setMidiInputEnabled
|
||||
*/
|
||||
bool isMidiInputEnabled (const String& midiInputDeviceName) const;
|
||||
|
||||
/** Registers a listener for callbacks when midi events arrive from a midi input.
|
||||
|
||||
The device name can be empty to indicate that it wants to receive all incoming
|
||||
events from all the enabled MIDI inputs. Or it can be the name of one of the
|
||||
MIDI input devices if it just wants the events from that device. (see
|
||||
MidiInput::getDevices() for the list of device names).
|
||||
|
||||
Only devices which are enabled (see the setMidiInputEnabled() method) will have their
|
||||
events forwarded on to listeners.
|
||||
*/
|
||||
void addMidiInputCallback (const String& midiInputDeviceName,
|
||||
MidiInputCallback* callback);
|
||||
|
||||
/** Removes a listener that was previously registered with addMidiInputCallback(). */
|
||||
void removeMidiInputCallback (const String& midiInputDeviceName,
|
||||
MidiInputCallback* callback);
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a midi output device to use as the default.
|
||||
|
||||
The list of devices can be obtained with the MidiOutput::getDevices() method.
|
||||
|
||||
The specified device will be opened automatically and can be retrieved with the
|
||||
getDefaultMidiOutput() method.
|
||||
|
||||
Pass in an empty string to deselect all devices. For the default device, you
|
||||
can use MidiOutput::getDevices() [MidiOutput::getDefaultDeviceIndex()].
|
||||
|
||||
@see getDefaultMidiOutput, getDefaultMidiOutputName
|
||||
*/
|
||||
void setDefaultMidiOutput (const String& deviceName);
|
||||
|
||||
/** Returns the name of the default midi output.
|
||||
@see setDefaultMidiOutput, getDefaultMidiOutput
|
||||
*/
|
||||
const String& getDefaultMidiOutputName() const noexcept { return defaultMidiOutputName; }
|
||||
|
||||
/** Returns the current default midi output device.
|
||||
If no device has been selected, or the device can't be opened, this will return nullptr.
|
||||
@see getDefaultMidiOutputName
|
||||
*/
|
||||
MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput; }
|
||||
|
||||
/** Returns a list of the types of device supported. */
|
||||
const OwnedArray<AudioIODeviceType>& getAvailableDeviceTypes();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a list of available types.
|
||||
|
||||
This will add a set of new AudioIODeviceType objects to the specified list, to
|
||||
represent each available types of device.
|
||||
|
||||
You can override this if your app needs to do something specific, like avoid
|
||||
using DirectSound devices, etc.
|
||||
*/
|
||||
virtual void createAudioDeviceTypes (OwnedArray<AudioIODeviceType>& types);
|
||||
|
||||
/** Adds a new device type to the list of types.
|
||||
The manager will take ownership of the object that is passed-in.
|
||||
*/
|
||||
void addAudioDeviceType (AudioIODeviceType* newDeviceType);
|
||||
|
||||
//==============================================================================
|
||||
/** Plays a beep through the current audio device.
|
||||
|
||||
This is here to allow the audio setup UI panels to easily include a "test"
|
||||
button so that the user can check where the audio is coming from.
|
||||
*/
|
||||
void playTestSound();
|
||||
|
||||
/** Turns on level-measuring.
|
||||
|
||||
When enabled, the device manager will measure the peak input level
|
||||
across all channels, and you can get this level by calling getCurrentInputLevel().
|
||||
|
||||
This is mainly intended for audio setup UI panels to use to create a mic
|
||||
level display, so that the user can check that they've selected the right
|
||||
device.
|
||||
|
||||
A simple filter is used to make the level decay smoothly, but this is
|
||||
only intended for giving rough feedback, and not for any kind of accurate
|
||||
measurement.
|
||||
*/
|
||||
void enableInputLevelMeasurement (bool enableMeasurement);
|
||||
|
||||
/** Returns the current input level.
|
||||
To use this, you must first enable it by calling enableInputLevelMeasurement().
|
||||
See enableInputLevelMeasurement() for more info.
|
||||
*/
|
||||
double getCurrentInputLevel() const;
|
||||
|
||||
/** Returns the a lock that can be used to synchronise access to the audio callback.
|
||||
Obviously while this is locked, you're blocking the audio thread from running, so
|
||||
it must only be used for very brief periods when absolutely necessary.
|
||||
*/
|
||||
CriticalSection& getAudioCallbackLock() noexcept { return audioCallbackLock; }
|
||||
|
||||
/** Returns the a lock that can be used to synchronise access to the midi callback.
|
||||
Obviously while this is locked, you're blocking the midi system from running, so
|
||||
it must only be used for very brief periods when absolutely necessary.
|
||||
*/
|
||||
CriticalSection& getMidiCallbackLock() noexcept { return midiCallbackLock; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
OwnedArray<AudioIODeviceType> availableDeviceTypes;
|
||||
OwnedArray<AudioDeviceSetup> lastDeviceTypeConfigs;
|
||||
|
||||
AudioDeviceSetup currentSetup;
|
||||
ScopedPointer<AudioIODevice> currentAudioDevice;
|
||||
Array<AudioIODeviceCallback*> callbacks;
|
||||
int numInputChansNeeded, numOutputChansNeeded;
|
||||
String currentDeviceType;
|
||||
BigInteger inputChannels, outputChannels;
|
||||
ScopedPointer<XmlElement> lastExplicitSettings;
|
||||
mutable bool listNeedsScanning;
|
||||
bool useInputNames;
|
||||
Atomic<int> inputLevelMeasurementEnabledCount;
|
||||
double inputLevel;
|
||||
ScopedPointer<AudioSampleBuffer> testSound;
|
||||
int testSoundPosition;
|
||||
AudioSampleBuffer tempBuffer;
|
||||
|
||||
struct MidiCallbackInfo
|
||||
{
|
||||
String deviceName;
|
||||
MidiInputCallback* callback;
|
||||
};
|
||||
|
||||
StringArray midiInsFromXml;
|
||||
OwnedArray<MidiInput> enabledMidiInputs;
|
||||
Array<MidiCallbackInfo> midiCallbacks;
|
||||
|
||||
String defaultMidiOutputName;
|
||||
ScopedPointer<MidiOutput> defaultMidiOutput;
|
||||
CriticalSection audioCallbackLock, midiCallbackLock;
|
||||
|
||||
double cpuUsageMs, timeToCpuScale;
|
||||
|
||||
//==============================================================================
|
||||
class CallbackHandler;
|
||||
friend class CallbackHandler;
|
||||
friend struct ContainerDeletePolicy<CallbackHandler>;
|
||||
ScopedPointer<CallbackHandler> callbackHandler;
|
||||
|
||||
void audioDeviceIOCallbackInt (const float** inputChannelData, int totalNumInputChannels,
|
||||
float** outputChannelData, int totalNumOutputChannels, int numSamples);
|
||||
void audioDeviceAboutToStartInt (AudioIODevice*);
|
||||
void audioDeviceStoppedInt();
|
||||
void audioDeviceErrorInt (const String&);
|
||||
void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&);
|
||||
void audioDeviceListChanged();
|
||||
|
||||
String restartDevice (int blockSizeToUse, double sampleRateToUse,
|
||||
const BigInteger& ins, const BigInteger& outs);
|
||||
void stopDevice();
|
||||
|
||||
void updateXml();
|
||||
|
||||
void createDeviceTypesIfNeeded();
|
||||
void scanDevicesIfNeeded();
|
||||
void deleteCurrentDevice();
|
||||
double chooseBestSampleRate (double preferred) const;
|
||||
int chooseBestBufferSize (int preferred) const;
|
||||
void insertDefaultDeviceNames (AudioDeviceSetup&) const;
|
||||
String initialiseDefault (const String& preferredDefaultDeviceName, const AudioDeviceSetup*);
|
||||
String initialiseFromXML (const XmlElement&, bool selectDefaultDeviceOnFailure,
|
||||
const String& preferredDefaultDeviceName, const AudioDeviceSetup*);
|
||||
|
||||
AudioIODeviceType* findType (const String& inputName, const String& outputName);
|
||||
AudioIODeviceType* findType (const String& typeName);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager)
|
||||
};
|
||||
|
||||
#endif // JUCE_AUDIODEVICEMANAGER_H_INCLUDED
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName)
|
||||
: name (deviceName), typeName (deviceTypeName)
|
||||
{
|
||||
}
|
||||
|
||||
AudioIODevice::~AudioIODevice() {}
|
||||
|
||||
void AudioIODeviceCallback::audioDeviceError (const String&) {}
|
||||
bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; }
|
||||
bool AudioIODevice::hasControlPanel() const { return false; }
|
||||
|
||||
bool AudioIODevice::showControlPanel()
|
||||
{
|
||||
jassertfalse; // this should only be called for devices which return true from
|
||||
// their hasControlPanel() method.
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_AUDIOIODEVICE_H_INCLUDED
|
||||
#define JUCE_AUDIOIODEVICE_H_INCLUDED
|
||||
|
||||
class AudioIODevice;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
One of these is passed to an AudioIODevice object to stream the audio data
|
||||
in and out.
|
||||
|
||||
The AudioIODevice will repeatedly call this class's audioDeviceIOCallback()
|
||||
method on its own high-priority audio thread, when it needs to send or receive
|
||||
the next block of data.
|
||||
|
||||
@see AudioIODevice, AudioDeviceManager
|
||||
*/
|
||||
class JUCE_API AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODeviceCallback() {}
|
||||
|
||||
/** Processes a block of incoming and outgoing audio data.
|
||||
|
||||
The subclass's implementation should use the incoming audio for whatever
|
||||
purposes it needs to, and must fill all the output channels with the next
|
||||
block of output data before returning.
|
||||
|
||||
The channel data is arranged with the same array indices as the channel name
|
||||
array returned by AudioIODevice::getOutputChannelNames(), but those channels
|
||||
that aren't specified in AudioIODevice::open() will have a null pointer for their
|
||||
associated channel, so remember to check for this.
|
||||
|
||||
@param inputChannelData a set of arrays containing the audio data for each
|
||||
incoming channel - this data is valid until the function
|
||||
returns. There will be one channel of data for each input
|
||||
channel that was enabled when the audio device was opened
|
||||
(see AudioIODevice::open())
|
||||
@param numInputChannels the number of pointers to channel data in the
|
||||
inputChannelData array.
|
||||
@param outputChannelData a set of arrays which need to be filled with the data
|
||||
that should be sent to each outgoing channel of the device.
|
||||
There will be one channel of data for each output channel
|
||||
that was enabled when the audio device was opened (see
|
||||
AudioIODevice::open())
|
||||
The initial contents of the array is undefined, so the
|
||||
callback function must fill all the channels with zeros if
|
||||
its output is silence. Failing to do this could cause quite
|
||||
an unpleasant noise!
|
||||
@param numOutputChannels the number of pointers to channel data in the
|
||||
outputChannelData array.
|
||||
@param numSamples the number of samples in each channel of the input and
|
||||
output arrays. The number of samples will depend on the
|
||||
audio device's buffer size and will usually remain constant,
|
||||
although this isn't guaranteed, so make sure your code can
|
||||
cope with reasonable changes in the buffer size from one
|
||||
callback to the next.
|
||||
*/
|
||||
virtual void audioDeviceIOCallback (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples) = 0;
|
||||
|
||||
/** Called to indicate that the device is about to start calling back.
|
||||
|
||||
This will be called just before the audio callbacks begin, either when this
|
||||
callback has just been added to an audio device, or after the device has been
|
||||
restarted because of a sample-rate or block-size change.
|
||||
|
||||
You can use this opportunity to find out the sample rate and block size
|
||||
that the device is going to use by calling the AudioIODevice::getCurrentSampleRate()
|
||||
and AudioIODevice::getCurrentBufferSizeSamples() on the supplied pointer.
|
||||
|
||||
@param device the audio IO device that will be used to drive the callback.
|
||||
Note that if you're going to store this this pointer, it is
|
||||
only valid until the next time that audioDeviceStopped is called.
|
||||
*/
|
||||
virtual void audioDeviceAboutToStart (AudioIODevice* device) = 0;
|
||||
|
||||
/** Called to indicate that the device has stopped. */
|
||||
virtual void audioDeviceStopped() = 0;
|
||||
|
||||
/** This can be overridden to be told if the device generates an error while operating.
|
||||
Be aware that this could be called by any thread! And not all devices perform
|
||||
this callback.
|
||||
*/
|
||||
virtual void audioDeviceError (const String& errorMessage);
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Base class for an audio device with synchronised input and output channels.
|
||||
|
||||
Subclasses of this are used to implement different protocols such as DirectSound,
|
||||
ASIO, CoreAudio, etc.
|
||||
|
||||
To create one of these, you'll need to use the AudioIODeviceType class - see the
|
||||
documentation for that class for more info.
|
||||
|
||||
For an easier way of managing audio devices and their settings, have a look at the
|
||||
AudioDeviceManager class.
|
||||
|
||||
@see AudioIODeviceType, AudioDeviceManager
|
||||
*/
|
||||
class JUCE_API AudioIODevice
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODevice();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the device's name, (as set in the constructor). */
|
||||
const String& getName() const noexcept { return name; }
|
||||
|
||||
/** Returns the type of the device.
|
||||
|
||||
E.g. "CoreAudio", "ASIO", etc. - this comes from the AudioIODeviceType that created it.
|
||||
*/
|
||||
const String& getTypeName() const noexcept { return typeName; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the names of all the available output channels on this device.
|
||||
To find out which of these are currently in use, call getActiveOutputChannels().
|
||||
*/
|
||||
virtual StringArray getOutputChannelNames() = 0;
|
||||
|
||||
/** Returns the names of all the available input channels on this device.
|
||||
To find out which of these are currently in use, call getActiveInputChannels().
|
||||
*/
|
||||
virtual StringArray getInputChannelNames() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the set of sample-rates this device supports.
|
||||
@see getCurrentSampleRate
|
||||
*/
|
||||
virtual Array<double> getAvailableSampleRates() = 0;
|
||||
|
||||
/** Returns the set of buffer sizes that are available.
|
||||
@see getCurrentBufferSizeSamples, getDefaultBufferSize
|
||||
*/
|
||||
virtual Array<int> getAvailableBufferSizes() = 0;
|
||||
|
||||
/** Returns the default buffer-size to use.
|
||||
@returns a number of samples
|
||||
@see getAvailableBufferSizes
|
||||
*/
|
||||
virtual int getDefaultBufferSize() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to open the device ready to play.
|
||||
|
||||
@param inputChannels a BigInteger in which a set bit indicates that the corresponding
|
||||
input channel should be enabled
|
||||
@param outputChannels a BigInteger in which a set bit indicates that the corresponding
|
||||
output channel should be enabled
|
||||
@param sampleRate the sample rate to try to use - to find out which rates are
|
||||
available, see getAvailableSampleRates()
|
||||
@param bufferSizeSamples the size of i/o buffer to use - to find out the available buffer
|
||||
sizes, see getAvailableBufferSizes()
|
||||
@returns an error description if there's a problem, or an empty string if it succeeds in
|
||||
opening the device
|
||||
@see close
|
||||
*/
|
||||
virtual String open (const BigInteger& inputChannels,
|
||||
const BigInteger& outputChannels,
|
||||
double sampleRate,
|
||||
int bufferSizeSamples) = 0;
|
||||
|
||||
/** Closes and releases the device if it's open. */
|
||||
virtual void close() = 0;
|
||||
|
||||
/** Returns true if the device is still open.
|
||||
|
||||
A device might spontaneously close itself if something goes wrong, so this checks if
|
||||
it's still open.
|
||||
*/
|
||||
virtual bool isOpen() = 0;
|
||||
|
||||
/** Starts the device actually playing.
|
||||
|
||||
This must be called after the device has been opened.
|
||||
|
||||
@param callback the callback to use for streaming the data.
|
||||
@see AudioIODeviceCallback, open
|
||||
*/
|
||||
virtual void start (AudioIODeviceCallback* callback) = 0;
|
||||
|
||||
/** Stops the device playing.
|
||||
|
||||
Once a device has been started, this will stop it. Any pending calls to the
|
||||
callback class will be flushed before this method returns.
|
||||
*/
|
||||
virtual void stop() = 0;
|
||||
|
||||
/** Returns true if the device is still calling back.
|
||||
|
||||
The device might mysteriously stop, so this checks whether it's
|
||||
still playing.
|
||||
*/
|
||||
virtual bool isPlaying() = 0;
|
||||
|
||||
/** Returns the last error that happened if anything went wrong. */
|
||||
virtual String getLastError() = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the buffer size that the device is currently using.
|
||||
|
||||
If the device isn't actually open, this value doesn't really mean much.
|
||||
*/
|
||||
virtual int getCurrentBufferSizeSamples() = 0;
|
||||
|
||||
/** Returns the sample rate that the device is currently using.
|
||||
|
||||
If the device isn't actually open, this value doesn't really mean much.
|
||||
*/
|
||||
virtual double getCurrentSampleRate() = 0;
|
||||
|
||||
/** Returns the device's current physical bit-depth.
|
||||
|
||||
If the device isn't actually open, this value doesn't really mean much.
|
||||
*/
|
||||
virtual int getCurrentBitDepth() = 0;
|
||||
|
||||
/** Returns a mask showing which of the available output channels are currently
|
||||
enabled.
|
||||
@see getOutputChannelNames
|
||||
*/
|
||||
virtual BigInteger getActiveOutputChannels() const = 0;
|
||||
|
||||
/** Returns a mask showing which of the available input channels are currently
|
||||
enabled.
|
||||
@see getInputChannelNames
|
||||
*/
|
||||
virtual BigInteger getActiveInputChannels() const = 0;
|
||||
|
||||
/** Returns the device's output latency.
|
||||
|
||||
This is the delay in samples between a callback getting a block of data, and
|
||||
that data actually getting played.
|
||||
*/
|
||||
virtual int getOutputLatencyInSamples() = 0;
|
||||
|
||||
/** Returns the device's input latency.
|
||||
|
||||
This is the delay in samples between some audio actually arriving at the soundcard,
|
||||
and the callback getting passed this block of data.
|
||||
*/
|
||||
virtual int getInputLatencyInSamples() = 0;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** True if this device can show a pop-up control panel for editing its settings.
|
||||
|
||||
This is generally just true of ASIO devices. If true, you can call showControlPanel()
|
||||
to display it.
|
||||
*/
|
||||
virtual bool hasControlPanel() const;
|
||||
|
||||
/** Shows a device-specific control panel if there is one.
|
||||
|
||||
This should only be called for devices which return true from hasControlPanel().
|
||||
*/
|
||||
virtual bool showControlPanel();
|
||||
|
||||
/** On devices which support it, this allows automatic gain control or other
|
||||
mic processing to be disabled.
|
||||
If the device doesn't support this operation, it'll return false.
|
||||
*/
|
||||
virtual bool setAudioPreprocessingEnabled (bool shouldBeEnabled);
|
||||
|
||||
//==============================================================================
|
||||
protected:
|
||||
/** Creates a device, setting its name and type member variables. */
|
||||
AudioIODevice (const String& deviceName,
|
||||
const String& typeName);
|
||||
|
||||
/** @internal */
|
||||
String name, typeName;
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_AUDIOIODEVICE_H_INCLUDED
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
AudioIODeviceType::AudioIODeviceType (const String& name)
|
||||
: typeName (name)
|
||||
{
|
||||
}
|
||||
|
||||
AudioIODeviceType::~AudioIODeviceType()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AudioIODeviceType::addListener (Listener* l) { listeners.add (l); }
|
||||
void AudioIODeviceType::removeListener (Listener* l) { listeners.remove (l); }
|
||||
|
||||
void AudioIODeviceType::callDeviceChangeListeners()
|
||||
{
|
||||
listeners.call (&AudioIODeviceType::Listener::audioDeviceListChanged);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_MAC
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! JUCE_IOS
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! (JUCE_WINDOWS && JUCE_WASAPI)
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! (JUCE_WINDOWS && JUCE_DIRECTSOUND)
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! (JUCE_WINDOWS && JUCE_ASIO)
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! (JUCE_LINUX && JUCE_ALSA)
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! (JUCE_LINUX && JUCE_JACK)
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! JUCE_ANDROID
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES)
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; }
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_AUDIOIODEVICETYPE_H_INCLUDED
|
||||
#define JUCE_AUDIOIODEVICETYPE_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc.
|
||||
|
||||
To get a list of available audio driver types, use the AudioDeviceManager::createAudioDeviceTypes()
|
||||
method. Each of the objects returned can then be used to list the available
|
||||
devices of that type. E.g.
|
||||
@code
|
||||
OwnedArray<AudioIODeviceType> types;
|
||||
myAudioDeviceManager.createAudioDeviceTypes (types);
|
||||
|
||||
for (int i = 0; i < types.size(); ++i)
|
||||
{
|
||||
String typeName (types[i]->getTypeName()); // This will be things like "DirectSound", "CoreAudio", etc.
|
||||
|
||||
types[i]->scanForDevices(); // This must be called before getting the list of devices
|
||||
|
||||
StringArray deviceNames (types[i]->getDeviceNames()); // This will now return a list of available devices of this type
|
||||
|
||||
for (int j = 0; j < deviceNames.size(); ++j)
|
||||
{
|
||||
AudioIODevice* device = types[i]->createDevice (deviceNames [j]);
|
||||
|
||||
...
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
|
||||
For an easier way of managing audio devices and their settings, have a look at the
|
||||
AudioDeviceManager class.
|
||||
|
||||
@see AudioIODevice, AudioDeviceManager
|
||||
*/
|
||||
class JUCE_API AudioIODeviceType
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns the name of this type of driver that this object manages.
|
||||
|
||||
This will be something like "DirectSound", "ASIO", "CoreAudio", "ALSA", etc.
|
||||
*/
|
||||
const String& getTypeName() const noexcept { return typeName; }
|
||||
|
||||
//==============================================================================
|
||||
/** Refreshes the object's cached list of known devices.
|
||||
|
||||
This must be called at least once before calling getDeviceNames() or any of
|
||||
the other device creation methods.
|
||||
*/
|
||||
virtual void scanForDevices() = 0;
|
||||
|
||||
/** Returns the list of available devices of this type.
|
||||
|
||||
The scanForDevices() method must have been called to create this list.
|
||||
|
||||
@param wantInputNames only really used by DirectSound where devices are split up
|
||||
into inputs and outputs, this indicates whether to use
|
||||
the input or output name to refer to a pair of devices.
|
||||
*/
|
||||
virtual StringArray getDeviceNames (bool wantInputNames = false) const = 0;
|
||||
|
||||
/** Returns the name of the default device.
|
||||
|
||||
This will be one of the names from the getDeviceNames() list.
|
||||
|
||||
@param forInput if true, this means that a default input device should be
|
||||
returned; if false, it should return the default output
|
||||
*/
|
||||
virtual int getDefaultDeviceIndex (bool forInput) const = 0;
|
||||
|
||||
/** Returns the index of a given device in the list of device names.
|
||||
If asInput is true, it shows the index in the inputs list, otherwise it
|
||||
looks for it in the outputs list.
|
||||
*/
|
||||
virtual int getIndexOfDevice (AudioIODevice* device, bool asInput) const = 0;
|
||||
|
||||
/** Returns true if two different devices can be used for the input and output.
|
||||
*/
|
||||
virtual bool hasSeparateInputsAndOutputs() const = 0;
|
||||
|
||||
/** Creates one of the devices of this type.
|
||||
|
||||
The deviceName must be one of the strings returned by getDeviceNames(), and
|
||||
scanForDevices() must have been called before this method is used.
|
||||
*/
|
||||
virtual AudioIODevice* createDevice (const String& outputDeviceName,
|
||||
const String& inputDeviceName) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A class for receiving events when audio devices are inserted or removed.
|
||||
|
||||
You can register an AudioIODeviceType::Listener with an~AudioIODeviceType object
|
||||
using the AudioIODeviceType::addListener() method, and it will be called when
|
||||
devices of that type are added or removed.
|
||||
|
||||
@see AudioIODeviceType::addListener, AudioIODeviceType::removeListener
|
||||
*/
|
||||
class Listener
|
||||
{
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
|
||||
/** Called when the list of available audio devices changes. */
|
||||
virtual void audioDeviceListChanged() = 0;
|
||||
};
|
||||
|
||||
/** Adds a listener that will be called when this type of device is added or
|
||||
removed from the system.
|
||||
*/
|
||||
void addListener (Listener* listener);
|
||||
|
||||
/** Removes a listener that was previously added with addListener(). */
|
||||
void removeListener (Listener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODeviceType();
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a CoreAudio device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_CoreAudio();
|
||||
/** Creates an iOS device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_iOSAudio();
|
||||
/** Creates a WASAPI device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_WASAPI();
|
||||
/** Creates a DirectSound device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_DirectSound();
|
||||
/** Creates an ASIO device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_ASIO();
|
||||
/** Creates an ALSA device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_ALSA();
|
||||
/** Creates a JACK device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_JACK();
|
||||
/** Creates an Android device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_Android();
|
||||
/** Creates an Android OpenSLES device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_OpenSLES();
|
||||
|
||||
protected:
|
||||
explicit AudioIODeviceType (const String& typeName);
|
||||
|
||||
/** Synchronously calls all the registered device list change listeners. */
|
||||
void callDeviceChangeListeners();
|
||||
|
||||
private:
|
||||
String typeName;
|
||||
ListenerList<Listener> listeners;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_AUDIOIODEVICETYPE_H_INCLUDED
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED
|
||||
#define JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains functions to control the system's master volume.
|
||||
*/
|
||||
class JUCE_API SystemAudioVolume
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Returns the operating system's current volume level in the range 0 to 1.0 */
|
||||
static float JUCE_CALLTYPE getGain();
|
||||
|
||||
/** Attempts to set the operating system's current volume level.
|
||||
@param newGain the level, between 0 and 1.0
|
||||
@returns true if the operation succeeds
|
||||
*/
|
||||
static bool JUCE_CALLTYPE setGain (float newGain);
|
||||
|
||||
/** Returns true if the system's audio output is currently muted. */
|
||||
static bool JUCE_CALLTYPE isMuted();
|
||||
|
||||
/** Attempts to mute the operating system's audio output.
|
||||
@param shouldBeMuted true if you want it to be muted
|
||||
@returns true if the operation succeeds
|
||||
*/
|
||||
static bool JUCE_CALLTYPE setMuted (bool shouldBeMuted);
|
||||
|
||||
private:
|
||||
SystemAudioVolume(); // Don't instantiate this class, just call its static fns.
|
||||
JUCE_DECLARE_NON_COPYABLE (SystemAudioVolume)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED
|
||||
Reference in New Issue
Block a user