/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-11 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. ============================================================================== */ BEGIN_JUCE_NAMESPACE //============================================================================== 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 (std::abs (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; JUCE_DECLARE_NON_COPYABLE (SimpleDeviceManagerInputLevelMeter); }; //============================================================================== class AudioDeviceSelectorComponent::MidiInputSelectorComponentListBox : public ListBox, public ListBoxModel { public: //============================================================================== MidiInputSelectorComponentListBox (AudioDeviceManager& deviceManager_, const String& noItemsMessage_, const int minNumber_, const int maxNumber_) : ListBox (String::empty, nullptr), deviceManager (deviceManager_), noItemsMessage (noItemsMessage_), minNumber (minNumber_), maxNumber (maxNumber_) { items = MidiInput::getDevices(); setModel (this); setOutlineThickness (1); } int getNumRows() { return items.size(); } void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) { if (isPositiveAndBelow (row, 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)); } private: //============================================================================== AudioDeviceManager& deviceManager; const String noItemsMessage; StringArray items; int minNumber, maxNumber; void flipEnablement (const int row) { if (isPositiveAndBelow (row, items.size())) { const String item (items [row]); deviceManager.setMidiInputEnabled (item, ! deviceManager.isMidiInputEnabled (item)); } } int getTickX() const { return getRowHeight() + 5; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInputSelectorComponentListBox); }; //============================================================================== struct AudioDeviceSetupDetails { AudioDeviceManager* manager; int minNumInputChannels, maxNumInputChannels; int minNumOutputChannels, maxNumOutputChannels; bool useStereoPairs; }; //============================================================================== class AudioDeviceSettingsPanel : public Component, public ChangeListener, public ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug) public ButtonListener { public: AudioDeviceSettingsPanel (AudioIODeviceType* type_, AudioDeviceSetupDetails& setup_, const bool hideAdvancedOptionsWithButton) : type (type_), setup (setup_) { if (hideAdvancedOptionsWithButton) { addAndMakeVisible (showAdvancedSettingsButton = new TextButton (TRANS("Show advanced settings..."))); showAdvancedSettingsButton->addListener (this); } type->scanForDevices(); setup.manager->addChangeListener (this); updateAllControls(); } ~AudioDeviceSettingsPanel() { setup.manager->removeChangeListener (this); } 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 != nullptr) { outputDeviceDropDown->setBounds (lx, y, w, h); if (testButton != nullptr) testButton->setBounds (proportionOfWidth (0.77f), outputDeviceDropDown->getY(), proportionOfWidth (0.18f), h); y += dh; } if (inputDeviceDropDown != nullptr) { 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 != nullptr) { const int bh = outputChanList->getBestHeight (maxBoxHeight); outputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh); y += bh + space; } if (inputChanList != nullptr) { const int bh = inputChanList->getBestHeight (maxBoxHeight); inputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh); y += bh + space; } y += space * 2; if (showAdvancedSettingsButton != nullptr) { showAdvancedSettingsButton->changeWidthToFitText (h); showAdvancedSettingsButton->setTopLeftPosition (lx, y); } if (sampleRateDropDown != nullptr) { sampleRateDropDown->setVisible (showAdvancedSettingsButton == nullptr || ! showAdvancedSettingsButton->isVisible()); sampleRateDropDown->setBounds (lx, y, w, h); y += dh; } if (bufferSizeDropDown != nullptr) { bufferSizeDropDown->setVisible (showAdvancedSettingsButton == nullptr || ! showAdvancedSettingsButton->isVisible()); bufferSizeDropDown->setBounds (lx, y, w, h); y += dh; } if (showUIButton != nullptr) { showUIButton->setVisible (showAdvancedSettingsButton == nullptr || ! showAdvancedSettingsButton->isVisible()); showUIButton->changeWidthToFitText (h); showUIButton->setTopLeftPosition (lx, y); } } void comboBoxChanged (ComboBox* comboBoxThatHasChanged) { if (comboBoxThatHasChanged == nullptr) return; AudioDeviceManager::AudioDeviceSetup config; setup.manager->getAudioDeviceSetup (config); String error; if (comboBoxThatHasChanged == outputDeviceDropDown || comboBoxThatHasChanged == inputDeviceDropDown) { if (outputDeviceDropDown != nullptr) config.outputDeviceName = outputDeviceDropDown->getSelectedId() < 0 ? String::empty : outputDeviceDropDown->getText(); if (inputDeviceDropDown != nullptr) 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::showMessageBoxAsync (AlertWindow::WarningIcon, "Error when trying to open audio device!", error); } } bool showDeviceControlPanel() { AudioIODevice* const device = setup.manager->getCurrentAudioDevice(); if (device == nullptr) return false; Component modalWindow (String::empty); modalWindow.setOpaque (true); modalWindow.addToDesktop (0); modalWindow.enterModalState(); return device->showControlPanel(); } void buttonClicked (Button* button) { if (button == showAdvancedSettingsButton) { showAdvancedSettingsButton->setVisible (false); resized(); } else if (button == showUIButton) { if (showDeviceControlPanel()) { setup.manager->closeAudioDevice(); setup.manager->restartLastAudioDevice(); getTopLevelComponent()->toFront (true); } } else if (button == testButton && testButton != nullptr) { setup.manager->playTestSound(); } } void updateAllControls() { updateOutputsComboBox(); updateInputsComboBox(); updateControlPanelButton(); AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice(); if (currentDevice != nullptr) { if (setup.maxNumOutputChannels > 0 && setup.minNumOutputChannels < setup.manager->getCurrentAudioDevice()->getOutputChannelNames().size()) { if (outputChanList == nullptr) { 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 { outputChanLabel = nullptr; outputChanList = nullptr; } if (setup.maxNumInputChannels > 0 && setup.minNumInputChannels < setup.manager->getCurrentAudioDevice()->getInputChannelNames().size()) { if (inputChanList == nullptr) { 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 { inputChanLabel = nullptr; inputChanList = nullptr; } updateSampleRateComboBox (currentDevice); updateBufferSizeComboBox (currentDevice); } else { jassert (setup.manager->getCurrentAudioDevice() == nullptr); // not the correct device type! sampleRateLabel = nullptr; bufferSizeLabel = nullptr; sampleRateDropDown = nullptr; bufferSizeDropDown = nullptr; if (outputDeviceDropDown != nullptr) outputDeviceDropDown->setSelectedId (-1, true); if (inputDeviceDropDown != nullptr) inputDeviceDropDown->setSelectedId (-1, true); } resized(); setSize (getWidth(), getLowestY() + 4); } void changeListenerCallback (ChangeBroadcaster*) { updateAllControls(); } private: AudioIODeviceType* const type; const AudioDeviceSetupDetails setup; ScopedPointer outputDeviceDropDown, inputDeviceDropDown, sampleRateDropDown, bufferSizeDropDown; ScopedPointer