Audacity 3.2.0
AudioSetupToolBar.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 AudioSetupToolBar.cpp
6
7*******************************************************************//*******************************************************************/
13
14#include "AudioSetupToolBar.h"
15#include "ToolManager.h"
16
17#include <thread>
18
19#include <wx/log.h>
20#include <wx/menu.h>
21#include <wx/sizer.h>
22#include <wx/stattext.h>
23#include <wx/tooltip.h>
24
25#include "../ActiveProject.h"
26
27#include "AColor.h"
28#include "AllThemeResources.h"
29#include "AudioIOBase.h"
30#include "DeviceToolBar.h"
31#include "../KeyboardCapture.h"
32#include "Project.h"
33#include "../ProjectWindows.h"
34#include "DeviceManager.h"
35#include "../prefs/PrefsDialog.h"
36#include "../prefs/DevicePrefs.h"
37#include "../widgets/AButton.h"
38#include "../widgets/BasicMenu.h"
39#include "../widgets/wxWidgetsWindowPlacement.h"
40
41namespace {
42 static constexpr int kHost = 15000;
43 static constexpr int kInput = 15200;
44 static constexpr int kInputChannels = 15400;
45 static constexpr int kOutput = 15600;
46 static constexpr int kAudioSettings = 15800;
47
49 {
50 public:
51 ViewDeviceSettingsDialog(wxWindow* parent, AudacityProject& project,
53 int page)
54 : PrefsDialog(parent, &project, title, factories)
55 , mPage(page)
56 {
57 }
58
59 long GetPreferredPage() override
60 {
61 return mPage;
62 }
63
64 void SavePreferredPage() override
65 {
66 }
67
68 private:
69 const int mPage;
70 };
71}
72
74
78
79BEGIN_EVENT_TABLE(AudioSetupToolBar, ToolBar)
80 EVT_BUTTON(ID_AUDIO_SETUP_BUTTON, AudioSetupToolBar::OnAudioSetup)
82
83//Standard constructor
85: ToolBar( project, AudioSetupBarID, XO("Audio Setup"), wxT("Audio Setup") )
86{
87 mSubscription = DeviceManager::Instance()->Subscribe(
89}
90
92{
93}
94
96{
97 auto &toolManager = ToolManager::Get( project );
98 return *static_cast<AudioSetupToolBar*>( toolManager.GetToolBar(AudioSetupBarID) );
99}
100
102{
103 return Get( const_cast<AudacityProject&>( project )) ;
104}
105
106void AudioSetupToolBar::Create(wxWindow *parent)
107{
108 ToolBar::Create(parent);
109
110 // Simulate a size event to set initial meter placement/size
111 wxSizeEvent event(GetSize(), GetId());
112 event.SetEventObject(this);
113 GetEventHandler()->ProcessEvent(event);
114}
115
117{
118 mInput.reset();
119 mOutput.reset();
120 mInputChannels.reset();
121 mHost.reset();
122}
123
125{
126 SetBackgroundColour( theTheme.Colour( clrMedium ) );
128
130
131#if wxUSE_TOOLTIPS
133 wxToolTip::Enable(true);
134 wxToolTip::SetDelay(1000);
135#endif
136
137 // Set default order and mode
139
141}
142
144{
145#ifndef USE_AQUA_THEME
146 wxSize s = mSizer->GetSize();
147 wxPoint p = mSizer->GetPosition();
148
149 wxRect bevelRect(p.x, p.y, s.GetWidth() - 1, s.GetHeight() - 1);
150 AColor::Bevel(*dc, true, bevelRect);
151#endif
152}
153
155{
156 bool bUseAqua = false;
157
158#ifdef EXPERIMENTAL_THEME_PREFS
159 gPrefs->Read(wxT("/GUI/ShowMac"), &bUseAqua, false);
160#endif
161
162#ifdef USE_AQUA_THEME
163 bUseAqua = !bUseAqua;
164#endif
165
166 const auto size = theTheme.ImageSize(bmpRecoloredSetupUpSmall);
167
168 if (bUseAqua) {
169 MakeMacRecoloredImageSize(bmpRecoloredSetupUpSmall, bmpMacUpButtonSmall, size);
170 MakeMacRecoloredImageSize(bmpRecoloredSetupDownSmall, bmpMacDownButtonSmall, size);
171 MakeMacRecoloredImageSize(bmpRecoloredSetupUpHiliteSmall, bmpMacHiliteUpButtonSmall, size);
172 MakeMacRecoloredImageSize(bmpRecoloredSetupHiliteSmall, bmpMacHiliteButtonSmall, size);
173 }
174 else {
175 MakeRecoloredImageSize(bmpRecoloredSetupUpSmall, bmpUpButtonSmall, size);
176 MakeRecoloredImageSize(bmpRecoloredSetupDownSmall, bmpDownButtonSmall, size);
177 MakeRecoloredImageSize(bmpRecoloredSetupUpHiliteSmall, bmpHiliteUpButtonSmall, size);
178 MakeRecoloredImageSize(bmpRecoloredSetupHiliteSmall, bmpHiliteButtonSmall, size);
179 }
180
181 mAudioSetup = MakeButton(this,
182 bmpRecoloredSetupUpSmall, bmpRecoloredSetupDownSmall,
183 bmpRecoloredSetupUpHiliteSmall, bmpRecoloredSetupHiliteSmall,
184 bmpSetup, bmpSetup, bmpSetup,
186 wxDefaultPosition,
187 true,
188 theTheme.ImageSize(bmpRecoloredSetupUpSmall));
189
190 mAudioSetup->SetLabel(XO("Audio Setup"));
191}
192
194{
195 int flags = wxALIGN_CENTER | wxRIGHT;
196
197 // (Re)allocate the button sizer
198 if (mSizer)
199 {
200 Detach(mSizer);
201 std::unique_ptr < wxSizer > {mSizer}; // DELETE it
202 }
203
204 Add((mSizer = safenew wxBoxSizer(wxHORIZONTAL)), 1, wxEXPAND);
205
206 auto text = safenew wxStaticText(this, wxID_ANY, "Audio Setup");
207 text->SetBackgroundColour(theTheme.Colour(clrMedium));
208 text->SetForegroundColour(theTheme.Colour(clrTrackPanelText));
209
210 auto vSizer = safenew wxBoxSizer(wxVERTICAL);
211 vSizer->AddSpacer(4);
212 vSizer->Add(mAudioSetup, 0, flags, 2);
213 vSizer->AddSpacer(4);
214 vSizer->Add(text, 0, flags, 2);
215
216 // Start with a little extra space
217 mSizer->Add(5, 55);
218 mSizer->Add(vSizer, 1, wxEXPAND);
219 mSizer->Add(5, 55);
220
221 // Layout the sizer
222 mSizer->Layout();
223
224 // Layout the toolbar
225 Layout();
226
227 SetMinSize(GetSizer()->GetMinSize());
228}
229
231{
232 bool isAudioSetupDown = false;
233
234 // ToolBar::ReCreateButtons() will get rid of its sizer and
235 // since we've attached our sizer to it, ours will get deleted too
236 // so clean ours up first.
237 if (mSizer)
238 {
239 isAudioSetupDown = mAudioSetup->IsDown();
240 Detach(mSizer);
241
242 std::unique_ptr < wxSizer > {mSizer}; // DELETE it
243 mSizer = nullptr;
244 }
245
247
248 if (isAudioSetupDown)
249 {
251 }
252
254
256}
257
258void AudioSetupToolBar::OnFocus(wxFocusEvent &event)
259{
260 KeyboardCapture::OnFocus( *this, event );
261}
262
263void AudioSetupToolBar::OnAudioSetup(wxCommandEvent& WXUNUSED(evt))
264{
265 // Be sure the pop-up happens even if there are exceptions, except for buttons which toggle.
266 auto cleanup = finally([&] { mAudioSetup->InteractionOver(); });
267
268 wxMenu menu;
269
270 AppendSubMenu(menu, mHost, "&Host");
271 menu.AppendSeparator();
272
273 AppendSubMenu(menu, mOutput, "&Playback Device");
274 menu.AppendSeparator();
275
276 AppendSubMenu(menu, mInput, "&Recording Device");
277 menu.AppendSeparator();
278
279 AppendSubMenu(menu, mInputChannels, "Recording &Channels");
280 menu.AppendSeparator();
281 menu.Append(kAudioSettings, _("&Audio Settings..."));
282
283 menu.Bind(wxEVT_MENU_CLOSE, [this](auto&) { mAudioSetup->PopUp(); });
284 menu.Bind(wxEVT_MENU, &AudioSetupToolBar::OnMenu, this);
285
286 wxWindow* btn = FindWindow(ID_AUDIO_SETUP_BUTTON);
287 wxRect r = btn->GetRect();
288 BasicMenu::Handle{ &menu }.Popup(
290 { r.GetLeft(), r.GetBottom() }
291 );
292}
293
295{
296 wxString desc;
297 const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
298 const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
299
300 auto selectedHost = GetSelectedRadioItemLabel(*mHost);
301 wxString oldHost = selectedHost ? *selectedHost : wxString{};
302
303 auto hostName = AudioIOHost.Read();
304
305 // if the prefs host name doesn't match the one displayed, it changed
306 // in another project's AudioSetupToolBar, so we need to repopulate everything.
307 if (oldHost != hostName)
309
310 auto devName = AudioIORecordingDevice.Read();
311 auto sourceName = AudioIORecordingSource.Read();
312 if (sourceName.empty())
313 desc = devName;
314 else
315 desc = devName + wxT(": ") + sourceName;
316
317 auto selectedInput = GetSelectedRadioItemLabel(*mInput);
318 if (*selectedInput != desc) {
319 if (auto item = mInput->FindItem(desc); item != wxNOT_FOUND) {
320 mInput->FindChildItem(item)->Check();
322 }
323 else if (mInput->GetMenuItemCount()) {
324 for (size_t i = 0; i < inMaps.size(); i++) {
325 if (inMaps[i].hostString == hostName &&
326 MakeDeviceSourceString(&inMaps[i]) == mInput->FindItem(kInput)->GetItemLabelText()) {
327 // use the default. It should exist but check just in case, falling back on the 0 index.
328 DeviceSourceMap* defaultMap = DeviceManager::Instance()->GetDefaultInputDevice(inMaps[i].hostIndex);
329 if (defaultMap) {
330 const auto menuId = mInput->FindItem(MakeDeviceSourceString(defaultMap));
331 auto item = mInput->FindChildItem(menuId);
332 if (item)
333 item->Check();
334
335 SetDevices(defaultMap, nullptr);
336 }
337 else {
338 //use the first item (0th index) if we have no familiar devices
339 auto item = mInput->FindChildItem(kInput);
340 if (item)
341 item->Check();
342
343 SetDevices(&inMaps[i], nullptr);
344 }
345 break;
346 }
347 }
348 }
349 }
350
351 devName = AudioIOPlaybackDevice.Read();
352 sourceName = AudioIOPlaybackSource.Read();
353 if (sourceName.empty())
354 desc = devName;
355 else
356 desc = devName + wxT(": ") + sourceName;
357
358 auto selectedOutput = GetSelectedRadioItemLabel(*mOutput);
359 if (*selectedOutput != desc) {
360 if (auto item = mOutput->FindItem(desc); item != wxNOT_FOUND) {
361 mOutput->FindChildItem(item)->Check();
362 }
363 else if (mOutput->GetMenuItemCount()) {
364 for (size_t i = 0; i < outMaps.size(); i++) {
365 if (outMaps[i].hostString == hostName &&
366 MakeDeviceSourceString(&outMaps[i]) == mOutput->FindItem(kOutput)->GetItemLabelText()) {
367 // use the default. It should exist but check just in case, falling back on the 0 index.
368 DeviceSourceMap* defaultMap = DeviceManager::Instance()->GetDefaultInputDevice(outMaps[i].hostIndex);
369 if (defaultMap) {
370 const auto menuId = mOutput->FindItem(MakeDeviceSourceString(defaultMap));
371 auto item = mOutput->FindChildItem(menuId);
372 if (item)
373 item->Check();
374
375 SetDevices(nullptr, defaultMap);
376 }
377 else {
378 //use the first item (0th index) if we have no familiar devices
379 auto item = mOutput->FindChildItem(kOutput);
380 if (item)
381 item->Check();
382
383 SetDevices(nullptr, &outMaps[i]);
384 }
385 break;
386 }
387 }
388 }
389 }
390
391 long oldChannels;
392 for (const auto & item : mInputChannels->GetMenuItems()) {
393 if (item->IsChecked())
394 oldChannels = item->GetId() - kInputChannels + 1;
395 }
396
397 auto newChannels = AudioIORecordChannels.ReadWithDefault(0);
398 if (newChannels > 0 && oldChannels != newChannels)
399 mInputChannels->FindChildItem(kInputChannels + newChannels - 1)->Check();
400
401 selectedHost = GetSelectedRadioItemLabel(*mHost);
402 if (!hostName.empty() && selectedHost && selectedHost != hostName) {
403 const auto id = mHost->FindItem(hostName);
404 if (id != wxNOT_FOUND) {
405 mHost->FindChildItem(id)->Check();
406 }
407 }
408
410
411 // Set label to pull in language change
412 SetLabel(XO("Audio Setup"));
413
414 // Give base class a chance
416
417 Layout();
418 Refresh();
419}
420
422{
423 if (id == DeviceToolbarPrefsID())
424 UpdatePrefs();
426}
427
428
430{
431 auto gAudioIO = AudioIOBase::Get();
432 if (gAudioIO) {
433 // we allow changes when monitoring, but not when recording
434 bool audioStreamActive = gAudioIO->IsStreamActive() && !gAudioIO->IsMonitoring();
435
436 if (audioStreamActive) {
438 }
439 else {
441 }
442 }
443}
444
446{
447#if wxUSE_TOOLTIPS
448 for (long iWinID = ID_AUDIO_SETUP_BUTTON; iWinID < BUTTON_COUNT; iWinID++)
449 {
450 auto pCtrl = static_cast<AButton*>(this->FindWindow(iWinID));
452 switch (iWinID)
453 {
455 name = wxT("Open Audio Setup");
456 break;
457 }
458 std::vector<ComponentInterfaceSymbol> commands(
459 1u, { name, Verbatim(pCtrl->GetLabel()) });
460
461 // Some have a second
462 switch (iWinID)
463 {
465 break;
466 }
468 mProject, *pCtrl, commands.data(), commands.size());
469 }
470#endif
471}
472
474{
475 FillHosts();
478 // make the device display selection reflect the prefs if they exist
479 UpdatePrefs();
480}
481
483{
484 const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
485 const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
486
487 wxArrayString hosts;
488
489 // go over our lists add the host to the list if it isn't there yet
490
491 for (auto & device : inMaps) {
492 if (!make_iterator_range(hosts).contains(device.hostString)) {
493 hosts.push_back(device.hostString);
494 }
495 }
496
497 for (auto & device : outMaps) {
498 if (!make_iterator_range(hosts).contains(device.hostString)) {
499 hosts.push_back(device.hostString);
500 }
501 }
502
503 mHost = std::make_unique<wxMenu>();
504
505 for (int i = 0; i < hosts.size(); ++i)
506 mHost->AppendRadioItem(kHost + i, hosts[i]);
507}
508
510{
511 const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
512 const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
513
514 //read what is in the prefs
515 auto host = AudioIOHost.Read();
516 int foundHostIndex = -1;
517
518 // if the host is not in the hosts combo then we rescanned.
519 // set it to blank so we search for another host.
520 if (mHost->FindItem(host) == wxNOT_FOUND) {
521 host = wxT("");
522 }
523
524 for (auto & device : outMaps) {
525 if (device.hostString == host) {
526 foundHostIndex = device.hostIndex;
527 break;
528 }
529 }
530
531 if (foundHostIndex == -1) {
532 for (auto & device : inMaps) {
533 if (device.hostString == host) {
534 foundHostIndex = device.hostIndex;
535 break;
536 }
537 }
538 }
539
540 // If no host was found based on the prefs device host, load the first available one
541 if (foundHostIndex == -1) {
542 if (outMaps.size()) {
543 foundHostIndex = outMaps[0].hostIndex;
544 }
545 else if (inMaps.size()) {
546 foundHostIndex = inMaps[0].hostIndex;
547 }
548 }
549
550 // Make sure in/out are clear in case no host was found
551 mInput = std::make_unique<wxMenu>();
552 mOutput = std::make_unique<wxMenu>();
553
554 // If we still have no host it means no devices, in which case do nothing.
555 if (foundHostIndex == -1) {
556 return;
557 }
558
559 // Repopulate the Input/Output device list available to the user
560 for (int nextMenuId = kInput, i = 0; i < inMaps.size(); ++i) {
561 auto& device = inMaps[i];
562
563 if (foundHostIndex == device.hostIndex) {
564 mInput->AppendRadioItem(nextMenuId, MakeDeviceSourceString(&device));
565 nextMenuId++;
566
567 if (host.empty()) {
568 host = device.hostString;
569 AudioIOHost.Write(host);
570
571 const auto id = mHost->FindItem(host);
572 if (id != wxNOT_FOUND) {
573 mHost->FindChildItem(id)->Check();
574 }
575 }
576 }
577 }
578
579 for (int nextMenuId = kOutput, i = 0; i < outMaps.size(); ++i) {
580 auto& device = outMaps[i];
581
582 if (foundHostIndex == device.hostIndex) {
583 mOutput->AppendRadioItem(nextMenuId, MakeDeviceSourceString(&device));
584 nextMenuId++;
585
586 if (host.empty()) {
587 host = device.hostString;
588 AudioIOHost.Write(host);
589 gPrefs->Flush();
590
591 const auto id = mHost->FindItem(host);
592 if (id != wxNOT_FOUND) {
593 mHost->FindChildItem(id)->Check();
594 }
595 }
596 }
597 }
598
599 // The setting of the Device is left up to OnMenu
600}
601
603{
604 const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
605 auto host = AudioIOHost.Read();
606 auto device = AudioIORecordingDevice.Read();
607 auto source = AudioIORecordingSource.Read();
608 long newChannels;
609
610 auto oldChannels = AudioIORecordChannels.Read();
611 mInputChannels = std::make_unique<wxMenu>();
612
613 for (auto & dev: inMaps) {
614 if (source == dev.sourceString &&
615 device == dev.deviceString &&
616 host == dev.hostString) {
617
618 // add one selection for each channel of this source
619 for (size_t j = 0; j < (unsigned int)dev.numChannels; j++) {
620 wxString name;
621
622 if (j == 0) {
623 name = _("1 (Mono) Recording Channel");
624 }
625 else if (j == 1) {
626 name = _("2 (Stereo) Recording Channels");
627 }
628 else {
629 name = wxString::Format(wxT("%d"), (int)j + 1);
630 }
631 mInputChannels->AppendRadioItem(kInputChannels + j, name);
632 }
633 newChannels = dev.numChannels;
634 if (oldChannels <= newChannels && oldChannels >= 1) {
635 newChannels = oldChannels;
636 }
637 if (newChannels >= 1) {
638 mInputChannels->FindItem(kInputChannels + newChannels - 1)->Check();
639 }
640 AudioIORecordChannels.Write(newChannels);
641 break;
642 }
643 }
644}
645
646std::unique_ptr<wxMenu> AudioSetupToolBar::CloneMenu(const wxMenu& menu) const
647{
648 auto clonedMenu = std::make_unique<wxMenu>();
649
650 for (const auto& item : menu.GetMenuItems()) {
651 auto cloneMenuItem = clonedMenu->AppendRadioItem(item->GetId(), item->GetItemLabelText());
652
653 if (item->IsChecked())
654 cloneMenuItem->Check();
655 }
656
657 return clonedMenu;
658}
659
660void AudioSetupToolBar::AppendSubMenu(wxMenu& menu, const std::unique_ptr<wxMenu>& submenu, const wxString& title)
661{
662 auto clone = CloneMenu(*submenu);
663 auto menuItem = menu.AppendSubMenu(clone.release(), title);
664
665 const auto selected = GetSelectedRadioItemLabel(*submenu);
666 if (!selected) {
667 menuItem->Enable(false);
668 }
669}
670
671std::optional<wxString> AudioSetupToolBar::GetSelectedRadioItemLabel(const wxMenu& menu) const
672{
673 const auto& items = menu.GetMenuItems();
674
675 for (const auto& item : items) {
676 if (item->IsChecked())
677 return item->GetItemLabelText();
678 }
679
680 return std::nullopt;
681}
682
684{
685 // Hosts may have disappeared or appeared so a complete repopulate is needed.
688}
689
690//return true if host changed, false otherwise.
692{
693 auto item = mHost->FindChildItem(hostId);
694 if (!item)
695 return false;
696
697 // Update cache with selected host
698 item->Check();
699
700 auto oldHost = AudioIOHost.Read();
701 wxString newHost = item->GetItemLabelText();
702
703 if (oldHost == newHost)
704 return false;
705
706 //change the host and switch to correct devices.
707 AudioIOHost.Write(newHost);
708 gPrefs->Flush();
709
710 // populate the devices
712
713 return true;
714}
715
717{
718 if (in) {
721 if (in->totalSources >= 1)
723 else
725 gPrefs->Flush();
726
728 }
729
730 if (out) {
732 if (out->totalSources >= 1) {
734 } else {
736 }
737 gPrefs->Flush();
738 }
739}
740
741void AudioSetupToolBar::ChangeDevice(int deviceId, bool isInput)
742{
743 int newIndex = -1;
744 auto& device = isInput ? mInput : mOutput;
745
746 auto host = AudioIOHost.Read();
747 const std::vector<DeviceSourceMap>& maps = isInput ? DeviceManager::Instance()->GetInputDeviceMaps()
749
750 auto item = device->FindChildItem(deviceId);
751 if (item) {
752 // Update cache with the chosen device
753 item->Check();
754 wxString newDevice = item->GetItemLabelText();
755
756 for (size_t i = 0; i < maps.size(); ++i) {
757 wxString name = MakeDeviceSourceString(&maps[i]);
758 if (name == newDevice && maps[i].hostString == host) {
759 newIndex = i;
760 }
761 }
762 }
763
764 if (newIndex < 0) {
765 wxLogDebug(wxT("AudioSetupToolBar::OnMenu(): couldn't find device indices"));
766 return;
767 }
768
769 SetDevices(isInput ? &maps[newIndex] : nullptr,
770 isInput ? nullptr : &maps[newIndex]);
771}
772
773void AudioSetupToolBar::OnMenu(wxCommandEvent& event)
774{
775 int id = event.GetId();
776 bool audioSettingsChosen = false;
777
778 if ((id >= kHost) && (id < kInput)) {
779 ChangeHost(id);
780 }
781 else if ((id >= kInputChannels) && (id < kOutput)) {
782 if (auto item = mInputChannels->FindChildItem(id)) {
783 // Update cache with selected number of input channels
784 item->Check();
786 }
787 }
788 else if ((id >= kInput) && (id < kInputChannels)) {
789 ChangeDevice(id, true);
790 }
791 else if ((id >= kOutput) && (id < kAudioSettings)) {
792 ChangeDevice(id, false);
793 }
794 else if (id == kAudioSettings) {
795 audioSettingsChosen = true;
796 }
797
798 auto gAudioIO = AudioIOBase::Get();
799 if (gAudioIO) {
800 // We cannot have gotten here if gAudioIO->IsAudioTokenActive(),
801 // per the setting of AudioIONotBusyFlag and AudioIOBusyFlag in
802 // AudacityProject::GetUpdateFlags().
803 // However, we can have an invalid audio token (so IsAudioTokenActive()
804 // is false), but be monitoring.
805 // If monitoring, have to stop the stream, so HandleDeviceChange() can work.
806 // We could disable the Preferences command while monitoring, i.e.,
807 // set AudioIONotBusyFlag/AudioIOBusyFlag according to monitoring, as well.
808 // Instead allow it because unlike recording, for example, monitoring
809 // is not clearly something that should prohibit changing device.
810 // TODO: We *could* be smarter in this method and call HandleDeviceChange()
811 // only when the device choices actually changed. True of lots of prefs!
812 // As is, we always stop monitoring before handling the device change.
813 if (gAudioIO->IsMonitoring())
814 {
815 gAudioIO->StopStream();
816 while (gAudioIO->IsBusy()) {
817 using namespace std::chrono;
818 std::this_thread::sleep_for(100ms);
819 }
820 }
821
822 if (audioSettingsChosen) {
823 PrefsPanel::Factories factories;
824 factories.push_back(PrefsPanel::PrefsNode(DevicePrefsFactory));
825
826 ViewDeviceSettingsDialog dialog(&GetProjectFrame(mProject), mProject, XO("Audio Settings:"), factories, 0);
827 dialog.SetSize(600, 420);
828 dialog.Center();
829
830 if (0 != dialog.ShowModal()) {
832 }
833 }
834 else {
835 gAudioIO->HandleDeviceChange();
837 }
838 }
839}
840
842 []( AudacityProject &project ){
843 return ToolBar::Holder{ safenew AudioSetupToolBar{ project } };
844 }
845};
846
847namespace {
849 /* i18n-hint: Clicking this menu item shows the toolbar
850 that manages the audio devices */
851 AudioSetupBarID, wxT("ShowAudioSetupTB"), XXO("&Audio Setup Toolbar")
852};
853}
854
StringSetting AudioIORecordingSource
StringSetting AudioIOPlaybackSource
StringSetting AudioIOPlaybackDevice
StringSetting AudioIORecordingDevice
StringSetting AudioIOHost
IntSetting AudioIORecordingSourceIndex
IntSetting AudioIORecordChannels
static RegisteredToolbarFactory factory
IMPLEMENT_CLASS(AudioSetupToolBar, ToolBar)
END_EVENT_TABLE()
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
DeviceChangeMessage
Definition: DeviceChange.h:16
wxString MakeDeviceSourceString(const DeviceSourceMap *map)
PrefsPanel * DevicePrefsFactory(wxWindow *parent, wxWindowID winid, AudacityProject *)
int DeviceToolbarPrefsID()
Methods for DeviceToolBar.
const TranslatableString name
Definition: Distortion.cpp:82
const TranslatableString desc
Definition: ExportPCM.cpp:58
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define _(s)
Definition: Internat.h:75
#define safenew
Definition: MemoryX.h:10
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:423
static const auto title
FileConfig * gPrefs
Definition: Prefs.cpp:71
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
THEME_API Theme theTheme
Definition: Theme.cpp:82
@ AudioSetupBarID
Definition: ToolBar.h:86
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
A wxButton with mouse-over behaviour.
Definition: AButton.h:25
void PushDown()
Definition: AButton.cpp:597
bool IsDown()
Definition: AButton.h:113
void InteractionOver()
Definition: AButton.h:122
void Disable()
Definition: AButton.cpp:580
void Enable()
Definition: AButton.cpp:571
void PopUp()
Definition: AButton.cpp:605
void SetLabel(const TranslatableString &label)
Definition: AButton.cpp:274
static void Bevel(wxDC &dc, bool up, const wxRect &r)
Definition: AColor.cpp:266
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:91
A toolbar to allow easier changing of input and output devices .
void SetDevices(const DeviceSourceMap *in, const DeviceSourceMap *out)
void UpdateSelectedPrefs(int) override
void RegenerateTooltips() override
std::unique_ptr< wxMenu > mHost
void Populate() override
void OnAudioSetup(wxCommandEvent &event)
static AudioSetupToolBar & Get(AudacityProject &project)
void EnableDisableButtons() override
void OnMenu(wxCommandEvent &event)
void ChangeDevice(int deviceId, bool isInput)
void Repaint(wxDC *dc) override
std::optional< wxString > GetSelectedRadioItemLabel(const wxMenu &menu) const
void OnRescannedDevices(DeviceChangeMessage)
std::unique_ptr< wxMenu > mInputChannels
void AppendSubMenu(wxMenu &menu, const std::unique_ptr< wxMenu > &submenu, const wxString &title)
void Create(wxWindow *parent) override
std::unique_ptr< wxMenu > mInput
void ReCreateButtons() override
std::unique_ptr< wxMenu > mOutput
bool ChangeHost(int hostId)
std::unique_ptr< wxMenu > CloneMenu(const wxMenu &menu) const
void OnFocus(wxFocusEvent &event)
void UpdatePrefs() override
void Popup(const BasicUI::WindowPlacement &window, const Point &pos={})
Display the menu at pos, invoke at most one action, then hide it.
Definition: BasicMenu.cpp:209
size_t size() const
How many attachment pointers are in the Site.
Definition: ClientData.h:251
DeviceSourceMap * GetDefaultInputDevice(int hostIndex)
const std::vector< DeviceSourceMap > & GetInputDeviceMaps()
const std::vector< DeviceSourceMap > & GetOutputDeviceMaps()
static DeviceManager * Instance()
Gets the singleton instance.
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
Dialog that shows the current PrefsPanel in a tabbed divider.
Definition: PrefsDialog.h:35
static void Broadcast(int id=0)
Call this static function to notify all PrefsListener objects.
Definition: Prefs.cpp:98
virtual void UpdateSelectedPrefs(int id)
Definition: Prefs.cpp:128
std::vector< PrefsPanel::PrefsNode > Factories
Definition: PrefsPanel.h:69
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:252
bool ReadWithDefault(T *pVar, const T &defaultValue) const
overload of ReadWithDefault returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:206
bool Reset()
Reset to the default value.
Definition: Prefs.h:277
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
wxColour & Colour(int iIndex)
wxSize ImageSize(int iIndex)
Works with ToolManager and ToolDock to provide a dockable window in which buttons can be placed.
Definition: ToolBar.h:99
AudacityProject & mProject
Definition: ToolBar.h:248
static void MakeRecoloredImageSize(teBmps eBmpOut, teBmps eBmpIn, const wxSize &size)
Definition: ToolBar.cpp:782
void Add(wxWindow *window, int proportion=0, int flag=wxALIGN_TOP, int border=0, wxObject *userData=NULL)
Definition: ToolBar.cpp:686
virtual void ReCreateButtons()
Definition: ToolBar.cpp:516
static AButton * MakeButton(wxWindow *parent, teBmps eUp, teBmps eDown, teBmps eHilite, teBmps eDownHi, teBmps eStandardUp, teBmps eStandardDown, teBmps eDisabled, wxWindowID id, wxPoint placement, bool processdownevents, wxSize size)
Definition: ToolBar.cpp:852
void SetLabel(const wxString &label) override
Definition: ToolBar.cpp:398
wxBoxSizer * GetSizer()
Definition: ToolBar.cpp:678
void UpdatePrefs() override
Definition: ToolBar.cpp:605
static void MakeMacRecoloredImageSize(teBmps eBmpOut, teBmps eBmpIn, const wxSize &size)
Definition: ToolBar.cpp:770
virtual void Create(wxWindow *parent)
Definition: ToolBar.cpp:475
void Detach(wxWindow *window)
Definition: ToolBar.cpp:752
wxWindowPtr< ToolBar > Holder
Definition: ToolBar.h:103
static void SetButtonToolTip(AudacityProject &project, AButton &button, const ComponentInterfaceSymbol commands[], size_t nCommands)
Definition: ToolBar.cpp:947
static ToolManager & Get(AudacityProject &project)
Holds a msgid for the translation catalog; may also bind format arguments.
ViewDeviceSettingsDialog(wxWindow *parent, AudacityProject &project, const TranslatableString &title, PrefsPanel::Factories &factories, int page)
void OnFocus(wxWindow &window, wxFocusEvent &event)
a function useful to implement a focus event handler The window releases the keyboard if the event is...
wxString sourceString
Definition: DeviceManager.h:34
wxString deviceString
Definition: DeviceManager.h:35
Window placement information for wxWidgetsBasicUI can be constructed from a wxWindow pointer.