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.

1122 lines
39KB

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