Audacity  2.2.2
DeviceToolBar.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  DeviceToolBar.cpp
6 
7  Dominic Mazzoni
8 
9 *******************************************************************//*******************************************************************/
15 
16 
17 #include "../Audacity.h"
18 #include "DeviceToolBar.h"
19 
20 // For compilers that support precompilation, includes "wx/wx.h".
21 #include <wx/wxprec.h>
22 
23 #ifndef WX_PRECOMP
24 #include <wx/choice.h>
25 #include <wx/event.h>
26 #include <wx/intl.h>
27 #include <wx/settings.h>
28 #include <wx/sizer.h>
29 #include <wx/statbmp.h>
30 #include <wx/tooltip.h>
31 #endif
32 
33 #include "ToolDock.h"
34 #include "../TrackPanel.h"
35 
36 #include "../AColor.h"
37 #include "../AllThemeResources.h"
38 #include "../AudioIO.h"
39 #include "../ImageManipulation.h"
40 #include "../Prefs.h"
41 #include "../Project.h"
42 #include "../ShuttleGui.h"
43 #include "../Theme.h"
44 #include "../widgets/Grabber.h"
45 #include "../DeviceManager.h"
46 #include "../widgets/ErrorDialog.h"
47 #include "../widgets/Grabber.h"
48 
49 #if wxUSE_ACCESSIBILITY
50 #include "../widgets/WindowAccessible.h"
51 #endif
52 
54 
58 
59 BEGIN_EVENT_TABLE(DeviceToolBar, ToolBar)
60  EVT_CHOICE(wxID_ANY, DeviceToolBar::OnChoice)
61  EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, DeviceToolBar::OnCaptureKey)
63 
64 //Standard contructor
66 : ToolBar(DeviceBarID, _("Device"), wxT("Device"), true)
67 {
68 }
69 
71 {
72 }
73 
74 void DeviceToolBar::Create(wxWindow *parent)
75 {
76  ToolBar::Create(parent);
77 
78  // Simulate a size event to set initial meter placement/size
79  wxSizeEvent event(GetSize(), GetId());
80  event.SetEventObject(this);
81  GetEventHandler()->ProcessEvent(event);
82 }
83 
85 {
86  mInput = NULL;
87  mOutput = NULL;
88  mInputChannels = NULL;
89  mHost = NULL;
90 }
91 
93 {
94  SetBackgroundColour( theTheme.Colour( clrMedium ) );
96  // Hosts
97  mHost = safenew wxChoice(this,
98  wxID_ANY,
99  wxDefaultPosition,
100  wxDefaultSize);
101 #if wxUSE_ACCESSIBILITY
102  // so that name can be set on a standard control
103  mHost->SetAccessible(safenew WindowAccessible(mHost));
104 #endif
105  Add(mHost, 0, wxALIGN_CENTER);
106 
107  // Input device
109  wxID_ANY,
110  theTheme.Bitmap(bmpMic)), 0, wxALIGN_CENTER);
111  mInput = safenew wxChoice(this,
112  wxID_ANY,
113  wxDefaultPosition,
114  wxDefaultSize);
115 #if wxUSE_ACCESSIBILITY
116  // so that name can be set on a standard control
117  mInput->SetAccessible(safenew WindowAccessible(mInput));
118 #endif
119  // Input channels
120  Add(mInput, 0, wxALIGN_CENTER);
121  mInputChannels = safenew wxChoice(this,
122  wxID_ANY,
123  wxDefaultPosition,
124  wxDefaultSize);
125 #if wxUSE_ACCESSIBILITY
126  // so that name can be set on a standard control
128 #endif
129  Add(mInputChannels, 0, wxALIGN_CENTER);
130 
131  // Output device
133  wxID_ANY,
134  theTheme.Bitmap(bmpSpeaker)), 0, wxALIGN_CENTER);
135  mOutput = safenew wxChoice(this,
136  wxID_ANY,
137  wxDefaultPosition,
138  wxDefaultSize);
139 #if wxUSE_ACCESSIBILITY
140  // so that name can be set on a standard control
141  mOutput->SetAccessible(safenew WindowAccessible(mOutput));
142 #endif
143  Add(mOutput, 0, wxALIGN_CENTER);
144 
145 
146 
147 
148  mHost->Bind(wxEVT_SET_FOCUS,
150  this);
151  mHost->Bind(wxEVT_KILL_FOCUS,
153  this);
154  mOutput->Bind(wxEVT_SET_FOCUS,
156  this);
157  mOutput->Bind(wxEVT_KILL_FOCUS,
159  this);
160  mInput->Bind(wxEVT_SET_FOCUS,
162  this);
163  mInput->Bind(wxEVT_KILL_FOCUS,
165  this);
166  mInputChannels->Bind(wxEVT_SET_FOCUS,
168  this);
169  mInputChannels->Bind(wxEVT_KILL_FOCUS,
171  this);
172 
173  SetNames();
174 
175  RefillCombos();
176 }
177 
179 {
180  FillHosts();
181  FillHostDevices();
183  // make the device display selection reflect the prefs if they exist
184  UpdatePrefs();
185 }
186 
187 void DeviceToolBar::OnFocus(wxFocusEvent &event)
188 {
189  if (event.GetEventType() == wxEVT_KILL_FOCUS) {
191  }
192  else {
194  }
195 
196  Refresh(false);
197 
198  event.Skip();
199 }
200 
201 void DeviceToolBar::OnCaptureKey(wxCommandEvent &event)
202 {
203  wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject();
204  int keyCode = kevent->GetKeyCode();
205 
206  // Pass UP/DOWN/LEFT/RIGHT through for input/output choice
207  if (FindFocus() == mInput && (keyCode == WXK_LEFT || keyCode == WXK_RIGHT
208  || keyCode == WXK_UP || keyCode == WXK_DOWN)) {
209  return;
210  }
211 
212  if (FindFocus() == mOutput && (keyCode == WXK_LEFT || keyCode == WXK_RIGHT
213  || keyCode == WXK_UP || keyCode == WXK_DOWN)) {
214  return;
215  }
216  event.Skip();
217 
218  return;
219 }
220 
222 {
223  wxString hostName;
224  wxString devName;
225  wxString sourceName;
226  wxString desc;
227  const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
228  const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
229 
230 
231  int hostSelectionIndex = mHost->GetSelection();
232  wxString oldHost = hostSelectionIndex >= 0 ? mHost->GetString(hostSelectionIndex) :
233  wxT("");
234  hostName = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
235 
236  // if the prefs host name doesn't match the one displayed, it changed
237  // in another project's DeviceToolBar, so we need to repopulate everything.
238  if (oldHost != hostName)
239  FillHostDevices();
240 
241  devName = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
242  sourceName = gPrefs->Read(wxT("/AudioIO/RecordingSource"), wxT(""));
243  if (sourceName == wxT(""))
244  desc = devName;
245  else
246  desc = devName + wxT(": ") + sourceName;
247 
248  if (mInput->GetStringSelection() != desc &&
249  mInput->FindString(desc) != wxNOT_FOUND) {
250  mInput->SetStringSelection(desc);
252  } else if (mInput->GetStringSelection() != desc && mInput->GetCount()) {
253  for (size_t i = 0; i < inMaps.size(); i++) {
254  if (inMaps[i].hostString == hostName &&
255  MakeDeviceSourceString(&inMaps[i]) == mInput->GetString(0)) {
256  // use the default. It should exist but check just in case, falling back on the 0 index.
257  DeviceSourceMap *defaultMap = DeviceManager::Instance()->GetDefaultInputDevice(inMaps[i].hostIndex);
258  if (defaultMap) {
259  mInput->SetStringSelection(MakeDeviceSourceString(defaultMap));
260  SetDevices(defaultMap, NULL);
261  } else {
262  //use the first item (0th index) if we have no familiar devices
263  mInput->SetSelection(0);
264  SetDevices(&inMaps[i], NULL);
265  }
266  break;
267  }
268  }
269  }
270 
271  devName = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
272  sourceName = gPrefs->Read(wxT("/AudioIO/PlaybackSource"), wxT(""));
273  if (sourceName == wxT(""))
274  desc = devName;
275  else
276  desc = devName + wxT(": ") + sourceName;
277 
278  if (mOutput->GetStringSelection() != desc &&
279  mOutput->FindString(desc) != wxNOT_FOUND) {
280  mOutput->SetStringSelection(desc);
281  } else if (mOutput->GetStringSelection() != desc &&
282  mOutput->GetCount()) {
283  for (size_t i = 0; i < outMaps.size(); i++) {
284  if (outMaps[i].hostString == hostName &&
285  MakeDeviceSourceString(&outMaps[i]) == mOutput->GetString(0)) {
286  // use the default. It should exist but check just in case, falling back on the 0 index.
287  DeviceSourceMap *defaultMap = DeviceManager::Instance()->GetDefaultOutputDevice(outMaps[i].hostIndex);
288  if (defaultMap) {
289  mOutput->SetStringSelection(MakeDeviceSourceString(defaultMap));
290  SetDevices(NULL, defaultMap);
291  } else {
292  //use the first item (0th index) if we have no familiar devices
293  mOutput->SetSelection(0);
294  SetDevices(NULL, &outMaps[i]);
295  }
296  break;
297  }
298  }
299  }
300 
301  long oldChannels, newChannels;
302  oldChannels = mInputChannels->GetSelection() + 1;
303  gPrefs->Read(wxT("/AudioIO/RecordChannels"), &newChannels, 0);
304  if (newChannels > 0 && oldChannels != newChannels)
305  mInputChannels->SetSelection(newChannels - 1);
306 
307  if (hostName != wxT("") && mHost->GetStringSelection() != hostName)
308  mHost->SetStringSelection(hostName);
309 
311 
312  // Set label to pull in language change
313  SetLabel(_("Device"));
314 
315  // Give base class a chance
317 
318  Layout();
319  Refresh();
320 }
321 
322 
324 {
325  if (gAudioIO) {
326  // we allow changes when monitoring, but not when recording
327  bool audioStreamActive = gAudioIO->IsStreamActive() && !gAudioIO->IsMonitoring();
328 
329  // Here we should relinquish focus
330  if (audioStreamActive) {
331  wxWindow *focus = wxWindow::FindFocus();
332  if (focus == mHost || focus == mInput || focus == mOutput || focus == mInputChannels) {
333  AudacityProject *activeProject = GetActiveProject();
334  if (activeProject) {
335  activeProject->GetTrackPanel()->SetFocus();
336  }
337  }
338  }
339 
340  mHost->Enable(!audioStreamActive);
341  mInput->Enable(!audioStreamActive);
342  mOutput->Enable(!audioStreamActive);
343  mInputChannels->Enable(!audioStreamActive);
344  }
345 }
346 
348 {
349  /* i18n-hint: (noun) It's the device used for playback.*/
350  mOutput->SetName(_("Playback Device"));
351  /* i18n-hint: (noun) It's the device used for recording.*/
352  mInput->SetName(_("Recording Device"));
353  mHost->SetName(_("Audio Host"));
354  mInputChannels->SetName(_("Recording Channels"));
355 }
356 
358 {
359 #if wxUSE_TOOLTIPS
360  SetNames();
361  mOutput->SetToolTip(mOutput->GetName() + wxT(" - ") + mOutput->GetStringSelection());
362  mInput->SetToolTip(mInput->GetName() + wxT(" - ") + mInput->GetStringSelection());
363  mHost->SetToolTip(mHost->GetName() + wxT(" - ") + mHost->GetStringSelection());
364  mInputChannels->SetToolTip(mInputChannels->GetName() + wxT(" - ") + mInputChannels->GetStringSelection());
365 #endif
366 }
367 
369 {
370  bool ret;
372  ret = ToolBar::Layout();
373  return ret;
374 }
375 
376 // returns true if the combo is constrained and false otherwise
377 // @param toolbarWidth the width of the toolbar in pixels
378 // @param ratio an in/out for the desired and resultant width ratio.
379 // @param flex the amount of extra space allowed to have past the available.
380 // the amount used is subtracted.
381 static bool RepositionCombo(wxWindow *combo, int toolbarWidth, wxSize desiredSize,
382  float &ratio, float &flex, int marginPixels, bool changesRatio)
383 {
384  float ratioChange;
385  bool constrained = false;
386 
387  // push margin pixels
388  desiredSize.x += marginPixels;
389 
390  // truncate the window size if necessary
391  if (desiredSize.x > toolbarWidth * (flex + ratio)) {
392  constrained = true;
393  desiredSize.SetWidth(toolbarWidth * (flex + ratio));
394  if (desiredSize.GetWidth() - marginPixels < 0)
395  desiredSize.SetWidth(marginPixels);
396  }
397 
398  // keep track of how much space gained or lost so it can be used by other combos.
399  if (changesRatio) {
400  ratioChange = (desiredSize.x / ((float) toolbarWidth)) - ratio;
401  ratio += ratioChange;
402  flex -= ratioChange;
403  }
404 
405  // pop the margin pixels
406  desiredSize.x -= marginPixels;
407 
408  combo->SetMinSize(desiredSize);
409 
410  return constrained;
411 }
412 
413 //These don't add up to 1 because there is a bit of margin that we allow
414 //the layout sizer to handle.
415 #define kHostWidthRatio 0.13f
416 #define kInputWidthRatio 0.32f
417 #define kOutputWidthRatio 0.32f
418 #define kChannelsWidthRatio 0.18f
419 
421 {
422  int w, h, dockw, dockh;
423  float ratioUnused;
424  bool constrained = true;
425  wxWindow *window;
426  wxSize desiredInput, desiredOutput, desiredHost, desiredChannels;
427  float hostRatio, outputRatio, inputRatio, channelsRatio;
428  // if the toolbar is docked then the width we should use is the project width.
429  // as the toolbar's with can extend past this.
430  GetClientSize(&w, &h);
431 
432  // FIXME: Note that there's some bug in here, in that even if the prefs show the toolbar
433  // docked, on initialization, this call to IsDocked() returns false.
434  if (IsDocked()) {
435  // If the toolbar is docked its width can be larger than what is actually viewable
436  // So take the min. We don't need to worry about having another toolbar to the left off us
437  // because if we are larger than the dock size we always get our own row.
438  // and if smaller then we don't use the dock size (because we take the min).
439  window = GetDock();
440  window->GetClientSize(&dockw, &dockh);
441  // The fudge factor here is because the docked toolbar is 2 pixels smaller.
442  int kFudge1 = -2;
443  // Grabber is Included, but not resizer if docked.
444  w-= grabberWidth + kFudge1;
445  if (dockw < w)
446  w = dockw;
447  } else {
448  // If not docked, we have a holding window.
449  wxWindow *pParent = GetParent();
450  if( pParent ){
451  int w1, h1;
452  pParent->GetSize( &w1, &h1 );
453  // This Resizer is considerably bigger than the 4px docked version.
454  // It's the diagonal striped resizer, not the veertical bars.
455  int kStripyResizerSize = 15;
456  // Grabber AND resizer included.
457  w = w1- (kStripyResizerSize + grabberWidth );
458  }
459  }
460  if (w <= 0)
461  return;
462 
463  // set up initial sizes and ratios
464  // Note that the y values of the desired sizes are not changed, so that the height
465  // of the toolbar is not changed
466  hostRatio = kHostWidthRatio;
467  inputRatio = kInputWidthRatio;
468  outputRatio = kOutputWidthRatio;
469  channelsRatio = kChannelsWidthRatio;
470 
471  desiredHost.x = mHost->GetBestSize().x *4;
472  desiredHost.y = mHost->GetSize().y;
473  desiredInput.x = mInput->GetBestSize().x *4;
474  desiredInput.y = mInput->GetSize().y;
475  desiredOutput.x = mOutput->GetBestSize().x *4;
476  desiredOutput.y = mOutput->GetSize().y;
477  desiredChannels.x = mInputChannels->GetBestSize().x *4;
478  desiredChannels.y = mInputChannels->GetSize().y;
479 
480  // wxGtk (Gnome) has larger comboboxes than the other platforms. For DeviceToolBar this prevents
481  // the toolbar docking on a single height row. So we shrink it to prevent this.
482 #ifdef __WXGTK__
483  desiredHost.SetHeight(mHost->GetBestSize().y -4);
484  desiredInput.SetHeight(desiredHost.GetHeight());
485  desiredOutput.SetHeight(desiredHost.GetHeight());
486  desiredChannels.SetHeight(desiredHost.GetHeight());
487 #endif
488 
490  int i = 0;
491  // limit the amount of times we solve contraints to 5
492  // As we now ask for more than is available, we only do this iteration once.
493  while (constrained && ratioUnused > 0.01f && i < 5) {
494  i++;
495  constrained = RepositionCombo(mHost, w, desiredHost, hostRatio, ratioUnused, 0, true);
496  constrained |= RepositionCombo(mInput, w, desiredInput, inputRatio, ratioUnused, theTheme.Bitmap(bmpMic).GetWidth(), true);
497  constrained |= RepositionCombo(mOutput, w, desiredOutput, outputRatio, ratioUnused, theTheme.Bitmap(bmpSpeaker).GetWidth(), true);
498  constrained |= RepositionCombo(mInputChannels, w, desiredChannels, channelsRatio, ratioUnused, 0, true);
499  }
500 
501  Update();
502 }
503 
505 {
506  wxArrayString hosts;
507  size_t i;
508 
509  const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
510  const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
511  // go over our lists add the host to the list if it isn't there yet
512  for (i = 0; i < inMaps.size(); i++)
513  if (hosts.Index(inMaps[i].hostString) == wxNOT_FOUND)
514  hosts.Add(inMaps[i].hostString);
515  for (i = 0; i < outMaps.size(); i++)
516  if (hosts.Index(outMaps[i].hostString) == wxNOT_FOUND)
517  hosts.Add(outMaps[i].hostString);
518 
519  mHost->Clear();
520  mHost->Append(hosts);
521 
522  if (hosts.GetCount() == 0)
523  mHost->Enable(false);
524 
525  mHost->InvalidateBestSize();
526  mHost->SetMaxSize(mHost->GetBestSize()*4);
527 }
528 
530 {
531  const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
532  const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
533 
534  //read what is in the prefs
535  wxString host = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
536  size_t i;
537  int foundHostIndex = -1;
538 
539  // if the host is not in the hosts combo then we rescanned.
540  // set it to blank so we search for another host.
541  if (mHost->FindString(host) == wxNOT_FOUND)
542  host = wxT("");
543 
544  for (i = 0; i < outMaps.size(); i++) {
545  if (outMaps[i].hostString == host) {
546  foundHostIndex = outMaps[i].hostIndex;
547  break;
548  }
549  }
550 
551  if (foundHostIndex == -1) {
552  for (i = 0; i < inMaps.size(); i++) {
553  if (inMaps[i].hostString == host) {
554  foundHostIndex = inMaps[i].hostIndex;
555  break;
556  }
557  }
558  }
559 
560  // If no host was found based on the prefs device host, load the first available one
561  if (foundHostIndex == -1) {
562  if (outMaps.size())
563  foundHostIndex = outMaps[0].hostIndex;
564  else if (inMaps.size())
565  foundHostIndex = inMaps[0].hostIndex;
566  }
567 
568  // Make sure in/out are clear in case no host was found
569  mInput->Clear();
570  mOutput->Clear();
571 
572  // If we still have no host it means no devices, in which case do nothing.
573  if (foundHostIndex == -1)
574  return;
575 
576  // Repopulate the Input/Output device list available to the user
577  for (i = 0; i < inMaps.size(); i++) {
578  if (foundHostIndex == inMaps[i].hostIndex) {
579  mInput->Append(MakeDeviceSourceString(&inMaps[i]));
580  if (host == wxT("")) {
581  host = inMaps[i].hostString;
582  gPrefs->Write(wxT("/AudioIO/Host"), host);
583  mHost->SetStringSelection(host);
584  }
585  }
586  }
587  mInput->Enable(mInput->GetCount() ? true : false);
588 
589  mInput->InvalidateBestSize();
590  mInput->SetMaxSize(mInput->GetBestSize()*4);
591 
592  for (i = 0; i < outMaps.size(); i++) {
593  if (foundHostIndex == outMaps[i].hostIndex) {
594  mOutput->Append(MakeDeviceSourceString(&outMaps[i]));
595  if (host == wxT("")) {
596  host = outMaps[i].hostString;
597  gPrefs->Write(wxT("/AudioIO/Host"), host);
598  gPrefs->Flush();
599  mHost->SetStringSelection(host);
600  }
601  }
602  }
603  mOutput->Enable(mOutput->GetCount() ? true : false);
604 
605  mOutput->InvalidateBestSize();
606  mOutput->SetMaxSize(mOutput->GetBestSize()*4);
607 
608  // The setting of the Device is left up to OnChoice
609 }
610 
611 //return 1 if host changed, 0 otherwise.
613 {
614  int hostSelectionIndex;
615  hostSelectionIndex = mHost->GetSelection();
616 
617  wxString oldHost = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
618  wxString newHost = hostSelectionIndex >= 0 ? mHost->GetString(hostSelectionIndex) :
619  oldHost;
620 
621  if (oldHost == newHost)
622  return 0;
623 
624  //change the host and switch to correct devices.
625  gPrefs->Write(wxT("/AudioIO/Host"), newHost);
626  gPrefs->Flush();
627 
628  // populate the devices
629  FillHostDevices();
630 
631  return 1;
632 }
633 
635 {
636  const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
637  wxString host = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
638  wxString device = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
639  wxString source = gPrefs->Read(wxT("/AudioIO/RecordingSource"), wxT(""));
640  long oldChannels = 2, newChannels;
641 
642  gPrefs->Read(wxT("/AudioIO/RecordChannels"), &oldChannels);
643  int index = -1;
644  size_t i, j;
645  mInputChannels->Clear();
646  for (i = 0; i < inMaps.size(); i++) {
647  if (source == inMaps[i].sourceString &&
648  device == inMaps[i].deviceString &&
649  host == inMaps[i].hostString) {
650 
651  // add one selection for each channel of this source
652  for (j = 0; j < (unsigned int) inMaps[i].numChannels; j++) {
653  wxString name;
654 
655  if (j == 0) {
656  name = _("1 (Mono) Recording Channel");
657  }
658  else if (j == 1) {
659  name = _("2 (Stereo) Recording Channels");
660  }
661  else {
662  name = wxString::Format(wxT("%d"), (int) j + 1);
663  }
664  mInputChannels->Append(name);
665  }
666  newChannels = inMaps[i].numChannels;
667  if (oldChannels <= newChannels && oldChannels >= 1)
668  newChannels = oldChannels;
669  if (newChannels >= 1)
670  mInputChannels->SetSelection(newChannels - 1);
671  gPrefs->Write(wxT("/AudioIO/RecordChannels"), newChannels);
672  mInputChannels->Enable(mInputChannels->GetCount() ? true : false);
673  index = i;
674  break;
675  }
676  }
677  if (index == -1)
678  mInputChannels->Enable(false);
679 
680  mInputChannels->InvalidateBestSize();
681  mInputChannels->SetMaxSize(mInputChannels->GetBestSize()*4);
682 }
683 
685 {
686  if (in) {
687  gPrefs->Write(wxT("/AudioIO/RecordingDevice"), in->deviceString);
688  gPrefs->Write(wxT("/AudioIO/RecordingSourceIndex"), in->sourceIndex);
689  if (in->totalSources >= 1) {
690  gPrefs->Write(wxT("/AudioIO/RecordingSource"), in->sourceString);
691  } else {
692  gPrefs->Write(wxT("/AudioIO/RecordingSource"), wxT(""));
693  }
694  gPrefs->Flush();
695 
697  }
698 
699  if (out) {
700  gPrefs->Write(wxT("/AudioIO/PlaybackDevice"), out->deviceString);
701  if (out->totalSources >= 1) {
702  gPrefs->Write(wxT("/AudioIO/PlaybackSource"), out->sourceString);
703  } else {
704  gPrefs->Write(wxT("/AudioIO/PlaybackSource"), wxT(""));
705  }
706  gPrefs->Flush();
707  }
708 }
709 
710 void DeviceToolBar::ChangeDevice(bool isInput)
711 {
712  int newIndex = -1;
713  wxChoice *combo = isInput ? mInput :mOutput;
714  size_t i;
715 
716  int selectionIndex = mInput->GetSelection();
717  wxString host = gPrefs->Read(wxT("/AudioIO/Host"), wxT(""));
718  const std::vector<DeviceSourceMap> &maps = isInput ? DeviceManager::Instance()->GetInputDeviceMaps()
720 
721  // Find device indices for input and output
722  if (selectionIndex >= 0 ) {
723  wxString newDevice = combo->GetStringSelection();
724  for (i = 0; i < maps.size(); ++i) {
725  wxString name;
726  name = MakeDeviceSourceString(&maps[i]);
727  if (name == newDevice && maps[i].hostString == host) {
728  newIndex = i;
729  }
730  }
731  }
732 
733  if (newIndex < 0) {
734  wxLogDebug(wxT("DeviceToolBar::OnChoice(): couldn't find device indices"));
735  return;
736  }
737 
738  SetDevices(isInput ? &maps[newIndex] : NULL,
739  isInput ? NULL : &maps[newIndex]);
740 }
741 
742 void DeviceToolBar::OnChoice(wxCommandEvent &event)
743 {
744  wxObject *eventObject = event.GetEventObject();
745  //if we've changed hosts, we've handled the device switching already.
746  if (eventObject == mHost) {
747  ChangeHost();
748  } else if (eventObject == mInputChannels) {
749  int channelsSelectionIndex = mInputChannels->GetSelection();
750  if (channelsSelectionIndex >= 0)
751  gPrefs->Write(wxT("/AudioIO/RecordChannels"),channelsSelectionIndex + 1);
752  } else if (eventObject == mInput) {
753  ChangeDevice(true);
754  }
755  else if (eventObject == mOutput) {
756  ChangeDevice(false);
757  }
758 
759  if (gAudioIO) {
760  // We cannot have gotten here if gAudioIO->IsAudioTokenActive(),
761  // per the setting of AudioIONotBusyFlag and AudioIOBusyFlag in
762  // AudacityProject::GetUpdateFlags().
763  // However, we can have an invalid audio token (so IsAudioTokenActive()
764  // is false), but be monitoring.
765  // If monitoring, have to stop the stream, so HandleDeviceChange() can work.
766  // We could disable the Preferences command while monitoring, i.e.,
767  // set AudioIONotBusyFlag/AudioIOBusyFlag according to monitoring, as well.
768  // Instead allow it because unlike recording, for example, monitoring
769  // is not clearly something that should prohibit changing device.
770  // TODO: We *could* be smarter in this method and call HandleDeviceChange()
771  // only when the device choices actually changed. True of lots of prefs!
772  // As is, we always stop monitoring before handling the device change.
773  if (gAudioIO->IsMonitoring())
774  {
775  gAudioIO->StopStream();
776  while (gAudioIO->IsBusy())
777  wxMilliSleep(100);
778  }
780  }
781 
782  // Update all projects' DeviceToolBar.
783  for (size_t i = 0; i < gAudacityProjects.size(); i++) {
784  gAudacityProjects[i]->GetDeviceToolBar()->UpdatePrefs();
785  }
786 }
787 
789 {
790  ShowComboDialog(mInput, wxString(_("Select Recording Device")));
791 }
793 {
794  ShowComboDialog(mOutput, wxString(_("Select Playback Device")));
795 }
797 {
798  ShowComboDialog(mHost, wxString(_("Select Audio Host")));
799 }
801 {
802  ShowComboDialog(mInputChannels, wxString(_("Select Recording Channels")));
803 }
804 
805 void DeviceToolBar::ShowComboDialog(wxChoice *combo, const wxString &title)
806 {
807  if (!combo || combo->GetCount() == 0) {
808  AudacityMessageBox(_("Device information is not available."));
809  return;
810  }
811 
812 #if USE_PORTMIXER
813  wxArrayString inputSources = combo->GetStrings();
814 
815  wxDialogWrapper dlg(nullptr, wxID_ANY, title);
816  dlg.SetName(dlg.GetTitle());
817  ShuttleGui S(&dlg, eIsCreating);
818  wxChoice *c;
819 
820  S.StartVerticalLay(true);
821  {
822  S.StartHorizontalLay(wxCENTER, false);
823  {
824  c = S.AddChoice(combo->GetName(),
825  combo->GetStringSelection(),
826  &inputSources);
827  }
828  S.EndHorizontalLay();
829  }
830  S.EndVerticalLay();
831  S.AddStandardButtons();
832 
833  dlg.GetSizer()->SetSizeHints(&dlg);
834  dlg.Center();
835 
836  if (dlg.ShowModal() == wxID_OK)
837  {
838  wxCommandEvent dummyEvent;
839  dummyEvent.SetEventObject(combo);
840  // SetSelection() doesn't send an event, so we call OnChoice explicitly
841  combo->SetSelection(c->GetSelection());
842  OnChoice(dummyEvent);
843  }
844 #endif
845 }
#define kOutputWidthRatio
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
void StopStream()
Stop recording, playback or input monitoring.
Definition: AudioIO.cpp:2535
bool IsDocked() const
Definition: ToolBar.cpp:390
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:209
void Populate() override
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:89
virtual void UpdatePrefs()
Definition: ToolBar.cpp:535
void SetLabel(const wxString &label) override
Definition: ToolBar.cpp:374
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
static void CaptureKeyboard(wxWindow *handler)
Definition: Project.cpp:5954
void FillHostDevices()
bool IsStreamActive()
Returns true if the audio i/o is running at all, but not during cleanup.
Definition: AudioIO.cpp:2918
const wxChar * desc
Definition: ExportPCM.cpp:58
AProjectArray gAudacityProjects
Definition: Project.cpp:303
void RegenerateTooltips() override
ToolDock * GetDock()
Definition: ToolBar.cpp:560
#define kInputWidthRatio
wxString sourceString
Definition: DeviceManager.h:36
const std::vector< DeviceSourceMap > & GetInputDeviceMaps()
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
wxChoice * mInputChannels
Definition: DeviceToolBar.h:72
DeviceSourceMap * GetDefaultInputDevice(int hostIndex)
void OnChoice(wxCommandEvent &event)
bool IsBusy()
Returns true if audio i/o is busy starting, stopping, playing, or recording.
Definition: AudioIO.cpp:2910
wxChoice * mInput
Definition: DeviceToolBar.h:70
#define safenew
Definition: Audacity.h:230
void ShowComboDialog(wxChoice *combo, const wxString &title)
void EndHorizontalLay()
const std::vector< DeviceSourceMap > & GetOutputDeviceMaps()
wxBitmap & Bitmap(int iIndex)
Definition: Theme.cpp:1244
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
void OnFocus(wxFocusEvent &event)
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:176
void EndVerticalLay()
wxChoice * mOutput
Definition: DeviceToolBar.h:71
wxString MakeDeviceSourceString(const DeviceSourceMap *map)
#define grabberWidth
Definition: Grabber.h:96
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
A toobar to allow easier changing of input and output devices .
Definition: DeviceToolBar.h:25
wxChoice * AddChoice(const wxString &Prompt, const wxString &Selected, const wxArrayString *pChoices)
Definition: ShuttleGui.cpp:371
void RepositionCombos()
void DeinitChildren()
static bool RepositionCombo(wxWindow *combo, int toolbarWidth, wxSize desiredSize, float &ratio, float &flex, int marginPixels, bool changesRatio)
void HandleDeviceChange()
update state after changing what audio devices are selected
Definition: AudioIO.cpp:1426
void Add(wxWindow *window, int proportion=0, int flag=wxALIGN_TOP, int border=0, wxObject *userData=NULL)
Definition: ToolBar.cpp:611
virtual void Create(wxWindow *parent)
Definition: ToolBar.cpp:438
wxString deviceString
Definition: DeviceManager.h:37
void SetDevices(const DeviceSourceMap *in, const DeviceSourceMap *out)
void Create(wxWindow *parent) override
static DeviceManager * Instance()
Gets the singleton instance.
void ShowChannelsDialog()
void ChangeDevice(bool isInput)
static void ReleaseKeyboard(wxWindow *handler)
Definition: Project.cpp:5965
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
void ShowOutputDialog()
void OnCaptureKey(wxCommandEvent &event)
AudioIO * gAudioIO
Definition: AudioIO.cpp:482
bool Layout() override
wxChoice * mHost
Definition: DeviceToolBar.h:73
const wxChar * name
Definition: Distortion.cpp:94
bool IsMonitoring()
Returns true if we're monitoring input (but not recording or playing actual audio) ...
Definition: AudioIO.cpp:2942
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:308
#define kHostWidthRatio
void ShowInputDialog()
void FillInputChannels()
TrackPanel * GetTrackPanel()
Definition: Project.h:307
void UpdatePrefs() override
IMPLEMENT_CLASS(DeviceToolBar, ToolBar)
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1225
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxButton *extra=NULL)
END_EVENT_TABLE()
void EnableDisableButtons() override
Works with ToolManager and ToolDock to provide a dockable window in which buttons can be placed...
Definition: ToolBar.h:87
DeviceSourceMap * GetDefaultOutputDevice(int hostIndex)
#define kChannelsWidthRatio
virtual ~DeviceToolBar()
void StartVerticalLay(int iProp=1)