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
40KB

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