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/combobox.h>
35#include <wx/log.h>
36#include <wx/textctrl.h>
37#include <wx/bmpbuttn.h>
38
39#include "portaudio.h"
40
41#include "Prefs.h"
42#include "ShuttleGui.h"
43#include "DeviceManager.h"
44#include "ProjectRate.h"
45
46#include "QualityPrefs.h"
47#include "QualitySettings.h"
48
49#include "AllThemeResources.h"
50#include "Theme.h"
51
52#define ID_DEFAULT_SAMPLE_RATE_CHOICE 7001
53
54enum {
55 HostID = 10000,
61};
62
63BEGIN_EVENT_TABLE(DevicePrefs, PrefsPanel)
64 EVT_CHOICE(HostID, DevicePrefs::OnHost)
69
70DevicePrefs::DevicePrefs(wxWindow * parent, wxWindowID winid, AudacityProject* project)
71: PrefsPanel(parent, winid, XO("Audio Settings"))
72, mProject(project)
73{
74 Populate();
75}
76
78{
79}
80
81
83{
85}
86
88{
89 return XO("Audio Settings");
90}
91
93{
94 return "Devices_Preferences";
95}
96
98{
99 // First any pre-processing for constructing the GUI.
101
104 ProjectRate::Get(*mProject).GetRate() :
106
107 auto it = std::find(
110
112 mSampleRateNames.size() - 1 :
113 std::distance(mSampleRateValues.begin(), it);
114
115 // Get current setting for devices
120
121 //------------------------- Main section --------------------
122 // Now construct the GUI itself.
123 // Use 'eIsCreatingFromPrefs' so that the GUI is
124 // initialised with values from gPrefs.
127 // ----------------------- End of main section --------------
128
129 wxCommandEvent e;
130 OnHost(e);
133}
134
135
136/*
137 * Get names of device hosts.
138 */
140{
141 // Gather list of hosts. Only added hosts that have devices attached.
142 // FIXME: TRAP_ERR PaErrorCode not handled in DevicePrefs GetNamesAndLabels()
143 // With an error code won't add hosts, but won't report a problem either.
144 int nDevices = Pa_GetDeviceCount();
145 for (int i = 0; i < nDevices; i++) {
146 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
147 if ((info!=NULL)&&(info->maxOutputChannels > 0 || info->maxInputChannels > 0)) {
148 wxString name = wxSafeConvertMB2WX(Pa_GetHostApiInfo(info->hostApi)->name);
150 .contains( Verbatim( name ) )) {
151 mHostNames.push_back( Verbatim( name ) );
152 mHostLabels.push_back(name);
153 }
154 }
155 }
156
157 //------------ Sample Rate Names
158 // JKC: I don't understand the following comment.
159 // Can someone please explain or correct it?
160 // XXX: This should use a previously changed, but not yet saved
161 // sound card setting from the "I/O" preferences tab.
162 // LLL: It means that until the user clicks "Ok" in preferences, the
163 // GetSupportedSampleRates() call should use the devices they
164 // may have changed on the Audio I/O page. As coded, the sample
165 // rates it will return could be completely invalid as they will
166 // be what's supported by the devices that were selected BEFORE
167 // coming into preferences.
168 //
169 // GetSupportedSampleRates() allows passing in device names, but
170 // how do you get at them as they are on the Audio I/O page????
171 for (int i = 0; i < AudioIOBase::NumStandardRates; i++)
172 {
173 int iRate = AudioIOBase::StandardRates[i];
174 mSampleRateValues.push_back(iRate);
175 mSampleRateNames.push_back(XO("%i Hz").Format(iRate));
176 }
177
178 mSampleRateNames.push_back(XO("Other..."));
179
180 // The label for the 'Other...' case can be any value at all.
181 mSampleRateValues.push_back(
182 44100); // If chosen, this value will be overwritten
183}
184
186{
187 ChoiceSetting HostSetting{
190 };
191 S.SetBorder(2);
192 S.StartScroller();
193
194 /* i18n-hint Software interface to audio devices */
195 S.StartStatic(XC("Interface", "device"));
196 {
197 S.StartMultiColumn(2);
198 {
199 S.Id(HostID);
200 mHost = S.TieChoice( XXO("&Host:"), HostSetting);
201
202 S.AddPrompt(XXO("Using:"));
203 S.AddFixedText( Verbatim(wxSafeConvertMB2WX(Pa_GetVersionText() ) ) );
204 }
205 S.EndMultiColumn();
206 }
207 S.EndStatic();
208
209 S.StartStatic(XO("Playback"));
210 {
211 S.StartMultiColumn(2);
212 {
213 S.Id(PlayID);
214 mPlay = S.AddChoice(XXO("&Device:"),
215 {} );
216 }
217 S.EndMultiColumn();
218 }
219 S.EndStatic();
220
221 // i18n-hint: modifier as in "Recording preferences", not progressive verb
222 S.StartStatic(XC("Recording", "preference"));
223 {
224 S.StartMultiColumn(2);
225 {
226 S.Id(RecordID);
227 mRecord = S.AddChoice(XXO("De&vice:"),
228 {} );
229
230 S.Id(ChannelsID);
231 mChannels = S.AddChoice(XXO("Cha&nnels:"),
232 {} );
233 }
234 S.EndMultiColumn();
235 }
236 S.EndStatic();
237
238
239 S.StartStatic(XO("Quality"));
240 {
241 S.StartMultiColumn(2);
242 {
243 if (mProject)
244 {
245 S.AddPrompt(XXO("&Project Sample Rate:"));
246
247 S.StartMultiColumn(3);
248 {
252
253 // Now do the edit box...
255 S.TieNumericTextBox({}, mOtherProjectSampleRateValue, 15);
256
257 auto helpBtn = S.AddBitmapButton(theTheme.Bitmap(bmpHelpIcon));
258
259 const auto helpText =
260 XO("Sample Rate used when recording new tracks, mixing down tracks and for playback in this project.")
261 .Translation();
262
263 helpBtn->SetToolTip(helpText);
264 helpBtn->SetLabel(helpText); // for screen readers
265 helpBtn->SetName(helpText);
266 }
267 S.EndMultiColumn();
268 }
269 }
270
271 S.AddPrompt(XXO("D&efault Sample Rate:"));
272
273 S.StartMultiColumn(2);
274 {
277 .TieNumberAsChoice(
280
282 S.TieNumericTextBox({}, mOtherDefaultSampleRateValue, 15);
283 }
284 S.EndMultiColumn();
285
286 S.TieChoice(
287 XXO("Default Sample &Format:"), QualitySettings::SampleFormatSetting);
288
289 S.EndMultiColumn();
290 }
291 S.EndStatic();
292
293 // These previously lived in recording preferences.
294 // However they are liable to become device specific.
295 // Buffering also affects playback, not just recording, so is a device characteristic.
296 S.StartStatic( XO("Latency"));
297 {
298 S.StartThreeColumn();
299 {
300 wxTextCtrl *w;
301 // only show the following controls if we use Portaudio v19, because
302 // for Portaudio v18 we always use default buffer sizes
303 w = S
304 .NameSuffix(XO("milliseconds"))
305 .TieNumericTextBox(XXO("&Buffer length:"),
307 25);
308 S.AddUnits(XO("milliseconds"));
309
310 w = S
311 .NameSuffix(XO("milliseconds"))
312 .TieNumericTextBox(XXO("&Latency compensation:"),
314 S.AddUnits(XO("milliseconds"));
315 }
316 S.EndThreeColumn();
317 }
318 S.EndStatic();
319
320 S.EndScroller();
321
322}
323
324void DevicePrefs::OnHost(wxCommandEvent & e)
325{
326 // Bail if we have no hosts
327 if (mHostNames.size() < 1)
328 return;
329
330 // Find the index for the host API selected
331 int index = -1;
332 auto apiName = mHostLabels[mHost->GetCurrentSelection()];
333 int nHosts = Pa_GetHostApiCount();
334 for (int i = 0; i < nHosts; ++i) {
335 wxString name = wxSafeConvertMB2WX(Pa_GetHostApiInfo(i)->name);
336 if (name == apiName) {
337 index = i;
338 break;
339 }
340 }
341 // We should always find the host!
342 if (index < 0) {
343 wxLogDebug(wxT("DevicePrefs::OnHost(): API index not found"));
344 return;
345 }
346
347 int nDevices = Pa_GetDeviceCount();
348
349 // FIXME: TRAP_ERR PaErrorCode not handled. nDevices can be negative number.
350 if (nDevices == 0) {
351 mHost->Clear();
352 mHost->Append(_("No audio interfaces"), (void *) NULL);
353 mHost->SetSelection(0);
354 }
355
356 const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
357 const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
358
359 wxArrayString playnames;
360 wxArrayString recordnames;
361 size_t i;
362 int devindex; /* temp variable to hold the numeric ID of each device in turn */
363 wxString device;
364 wxString recDevice;
365
366 recDevice = mRecordDevice;
367 if (!this->mRecordSource.empty())
368 recDevice += wxT(": ") + mRecordSource;
369
370 mRecord->Clear();
371 for (i = 0; i < inMaps.size(); i++) {
372 if (index == inMaps[i].hostIndex) {
373 device = MakeDeviceSourceString(&inMaps[i]);
374 devindex = mRecord->Append(device);
375 // We need to const cast here because SetClientData is a wx function
376 // It is okay because the original variable is non-const.
377 mRecord->SetClientData(devindex, const_cast<DeviceSourceMap *>(&inMaps[i]));
378 if (device == recDevice) { /* if this is the default device, select it */
379 mRecord->SetSelection(devindex);
380 }
381 }
382 }
383
384 mPlay->Clear();
385 for (i = 0; i < outMaps.size(); i++) {
386 if (index == outMaps[i].hostIndex) {
387 device = MakeDeviceSourceString(&outMaps[i]);
388 devindex = mPlay->Append(device);
389 mPlay->SetClientData(devindex, const_cast<DeviceSourceMap *>(&outMaps[i]));
390 if (device == mPlayDevice) { /* if this is the default device, select it */
391 mPlay->SetSelection(devindex);
392 }
393 }
394 }
395
396 /* deal with not having any devices at all */
397 if (mPlay->GetCount() == 0) {
398 playnames.push_back(_("No devices found"));
399 mPlay->Append(playnames[0], (void *) NULL);
400 mPlay->SetSelection(0);
401 }
402 if (mRecord->GetCount() == 0) {
403 recordnames.push_back(_("No devices found"));
404 mRecord->Append(recordnames[0], (void *) NULL);
405 mRecord->SetSelection(0);
406 }
407
408 /* what if we have no device selected? we should choose the default on
409 * this API, as defined by PortAudio. We then fall back to using 0 only if
410 * that fails */
411 if (mPlay->GetCount() && mPlay->GetSelection() == wxNOT_FOUND) {
413 if (defaultMap)
414 mPlay->SetStringSelection(MakeDeviceSourceString(defaultMap));
415
416 if (mPlay->GetSelection() == wxNOT_FOUND) {
417 mPlay->SetSelection(0);
418 }
419 }
420
421 if (mRecord->GetCount() && mRecord->GetSelection() == wxNOT_FOUND) {
423 if (defaultMap)
424 mRecord->SetStringSelection(MakeDeviceSourceString(defaultMap));
425
426 if (mPlay->GetSelection() == wxNOT_FOUND) {
427 mPlay->SetSelection(0);
428 }
429 }
430
431 ShuttleGui::SetMinSize(mPlay, mPlay->GetStrings());
432 ShuttleGui::SetMinSize(mRecord, mRecord->GetStrings());
433 OnDevice(e);
434}
435
436void DevicePrefs::OnDevice(wxCommandEvent & WXUNUSED(event))
437{
438 int ndx = mRecord->GetCurrentSelection();
439 if (ndx == wxNOT_FOUND) {
440 ndx = 0;
441 }
442
443 int sel = mChannels->GetSelection();
444 int cnt = 0;
445
446 DeviceSourceMap *inMap = (DeviceSourceMap *) mRecord->GetClientData(ndx);
447 if (inMap != NULL) {
448 cnt = inMap->numChannels;
449 }
450
451 if (sel != wxNOT_FOUND) {
452 mRecordChannels = sel + 1;
453 }
454
455 mChannels->Clear();
456
457 // Mimic old behavior
458 if (cnt <= 0) {
459 cnt = 16;
460 }
461
462 // Place an artificial limit on the number of channels to prevent an
463 // outrageous number. I don't know if this is really necessary, but
464 // it doesn't hurt.
465 if (cnt > 256) {
466 cnt = 256;
467 }
468
469 wxArrayStringEx channelnames;
470
471 // Channel counts, mono, stereo etc...
472 for (int i = 0; i < cnt; i++) {
473 wxString name;
474
475 if (i == 0) {
476 name = _("1 (Mono)");
477 }
478 else if (i == 1) {
479 name = _("2 (Stereo)");
480 }
481 else {
482 name = wxString::Format(wxT("%d"), i + 1);
483 }
484
485 channelnames.push_back(name);
486 int index = mChannels->Append(name);
487 if (i == mRecordChannels - 1) {
488 mChannels->SetSelection(index);
489 }
490 }
491
492 if (mChannels->GetCount() && mChannels->GetCurrentSelection() == wxNOT_FOUND) {
493 mChannels->SetSelection(0);
494 }
495
496 ShuttleGui::SetMinSize(mChannels, channelnames);
497 Layout();
498}
499
501{
502 const int sel = mDefaultSampleRates->GetSelection();
503 mOtherDefaultSampleRate->Enable(sel == (int)mDefaultSampleRates->GetCount() - 1);
504}
505
507{
508 if (mProjectSampleRates == nullptr)
509 return;
510
511 const int sel = mProjectSampleRates->GetSelection();
513 sel == (int)mProjectSampleRates->GetCount() - 1);
514}
515
517{
520 DeviceSourceMap *map = NULL;
521
522 if (mPlay->GetCount() > 0) {
523 map = (DeviceSourceMap *) mPlay->GetClientData(
524 mPlay->GetSelection());
525 }
526 if (map)
528
529 map = NULL;
530 if (mRecord->GetCount() > 0) {
531 map = (DeviceSourceMap *) mRecord->GetClientData(mRecord->GetSelection());
532 }
533 if (map) {
536 if (map->totalSources >= 1)
538 else
540 AudioIORecordChannels.Write(mChannels->GetSelection() + 1);
541 }
542
545
547
548 // The complex compound control may have value 'other' in which case the
549 // value in prefs comes from the second field.
550 if (mOtherDefaultSampleRate->IsEnabled())
551 {
553 gPrefs->Flush();
554 }
555
556 if (mProject)
557 {
558 auto& projectRate = ProjectRate::Get(*mProject);
559 if (mOtherProjectSampleRate->IsEnabled())
560 projectRate.SetRate(mOtherProjectSampleRateValue);
561 else
562 projectRate.SetRate(mSampleRateValues[mProjectSampleRates->GetSelection()]);
563 }
564
565 return true;
566}
567
568PrefsPanel *DevicePrefsFactory(wxWindow *parent, wxWindowID winid, AudacityProject *project)
569{
570 wxASSERT(parent); // to justify safenew
571 return safenew DevicePrefs(parent, winid, project);
572}
573
574namespace{
577 };
578}
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:58
@ DefaultSampleRateChoice
Definition: DevicePrefs.cpp:59
@ PlayID
Definition: DevicePrefs.cpp:56
@ HostID
Definition: DevicePrefs.cpp:55
@ RecordID
Definition: DevicePrefs.cpp:57
@ ProjectSampleRateChoice
Definition: DevicePrefs.cpp:60
PrefsPanel * DevicePrefsFactory(wxWindow *parent, wxWindowID winid, AudacityProject *project)
#define DEVICE_PREFS_PLUGIN_SYMBOL
Definition: DevicePrefs.h:23
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
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
ByColumns_t ByColumns
Definition: Prefs.cpp:489
an object holding per-project preferred sample rate
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:46
@ eIsSavingToPrefs
Definition: ShuttleGui.h:47
const auto project
THEME_API Theme theTheme
Definition: Theme.cpp:82
#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
static const int StandardRates[]
Array of common audio sample rates.
Definition: AudioIOBase.h:189
static const int NumStandardRates
How many standard sample rates there are.
Definition: AudioIOBase.h:191
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:26
wxTextCtrl * mOtherProjectSampleRate
Definition: DevicePrefs.h:63
void OnDevice(wxCommandEvent &e)
wxChoice * mDefaultSampleRates
Definition: DevicePrefs.h:68
wxArrayStringEx mHostLabels
Definition: DevicePrefs.h:50
void OnProjectSampleRateChoice(wxCommandEvent &e)
wxChoice * mHost
Definition: DevicePrefs.h:57
wxTextCtrl * mOtherDefaultSampleRate
Definition: DevicePrefs.h:69
ComponentInterfaceSymbol GetSymbol() const override
Definition: DevicePrefs.cpp:82
void PopulateOrExchange(ShuttleGui &S) override
wxChoice * mRecord
Definition: DevicePrefs.h:59
int mOtherDefaultSampleRateValue
Definition: DevicePrefs.h:70
wxString mRecordDevice
Definition: DevicePrefs.h:53
wxString mPlayDevice
Definition: DevicePrefs.h:52
TranslatableStrings mSampleRateNames
Definition: DevicePrefs.h:72
void OnDefaultSampleRateChoice(wxCommandEvent &e)
wxString mRecordSource
Definition: DevicePrefs.h:54
void OnHost(wxCommandEvent &e)
TranslatableString GetDescription() const override
Definition: DevicePrefs.cpp:87
virtual ~DevicePrefs()
Definition: DevicePrefs.cpp:77
wxChoice * mPlay
Definition: DevicePrefs.h:58
void GetNamesAndLabels()
wxChoice * mProjectSampleRates
Definition: DevicePrefs.h:62
ManualPageID HelpPageName() override
If not empty string, the Help button is added below the panel.
Definition: DevicePrefs.cpp:92
wxChoice * mChannels
Definition: DevicePrefs.h:60
std::vector< int > mSampleRateValues
Definition: DevicePrefs.h:73
int mProjectSampleRateIndex
Definition: DevicePrefs.h:65
long mRecordChannels
Definition: DevicePrefs.h:55
AudacityProject * mProject
Definition: DevicePrefs.h:47
void Populate()
Definition: DevicePrefs.cpp:97
int mOtherProjectSampleRateValue
Definition: DevicePrefs.h:66
bool Commit() override
TranslatableStrings mHostNames
Definition: DevicePrefs.h:49
Abstract base class used in importing a file.
Base class for a panel in the PrefsDialog. Classes derived from this class include BatchPrefs,...
Definition: PrefsPanel.h:51
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
bool Write(const T &value)
Write value to config and return true if successful.
Definition: Prefs.h:257
void Invalidate() override
Definition: Prefs.h:287
bool Reset()
Reset to the default value.
Definition: Prefs.h:282
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:205
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:630
static void SetMinSize(wxWindow *window, const TranslatableStrings &items)
wxBitmap & Bitmap(int iIndex)
Holds a msgid for the translation catalog; may also bind format arguments.
virtual bool Flush() noexcept=0
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
PROJECT_RATE_API IntSetting DefaultSampleRate
PROJECT_RATE_API EnumSetting< sampleFormat > SampleFormatSetting
PrefsPanel::Registration sAttachment
wxString sourceString
Definition: DeviceManager.h:34
wxString deviceString
Definition: DeviceManager.h:35