JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_AudioDeviceSelectorComponent.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
30 public Timer
31{
33 {
34 startTimerHz (20);
35 inputLevelGetter = manager.getInputLevelGetter();
36 }
37
38 void timerCallback() override
39 {
40 if (isShowing())
41 {
42 auto newLevel = (float) inputLevelGetter->getCurrentLevel();
43
44 if (std::abs (level - newLevel) > 0.005f)
45 {
46 level = newLevel;
47 repaint();
48 }
49 }
50 else
51 {
52 level = 0;
53 }
54 }
55
56 void paint (Graphics& g) override
57 {
58 // (add a bit of a skew to make the level more obvious)
59 getLookAndFeel().drawLevelMeter (g, getWidth(), getHeight(),
60 (float) std::exp (std::log (level) / 3.0));
61 }
62
63 AudioDeviceManager& manager;
65 float level = 0;
66
68};
69
70static void drawTextLayout (Graphics& g, Component& owner, StringRef text, const Rectangle<int>& textBounds, bool enabled)
71{
72 const auto textColour = owner.findColour (ListBox::textColourId, true).withMultipliedAlpha (enabled ? 1.0f : 0.6f);
73
74 AttributedString attributedString { text };
75 attributedString.setColour (textColour);
76 attributedString.setFont ((float) textBounds.getHeight() * 0.6f);
79
80 TextLayout textLayout;
81 textLayout.createLayout (attributedString,
82 (float) textBounds.getWidth(),
83 (float) textBounds.getHeight());
84 textLayout.draw (g, textBounds.toFloat());
85}
86
87
88//==============================================================================
90 private ListBoxModel
91{
92public:
94 : ListBox ({}, nullptr),
95 deviceManager (dm),
96 noItemsMessage (noItems)
97 {
98 updateDevices();
99 setModel (this);
101 }
102
103 void updateDevices()
104 {
106 }
107
108 int getNumRows() override
109 {
110 return items.size();
111 }
112
113 void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) override
114 {
115 if (isPositiveAndBelow (row, items.size()))
116 {
117 if (rowIsSelected)
119 .withMultipliedAlpha (0.3f));
120
121 auto item = items[row];
122 bool enabled = deviceManager.isMidiInputDeviceEnabled (item.identifier);
123
124 auto x = getTickX();
125 auto tickW = (float) height * 0.75f;
126
127 getLookAndFeel().drawTickBox (g, *this, (float) x - tickW, ((float) height - tickW) * 0.5f, tickW, tickW,
128 enabled, true, true, false);
129
130 drawTextLayout (g, *this, item.name, { x + 5, 0, width - x - 5, height }, enabled);
131 }
132 }
133
134 void listBoxItemClicked (int row, const MouseEvent& e) override
135 {
136 selectRow (row);
137
138 if (e.x < getTickX())
139 flipEnablement (row);
140 }
141
142 void listBoxItemDoubleClicked (int row, const MouseEvent&) override
143 {
144 flipEnablement (row);
145 }
146
147 void returnKeyPressed (int row) override
148 {
149 flipEnablement (row);
150 }
151
152 void paint (Graphics& g) override
153 {
154 ListBox::paint (g);
155
156 if (items.isEmpty())
157 {
158 g.setColour (Colours::grey);
159 g.setFont (0.5f * (float) getRowHeight());
160 g.drawText (noItemsMessage,
161 0, 0, getWidth(), getHeight() / 2,
163 }
164 }
165
166 int getBestHeight (int preferredHeight)
167 {
168 auto extra = getOutlineThickness() * 2;
169
170 return jmax (getRowHeight() * 2 + extra,
172 preferredHeight));
173 }
174
175private:
176 //==============================================================================
177 AudioDeviceManager& deviceManager;
178 const String noItemsMessage;
180
181 void flipEnablement (const int row)
182 {
183 if (isPositiveAndBelow (row, items.size()))
184 {
185 auto identifier = items[row].identifier;
186 deviceManager.setMidiInputDeviceEnabled (identifier, ! deviceManager.isMidiInputDeviceEnabled (identifier));
187 }
188 }
189
190 int getTickX() const
191 {
192 return getRowHeight();
193 }
194
195 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInputSelectorComponentListBox)
196};
197
198
199//==============================================================================
201{
202 AudioDeviceManager* manager;
203 int minNumInputChannels, maxNumInputChannels;
204 int minNumOutputChannels, maxNumOutputChannels;
205 bool useStereoPairs;
206};
207
208static String getNoDeviceString() { return "<< " + TRANS ("none") + " >>"; }
209
210//==============================================================================
212 private ChangeListener
213{
214public:
216 : deviceManager (dm)
217 {
218 deviceManager.addChangeListener (this);
219 selector.onChange = [&]
220 {
221 const auto selectedId = selector.getSelectedId();
222 jassert (selectedId != 0);
223
224 const auto deviceId = selectedId == -1
225 ? String{}
227 deviceManager.setDefaultMidiOutputDevice (deviceId);
228 };
229
230 updateListOfDevices();
231 addAndMakeVisible (selector);
232 }
233
234 ~MidiOutputSelector() final
235 {
236 deviceManager.removeChangeListener (this);
237 }
238
239 void resized() final { selector.setBounds (getLocalBounds()); }
240
241private:
242 void updateListOfDevices()
243 {
244 selector.clear();
245
247
248 selector.addItem (getNoDeviceString(), -1);
249 selector.setSelectedId (-1, dontSendNotification);
250 selector.addSeparator();
251
252 for (auto [id, midiOutput] : enumerate (midiOutputs, 1))
253 {
254 selector.addItem (midiOutput.name, id);
255
256 if (midiOutput.identifier == deviceManager.getDefaultMidiOutputIdentifier())
257 selector.setSelectedId (id, dontSendNotification);
258 }
259 }
260
261 void changeListenerCallback (ChangeBroadcaster*) final { updateListOfDevices(); }
262
263 ComboBox selector;
264 AudioDeviceManager& deviceManager;
265};
266
267//==============================================================================
269 private ChangeListener
270{
271public:
273 const bool hideAdvancedOptionsWithButton,
275 : type (t), setup (setupDetails), parent (p)
276 {
277 if (hideAdvancedOptionsWithButton)
278 {
279 showAdvancedSettingsButton = std::make_unique <TextButton> (TRANS ("Show advanced settings..."));
280 addAndMakeVisible (showAdvancedSettingsButton.get());
281 showAdvancedSettingsButton->setClickingTogglesState (true);
282 showAdvancedSettingsButton->onClick = [this] { toggleAdvancedSettings(); };
283 }
284
285 type.scanForDevices();
286
287 setup.manager->addChangeListener (this);
288
289 updateAllControls();
290 }
291
293 {
294 setup.manager->removeChangeListener (this);
295 }
296
297 void resized() override
298 {
299 Rectangle<int> r (proportionOfWidth (0.35f), 0, proportionOfWidth (0.6f), 3000);
300
301 const int maxListBoxHeight = 100;
302 const int h = parent.getItemHeight();
303 const int space = h / 4;
304
305 if (outputDeviceDropDown != nullptr)
306 {
307 auto row = r.removeFromTop (h);
308
309 if (testButton != nullptr)
310 {
311 testButton->changeWidthToFitText (h);
312 testButton->setBounds (row.removeFromRight (testButton->getWidth()));
313 row.removeFromRight (space);
314 }
315
316 outputDeviceDropDown->setBounds (row);
317 r.removeFromTop (space);
318 }
319
320 if (inputDeviceDropDown != nullptr)
321 {
322 auto row = r.removeFromTop (h);
323
324 inputLevelMeter->setBounds (row.removeFromRight (testButton != nullptr ? testButton->getWidth() : row.getWidth() / 6));
325 row.removeFromRight (space);
326 inputDeviceDropDown->setBounds (row);
327 r.removeFromTop (space);
328 }
329
330 if (outputChanList != nullptr)
331 {
332 outputChanList->setRowHeight (jmin (22, h));
333 outputChanList->setBounds (r.removeFromTop (outputChanList->getBestHeight (maxListBoxHeight)));
334 outputChanLabel->setBounds (0, outputChanList->getBounds().getCentreY() - h / 2, r.getX(), h);
335 r.removeFromTop (space);
336 }
337
338 if (inputChanList != nullptr)
339 {
340 inputChanList->setRowHeight (jmin (22, h));
341 inputChanList->setBounds (r.removeFromTop (inputChanList->getBestHeight (maxListBoxHeight)));
342 inputChanLabel->setBounds (0, inputChanList->getBounds().getCentreY() - h / 2, r.getX(), h);
343 r.removeFromTop (space);
344 }
345
346 r.removeFromTop (space * 2);
347
348 if (showAdvancedSettingsButton != nullptr
349 && sampleRateDropDown != nullptr && bufferSizeDropDown != nullptr)
350 {
351 showAdvancedSettingsButton->setBounds (r.removeFromTop (h));
352 r.removeFromTop (space);
353 showAdvancedSettingsButton->changeWidthToFitText();
354 }
355
356 auto advancedSettingsVisible = showAdvancedSettingsButton == nullptr
357 || showAdvancedSettingsButton->getToggleState();
358
359 if (sampleRateDropDown != nullptr)
360 {
361 sampleRateDropDown->setVisible (advancedSettingsVisible);
362
364 {
365 sampleRateDropDown->setBounds (r.removeFromTop (h));
366 r.removeFromTop (space);
367 }
368 }
369
370 if (bufferSizeDropDown != nullptr)
371 {
372 bufferSizeDropDown->setVisible (advancedSettingsVisible);
373
375 {
376 bufferSizeDropDown->setBounds (r.removeFromTop (h));
377 r.removeFromTop (space);
378 }
379 }
380
381 r.removeFromTop (space);
382
383 if (showUIButton != nullptr || resetDeviceButton != nullptr)
384 {
385 auto buttons = r.removeFromTop (h);
386
387 if (showUIButton != nullptr)
388 {
389 showUIButton->setVisible (advancedSettingsVisible);
390 showUIButton->changeWidthToFitText (h);
391 showUIButton->setBounds (buttons.removeFromLeft (showUIButton->getWidth()));
392 buttons.removeFromLeft (space);
393 }
394
395 if (resetDeviceButton != nullptr)
396 {
397 resetDeviceButton->setVisible (advancedSettingsVisible);
398 resetDeviceButton->changeWidthToFitText (h);
399 resetDeviceButton->setBounds (buttons.removeFromLeft (resetDeviceButton->getWidth()));
400 }
401
402 r.removeFromTop (space);
403 }
404
405 setSize (getWidth(), r.getY());
406 }
407
408 void updateConfig (bool updateOutputDevice, bool updateInputDevice, bool updateSampleRate, bool updateBufferSize)
409 {
410 auto config = setup.manager->getAudioDeviceSetup();
411 String error;
412
414 {
415 if (outputDeviceDropDown != nullptr)
416 config.outputDeviceName = outputDeviceDropDown->getSelectedId() < 0 ? String()
417 : outputDeviceDropDown->getText();
418
419 if (inputDeviceDropDown != nullptr)
420 config.inputDeviceName = inputDeviceDropDown->getSelectedId() < 0 ? String()
421 : inputDeviceDropDown->getText();
422
423 if (! type.hasSeparateInputsAndOutputs())
424 config.inputDeviceName = config.outputDeviceName;
425
427 config.useDefaultInputChannels = true;
428 else
429 config.useDefaultOutputChannels = true;
430
431 error = setup.manager->setAudioDeviceSetup (config, true);
432
433 showCorrectDeviceName (inputDeviceDropDown.get(), true);
434 showCorrectDeviceName (outputDeviceDropDown.get(), false);
435
436 updateControlPanelButton();
437 resized();
438 }
439 else if (updateSampleRate)
440 {
441 if (sampleRateDropDown->getSelectedId() > 0)
442 {
443 config.sampleRate = sampleRateDropDown->getSelectedId();
444 error = setup.manager->setAudioDeviceSetup (config, true);
445 }
446 }
447 else if (updateBufferSize)
448 {
449 if (bufferSizeDropDown->getSelectedId() > 0)
450 {
451 config.bufferSize = bufferSizeDropDown->getSelectedId();
452 error = setup.manager->setAudioDeviceSetup (config, true);
453 }
454 }
455
456 if (error.isNotEmpty())
457 messageBox = AlertWindow::showScopedAsync (MessageBoxOptions().withIconType (MessageBoxIconType::WarningIcon)
458 .withTitle (TRANS ("Error when trying to open audio device!"))
459 .withMessage (error)
460 .withButton (TRANS ("OK")),
461 nullptr);
462 }
463
464 bool showDeviceControlPanel()
465 {
466 if (auto* device = setup.manager->getCurrentAudioDevice())
467 {
469 modalWindow.setOpaque (true);
470 modalWindow.addToDesktop (0);
471 modalWindow.enterModalState();
472
473 return device->showControlPanel();
474 }
475
476 return false;
477 }
478
479 void toggleAdvancedSettings()
480 {
481 showAdvancedSettingsButton->setButtonText ((showAdvancedSettingsButton->getToggleState() ? "Hide " : "Show ")
482 + String ("advanced settings..."));
483 resized();
484 }
485
486 void showDeviceUIPanel()
487 {
488 if (showDeviceControlPanel())
489 {
490 setup.manager->closeAudioDevice();
491 setup.manager->restartLastAudioDevice();
493 }
494 }
495
496 void playTestSound()
497 {
498 setup.manager->playTestSound();
499 }
500
501 void updateAllControls()
502 {
503 updateOutputsComboBox();
504 updateInputsComboBox();
505
506 updateControlPanelButton();
507 updateResetButton();
508
509 if (auto* currentDevice = setup.manager->getCurrentAudioDevice())
510 {
511 if (setup.maxNumOutputChannels > 0
512 && setup.minNumOutputChannels < setup.manager->getCurrentAudioDevice()->getOutputChannelNames().size())
513 {
514 if (outputChanList == nullptr)
515 {
516 outputChanList = std::make_unique<ChannelSelectorListBox> (setup, ChannelSelectorListBox::audioOutputType,
517 TRANS ("(no audio output channels found)"));
518 addAndMakeVisible (outputChanList.get());
519 outputChanLabel = std::make_unique<Label> (String{}, TRANS ("Active output channels:"));
520 outputChanLabel->setJustificationType (Justification::centredRight);
521 outputChanLabel->attachToComponent (outputChanList.get(), true);
522 }
523
524 outputChanList->refresh();
525 }
526 else
527 {
528 outputChanLabel.reset();
529 outputChanList.reset();
530 }
531
532 if (setup.maxNumInputChannels > 0
533 && setup.minNumInputChannels < setup.manager->getCurrentAudioDevice()->getInputChannelNames().size())
534 {
535 if (inputChanList == nullptr)
536 {
537 inputChanList = std::make_unique<ChannelSelectorListBox> (setup, ChannelSelectorListBox::audioInputType,
538 TRANS ("(no audio input channels found)"));
539 addAndMakeVisible (inputChanList.get());
540 inputChanLabel = std::make_unique<Label> (String{}, TRANS ("Active input channels:"));
541 inputChanLabel->setJustificationType (Justification::centredRight);
542 inputChanLabel->attachToComponent (inputChanList.get(), true);
543 }
544
545 inputChanList->refresh();
546 }
547 else
548 {
549 inputChanLabel.reset();
550 inputChanList.reset();
551 }
552
553 updateSampleRateComboBox (currentDevice);
554 updateBufferSizeComboBox (currentDevice);
555 }
556 else
557 {
558 jassert (setup.manager->getCurrentAudioDevice() == nullptr); // not the correct device type!
559
560 inputChanLabel.reset();
561 outputChanLabel.reset();
562 sampleRateLabel.reset();
563 bufferSizeLabel.reset();
564
565 inputChanList.reset();
566 outputChanList.reset();
567 sampleRateDropDown.reset();
568 bufferSizeDropDown.reset();
569
570 if (outputDeviceDropDown != nullptr)
571 outputDeviceDropDown->setSelectedId (-1, dontSendNotification);
572
573 if (inputDeviceDropDown != nullptr)
574 inputDeviceDropDown->setSelectedId (-1, dontSendNotification);
575 }
576
578 resized();
579 setSize (getWidth(), getLowestY() + 4);
580 }
581
583 {
584 updateAllControls();
585 }
586
587 void resetDevice()
588 {
589 setup.manager->closeAudioDevice();
590 setup.manager->restartLastAudioDevice();
591 }
592
593private:
594 AudioIODeviceType& type;
595 const AudioDeviceSetupDetails setup;
596 AudioDeviceSelectorComponent& parent;
597
598 std::unique_ptr<ComboBox> outputDeviceDropDown, inputDeviceDropDown, sampleRateDropDown, bufferSizeDropDown;
599 std::unique_ptr<Label> outputDeviceLabel, inputDeviceLabel, sampleRateLabel, bufferSizeLabel, inputChanLabel, outputChanLabel;
601 std::unique_ptr<Component> inputLevelMeter;
602 std::unique_ptr<TextButton> showUIButton, showAdvancedSettingsButton, resetDeviceButton;
603
604 void showCorrectDeviceName (ComboBox* box, bool isInput)
605 {
606 if (box != nullptr)
607 {
608 auto* currentDevice = setup.manager->getCurrentAudioDevice();
609 auto index = type.getIndexOfDevice (currentDevice, isInput);
610
611 box->setSelectedId (index < 0 ? index : index + 1, dontSendNotification);
612
613 if (testButton != nullptr && ! isInput)
614 testButton->setEnabled (index >= 0);
615 }
616 }
617
618 void addNamesToDeviceBox (ComboBox& combo, bool isInputs)
619 {
620 const StringArray devs (type.getDeviceNames (isInputs));
621
623
624 for (int i = 0; i < devs.size(); ++i)
625 combo.addItem (devs[i], i + 1);
626
627 combo.addItem (getNoDeviceString(), -1);
628 combo.setSelectedId (-1, dontSendNotification);
629 }
630
631 int getLowestY() const
632 {
633 int y = 0;
634
635 for (auto* c : getChildren())
636 y = jmax (y, c->getBottom());
637
638 return y;
639 }
640
641 void updateControlPanelButton()
642 {
643 auto* currentDevice = setup.manager->getCurrentAudioDevice();
644 showUIButton.reset();
645
646 if (currentDevice != nullptr && currentDevice->hasControlPanel())
647 {
648 showUIButton = std::make_unique<TextButton> (TRANS ("Control Panel"),
649 TRANS ("Opens the device's own control panel"));
650 addAndMakeVisible (showUIButton.get());
651 showUIButton->onClick = [this] { showDeviceUIPanel(); };
652 }
653
654 resized();
655 }
656
657 void updateResetButton()
658 {
659 if (auto* currentDevice = setup.manager->getCurrentAudioDevice())
660 {
661 if (currentDevice->hasControlPanel())
662 {
663 if (resetDeviceButton == nullptr)
664 {
665 resetDeviceButton = std::make_unique<TextButton> (TRANS ("Reset Device"),
666 TRANS ("Resets the audio interface - sometimes needed after changing a device's properties in its custom control panel"));
667 addAndMakeVisible (resetDeviceButton.get());
668 resetDeviceButton->onClick = [this] { resetDevice(); };
669 resized();
670 }
671
672 return;
673 }
674 }
675
676 resetDeviceButton.reset();
677 }
678
679 void updateOutputsComboBox()
680 {
681 if (setup.maxNumOutputChannels > 0 || ! type.hasSeparateInputsAndOutputs())
682 {
683 if (outputDeviceDropDown == nullptr)
684 {
685 outputDeviceDropDown = std::make_unique<ComboBox>();
686 outputDeviceDropDown->onChange = [this] { updateConfig (true, false, false, false); };
687
688 addAndMakeVisible (outputDeviceDropDown.get());
689
690 outputDeviceLabel = std::make_unique<Label> (String{}, type.hasSeparateInputsAndOutputs() ? TRANS ("Output:")
691 : TRANS ("Device:"));
692 outputDeviceLabel->attachToComponent (outputDeviceDropDown.get(), true);
693
694 if (setup.maxNumOutputChannels > 0)
695 {
696 testButton = std::make_unique<TextButton> (TRANS ("Test"), TRANS ("Plays a test tone"));
697 addAndMakeVisible (testButton.get());
698 testButton->onClick = [this] { playTestSound(); };
699 }
700 }
701
702 addNamesToDeviceBox (*outputDeviceDropDown, false);
703 }
704
705 showCorrectDeviceName (outputDeviceDropDown.get(), false);
706 }
707
708 void updateInputsComboBox()
709 {
710 if (setup.maxNumInputChannels > 0 && type.hasSeparateInputsAndOutputs())
711 {
712 if (inputDeviceDropDown == nullptr)
713 {
714 inputDeviceDropDown = std::make_unique<ComboBox>();
715 inputDeviceDropDown->onChange = [this] { updateConfig (false, true, false, false); };
716 addAndMakeVisible (inputDeviceDropDown.get());
717
718 inputDeviceLabel = std::make_unique<Label> (String{}, TRANS ("Input:"));
719 inputDeviceLabel->attachToComponent (inputDeviceDropDown.get(), true);
720
721 inputLevelMeter = std::make_unique<SimpleDeviceManagerInputLevelMeter> (*setup.manager);
722 addAndMakeVisible (inputLevelMeter.get());
723 }
724
725 addNamesToDeviceBox (*inputDeviceDropDown, true);
726 }
727
728 showCorrectDeviceName (inputDeviceDropDown.get(), true);
729 }
730
731 void updateSampleRateComboBox (AudioIODevice* currentDevice)
732 {
733 if (sampleRateDropDown == nullptr)
734 {
735 sampleRateDropDown = std::make_unique<ComboBox>();
736 addAndMakeVisible (sampleRateDropDown.get());
737
738 sampleRateLabel = std::make_unique<Label> (String{}, TRANS ("Sample rate:"));
739 sampleRateLabel->attachToComponent (sampleRateDropDown.get(), true);
740 }
741 else
742 {
743 sampleRateDropDown->clear();
744 sampleRateDropDown->onChange = nullptr;
745 }
746
747 const auto getFrequencyString = [] (int rate) { return String (rate) + " Hz"; };
748
749 for (auto rate : currentDevice->getAvailableSampleRates())
750 {
751 const auto intRate = roundToInt (rate);
752 sampleRateDropDown->addItem (getFrequencyString (intRate), intRate);
753 }
754
755 const auto intRate = roundToInt (currentDevice->getCurrentSampleRate());
756 sampleRateDropDown->setText (getFrequencyString (intRate), dontSendNotification);
757
758 sampleRateDropDown->onChange = [this] { updateConfig (false, false, true, false); };
759 }
760
761 void updateBufferSizeComboBox (AudioIODevice* currentDevice)
762 {
763 if (bufferSizeDropDown == nullptr)
764 {
765 bufferSizeDropDown = std::make_unique<ComboBox>();
766 addAndMakeVisible (bufferSizeDropDown.get());
767
768 bufferSizeLabel = std::make_unique<Label> (String{}, TRANS ("Audio buffer size:"));
769 bufferSizeLabel->attachToComponent (bufferSizeDropDown.get(), true);
770 }
771 else
772 {
773 bufferSizeDropDown->clear();
774 bufferSizeDropDown->onChange = nullptr;
775 }
776
777 auto currentRate = currentDevice->getCurrentSampleRate();
778
779 if (exactlyEqual (currentRate, 0.0))
780 currentRate = 48000.0;
781
782 for (auto bs : currentDevice->getAvailableBufferSizes())
783 bufferSizeDropDown->addItem (String (bs) + " samples (" + String (bs * 1000.0 / currentRate, 1) + " ms)", bs);
784
785 bufferSizeDropDown->setSelectedId (currentDevice->getCurrentBufferSizeSamples(), dontSendNotification);
786 bufferSizeDropDown->onChange = [this] { updateConfig (false, false, false, true); };
787 }
788
789public:
790 //==============================================================================
792 private ListBoxModel
793 {
794 public:
795 enum BoxType
796 {
797 audioInputType,
798 audioOutputType
799 };
800
801 //==============================================================================
803 : ListBox ({}, nullptr), setup (setupDetails), type (boxType), noItemsMessage (noItemsText)
804 {
805 refresh();
806 setModel (this);
808 }
809
810 void refresh()
811 {
812 items.clear();
813
814 if (auto* currentDevice = setup.manager->getCurrentAudioDevice())
815 {
816 if (type == audioInputType)
817 items = currentDevice->getInputChannelNames();
818 else if (type == audioOutputType)
819 items = currentDevice->getOutputChannelNames();
820
821 if (setup.useStereoPairs)
822 {
824
825 for (int i = 0; i < items.size(); i += 2)
826 {
827 auto& name = items[i];
828
829 if (i + 1 >= items.size())
830 pairs.add (name.trim());
831 else
832 pairs.add (getNameForChannelPair (name, items[i + 1]));
833 }
834
835 items = pairs;
836 }
837 }
838
840 repaint();
841 }
842
843 int getNumRows() override
844 {
845 return items.size();
846 }
847
848 void paintListBoxItem (int row, Graphics& g, int width, int height, bool) override
849 {
850 if (isPositiveAndBelow (row, items.size()))
851 {
853
854 auto item = items[row];
855 bool enabled = false;
856 auto config = setup.manager->getAudioDeviceSetup();
857
858 if (setup.useStereoPairs)
859 {
860 if (type == audioInputType)
861 enabled = config.inputChannels[row * 2] || config.inputChannels[row * 2 + 1];
862 else if (type == audioOutputType)
863 enabled = config.outputChannels[row * 2] || config.outputChannels[row * 2 + 1];
864 }
865 else
866 {
867 if (type == audioInputType)
868 enabled = config.inputChannels[row];
869 else if (type == audioOutputType)
870 enabled = config.outputChannels[row];
871 }
872
873 auto x = getTickX();
874 auto tickW = (float) height * 0.75f;
875
876 getLookAndFeel().drawTickBox (g, *this, (float) x - tickW, ((float) height - tickW) * 0.5f, tickW, tickW,
877 enabled, true, true, false);
878
879 drawTextLayout (g, *this, item, { x + 5, 0, width - x - 5, height }, enabled);
880 }
881 }
882
883 void listBoxItemClicked (int row, const MouseEvent& e) override
884 {
885 selectRow (row);
886
887 if (e.x < getTickX())
888 flipEnablement (row);
889 }
890
891 void listBoxItemDoubleClicked (int row, const MouseEvent&) override
892 {
893 flipEnablement (row);
894 }
895
896 void returnKeyPressed (int row) override
897 {
898 flipEnablement (row);
899 }
900
901 void paint (Graphics& g) override
902 {
903 ListBox::paint (g);
904
905 if (items.isEmpty())
906 {
907 g.setColour (Colours::grey);
908 g.setFont (0.5f * (float) getRowHeight());
909 g.drawText (noItemsMessage,
910 0, 0, getWidth(), getHeight() / 2,
912 }
913 }
914
915 int getBestHeight (int maxHeight)
916 {
917 return getRowHeight() * jlimit (2, jmax (2, maxHeight / getRowHeight()),
918 getNumRows())
919 + getOutlineThickness() * 2;
920 }
921
922 private:
923 //==============================================================================
924 const AudioDeviceSetupDetails setup;
925 const BoxType type;
926 const String noItemsMessage;
927 StringArray items;
928
929 static String getNameForChannelPair (const String& name1, const String& name2)
930 {
932
933 for (int j = 0; j < name1.length(); ++j)
934 if (name1.substring (0, j).equalsIgnoreCase (name2.substring (0, j)))
935 commonBit = name1.substring (0, j);
936
937 // Make sure we only split the name at a space, because otherwise, things
938 // like "input 11" + "input 12" would become "input 11 + 2"
939 while (commonBit.isNotEmpty() && ! CharacterFunctions::isWhitespace (commonBit.getLastCharacter()))
941
942 return name1.trim() + " + " + name2.substring (commonBit.length()).trim();
943 }
944
945 void flipEnablement (int row)
946 {
947 jassert (type == audioInputType || type == audioOutputType);
948
949 if (isPositiveAndBelow (row, items.size()))
950 {
951 auto config = setup.manager->getAudioDeviceSetup();
952
953 if (setup.useStereoPairs)
954 {
955 BigInteger bits;
956 auto& original = (type == audioInputType ? config.inputChannels
957 : config.outputChannels);
958
959 for (int i = 0; i < 256; i += 2)
960 bits.setBit (i / 2, original[i] || original[i + 1]);
961
962 if (type == audioInputType)
963 {
965 flipBit (bits, row, setup.minNumInputChannels / 2, setup.maxNumInputChannels / 2);
966 }
967 else
968 {
969 config.useDefaultOutputChannels = false;
970 flipBit (bits, row, setup.minNumOutputChannels / 2, setup.maxNumOutputChannels / 2);
971 }
972
973 for (int i = 0; i < 256; ++i)
974 original.setBit (i, bits[i / 2]);
975 }
976 else
977 {
978 if (type == audioInputType)
979 {
980 config.useDefaultInputChannels = false;
981 flipBit (config.inputChannels, row, setup.minNumInputChannels, setup.maxNumInputChannels);
982 }
983 else
984 {
985 config.useDefaultOutputChannels = false;
986 flipBit (config.outputChannels, row, setup.minNumOutputChannels, setup.maxNumOutputChannels);
987 }
988 }
989
990 setup.manager->setAudioDeviceSetup (config, true);
991 }
992 }
993
994 static void flipBit (BigInteger& chans, int index, int minNumber, int maxNumber)
995 {
996 auto numActive = chans.countNumberOfSetBits();
997
998 if (chans[index])
999 {
1000 if (numActive > minNumber)
1001 chans.setBit (index, false);
1002 }
1003 else
1004 {
1005 if (numActive >= maxNumber)
1006 {
1007 auto firstActiveChan = chans.findNextSetBit (0);
1008 chans.clearBit (index > firstActiveChan ? firstActiveChan : chans.getHighestBit());
1009 }
1010
1011 chans.setBit (index, true);
1012 }
1013 }
1014
1015 int getTickX() const
1016 {
1017 return getRowHeight();
1018 }
1019
1020 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelSelectorListBox)
1021 };
1022
1023private:
1024 std::unique_ptr<ChannelSelectorListBox> inputChanList, outputChanList;
1025 ScopedMessageBox messageBox;
1026
1027 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceSettingsPanel)
1028};
1029
1030
1031//==============================================================================
1041 : deviceManager (dm),
1042 itemHeight (24),
1043 minOutputChannels (minOutputChannelsToUse),
1044 maxOutputChannels (maxOutputChannelsToUse),
1045 minInputChannels (minInputChannelsToUse),
1046 maxInputChannels (maxInputChannelsToUse),
1047 showChannelsAsStereoPairs (showChannelsAsStereoPairsToUse),
1048 hideAdvancedOptionsWithButton (hideAdvancedOptionsWithButtonToUse)
1049{
1050 jassert (minOutputChannels >= 0 && minOutputChannels <= maxOutputChannels);
1051 jassert (minInputChannels >= 0 && minInputChannels <= maxInputChannels);
1052
1054
1055 if (types.size() > 1)
1056 {
1057 deviceTypeDropDown = std::make_unique<ComboBox>();
1058
1059 for (int i = 0; i < types.size(); ++i)
1060 deviceTypeDropDown->addItem (types.getUnchecked (i)->getTypeName(), i + 1);
1061
1062 addAndMakeVisible (deviceTypeDropDown.get());
1063 deviceTypeDropDown->onChange = [this] { updateDeviceType(); };
1064
1065 deviceTypeDropDownLabel = std::make_unique<Label> (String{}, TRANS ("Audio device type:"));
1066 deviceTypeDropDownLabel->setJustificationType (Justification::centredRight);
1067 deviceTypeDropDownLabel->attachToComponent (deviceTypeDropDown.get(), true);
1068 }
1069
1071 {
1072 midiInputsList = std::make_unique <MidiInputSelectorComponentListBox> (deviceManager,
1073 "(" + TRANS ("No MIDI inputs available") + ")");
1074 addAndMakeVisible (midiInputsList.get());
1075
1076 midiInputsLabel = std::make_unique<Label> (String{}, TRANS ("Active MIDI inputs:"));
1077 midiInputsLabel->setJustificationType (Justification::topRight);
1078 midiInputsLabel->attachToComponent (midiInputsList.get(), true);
1079
1081 {
1082 bluetoothButton = std::make_unique<TextButton> (TRANS ("Bluetooth MIDI"), TRANS ("Scan for bluetooth MIDI devices"));
1083 addAndMakeVisible (bluetoothButton.get());
1084 bluetoothButton->onClick = [this] { handleBluetoothButton(); };
1085 }
1086 }
1087 else
1088 {
1089 midiInputsList.reset();
1090 midiInputsLabel.reset();
1091 bluetoothButton.reset();
1092 }
1093
1095 {
1096 midiOutputSelector = std::make_unique<MidiOutputSelector> (deviceManager);
1097 addAndMakeVisible (midiOutputSelector.get());
1098
1099 midiOutputLabel = std::make_unique<Label> ("lm", TRANS ("MIDI Output:"));
1100 midiOutputLabel->attachToComponent (midiOutputSelector.get(), true);
1101 }
1102 else
1103 {
1104 midiOutputSelector.reset();
1105 midiOutputLabel.reset();
1106 }
1107
1109 updateAllControls();
1110}
1111
1116
1118{
1119 itemHeight = newItemHeight;
1120 resized();
1121}
1122
1124{
1125 Rectangle<int> r (proportionOfWidth (0.35f), 15, proportionOfWidth (0.6f), 3000);
1126 auto space = itemHeight / 4;
1127
1128 if (deviceTypeDropDown != nullptr)
1129 {
1130 deviceTypeDropDown->setBounds (r.removeFromTop (itemHeight));
1131 r.removeFromTop (space * 3);
1132 }
1133
1134 if (audioDeviceSettingsComp != nullptr)
1135 {
1136 audioDeviceSettingsComp->resized();
1137 audioDeviceSettingsComp->setBounds (r.removeFromTop (audioDeviceSettingsComp->getHeight())
1138 .withX (0).withWidth (getWidth()));
1139 r.removeFromTop (space);
1140 }
1141
1142 if (midiInputsList != nullptr)
1143 {
1144 midiInputsList->setRowHeight (jmin (22, itemHeight));
1145 midiInputsList->setBounds (r.removeFromTop (midiInputsList->getBestHeight (jmin (itemHeight * 8,
1146 getHeight() - r.getY() - space - itemHeight))));
1147 r.removeFromTop (space);
1148 }
1149
1150 if (bluetoothButton != nullptr)
1151 {
1152 bluetoothButton->setBounds (r.removeFromTop (24));
1153 r.removeFromTop (space);
1154 }
1155
1156 if (midiOutputSelector != nullptr)
1157 midiOutputSelector->setBounds (r.removeFromTop (itemHeight));
1158
1159 r.removeFromTop (itemHeight);
1160 setSize (getWidth(), r.getY());
1161}
1162
1164{
1165 if (child == audioDeviceSettingsComp.get())
1166 resized();
1167}
1168
1169void AudioDeviceSelectorComponent::updateDeviceType()
1170{
1171 if (auto* type = deviceManager.getAvailableDeviceTypes() [deviceTypeDropDown->getSelectedId() - 1])
1172 {
1173 audioDeviceSettingsComp.reset();
1174 deviceManager.setCurrentAudioDeviceType (type->getTypeName(), true);
1175 updateAllControls(); // needed in case the type hasn't actually changed
1176 }
1177}
1178
1179void AudioDeviceSelectorComponent::changeListenerCallback (ChangeBroadcaster*)
1180{
1181 updateAllControls();
1182}
1183
1184void AudioDeviceSelectorComponent::updateAllControls()
1185{
1186 if (deviceTypeDropDown != nullptr)
1187 deviceTypeDropDown->setText (deviceManager.getCurrentAudioDeviceType(), dontSendNotification);
1188
1189 if (audioDeviceSettingsComp == nullptr
1190 || audioDeviceSettingsCompType != deviceManager.getCurrentAudioDeviceType())
1191 {
1192 audioDeviceSettingsCompType = deviceManager.getCurrentAudioDeviceType();
1193 audioDeviceSettingsComp.reset();
1194
1195 if (auto* type = deviceManager.getAvailableDeviceTypes() [deviceTypeDropDown == nullptr
1196 ? 0 : deviceTypeDropDown->getSelectedId() - 1])
1197 {
1198 AudioDeviceSetupDetails details;
1199 details.manager = &deviceManager;
1200 details.minNumInputChannels = minInputChannels;
1201 details.maxNumInputChannels = maxInputChannels;
1202 details.minNumOutputChannels = minOutputChannels;
1203 details.maxNumOutputChannels = maxOutputChannels;
1204 details.useStereoPairs = showChannelsAsStereoPairs;
1205
1206 audioDeviceSettingsComp = std::make_unique<AudioDeviceSettingsPanel> (*type, details, hideAdvancedOptionsWithButton, *this);
1207 addAndMakeVisible (audioDeviceSettingsComp.get());
1208 }
1209 }
1210
1211 if (midiInputsList != nullptr)
1212 {
1213 midiInputsList->updateDevices();
1214 midiInputsList->updateContent();
1215 midiInputsList->repaint();
1216 }
1217
1218 resized();
1219}
1220
1221void AudioDeviceSelectorComponent::handleBluetoothButton()
1222{
1224 {
1226 }
1227 else
1228 {
1230 {
1233 });
1234 }
1235}
1236
1238{
1239 return midiInputsList.get();
1240}
1241
1242} // namespace juce
static ScopedMessageBox showScopedAsync(const MessageBoxOptions &options, std::function< void(int)> callback)
Shows an alert window using the specified options.
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
bool isEmpty() const noexcept
Returns true if the array is empty, false otherwise.
Definition juce_Array.h:222
int size() const noexcept
Returns the current number of elements in the array.
Definition juce_Array.h:215
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Definition juce_Array.h:418
void clear()
Removes all elements from the array.
Definition juce_Array.h:188
@ none
No word-wrapping: lines extend indefinitely.
Manages the state of some audio and midi i/o devices.
bool isMidiInputDeviceEnabled(const String &deviceIdentifier) const
Returns true if a given midi input device is being used.
LevelMeter::Ptr getInputLevelGetter() noexcept
Returns a reference-counted object that can be used to get the current input level.
AudioDeviceSetup getAudioDeviceSetup() const
Returns the current device properties that are in use.
const String & getDefaultMidiOutputIdentifier() const noexcept
Returns the name of the default midi output.
String setAudioDeviceSetup(const AudioDeviceSetup &newSetup, bool treatAsChosenDevice)
Changes the current device or its settings.
AudioIODevice * getCurrentAudioDevice() const noexcept
Returns the currently-active audio device.
String getCurrentAudioDeviceType() const
Returns the type of audio device currently in use.
void setDefaultMidiOutputDevice(const String &deviceIdentifier)
Sets a midi output device to use as the default.
void setMidiInputDeviceEnabled(const String &deviceIdentifier, bool enabled)
Enables or disables a midi input device.
void setCurrentAudioDeviceType(const String &type, bool treatAsChosenDevice)
Changes the class of audio device being used.
const OwnedArray< AudioIODeviceType > & getAvailableDeviceTypes()
Returns a list of the types of device supported.
void restartLastAudioDevice()
Tries to reload the last audio device that was running.
void playTestSound()
Plays a beep through the current audio device.
void closeAudioDevice()
Closes the currently-open device.
int getNumRows() override
This has to return the number of items in the list.
void listBoxItemClicked(int row, const MouseEvent &e) override
This can be overridden to react to the user clicking on a row.
void paintListBoxItem(int row, Graphics &g, int width, int height, bool rowIsSelected) override
This method must be implemented to draw a row of the list.
void listBoxItemDoubleClicked(int row, const MouseEvent &) override
This can be overridden to react to the user double-clicking on a row.
void returnKeyPressed(int row) override
Override this to be informed when the return key is pressed.
void paint(Graphics &g) override
Components can override this method to draw their content.
void resized() final
Called when this component's size has been changed.
A component containing controls to let the user change the audio settings of an AudioDeviceManager ob...
void resized() override
Called when this component's size has been changed.
ListBox * getMidiInputSelectorListBox() const noexcept
Returns the ListBox that's being used to show the midi inputs, or nullptr if there isn't one.
AudioDeviceManager & deviceManager
The device manager that this component is controlling.
AudioDeviceSelectorComponent(AudioDeviceManager &deviceManager, int minAudioInputChannels, int maxAudioInputChannels, int minAudioOutputChannels, int maxAudioOutputChannels, bool showMidiInputOptions, bool showMidiOutputSelector, bool showChannelsAsStereoPairs, bool hideAdvancedOptionsWithButton)
Creates the component.
void setItemHeight(int itemHeight)
Sets the standard height used for items in the panel.
int getItemHeight() const noexcept
Returns the standard height used for items in the panel.
void childBoundsChanged(Component *child) override
Called when one of this component's children is moved or resized.
void listBoxItemClicked(int row, const MouseEvent &e) override
This can be overridden to react to the user clicking on a row.
void paint(Graphics &g) override
Components can override this method to draw their content.
void listBoxItemDoubleClicked(int row, const MouseEvent &) override
This can be overridden to react to the user double-clicking on a row.
void returnKeyPressed(int row) override
Override this to be informed when the return key is pressed.
void paintListBoxItem(int row, Graphics &g, int width, int height, bool) override
This method must be implemented to draw a row of the list.
int getNumRows() override
This has to return the number of items in the list.
void changeListenerCallback(ChangeBroadcaster *) override
Your subclass should implement this method to receive the callback.
void resized() override
Called when this component's size has been changed.
Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc.
virtual bool hasSeparateInputsAndOutputs() const =0
Returns true if two different devices can be used for the input and output.
virtual void scanForDevices()=0
Refreshes the object's cached list of known devices.
virtual StringArray getOutputChannelNames()=0
Returns the names of all the available output channels on this device.
virtual StringArray getInputChannelNames()=0
Returns the names of all the available input channels on this device.
static bool isAvailable()
Checks if a Bluetooth MIDI pairing dialogue is available on this platform.
static bool open(ModalComponentManager::Callback *exitCallback=nullptr, Rectangle< int > *btWindowBounds=nullptr)
Opens the Bluetooth MIDI pairing dialogue, if it is available.
Holds a list of ChangeListeners, and sends messages to them when instructed.
void addChangeListener(ChangeListener *listener)
Registers a listener to receive change callbacks from this broadcaster.
void removeChangeListener(ChangeListener *listener)
Unregisters a listener from the list.
Receives change event callbacks that are sent out by a ChangeBroadcaster.
static bool isWhitespace(char character) noexcept
Checks whether a character is whitespace.
void clear(NotificationType notification=sendNotificationAsync)
Removes all the items from the drop-down list.
void setSelectedId(int newItemId, NotificationType notification=sendNotificationAsync)
Sets one of the items to be the current selection.
std::function< void()> onChange
You can assign a lambda to this callback object to have it called when the selected ID is changed.
void addSeparator()
Adds a separator line to the drop-down list.
int getSelectedId() const noexcept
Returns the ID of the item that's currently shown in the box.
void addItem(const String &newItemText, int newItemId)
Adds an item to be shown in the drop-down list.
The base class for all JUCE user-interface objects.
int proportionOfWidth(float proportion) const noexcept
Returns a proportion of the component's width.
Component * getTopLevelComponent() const noexcept
Returns the highest-level component which contains this one or its parents.
int getBottom() const noexcept
Returns the y coordinate of the bottom edge of this component.
int getHeight() const noexcept
Returns the component's height in pixels.
void toFront(bool shouldAlsoGainKeyboardFocus)
Brings the component to the front of its siblings.
bool isShowing() const
Tests whether this component and all its parents are visible.
void sendLookAndFeelChange()
Calls the methods repaint(), lookAndFeelChanged(), and colourChanged() in this component and all its ...
void addAndMakeVisible(Component *child, int zOrder=-1)
Adds a child component to this one, and also makes the child visible if it isn't already.
void repaint()
Marks the whole component as needing to be redrawn.
Component() noexcept
Creates a component.
void setBounds(int x, int y, int width, int height)
Changes the component's position and size.
void setSize(int newWidth, int newHeight)
Changes the size of the component.
Colour findColour(int colourID, bool inheritFromParent=false) const
Looks for a colour that has been registered with the given colour ID number.
int getWidth() const noexcept
Returns the component's width in pixels.
LookAndFeel & getLookAndFeel() const noexcept
Finds the appropriate look-and-feel to use for this component.
Rectangle< int > getLocalBounds() const noexcept
Returns the component's bounds, relative to its own origin.
const Array< Component * > & getChildren() const noexcept
Provides access to the underlying array of child components.
A graphics context, used for drawing a component or image.
void drawText(const String &text, int x, int y, int width, int height, Justification justificationType, bool useEllipsesIfTooBig=true) const
Draws a line of text within a specified rectangle.
void setFont(const Font &newFont)
Changes the font to use for subsequent text-drawing functions.
void setColour(Colour newColour)
Changes the current drawing colour.
void fillAll() const
Fills the context's entire clip region with the current colour or brush.
@ centredRight
Indicates that the item should be centred vertically but placed on the right hand side.
@ centred
Indicates that the item should be centred vertically and horizontally.
@ centredLeft
Indicates that the item should be centred vertically but placed on the left hand side.
@ topRight
Indicates that the item should be placed in the top-right corner.
A subclass of this is used to drive a ListBox.
A list of items that can be scrolled vertically.
void paint(Graphics &) override
Components can override this method to draw their content.
void setOutlineThickness(int outlineThickness)
Sets the thickness of a border that will be drawn around the box.
void setModel(ListBoxModel *newModel)
Changes the current data model to display.
@ textColourId
The preferred colour to use for drawing text in the listbox.
@ backgroundColourId
The background colour to fill the list with.
int getOutlineThickness() const noexcept
Returns the thickness of outline that will be drawn around the listbox.
void updateContent()
Causes the list to refresh its content.
int getRowHeight() const noexcept
Returns the height of a row in the list.
void selectRow(int rowNumber, bool dontScrollToShowThisRow=false, bool deselectOthersFirst=true)
Selects a row.
static Array< MidiDeviceInfo > getAvailableDevices()
Returns a list of the available midi input devices.
static Array< MidiDeviceInfo > getAvailableDevices()
Returns a list of the available midi output devices.
Contains position and status information about a mouse event.
const int x
The x-position of the mouse when the event occurred.
An array designed for holding objects.
int size() const noexcept
Returns the number of items currently in the array.
ObjectClass * getUnchecked(int index) const noexcept
Returns a pointer to the object at this index in the array, without checking whether the index is in-...
Manages a rectangle and allows geometric operations to be performed on it.
Rectangle withX(ValueType newX) const noexcept
Returns a rectangle which has the same size and y-position as this one, but with a different x-positi...
ValueType getX() const noexcept
Returns the x coordinate of the rectangle's left-hand-side.
Rectangle removeFromTop(ValueType amountToRemove) noexcept
Removes a strip from the top of this rectangle, reducing this rectangle by the specified amount and r...
ValueType getY() const noexcept
Returns the y coordinate of the rectangle's top edge.
Rectangle withWidth(ValueType newWidth) const noexcept
Returns a rectangle which has the same position and height as this one, but with a different width.
@ bluetoothMidi
Permission to scan for and pair to Bluetooth MIDI devices (required on Android).
static bool isGranted(PermissionID permission)
Returns true if the app has been already granted this permission, either via a previous runtime reque...
static void request(PermissionID permission, Callback callback)
Call this method to request a runtime permission.
A special array for holding a list of strings.
int size() const noexcept
Returns the number of strings in the array.
The JUCE String class!
Definition juce_String.h:53
String trim() const
Returns a copy of this string with any whitespace characters removed from the start and end.
String dropLastCharacters(int numberToDrop) const
Returns a version of this string with a number of characters removed from the end.
String substring(int startIndex, int endIndex) const
Returns a subsection of the string.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
@ highlightColourId
The colour with which to fill the background of highlighted sections of the text - this can be transp...
Makes repeated callbacks to a virtual method at a specified time interval.
Definition juce_Timer.h:52
void startTimerHz(int timerFrequencyHz) noexcept
Starts the timer with an interval specified in Hertz.
T exp(T... args)
T get(T... args)
#define TRANS(stringLiteral)
Uses the LocalisedStrings class to translate the given string literal.
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
T log(T... args)
typedef float
JUCE Namespace.
constexpr auto enumerate(Range &&range, Index startingValue={})
Given a range and an optional starting offset, returns an IteratorPair that holds EnumerateIterators ...
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr bool exactlyEqual(Type a, Type b)
Equivalent to operator==, but suppresses float-equality warnings.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
@ WarningIcon
An exclamation mark to indicate that the dialog is a warning about something and shouldn't be ignored...
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
@ dontSendNotification
No notification message should be sent.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
T reset(T... args)
bool useDefaultInputChannels
If this is true, it indicates that the inputChannels array should be ignored, and instead,...
void paint(Graphics &g) override
Components can override this method to draw their content.
void timerCallback() override
The user-defined callback routine that actually gets called periodically.