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/intl.h>
35#include <wx/log.h>
36#include <wx/textctrl.h>
37
38#include "portaudio.h"
39
40#include "Prefs.h"
41#include "../ShuttleGui.h"
42#include "DeviceManager.h"
43
44enum {
45 HostID = 10000,
49};
50
51BEGIN_EVENT_TABLE(DevicePrefs, PrefsPanel)
52 EVT_CHOICE(HostID, DevicePrefs::OnHost)
55
56DevicePrefs::DevicePrefs(wxWindow * parent, wxWindowID winid)
57: PrefsPanel(parent, winid, XO("Devices"))
58{
59 Populate();
60}
61
63{
64}
65
66
68{
70}
71
73{
74 return XO("Preferences for Device");
75}
76
78{
79 return "Devices_Preferences";
80}
81
83{
84 // First any pre-processing for constructing the GUI.
86
87 // Get current setting for devices
92
93 //------------------------- Main section --------------------
94 // Now construct the GUI itself.
95 // Use 'eIsCreatingFromPrefs' so that the GUI is
96 // initialised with values from gPrefs.
99 // ----------------------- End of main section --------------
100
101 wxCommandEvent e;
102 OnHost(e);
103}
104
105
106/*
107 * Get names of device hosts.
108 */
110{
111 // Gather list of hosts. Only added hosts that have devices attached.
112 // FIXME: TRAP_ERR PaErrorCode not handled in DevicePrefs GetNamesAndLabels()
113 // With an error code won't add hosts, but won't report a problem either.
114 int nDevices = Pa_GetDeviceCount();
115 for (int i = 0; i < nDevices; i++) {
116 const PaDeviceInfo *info = Pa_GetDeviceInfo(i);
117 if ((info!=NULL)&&(info->maxOutputChannels > 0 || info->maxInputChannels > 0)) {
118 wxString name = wxSafeConvertMB2WX(Pa_GetHostApiInfo(info->hostApi)->name);
120 .contains( Verbatim( name ) )) {
121 mHostNames.push_back( Verbatim( name ) );
122 mHostLabels.push_back(name);
123 }
124 }
125 }
126}
127
129{
130 ChoiceSetting HostSetting{
133 };
134 S.SetBorder(2);
135 S.StartScroller();
136
137 /* i18n-hint Software interface to audio devices */
138 S.StartStatic(XC("Interface", "device"));
139 {
140 S.StartMultiColumn(2);
141 {
142 S.Id(HostID);
143 mHost = S.TieChoice( XXO("&Host:"), HostSetting);
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 // i18n-hint: modifier as in "Recording preferences", not progressive verb
165 S.StartStatic(XC("Recording", "preference"));
166 {
167 S.StartMultiColumn(2);
168 {
169 S.Id(RecordID);
170 mRecord = S.AddChoice(XXO("De&vice:"),
171 {} );
172
173 S.Id(ChannelsID);
174 mChannels = S.AddChoice(XXO("Cha&nnels:"),
175 {} );
176 }
177 S.EndMultiColumn();
178 }
179 S.EndStatic();
180
181 // These previously lived in recording preferences.
182 // However they are liable to become device specific.
183 // Buffering also affects playback, not just recording, so is a device characteristic.
184 S.StartStatic( XO("Latency"));
185 {
186 S.StartThreeColumn();
187 {
188 wxTextCtrl *w;
189 // only show the following controls if we use Portaudio v19, because
190 // for Portaudio v18 we always use default buffer sizes
191 w = S
192 .NameSuffix(XO("milliseconds"))
193 .TieNumericTextBox(XXO("&Buffer length:"),
195 25);
196 S.AddUnits(XO("milliseconds"));
197
198 w = S
199 .NameSuffix(XO("milliseconds"))
200 .TieNumericTextBox(XXO("&Latency compensation:"),
202 S.AddUnits(XO("milliseconds"));
203 }
204 S.EndThreeColumn();
205 }
206 S.EndStatic();
207 S.EndScroller();
208
209}
210
211void DevicePrefs::OnHost(wxCommandEvent & e)
212{
213 // Bail if we have no hosts
214 if (mHostNames.size() < 1)
215 return;
216
217 // Find the index for the host API selected
218 int index = -1;
219 auto apiName = mHostLabels[mHost->GetCurrentSelection()];
220 int nHosts = Pa_GetHostApiCount();
221 for (int i = 0; i < nHosts; ++i) {
222 wxString name = wxSafeConvertMB2WX(Pa_GetHostApiInfo(i)->name);
223 if (name == apiName) {
224 index = i;
225 break;
226 }
227 }
228 // We should always find the host!
229 if (index < 0) {
230 wxLogDebug(wxT("DevicePrefs::OnHost(): API index not found"));
231 return;
232 }
233
234 int nDevices = Pa_GetDeviceCount();
235
236 // FIXME: TRAP_ERR PaErrorCode not handled. nDevices can be negative number.
237 if (nDevices == 0) {
238 mHost->Clear();
239 mHost->Append(_("No audio interfaces"), (void *) NULL);
240 mHost->SetSelection(0);
241 }
242
243 const std::vector<DeviceSourceMap> &inMaps = DeviceManager::Instance()->GetInputDeviceMaps();
244 const std::vector<DeviceSourceMap> &outMaps = DeviceManager::Instance()->GetOutputDeviceMaps();
245
246 wxArrayString playnames;
247 wxArrayString recordnames;
248 size_t i;
249 int devindex; /* temp variable to hold the numeric ID of each device in turn */
250 wxString device;
251 wxString recDevice;
252
253 recDevice = mRecordDevice;
254 if (!this->mRecordSource.empty())
255 recDevice += wxT(": ") + mRecordSource;
256
257 mRecord->Clear();
258 for (i = 0; i < inMaps.size(); i++) {
259 if (index == inMaps[i].hostIndex) {
260 device = MakeDeviceSourceString(&inMaps[i]);
261 devindex = mRecord->Append(device);
262 // We need to const cast here because SetClientData is a wx function
263 // It is okay because the original variable is non-const.
264 mRecord->SetClientData(devindex, const_cast<DeviceSourceMap *>(&inMaps[i]));
265 if (device == recDevice) { /* if this is the default device, select it */
266 mRecord->SetSelection(devindex);
267 }
268 }
269 }
270
271 mPlay->Clear();
272 for (i = 0; i < outMaps.size(); i++) {
273 if (index == outMaps[i].hostIndex) {
274 device = MakeDeviceSourceString(&outMaps[i]);
275 devindex = mPlay->Append(device);
276 mPlay->SetClientData(devindex, const_cast<DeviceSourceMap *>(&outMaps[i]));
277 if (device == mPlayDevice) { /* if this is the default device, select it */
278 mPlay->SetSelection(devindex);
279 }
280 }
281 }
282
283 /* deal with not having any devices at all */
284 if (mPlay->GetCount() == 0) {
285 playnames.push_back(_("No devices found"));
286 mPlay->Append(playnames[0], (void *) NULL);
287 mPlay->SetSelection(0);
288 }
289 if (mRecord->GetCount() == 0) {
290 recordnames.push_back(_("No devices found"));
291 mRecord->Append(recordnames[0], (void *) NULL);
292 mRecord->SetSelection(0);
293 }
294
295 /* what if we have no device selected? we should choose the default on
296 * this API, as defined by PortAudio. We then fall back to using 0 only if
297 * that fails */
298 if (mPlay->GetCount() && mPlay->GetSelection() == wxNOT_FOUND) {
300 if (defaultMap)
301 mPlay->SetStringSelection(MakeDeviceSourceString(defaultMap));
302
303 if (mPlay->GetSelection() == wxNOT_FOUND) {
304 mPlay->SetSelection(0);
305 }
306 }
307
308 if (mRecord->GetCount() && mRecord->GetSelection() == wxNOT_FOUND) {
310 if (defaultMap)
311 mRecord->SetStringSelection(MakeDeviceSourceString(defaultMap));
312
313 if (mPlay->GetSelection() == wxNOT_FOUND) {
314 mPlay->SetSelection(0);
315 }
316 }
317
318 ShuttleGui::SetMinSize(mPlay, mPlay->GetStrings());
319 ShuttleGui::SetMinSize(mRecord, mRecord->GetStrings());
320 OnDevice(e);
321}
322
323void DevicePrefs::OnDevice(wxCommandEvent & WXUNUSED(event))
324{
325 int ndx = mRecord->GetCurrentSelection();
326 if (ndx == wxNOT_FOUND) {
327 ndx = 0;
328 }
329
330 int sel = mChannels->GetSelection();
331 int cnt = 0;
332
333 DeviceSourceMap *inMap = (DeviceSourceMap *) mRecord->GetClientData(ndx);
334 if (inMap != NULL) {
335 cnt = inMap->numChannels;
336 }
337
338 if (sel != wxNOT_FOUND) {
339 mRecordChannels = sel + 1;
340 }
341
342 mChannels->Clear();
343
344 // Mimic old behavior
345 if (cnt <= 0) {
346 cnt = 16;
347 }
348
349 // Place an artificial limit on the number of channels to prevent an
350 // outrageous number. I don't know if this is really necessary, but
351 // it doesn't hurt.
352 if (cnt > 256) {
353 cnt = 256;
354 }
355
356 wxArrayStringEx channelnames;
357
358 // Channel counts, mono, stereo etc...
359 for (int i = 0; i < cnt; i++) {
360 wxString name;
361
362 if (i == 0) {
363 name = _("1 (Mono)");
364 }
365 else if (i == 1) {
366 name = _("2 (Stereo)");
367 }
368 else {
369 name = wxString::Format(wxT("%d"), i + 1);
370 }
371
372 channelnames.push_back(name);
373 int index = mChannels->Append(name);
374 if (i == mRecordChannels - 1) {
375 mChannels->SetSelection(index);
376 }
377 }
378
379 if (mChannels->GetCount() && mChannels->GetCurrentSelection() == wxNOT_FOUND) {
380 mChannels->SetSelection(0);
381 }
382
383 ShuttleGui::SetMinSize(mChannels, channelnames);
384 Layout();
385}
386
388{
391 DeviceSourceMap *map = NULL;
392
393 if (mPlay->GetCount() > 0) {
394 map = (DeviceSourceMap *) mPlay->GetClientData(
395 mPlay->GetSelection());
396 }
397 if (map)
399
400 map = NULL;
401 if (mRecord->GetCount() > 0) {
402 map = (DeviceSourceMap *) mRecord->GetClientData(mRecord->GetSelection());
403 }
404 if (map) {
407 if (map->totalSources >= 1)
409 else
411 AudioIORecordChannels.Write(mChannels->GetSelection() + 1);
412 }
413
416 return true;
417}
418
419PrefsPanel *DevicePrefsFactory(wxWindow *parent, wxWindowID winid, AudacityProject *)
420{
421 wxASSERT(parent); // to justify safenew
422 return safenew DevicePrefs(parent, winid);
423}
424
425namespace{
428 };
429}
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)
PrefsPanel * DevicePrefsFactory(wxWindow *parent, wxWindowID winid, AudacityProject *)
@ ChannelsID
Definition: DevicePrefs.cpp:48
@ PlayID
Definition: DevicePrefs.cpp:46
@ HostID
Definition: DevicePrefs.cpp:45
@ RecordID
Definition: DevicePrefs.cpp:47
#define DEVICE_PREFS_PLUGIN_SYMBOL
Definition: DevicePrefs.h:22
const TranslatableString name
Definition: Distortion.cpp:82
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define XC(s, c)
Definition: Internat.h:37
#define _(s)
Definition: Internat.h:75
#define safenew
Definition: MemoryX.h:10
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:431
ByColumns_t ByColumns
Definition: Prefs.cpp:474
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:48
@ eIsSavingToPrefs
Definition: ShuttleGui.h:49
#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:89
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:67
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:72
virtual ~DevicePrefs()
Definition: DevicePrefs.cpp:62
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:77
wxChoice * mChannels
Definition: DevicePrefs.h:54
long mRecordChannels
Definition: DevicePrefs.h:49
void Populate()
Definition: DevicePrefs.cpp:82
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:628
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