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.

1120 lines
39KB

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