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.

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