Audacity 3.2.0
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#include "AudioIOBase.h"
28
29#include "RecordingPrefs.h"
30
31#include <wx/defs.h>
32
33#include <wx/choice.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
43enum {
44 HostID = 10000,
48};
49
50BEGIN_EVENT_TABLE(DevicePrefs, PrefsPanel)
51 EVT_CHOICE(HostID, DevicePrefs::OnHost)
54
55DevicePrefs::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
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 ChoiceSetting HostSetting{
132 };
133 S.SetBorder(2);
134 S.StartScroller();
135
136 /* i18n-hint Software interface to audio devices */
137 S.StartStatic(XC("Interface", "device"));
138 {
139 S.StartMultiColumn(2);
140 {
141 S.Id(HostID);
142 mHost = S.TieChoice( XXO("&Host:"), HostSetting);
143
144 S.AddPrompt(XXO("Using:"));
145 S.AddFixedText( Verbatim(wxSafeConvertMB2WX(Pa_GetVersionText() ) ) );
146 }
147 S.EndMultiColumn();
148 }
149 S.EndStatic();
150
151 S.StartStatic(XO("Playback"));
152 {
153 S.StartMultiColumn(2);
154 {
155 S.Id(PlayID);
156 mPlay = S.AddChoice(XXO("&Device:"),
157 {} );
158 }
159 S.EndMultiColumn();
160 }
161 S.EndStatic();
162
163 // i18n-hint: modifier as in "Recording preferences", not progressive verb
164 S.StartStatic(XC("Recording", "preference"));
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:"),
194 25);
195 S.AddUnits(XO("milliseconds"));
196
197 w = S
198 .NameSuffix(XO("milliseconds"))
199 .TieNumericTextBox(XXO("&Latency compensation:"),
201 S.AddUnits(XO("milliseconds"));
202 }
203 S.EndThreeColumn();
204 }
205 S.EndStatic();
206 S.EndScroller();
207
208}
209
210void DevicePrefs::OnHost(wxCommandEvent & e)
211{
212 // Bail if we have no hosts
213 if (mHostNames.size() < 1)
214 return;
215
216 // Find the index for the host API selected
217 int index = -1;
218 auto apiName = mHostLabels[mHost->GetCurrentSelection()];
219 int nHosts = Pa_GetHostApiCount();
220 for (int i = 0; i < nHosts; ++i) {
221 wxString name = wxSafeConvertMB2WX(Pa_GetHostApiInfo(i)->name);
222 if (name == apiName) {
223 index = i;
224 break;
225 }
226 }
227 // We should always find the host!
228 if (index < 0) {
229 wxLogDebug(wxT("DevicePrefs::OnHost(): API index not found"));
230 return;
231 }
232
233 int nDevices = Pa_GetDeviceCount();
234
235 // FIXME: TRAP_ERR PaErrorCode not handled. nDevices can be negative number.
236 if (nDevices == 0) {
237 mHost->Clear();
238 mHost->Append(_("No audio interfaces"), (void *) NULL);
239 mHost->SetSelection(0);
240 }
241
242 const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
243 const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
244
245 wxArrayString playnames;
246 wxArrayString recordnames;
247 size_t i;
248 int devindex; /* temp variable to hold the numeric ID of each device in turn */
249 wxString device;
250 wxString recDevice;
251
252 recDevice = mRecordDevice;
253 if (!this->mRecordSource.empty())
254 recDevice += wxT(": ") + mRecordSource;
255
256 mRecord->Clear();
257 for (i = 0; i < inMaps.size(); i++) {
258 if (index == inMaps[i].hostIndex) {
259 device = MakeDeviceSourceString(&inMaps[i]);
260 devindex = mRecord->Append(device);
261 // We need to const cast here because SetClientData is a wx function
262 // It is okay because the original variable is non-const.
263 mRecord->SetClientData(devindex, const_cast<DeviceSourceMap *>(&inMaps[i]));
264 if (device == recDevice) { /* if this is the default device, select it */
265 mRecord->SetSelection(devindex);
266 }
267 }
268 }
269
270 mPlay->Clear();
271 for (i = 0; i < outMaps.size(); i++) {
272 if (index == outMaps[i].hostIndex) {
273 device = MakeDeviceSourceString(&outMaps[i]);
274 devindex = mPlay->Append(device);
275 mPlay->SetClientData(devindex, const_cast<DeviceSourceMap *>(&outMaps[i]));
276 if (device == mPlayDevice) { /* if this is the default device, select it */
277 mPlay->SetSelection(devindex);
278 }
279 }
280 }
281
282 /* deal with not having any devices at all */
283 if (mPlay->GetCount() == 0) {
284 playnames.push_back(_("No devices found"));
285 mPlay->Append(playnames[0], (void *) NULL);
286 mPlay->SetSelection(0);
287 }
288 if (mRecord->GetCount() == 0) {
289 recordnames.push_back(_("No devices found"));
290 mRecord->Append(recordnames[0], (void *) NULL);
291 mRecord->SetSelection(0);
292 }
293
294 /* what if we have no device selected? we should choose the default on
295 * this API, as defined by PortAudio. We then fall back to using 0 only if
296 * that fails */
297 if (mPlay->GetCount() && mPlay->GetSelection() == wxNOT_FOUND) {
299 if (defaultMap)
300 mPlay->SetStringSelection(MakeDeviceSourceString(defaultMap));
301
302 if (mPlay->GetSelection() == wxNOT_FOUND) {
303 mPlay->SetSelection(0);
304 }
305 }
306
307 if (mRecord->GetCount() && mRecord->GetSelection() == wxNOT_FOUND) {
309 if (defaultMap)
310 mRecord->SetStringSelection(MakeDeviceSourceString(defaultMap));
311
312 if (mPlay->GetSelection() == wxNOT_FOUND) {
313 mPlay->SetSelection(0);
314 }
315 }
316
317 ShuttleGui::SetMinSize(mPlay, mPlay->GetStrings());
318 ShuttleGui::SetMinSize(mRecord, mRecord->GetStrings());
319 OnDevice(e);
320}
321
322void DevicePrefs::OnDevice(wxCommandEvent & WXUNUSED(event))
323{
324 int ndx = mRecord->GetCurrentSelection();
325 if (ndx == wxNOT_FOUND) {
326 ndx = 0;
327 }
328
329 int sel = mChannels->GetSelection();
330 int cnt = 0;
331
332 DeviceSourceMap *inMap = (DeviceSourceMap *) mRecord->GetClientData(ndx);
333 if (inMap != NULL) {
334 cnt = inMap->numChannels;
335 }
336
337 if (sel != wxNOT_FOUND) {
338 mRecordChannels = sel + 1;
339 }
340
341 mChannels->Clear();
342
343 // Mimic old behavior
344 if (cnt <= 0) {
345 cnt = 16;
346 }
347
348 // Place an artificial limit on the number of channels to prevent an
349 // outrageous number. I don't know if this is really necessary, but
350 // it doesn't hurt.
351 if (cnt > 256) {
352 cnt = 256;
353 }
354
355 wxArrayStringEx channelnames;
356
357 // Channel counts, mono, stereo etc...
358 for (int i = 0; i < cnt; i++) {
359 wxString name;
360
361 if (i == 0) {
362 name = _("1 (Mono)");
363 }
364 else if (i == 1) {
365 name = _("2 (Stereo)");
366 }
367 else {
368 name = wxString::Format(wxT("%d"), i + 1);
369 }
370
371 channelnames.push_back(name);
372 int index = mChannels->Append(name);
373 if (i == mRecordChannels - 1) {
374 mChannels->SetSelection(index);
375 }
376 }
377
378 if (mChannels->GetCount() && mChannels->GetCurrentSelection() == wxNOT_FOUND) {
379 mChannels->SetSelection(0);
380 }
381
382 ShuttleGui::SetMinSize(mChannels, channelnames);
383 Layout();
384}
385
387{
390 DeviceSourceMap *map = NULL;
391
392 if (mPlay->GetCount() > 0) {
393 map = (DeviceSourceMap *) mPlay->GetClientData(
394 mPlay->GetSelection());
395 }
396 if (map)
398
399 map = NULL;
400 if (mRecord->GetCount() > 0) {
401 map = (DeviceSourceMap *) mRecord->GetClientData(mRecord->GetSelection());
402 }
403 if (map) {
406 if (map->totalSources >= 1)
408 else
410 AudioIORecordChannels.Write(mChannels->GetSelection() + 1);
411 }
412
415 return true;
416}
417
418PrefsPanel *DevicePrefsFactory(wxWindow *parent, wxWindowID winid, AudacityProject *)
419{
420 wxASSERT(parent); // to justify safenew
421 return safenew DevicePrefs(parent, winid);
422}
423
424namespace{
427 };
428}
wxT("CloseDown"))
DoubleSetting AudioIOLatencyCorrection
StringSetting AudioIORecordingSource
StringSetting AudioIOPlaybackDevice
DoubleSetting AudioIOLatencyDuration
StringSetting AudioIORecordingDevice
StringSetting AudioIOHost
IntSetting AudioIORecordingSourceIndex
IntSetting AudioIORecordChannels
END_EVENT_TABLE()
wxString MakeDeviceSourceString(const DeviceSourceMap *map)
@ ChannelsID
Definition: DevicePrefs.cpp:47
@ PlayID
Definition: DevicePrefs.cpp:45
@ HostID
Definition: DevicePrefs.cpp:44
@ RecordID
Definition: DevicePrefs.cpp:46
PrefsPanel * DevicePrefsFactory(wxWindow *parent, wxWindowID winid, AudacityProject *)
#define DEVICE_PREFS_PLUGIN_SYMBOL
Definition: DevicePrefs.h:22
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define XC(s, c)
Definition: Internat.h:37
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:10
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:448
ByColumns_t ByColumns
Definition: Prefs.cpp:474
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:46
@ eIsSavingToPrefs
Definition: ShuttleGui.h:47
#define S(N)
Definition: ToChars.cpp:64
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
DeviceSourceMap * GetDefaultInputDevice(int hostIndex)
const std::vector< DeviceSourceMap > & GetInputDeviceMaps()
const std::vector< DeviceSourceMap > & GetOutputDeviceMaps()
static DeviceManager * Instance()
Gets the singleton instance.
DeviceSourceMap * GetDefaultOutputDevice(int hostIndex)
A PrefsPanel used to select recording and playback devices and other settings.
Definition: DevicePrefs.h:25
void OnDevice(wxCommandEvent &e)
wxArrayStringEx mHostLabels
Definition: DevicePrefs.h:44
wxChoice * mHost
Definition: DevicePrefs.h:51
ComponentInterfaceSymbol GetSymbol() const override
Definition: DevicePrefs.cpp:66
void PopulateOrExchange(ShuttleGui &S) override
wxChoice * mRecord
Definition: DevicePrefs.h:53
wxString mRecordDevice
Definition: DevicePrefs.h:47
wxString mPlayDevice
Definition: DevicePrefs.h:46
wxString mRecordSource
Definition: DevicePrefs.h:48
void OnHost(wxCommandEvent &e)
TranslatableString GetDescription() const override
Definition: DevicePrefs.cpp:71
virtual ~DevicePrefs()
Definition: DevicePrefs.cpp:61
wxChoice * mPlay
Definition: DevicePrefs.h:52
void GetNamesAndLabels()
ManualPageID HelpPageName() override
If not empty string, the Help button is added below the panel.
Definition: DevicePrefs.cpp:76
wxChoice * mChannels
Definition: DevicePrefs.h:54
long mRecordChannels
Definition: DevicePrefs.h:49
void Populate()
Definition: DevicePrefs.cpp:81
bool Commit() override
TranslatableStrings mHostNames
Definition: DevicePrefs.h:43
Base class for a panel in the PrefsDialog. Classes derived from this class include BatchPrefs,...
Definition: PrefsPanel.h:51
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:252
void Invalidate() override
Definition: Prefs.h:282
bool Reset()
Reset to the default value.
Definition: Prefs.h:277
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
static void SetMinSize(wxWindow *window, const TranslatableStrings &items)
Holds a msgid for the translation catalog; may also bind format arguments.
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
PrefsPanel::Registration sAttachment
wxString sourceString
Definition: DeviceManager.h:34
wxString deviceString
Definition: DeviceManager.h:35