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