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