1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-18 00:54:19 +00:00
JUCE/src/juce_appframework/audio/devices/juce_AudioDeviceManager.cpp
2007-07-31 11:27:20 +00:00

588 lines
19 KiB
C++

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-7 by Raw Material Software ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the
GNU General Public License, as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with JUCE; if not, visit www.gnu.org/licenses or write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
------------------------------------------------------------------------------
If you'd like to release a closed-source product which uses JUCE, commercial
licenses are also available: visit www.rawmaterialsoftware.com/juce for
more information.
==============================================================================
*/
#include "../../../juce_core/basics/juce_StandardHeader.h"
BEGIN_JUCE_NAMESPACE
#include "juce_AudioDeviceManager.h"
#include "../../gui/components/juce_Desktop.h"
//==============================================================================
AudioDeviceManager::AudioDeviceManager()
: currentAudioDevice (0),
currentCallback (0),
numInputChansNeeded (0),
numOutputChansNeeded (2),
lastExplicitSettings (0),
listNeedsScanning (true),
useInputNames (false),
enabledMidiInputs (4),
midiCallbacks (4),
midiCallbackDevices (4),
cpuUsageMs (0),
timeToCpuScale (0)
{
callbackHandler.owner = this;
AudioIODeviceType::createDeviceTypes (availableDeviceTypes);
}
AudioDeviceManager::~AudioDeviceManager()
{
stopDevice();
deleteAndZero (currentAudioDevice);
delete lastExplicitSettings;
}
//==============================================================================
const String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
const int numOutputChannelsNeeded,
const XmlElement* const e,
const bool selectDefaultDeviceOnFailure)
{
if (listNeedsScanning)
refreshDeviceList();
numInputChansNeeded = numInputChannelsNeeded;
numOutputChansNeeded = numOutputChannelsNeeded;
if (e != 0 && e->hasTagName (T("DEVICESETUP")))
{
lastExplicitSettings = new XmlElement (*e);
BitArray ins, outs;
ins.parseString (e->getStringAttribute (T("audioDeviceInChans"), T("11")), 2);
outs.parseString (e->getStringAttribute (T("audioDeviceOutChans"), T("11")), 2);
String error (setAudioDevice (e->getStringAttribute (T("audioDeviceName")),
e->getIntAttribute (T("audioDeviceBufferSize")),
e->getDoubleAttribute (T("audioDeviceRate")),
e->hasAttribute (T("audioDeviceInChans")) ? &ins : 0,
e->hasAttribute (T("audioDeviceOutChans")) ? &outs : 0,
true));
forEachXmlChildElementWithTagName (*e, c, T("MIDIINPUT"))
{
setMidiInputEnabled (c->getStringAttribute (T("name")), true);
}
if (error.isNotEmpty() && selectDefaultDeviceOnFailure)
initialise (numInputChannelsNeeded, numOutputChannelsNeeded, 0, false);
return error;
}
else
{
setInputDeviceNamesUsed (numOutputChannelsNeeded == 0);
String defaultDevice;
if (availableDeviceTypes [0] != 0)
defaultDevice = availableDeviceTypes[0]->getDefaultDeviceName (numOutputChannelsNeeded == 0);
return setAudioDevice (defaultDevice, 0, 0, 0, 0, false);
}
}
XmlElement* AudioDeviceManager::createStateXml() const
{
return lastExplicitSettings != 0 ? new XmlElement (*lastExplicitSettings) : 0;
}
//==============================================================================
const StringArray AudioDeviceManager::getAvailableAudioDeviceNames() const
{
if (listNeedsScanning)
refreshDeviceList();
StringArray names;
for (int i = 0; i < availableDeviceTypes.size(); ++i)
names.addArray (availableDeviceTypes[i]->getDeviceNames (useInputNames));
return names;
}
void AudioDeviceManager::refreshDeviceList() const
{
listNeedsScanning = false;
for (int i = 0; i < availableDeviceTypes.size(); ++i)
availableDeviceTypes[i]->scanForDevices();
}
void AudioDeviceManager::setInputDeviceNamesUsed (const bool useInputNames_)
{
useInputNames = useInputNames_;
sendChangeMessage (this);
}
void AudioDeviceManager::addDeviceNamesToComboBox (ComboBox& combo) const
{
int n = 0;
for (int i = 0; i < availableDeviceTypes.size(); ++i)
{
AudioIODeviceType* const type = availableDeviceTypes[i];
if (availableDeviceTypes.size() > 1)
combo.addSectionHeading (type->getTypeName() + T(" devices:"));
const StringArray names (type->getDeviceNames (useInputNames));
for (int j = 0; j < names.size(); ++j)
combo.addItem (names[j], ++n);
combo.addSeparator();
}
combo.addItem ("<< no audio device >>", -1);
}
const String AudioDeviceManager::getCurrentAudioDeviceName() const
{
if (currentAudioDevice != 0)
return currentAudioDevice->getName();
return String::empty;
}
const String AudioDeviceManager::setAudioDevice (const String& deviceNameToUse,
int blockSizeToUse,
double sampleRateToUse,
const BitArray* inChans,
const BitArray* outChans,
const bool treatAsChosenDevice)
{
stopDevice();
String error;
if (deviceNameToUse.isNotEmpty())
{
const StringArray devNames (getAvailableAudioDeviceNames());
int index = devNames.indexOf (deviceNameToUse, true);
if (index >= 0)
{
if (currentAudioDevice == 0
|| currentAudioDevice->getLastError().isNotEmpty()
|| ! deviceNameToUse.equalsIgnoreCase (currentAudioDevice->getName()))
{
// change of device..
deleteAndZero (currentAudioDevice);
int n = 0;
for (int i = 0; i < availableDeviceTypes.size(); ++i)
{
AudioIODeviceType* const type = availableDeviceTypes[i];
const StringArray names (type->getDeviceNames (useInputNames));
if (index >= n && index < n + names.size())
{
currentAudioDevice = type->createDevice (deviceNameToUse);
break;
}
n += names.size();
}
error = currentAudioDevice->getLastError();
if (error.isNotEmpty())
{
deleteAndZero (currentAudioDevice);
return error;
}
inputChannels.clear();
inputChannels.setRange (0, numInputChansNeeded, true);
outputChannels.clear();
outputChannels.setRange (0, numOutputChansNeeded, true);
}
if (inChans != 0)
inputChannels = *inChans;
if (outChans != 0)
outputChannels = *outChans;
error = restartDevice (blockSizeToUse,
sampleRateToUse,
inputChannels,
outputChannels);
if (error.isNotEmpty())
{
deleteAndZero (currentAudioDevice);
}
}
else
{
deleteAndZero (currentAudioDevice);
error << "No such device: " << deviceNameToUse;
}
}
else
{
deleteAndZero (currentAudioDevice);
}
if (treatAsChosenDevice && error.isEmpty())
{
delete lastExplicitSettings;
lastExplicitSettings = new XmlElement (T("DEVICESETUP"));
lastExplicitSettings->setAttribute (T("audioDeviceName"), getCurrentAudioDeviceName());
if (currentAudioDevice != 0)
{
lastExplicitSettings->setAttribute (T("audioDeviceRate"), currentAudioDevice->getCurrentSampleRate());
if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples())
lastExplicitSettings->setAttribute (T("audioDeviceBufferSize"), currentAudioDevice->getCurrentBufferSizeSamples());
lastExplicitSettings->setAttribute (T("audioDeviceInChans"), inputChannels.toString (2));
lastExplicitSettings->setAttribute (T("audioDeviceOutChans"), outputChannels.toString (2));
}
for (int i = 0; i < enabledMidiInputs.size(); ++i)
{
XmlElement* m = new XmlElement (T("MIDIINPUT"));
m->setAttribute (T("name"), enabledMidiInputs[i]->getName());
lastExplicitSettings->addChildElement (m);
}
}
return error;
}
const String AudioDeviceManager::restartDevice (int blockSizeToUse,
double sampleRateToUse,
const BitArray& inChans,
const BitArray& outChans)
{
stopDevice();
inputChannels = inChans;
outputChannels = outChans;
if (sampleRateToUse > 0)
{
bool ok = false;
for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;)
{
const double sr = currentAudioDevice->getSampleRate (i);
if (sr == sampleRateToUse)
ok = true;
}
if (! ok)
sampleRateToUse = 0;
}
if (sampleRateToUse == 0)
{
double lowestAbove44 = 0.0;
for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;)
{
const double sr = currentAudioDevice->getSampleRate (i);
if (sr >= 44100.0 && (lowestAbove44 == 0 || sr < lowestAbove44))
lowestAbove44 = sr;
}
if (lowestAbove44 == 0.0)
sampleRateToUse = currentAudioDevice->getSampleRate (0);
else
sampleRateToUse = lowestAbove44;
}
const String error (currentAudioDevice->open (inChans, outChans,
sampleRateToUse, blockSizeToUse));
if (error.isEmpty())
currentAudioDevice->start (&callbackHandler);
sendChangeMessage (this);
return error;
}
void AudioDeviceManager::stopDevice()
{
if (currentAudioDevice != 0)
currentAudioDevice->stop();
}
void AudioDeviceManager::setInputChannels (const BitArray& newEnabledChannels,
const bool treatAsChosenDevice)
{
if (currentAudioDevice != 0
&& newEnabledChannels != inputChannels)
{
setAudioDevice (currentAudioDevice->getName(),
currentAudioDevice->getCurrentBufferSizeSamples(),
currentAudioDevice->getCurrentSampleRate(),
&newEnabledChannels, 0,
treatAsChosenDevice);
}
}
void AudioDeviceManager::setOutputChannels (const BitArray& newEnabledChannels,
const bool treatAsChosenDevice)
{
if (currentAudioDevice != 0
&& newEnabledChannels != inputChannels)
{
setAudioDevice (currentAudioDevice->getName(),
currentAudioDevice->getCurrentBufferSizeSamples(),
currentAudioDevice->getCurrentSampleRate(),
0, &newEnabledChannels,
treatAsChosenDevice);
}
}
//==============================================================================
void AudioDeviceManager::setAudioCallback (AudioIODeviceCallback* newCallback)
{
if (newCallback != currentCallback)
{
AudioIODeviceCallback* lastCallback = currentCallback;
audioCallbackLock.enter();
currentCallback = 0;
audioCallbackLock.exit();
if (currentAudioDevice != 0)
{
if (lastCallback != 0)
lastCallback->audioDeviceStopped();
if (newCallback != 0)
newCallback->audioDeviceAboutToStart (currentAudioDevice->getCurrentSampleRate(),
currentAudioDevice->getCurrentBufferSizeSamples());
}
currentCallback = newCallback;
}
}
void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData,
int totalNumInputChannels,
float** outputChannelData,
int totalNumOutputChannels,
int numSamples)
{
const ScopedLock sl (audioCallbackLock);
if (currentCallback != 0)
{
const double callbackStartTime = Time::getMillisecondCounterHiRes();
currentCallback->audioDeviceIOCallback (inputChannelData,
totalNumInputChannels,
outputChannelData,
totalNumOutputChannels,
numSamples);
const double msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime;
const double filterAmount = 0.2;
cpuUsageMs += filterAmount * (msTaken - cpuUsageMs);
}
}
void AudioDeviceManager::audioDeviceAboutToStartInt (double sampleRate, int blockSize)
{
cpuUsageMs = 0;
if (sampleRate > 0.0 && blockSize > 0)
{
const double msPerBlock = 1000.0 * blockSize / sampleRate;
timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0;
}
if (currentCallback != 0)
currentCallback->audioDeviceAboutToStart (sampleRate, blockSize);
sendChangeMessage (this);
}
void AudioDeviceManager::audioDeviceStoppedInt()
{
cpuUsageMs = 0;
timeToCpuScale = 0;
sendChangeMessage (this);
if (currentCallback != 0)
currentCallback->audioDeviceStopped();
}
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)
{
MidiInput* const min = MidiInput::openDevice (index, &callbackHandler);
if (min != 0)
{
enabledMidiInputs.add (min);
min->start();
}
}
}
else
{
for (int i = enabledMidiInputs.size(); --i >= 0;)
if (enabledMidiInputs[i]->getName() == name)
enabledMidiInputs.remove (i);
}
sendChangeMessage (this);
}
}
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* callback)
{
removeMidiInputCallback (callback);
if (name.isEmpty())
{
midiCallbacks.add (callback);
midiCallbackDevices.add (0);
}
else
{
for (int i = enabledMidiInputs.size(); --i >= 0;)
{
if (enabledMidiInputs[i]->getName() == name)
{
const ScopedLock sl (midiCallbackLock);
if (! midiCallbacks.contains (callback))
{
midiCallbacks.add (callback);
midiCallbackDevices.add (enabledMidiInputs[i]);
}
break;
}
}
}
}
void AudioDeviceManager::removeMidiInputCallback (MidiInputCallback* callback)
{
const ScopedLock sl (midiCallbackLock);
const int index = midiCallbacks.indexOf (callback);
midiCallbacks.remove (index);
midiCallbackDevices.remove (index);
}
void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source,
const MidiMessage& message)
{
if (! message.isActiveSense())
{
const bool isDefaultSource = (source == 0 || source == enabledMidiInputs.getFirst());
const ScopedLock sl (midiCallbackLock);
for (int i = midiCallbackDevices.size(); --i >= 0;)
{
MidiInput* const md = midiCallbackDevices.getUnchecked(i);
if (md == source || (md == 0 && isDefaultSource))
midiCallbacks.getUnchecked(i)->handleIncomingMidiMessage (source, message);
}
}
}
//==============================================================================
void AudioDeviceManager::CallbackHandler::audioDeviceIOCallback (const float** inputChannelData,
int totalNumInputChannels,
float** outputChannelData,
int totalNumOutputChannels,
int numSamples)
{
owner->audioDeviceIOCallbackInt (inputChannelData, totalNumInputChannels, outputChannelData, totalNumOutputChannels, numSamples);
}
void AudioDeviceManager::CallbackHandler::audioDeviceAboutToStart (double sampleRate, int blockSize)
{
owner->audioDeviceAboutToStartInt (sampleRate, blockSize);
}
void AudioDeviceManager::CallbackHandler::audioDeviceStopped()
{
owner->audioDeviceStoppedInt();
}
void AudioDeviceManager::CallbackHandler::handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message)
{
owner->handleIncomingMidiMessageInt (source, message);
}
END_JUCE_NAMESPACE