mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-03 03:30:06 +00:00
1170 lines
40 KiB
C++
1170 lines
40 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
|
Copyright 2004-9 by Raw Material Software Ltd.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the GNU General
|
|
Public License (Version 2), as published by the Free Software Foundation.
|
|
A copy of the license is included in the JUCE distribution, or can be found
|
|
online at www.gnu.org/licenses.
|
|
|
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
To release a closed-source product which uses JUCE, commercial licenses are
|
|
available: visit www.rawmaterialsoftware.com/juce for more information.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#include "../../../core/juce_StandardHeader.h"
|
|
|
|
BEGIN_JUCE_NAMESPACE
|
|
|
|
#include "juce_AudioDeviceSelectorComponent.h"
|
|
#include "../buttons/juce_TextButton.h"
|
|
#include "../menus/juce_PopupMenu.h"
|
|
#include "../windows/juce_AlertWindow.h"
|
|
#include "../lookandfeel/juce_LookAndFeel.h"
|
|
#include "../../../text/juce_LocalisedStrings.h"
|
|
|
|
|
|
//==============================================================================
|
|
class SimpleDeviceManagerInputLevelMeter : public Component,
|
|
public Timer
|
|
{
|
|
public:
|
|
SimpleDeviceManagerInputLevelMeter (AudioDeviceManager* const manager_)
|
|
: manager (manager_),
|
|
level (0)
|
|
{
|
|
startTimer (50);
|
|
manager->enableInputLevelMeasurement (true);
|
|
}
|
|
|
|
~SimpleDeviceManagerInputLevelMeter()
|
|
{
|
|
manager->enableInputLevelMeasurement (false);
|
|
}
|
|
|
|
void timerCallback()
|
|
{
|
|
const float newLevel = (float) manager->getCurrentInputLevel();
|
|
|
|
if (fabsf (level - newLevel) > 0.005f)
|
|
{
|
|
level = newLevel;
|
|
repaint();
|
|
}
|
|
}
|
|
|
|
void paint (Graphics& g)
|
|
{
|
|
getLookAndFeel().drawLevelMeter (g, getWidth(), getHeight(),
|
|
(float) exp (log (level) / 3.0)); // (add a bit of a skew to make the level more obvious)
|
|
}
|
|
|
|
private:
|
|
AudioDeviceManager* const manager;
|
|
float level;
|
|
|
|
SimpleDeviceManagerInputLevelMeter (const SimpleDeviceManagerInputLevelMeter&);
|
|
SimpleDeviceManagerInputLevelMeter& operator= (const SimpleDeviceManagerInputLevelMeter&);
|
|
};
|
|
|
|
|
|
//==============================================================================
|
|
class MidiInputSelectorComponentListBox : public ListBox,
|
|
public ListBoxModel
|
|
{
|
|
public:
|
|
//==============================================================================
|
|
MidiInputSelectorComponentListBox (AudioDeviceManager& deviceManager_,
|
|
const String& noItemsMessage_,
|
|
const int minNumber_,
|
|
const int maxNumber_)
|
|
: ListBox (String::empty, 0),
|
|
deviceManager (deviceManager_),
|
|
noItemsMessage (noItemsMessage_),
|
|
minNumber (minNumber_),
|
|
maxNumber (maxNumber_)
|
|
{
|
|
items = MidiInput::getDevices();
|
|
|
|
setModel (this);
|
|
setOutlineThickness (1);
|
|
}
|
|
|
|
~MidiInputSelectorComponentListBox()
|
|
{
|
|
}
|
|
|
|
int getNumRows()
|
|
{
|
|
return items.size();
|
|
}
|
|
|
|
void paintListBoxItem (int row,
|
|
Graphics& g,
|
|
int width, int height,
|
|
bool rowIsSelected)
|
|
{
|
|
if (((unsigned int) row) < (unsigned int) items.size())
|
|
{
|
|
if (rowIsSelected)
|
|
g.fillAll (findColour (TextEditor::highlightColourId)
|
|
.withMultipliedAlpha (0.3f));
|
|
|
|
const String item (items [row]);
|
|
bool enabled = deviceManager.isMidiInputEnabled (item);
|
|
|
|
const int x = getTickX();
|
|
const float tickW = height * 0.75f;
|
|
|
|
getLookAndFeel().drawTickBox (g, *this, x - tickW, (height - tickW) / 2, tickW, tickW,
|
|
enabled, true, true, false);
|
|
|
|
g.setFont (height * 0.6f);
|
|
g.setColour (findColour (ListBox::textColourId, true).withMultipliedAlpha (enabled ? 1.0f : 0.6f));
|
|
g.drawText (item, x, 0, width - x - 2, height, Justification::centredLeft, true);
|
|
}
|
|
}
|
|
|
|
void listBoxItemClicked (int row, const MouseEvent& e)
|
|
{
|
|
selectRow (row);
|
|
|
|
if (e.x < getTickX())
|
|
flipEnablement (row);
|
|
}
|
|
|
|
void listBoxItemDoubleClicked (int row, const MouseEvent&)
|
|
{
|
|
flipEnablement (row);
|
|
}
|
|
|
|
void returnKeyPressed (int row)
|
|
{
|
|
flipEnablement (row);
|
|
}
|
|
|
|
void paint (Graphics& g)
|
|
{
|
|
ListBox::paint (g);
|
|
|
|
if (items.size() == 0)
|
|
{
|
|
g.setColour (Colours::grey);
|
|
g.setFont (13.0f);
|
|
g.drawText (noItemsMessage,
|
|
0, 0, getWidth(), getHeight() / 2,
|
|
Justification::centred, true);
|
|
}
|
|
}
|
|
|
|
int getBestHeight (const int preferredHeight)
|
|
{
|
|
const int extra = getOutlineThickness() * 2;
|
|
|
|
return jmax (getRowHeight() * 2 + extra,
|
|
jmin (getRowHeight() * getNumRows() + extra,
|
|
preferredHeight));
|
|
}
|
|
|
|
//==============================================================================
|
|
juce_UseDebuggingNewOperator
|
|
|
|
private:
|
|
AudioDeviceManager& deviceManager;
|
|
const String noItemsMessage;
|
|
StringArray items;
|
|
int minNumber, maxNumber;
|
|
|
|
void flipEnablement (const int row)
|
|
{
|
|
if (((unsigned int) row) < (unsigned int) items.size())
|
|
{
|
|
const String item (items [row]);
|
|
deviceManager.setMidiInputEnabled (item, ! deviceManager.isMidiInputEnabled (item));
|
|
}
|
|
}
|
|
|
|
int getTickX() const
|
|
{
|
|
return getRowHeight() + 5;
|
|
}
|
|
|
|
MidiInputSelectorComponentListBox (const MidiInputSelectorComponentListBox&);
|
|
MidiInputSelectorComponentListBox& operator= (const MidiInputSelectorComponentListBox&);
|
|
};
|
|
|
|
|
|
//==============================================================================
|
|
class AudioDeviceSettingsPanel : public Component,
|
|
public ComboBoxListener,
|
|
public ChangeListener,
|
|
public ButtonListener
|
|
{
|
|
public:
|
|
AudioDeviceSettingsPanel (AudioIODeviceType* type_,
|
|
AudioIODeviceType::DeviceSetupDetails& setup_,
|
|
const bool hideAdvancedOptionsWithButton)
|
|
: type (type_),
|
|
setup (setup_)
|
|
{
|
|
sampleRateDropDown = 0;
|
|
sampleRateLabel = 0;
|
|
bufferSizeDropDown = 0;
|
|
bufferSizeLabel = 0;
|
|
outputDeviceDropDown = 0;
|
|
outputDeviceLabel = 0;
|
|
inputDeviceDropDown = 0;
|
|
inputDeviceLabel = 0;
|
|
testButton = 0;
|
|
inputLevelMeter = 0;
|
|
showUIButton = 0;
|
|
inputChanList = 0;
|
|
outputChanList = 0;
|
|
inputChanLabel = 0;
|
|
outputChanLabel = 0;
|
|
showAdvancedSettingsButton = 0;
|
|
|
|
if (hideAdvancedOptionsWithButton)
|
|
{
|
|
addAndMakeVisible (showAdvancedSettingsButton = new TextButton (TRANS("Show advanced settings...")));
|
|
showAdvancedSettingsButton->addButtonListener (this);
|
|
}
|
|
|
|
type->scanForDevices();
|
|
|
|
setup.manager->addChangeListener (this);
|
|
changeListenerCallback (0);
|
|
}
|
|
|
|
~AudioDeviceSettingsPanel()
|
|
{
|
|
setup.manager->removeChangeListener (this);
|
|
|
|
deleteAndZero (outputDeviceLabel);
|
|
deleteAndZero (inputDeviceLabel);
|
|
deleteAndZero (sampleRateLabel);
|
|
deleteAndZero (bufferSizeLabel);
|
|
deleteAndZero (showUIButton);
|
|
deleteAndZero (inputChanLabel);
|
|
deleteAndZero (outputChanLabel);
|
|
deleteAndZero (showAdvancedSettingsButton);
|
|
|
|
deleteAllChildren();
|
|
}
|
|
|
|
void resized()
|
|
{
|
|
const int lx = proportionOfWidth (0.35f);
|
|
const int w = proportionOfWidth (0.4f);
|
|
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);
|
|
|
|
if (testButton != 0)
|
|
testButton->setBounds (proportionOfWidth (0.77f),
|
|
outputDeviceDropDown->getY(),
|
|
proportionOfWidth (0.18f),
|
|
h);
|
|
y += dh;
|
|
}
|
|
|
|
if (inputDeviceDropDown != 0)
|
|
{
|
|
inputDeviceDropDown->setBounds (lx, y, w, h);
|
|
|
|
inputLevelMeter->setBounds (proportionOfWidth (0.77f),
|
|
inputDeviceDropDown->getY(),
|
|
proportionOfWidth (0.18f),
|
|
h);
|
|
y += dh;
|
|
}
|
|
|
|
const int maxBoxHeight = 100;//(getHeight() - y - dh * 2) / numBoxes;
|
|
|
|
if (outputChanList != 0)
|
|
{
|
|
const int bh = outputChanList->getBestHeight (maxBoxHeight);
|
|
outputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh);
|
|
y += bh + space;
|
|
}
|
|
|
|
if (inputChanList != 0)
|
|
{
|
|
const int bh = inputChanList->getBestHeight (maxBoxHeight);
|
|
inputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh);
|
|
y += bh + space;
|
|
}
|
|
|
|
y += space * 2;
|
|
|
|
if (showAdvancedSettingsButton != 0)
|
|
{
|
|
showAdvancedSettingsButton->changeWidthToFitText (h);
|
|
showAdvancedSettingsButton->setTopLeftPosition (lx, y);
|
|
}
|
|
|
|
if (sampleRateDropDown != 0)
|
|
{
|
|
sampleRateDropDown->setVisible (showAdvancedSettingsButton == 0
|
|
|| ! showAdvancedSettingsButton->isVisible());
|
|
|
|
sampleRateDropDown->setBounds (lx, y, w, h);
|
|
y += dh;
|
|
}
|
|
|
|
if (bufferSizeDropDown != 0)
|
|
{
|
|
bufferSizeDropDown->setVisible (showAdvancedSettingsButton == 0
|
|
|| ! showAdvancedSettingsButton->isVisible());
|
|
bufferSizeDropDown->setBounds (lx, y, w, h);
|
|
y += dh;
|
|
}
|
|
|
|
if (showUIButton != 0)
|
|
{
|
|
showUIButton->setVisible (showAdvancedSettingsButton == 0
|
|
|| ! showAdvancedSettingsButton->isVisible());
|
|
showUIButton->changeWidthToFitText (h);
|
|
showUIButton->setTopLeftPosition (lx, y);
|
|
}
|
|
}
|
|
|
|
void comboBoxChanged (ComboBox* comboBoxThatHasChanged)
|
|
{
|
|
if (comboBoxThatHasChanged == 0)
|
|
return;
|
|
|
|
AudioDeviceManager::AudioDeviceSetup config;
|
|
setup.manager->getAudioDeviceSetup (config);
|
|
String error;
|
|
|
|
if (comboBoxThatHasChanged == outputDeviceDropDown
|
|
|| comboBoxThatHasChanged == inputDeviceDropDown)
|
|
{
|
|
if (outputDeviceDropDown != 0)
|
|
config.outputDeviceName = outputDeviceDropDown->getSelectedId() < 0 ? String::empty
|
|
: outputDeviceDropDown->getText();
|
|
|
|
if (inputDeviceDropDown != 0)
|
|
config.inputDeviceName = inputDeviceDropDown->getSelectedId() < 0 ? String::empty
|
|
: inputDeviceDropDown->getText();
|
|
|
|
if (! type->hasSeparateInputsAndOutputs())
|
|
config.inputDeviceName = config.outputDeviceName;
|
|
|
|
if (comboBoxThatHasChanged == inputDeviceDropDown)
|
|
config.useDefaultInputChannels = true;
|
|
else
|
|
config.useDefaultOutputChannels = true;
|
|
|
|
error = setup.manager->setAudioDeviceSetup (config, true);
|
|
|
|
showCorrectDeviceName (inputDeviceDropDown, true);
|
|
showCorrectDeviceName (outputDeviceDropDown, false);
|
|
|
|
updateControlPanelButton();
|
|
resized();
|
|
}
|
|
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,
|
|
"Error when trying to open audio device!",
|
|
error);
|
|
}
|
|
}
|
|
|
|
void buttonClicked (Button* button)
|
|
{
|
|
if (button == showAdvancedSettingsButton)
|
|
{
|
|
showAdvancedSettingsButton->setVisible (false);
|
|
resized();
|
|
}
|
|
else if (button == showUIButton)
|
|
{
|
|
AudioIODevice* const device = setup.manager->getCurrentAudioDevice();
|
|
|
|
if (device != 0 && device->showControlPanel())
|
|
{
|
|
setup.manager->closeAudioDevice();
|
|
setup.manager->restartLastAudioDevice();
|
|
getTopLevelComponent()->toFront (true);
|
|
}
|
|
}
|
|
else if (button == testButton && testButton != 0)
|
|
{
|
|
setup.manager->playTestSound();
|
|
}
|
|
}
|
|
|
|
void updateControlPanelButton()
|
|
{
|
|
AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice();
|
|
|
|
deleteAndZero (showUIButton);
|
|
|
|
if (currentDevice != 0 && currentDevice->hasControlPanel())
|
|
{
|
|
addAndMakeVisible (showUIButton = new TextButton (TRANS ("show this device's control panel"),
|
|
TRANS ("opens the device's own control panel")));
|
|
showUIButton->addButtonListener (this);
|
|
}
|
|
|
|
resized();
|
|
}
|
|
|
|
void changeListenerCallback (void*)
|
|
{
|
|
AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice();
|
|
|
|
if (setup.maxNumOutputChannels > 0 || ! type->hasSeparateInputsAndOutputs())
|
|
{
|
|
if (outputDeviceDropDown == 0)
|
|
{
|
|
outputDeviceDropDown = new ComboBox (String::empty);
|
|
outputDeviceDropDown->addListener (this);
|
|
addAndMakeVisible (outputDeviceDropDown);
|
|
|
|
outputDeviceLabel = new Label (String::empty,
|
|
type->hasSeparateInputsAndOutputs() ? TRANS ("output:")
|
|
: TRANS ("device:"));
|
|
outputDeviceLabel->attachToComponent (outputDeviceDropDown, true);
|
|
|
|
if (setup.maxNumOutputChannels > 0)
|
|
{
|
|
addAndMakeVisible (testButton = new TextButton (TRANS ("Test")));
|
|
testButton->addButtonListener (this);
|
|
}
|
|
}
|
|
|
|
addNamesToDeviceBox (*outputDeviceDropDown, false);
|
|
}
|
|
|
|
if (setup.maxNumInputChannels > 0 && type->hasSeparateInputsAndOutputs())
|
|
{
|
|
if (inputDeviceDropDown == 0)
|
|
{
|
|
inputDeviceDropDown = new ComboBox (String::empty);
|
|
inputDeviceDropDown->addListener (this);
|
|
addAndMakeVisible (inputDeviceDropDown);
|
|
|
|
inputDeviceLabel = new Label (String::empty, TRANS ("input:"));
|
|
inputDeviceLabel->attachToComponent (inputDeviceDropDown, true);
|
|
|
|
addAndMakeVisible (inputLevelMeter
|
|
= new SimpleDeviceManagerInputLevelMeter (setup.manager));
|
|
}
|
|
|
|
addNamesToDeviceBox (*inputDeviceDropDown, true);
|
|
}
|
|
|
|
updateControlPanelButton();
|
|
showCorrectDeviceName (inputDeviceDropDown, true);
|
|
showCorrectDeviceName (outputDeviceDropDown, false);
|
|
|
|
if (currentDevice != 0)
|
|
{
|
|
if (setup.maxNumOutputChannels > 0
|
|
&& setup.minNumOutputChannels < setup.manager->getCurrentAudioDevice()->getOutputChannelNames().size())
|
|
{
|
|
if (outputChanList == 0)
|
|
{
|
|
addAndMakeVisible (outputChanList
|
|
= new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioOutputType,
|
|
TRANS ("(no audio output channels found)")));
|
|
outputChanLabel = new Label (String::empty, TRANS ("active output channels:"));
|
|
outputChanLabel->attachToComponent (outputChanList, true);
|
|
}
|
|
|
|
outputChanList->refresh();
|
|
}
|
|
else
|
|
{
|
|
deleteAndZero (outputChanLabel);
|
|
deleteAndZero (outputChanList);
|
|
}
|
|
|
|
if (setup.maxNumInputChannels > 0
|
|
&& setup.minNumInputChannels < setup.manager->getCurrentAudioDevice()->getInputChannelNames().size())
|
|
{
|
|
if (inputChanList == 0)
|
|
{
|
|
addAndMakeVisible (inputChanList
|
|
= new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioInputType,
|
|
TRANS ("(no audio input channels found)")));
|
|
inputChanLabel = new Label (String::empty, TRANS ("active input channels:"));
|
|
inputChanLabel->attachToComponent (inputChanList, true);
|
|
}
|
|
|
|
inputChanList->refresh();
|
|
}
|
|
else
|
|
{
|
|
deleteAndZero (inputChanLabel);
|
|
deleteAndZero (inputChanList);
|
|
}
|
|
|
|
// 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 = roundToInt (currentDevice->getSampleRate (i));
|
|
sampleRateDropDown->addItem (String (rate) + " Hz", rate);
|
|
}
|
|
|
|
sampleRateDropDown->setSelectedId (roundToInt (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 = 48000.0;
|
|
|
|
for (int i = 0; i < numBufferSizes; ++i)
|
|
{
|
|
const int bs = currentDevice->getBufferSizeSamples (i);
|
|
bufferSizeDropDown->addItem (String (bs)
|
|
+ " samples ("
|
|
+ String (bs * 1000.0 / currentRate, 1)
|
|
+ " 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;
|
|
Label* inputChanLabel;
|
|
Label* outputChanLabel;
|
|
TextButton* testButton;
|
|
Component* inputLevelMeter;
|
|
TextButton* showUIButton;
|
|
TextButton* showAdvancedSettingsButton;
|
|
|
|
void showCorrectDeviceName (ComboBox* const box, const bool isInput)
|
|
{
|
|
if (box != 0)
|
|
{
|
|
AudioIODevice* const currentDevice = dynamic_cast <AudioIODevice*> (setup.manager->getCurrentAudioDevice());
|
|
|
|
const int index = type->getIndexOfDevice (currentDevice, isInput);
|
|
|
|
box->setSelectedId (index + 1, true);
|
|
|
|
if (testButton != 0 && ! isInput)
|
|
testButton->setEnabled (index >= 0);
|
|
}
|
|
}
|
|
|
|
void addNamesToDeviceBox (ComboBox& combo, bool isInputs)
|
|
{
|
|
const StringArray devs (type->getDeviceNames (isInputs));
|
|
|
|
combo.clear (true);
|
|
|
|
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;
|
|
}
|
|
|
|
public:
|
|
//==============================================================================
|
|
class ChannelSelectorListBox : public ListBox,
|
|
public ListBoxModel
|
|
{
|
|
public:
|
|
enum BoxType
|
|
{
|
|
audioInputType,
|
|
audioOutputType
|
|
};
|
|
|
|
//==============================================================================
|
|
ChannelSelectorListBox (const AudioIODeviceType::DeviceSetupDetails& setup_,
|
|
const BoxType type_,
|
|
const String& noItemsMessage_)
|
|
: ListBox (String::empty, 0),
|
|
setup (setup_),
|
|
type (type_),
|
|
noItemsMessage (noItemsMessage_)
|
|
{
|
|
refresh();
|
|
setModel (this);
|
|
setOutlineThickness (1);
|
|
}
|
|
|
|
~ChannelSelectorListBox()
|
|
{
|
|
}
|
|
|
|
void refresh()
|
|
{
|
|
items.clear();
|
|
|
|
AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice();
|
|
|
|
if (currentDevice != 0)
|
|
{
|
|
if (type == audioInputType)
|
|
items = currentDevice->getInputChannelNames();
|
|
else if (type == audioOutputType)
|
|
items = currentDevice->getOutputChannelNames();
|
|
|
|
if (setup.useStereoPairs)
|
|
{
|
|
StringArray pairs;
|
|
|
|
for (int i = 0; i < items.size(); i += 2)
|
|
{
|
|
String name (items[i]);
|
|
String name2 (items[i + 1]);
|
|
|
|
String commonBit;
|
|
|
|
for (int j = 0; j < name.length(); ++j)
|
|
if (name.substring (0, j).equalsIgnoreCase (name2.substring (0, j)))
|
|
commonBit = name.substring (0, j);
|
|
|
|
pairs.add (name.trim()
|
|
+ " + "
|
|
+ name2.substring (commonBit.length()).trim());
|
|
}
|
|
|
|
items = pairs;
|
|
}
|
|
}
|
|
|
|
updateContent();
|
|
repaint();
|
|
}
|
|
|
|
int getNumRows()
|
|
{
|
|
return items.size();
|
|
}
|
|
|
|
void paintListBoxItem (int row,
|
|
Graphics& g,
|
|
int width, int height,
|
|
bool rowIsSelected)
|
|
{
|
|
if (((unsigned int) row) < (unsigned int) items.size())
|
|
{
|
|
if (rowIsSelected)
|
|
g.fillAll (findColour (TextEditor::highlightColourId)
|
|
.withMultipliedAlpha (0.3f));
|
|
|
|
const String item (items [row]);
|
|
bool enabled = false;
|
|
|
|
AudioDeviceManager::AudioDeviceSetup config;
|
|
setup.manager->getAudioDeviceSetup (config);
|
|
|
|
if (setup.useStereoPairs)
|
|
{
|
|
if (type == audioInputType)
|
|
enabled = config.inputChannels [row * 2] || config.inputChannels [row * 2 + 1];
|
|
else if (type == audioOutputType)
|
|
enabled = config.outputChannels [row * 2] || config.outputChannels [row * 2 + 1];
|
|
}
|
|
else
|
|
{
|
|
if (type == audioInputType)
|
|
enabled = config.inputChannels [row];
|
|
else if (type == audioOutputType)
|
|
enabled = config.outputChannels [row];
|
|
}
|
|
|
|
const int x = getTickX();
|
|
const float tickW = height * 0.75f;
|
|
|
|
getLookAndFeel().drawTickBox (g, *this, x - tickW, (height - tickW) / 2, tickW, tickW,
|
|
enabled, true, true, false);
|
|
|
|
g.setFont (height * 0.6f);
|
|
g.setColour (findColour (ListBox::textColourId, true).withMultipliedAlpha (enabled ? 1.0f : 0.6f));
|
|
g.drawText (item, x, 0, width - x - 2, height, Justification::centredLeft, true);
|
|
}
|
|
}
|
|
|
|
void listBoxItemClicked (int row, const MouseEvent& e)
|
|
{
|
|
selectRow (row);
|
|
|
|
if (e.x < getTickX())
|
|
flipEnablement (row);
|
|
}
|
|
|
|
void listBoxItemDoubleClicked (int row, const MouseEvent&)
|
|
{
|
|
flipEnablement (row);
|
|
}
|
|
|
|
void returnKeyPressed (int row)
|
|
{
|
|
flipEnablement (row);
|
|
}
|
|
|
|
void paint (Graphics& g)
|
|
{
|
|
ListBox::paint (g);
|
|
|
|
if (items.size() == 0)
|
|
{
|
|
g.setColour (Colours::grey);
|
|
g.setFont (13.0f);
|
|
g.drawText (noItemsMessage,
|
|
0, 0, getWidth(), getHeight() / 2,
|
|
Justification::centred, true);
|
|
}
|
|
}
|
|
|
|
int getBestHeight (int maxHeight)
|
|
{
|
|
return getRowHeight() * jlimit (2, jmax (2, maxHeight / getRowHeight()),
|
|
getNumRows())
|
|
+ getOutlineThickness() * 2;
|
|
}
|
|
|
|
//==============================================================================
|
|
juce_UseDebuggingNewOperator
|
|
|
|
private:
|
|
const AudioIODeviceType::DeviceSetupDetails setup;
|
|
const BoxType type;
|
|
const String noItemsMessage;
|
|
StringArray items;
|
|
|
|
void flipEnablement (const int row)
|
|
{
|
|
jassert (type == audioInputType || type == audioOutputType);
|
|
|
|
if (((unsigned int) row) < (unsigned int) items.size())
|
|
{
|
|
AudioDeviceManager::AudioDeviceSetup config;
|
|
setup.manager->getAudioDeviceSetup (config);
|
|
|
|
if (setup.useStereoPairs)
|
|
{
|
|
BigInteger bits;
|
|
BigInteger& original = (type == audioInputType ? config.inputChannels
|
|
: config.outputChannels);
|
|
|
|
int i;
|
|
for (i = 0; i < 256; i += 2)
|
|
bits.setBit (i / 2, original [i] || original [i + 1]);
|
|
|
|
if (type == audioInputType)
|
|
{
|
|
config.useDefaultInputChannels = false;
|
|
flipBit (bits, row, setup.minNumInputChannels / 2, setup.maxNumInputChannels / 2);
|
|
}
|
|
else
|
|
{
|
|
config.useDefaultOutputChannels = false;
|
|
flipBit (bits, row, setup.minNumOutputChannels / 2, setup.maxNumOutputChannels / 2);
|
|
}
|
|
|
|
for (i = 0; i < 256; ++i)
|
|
original.setBit (i, bits [i / 2]);
|
|
}
|
|
else
|
|
{
|
|
if (type == audioInputType)
|
|
{
|
|
config.useDefaultInputChannels = false;
|
|
flipBit (config.inputChannels, row, setup.minNumInputChannels, setup.maxNumInputChannels);
|
|
}
|
|
else
|
|
{
|
|
config.useDefaultOutputChannels = false;
|
|
flipBit (config.outputChannels, row, setup.minNumOutputChannels, setup.maxNumOutputChannels);
|
|
}
|
|
}
|
|
|
|
String error (setup.manager->setAudioDeviceSetup (config, true));
|
|
|
|
if (! error.isEmpty())
|
|
{
|
|
//xxx
|
|
}
|
|
}
|
|
}
|
|
|
|
static void flipBit (BigInteger& chans, int index, int minNumber, int maxNumber)
|
|
{
|
|
const int numActive = chans.countNumberOfSetBits();
|
|
|
|
if (chans [index])
|
|
{
|
|
if (numActive > minNumber)
|
|
chans.setBit (index, false);
|
|
}
|
|
else
|
|
{
|
|
if (numActive >= maxNumber)
|
|
{
|
|
const int firstActiveChan = chans.findNextSetBit();
|
|
|
|
chans.setBit (index > firstActiveChan
|
|
? firstActiveChan : chans.getHighestBit(),
|
|
false);
|
|
}
|
|
|
|
chans.setBit (index, true);
|
|
}
|
|
}
|
|
|
|
int getTickX() const
|
|
{
|
|
return getRowHeight() + 5;
|
|
}
|
|
|
|
ChannelSelectorListBox (const ChannelSelectorListBox&);
|
|
ChannelSelectorListBox& operator= (const ChannelSelectorListBox&);
|
|
};
|
|
|
|
private:
|
|
ChannelSelectorListBox* inputChanList;
|
|
ChannelSelectorListBox* outputChanList;
|
|
|
|
AudioDeviceSettingsPanel (const AudioDeviceSettingsPanel&);
|
|
AudioDeviceSettingsPanel& operator= (const AudioDeviceSettingsPanel&);
|
|
};
|
|
|
|
|
|
//==============================================================================
|
|
AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager& deviceManager_,
|
|
const int minInputChannels_,
|
|
const int maxInputChannels_,
|
|
const int minOutputChannels_,
|
|
const int maxOutputChannels_,
|
|
const bool showMidiInputOptions,
|
|
const bool showMidiOutputSelector,
|
|
const bool showChannelsAsStereoPairs_,
|
|
const bool hideAdvancedOptionsWithButton_)
|
|
: deviceManager (deviceManager_),
|
|
deviceTypeDropDown (0),
|
|
deviceTypeDropDownLabel (0),
|
|
audioDeviceSettingsComp (0),
|
|
minOutputChannels (minOutputChannels_),
|
|
maxOutputChannels (maxOutputChannels_),
|
|
minInputChannels (minInputChannels_),
|
|
maxInputChannels (maxInputChannels_),
|
|
showChannelsAsStereoPairs (showChannelsAsStereoPairs_),
|
|
hideAdvancedOptionsWithButton (hideAdvancedOptionsWithButton_)
|
|
{
|
|
jassert (minOutputChannels >= 0 && minOutputChannels <= maxOutputChannels);
|
|
jassert (minInputChannels >= 0 && minInputChannels <= maxInputChannels);
|
|
|
|
if (deviceManager_.getAvailableDeviceTypes().size() > 1)
|
|
{
|
|
deviceTypeDropDown = new ComboBox (String::empty);
|
|
|
|
for (int i = 0; i < deviceManager_.getAvailableDeviceTypes().size(); ++i)
|
|
{
|
|
deviceTypeDropDown
|
|
->addItem (deviceManager_.getAvailableDeviceTypes().getUnchecked(i)->getTypeName(),
|
|
i + 1);
|
|
}
|
|
|
|
addAndMakeVisible (deviceTypeDropDown);
|
|
deviceTypeDropDown->addListener (this);
|
|
|
|
deviceTypeDropDownLabel = new Label (String::empty, TRANS ("audio device type:"));
|
|
deviceTypeDropDownLabel->setJustificationType (Justification::centredRight);
|
|
deviceTypeDropDownLabel->attachToComponent (deviceTypeDropDown, true);
|
|
}
|
|
|
|
if (showMidiInputOptions)
|
|
{
|
|
addAndMakeVisible (midiInputsList
|
|
= new MidiInputSelectorComponentListBox (deviceManager,
|
|
TRANS("(no midi inputs available)"),
|
|
0, 0));
|
|
|
|
midiInputsLabel = new Label (String::empty, TRANS ("active midi inputs:"));
|
|
midiInputsLabel->setJustificationType (Justification::topRight);
|
|
midiInputsLabel->attachToComponent (midiInputsList, true);
|
|
}
|
|
else
|
|
{
|
|
midiInputsList = 0;
|
|
midiInputsLabel = 0;
|
|
}
|
|
|
|
if (showMidiOutputSelector)
|
|
{
|
|
addAndMakeVisible (midiOutputSelector = new ComboBox (String::empty));
|
|
midiOutputSelector->addListener (this);
|
|
|
|
midiOutputLabel = new Label ("lm", TRANS("Midi Output:"));
|
|
midiOutputLabel->attachToComponent (midiOutputSelector, true);
|
|
}
|
|
else
|
|
{
|
|
midiOutputSelector = 0;
|
|
midiOutputLabel = 0;
|
|
}
|
|
|
|
deviceManager_.addChangeListener (this);
|
|
changeListenerCallback (0);
|
|
}
|
|
|
|
AudioDeviceSelectorComponent::~AudioDeviceSelectorComponent()
|
|
{
|
|
deviceManager.removeChangeListener (this);
|
|
deleteAllChildren();
|
|
}
|
|
|
|
void AudioDeviceSelectorComponent::resized()
|
|
{
|
|
const int lx = proportionOfWidth (0.35f);
|
|
const int w = proportionOfWidth (0.4f);
|
|
const int h = 24;
|
|
const int space = 6;
|
|
const int dh = h + space;
|
|
int y = 15;
|
|
|
|
if (deviceTypeDropDown != 0)
|
|
{
|
|
deviceTypeDropDown->setBounds (lx, y, proportionOfWidth (0.3f), h);
|
|
y += dh + space * 2;
|
|
}
|
|
|
|
if (audioDeviceSettingsComp != 0)
|
|
{
|
|
audioDeviceSettingsComp->setBounds (0, y, getWidth(), audioDeviceSettingsComp->getHeight());
|
|
y += audioDeviceSettingsComp->getHeight() + space;
|
|
}
|
|
|
|
if (midiInputsList != 0)
|
|
{
|
|
const int bh = midiInputsList->getBestHeight (jmin (h * 8, getHeight() - y - space - h));
|
|
midiInputsList->setBounds (lx, y, w, bh);
|
|
y += bh + space;
|
|
}
|
|
|
|
if (midiOutputSelector != 0)
|
|
midiOutputSelector->setBounds (lx, y, w, h);
|
|
}
|
|
|
|
void AudioDeviceSelectorComponent::childBoundsChanged (Component* child)
|
|
{
|
|
if (child == audioDeviceSettingsComp)
|
|
resized();
|
|
}
|
|
|
|
void AudioDeviceSelectorComponent::buttonClicked (Button*)
|
|
{
|
|
AudioIODevice* const device = deviceManager.getCurrentAudioDevice();
|
|
|
|
if (device != 0 && device->hasControlPanel())
|
|
{
|
|
if (device->showControlPanel())
|
|
deviceManager.restartLastAudioDevice();
|
|
|
|
getTopLevelComponent()->toFront (true);
|
|
}
|
|
}
|
|
|
|
void AudioDeviceSelectorComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
|
|
{
|
|
if (comboBoxThatHasChanged == deviceTypeDropDown)
|
|
{
|
|
AudioIODeviceType* const type = deviceManager.getAvailableDeviceTypes() [deviceTypeDropDown->getSelectedId() - 1];
|
|
|
|
if (type != 0)
|
|
{
|
|
deleteAndZero (audioDeviceSettingsComp);
|
|
|
|
deviceManager.setCurrentAudioDeviceType (type->getTypeName(), true);
|
|
|
|
changeListenerCallback (0); // needed in case the type hasn't actally changed
|
|
}
|
|
}
|
|
else if (comboBoxThatHasChanged == midiOutputSelector)
|
|
{
|
|
deviceManager.setDefaultMidiOutput (midiOutputSelector->getText());
|
|
}
|
|
}
|
|
|
|
void AudioDeviceSelectorComponent::changeListenerCallback (void*)
|
|
{
|
|
if (deviceTypeDropDown != 0)
|
|
{
|
|
deviceTypeDropDown->setText (deviceManager.getCurrentAudioDeviceType(), false);
|
|
}
|
|
|
|
if (audioDeviceSettingsComp == 0
|
|
|| audioDeviceSettingsCompType != deviceManager.getCurrentAudioDeviceType())
|
|
{
|
|
audioDeviceSettingsCompType = deviceManager.getCurrentAudioDeviceType();
|
|
|
|
deleteAndZero (audioDeviceSettingsComp);
|
|
|
|
AudioIODeviceType* const type
|
|
= deviceManager.getAvailableDeviceTypes() [deviceTypeDropDown == 0
|
|
? 0 : deviceTypeDropDown->getSelectedId() - 1];
|
|
|
|
if (type != 0)
|
|
{
|
|
AudioIODeviceType::DeviceSetupDetails details;
|
|
details.manager = &deviceManager;
|
|
details.minNumInputChannels = minInputChannels;
|
|
details.maxNumInputChannels = maxInputChannels;
|
|
details.minNumOutputChannels = minOutputChannels;
|
|
details.maxNumOutputChannels = maxOutputChannels;
|
|
details.useStereoPairs = showChannelsAsStereoPairs;
|
|
|
|
audioDeviceSettingsComp = new AudioDeviceSettingsPanel (type, details, hideAdvancedOptionsWithButton);
|
|
|
|
if (audioDeviceSettingsComp != 0)
|
|
{
|
|
addAndMakeVisible (audioDeviceSettingsComp);
|
|
audioDeviceSettingsComp->resized();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (midiInputsList != 0)
|
|
{
|
|
midiInputsList->updateContent();
|
|
midiInputsList->repaint();
|
|
}
|
|
|
|
if (midiOutputSelector != 0)
|
|
{
|
|
midiOutputSelector->clear();
|
|
|
|
const StringArray midiOuts (MidiOutput::getDevices());
|
|
|
|
midiOutputSelector->addItem (TRANS("<< none >>"), -1);
|
|
midiOutputSelector->addSeparator();
|
|
|
|
for (int i = 0; i < midiOuts.size(); ++i)
|
|
midiOutputSelector->addItem (midiOuts[i], i + 1);
|
|
|
|
int current = -1;
|
|
|
|
if (deviceManager.getDefaultMidiOutput() != 0)
|
|
current = 1 + midiOuts.indexOf (deviceManager.getDefaultMidiOutputName());
|
|
|
|
midiOutputSelector->setSelectedId (current, true);
|
|
}
|
|
|
|
resized();
|
|
}
|
|
|
|
|
|
END_JUCE_NAMESPACE
|