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.

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