Audacity  2.2.2
DevicePrefs.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  DevicePrefs.cpp
6 
7  Joshua Haberman
8  Dominic Mazzoni
9  James Crook
10 
11 *******************************************************************//********************************************************************/
24 
25 #include "../Audacity.h"
26 
27 #include <wx/defs.h>
28 
29 #include <wx/choice.h>
30 #include <wx/intl.h>
31 #include <wx/log.h>
32 
33 #include "portaudio.h"
34 
35 #include "../AudioIO.h"
36 #include "../Internat.h"
37 #include "../Prefs.h"
38 #include "../ShuttleGui.h"
39 #include "../DeviceManager.h"
40 
41 #include "DevicePrefs.h"
42 
43 enum {
44  HostID = 10000,
48 };
49 
50 BEGIN_EVENT_TABLE(DevicePrefs, PrefsPanel)
51  EVT_CHOICE(HostID, DevicePrefs::OnHost)
52  EVT_CHOICE(RecordID, DevicePrefs::OnDevice)
54 
55 DevicePrefs::DevicePrefs(wxWindow * parent, wxWindowID winid)
56 : PrefsPanel(parent, winid, _("Devices"))
57 {
58  Populate();
59 }
60 
62 {
63 }
64 
66 {
67  // First any pre-processing for constructing the GUI.
69 
70  // Get current setting for devices
71  mPlayDevice = gPrefs->Read(wxT("/AudioIO/PlaybackDevice"), wxT(""));
72  mRecordDevice = gPrefs->Read(wxT("/AudioIO/RecordingDevice"), wxT(""));
73  mRecordSource = gPrefs->Read(wxT("/AudioIO/RecordingSource"), wxT(""));
74  mRecordChannels = gPrefs->Read(wxT("/AudioIO/RecordChannels"), 2L);
75 
76  //------------------------- Main section --------------------
77  // Now construct the GUI itself.
78  // Use 'eIsCreatingFromPrefs' so that the GUI is
79  // initialised with values from gPrefs.
82  // ----------------------- End of main section --------------
83 
84  wxCommandEvent e;
85  OnHost(e);
86 }
87 
88 /*
89  * Get names of device hosts.
90  */
92 {
93  // Gather list of hosts. Only added hosts that have devices attached.
94  // FIXME: TRAP_ERR PaErrorCode not handled in DevicePrefs GetNamesAndLabels()
95  // With an error code won't add hosts, but won't report a problem either.
96  int nDevices = Pa_GetDeviceCount();
97  for (int i = 0; i < nDevices; i++) {
98  const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
99  if ((info!=NULL)&&(info->maxOutputChannels > 0 || info->maxInputChannels > 0)) {
100  wxString name = wxSafeConvertMB2WX(Pa_GetHostApiInfo(info->hostApi)->name);
101  if (mHostNames.Index(name) == wxNOT_FOUND) {
102  mHostNames.Add(name);
103  mHostLabels.Add(name);
104  }
105  }
106  }
107 }
108 
110 {
111  wxArrayString empty;
112 
113  S.SetBorder(2);
114  S.StartScroller();
115 
116  S.StartStatic(_("Interface"));
117  {
118  S.StartMultiColumn(2);
119  {
120  S.Id(HostID);
121  mHost = S.TieChoice(_("&Host:"),
122  wxT("/AudioIO/Host"),
123  wxT(""),
124  mHostNames,
125  mHostLabels);
126 
127  S.AddPrompt(_("Using:"));
128  S.AddFixedText(wxString(wxSafeConvertMB2WX(Pa_GetVersionText())));
129  }
130  S.EndMultiColumn();
131  }
132  S.EndStatic();
133 
134  S.StartStatic(_("Playback"));
135  {
136  S.StartMultiColumn(2);
137  {
138  S.Id(PlayID);
139  mPlay = S.AddChoice(_("&Device:"),
140  wxEmptyString,
141  &empty);
142  }
143  S.EndMultiColumn();
144  }
145  S.EndStatic();
146 
147  S.StartStatic(_("Recording"));
148  {
149  S.StartMultiColumn(2);
150  {
151  S.Id(RecordID);
152  mRecord = S.AddChoice(_("De&vice:"),
153  wxEmptyString,
154  &empty);
155 
156  S.Id(ChannelsID);
157  mChannels = S.AddChoice(_("Cha&nnels:"),
158  wxEmptyString,
159  &empty);
160  }
161  S.EndMultiColumn();
162  }
163  S.EndStatic();
164 
165  // These previously lived in recording preferences.
166  // However they are liable to become device specific.
167  // Buffering also affects playback, not just recording, so is a device characteristic.
168  S.StartStatic( _("Latency"));
169  {
170  S.StartThreeColumn();
171  {
172  wxTextCtrl *w;
173  // only show the following controls if we use Portaudio v19, because
174  // for Portaudio v18 we always use default buffer sizes
175  w = S.TieNumericTextBox(_("&Buffer length:"),
176  wxT("/AudioIO/LatencyDuration"),
178  9);
179  S.AddUnits(_("milliseconds"));
180  if( w ) w->SetName(w->GetName() + wxT(" ") + _("milliseconds"));
181 
182  w = S.TieNumericTextBox(_("Track &shift after record:"),
183  wxT("/AudioIO/LatencyCorrection"),
185  9);
186  S.AddUnits(_("milliseconds"));
187  if( w ) w->SetName(w->GetName() + wxT(" ") + _("milliseconds"));
188  }
189  S.EndThreeColumn();
190  }
191  S.EndStatic();
192  S.EndScroller();
193 
194 }
195 
196 void DevicePrefs::OnHost(wxCommandEvent & e)
197 {
198  // Bail if we have no hosts
199  if (mHostNames.size() < 1)
200  return;
201 
202  // Find the index for the host API selected
203  int index = -1;
204  wxString apiName = mHostNames[mHost->GetCurrentSelection()];
205  int nHosts = Pa_GetHostApiCount();
206  for (int i = 0; i < nHosts; ++i) {
207  wxString name = wxSafeConvertMB2WX(Pa_GetHostApiInfo(i)->name);
208  if (name == apiName) {
209  index = i;
210  break;
211  }
212  }
213  // We should always find the host!
214  if (index < 0) {
215  wxLogDebug(wxT("DevicePrefs::OnHost(): API index not found"));
216  return;
217  }
218 
219  int nDevices = Pa_GetDeviceCount();
220 
221  // FIXME: TRAP_ERR PaErrorCode not handled. nDevices can be negative number.
222  if (nDevices == 0) {
223  mHost->Clear();
224  mHost->Append(_("No audio interfaces"), (void *) NULL);
225  mHost->SetSelection(0);
226  }
227 
228  const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
229  const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
230 
231  wxArrayString playnames;
232  wxArrayString recordnames;
233  size_t i;
234  int devindex; /* temp variable to hold the numeric ID of each device in turn */
235  wxString device;
236  wxString recDevice;
237 
238  recDevice = mRecordDevice;
239  if (this->mRecordSource != wxT(""))
240  recDevice += wxT(": ") + mRecordSource;
241 
242  mRecord->Clear();
243  for (i = 0; i < inMaps.size(); i++) {
244  if (index == inMaps[i].hostIndex) {
245  device = MakeDeviceSourceString(&inMaps[i]);
246  devindex = mRecord->Append(device);
247  // We need to const cast here because SetClientData is a wx function
248  // It is okay beause the original variable is non-const.
249  mRecord->SetClientData(devindex, const_cast<DeviceSourceMap *>(&inMaps[i]));
250  if (device == recDevice) { /* if this is the default device, select it */
251  mRecord->SetSelection(devindex);
252  }
253  }
254  }
255 
256  mPlay->Clear();
257  for (i = 0; i < outMaps.size(); i++) {
258  if (index == outMaps[i].hostIndex) {
259  device = MakeDeviceSourceString(&outMaps[i]);
260  devindex = mPlay->Append(device);
261  mPlay->SetClientData(devindex, const_cast<DeviceSourceMap *>(&outMaps[i]));
262  if (device == mPlayDevice) { /* if this is the default device, select it */
263  mPlay->SetSelection(devindex);
264  }
265  }
266  }
267 
268  /* deal with not having any devices at all */
269  if (mPlay->GetCount() == 0) {
270  playnames.Add(_("No devices found"));
271  mPlay->Append(playnames[0], (void *) NULL);
272  mPlay->SetSelection(0);
273  }
274  if (mRecord->GetCount() == 0) {
275  recordnames.Add(_("No devices found"));
276  mRecord->Append(recordnames[0], (void *) NULL);
277  mRecord->SetSelection(0);
278  }
279 
280  /* what if we have no device selected? we should choose the default on
281  * this API, as defined by PortAudio. We then fall back to using 0 only if
282  * that fails */
283  if (mPlay->GetCount() && mPlay->GetSelection() == wxNOT_FOUND) {
285  if (defaultMap)
286  mPlay->SetStringSelection(MakeDeviceSourceString(defaultMap));
287 
288  if (mPlay->GetSelection() == wxNOT_FOUND) {
289  mPlay->SetSelection(0);
290  }
291  }
292 
293  if (mRecord->GetCount() && mRecord->GetSelection() == wxNOT_FOUND) {
295  if (defaultMap)
296  mRecord->SetStringSelection(MakeDeviceSourceString(defaultMap));
297 
298  if (mPlay->GetSelection() == wxNOT_FOUND) {
299  mPlay->SetSelection(0);
300  }
301  }
302 
303  ShuttleGui S(this, eIsCreating);
304  S.SetSizeHints(mPlay, mPlay->GetStrings());
305  S.SetSizeHints(mRecord, mRecord->GetStrings());
306  OnDevice(e);
307 }
308 
309 void DevicePrefs::OnDevice(wxCommandEvent & WXUNUSED(event))
310 {
311  int ndx = mRecord->GetCurrentSelection();
312  if (ndx == wxNOT_FOUND) {
313  ndx = 0;
314  }
315 
316  int sel = mChannels->GetSelection();
317  int cnt = 0;
318 
319  DeviceSourceMap *inMap = (DeviceSourceMap *) mRecord->GetClientData(ndx);
320  if (inMap != NULL) {
321  cnt = inMap->numChannels;
322  }
323 
324  if (sel != wxNOT_FOUND) {
325  mRecordChannels = sel + 1;
326  }
327 
328  mChannels->Clear();
329 
330  // Mimic old behavior
331  if (cnt <= 0) {
332  cnt = 16;
333  }
334 
335  // Place an artifical limit on the number of channels to prevent an
336  // outrageous number. I don't know if this is really necessary, but
337  // it doesn't hurt.
338  if (cnt > 256) {
339  cnt = 256;
340  }
341 
342  wxArrayString channelnames;
343 
344  // Channel counts, mono, stereo etc...
345  for (int i = 0; i < cnt; i++) {
346  wxString name;
347 
348  if (i == 0) {
349  name = _("1 (Mono)");
350  }
351  else if (i == 1) {
352  name = _("2 (Stereo)");
353  }
354  else {
355  name = wxString::Format(wxT("%d"), i + 1);
356  }
357 
358  channelnames.Add(name);
359  int index = mChannels->Append(name);
360  if (i == mRecordChannels - 1) {
361  mChannels->SetSelection(index);
362  }
363  }
364 
365  if (mChannels->GetCount() && mChannels->GetCurrentSelection() == wxNOT_FOUND) {
366  mChannels->SetSelection(0);
367  }
368 
369  ShuttleGui S(this, eIsCreating);
370  S.SetSizeHints(mChannels, channelnames);
371  Layout();
372 }
373 
375 {
376  ShuttleGui S(this, eIsSavingToPrefs);
378  DeviceSourceMap *map = NULL;
379 
380  if (mPlay->GetCount() > 0) {
381  map = (DeviceSourceMap *) mPlay->GetClientData(
382  mPlay->GetSelection());
383  }
384  if (map) {
385  gPrefs->Write(wxT("/AudioIO/PlaybackDevice"), map->deviceString);
386  }
387 
388  map = NULL;
389  if (mRecord->GetCount() > 0) {
390  map = (DeviceSourceMap *) mRecord->GetClientData(mRecord->GetSelection());
391  }
392  if (map) {
393  gPrefs->Write(wxT("/AudioIO/RecordingDevice"),
394  map->deviceString);
395  gPrefs->Write(wxT("/AudioIO/RecordingSourceIndex"),
396  map->sourceIndex);
397  if (map->totalSources >= 1) {
398  gPrefs->Write(wxT("/AudioIO/RecordingSource"),
399  map->sourceString);
400  } else {
401  gPrefs->Write(wxT("/AudioIO/RecordingSource"),
402  wxT(""));
403  }
404  gPrefs->Write(wxT("/AudioIO/RecordChannels"),
405  mChannels->GetSelection() + 1);
406  }
407 
408  return true;
409 }
410 
412 {
413  return "Devices_Preferences";
414 }
415 
416 PrefsPanel *DevicePrefsFactory::operator () (wxWindow *parent, wxWindowID winid)
417 {
418  wxASSERT(parent); // to justify safenew
419  return safenew DevicePrefs(parent, winid);
420 }
wxChoice * TieChoice(const wxString &Prompt, WrappedType &WrappedRef, const wxArrayString *pChoices)
wxString mRecordSource
Definition: DevicePrefs.h:46
void SetSizeHints(int minX, int minY)
Used to modify an already placed Window.
Definition: ShuttleGui.cpp:194
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
void EndThreeColumn()
Definition: ShuttleGui.h:139
wxChoice * mHost
Definition: DevicePrefs.h:49
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
wxString HelpPageName() override
void EndMultiColumn()
wxChoice * mChannels
Definition: DevicePrefs.h:52
wxString sourceString
Definition: DeviceManager.h:36
const std::vector< DeviceSourceMap > & GetInputDeviceMaps()
DeviceSourceMap * GetDefaultInputDevice(int hostIndex)
void EndScroller()
Definition: ShuttleGui.cpp:828
void PopulateOrExchange(ShuttleGui &S) override
#define safenew
Definition: Audacity.h:230
const std::vector< DeviceSourceMap > & GetOutputDeviceMaps()
void AddUnits(const wxString &Prompt)
Left aligned text string.
Definition: ShuttleGui.cpp:260
void AddPrompt(const wxString &Prompt)
Right aligned text string.
Definition: ShuttleGui.cpp:239
wxChoice * mRecord
Definition: DevicePrefs.h:51
#define DEFAULT_LATENCY_CORRECTION
Definition: AudioIO.h:85
wxString MakeDeviceSourceString(const DeviceSourceMap *map)
wxScrolledWindow * StartScroller(int iStyle=0)
Definition: ShuttleGui.cpp:795
void Populate()
Definition: DevicePrefs.cpp:65
bool Commit() override
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
wxChoice * AddChoice(const wxString &Prompt, const wxString &Selected, const wxArrayString *pChoices)
Definition: ShuttleGui.cpp:371
ShuttleGui & Id(int id)
wxTextCtrl * TieNumericTextBox(const wxString &Prompt, WrappedType &WrappedRef, const int nChars)
void AddFixedText(const wxString &Str, bool bCenter=false)
Definition: ShuttleGui.cpp:397
wxString deviceString
Definition: DeviceManager.h:37
void StartThreeColumn()
Definition: ShuttleGui.h:138
void GetNamesAndLabels()
Definition: DevicePrefs.cpp:91
static DeviceManager * Instance()
Gets the singleton instance.
wxChoice * mPlay
Definition: DevicePrefs.h:50
Used within the PrefsDialog, classes derived from this class include AudioIOPrefs, BatchPrefs, DirectoriesPrefs, FileFormatPrefs, GUIPrefs, KeyConfigPrefs, MousePrefs, QualityPrefs, SpectrumPrefs and ThemePrefs.
Definition: PrefsPanel.h:45
void OnHost(wxCommandEvent &e)
_("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
PrefsPanel * operator()(wxWindow *parent, wxWindowID winid) override
virtual ~DevicePrefs()
Definition: DevicePrefs.cpp:61
const wxChar * name
Definition: Distortion.cpp:94
A PrefsPanel used to select recording and playback devices and other settings.
Definition: DevicePrefs.h:25
wxStaticBox * StartStatic(const wxString &Str, int iProp=0)
Definition: ShuttleGui.cpp:763
wxArrayString mHostLabels
Definition: DevicePrefs.h:42
wxString mPlayDevice
Definition: DevicePrefs.h:44
long mRecordChannels
Definition: DevicePrefs.h:47
wxString mRecordDevice
Definition: DevicePrefs.h:45
END_EVENT_TABLE()
#define DEFAULT_LATENCY_DURATION
Definition: AudioIO.h:84
void SetBorder(int Border)
Definition: ShuttleGui.h:286
wxArrayString mHostNames
Definition: DevicePrefs.h:41
DeviceSourceMap * GetDefaultOutputDevice(int hostIndex)
void OnDevice(wxCommandEvent &e)