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)
56 : PrefsPanel(parent, _("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);
127 
128  S.AddPrompt(_("Using:"));
129  S.AddFixedText(wxString(wxSafeConvertMB2WX(Pa_GetVersionText())));
130  }
131  S.EndMultiColumn();
132  }
133  S.EndStatic();
134 
135  S.StartStatic(_("Playback"));
136  {
137  S.StartMultiColumn(2);
138  {
139  S.Id(PlayID);
140  mPlay = S.AddChoice(_("&Device:"),
141  wxEmptyString,
142  &empty);
143  }
144  S.EndMultiColumn();
145  }
146  S.EndStatic();
147 
148  S.StartStatic(_("Recording"));
149  {
150  S.StartMultiColumn(2);
151  {
152  S.Id(RecordID);
153  mRecord = S.AddChoice(_("De&vice:"),
154  wxEmptyString,
155  &empty);
156 
157  S.Id(ChannelsID);
158  mChannels = S.AddChoice(_("Cha&nnels:"),
159  wxEmptyString,
160  &empty);
161  }
162  S.EndMultiColumn();
163  }
164  S.EndStatic();
165 
166  // These previously lived in recording preferences.
167  // However they are liable to become device specific.
168  // Buffering also affects playback, not just recording, so is a device characteristic.
169  S.StartStatic( _("Latency"));
170  {
171  S.StartThreeColumn();
172  {
173  wxTextCtrl *w;
174  // only show the following controls if we use Portaudio v19, because
175  // for Portaudio v18 we always use default buffer sizes
176  w = S.TieNumericTextBox(_("&Buffer length:"),
177  wxT("/AudioIO/LatencyDuration"),
179  9);
180  S.AddUnits(_("milliseconds"));
181  w->SetName(w->GetName() + wxT(" ") + _("milliseconds"));
182 
183  w = S.TieNumericTextBox(_("Track &shift after record:"),
184  wxT("/AudioIO/LatencyCorrection"),
186  9);
187  S.AddUnits(_("milliseconds"));
188  w->SetName(w->GetName() + wxT(" ") + _("milliseconds"));
189  }
190  S.EndThreeColumn();
191  }
192  S.EndStatic();
193  S.EndScroller();
194 
195 }
196 
197 void DevicePrefs::OnHost(wxCommandEvent & e)
198 {
199  // Bail if we have no hosts
200  if (mHostNames.size() < 1)
201  return;
202 
203  // Find the index for the host API selected
204  int index = -1;
205  wxString apiName = mHostNames[mHost->GetCurrentSelection()];
206  int nHosts = Pa_GetHostApiCount();
207  for (int i = 0; i < nHosts; ++i) {
208  wxString name = wxSafeConvertMB2WX(Pa_GetHostApiInfo(i)->name);
209  if (name == apiName) {
210  index = i;
211  break;
212  }
213  }
214  // We should always find the host!
215  if (index < 0) {
216  wxLogDebug(wxT("DevicePrefs::OnHost(): API index not found"));
217  return;
218  }
219 
220  int nDevices = Pa_GetDeviceCount();
221 
222  // FIXME: TRAP_ERR PaErrorCode not handled. nDevices can be negative number.
223  if (nDevices == 0) {
224  mHost->Clear();
225  mHost->Append(_("No audio interfaces"), (void *) NULL);
226  mHost->SetSelection(0);
227  }
228 
229  const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
230  const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
231 
232  wxArrayString playnames;
233  wxArrayString recordnames;
234  size_t i;
235  int devindex; /* temp variable to hold the numeric ID of each device in turn */
236  wxString device;
237  wxString recDevice;
238 
239  recDevice = mRecordDevice;
240  if (this->mRecordSource != wxT(""))
241  recDevice += wxT(": ") + mRecordSource;
242 
243  mRecord->Clear();
244  for (i = 0; i < inMaps.size(); i++) {
245  if (index == inMaps[i].hostIndex) {
246  device = MakeDeviceSourceString(&inMaps[i]);
247  devindex = mRecord->Append(device);
248  // We need to const cast here because SetClientData is a wx function
249  // It is okay beause the original variable is non-const.
250  mRecord->SetClientData(devindex, const_cast<DeviceSourceMap *>(&inMaps[i]));
251  if (device == recDevice) { /* if this is the default device, select it */
252  mRecord->SetSelection(devindex);
253  }
254  }
255  }
256 
257  mPlay->Clear();
258  for (i = 0; i < outMaps.size(); i++) {
259  if (index == outMaps[i].hostIndex) {
260  device = MakeDeviceSourceString(&outMaps[i]);
261  devindex = mPlay->Append(device);
262  mPlay->SetClientData(devindex, const_cast<DeviceSourceMap *>(&outMaps[i]));
263  if (device == mPlayDevice) { /* if this is the default device, select it */
264  mPlay->SetSelection(devindex);
265  }
266  }
267  }
268 
269  /* deal with not having any devices at all */
270  if (mPlay->GetCount() == 0) {
271  playnames.Add(_("No devices found"));
272  mPlay->Append(playnames[0], (void *) NULL);
273  mPlay->SetSelection(0);
274  }
275  if (mRecord->GetCount() == 0) {
276  recordnames.Add(_("No devices found"));
277  mRecord->Append(recordnames[0], (void *) NULL);
278  mRecord->SetSelection(0);
279  }
280 
281  /* what if we have no device selected? we should choose the default on
282  * this API, as defined by PortAudio. We then fall back to using 0 only if
283  * that fails */
284  if (mPlay->GetCount() && mPlay->GetSelection() == wxNOT_FOUND) {
286  if (defaultMap)
287  mPlay->SetStringSelection(MakeDeviceSourceString(defaultMap));
288 
289  if (mPlay->GetSelection() == wxNOT_FOUND) {
290  mPlay->SetSelection(0);
291  }
292  }
293 
294  if (mRecord->GetCount() && mRecord->GetSelection() == wxNOT_FOUND) {
296  if (defaultMap)
297  mRecord->SetStringSelection(MakeDeviceSourceString(defaultMap));
298 
299  if (mPlay->GetSelection() == wxNOT_FOUND) {
300  mPlay->SetSelection(0);
301  }
302  }
303 
304  ShuttleGui S(this, eIsCreating);
305  S.SetSizeHints(mPlay, mPlay->GetStrings());
306  S.SetSizeHints(mRecord, mRecord->GetStrings());
307  OnDevice(e);
308 }
309 
310 void DevicePrefs::OnDevice(wxCommandEvent & WXUNUSED(event))
311 {
312  int ndx = mRecord->GetCurrentSelection();
313  if (ndx == wxNOT_FOUND) {
314  ndx = 0;
315  }
316 
317  int sel = mChannels->GetSelection();
318  int cnt = 0;
319 
320  DeviceSourceMap *inMap = (DeviceSourceMap *) mRecord->GetClientData(ndx);
321  if (inMap != NULL) {
322  cnt = inMap->numChannels;
323  }
324 
325  if (sel != wxNOT_FOUND) {
326  mRecordChannels = sel + 1;
327  }
328 
329  mChannels->Clear();
330 
331  // Mimic old behavior
332  if (cnt <= 0) {
333  cnt = 16;
334  }
335 
336  // Place an artifical limit on the number of channels to prevent an
337  // outrageous number. I don't know if this is really necessary, but
338  // it doesn't hurt.
339  if (cnt > 256) {
340  cnt = 256;
341  }
342 
343  wxArrayString channelnames;
344 
345  // Channel counts, mono, stereo etc...
346  for (int i = 0; i < cnt; i++) {
347  wxString name;
348 
349  if (i == 0) {
350  name = _("1 (Mono)");
351  }
352  else if (i == 1) {
353  name = _("2 (Stereo)");
354  }
355  else {
356  name = wxString::Format(wxT("%d"), i + 1);
357  }
358 
359  channelnames.Add(name);
360  int index = mChannels->Append(name);
361  if (i == mRecordChannels - 1) {
362  mChannels->SetSelection(index);
363  }
364  }
365 
366  if (mChannels->GetCount() && mChannels->GetCurrentSelection() == wxNOT_FOUND) {
367  mChannels->SetSelection(0);
368  }
369 
370  ShuttleGui S(this, eIsCreating);
371  S.SetSizeHints(mChannels, channelnames);
372  Layout();
373 }
374 
376 {
377  ShuttleGui S(this, eIsSavingToPrefs);
379  DeviceSourceMap *map = NULL;
380 
381  if (mPlay->GetCount() > 0) {
382  map = (DeviceSourceMap *) mPlay->GetClientData(
383  mPlay->GetSelection());
384  }
385  if (map) {
386  gPrefs->Write(wxT("/AudioIO/PlaybackDevice"), map->deviceString);
387  }
388 
389  map = NULL;
390  if (mRecord->GetCount() > 0) {
391  map = (DeviceSourceMap *) mRecord->GetClientData(mRecord->GetSelection());
392  }
393  if (map) {
394  gPrefs->Write(wxT("/AudioIO/RecordingDevice"),
395  map->deviceString);
396  gPrefs->Write(wxT("/AudioIO/RecordingSourceIndex"),
397  map->sourceIndex);
398  if (map->totalSources >= 1) {
399  gPrefs->Write(wxT("/AudioIO/RecordingSource"),
400  map->sourceString);
401  } else {
402  gPrefs->Write(wxT("/AudioIO/RecordingSource"),
403  wxT(""));
404  }
405  gPrefs->Write(wxT("/AudioIO/RecordChannels"),
406  mChannels->GetSelection() + 1);
407  }
408 
409  return true;
410 }
411 
413 {
414  return "Devices_Preferences";
415 }
416 
418 {
419  wxASSERT(parent); // to justify safenew
420  return safenew DevicePrefs(parent);
421 }
wxChoice * TieChoice(const wxString &Prompt, WrappedType &WrappedRef, const wxArrayString *pChoices)
wxString mRecordSource
Definition: DevicePrefs.h:47
void EndThreeColumn()
Definition: ShuttleGui.h:128
wxChoice * mHost
Definition: DevicePrefs.h:50
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:366
wxString HelpPageName() override
void EndMultiColumn()
wxChoice * mChannels
Definition: DevicePrefs.h:53
wxString sourceString
Definition: DeviceManager.h:36
const std::vector< DeviceSourceMap > & GetInputDeviceMaps()
DeviceSourceMap * GetDefaultInputDevice(int hostIndex)
void EndScroller()
Definition: ShuttleGui.cpp:770
#define safenew
Definition: Audacity.h:223
const std::vector< DeviceSourceMap > & GetOutputDeviceMaps()
void AddUnits(const wxString &Prompt)
Left aligned text string.
Definition: ShuttleGui.cpp:229
void AddPrompt(const wxString &Prompt)
Right aligned text string.
Definition: ShuttleGui.cpp:215
wxChoice * mRecord
Definition: DevicePrefs.h:52
#define DEFAULT_LATENCY_CORRECTION
Definition: AudioIO.h:84
void SetSizeHints(int minX=-1, int minY=-1)
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
wxString MakeDeviceSourceString(const DeviceSourceMap *map)
wxScrolledWindow * StartScroller(int iStyle=0)
Definition: ShuttleGui.cpp:733
void Populate()
Definition: DevicePrefs.cpp:65
PrefsPanel * Create(wxWindow *parent) override
bool Commit() override
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:998
wxChoice * AddChoice(const wxString &Prompt, const wxString &Selected, const wxArrayString *pChoices)
Definition: ShuttleGui.cpp:331
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:356
wxString deviceString
Definition: DeviceManager.h:37
void PopulateOrExchange(ShuttleGui &S)
void StartThreeColumn()
Definition: ShuttleGui.h:127
void GetNamesAndLabels()
Definition: DevicePrefs.cpp:91
static DeviceManager * Instance()
Gets the singleton instance.
wxChoice * mPlay
Definition: DevicePrefs.h:51
Used within the PrefsDialog, classes derived from this class include AudioIOPrefs, BatchPrefs, DirectoriesPrefs, FileFormatPrefs, GUIPrefs, KeyConfigPrefs, MousePrefs, QualityPrefs, SpectrumPrefs and ThemePrefs.
Definition: PrefsPanel.h:43
_("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 &)
void OnHost(wxCommandEvent &e)
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:26
wxStaticBox * StartStatic(const wxString &Str, int iProp=0)
Definition: ShuttleGui.cpp:701
wxArrayString mHostLabels
Definition: DevicePrefs.h:43
wxString mPlayDevice
Definition: DevicePrefs.h:45
long mRecordChannels
Definition: DevicePrefs.h:48
wxString mRecordDevice
Definition: DevicePrefs.h:46
END_EVENT_TABLE()
#define DEFAULT_LATENCY_DURATION
Definition: AudioIO.h:83
void SetBorder(int Border)
Definition: ShuttleGui.h:251
wxArrayString mHostNames
Definition: DevicePrefs.h:42
DeviceSourceMap * GetDefaultOutputDevice(int hostIndex)
void OnDevice(wxCommandEvent &e)