The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1128 lines
39KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. BEGIN_JUCE_NAMESPACE
  19. //==============================================================================
  20. class SimpleDeviceManagerInputLevelMeter : public Component,
  21. public Timer
  22. {
  23. public:
  24. SimpleDeviceManagerInputLevelMeter (AudioDeviceManager* const manager_)
  25. : manager (manager_),
  26. level (0)
  27. {
  28. startTimer (50);
  29. manager->enableInputLevelMeasurement (true);
  30. }
  31. ~SimpleDeviceManagerInputLevelMeter()
  32. {
  33. manager->enableInputLevelMeasurement (false);
  34. }
  35. void timerCallback()
  36. {
  37. const float newLevel = (float) manager->getCurrentInputLevel();
  38. if (std::abs (level - newLevel) > 0.005f)
  39. {
  40. level = newLevel;
  41. repaint();
  42. }
  43. }
  44. void paint (Graphics& g)
  45. {
  46. getLookAndFeel().drawLevelMeter (g, getWidth(), getHeight(),
  47. (float) exp (log (level) / 3.0)); // (add a bit of a skew to make the level more obvious)
  48. }
  49. private:
  50. AudioDeviceManager* const manager;
  51. float level;
  52. JUCE_DECLARE_NON_COPYABLE (SimpleDeviceManagerInputLevelMeter);
  53. };
  54. //==============================================================================
  55. class AudioDeviceSelectorComponent::MidiInputSelectorComponentListBox : public ListBox,
  56. public ListBoxModel
  57. {
  58. public:
  59. //==============================================================================
  60. MidiInputSelectorComponentListBox (AudioDeviceManager& deviceManager_,
  61. const String& noItemsMessage_,
  62. const int minNumber_,
  63. const int maxNumber_)
  64. : ListBox (String::empty, nullptr),
  65. deviceManager (deviceManager_),
  66. noItemsMessage (noItemsMessage_),
  67. minNumber (minNumber_),
  68. maxNumber (maxNumber_)
  69. {
  70. items = MidiInput::getDevices();
  71. setModel (this);
  72. setOutlineThickness (1);
  73. }
  74. int getNumRows()
  75. {
  76. return items.size();
  77. }
  78. void paintListBoxItem (int row,
  79. Graphics& g,
  80. int width, int height,
  81. bool rowIsSelected)
  82. {
  83. if (isPositiveAndBelow (row, items.size()))
  84. {
  85. if (rowIsSelected)
  86. g.fillAll (findColour (TextEditor::highlightColourId)
  87. .withMultipliedAlpha (0.3f));
  88. const String item (items [row]);
  89. bool enabled = deviceManager.isMidiInputEnabled (item);
  90. const int x = getTickX();
  91. const float tickW = height * 0.75f;
  92. getLookAndFeel().drawTickBox (g, *this, x - tickW, (height - tickW) / 2, tickW, tickW,
  93. enabled, true, true, false);
  94. g.setFont (height * 0.6f);
  95. g.setColour (findColour (ListBox::textColourId, true).withMultipliedAlpha (enabled ? 1.0f : 0.6f));
  96. g.drawText (item, x, 0, width - x - 2, height, Justification::centredLeft, true);
  97. }
  98. }
  99. void listBoxItemClicked (int row, const MouseEvent& e)
  100. {
  101. selectRow (row);
  102. if (e.x < getTickX())
  103. flipEnablement (row);
  104. }
  105. void listBoxItemDoubleClicked (int row, const MouseEvent&)
  106. {
  107. flipEnablement (row);
  108. }
  109. void returnKeyPressed (int row)
  110. {
  111. flipEnablement (row);
  112. }
  113. void paint (Graphics& g)
  114. {
  115. ListBox::paint (g);
  116. if (items.size() == 0)
  117. {
  118. g.setColour (Colours::grey);
  119. g.setFont (13.0f);
  120. g.drawText (noItemsMessage,
  121. 0, 0, getWidth(), getHeight() / 2,
  122. Justification::centred, true);
  123. }
  124. }
  125. int getBestHeight (const int preferredHeight)
  126. {
  127. const int extra = getOutlineThickness() * 2;
  128. return jmax (getRowHeight() * 2 + extra,
  129. jmin (getRowHeight() * getNumRows() + extra,
  130. preferredHeight));
  131. }
  132. private:
  133. //==============================================================================
  134. AudioDeviceManager& deviceManager;
  135. const String noItemsMessage;
  136. StringArray items;
  137. int minNumber, maxNumber;
  138. void flipEnablement (const int row)
  139. {
  140. if (isPositiveAndBelow (row, items.size()))
  141. {
  142. const String item (items [row]);
  143. deviceManager.setMidiInputEnabled (item, ! deviceManager.isMidiInputEnabled (item));
  144. }
  145. }
  146. int getTickX() const
  147. {
  148. return getRowHeight() + 5;
  149. }
  150. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInputSelectorComponentListBox);
  151. };
  152. //==============================================================================
  153. struct AudioDeviceSetupDetails
  154. {
  155. AudioDeviceManager* manager;
  156. int minNumInputChannels, maxNumInputChannels;
  157. int minNumOutputChannels, maxNumOutputChannels;
  158. bool useStereoPairs;
  159. };
  160. //==============================================================================
  161. class AudioDeviceSettingsPanel : public Component,
  162. public ChangeListener,
  163. public ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug)
  164. public ButtonListener
  165. {
  166. public:
  167. AudioDeviceSettingsPanel (AudioIODeviceType* type_,
  168. AudioDeviceSetupDetails& setup_,
  169. const bool hideAdvancedOptionsWithButton)
  170. : type (type_),
  171. setup (setup_)
  172. {
  173. if (hideAdvancedOptionsWithButton)
  174. {
  175. addAndMakeVisible (showAdvancedSettingsButton = new TextButton (TRANS("Show advanced settings...")));
  176. showAdvancedSettingsButton->addListener (this);
  177. }
  178. type->scanForDevices();
  179. setup.manager->addChangeListener (this);
  180. updateAllControls();
  181. }
  182. ~AudioDeviceSettingsPanel()
  183. {
  184. setup.manager->removeChangeListener (this);
  185. }
  186. void resized()
  187. {
  188. const int lx = proportionOfWidth (0.35f);
  189. const int w = proportionOfWidth (0.4f);
  190. const int h = 24;
  191. const int space = 6;
  192. const int dh = h + space;
  193. int y = 0;
  194. if (outputDeviceDropDown != nullptr)
  195. {
  196. outputDeviceDropDown->setBounds (lx, y, w, h);
  197. if (testButton != nullptr)
  198. testButton->setBounds (proportionOfWidth (0.77f),
  199. outputDeviceDropDown->getY(),
  200. proportionOfWidth (0.18f),
  201. h);
  202. y += dh;
  203. }
  204. if (inputDeviceDropDown != nullptr)
  205. {
  206. inputDeviceDropDown->setBounds (lx, y, w, h);
  207. inputLevelMeter->setBounds (proportionOfWidth (0.77f),
  208. inputDeviceDropDown->getY(),
  209. proportionOfWidth (0.18f),
  210. h);
  211. y += dh;
  212. }
  213. const int maxBoxHeight = 100;//(getHeight() - y - dh * 2) / numBoxes;
  214. if (outputChanList != nullptr)
  215. {
  216. const int bh = outputChanList->getBestHeight (maxBoxHeight);
  217. outputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh);
  218. y += bh + space;
  219. }
  220. if (inputChanList != nullptr)
  221. {
  222. const int bh = inputChanList->getBestHeight (maxBoxHeight);
  223. inputChanList->setBounds (lx, y, proportionOfWidth (0.55f), bh);
  224. y += bh + space;
  225. }
  226. y += space * 2;
  227. if (showAdvancedSettingsButton != nullptr)
  228. {
  229. showAdvancedSettingsButton->changeWidthToFitText (h);
  230. showAdvancedSettingsButton->setTopLeftPosition (lx, y);
  231. }
  232. if (sampleRateDropDown != nullptr)
  233. {
  234. sampleRateDropDown->setVisible (showAdvancedSettingsButton == nullptr
  235. || ! showAdvancedSettingsButton->isVisible());
  236. sampleRateDropDown->setBounds (lx, y, w, h);
  237. y += dh;
  238. }
  239. if (bufferSizeDropDown != nullptr)
  240. {
  241. bufferSizeDropDown->setVisible (showAdvancedSettingsButton == nullptr
  242. || ! showAdvancedSettingsButton->isVisible());
  243. bufferSizeDropDown->setBounds (lx, y, w, h);
  244. y += dh;
  245. }
  246. if (showUIButton != nullptr)
  247. {
  248. showUIButton->setVisible (showAdvancedSettingsButton == nullptr
  249. || ! showAdvancedSettingsButton->isVisible());
  250. showUIButton->changeWidthToFitText (h);
  251. showUIButton->setTopLeftPosition (lx, y);
  252. }
  253. }
  254. void comboBoxChanged (ComboBox* comboBoxThatHasChanged)
  255. {
  256. if (comboBoxThatHasChanged == nullptr)
  257. return;
  258. AudioDeviceManager::AudioDeviceSetup config;
  259. setup.manager->getAudioDeviceSetup (config);
  260. String error;
  261. if (comboBoxThatHasChanged == outputDeviceDropDown
  262. || comboBoxThatHasChanged == inputDeviceDropDown)
  263. {
  264. if (outputDeviceDropDown != nullptr)
  265. config.outputDeviceName = outputDeviceDropDown->getSelectedId() < 0 ? String::empty
  266. : outputDeviceDropDown->getText();
  267. if (inputDeviceDropDown != nullptr)
  268. config.inputDeviceName = inputDeviceDropDown->getSelectedId() < 0 ? String::empty
  269. : inputDeviceDropDown->getText();
  270. if (! type->hasSeparateInputsAndOutputs())
  271. config.inputDeviceName = config.outputDeviceName;
  272. if (comboBoxThatHasChanged == inputDeviceDropDown)
  273. config.useDefaultInputChannels = true;
  274. else
  275. config.useDefaultOutputChannels = true;
  276. error = setup.manager->setAudioDeviceSetup (config, true);
  277. showCorrectDeviceName (inputDeviceDropDown, true);
  278. showCorrectDeviceName (outputDeviceDropDown, false);
  279. updateControlPanelButton();
  280. resized();
  281. }
  282. else if (comboBoxThatHasChanged == sampleRateDropDown)
  283. {
  284. if (sampleRateDropDown->getSelectedId() > 0)
  285. {
  286. config.sampleRate = sampleRateDropDown->getSelectedId();
  287. error = setup.manager->setAudioDeviceSetup (config, true);
  288. }
  289. }
  290. else if (comboBoxThatHasChanged == bufferSizeDropDown)
  291. {
  292. if (bufferSizeDropDown->getSelectedId() > 0)
  293. {
  294. config.bufferSize = bufferSizeDropDown->getSelectedId();
  295. error = setup.manager->setAudioDeviceSetup (config, true);
  296. }
  297. }
  298. if (error.isNotEmpty())
  299. {
  300. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  301. "Error when trying to open audio device!",
  302. error);
  303. }
  304. }
  305. bool showDeviceControlPanel()
  306. {
  307. AudioIODevice* const device = setup.manager->getCurrentAudioDevice();
  308. if (device == nullptr)
  309. return false;
  310. Component modalWindow (String::empty);
  311. modalWindow.setOpaque (true);
  312. modalWindow.addToDesktop (0);
  313. modalWindow.enterModalState();
  314. return device->showControlPanel();
  315. }
  316. void buttonClicked (Button* button)
  317. {
  318. if (button == showAdvancedSettingsButton)
  319. {
  320. showAdvancedSettingsButton->setVisible (false);
  321. resized();
  322. }
  323. else if (button == showUIButton)
  324. {
  325. if (showDeviceControlPanel())
  326. {
  327. setup.manager->closeAudioDevice();
  328. setup.manager->restartLastAudioDevice();
  329. getTopLevelComponent()->toFront (true);
  330. }
  331. }
  332. else if (button == testButton && testButton != nullptr)
  333. {
  334. setup.manager->playTestSound();
  335. }
  336. }
  337. void updateAllControls()
  338. {
  339. updateOutputsComboBox();
  340. updateInputsComboBox();
  341. updateControlPanelButton();
  342. AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice();
  343. if (currentDevice != nullptr)
  344. {
  345. if (setup.maxNumOutputChannels > 0
  346. && setup.minNumOutputChannels < setup.manager->getCurrentAudioDevice()->getOutputChannelNames().size())
  347. {
  348. if (outputChanList == nullptr)
  349. {
  350. addAndMakeVisible (outputChanList
  351. = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioOutputType,
  352. TRANS ("(no audio output channels found)")));
  353. outputChanLabel = new Label (String::empty, TRANS ("active output channels:"));
  354. outputChanLabel->attachToComponent (outputChanList, true);
  355. }
  356. outputChanList->refresh();
  357. }
  358. else
  359. {
  360. outputChanLabel = nullptr;
  361. outputChanList = nullptr;
  362. }
  363. if (setup.maxNumInputChannels > 0
  364. && setup.minNumInputChannels < setup.manager->getCurrentAudioDevice()->getInputChannelNames().size())
  365. {
  366. if (inputChanList == nullptr)
  367. {
  368. addAndMakeVisible (inputChanList
  369. = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioInputType,
  370. TRANS ("(no audio input channels found)")));
  371. inputChanLabel = new Label (String::empty, TRANS ("active input channels:"));
  372. inputChanLabel->attachToComponent (inputChanList, true);
  373. }
  374. inputChanList->refresh();
  375. }
  376. else
  377. {
  378. inputChanLabel = nullptr;
  379. inputChanList = nullptr;
  380. }
  381. updateSampleRateComboBox (currentDevice);
  382. updateBufferSizeComboBox (currentDevice);
  383. }
  384. else
  385. {
  386. jassert (setup.manager->getCurrentAudioDevice() == nullptr); // not the correct device type!
  387. sampleRateLabel = nullptr;
  388. bufferSizeLabel = nullptr;
  389. sampleRateDropDown = nullptr;
  390. bufferSizeDropDown = nullptr;
  391. if (outputDeviceDropDown != nullptr)
  392. outputDeviceDropDown->setSelectedId (-1, true);
  393. if (inputDeviceDropDown != nullptr)
  394. inputDeviceDropDown->setSelectedId (-1, true);
  395. }
  396. resized();
  397. setSize (getWidth(), getLowestY() + 4);
  398. }
  399. void changeListenerCallback (ChangeBroadcaster*)
  400. {
  401. updateAllControls();
  402. }
  403. private:
  404. AudioIODeviceType* const type;
  405. const AudioDeviceSetupDetails setup;
  406. ScopedPointer<ComboBox> outputDeviceDropDown, inputDeviceDropDown, sampleRateDropDown, bufferSizeDropDown;
  407. ScopedPointer<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel;
  408. ScopedPointer<TextButton> testButton;
  409. ScopedPointer<Component> inputLevelMeter;
  410. ScopedPointer<TextButton> showUIButton, showAdvancedSettingsButton;
  411. void showCorrectDeviceName (ComboBox* const box, const bool isInput)
  412. {
  413. if (box != nullptr)
  414. {
  415. AudioIODevice* const currentDevice = dynamic_cast <AudioIODevice*> (setup.manager->getCurrentAudioDevice());
  416. const int index = type->getIndexOfDevice (currentDevice, isInput);
  417. box->setSelectedId (index + 1, true);
  418. if (testButton != nullptr && ! isInput)
  419. testButton->setEnabled (index >= 0);
  420. }
  421. }
  422. void addNamesToDeviceBox (ComboBox& combo, bool isInputs)
  423. {
  424. const StringArray devs (type->getDeviceNames (isInputs));
  425. combo.clear (true);
  426. for (int i = 0; i < devs.size(); ++i)
  427. combo.addItem (devs[i], i + 1);
  428. combo.addItem (TRANS("<< none >>"), -1);
  429. combo.setSelectedId (-1, true);
  430. }
  431. int getLowestY() const
  432. {
  433. int y = 0;
  434. for (int i = getNumChildComponents(); --i >= 0;)
  435. y = jmax (y, getChildComponent (i)->getBottom());
  436. return y;
  437. }
  438. void updateControlPanelButton()
  439. {
  440. AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice();
  441. showUIButton = nullptr;
  442. if (currentDevice != nullptr && currentDevice->hasControlPanel())
  443. {
  444. addAndMakeVisible (showUIButton = new TextButton (TRANS ("show this device's control panel"),
  445. TRANS ("opens the device's own control panel")));
  446. showUIButton->addListener (this);
  447. }
  448. resized();
  449. }
  450. void updateOutputsComboBox()
  451. {
  452. if (setup.maxNumOutputChannels > 0 || ! type->hasSeparateInputsAndOutputs())
  453. {
  454. if (outputDeviceDropDown == nullptr)
  455. {
  456. outputDeviceDropDown = new ComboBox (String::empty);
  457. outputDeviceDropDown->addListener (this);
  458. addAndMakeVisible (outputDeviceDropDown);
  459. outputDeviceLabel = new Label (String::empty,
  460. type->hasSeparateInputsAndOutputs() ? TRANS ("output:")
  461. : TRANS ("device:"));
  462. outputDeviceLabel->attachToComponent (outputDeviceDropDown, true);
  463. if (setup.maxNumOutputChannels > 0)
  464. {
  465. addAndMakeVisible (testButton = new TextButton (TRANS ("Test")));
  466. testButton->addListener (this);
  467. }
  468. }
  469. addNamesToDeviceBox (*outputDeviceDropDown, false);
  470. }
  471. showCorrectDeviceName (outputDeviceDropDown, false);
  472. }
  473. void updateInputsComboBox()
  474. {
  475. if (setup.maxNumInputChannels > 0 && type->hasSeparateInputsAndOutputs())
  476. {
  477. if (inputDeviceDropDown == nullptr)
  478. {
  479. inputDeviceDropDown = new ComboBox (String::empty);
  480. inputDeviceDropDown->addListener (this);
  481. addAndMakeVisible (inputDeviceDropDown);
  482. inputDeviceLabel = new Label (String::empty, TRANS ("input:"));
  483. inputDeviceLabel->attachToComponent (inputDeviceDropDown, true);
  484. addAndMakeVisible (inputLevelMeter
  485. = new SimpleDeviceManagerInputLevelMeter (setup.manager));
  486. }
  487. addNamesToDeviceBox (*inputDeviceDropDown, true);
  488. }
  489. showCorrectDeviceName (inputDeviceDropDown, true);
  490. }
  491. void updateSampleRateComboBox (AudioIODevice* currentDevice)
  492. {
  493. if (sampleRateDropDown == nullptr)
  494. {
  495. addAndMakeVisible (sampleRateDropDown = new ComboBox (String::empty));
  496. sampleRateLabel = new Label (String::empty, TRANS ("sample rate:"));
  497. sampleRateLabel->attachToComponent (sampleRateDropDown, true);
  498. }
  499. else
  500. {
  501. sampleRateDropDown->clear();
  502. sampleRateDropDown->removeListener (this);
  503. }
  504. const int numRates = currentDevice->getNumSampleRates();
  505. for (int i = 0; i < numRates; ++i)
  506. {
  507. const int rate = roundToInt (currentDevice->getSampleRate (i));
  508. sampleRateDropDown->addItem (String (rate) + " Hz", rate);
  509. }
  510. sampleRateDropDown->setSelectedId (roundToInt (currentDevice->getCurrentSampleRate()), true);
  511. sampleRateDropDown->addListener (this);
  512. }
  513. void updateBufferSizeComboBox (AudioIODevice* currentDevice)
  514. {
  515. if (bufferSizeDropDown == nullptr)
  516. {
  517. addAndMakeVisible (bufferSizeDropDown = new ComboBox (String::empty));
  518. bufferSizeLabel = new Label (String::empty, TRANS ("audio buffer size:"));
  519. bufferSizeLabel->attachToComponent (bufferSizeDropDown, true);
  520. }
  521. else
  522. {
  523. bufferSizeDropDown->clear();
  524. bufferSizeDropDown->removeListener (this);
  525. }
  526. const int numBufferSizes = currentDevice->getNumBufferSizesAvailable();
  527. double currentRate = currentDevice->getCurrentSampleRate();
  528. if (currentRate == 0)
  529. currentRate = 48000.0;
  530. for (int i = 0; i < numBufferSizes; ++i)
  531. {
  532. const int bs = currentDevice->getBufferSizeSamples (i);
  533. bufferSizeDropDown->addItem (String (bs)
  534. + " samples ("
  535. + String (bs * 1000.0 / currentRate, 1)
  536. + " ms)",
  537. bs);
  538. }
  539. bufferSizeDropDown->setSelectedId (currentDevice->getCurrentBufferSizeSamples(), true);
  540. bufferSizeDropDown->addListener (this);
  541. }
  542. public:
  543. //==============================================================================
  544. class ChannelSelectorListBox : public ListBox,
  545. public ListBoxModel
  546. {
  547. public:
  548. enum BoxType
  549. {
  550. audioInputType,
  551. audioOutputType
  552. };
  553. //==============================================================================
  554. ChannelSelectorListBox (const AudioDeviceSetupDetails& setup_,
  555. const BoxType type_,
  556. const String& noItemsMessage_)
  557. : ListBox (String::empty, nullptr),
  558. setup (setup_),
  559. type (type_),
  560. noItemsMessage (noItemsMessage_)
  561. {
  562. refresh();
  563. setModel (this);
  564. setOutlineThickness (1);
  565. }
  566. void refresh()
  567. {
  568. items.clear();
  569. AudioIODevice* const currentDevice = setup.manager->getCurrentAudioDevice();
  570. if (currentDevice != nullptr)
  571. {
  572. if (type == audioInputType)
  573. items = currentDevice->getInputChannelNames();
  574. else if (type == audioOutputType)
  575. items = currentDevice->getOutputChannelNames();
  576. if (setup.useStereoPairs)
  577. {
  578. StringArray pairs;
  579. for (int i = 0; i < items.size(); i += 2)
  580. {
  581. const String name (items[i]);
  582. const String name2 (items[i + 1]);
  583. String commonBit;
  584. for (int j = 0; j < name.length(); ++j)
  585. if (name.substring (0, j).equalsIgnoreCase (name2.substring (0, j)))
  586. commonBit = name.substring (0, j);
  587. // Make sure we only split the name at a space, because otherwise, things
  588. // like "input 11" + "input 12" would become "input 11 + 2"
  589. while (commonBit.isNotEmpty() && ! CharacterFunctions::isWhitespace (commonBit.getLastCharacter()))
  590. commonBit = commonBit.dropLastCharacters (1);
  591. pairs.add (name.trim() + " + " + name2.substring (commonBit.length()).trim());
  592. }
  593. items = pairs;
  594. }
  595. }
  596. updateContent();
  597. repaint();
  598. }
  599. int getNumRows()
  600. {
  601. return items.size();
  602. }
  603. void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected)
  604. {
  605. if (isPositiveAndBelow (row, items.size()))
  606. {
  607. if (rowIsSelected)
  608. g.fillAll (findColour (TextEditor::highlightColourId)
  609. .withMultipliedAlpha (0.3f));
  610. const String item (items [row]);
  611. bool enabled = false;
  612. AudioDeviceManager::AudioDeviceSetup config;
  613. setup.manager->getAudioDeviceSetup (config);
  614. if (setup.useStereoPairs)
  615. {
  616. if (type == audioInputType)
  617. enabled = config.inputChannels [row * 2] || config.inputChannels [row * 2 + 1];
  618. else if (type == audioOutputType)
  619. enabled = config.outputChannels [row * 2] || config.outputChannels [row * 2 + 1];
  620. }
  621. else
  622. {
  623. if (type == audioInputType)
  624. enabled = config.inputChannels [row];
  625. else if (type == audioOutputType)
  626. enabled = config.outputChannels [row];
  627. }
  628. const int x = getTickX();
  629. const float tickW = height * 0.75f;
  630. getLookAndFeel().drawTickBox (g, *this, x - tickW, (height - tickW) / 2, tickW, tickW,
  631. enabled, true, true, false);
  632. g.setFont (height * 0.6f);
  633. g.setColour (findColour (ListBox::textColourId, true).withMultipliedAlpha (enabled ? 1.0f : 0.6f));
  634. g.drawText (item, x, 0, width - x - 2, height, Justification::centredLeft, true);
  635. }
  636. }
  637. void listBoxItemClicked (int row, const MouseEvent& e)
  638. {
  639. selectRow (row);
  640. if (e.x < getTickX())
  641. flipEnablement (row);
  642. }
  643. void listBoxItemDoubleClicked (int row, const MouseEvent&)
  644. {
  645. flipEnablement (row);
  646. }
  647. void returnKeyPressed (int row)
  648. {
  649. flipEnablement (row);
  650. }
  651. void paint (Graphics& g)
  652. {
  653. ListBox::paint (g);
  654. if (items.size() == 0)
  655. {
  656. g.setColour (Colours::grey);
  657. g.setFont (13.0f);
  658. g.drawText (noItemsMessage,
  659. 0, 0, getWidth(), getHeight() / 2,
  660. Justification::centred, true);
  661. }
  662. }
  663. int getBestHeight (int maxHeight)
  664. {
  665. return getRowHeight() * jlimit (2, jmax (2, maxHeight / getRowHeight()),
  666. getNumRows())
  667. + getOutlineThickness() * 2;
  668. }
  669. private:
  670. //==============================================================================
  671. const AudioDeviceSetupDetails setup;
  672. const BoxType type;
  673. const String noItemsMessage;
  674. StringArray items;
  675. void flipEnablement (const int row)
  676. {
  677. jassert (type == audioInputType || type == audioOutputType);
  678. if (isPositiveAndBelow (row, items.size()))
  679. {
  680. AudioDeviceManager::AudioDeviceSetup config;
  681. setup.manager->getAudioDeviceSetup (config);
  682. if (setup.useStereoPairs)
  683. {
  684. BigInteger bits;
  685. BigInteger& original = (type == audioInputType ? config.inputChannels
  686. : config.outputChannels);
  687. int i;
  688. for (i = 0; i < 256; i += 2)
  689. bits.setBit (i / 2, original [i] || original [i + 1]);
  690. if (type == audioInputType)
  691. {
  692. config.useDefaultInputChannels = false;
  693. flipBit (bits, row, setup.minNumInputChannels / 2, setup.maxNumInputChannels / 2);
  694. }
  695. else
  696. {
  697. config.useDefaultOutputChannels = false;
  698. flipBit (bits, row, setup.minNumOutputChannels / 2, setup.maxNumOutputChannels / 2);
  699. }
  700. for (i = 0; i < 256; ++i)
  701. original.setBit (i, bits [i / 2]);
  702. }
  703. else
  704. {
  705. if (type == audioInputType)
  706. {
  707. config.useDefaultInputChannels = false;
  708. flipBit (config.inputChannels, row, setup.minNumInputChannels, setup.maxNumInputChannels);
  709. }
  710. else
  711. {
  712. config.useDefaultOutputChannels = false;
  713. flipBit (config.outputChannels, row, setup.minNumOutputChannels, setup.maxNumOutputChannels);
  714. }
  715. }
  716. String error (setup.manager->setAudioDeviceSetup (config, true));
  717. if (! error.isEmpty())
  718. {
  719. //xxx
  720. }
  721. }
  722. }
  723. static void flipBit (BigInteger& chans, int index, int minNumber, int maxNumber)
  724. {
  725. const int numActive = chans.countNumberOfSetBits();
  726. if (chans [index])
  727. {
  728. if (numActive > minNumber)
  729. chans.setBit (index, false);
  730. }
  731. else
  732. {
  733. if (numActive >= maxNumber)
  734. {
  735. const int firstActiveChan = chans.findNextSetBit();
  736. chans.setBit (index > firstActiveChan
  737. ? firstActiveChan : chans.getHighestBit(),
  738. false);
  739. }
  740. chans.setBit (index, true);
  741. }
  742. }
  743. int getTickX() const
  744. {
  745. return getRowHeight() + 5;
  746. }
  747. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelSelectorListBox);
  748. };
  749. private:
  750. ScopedPointer<ChannelSelectorListBox> inputChanList, outputChanList;
  751. JUCE_DECLARE_NON_COPYABLE (AudioDeviceSettingsPanel);
  752. };
  753. //==============================================================================
  754. AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager& deviceManager_,
  755. const int minInputChannels_,
  756. const int maxInputChannels_,
  757. const int minOutputChannels_,
  758. const int maxOutputChannels_,
  759. const bool showMidiInputOptions,
  760. const bool showMidiOutputSelector,
  761. const bool showChannelsAsStereoPairs_,
  762. const bool hideAdvancedOptionsWithButton_)
  763. : deviceManager (deviceManager_),
  764. minOutputChannels (minOutputChannels_),
  765. maxOutputChannels (maxOutputChannels_),
  766. minInputChannels (minInputChannels_),
  767. maxInputChannels (maxInputChannels_),
  768. showChannelsAsStereoPairs (showChannelsAsStereoPairs_),
  769. hideAdvancedOptionsWithButton (hideAdvancedOptionsWithButton_)
  770. {
  771. jassert (minOutputChannels >= 0 && minOutputChannels <= maxOutputChannels);
  772. jassert (minInputChannels >= 0 && minInputChannels <= maxInputChannels);
  773. const OwnedArray<AudioIODeviceType>& types = deviceManager_.getAvailableDeviceTypes();
  774. if (types.size() > 1)
  775. {
  776. deviceTypeDropDown = new ComboBox (String::empty);
  777. for (int i = 0; i < types.size(); ++i)
  778. deviceTypeDropDown->addItem (types.getUnchecked(i)->getTypeName(), i + 1);
  779. addAndMakeVisible (deviceTypeDropDown);
  780. deviceTypeDropDown->addListener (this);
  781. deviceTypeDropDownLabel = new Label (String::empty, TRANS ("audio device type:"));
  782. deviceTypeDropDownLabel->setJustificationType (Justification::centredRight);
  783. deviceTypeDropDownLabel->attachToComponent (deviceTypeDropDown, true);
  784. }
  785. if (showMidiInputOptions)
  786. {
  787. addAndMakeVisible (midiInputsList
  788. = new MidiInputSelectorComponentListBox (deviceManager,
  789. TRANS("(no midi inputs available)"),
  790. 0, 0));
  791. midiInputsLabel = new Label (String::empty, TRANS ("active midi inputs:"));
  792. midiInputsLabel->setJustificationType (Justification::topRight);
  793. midiInputsLabel->attachToComponent (midiInputsList, true);
  794. }
  795. else
  796. {
  797. midiInputsList = nullptr;
  798. midiInputsLabel = nullptr;
  799. }
  800. if (showMidiOutputSelector)
  801. {
  802. addAndMakeVisible (midiOutputSelector = new ComboBox (String::empty));
  803. midiOutputSelector->addListener (this);
  804. midiOutputLabel = new Label ("lm", TRANS("Midi Output:"));
  805. midiOutputLabel->attachToComponent (midiOutputSelector, true);
  806. }
  807. else
  808. {
  809. midiOutputSelector = nullptr;
  810. midiOutputLabel = nullptr;
  811. }
  812. deviceManager_.addChangeListener (this);
  813. updateAllControls();
  814. }
  815. AudioDeviceSelectorComponent::~AudioDeviceSelectorComponent()
  816. {
  817. deviceManager.removeChangeListener (this);
  818. }
  819. void AudioDeviceSelectorComponent::resized()
  820. {
  821. const int lx = proportionOfWidth (0.35f);
  822. const int w = proportionOfWidth (0.4f);
  823. const int h = 24;
  824. const int space = 6;
  825. const int dh = h + space;
  826. int y = 15;
  827. if (deviceTypeDropDown != nullptr)
  828. {
  829. deviceTypeDropDown->setBounds (lx, y, proportionOfWidth (0.3f), h);
  830. y += dh + space * 2;
  831. }
  832. if (audioDeviceSettingsComp != nullptr)
  833. {
  834. audioDeviceSettingsComp->setBounds (0, y, getWidth(), audioDeviceSettingsComp->getHeight());
  835. y += audioDeviceSettingsComp->getHeight() + space;
  836. }
  837. if (midiInputsList != nullptr)
  838. {
  839. const int bh = midiInputsList->getBestHeight (jmin (h * 8, getHeight() - y - space - h));
  840. midiInputsList->setBounds (lx, y, w, bh);
  841. y += bh + space;
  842. }
  843. if (midiOutputSelector != nullptr)
  844. midiOutputSelector->setBounds (lx, y, w, h);
  845. }
  846. void AudioDeviceSelectorComponent::childBoundsChanged (Component* child)
  847. {
  848. if (child == audioDeviceSettingsComp)
  849. resized();
  850. }
  851. void AudioDeviceSelectorComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
  852. {
  853. if (comboBoxThatHasChanged == deviceTypeDropDown)
  854. {
  855. AudioIODeviceType* const type = deviceManager.getAvailableDeviceTypes() [deviceTypeDropDown->getSelectedId() - 1];
  856. if (type != nullptr)
  857. {
  858. audioDeviceSettingsComp = nullptr;
  859. deviceManager.setCurrentAudioDeviceType (type->getTypeName(), true);
  860. updateAllControls(); // needed in case the type hasn't actally changed
  861. }
  862. }
  863. else if (comboBoxThatHasChanged == midiOutputSelector)
  864. {
  865. deviceManager.setDefaultMidiOutput (midiOutputSelector->getText());
  866. }
  867. }
  868. void AudioDeviceSelectorComponent::changeListenerCallback (ChangeBroadcaster*)
  869. {
  870. updateAllControls();
  871. }
  872. void AudioDeviceSelectorComponent::updateAllControls()
  873. {
  874. if (deviceTypeDropDown != nullptr)
  875. deviceTypeDropDown->setText (deviceManager.getCurrentAudioDeviceType(), false);
  876. if (audioDeviceSettingsComp == nullptr
  877. || audioDeviceSettingsCompType != deviceManager.getCurrentAudioDeviceType())
  878. {
  879. audioDeviceSettingsCompType = deviceManager.getCurrentAudioDeviceType();
  880. audioDeviceSettingsComp = nullptr;
  881. AudioIODeviceType* const type
  882. = deviceManager.getAvailableDeviceTypes() [deviceTypeDropDown == nullptr
  883. ? 0 : deviceTypeDropDown->getSelectedId() - 1];
  884. if (type != nullptr)
  885. {
  886. AudioDeviceSetupDetails details;
  887. details.manager = &deviceManager;
  888. details.minNumInputChannels = minInputChannels;
  889. details.maxNumInputChannels = maxInputChannels;
  890. details.minNumOutputChannels = minOutputChannels;
  891. details.maxNumOutputChannels = maxOutputChannels;
  892. details.useStereoPairs = showChannelsAsStereoPairs;
  893. audioDeviceSettingsComp = new AudioDeviceSettingsPanel (type, details, hideAdvancedOptionsWithButton);
  894. if (audioDeviceSettingsComp != nullptr)
  895. {
  896. addAndMakeVisible (audioDeviceSettingsComp);
  897. audioDeviceSettingsComp->resized();
  898. }
  899. }
  900. }
  901. if (midiInputsList != nullptr)
  902. {
  903. midiInputsList->updateContent();
  904. midiInputsList->repaint();
  905. }
  906. if (midiOutputSelector != nullptr)
  907. {
  908. midiOutputSelector->clear();
  909. const StringArray midiOuts (MidiOutput::getDevices());
  910. midiOutputSelector->addItem (TRANS("<< none >>"), -1);
  911. midiOutputSelector->addSeparator();
  912. for (int i = 0; i < midiOuts.size(); ++i)
  913. midiOutputSelector->addItem (midiOuts[i], i + 1);
  914. int current = -1;
  915. if (deviceManager.getDefaultMidiOutput() != nullptr)
  916. current = 1 + midiOuts.indexOf (deviceManager.getDefaultMidiOutputName());
  917. midiOutputSelector->setSelectedId (current, true);
  918. }
  919. resized();
  920. }
  921. END_JUCE_NAMESPACE