1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-22 01:34:21 +00:00
This commit is contained in:
jules 2008-07-02 20:10:02 +00:00
parent ad56914057
commit 3b59053b2c
15 changed files with 4828 additions and 1574 deletions

View file

@ -78,7 +78,7 @@ static void getDeviceSampleRates (snd_pcm_t* handle, Array <int>& rates)
if (snd_pcm_hw_params_any (handle, hwParams) >= 0
&& snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0)
{
rates.add (ratesToTry[i]);
rates.addIfNotAlreadyThere (ratesToTry[i]);
}
}
}
@ -102,6 +102,9 @@ static void getDeviceProperties (const String& id,
unsigned int& maxChansIn,
Array <int>& rates)
{
if (id.isEmpty())
return;
snd_ctl_t* handle;
if (snd_ctl_open (&handle, id.upToLastOccurrenceOf (T(","), false, false), SND_CTL_NONBLOCK) >= 0)
@ -149,7 +152,7 @@ static void getDeviceProperties (const String& id,
class ALSADevice
{
public:
ALSADevice (const String& deviceName,
ALSADevice (const String& id,
const bool forInput)
: handle (0),
bitDepth (16),
@ -157,7 +160,7 @@ public:
isInput (forInput),
sampleFormat (AudioDataConverters::int16LE)
{
failed (snd_pcm_open (&handle, deviceName,
failed (snd_pcm_open (&handle, id,
forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
SND_PCM_ASYNC));
}
@ -364,12 +367,14 @@ private:
class ALSAThread : public Thread
{
public:
ALSAThread (const String& deviceName_)
ALSAThread (const String& inputId_,
const String& outputId_)
: Thread ("Juce ALSA"),
sampleRate (0),
bufferSize (0),
callback (0),
deviceName (deviceName_),
inputId (inputId_),
outputId (outputId_),
outputDevice (0),
inputDevice (0),
numCallbacks (0),
@ -402,16 +407,9 @@ public:
currentInputChans.clear();
currentOutputChans.clear();
numChannelsRunning = jmax (inputChannels.getHighestBit(),
outputChannels.getHighestBit()) + 1;
numChannelsRunning = jmin (maxNumChans, jlimit ((int) minChansIn,
(int) maxChansIn,
numChannelsRunning));
if (inputChannels.getHighestBit() >= 0)
{
for (int i = 0; i < numChannelsRunning; ++i)
for (int i = 0; i <= inputChannels.getHighestBit(); ++i)
{
inputChannelData [i] = (float*) juce_calloc (sizeof (float) * bufferSize);
@ -425,7 +423,7 @@ public:
if (outputChannels.getHighestBit() >= 0)
{
for (int i = 0; i < numChannelsRunning; ++i)
for (int i = 0; i <= outputChannels.getHighestBit(); ++i)
{
outputChannelData [i] = (float*) juce_calloc (sizeof (float) * bufferSize);
@ -437,9 +435,9 @@ public:
}
}
if (totalNumOutputChannels > 0)
if (totalNumOutputChannels > 0 && outputId.isNotEmpty())
{
outputDevice = new ALSADevice (deviceName, false);
outputDevice = new ALSADevice (outputId, false);
if (outputDevice->error.isNotEmpty())
{
@ -448,7 +446,9 @@ public:
return;
}
if (! outputDevice->setParameters ((unsigned int) sampleRate, numChannelsRunning, bufferSize))
if (! outputDevice->setParameters ((unsigned int) sampleRate,
currentOutputChans.getHighestBit() + 1,
bufferSize))
{
error = outputDevice->error;
deleteAndZero (outputDevice);
@ -456,9 +456,9 @@ public:
}
}
if (totalNumInputChannels > 0)
if (totalNumInputChannels > 0 && inputId.isNotEmpty())
{
inputDevice = new ALSADevice (deviceName, true);
inputDevice = new ALSADevice (inputId, true);
if (inputDevice->error.isNotEmpty())
{
@ -467,7 +467,9 @@ public:
return;
}
if (! inputDevice->setParameters ((unsigned int) sampleRate, numChannelsRunning, bufferSize))
if (! inputDevice->setParameters ((unsigned int) sampleRate,
currentInputChans.getHighestBit() + 1,
bufferSize))
{
error = inputDevice->error;
deleteAndZero (inputDevice);
@ -527,7 +529,6 @@ public:
zeromem (inputChannelDataForCallback, sizeof (inputChannelDataForCallback));
totalNumOutputChannels = 0;
totalNumInputChannels = 0;
numChannelsRunning = 0;
numCallbacks = 0;
}
@ -544,8 +545,6 @@ public:
{
if (inputDevice != 0)
{
jassert (numChannelsRunning >= inputDevice->numChannelsRunning);
if (! inputDevice->read (inputChannelData, bufferSize))
{
DBG ("ALSA: read failure");
@ -584,7 +583,6 @@ public:
failed (snd_pcm_avail_update (outputDevice->handle));
jassert (numChannelsRunning >= outputDevice->numChannelsRunning);
if (! outputDevice->write (outputChannelData, bufferSize))
{
DBG ("ALSA: write failure");
@ -619,7 +617,7 @@ public:
private:
//==============================================================================
const String deviceName;
const String inputId, outputId;
ALSADevice* outputDevice;
ALSADevice* inputDevice;
int numCallbacks;
@ -632,7 +630,6 @@ private:
float* inputChannelData [maxNumChans];
float* inputChannelDataForCallback [maxNumChans];
int totalNumOutputChannels;
int numChannelsRunning;
unsigned int minChansOut, maxChansOut;
unsigned int minChansIn, maxChansIn;
@ -656,8 +653,10 @@ private:
maxChansOut = 0;
minChansIn = 0;
maxChansIn = 0;
unsigned int dummy = 0;
getDeviceProperties (deviceName, minChansOut, maxChansOut, minChansIn, maxChansIn, sampleRates);
getDeviceProperties (inputId, dummy, dummy, minChansIn, maxChansIn, sampleRates);
getDeviceProperties (outputId, minChansOut, maxChansOut, dummy, dummy, sampleRates);
unsigned int i;
for (i = 0; i < maxChansOut; ++i)
@ -674,13 +673,16 @@ class ALSAAudioIODevice : public AudioIODevice
{
public:
ALSAAudioIODevice (const String& deviceName,
const String& deviceId)
const String& inputId_,
const String& outputId_)
: AudioIODevice (deviceName, T("ALSA")),
inputId (inputId_),
outputId (outputId_),
isOpen_ (false),
isStarted (false),
internal (0)
{
internal = new ALSAThread (deviceId);
internal = new ALSAThread (inputId, outputId);
}
~ALSAAudioIODevice()
@ -839,6 +841,8 @@ public:
return internal->error;
}
String inputId, outputId;
private:
bool isOpen_, isStarted;
ALSAThread* internal;
@ -863,10 +867,14 @@ public:
//==============================================================================
void scanForDevices()
{
hasScanned = true;
if (hasScanned)
return;
names.clear();
ids.clear();
hasScanned = true;
inputNames.clear();
inputIds.clear();
outputNames.clear();
outputIds.clear();
snd_ctl_t* handle;
snd_ctl_card_info_t* info;
@ -874,7 +882,7 @@ public:
int cardNum = -1;
while (ids.size() <= 24)
while (outputIds.size() + inputIds.size() <= 32)
{
snd_card_next (&cardNum);
@ -900,18 +908,26 @@ public:
String id, name;
id << "hw:" << cardId << ',' << device;
if (testDevice (id))
bool isInput, isOutput;
if (testDevice (id, isInput, isOutput))
{
name << snd_ctl_card_info_get_name (info);
if (name.isEmpty())
name = id;
if (device > 0)
name << " (" << (device + 1) << ')';
if (isInput)
{
inputNames.add (name);
inputIds.add (id);
}
ids.add (id);
names.add (name);
if (isOutput)
{
outputNames.add (name);
outputIds.add (id);
}
}
}
}
@ -919,35 +935,54 @@ public:
snd_ctl_close (handle);
}
}
inputNames.appendNumbersToDuplicates (false, true);
outputNames.appendNumbersToDuplicates (false, true);
}
const StringArray getDeviceNames (const bool /*preferInputNames*/) const
const StringArray getDeviceNames (const bool wantInputNames) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
StringArray namesCopy (names);
namesCopy.removeDuplicates (true);
return namesCopy;
return wantInputNames ? inputNames : outputNames;
}
const String getDefaultDeviceName (const bool /*preferInputNames*/,
const int /*numInputChannelsNeeded*/,
const int /*numOutputChannelsNeeded*/) const
int getDefaultDeviceIndex (const bool forInput) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
return 0;
}
bool hasSeparateInputsAndOutputs() const { return true; }
int getIndexOfDevice (AudioIODevice* device, const bool asInput) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
return names[0];
ALSAAudioIODevice* const d = dynamic_cast <ALSAAudioIODevice*> (device);
if (d == 0)
return -1;
return asInput ? inputIds.indexOf (d->inputId)
: outputIds.indexOf (d->outputId);
}
AudioIODevice* createDevice (const String& deviceName)
AudioIODevice* createDevice (const String& outputDeviceName,
const String& inputDeviceName)
{
jassert (hasScanned); // need to call scanForDevices() before doing this
const int index = names.indexOf (deviceName);
const int inputIndex = inputNames.indexOf (inputDeviceName);
const int outputIndex = outputNames.indexOf (outputDeviceName);
String deviceName (outputDeviceName);
if (deviceName.isEmpty())
deviceName = inputDeviceName;
if (index >= 0)
return new ALSAAudioIODevice (deviceName, ids [index]);
return new ALSAAudioIODevice (deviceName,
inputIds [inputIndex],
outputIds [outputIndex]);
return 0;
}
@ -956,10 +991,10 @@ public:
juce_UseDebuggingNewOperator
private:
StringArray names, ids;
StringArray inputNames, outputNames, inputIds, outputIds;
bool hasScanned;
static bool testDevice (const String& id)
static bool testDevice (const String& id, bool& isInput, bool& isOutput)
{
unsigned int minChansOut = 0, maxChansOut = 0;
unsigned int minChansIn = 0, maxChansIn = 0;
@ -972,7 +1007,10 @@ private:
+ T(" ins=") + String ((int) minChansIn) + T("-") + String ((int) maxChansIn)
+ T(" rates=") + String (rates.size()));
return (maxChansOut > 0 || maxChansIn > 0) && rates.size() > 0;
isInput = maxChansIn > 0;
isOutput = maxChansOut > 0;
return (isInput || isOutput) && rates.size() > 0;
}
ALSAAudioIODeviceType (const ALSAAudioIODeviceType&);

View file

@ -40,6 +40,11 @@ BEGIN_JUCE_NAMESPACE
#include "../../../src/juce_core/threads/juce_ScopedLock.h"
#include "../../../src/juce_core/threads/juce_Thread.h"
#include "../../../src/juce_core/text/juce_LocalisedStrings.h"
#include "../../../src/juce_appframework/gui/components/buttons/juce_TextButton.h"
#include "../../../src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h"
#include "../../../src/juce_appframework/gui/components/controls/juce_ComboBox.h"
#include "../../../src/juce_appframework/gui/components/special/juce_AudioDeviceSelectorComponent.h"
#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h"
//==============================================================================
@ -613,7 +618,7 @@ public:
for (i = numOutputChans; --i >= 0;)
{
const CallbackDetailsForChannel& info = outputChannelInfo[i];
const float* src = tempOutputBuffers [info.sourceChannelNum];
const float* src = tempOutputBuffers [i];
float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData)
+ info.dataOffsetSamples;
const int stride = info.dataStrideSamples;
@ -833,31 +838,52 @@ class CoreAudioIODevice : public AudioIODevice
{
public:
CoreAudioIODevice (const String& deviceName,
AudioDeviceID deviceId1)
AudioDeviceID inputDeviceId,
const int inputIndex_,
AudioDeviceID outputDeviceId,
const int outputIndex_)
: AudioIODevice (deviceName, "CoreAudio"),
inputIndex (inputIndex_),
outputIndex (outputIndex_),
isOpen_ (false),
isStarted (false)
{
internal = 0;
CoreAudioInternal* device = 0;
CoreAudioInternal* device = new CoreAudioInternal (deviceId1);
lastError = device->error;
if (lastError.isNotEmpty())
if (outputDeviceId == 0 || outputDeviceId == inputDeviceId)
{
deleteAndZero (device);
jassert (inputDeviceId != 0);
device = new CoreAudioInternal (inputDeviceId);
lastError = device->error;
if (lastError.isNotEmpty())
deleteAndZero (device);
}
else
{
CoreAudioInternal* secondDevice = device->getRelatedDevice();
device = new CoreAudioInternal (outputDeviceId);
lastError = device->error;
if (secondDevice != 0)
if (lastError.isNotEmpty())
{
if (device->inChanNames.size() > secondDevice->inChanNames.size())
swapVariables (device, secondDevice);
deleteAndZero (device);
}
else if (inputDeviceId != 0)
{
CoreAudioInternal* secondDevice = new CoreAudioInternal (inputDeviceId);
lastError = device->error;
device->inputDevice = secondDevice;
secondDevice->isSlaveDevice = true;
if (lastError.isNotEmpty())
{
delete secondDevice;
}
else
{
device->inputDevice = secondDevice;
secondDevice->isSlaveDevice = true;
}
}
}
@ -1035,6 +1061,8 @@ public:
return lastError;
}
int inputIndex, outputIndex;
juce_UseDebuggingNewOperator
private:
@ -1065,6 +1093,326 @@ private:
const CoreAudioIODevice& operator= (const CoreAudioIODevice&);
};
//==============================================================================
class CoreAudioDevicePanel : public Component,
public ComboBoxListener,
public ChangeListener,
public ButtonListener
{
public:
CoreAudioDevicePanel (AudioIODeviceType* type_,
AudioIODeviceType::DeviceSetupDetails& setup_)
: type (type_),
setup (setup_)
{
sampleRateDropDown = 0;
sampleRateLabel = 0;
bufferSizeDropDown = 0;
bufferSizeLabel = 0;
outputDeviceDropDown = 0;
outputDeviceLabel = 0;
inputDeviceDropDown = 0;
inputDeviceLabel = 0;
testButton = 0;
inputLevelMeter = 0;
type->scanForDevices();
if (setup.maxNumOutputChannels > 0)
{
outputDeviceDropDown = new ComboBox (String::empty);
addNamesToDeviceBox (*outputDeviceDropDown, false);
outputDeviceDropDown->addListener (this);
addAndMakeVisible (outputDeviceDropDown);
outputDeviceLabel = new Label (String::empty, TRANS ("output:"));
outputDeviceLabel->attachToComponent (outputDeviceDropDown, true);
addAndMakeVisible (testButton = new TextButton (TRANS ("Test")));
testButton->addButtonListener (this);
}
if (setup.maxNumInputChannels > 0)
{
inputDeviceDropDown = new ComboBox (String::empty);
addNamesToDeviceBox (*inputDeviceDropDown, true);
inputDeviceDropDown->addListener (this);
addAndMakeVisible (inputDeviceDropDown);
inputDeviceLabel = new Label (String::empty, TRANS ("input:"));
inputDeviceLabel->attachToComponent (inputDeviceDropDown, true);
addAndMakeVisible (inputLevelMeter = AudioDeviceSelectorComponent::createSimpleLevelMeterComponent (setup_.manager));
}
setup.manager->addChangeListener (this);
changeListenerCallback (0);
}
~CoreAudioDevicePanel()
{
setup.manager->removeChangeListener (this);
deleteAndZero (outputDeviceLabel);
deleteAndZero (inputDeviceLabel);
deleteAndZero (sampleRateLabel);
deleteAndZero (bufferSizeLabel);
deleteAllChildren();
}
void resized()
{
const int lx = proportionOfWidth (0.35f);
const int w = proportionOfWidth (0.5f);
const int h = 24;
const int space = 6;
const int dh = h + space;
int y = 0;
if (outputDeviceDropDown != 0)
{
outputDeviceDropDown->setBounds (lx, y, w, h);
testButton->setBounds (outputDeviceDropDown->getRight() + 8,
outputDeviceDropDown->getY(),
getWidth() - outputDeviceDropDown->getRight() - 10,
h);
y += dh;
}
if (inputDeviceDropDown != 0)
{
inputDeviceDropDown->setBounds (lx, y, w, h);
inputLevelMeter->setBounds (inputDeviceDropDown->getRight() + 8,
inputDeviceDropDown->getY(),
getWidth() - inputDeviceDropDown->getRight() - 10,
h);
y += dh;
}
y += space * 2;
if (sampleRateDropDown != 0)
{
sampleRateDropDown->setBounds (lx, y, w, h);
y += dh;
}
if (bufferSizeDropDown != 0)
{
bufferSizeDropDown->setBounds (lx, y, w, h);
y += dh;
}
}
void comboBoxChanged (ComboBox* comboBoxThatHasChanged)
{
if (comboBoxThatHasChanged == 0)
return;
AudioDeviceManager::AudioDeviceSetup config;
setup.manager->getAudioDeviceSetup (config);
String error;
if (comboBoxThatHasChanged == outputDeviceDropDown
|| comboBoxThatHasChanged == inputDeviceDropDown)
{
config.outputDeviceName = outputDeviceDropDown->getSelectedId() < 0 ? String::empty
: outputDeviceDropDown->getText();
config.inputDeviceName = inputDeviceDropDown->getSelectedId() < 0 ? String::empty
: inputDeviceDropDown->getText();
if (comboBoxThatHasChanged == inputDeviceDropDown)
config.useDefaultInputChannels = true;
else
config.useDefaultOutputChannels = true;
error = setup.manager->setAudioDeviceSetup (config, true);
showCorrectDeviceName (inputDeviceDropDown, true);
showCorrectDeviceName (outputDeviceDropDown, false);
}
else if (comboBoxThatHasChanged == sampleRateDropDown)
{
if (sampleRateDropDown->getSelectedId() > 0)
{
config.sampleRate = sampleRateDropDown->getSelectedId();
error = setup.manager->setAudioDeviceSetup (config, true);
}
}
else if (comboBoxThatHasChanged == bufferSizeDropDown)
{
if (bufferSizeDropDown->getSelectedId() > 0)
{
config.bufferSize = bufferSizeDropDown->getSelectedId();
error = setup.manager->setAudioDeviceSetup (config, true);
}
}
if (error.isNotEmpty())
{
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
T("Error when trying to open audio device!"),
error);
}
}
void buttonClicked (Button*)
{
setup.manager->playTestSound();
}
void changeListenerCallback (void*)
{
AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice();
if (currentDevice != 0)
{
showCorrectDeviceName (inputDeviceDropDown, true);
showCorrectDeviceName (outputDeviceDropDown, false);
// sample rate..
{
if (sampleRateDropDown == 0)
{
addAndMakeVisible (sampleRateDropDown = new ComboBox (String::empty));
sampleRateDropDown->addListener (this);
delete sampleRateLabel;
sampleRateLabel = new Label (String::empty, TRANS ("sample rate:"));
sampleRateLabel->attachToComponent (sampleRateDropDown, true);
}
else
{
sampleRateDropDown->clear();
sampleRateDropDown->removeListener (this);
}
const int numRates = currentDevice->getNumSampleRates();
for (int i = 0; i < numRates; ++i)
{
const int rate = roundDoubleToInt (currentDevice->getSampleRate (i));
sampleRateDropDown->addItem (String (rate) + T(" Hz"), rate);
}
sampleRateDropDown->setSelectedId (roundDoubleToInt (currentDevice->getCurrentSampleRate()), true);
sampleRateDropDown->addListener (this);
}
// buffer size
{
if (bufferSizeDropDown == 0)
{
addAndMakeVisible (bufferSizeDropDown = new ComboBox (String::empty));
bufferSizeDropDown->addListener (this);
delete bufferSizeLabel;
bufferSizeLabel = new Label (String::empty, TRANS ("audio buffer size:"));
bufferSizeLabel->attachToComponent (bufferSizeDropDown, true);
}
else
{
bufferSizeDropDown->clear();
}
const int numBufferSizes = currentDevice->getNumBufferSizesAvailable();
double currentRate = currentDevice->getCurrentSampleRate();
if (currentRate == 0)
currentRate = 44100.0;
for (int i = 0; i < numBufferSizes; ++i)
{
const int bs = currentDevice->getBufferSizeSamples (i);
bufferSizeDropDown->addItem (String (bs)
+ T(" samples (")
+ String (bs * 1000.0 / currentRate, 1)
+ T(" ms)"),
bs);
}
bufferSizeDropDown->setSelectedId (currentDevice->getCurrentBufferSizeSamples(), true);
}
}
else
{
jassert (setup.manager->getCurrentAudioDevice() == 0); // not the correct device type!
deleteAndZero (sampleRateLabel);
deleteAndZero (bufferSizeLabel);
deleteAndZero (sampleRateDropDown);
deleteAndZero (bufferSizeDropDown);
if (outputDeviceDropDown != 0)
outputDeviceDropDown->setSelectedId (-1, true);
if (inputDeviceDropDown != 0)
inputDeviceDropDown->setSelectedId (-1, true);
}
resized();
setSize (getWidth(), getLowestY() + 4);
}
private:
AudioIODeviceType* const type;
const AudioIODeviceType::DeviceSetupDetails setup;
ComboBox* outputDeviceDropDown;
ComboBox* inputDeviceDropDown;
ComboBox* sampleRateDropDown;
ComboBox* bufferSizeDropDown;
Label* outputDeviceLabel;
Label* inputDeviceLabel;
Label* sampleRateLabel;
Label* bufferSizeLabel;
TextButton* testButton;
Component* inputLevelMeter;
void showCorrectDeviceName (ComboBox* const box, const bool isInput)
{
if (box != 0)
{
CoreAudioIODevice* const currentDevice = dynamic_cast <CoreAudioIODevice*> (setup.manager->getCurrentAudioDevice());
const int index = (currentDevice == 0) ? -1
: (isInput ? currentDevice->inputIndex
: currentDevice->outputIndex);
if (index >= 0)
box->setText (type->getDeviceNames (isInput) [index], true);
else
box->setSelectedId (-1, true);
if (! isInput)
testButton->setEnabled (index >= 0);
}
}
void addNamesToDeviceBox (ComboBox& combo, bool isInputs)
{
const StringArray devs (type->getDeviceNames (isInputs));
for (int i = 0; i < devs.size(); ++i)
combo.addItem (devs[i], i + 1);
combo.addItem (TRANS("<< none >>"), -1);
combo.setSelectedId (-1, true);
}
int getLowestY() const
{
int y = 0;
for (int i = getNumChildComponents(); --i >= 0;)
y = jmax (y, getChildComponent (i)->getBottom());
return y;
}
CoreAudioDevicePanel (const CoreAudioDevicePanel&);
const CoreAudioDevicePanel& operator= (const CoreAudioDevicePanel&);
};
//==============================================================================
class CoreAudioIODeviceType : public AudioIODeviceType
@ -1086,8 +1434,10 @@ public:
{
hasScanned = true;
names.clear();
ids.clear();
inputDeviceNames.clear();
outputDeviceNames.clear();
inputIds.clear();
outputIds.clear();
UInt32 size;
if (OK (AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &size, 0)))
@ -1109,8 +1459,20 @@ public:
if (! alreadyLogged)
log (T("CoreAudio device: ") + nameString);
names.add (nameString);
ids.add (devs[i]);
const int numIns = getNumChannels (devs[i], true);
const int numOuts = getNumChannels (devs[i], false);
if (numIns > 0)
{
inputDeviceNames.add (nameString);
inputIds.add (devs[i]);
}
if (numOuts > 0)
{
outputDeviceNames.add (nameString);
outputIds.add (devs[i]);
}
}
}
@ -1119,52 +1481,83 @@ public:
juce_free (devs);
}
inputDeviceNames.appendNumbersToDuplicates (false, true);
outputDeviceNames.appendNumbersToDuplicates (false, true);
}
const StringArray getDeviceNames (const bool /*preferInputNames*/) const
const StringArray getDeviceNames (const bool wantInputNames) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
StringArray namesCopy (names);
namesCopy.removeDuplicates (true);
return namesCopy;
if (wantInputNames)
return inputDeviceNames;
else
return outputDeviceNames;
}
const String getDefaultDeviceName (const bool preferInputNames,
const int numInputChannelsNeeded,
const int numOutputChannelsNeeded) const
int getDefaultDeviceIndex (const bool forInput) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
String result (names[0]);
AudioDeviceID deviceID;
UInt32 size = sizeof (deviceID);
// if they're asking for any input channels at all, use the default input, so we
// get the built-in mic rather than the built-in output with no inputs..
if (AudioHardwareGetProperty (numInputChannelsNeeded > 0
? kAudioHardwarePropertyDefaultInputDevice
: kAudioHardwarePropertyDefaultOutputDevice,
if (AudioHardwareGetProperty (forInput ? kAudioHardwarePropertyDefaultInputDevice
: kAudioHardwarePropertyDefaultOutputDevice,
&size, &deviceID) == noErr)
{
for (int i = ids.size(); --i >= 0;)
if (ids[i] == deviceID)
result = names[i];
if (forInput)
{
for (int i = inputIds.size(); --i >= 0;)
if (inputIds[i] == deviceID)
return i;
}
else
{
for (int i = outputIds.size(); --i >= 0;)
if (outputIds[i] == deviceID)
return i;
}
}
return result;
return 0;
}
AudioIODevice* createDevice (const String& deviceName)
int getIndexOfDevice (AudioIODevice* device, const bool asInput) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
const int index = names.indexOf (deviceName);
CoreAudioIODevice* const d = dynamic_cast <CoreAudioIODevice*> (device);
if (d == 0)
return -1;
return asInput ? d->inputIndex
: d->outputIndex;
}
bool hasSeparateInputsAndOutputs() const { return true; }
AudioIODevice* createDevice (const String& outputDeviceName,
const String& inputDeviceName)
{
jassert (hasScanned); // need to call scanForDevices() before doing this
const int inputIndex = inputDeviceNames.indexOf (inputDeviceName);
const int outputIndex = outputDeviceNames.indexOf (outputDeviceName);
String deviceName (outputDeviceName);
if (deviceName.isEmpty())
deviceName = inputDeviceName;
if (index >= 0)
return new CoreAudioIODevice (deviceName, ids [index]);
return new CoreAudioIODevice (deviceName,
inputIds [inputIndex],
inputIndex,
outputIds [outputIndex],
outputIndex);
return 0;
}
@ -1173,11 +1566,37 @@ public:
juce_UseDebuggingNewOperator
private:
StringArray names;
Array <AudioDeviceID> ids;
StringArray inputDeviceNames, outputDeviceNames;
Array <AudioDeviceID> inputIds, outputIds;
bool hasScanned;
static int getNumChannels (AudioDeviceID deviceID, bool input)
{
int total = 0;
UInt32 size;
if (OK (AudioDeviceGetPropertyInfo (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, 0)))
{
AudioBufferList* const bufList = (AudioBufferList*) juce_calloc (size);
if (OK (AudioDeviceGetProperty (deviceID, 0, input, kAudioDevicePropertyStreamConfiguration, &size, bufList)))
{
const int numStreams = bufList->mNumberBuffers;
for (int i = 0; i < numStreams; ++i)
{
const AudioBuffer& b = bufList->mBuffers[i];
total += b.mNumberChannels;
}
}
juce_free (bufList);
}
return total;
}
CoreAudioIODeviceType (const CoreAudioIODeviceType&);
const CoreAudioIODeviceType& operator= (const CoreAudioIODeviceType&);
};

View file

@ -66,12 +66,17 @@
BEGIN_JUCE_NAMESPACE
#include "../../../src/juce_appframework/audio/devices/juce_AudioIODeviceType.h"
#include "../../../src/juce_appframework/audio/devices/juce_AudioDeviceManager.h"
#include "../../../src/juce_appframework/gui/components/buttons/juce_TextButton.h"
#include "../../../src/juce_appframework/gui/components/lookandfeel/juce_LookAndFeel.h"
#include "../../../src/juce_core/threads/juce_ScopedLock.h"
#include "../../../src/juce_appframework/gui/components/juce_Component.h"
#include "../../../src/juce_core/basics/juce_Time.h"
#include "../../../src/juce_core/threads/juce_Thread.h"
#include "../../../src/juce_appframework/events/juce_Timer.h"
#include "../../../src/juce_appframework/events/juce_MessageManager.h"
#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h"
#include "../../../src/juce_appframework/gui/components/controls/juce_ListBox.h"
#include "../../../src/juce_core/text/juce_LocalisedStrings.h"
//==============================================================================
@ -120,7 +125,6 @@ static const int maxASIOChannels = 160;
//==============================================================================
class JUCE_API ASIOAudioIODevice : public AudioIODevice,
private Thread,
private Timer
{
public:
@ -128,7 +132,6 @@ public:
ASIOAudioIODevice (const String& name_, const CLSID classId_, const int slotNumber)
: AudioIODevice (name_, T("ASIO")),
Thread ("Juce ASIO"),
asioObject (0),
classId (classId_),
currentBitDepth (16),
@ -496,8 +499,6 @@ public:
{
buffersCreated = true;
jassert (! isThreadRunning());
juce_free (tempBuffer);
tempBuffer = (float*) juce_calloc (totalBuffers * currentBlockSizeSamples * sizeof (float) + 128);
@ -601,7 +602,6 @@ public:
}
isOpen_ = true;
isThreadReady = false;
log ("starting ASIO");
calledback = false;
@ -808,26 +808,6 @@ public:
return done;
}
void run()
{
isThreadReady = true;
for (;;)
{
event1.wait();
if (threadShouldExit())
break;
processBuffer();
}
if (bufferIndex < 0)
{
log ("! ASIO callback never called");
}
}
void resetRequest() throw()
{
needToReset = true;
@ -912,7 +892,7 @@ private:
bool isOpen_, isStarted;
bool volatile isASIOOpen;
bool volatile calledback;
bool volatile littleEndian, postOutput, needToReset, isReSync, isThreadReady;
bool volatile littleEndian, postOutput, needToReset, isReSync;
bool volatile insideControlPanelModalLoop;
bool volatile shouldUsePreferredSize;
@ -1809,46 +1789,65 @@ public:
}
}
const StringArray getDeviceNames (const bool /*preferInputNames*/) const
const StringArray getDeviceNames (const bool /*wantInputNames*/) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
return deviceNames;
}
const String getDefaultDeviceName (const bool /*preferInputNames*/,
const int /*numInputChannelsNeeded*/,
const int /*numOutputChannelsNeeded*/) const
int getDefaultDeviceIndex (const bool) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
return deviceNames [0];
for (int i = deviceNames.size(); --i >= 0;)
if (deviceNames[i].containsIgnoreCase (T("asio4all")))
return i; // asio4all is a safe choice for a default..
#if JUCE_DEBUG
if (deviceNames.size() > 1 && deviceNames[0].containsIgnoreCase (T("digidesign")))
return 1; // (the digi m-box driver crashes the app when you run
// it in the debugger, which can be a bit annoying)
#endif
return 0;
}
AudioIODevice* createDevice (const String& deviceName)
static int findFreeSlot()
{
for (int i = 0; i < numElementsInArray (currentASIODev); ++i)
if (currentASIODev[i] == 0)
return i;
jassertfalse; // unfortunately you can only have a finite number
// of ASIO devices open at the same time..
return -1;
}
int getIndexOfDevice (AudioIODevice* d, const bool /*asInput*/) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
const int index = deviceNames.indexOf (deviceName);
return d == 0 ? -1 : deviceNames.indexOf (d->getName());
}
bool hasSeparateInputsAndOutputs() const { return false; }
AudioIODevice* createDevice (const String& outputDeviceName,
const String& inputDeviceName)
{
jassert (inputDeviceName == outputDeviceName);
(void) inputDeviceName;
jassert (hasScanned); // need to call scanForDevices() before doing this
const int index = deviceNames.indexOf (outputDeviceName);
if (index >= 0)
{
int freeSlot = -1;
for (int i = 0; i < numElementsInArray (currentASIODev); ++i)
{
if (currentASIODev[i] == 0)
{
freeSlot = i;
break;
}
}
jassert (freeSlot >= 0); // unfortunately you can only have a finite number
// of ASIO devices open at the same time..
const int freeSlot = findFreeSlot();
if (freeSlot >= 0)
return new ASIOAudioIODevice (deviceName, *(classIds [index]), freeSlot);
return new ASIOAudioIODevice (outputDeviceName, *(classIds [index]), freeSlot);
}
return 0;
@ -1923,6 +1922,7 @@ private:
return ok;
}
//==============================================================================
void addDriverInfo (const String& keyName, HKEY hk)
{
HKEY subKey;
@ -1974,6 +1974,17 @@ AudioIODeviceType* juce_createASIOAudioIODeviceType()
return new ASIOAudioIODeviceType();
}
AudioIODevice* juce_createASIOAudioIODeviceForGUID (const String& name,
void* guid)
{
const int freeSlot = ASIOAudioIODeviceType::findFreeSlot();
if (freeSlot < 0)
return 0;
return new ASIOAudioIODevice (name, *(CLSID*) guid, freeSlot);
}
END_JUCE_NAMESPACE

View file

@ -141,12 +141,16 @@ DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown)
BEGIN_JUCE_NAMESPACE
#include "../../../src/juce_appframework/audio/devices/juce_AudioIODeviceType.h"
#include "../../../src/juce_appframework/audio/devices/juce_AudioDeviceManager.h"
#include "../../../src/juce_appframework/application/juce_Application.h"
#include "../../../src/juce_appframework/gui/components/windows/juce_AlertWindow.h"
#include "../../../src/juce_core/threads/juce_Thread.h"
#include "../../../src/juce_core/basics/juce_Singleton.h"
#include "../../../src/juce_core/basics/juce_Time.h"
#include "../../../src/juce_core/containers/juce_OwnedArray.h"
#include "../../../src/juce_core/text/juce_LocalisedStrings.h"
#include "../../../src/juce_appframework/gui/components/buttons/juce_TextButton.h"
#include "../../../src/juce_appframework/gui/components/special/juce_AudioDeviceSelectorComponent.h"
static const String getDSErrorMessage (HRESULT hr)
@ -949,7 +953,7 @@ public:
};
//==============================================================================
static int findBestMatchForName (const String& name, const StringArray& names)
/*static int findBestMatchForName (const String& name, const StringArray& names)
{
int i = names.indexOf (name);
@ -978,21 +982,21 @@ static int findBestMatchForName (const String& name, const StringArray& names)
}
return bestResult;
}
}*/
class DSoundAudioIODevice : public AudioIODevice,
public Thread
{
public:
DSoundAudioIODevice (const String& deviceName,
const int index,
const int inputIndex_)
const int outputDeviceIndex_,
const int inputDeviceIndex_)
: AudioIODevice (deviceName, "DirectSound"),
Thread ("Juce DSound"),
isOpen_ (false),
isStarted (false),
deviceIndex (index),
inputIndex (inputIndex_),
outputDeviceIndex (outputDeviceIndex_),
inputDeviceIndex (inputDeviceIndex_),
inChans (4),
outChans (4),
numInputBuffers (0),
@ -1004,6 +1008,17 @@ public:
callback (0),
bufferSizeSamples (0)
{
if (outputDeviceIndex_ >= 0)
{
outChannels.add (TRANS("Left"));
outChannels.add (TRANS("Right"));
}
if (inputDeviceIndex_ >= 0)
{
inChannels.add (TRANS("Left"));
inChannels.add (TRANS("Right"));
}
}
~DSoundAudioIODevice()
@ -1176,13 +1191,13 @@ public:
juce_UseDebuggingNewOperator
StringArray inChannels, outChannels;
int outputDeviceIndex, inputDeviceIndex;
private:
bool isOpen_;
bool isStarted;
String lastError;
int deviceIndex, inputIndex;
OwnedArray <DSoundInternalInChannel> inChans;
OwnedArray <DSoundInternalOutChannel> outChans;
WaitableEvent startEvent;
@ -1381,6 +1396,7 @@ public:
}
};
//==============================================================================
class DSoundAudioIODeviceType : public AudioIODeviceType
{
@ -1413,72 +1429,46 @@ public:
}
}
const StringArray getDeviceNames (const bool preferInputNames) const
const StringArray getDeviceNames (const bool wantInputNames) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
return preferInputNames ? inputDeviceNames
: outputDeviceNames;
return wantInputNames ? inputDeviceNames
: outputDeviceNames;
}
const String getDefaultDeviceName (const bool preferInputNames,
const int /*numInputChannelsNeeded*/,
const int /*numOutputChannelsNeeded*/) const
int getDefaultDeviceIndex (const bool /*forInput*/) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
return getDeviceNames (preferInputNames) [0];
return 0;
}
AudioIODevice* createDevice (const String& deviceName)
int getIndexOfDevice (AudioIODevice* device, const bool asInput) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
if (deviceName.isEmpty() || deviceName.equalsIgnoreCase (T("DirectSound")))
{
DSoundAudioIODevice* device = new DSoundAudioIODevice (deviceName, -1, -1);
DSoundAudioIODevice* const d = dynamic_cast <DSoundAudioIODevice*> (device);
if (d == 0)
return -1;
int i;
for (i = 0; i < outputDeviceNames.size(); ++i)
{
device->outChannels.add (outputDeviceNames[i] + TRANS(" (left)"));
device->outChannels.add (outputDeviceNames[i] + TRANS(" (right)"));
}
return asInput ? d->inputDeviceIndex
: d->outputDeviceIndex;
}
for (i = 0; i < inputDeviceNames.size(); ++i)
{
device->inChannels.add (inputDeviceNames[i] + TRANS(" (left)"));
device->inChannels.add (inputDeviceNames[i] + TRANS(" (right)"));
}
bool hasSeparateInputsAndOutputs() const { return true; }
return device;
}
else if (outputDeviceNames.contains (deviceName)
|| inputDeviceNames.contains (deviceName))
{
int outputIndex = outputDeviceNames.indexOf (deviceName);
int inputIndex = findBestMatchForName (deviceName, inputDeviceNames);
AudioIODevice* createDevice (const String& outputDeviceName,
const String& inputDeviceName)
{
jassert (hasScanned); // need to call scanForDevices() before doing this
if (outputIndex < 0)
{
// using an input device name instead..
inputIndex = inputDeviceNames.indexOf (deviceName);
outputIndex = jmax (0, findBestMatchForName (deviceName, outputDeviceNames));
}
const int outputIndex = outputDeviceNames.indexOf (outputDeviceName);
const int inputIndex = inputDeviceNames.indexOf (inputDeviceName);
DSoundAudioIODevice* device = new DSoundAudioIODevice (deviceName, outputIndex, inputIndex);
device->outChannels.add (TRANS("Left"));
device->outChannels.add (TRANS("Right"));
if (inputIndex >= 0)
{
device->inChannels.add (TRANS("Left"));
device->inChannels.add (TRANS("Right"));
}
return device;
}
if (outputIndex >= 0 || inputIndex >= 0)
return new DSoundAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
: inputDeviceName,
outputIndex, inputIndex);
return 0;
}
@ -1619,8 +1609,8 @@ const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels,
right = inputBuffers[numIns++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
if (left != 0 || right != 0)
inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [i / 2],
dlh.inputGuids [i / 2],
inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex],
dlh.inputGuids [inputDeviceIndex],
(int) sampleRate, bufferSizeSamples,
left, right));
}
@ -1647,8 +1637,8 @@ const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels,
right = outputBuffers[numOuts++] = (float*) juce_calloc ((bufferSizeSamples + 16) * sizeof (float));
if (left != 0 || right != 0)
outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[i / 2],
dlh.outputGuids [i / 2],
outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex],
dlh.outputGuids [outputDeviceIndex],
(int) sampleRate, bufferSizeSamples,
left, right));
}
@ -1714,6 +1704,7 @@ const String DSoundAudioIODevice::openDevice (const BitArray& inputChannels,
return error;
}
#undef log
END_JUCE_NAMESPACE

View file

@ -154,7 +154,7 @@ void AudioFilterStreamingDeviceManager::setFilter (AudioProcessor* filterToStrea
{
if (streamer != 0)
{
removeMidiInputCallback (streamer);
removeMidiInputCallback (String::empty, streamer);
setAudioCallback (0);
delete streamer;

View file

@ -479,7 +479,7 @@ public:
~AudioDemo()
{
audioDeviceManager.removeMidiInputCallback (&synthSource.midiCollector);
audioDeviceManager.removeMidiInputCallback (String::empty, &synthSource.midiCollector);
audioDeviceManager.setAudioCallback (0);
transportSource.removeChangeListener (this);

File diff suppressed because it is too large Load diff

View file

@ -196,7 +196,7 @@
/** Enabling this builds support for VST audio plugins.
@see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU
*/
#define JUCE_PLUGINHOST_VST 1
//#define JUCE_PLUGINHOST_VST 1
/** Enabling this builds support for AudioUnit audio plugins.
@see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST
@ -4138,6 +4138,8 @@ public:
@param minutes minutes 0 to 59
@param seconds seconds 0 to 59
@param milliseconds milliseconds 0 to 999
@param useLocalTime if true, encode using the current machine's local time; if
false, it will always work in GMT.
*/
Time (const int year,
const int month,
@ -4145,7 +4147,8 @@ public:
const int hours,
const int minutes,
const int seconds = 0,
const int milliseconds = 0) throw();
const int milliseconds = 0,
const bool useLocalTime = true) throw();
/** Destructor. */
~Time() throw();
@ -12994,6 +12997,11 @@ public:
*/
static void JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) throw();
/** WIN32 ONLY - Gets the command-line params as a string.
This is needed to avoid unicode problems with the argc type params.
*/
static const String JUCE_CALLTYPE getCurrentCommandLineParams() throw();
#endif
/** Clears the floating point unit's flags.
@ -20315,6 +20323,13 @@ public:
/** Tries to give the window keyboard focus. */
virtual void grabFocus() = 0;
/** Tells the window that text input may be required at the given position.
This may cause things like a virtual on-screen keyboard to appear, depending
on the OS.
*/
virtual void textInputRequired (int x, int y) = 0;
/** Called when the window gains keyboard focus. */
void handleFocusGain();
/** Called when the window loses keyboard focus. */
@ -31673,6 +31688,9 @@ private:
#ifndef __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__
#define __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__
class AudioDeviceManager;
class Component;
/**
Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc.
@ -31709,19 +31727,6 @@ class JUCE_API AudioIODeviceType
{
public:
/** 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.
The objects that are created should be managed by the caller (the OwnedArray
will delete them when the array is itself deleted).
When created, the objects are uninitialised, so you should call scanForDevices()
on each one before getting its list of devices.
*/
static void createDeviceTypes (OwnedArray <AudioIODeviceType>& list);
/** Returns the name of this type of driver that this object manages.
This will be something like "DirectSound", "ASIO", "CoreAudio", "ALSA", etc.
@ -31739,34 +31744,46 @@ public:
The scanForDevices() method must have been called to create this list.
@param preferInputNames 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.
@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 const StringArray getDeviceNames (const bool preferInputNames = false) const = 0;
virtual const StringArray getDeviceNames (const bool wantInputNames = false) const = 0;
/** Returns the name of the default device.
This will be one of the names from the getDeviceNames() list.
@param preferInputNames 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.
@param numInputChannelsNeeded the number of input channels the user is expecting to need - this
may be used to help decide which device would be most suitable
@param numOutputChannelsNeeded the number of output channels the user is expecting to need - this
may be used to help decide which device would be most suitable
@param forInput if true, this means that a default input device should be
returned; if false, it should return the default output
*/
virtual const String getDefaultDeviceName (const bool preferInputNames,
const int numInputChannelsNeeded,
const int numOutputChannelsNeeded) const = 0;
virtual int getDefaultDeviceIndex (const 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, const 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& deviceName) = 0;
virtual AudioIODevice* createDevice (const String& outputDeviceName,
const String& inputDeviceName) = 0;
struct DeviceSetupDetails
{
AudioDeviceManager* manager;
int minNumInputChannels, maxNumInputChannels;
int minNumOutputChannels, maxNumOutputChannels;
bool useStereoPairs;
};
/** Destructor. */
virtual ~AudioIODeviceType();
@ -33561,52 +33578,41 @@ public:
*/
XmlElement* createStateXml() const;
/** Returns a list of the audio devices that can be used.
On Windows, this will include both DSound and ASIO devices if they are available. On
the Mac, it'll be a list of the CoreAudio devices.
These names are used by setAudioDevice() when changing devices.
/**
*/
const StringArray getAvailableAudioDeviceNames() const;
struct AudioDeviceSetup
{
AudioDeviceSetup();
/** Rescans the list of known audio devices, in case it's changed. */
void refreshDeviceList() const;
bool operator== (const AudioDeviceSetup& other) const;
/** Sets a flag to indicate that when listing audio device names, it should treat them
as inputs rather than outputs.
/**
*/
String outputDeviceName;
/**
*/
String inputDeviceName;
/**
*/
double sampleRate;
/**
*/
int bufferSize;
/**
*/
BitArray inputChannels;
bool useDefaultInputChannels;
/**
*/
BitArray outputChannels;
bool useDefaultOutputChannels;
};
This only really applies to DirectSound, where input and output devices are
separate. On ASIO and CoreAudio this makes no difference.
@see getAvailableAudioDeviceNames
/**
*/
void setInputDeviceNamesUsed (const bool useInputNames);
void getAudioDeviceSetup (AudioDeviceSetup& setup);
/** Just adds the list of device names to a combo box.
The only reason this is in this class is so that it can divide DSound
and ASIO devices into labelled sections, which makes it look much neater.
*/
void addDeviceNamesToComboBox (ComboBox& combo) const;
/** Changes the audio device that should be used.
If deviceName is empty or not a valid name returned by getAvailableAudioDeviceNames(),
it will disable the current device.
@param deviceName the name of the device you want to use (or an empty string to
deselect the current device)
@param blockSizeToUse the samples-per-block you want to use, or 0 to use a default
value
@param sampleRateToUse the target sample-rate, or 0 to use a default that the device
is capable of
@param inputChans which of the audio device's input channels to open - pass 0 to
open as many of the the first ones as are needed for the number
of input channels that the app has requested
@param outputChans which of the audio device's input channels to open - pass 0 to
open as many of the the first ones as are needed for the number
of output channels that the app has requested
/**
@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
@ -33614,15 +33620,17 @@ public:
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.
*/
const String setAudioDevice (const String& deviceName,
int blockSizeToUse,
double sampleRateToUse,
const BitArray* inputChans,
const BitArray* outputChans,
const bool treatAsChosenDevice);
const String setAudioDeviceSetup (const AudioDeviceSetup& newSetup,
const bool treatAsChosenDevice);
/** Returns the currently-active audio device. */
AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; }
const String getCurrentAudioDeviceType() const throw() { return currentDeviceType; }
void setCurrentAudioDeviceType (const String& type,
const bool treatAsChosenDevice);
/** Closes the currently-open device.
@ -33641,59 +33649,6 @@ public:
*/
void restartLastAudioDevice();
/** Returns the name of the currently selected audio device.
This will be an empty string if none is active.
*/
const String getCurrentAudioDeviceName() const;
/** Returns the currently-active audio device. */
AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; }
/** Returns the set of audio input channels currently being used.
To select different channels, use setInputChannels(), or call setAudioDevice() to
reopen the device with a different set of channels.
*/
const BitArray getInputChannels() const throw() { return inputChannels; }
/** Changes the active set of input channels.
@param newEnabledChannels the set of channels to enable
@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().
@see getInputChannels
*/
void setInputChannels (const BitArray& newEnabledChannels,
const bool treatAsChosenDevice);
/** Returns the set of audio output channels currently being used.
To select different channels, use setOutputChannels(), or call setAudioDevice() to
reopen the device with a different set of channels.
*/
const BitArray getOutputChannels() const throw() { return outputChannels; }
/** Changes the active set of output channels.
@param newEnabledChannels the set of channels to enable
@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().
@see getOutputChannels
*/
void setOutputChannels (const BitArray& newEnabledChannels,
const bool treatAsChosenDevice);
/** Gives the manager an audio callback to use.
The manager will redirect callbacks from whatever audio device is currently
@ -33751,7 +33706,8 @@ public:
/** Removes a listener that was previously registered with addMidiInputCallback().
*/
void removeMidiInputCallback (MidiInputCallback* callback);
void removeMidiInputCallback (const String& midiInputDeviceName,
MidiInputCallback* callback);
/** Sets a midi output device to use as the default.
@ -33782,19 +33738,69 @@ public:
*/
MidiOutput* getDefaultMidiOutput() const throw() { return defaultMidiOutput; }
/**
*/
const OwnedArray <AudioIODeviceType>& getAvailableDeviceTypes() const throw() { return availableDeviceTypes; }
/** 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);
/** 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 (const 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;
juce_UseDebuggingNewOperator
private:
OwnedArray <AudioIODeviceType> availableDeviceTypes;
OwnedArray <AudioDeviceSetup> lastDeviceTypeConfigs;
AudioDeviceSetup currentSetup;
AudioIODevice* currentAudioDevice;
AudioIODeviceCallback* currentCallback;
int numInputChansNeeded, numOutputChansNeeded;
String currentDeviceType;
BitArray inputChannels, outputChannels;
XmlElement* lastExplicitSettings;
mutable bool listNeedsScanning;
bool useInputNames;
bool useInputNames, inputLevelMeasurementEnabled;
double inputLevel;
AudioSampleBuffer* testSound;
int testSoundPosition;
StringArray midiInsFromXml;
OwnedArray <MidiInput> enabledMidiInputs;
@ -33826,11 +33832,6 @@ private:
};
CallbackHandler callbackHandler;
String lastRunningDevice;
int lastRunningBlockSize;
double lastRunningSampleRate;
BitArray lastRunningIns, lastRunningOuts;
friend class CallbackHandler;
void audioDeviceIOCallbackInt (const float** inputChannelData,
@ -33849,6 +33850,15 @@ private:
void updateXml();
void createDeviceTypesIfNeeded();
void scanDevicesIfNeeded();
void deleteCurrentDevice();
double chooseBestSampleRate (double preferred) const;
AudioIODeviceType* getCurrentDeviceTypeObject() const;
void insertDefaultDeviceNames (AudioDeviceSetup& setup) const;
AudioIODeviceType* findType (const String& inputName, const String& outputName);
AudioDeviceManager (const AudioDeviceManager&);
const AudioDeviceManager& operator= (const AudioDeviceManager&);
};
@ -49093,8 +49103,8 @@ private:
@see DirectoryContentsList, FileTreeComponent
*/
class JUCE_API FileListComponent : public DirectoryContentsDisplayComponent,
public ListBox,
class JUCE_API FileListComponent : public ListBox,
public DirectoryContentsDisplayComponent,
private ListBoxModel,
private ChangeListener
{
@ -49454,8 +49464,8 @@ private:
@see DirectoryContentsList, FileListComponent
*/
class JUCE_API FileTreeComponent : public DirectoryContentsDisplayComponent,
public TreeView
class JUCE_API FileTreeComponent : public TreeView,
public DirectoryContentsDisplayComponent
{
public:
@ -50416,7 +50426,7 @@ private:
#ifndef __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__
#define __JUCE_AUDIODEVICESELECTORCOMPONENT_JUCEHEADER__
class AudioDeviceSelectorComponentListBox;
class MidiInputSelectorComponentListBox;
/**
A component containing controls to let the user change the audio settings of
@ -50467,23 +50477,22 @@ public:
/** @internal */
void changeListenerCallback (void*);
/** Called by the device-specific displays to create a little level meter that
just displays the current total input levels from the given device manager.
*/
static Component* createSimpleLevelMeterComponent (AudioDeviceManager* managerToDisplay);
juce_UseDebuggingNewOperator
private:
AudioDeviceManager& deviceManager;
ComboBox* audioDeviceDropDown;
ComboBox* deviceTypeDropDown;
Label* deviceTypeDropDownLabel;
Component* audioDeviceSettingsComp;
String audioDeviceSettingsCompType;
const int minOutputChannels, maxOutputChannels, minInputChannels, maxInputChannels;
ComboBox* sampleRateDropDown;
AudioDeviceSelectorComponentListBox* inputChansBox;
Label* inputsLabel;
AudioDeviceSelectorComponentListBox* outputChansBox;
Label* outputsLabel;
Label* sampleRateLabel;
ComboBox* bufferSizeDropDown;
Label* bufferSizeLabel;
Button* launchUIButton;
AudioDeviceSelectorComponentListBox* midiInputsList;
MidiInputSelectorComponentListBox* midiInputsList;
Label* midiInputsLabel;
ComboBox* midiOutputSelector;
Label* midiOutputLabel;
@ -51562,7 +51571,7 @@ public:
@see newOpenGLContextCreated()
*/
OpenGLContext* getCurrentContext() const throw();
OpenGLContext* getCurrentContext() const throw() { return context; }
/** Makes this component the current openGL context.
@ -53340,23 +53349,24 @@ END_JUCE_NAMESPACE
#ifdef _CONSOLE
#define START_JUCE_APPLICATION(AppClass) \
int main (int argc, char* argv[]) \
int main (int, char* argv[]) \
{ \
return JUCE_NAMESPACE::JUCEApplication::main (argc, argv, new AppClass()); \
JUCE_NAMESPACE::String commandLineString (JUCE_NAMESPACE::PlatformUtilities::getCurrentCommandLineParams()); \
return JUCE_NAMESPACE::JUCEApplication::main (commandLineString, new AppClass()); \
}
#elif ! defined (_AFXDLL)
#ifdef _WINDOWS_
#define START_JUCE_APPLICATION(AppClass) \
int WINAPI WinMain (HINSTANCE, HINSTANCE, LPSTR commandLine, int) \
int WINAPI WinMain (HINSTANCE, HINSTANCE, LPSTR, int) \
{ \
JUCE_NAMESPACE::String commandLineString (commandLine); \
JUCE_NAMESPACE::String commandLineString (JUCE_NAMESPACE::PlatformUtilities::getCurrentCommandLineParams()); \
return JUCE_NAMESPACE::JUCEApplication::main (commandLineString, new AppClass()); \
}
#else
#define START_JUCE_APPLICATION(AppClass) \
int __stdcall WinMain (int, int, const char* commandLine, int) \
int __stdcall WinMain (int, int, const char*, int) \
{ \
JUCE_NAMESPACE::String commandLineString (commandLine); \
JUCE_NAMESPACE::String commandLineString (JUCE_NAMESPACE::PlatformUtilities::getCurrentCommandLineParams()); \
return JUCE_NAMESPACE::JUCEApplication::main (commandLineString, new AppClass()); \
}
#endif

View file

@ -36,8 +36,30 @@ BEGIN_JUCE_NAMESPACE
#include "juce_AudioDeviceManager.h"
#include "../../gui/components/juce_Desktop.h"
#include "../../../juce_core/text/juce_LocalisedStrings.h"
#include "../dsp/juce_AudioSampleBuffer.h"
//==============================================================================
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;
}
//==============================================================================
AudioDeviceManager::AudioDeviceManager()
: currentAudioDevice (0),
@ -47,6 +69,9 @@ AudioDeviceManager::AudioDeviceManager()
lastExplicitSettings (0),
listNeedsScanning (true),
useInputNames (false),
inputLevelMeasurementEnabled (false),
inputLevel (0),
testSound (0),
enabledMidiInputs (4),
midiCallbacks (4),
midiCallbackDevices (4),
@ -55,19 +80,59 @@ AudioDeviceManager::AudioDeviceManager()
timeToCpuScale (0)
{
callbackHandler.owner = this;
AudioIODeviceType::createDeviceTypes (availableDeviceTypes);
}
AudioDeviceManager::~AudioDeviceManager()
{
stopDevice();
deleteAndZero (currentAudioDevice);
deleteAndZero (defaultMidiOutput);
delete lastExplicitSettings;
delete testSound;
}
//==============================================================================
void AudioDeviceManager::createDeviceTypesIfNeeded()
{
if (availableDeviceTypes.size() == 0)
{
createAudioDeviceTypes (availableDeviceTypes);
while (lastDeviceTypeConfigs.size() < availableDeviceTypes.size())
lastDeviceTypeConfigs.add (new AudioDeviceSetup());
if (availableDeviceTypes.size() > 0)
currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName();
}
}
//==============================================================================
extern AudioIODeviceType* juce_createDefaultAudioIODeviceType();
#if JUCE_WIN32 && JUCE_ASIO
extern AudioIODeviceType* juce_createASIOAudioIODeviceType();
#endif
#if JUCE_WIN32 && JUCE_WDM_AUDIO
extern AudioIODeviceType* juce_createWDMAudioIODeviceType();
#endif
void AudioDeviceManager::createAudioDeviceTypes (OwnedArray <AudioIODeviceType>& list)
{
AudioIODeviceType* const defaultDeviceType = juce_createDefaultAudioIODeviceType();
if (defaultDeviceType != 0)
list.add (defaultDeviceType);
#if JUCE_WIN32 && JUCE_ASIO
list.add (juce_createASIOAudioIODeviceType());
#endif
#if JUCE_WIN32 && JUCE_WDM_AUDIO
list.add (juce_createWDMAudioIODeviceType());
#endif
}
//==============================================================================
const String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
const int numOutputChannelsNeeded,
@ -75,26 +140,51 @@ const String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
const bool selectDefaultDeviceOnFailure,
const String& preferredDefaultDeviceName)
{
if (listNeedsScanning)
refreshDeviceList();
scanDevicesIfNeeded();
numInputChansNeeded = numInputChannelsNeeded;
numOutputChansNeeded = numOutputChannelsNeeded;
if (e != 0 && e->hasTagName (T("DEVICESETUP")))
{
delete lastExplicitSettings;
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;
AudioDeviceSetup setup;
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));
if (e->getStringAttribute (T("audioDeviceName")).isNotEmpty())
{
setup.inputDeviceName = setup.outputDeviceName
= e->getStringAttribute (T("audioDeviceName"));
}
else
{
setup.inputDeviceName = e->getStringAttribute (T("audioInputDeviceName"));
setup.outputDeviceName = e->getStringAttribute (T("audioOutputDeviceName"));
}
currentDeviceType = e->getStringAttribute (T("deviceType"));
if (currentDeviceType.isEmpty())
{
AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName);
if (type != 0)
currentDeviceType = type->getTypeName();
else if (availableDeviceTypes.size() > 0)
currentDeviceType = availableDeviceTypes[0]->getTypeName();
}
setup.bufferSize = e->getIntAttribute (T("audioDeviceBufferSize"));
setup.sampleRate = e->getDoubleAttribute (T("audioDeviceRate"));
setup.inputChannels.parseString (e->getStringAttribute (T("audioDeviceInChans"), T("11")), 2);
setup.outputChannels.parseString (e->getStringAttribute (T("audioDeviceOutChans"), T("11")), 2);
setup.useDefaultInputChannels = ! e->hasAttribute (T("audioDeviceInChans"));
setup.useDefaultOutputChannels = ! e->hasAttribute (T("audioDeviceOutChans"));
error = setAudioDeviceSetup (setup, true);
midiInsFromXml.clear();
forEachXmlChildElementWithTagName (*e, c, T("MIDIINPUT"))
@ -115,33 +205,54 @@ const String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
}
else
{
setInputDeviceNamesUsed (numOutputChannelsNeeded == 0);
String defaultDevice;
AudioDeviceSetup setup;
if (preferredDefaultDeviceName.isNotEmpty())
{
for (int i = 0; i < availableDeviceTypes.size(); ++i)
for (int j = availableDeviceTypes.size(); --j >= 0;)
{
const StringArray devs (availableDeviceTypes.getUnchecked(i)->getDeviceNames());
AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j);
for (int j = 0; j < devs.size(); ++j)
StringArray outs (type->getDeviceNames (false));
int i;
for (i = 0; i < outs.size(); ++i)
{
if (devs[j].matchesWildcard (preferredDefaultDeviceName, true))
if (outs[i].matchesWildcard (preferredDefaultDeviceName, true))
{
defaultDevice = devs[j];
setup.outputDeviceName = outs[i];
break;
}
}
StringArray ins (type->getDeviceNames (true));
for (i = 0; i < ins.size(); ++i)
{
if (ins[i].matchesWildcard (preferredDefaultDeviceName, true))
{
setup.inputDeviceName = ins[i];
break;
}
}
}
}
if (defaultDevice.isEmpty() && availableDeviceTypes [0] != 0)
defaultDevice = availableDeviceTypes[0]->getDefaultDeviceName (numOutputChannelsNeeded == 0,
numInputChannelsNeeded,
numOutputChannelsNeeded);
insertDefaultDeviceNames (setup);
return setAudioDeviceSetup (setup, false);
}
}
return setAudioDevice (defaultDevice, 0, 0, 0, 0, false);
void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const
{
AudioIODeviceType* type = getCurrentDeviceTypeObject();
if (type != 0)
{
if (setup.outputDeviceName.isEmpty())
setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)];
if (setup.inputDeviceName.isEmpty())
setup.inputDeviceName = type->getDeviceNames (true) [type->getDefaultDeviceIndex (true)];
}
}
@ -151,144 +262,372 @@ XmlElement* AudioDeviceManager::createStateXml() const
}
//==============================================================================
const StringArray AudioDeviceManager::getAvailableAudioDeviceNames() const
void AudioDeviceManager::scanDevicesIfNeeded()
{
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];
listNeedsScanning = false;
if (availableDeviceTypes.size() > 1)
combo.addSectionHeading (type->getTypeName() + T(" devices:"));
createDeviceTypesIfNeeded();
const StringArray names (type->getDeviceNames (useInputNames));
for (int i = availableDeviceTypes.size(); --i >= 0;)
availableDeviceTypes.getUnchecked(i)->scanForDevices();
}
}
for (int j = 0; j < names.size(); ++j)
combo.addItem (names[j], ++n);
AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const String& outputName)
{
scanDevicesIfNeeded();
combo.addSeparator();
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;
}
}
combo.addItem (TRANS("<< no audio device >>"), -1);
return 0;
}
const String AudioDeviceManager::getCurrentAudioDeviceName() const
void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup)
{
if (currentAudioDevice != 0)
return currentAudioDevice->getName();
return String::empty;
setup = currentSetup;
}
const String AudioDeviceManager::setAudioDevice (const String& deviceNameToUse,
int blockSizeToUse,
double sampleRateToUse,
const BitArray* inChans,
const BitArray* outChans,
const bool treatAsChosenDevice)
void AudioDeviceManager::deleteCurrentDevice()
{
deleteAndZero (currentAudioDevice);
currentSetup.inputDeviceName = String::empty;
currentSetup.outputDeviceName = String::empty;
}
void AudioDeviceManager::setCurrentAudioDeviceType (const String& type,
const bool treatAsChosenDevice)
{
for (int i = 0; i < availableDeviceTypes.size(); ++i)
{
if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type
&& currentDeviceType != type)
{
currentDeviceType = type;
AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i));
insertDefaultDeviceNames (s);
setAudioDeviceSetup (s, treatAsChosenDevice);
sendChangeMessage (this);
break;
}
}
}
AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const
{
for (int i = 0; i < availableDeviceTypes.size(); ++i)
if (availableDeviceTypes[i]->getTypeName() == currentDeviceType)
return availableDeviceTypes[i];
return availableDeviceTypes[0];
}
const String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup,
const bool treatAsChosenDevice)
{
jassert (&newSetup != &currentSetup); // this will have no effect
if (newSetup == currentSetup && currentAudioDevice != 0)
return String::empty;
if (! (newSetup == currentSetup))
sendChangeMessage (this);
stopDevice();
String error;
AudioIODeviceType* type = getCurrentDeviceTypeObject();
if (type == 0 || (newSetup.inputDeviceName.isEmpty()
&& newSetup.outputDeviceName.isEmpty()))
{
deleteCurrentDevice();
if (treatAsChosenDevice)
updateXml();
return String::empty;
}
if (currentSetup.inputDeviceName != newSetup.inputDeviceName
|| currentSetup.outputDeviceName != newSetup.outputDeviceName
|| currentAudioDevice == 0)
{
deleteCurrentDevice();
scanDevicesIfNeeded();
if (newSetup.outputDeviceName.isNotEmpty()
&& ! type->getDeviceNames (false).contains (newSetup.outputDeviceName))
{
return "No such device: " + newSetup.outputDeviceName;
}
if (newSetup.inputDeviceName.isNotEmpty()
&& ! type->getDeviceNames (true).contains (newSetup.inputDeviceName))
{
return "No such device: " + newSetup.outputDeviceName;
}
currentAudioDevice = type->createDevice (newSetup.outputDeviceName,
newSetup.inputDeviceName);
if (currentAudioDevice == 0)
error = "Can't open device";
else
error = currentAudioDevice->getLastError();
if (error.isNotEmpty())
{
deleteCurrentDevice();
return error;
}
inputChannels.clear();
inputChannels.setRange (0, numInputChansNeeded, true);
outputChannels.clear();
outputChannels.setRange (0, numOutputChansNeeded, true);
}
else
{
if (! newSetup.useDefaultInputChannels)
inputChannels = newSetup.inputChannels;
if (! newSetup.useDefaultOutputChannels)
outputChannels = newSetup.outputChannels;
}
currentSetup = newSetup;
currentSetup.sampleRate = chooseBestSampleRate (newSetup.sampleRate);
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 != 0);
if (rate > 0)
{
bool ok = false;
for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;)
{
const double sr = currentAudioDevice->getSampleRate (i);
if (sr == rate)
ok = true;
}
if (! ok)
rate = 0;
}
if (rate == 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)
rate = currentAudioDevice->getSampleRate (0);
else
rate = lowestAbove44;
}
return rate;
}
/*const String AudioDeviceManager::setAudioDevices (const String& outputDeviceName,
const String& inputDeviceName,
int blockSizeToUse,
double sampleRateToUse,
const BitArray* inChans,
const BitArray* outChans,
const bool treatAsChosenDevice)
{
stopDevice();
String error;
if (deviceNameToUse.isNotEmpty())
if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty())
{
const StringArray devNames (getAvailableAudioDeviceNames());
const StringArray outputNames (getAvailableAudioOutputDeviceNames());
int outputIndex = outputNames.indexOf (outputDeviceName);
int index = devNames.indexOf (deviceNameToUse, true);
if (index >= 0)
if (outputIndex < 0 && outputDeviceName.isNotEmpty())
{
if (currentAudioDevice == 0
|| currentAudioDevice->getLastError().isNotEmpty()
|| ! deviceNameToUse.equalsIgnoreCase (currentAudioDevice->getName()))
deleteDevices();
error << "No such device: " << outputDeviceName;
return error;
}
const StringArray inputNames (getAvailableAudioInputDeviceNames());
int inputIndex = inputNames.indexOf (inputDeviceName);
if (inputIndex < 0 && inputDeviceName.isNotEmpty())
{
deleteDevices();
error << "No such device: " << inputDeviceName;
return error;
}
if (currentAudioInputDevice == 0
|| currentAudioInputDevice->getLastError().isNotEmpty()
|| ! inputDeviceName.equalsIgnoreCase (currentAudioInputDevice->getName())
|| currentAudioOutputDevice == 0
|| currentAudioOutputDevice->getLastError().isNotEmpty()
|| ! outputDeviceName.equalsIgnoreCase (currentAudioOutputDevice->getName()))
{
// change of device..
deleteDevices();
int n = 0;
for (int i = 0; i < availableDeviceTypes.size(); ++i)
{
// change of device..
deleteAndZero (currentAudioDevice);
AudioIODeviceType* const type = availableDeviceTypes[i];
const StringArray names (type->getDeviceNames (true));
int n = 0;
if (inputIndex >= n && inputIndex < n + names.size())
{
currentAudioInputDevice = type->createDevice (inputDeviceName);
if (currentAudioInputDevice == 0)
error = "Can't open device: " + inputDeviceName;
else
error = currentAudioInputDevice->getLastError();
if (error.isNotEmpty())
{
deleteDevices();
return error;
}
break;
}
n += names.size();
}
//xxx
if (true)//inputDeviceName != outputDeviceName)
{
// Using different input and output devices..
n = 0;
for (int i = 0; i < availableDeviceTypes.size(); ++i)
{
AudioIODeviceType* const type = availableDeviceTypes[i];
const StringArray names (type->getDeviceNames (useInputNames));
const StringArray names (type->getDeviceNames (false));
if (index >= n && index < n + names.size())
if (outputIndex >= n && outputIndex < n + names.size())
{
currentAudioDevice = type->createDevice (deviceNameToUse);
currentAudioOutputDevice = type->createDevice (outputDeviceName);
if (currentAudioOutputDevice == 0)
error = "Can't open device: " + outputDeviceName;
else
error = currentAudioOutputDevice->getLastError();
if (error.isNotEmpty())
{
deleteDevices();
return error;
}
break;
}
n += names.size();
}
error = currentAudioDevice->getLastError();
if (error.isNotEmpty())
if (currentAudioInputDevice != 0 && currentAudioOutputDevice != 0)
{
deleteAndZero (currentAudioDevice);
return error;
//xxx enable this optim
}
inputChannels.clear();
inputChannels.setRange (0, numInputChansNeeded, true);
outputChannels.clear();
outputChannels.setRange (0, numOutputChansNeeded, true);
ConglomeratingAudioIODevice* const combiner = new ConglomeratingAudioIODevice();
combiner->addDevice (currentAudioInputDevice, false, true);
combiner->addDevice (currentAudioOutputDevice, true, false);
currentAudioDevice = combiner;
}
if (inChans != 0)
inputChannels = *inChans;
if (outChans != 0)
outputChannels = *outChans;
error = restartDevice (blockSizeToUse,
sampleRateToUse,
inputChannels,
outputChannels);
if (error.isNotEmpty())
else
{
deleteAndZero (currentAudioDevice);
// Using a single device for in + out..
currentAudioOutputDevice = currentAudioInputDevice;
currentAudioDevice = currentAudioInputDevice;
}
inputChannels.clear();
inputChannels.setRange (0, numInputChansNeeded, true);
outputChannels.clear();
outputChannels.setRange (0, numOutputChansNeeded, true);
}
else
{
deleteAndZero (currentAudioDevice);
error << "No such device: " << deviceNameToUse;
}
if (inChans != 0)
inputChannels = *inChans;
if (outChans != 0)
outputChannels = *outChans;
error = restartDevice (blockSizeToUse,
sampleRateToUse,
inputChannels,
outputChannels);
if (error.isNotEmpty())
deleteDevices();
}
else
{
deleteAndZero (currentAudioDevice);
deleteDevices();
}
if (treatAsChosenDevice && error.isEmpty())
@ -342,7 +681,7 @@ const String AudioDeviceManager::restartDevice (int blockSizeToUse,
}
const String error (currentAudioDevice->open (inChans, outChans,
sampleRateToUse, blockSizeToUse));
sampleRateToUse, blockSizeToUse));
if (error.isEmpty())
currentAudioDevice->start (&callbackHandler);
@ -350,7 +689,7 @@ const String AudioDeviceManager::restartDevice (int blockSizeToUse,
sendChangeMessage (this);
return error;
}
*/
void AudioDeviceManager::stopDevice()
{
if (currentAudioDevice != 0)
@ -359,25 +698,16 @@ void AudioDeviceManager::stopDevice()
void AudioDeviceManager::closeAudioDevice()
{
if (currentAudioDevice != 0)
{
lastRunningDevice = currentAudioDevice->getName();
lastRunningBlockSize = currentAudioDevice->getCurrentBufferSizeSamples();
lastRunningSampleRate = currentAudioDevice->getCurrentSampleRate();
lastRunningIns = inputChannels;
lastRunningOuts = outputChannels;
stopDevice();
setAudioDevice (String::empty, 0, 0, 0, 0, false);
}
stopDevice();
deleteAndZero (currentAudioDevice);
}
void AudioDeviceManager::restartLastAudioDevice()
{
if (currentAudioDevice == 0)
{
if (lastRunningDevice.isEmpty())
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
@ -386,50 +716,19 @@ void AudioDeviceManager::restartLastAudioDevice()
return;
}
setAudioDevice (lastRunningDevice,
lastRunningBlockSize,
lastRunningSampleRate,
&lastRunningIns,
&lastRunningOuts,
false);
}
}
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 != outputChannels)
{
setAudioDevice (currentAudioDevice->getName(),
currentAudioDevice->getCurrentBufferSizeSamples(),
currentAudioDevice->getCurrentSampleRate(),
0, &newEnabledChannels,
treatAsChosenDevice);
AudioDeviceSetup s (currentSetup);
setAudioDeviceSetup (s, false);
}
}
void AudioDeviceManager::updateXml()
{
delete lastExplicitSettings;
lastExplicitSettings = new XmlElement (T("DEVICESETUP"));
lastExplicitSettings->setAttribute (T("audioDeviceName"), getCurrentAudioDeviceName());
lastExplicitSettings->setAttribute (T("deviceType"), currentDeviceType);
lastExplicitSettings->setAttribute (T("audioOutputDeviceName"), currentSetup.outputDeviceName);
lastExplicitSettings->setAttribute (T("audioInputDeviceName"), currentSetup.inputDeviceName);
if (currentAudioDevice != 0)
{
@ -438,8 +737,11 @@ void AudioDeviceManager::updateXml()
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));
if (! currentSetup.useDefaultInputChannels)
lastExplicitSettings->setAttribute (T("audioDeviceInChans"), currentSetup.inputChannels.toString (2));
if (! currentSetup.useDefaultOutputChannels)
lastExplicitSettings->setAttribute (T("audioDeviceOutChans"), currentSetup.outputChannels.toString (2));
}
for (int i = 0; i < enabledMidiInputs.size(); ++i)
@ -497,21 +799,43 @@ void AudioDeviceManager::setAudioCallback (AudioIODeviceCallback* newCallback)
}
void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData,
int totalNumInputChannels,
int numInputChannels,
float** outputChannelData,
int totalNumOutputChannels,
int numOutputChannels,
int numSamples)
{
const ScopedLock sl (audioCallbackLock);
if (inputLevelMeasurementEnabled)
{
for (int j = 0; j < numSamples; ++j)
{
float s = 0;
for (int i = 0; i < numInputChannels; ++i)
s += fabsf (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;
}
}
if (currentCallback != 0)
{
const double callbackStartTime = Time::getMillisecondCounterHiRes();
currentCallback->audioDeviceIOCallback (inputChannelData,
totalNumInputChannels,
numInputChannels,
outputChannelData,
totalNumOutputChannels,
numOutputChannels,
numSamples);
const double msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime;
@ -520,9 +844,25 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat
}
else
{
for (int i = 0; i < totalNumOutputChannels; ++i)
if (outputChannelData [i] != 0)
zeromem (outputChannelData[i], sizeof (float) * numSamples);
for (int i = 0; i < numOutputChannels; ++i)
zeromem (outputChannelData[i], sizeof (float) * numSamples);
}
if (testSound != 0)
{
const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition);
const float* const src = testSound->getSampleData (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())
{
delete testSound;
testSound = 0;
}
}
}
@ -605,7 +945,7 @@ bool AudioDeviceManager::isMidiInputEnabled (const String& name) const
void AudioDeviceManager::addMidiInputCallback (const String& name,
MidiInputCallback* callback)
{
removeMidiInputCallback (callback);
removeMidiInputCallback (name, callback);
if (name.isEmpty())
{
@ -632,13 +972,24 @@ void AudioDeviceManager::addMidiInputCallback (const String& name,
}
}
void AudioDeviceManager::removeMidiInputCallback (MidiInputCallback* callback)
void AudioDeviceManager::removeMidiInputCallback (const String& name,
MidiInputCallback* /*callback*/)
{
const ScopedLock sl (midiCallbackLock);
const int index = midiCallbacks.indexOf (callback);
midiCallbacks.remove (index);
midiCallbackDevices.remove (index);
for (int i = midiCallbacks.size(); --i >= 0;)
{
String devName;
if (midiCallbackDevices.getUnchecked(i) != 0)
devName = midiCallbackDevices.getUnchecked(i)->getName();
if (devName == name)
{
midiCallbacks.remove (i);
midiCallbackDevices.remove (i);
}
}
}
void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source,
@ -678,12 +1029,12 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName)
//==============================================================================
void AudioDeviceManager::CallbackHandler::audioDeviceIOCallback (const float** inputChannelData,
int totalNumInputChannels,
int numInputChannels,
float** outputChannelData,
int totalNumOutputChannels,
int numOutputChannels,
int numSamples)
{
owner->audioDeviceIOCallbackInt (inputChannelData, totalNumInputChannels, outputChannelData, totalNumOutputChannels, numSamples);
owner->audioDeviceIOCallbackInt (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples);
}
void AudioDeviceManager::CallbackHandler::audioDeviceAboutToStart (AudioIODevice* device)
@ -701,5 +1052,56 @@ void AudioDeviceManager::CallbackHandler::handleIncomingMidiMessage (MidiInput*
owner->handleIncomingMidiMessageInt (source, message);
}
//==============================================================================
void AudioDeviceManager::playTestSound()
{
audioCallbackLock.enter();
AudioSampleBuffer* oldSound = testSound;
testSound = 0;
audioCallbackLock.exit();
delete oldSound;
testSoundPosition = 0;
if (currentAudioDevice != 0)
{
const double sampleRate = currentAudioDevice->getCurrentSampleRate();
const int soundLength = (int) sampleRate;
AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength);
float* samples = newSound->getSampleData (0);
const double frequency = MidiMessage::getMidiNoteInHertz (80);
const float amplitude = 0.5f;
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
for (int i = 0; i < soundLength; ++i)
samples[i] = amplitude * (float) 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 (inputLevelMeasurementEnabled != enableMeasurement)
{
const ScopedLock sl (audioCallbackLock);
inputLevelMeasurementEnabled = enableMeasurement;
inputLevel = 0;
}
}
double AudioDeviceManager::getCurrentInputLevel() const
{
jassert (inputLevelMeasurementEnabled); // you need to call enableInputLevelMeasurement() before using this!
return inputLevel;
}
END_JUCE_NAMESPACE

View file

@ -37,6 +37,7 @@
#include "juce_MidiOutput.h"
#include "../../../juce_core/text/juce_XmlElement.h"
#include "../../gui/components/controls/juce_ComboBox.h"
#include "../dsp/juce_AudioSampleBuffer.h"
//==============================================================================
@ -130,54 +131,42 @@ public:
*/
XmlElement* createStateXml() const;
//==============================================================================
/** Returns a list of the audio devices that can be used.
On Windows, this will include both DSound and ASIO devices if they are available. On
the Mac, it'll be a list of the CoreAudio devices.
These names are used by setAudioDevice() when changing devices.
/**
*/
const StringArray getAvailableAudioDeviceNames() const;
struct AudioDeviceSetup
{
AudioDeviceSetup();
/** Rescans the list of known audio devices, in case it's changed. */
void refreshDeviceList() const;
bool operator== (const AudioDeviceSetup& other) const;
/** Sets a flag to indicate that when listing audio device names, it should treat them
as inputs rather than outputs.
/**
*/
String outputDeviceName;
/**
*/
String inputDeviceName;
/**
*/
double sampleRate;
/**
*/
int bufferSize;
/**
*/
BitArray inputChannels;
bool useDefaultInputChannels;
/**
*/
BitArray outputChannels;
bool useDefaultOutputChannels;
};
This only really applies to DirectSound, where input and output devices are
separate. On ASIO and CoreAudio this makes no difference.
@see getAvailableAudioDeviceNames
/**
*/
void setInputDeviceNamesUsed (const bool useInputNames);
void getAudioDeviceSetup (AudioDeviceSetup& setup);
/** Just adds the list of device names to a combo box.
The only reason this is in this class is so that it can divide DSound
and ASIO devices into labelled sections, which makes it look much neater.
*/
void addDeviceNamesToComboBox (ComboBox& combo) const;
/** Changes the audio device that should be used.
If deviceName is empty or not a valid name returned by getAvailableAudioDeviceNames(),
it will disable the current device.
@param deviceName the name of the device you want to use (or an empty string to
deselect the current device)
@param blockSizeToUse the samples-per-block you want to use, or 0 to use a default
value
@param sampleRateToUse the target sample-rate, or 0 to use a default that the device
is capable of
@param inputChans which of the audio device's input channels to open - pass 0 to
open as many of the the first ones as are needed for the number
of input channels that the app has requested
@param outputChans which of the audio device's input channels to open - pass 0 to
open as many of the the first ones as are needed for the number
of output channels that the app has requested
/**
@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
@ -185,15 +174,18 @@ public:
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.
*/
const String setAudioDevice (const String& deviceName,
int blockSizeToUse,
double sampleRateToUse,
const BitArray* inputChans,
const BitArray* outputChans,
const bool treatAsChosenDevice);
const String setAudioDeviceSetup (const AudioDeviceSetup& newSetup,
const bool treatAsChosenDevice);
/** Returns the currently-active audio device. */
AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; }
const String getCurrentAudioDeviceType() const throw() { return currentDeviceType; }
void setCurrentAudioDeviceType (const String& type,
const bool treatAsChosenDevice);
/** Closes the currently-open device.
@ -212,59 +204,7 @@ public:
*/
void restartLastAudioDevice();
/** Returns the name of the currently selected audio device.
This will be an empty string if none is active.
*/
const String getCurrentAudioDeviceName() const;
/** Returns the currently-active audio device. */
AudioIODevice* getCurrentAudioDevice() const throw() { return currentAudioDevice; }
/** Returns the set of audio input channels currently being used.
To select different channels, use setInputChannels(), or call setAudioDevice() to
reopen the device with a different set of channels.
*/
const BitArray getInputChannels() const throw() { return inputChannels; }
/** Changes the active set of input channels.
@param newEnabledChannels the set of channels to enable
@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().
@see getInputChannels
*/
void setInputChannels (const BitArray& newEnabledChannels,
const bool treatAsChosenDevice);
/** Returns the set of audio output channels currently being used.
To select different channels, use setOutputChannels(), or call setAudioDevice() to
reopen the device with a different set of channels.
*/
const BitArray getOutputChannels() const throw() { return outputChannels; }
/** Changes the active set of output channels.
@param newEnabledChannels the set of channels to enable
@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().
@see getOutputChannels
*/
void setOutputChannels (const BitArray& newEnabledChannels,
const bool treatAsChosenDevice);
//==============================================================================
/** Gives the manager an audio callback to use.
The manager will redirect callbacks from whatever audio device is currently
@ -274,6 +214,7 @@ public:
*/
void setAudioCallback (AudioIODeviceCallback* newCallback);
//==============================================================================
/** Returns the average proportion of available CPU being spent inside the audio callbacks.
Returns a value between 0 and 1.0
@ -323,7 +264,8 @@ public:
/** Removes a listener that was previously registered with addMidiInputCallback().
*/
void removeMidiInputCallback (MidiInputCallback* callback);
void removeMidiInputCallback (const String& midiInputDeviceName,
MidiInputCallback* callback);
//==============================================================================
/** Sets a midi output device to use as the default.
@ -355,6 +297,51 @@ public:
*/
MidiOutput* getDefaultMidiOutput() const throw() { return defaultMidiOutput; }
/**
*/
const OwnedArray <AudioIODeviceType>& getAvailableDeviceTypes() const throw() { return availableDeviceTypes; }
//==============================================================================
/** 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);
//==============================================================================
/** 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 (const 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;
//==============================================================================
juce_UseDebuggingNewOperator
@ -362,14 +349,20 @@ public:
private:
//==============================================================================
OwnedArray <AudioIODeviceType> availableDeviceTypes;
OwnedArray <AudioDeviceSetup> lastDeviceTypeConfigs;
AudioDeviceSetup currentSetup;
AudioIODevice* currentAudioDevice;
AudioIODeviceCallback* currentCallback;
int numInputChansNeeded, numOutputChansNeeded;
String currentDeviceType;
BitArray inputChannels, outputChannels;
XmlElement* lastExplicitSettings;
mutable bool listNeedsScanning;
bool useInputNames;
bool useInputNames, inputLevelMeasurementEnabled;
double inputLevel;
AudioSampleBuffer* testSound;
int testSoundPosition;
StringArray midiInsFromXml;
OwnedArray <MidiInput> enabledMidiInputs;
@ -402,11 +395,6 @@ private:
};
CallbackHandler callbackHandler;
String lastRunningDevice;
int lastRunningBlockSize;
double lastRunningSampleRate;
BitArray lastRunningIns, lastRunningOuts;
friend class CallbackHandler;
void audioDeviceIOCallbackInt (const float** inputChannelData,
@ -425,6 +413,15 @@ private:
void updateXml();
void createDeviceTypesIfNeeded();
void scanDevicesIfNeeded();
void deleteCurrentDevice();
double chooseBestSampleRate (double preferred) const;
AudioIODeviceType* getCurrentDeviceTypeObject() const;
void insertDefaultDeviceNames (AudioDeviceSetup& setup) const;
AudioIODeviceType* findType (const String& inputName, const String& outputName);
AudioDeviceManager (const AudioDeviceManager&);
const AudioDeviceManager& operator= (const AudioDeviceManager&);
};

View file

@ -307,6 +307,7 @@ public:
*/
virtual int getInputLatencyInSamples() = 0;
//==============================================================================
/** True if this device can show a pop-up control panel for editing its settings.

View file

@ -46,33 +46,5 @@ AudioIODeviceType::~AudioIODeviceType()
{
}
//==============================================================================
extern AudioIODeviceType* juce_createDefaultAudioIODeviceType();
#if JUCE_WIN32 && JUCE_ASIO
extern AudioIODeviceType* juce_createASIOAudioIODeviceType();
#endif
#if JUCE_WIN32 && JUCE_WDM_AUDIO
extern AudioIODeviceType* juce_createWDMAudioIODeviceType();
#endif
//==============================================================================
void AudioIODeviceType::createDeviceTypes (OwnedArray <AudioIODeviceType>& list)
{
AudioIODeviceType* const defaultDeviceType = juce_createDefaultAudioIODeviceType();
if (defaultDeviceType != 0)
list.add (defaultDeviceType);
#if JUCE_WIN32 && JUCE_ASIO
list.add (juce_createASIOAudioIODeviceType());
#endif
#if JUCE_WIN32 && JUCE_WDM_AUDIO
list.add (juce_createWDMAudioIODeviceType());
#endif
}
END_JUCE_NAMESPACE

View file

@ -33,7 +33,8 @@
#define __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__
#include "juce_AudioIODevice.h"
class AudioDeviceManager;
class Component;
//==============================================================================
/**
@ -71,20 +72,6 @@
class JUCE_API AudioIODeviceType
{
public:
//==============================================================================
/** 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.
The objects that are created should be managed by the caller (the OwnedArray
will delete them when the array is itself deleted).
When created, the objects are uninitialised, so you should call scanForDevices()
on each one before getting its list of devices.
*/
static void createDeviceTypes (OwnedArray <AudioIODeviceType>& list);
//==============================================================================
/** Returns the name of this type of driver that this object manages.
@ -104,35 +91,47 @@ public:
The scanForDevices() method must have been called to create this list.
@param preferInputNames 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.
@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 const StringArray getDeviceNames (const bool preferInputNames = false) const = 0;
virtual const StringArray getDeviceNames (const bool wantInputNames = false) const = 0;
/** Returns the name of the default device.
This will be one of the names from the getDeviceNames() list.
@param preferInputNames 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.
@param numInputChannelsNeeded the number of input channels the user is expecting to need - this
may be used to help decide which device would be most suitable
@param numOutputChannelsNeeded the number of output channels the user is expecting to need - this
may be used to help decide which device would be most suitable
@param forInput if true, this means that a default input device should be
returned; if false, it should return the default output
*/
virtual const String getDefaultDeviceName (const bool preferInputNames,
const int numInputChannelsNeeded,
const int numOutputChannelsNeeded) const = 0;
virtual int getDefaultDeviceIndex (const 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, const 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& deviceName) = 0;
virtual AudioIODevice* createDevice (const String& outputDeviceName,
const String& inputDeviceName) = 0;
//==============================================================================
struct DeviceSetupDetails
{
AudioDeviceManager* manager;
int minNumInputChannels, maxNumInputChannels;
int minNumOutputChannels, maxNumOutputChannels;
bool useStereoPairs;
};
//==============================================================================
/** Destructor. */

View file

@ -35,7 +35,7 @@
#include "../controls/juce_ComboBox.h"
#include "../controls/juce_ListBox.h"
#include "../../../audio/devices/juce_AudioDeviceManager.h"
class AudioDeviceSelectorComponentListBox;
class MidiInputSelectorComponentListBox;
//==============================================================================
/**
@ -89,24 +89,24 @@ public:
/** @internal */
void changeListenerCallback (void*);
//==============================================================================
/** Called by the device-specific displays to create a little level meter that
just displays the current total input levels from the given device manager.
*/
static Component* createSimpleLevelMeterComponent (AudioDeviceManager* managerToDisplay);
//==============================================================================
juce_UseDebuggingNewOperator
private:
AudioDeviceManager& deviceManager;
ComboBox* audioDeviceDropDown;
ComboBox* deviceTypeDropDown;
Label* deviceTypeDropDownLabel;
Component* audioDeviceSettingsComp;
String audioDeviceSettingsCompType;
const int minOutputChannels, maxOutputChannels, minInputChannels, maxInputChannels;
ComboBox* sampleRateDropDown;
AudioDeviceSelectorComponentListBox* inputChansBox;
Label* inputsLabel;
AudioDeviceSelectorComponentListBox* outputChansBox;
Label* outputsLabel;
Label* sampleRateLabel;
ComboBox* bufferSizeDropDown;
Label* bufferSizeLabel;
Button* launchUIButton;
AudioDeviceSelectorComponentListBox* midiInputsList;
MidiInputSelectorComponentListBox* midiInputsList;
Label* midiInputsLabel;
ComboBox* midiOutputSelector;
Label* midiOutputLabel;