Workaround a GCC 5 issue
[openal-soft.git] / utils / alsoft-config / mainwindow.cpp
blob547dc3003728c192c719dd2aec2be4bb0bcd13eb
2 #include "config.h"
4 #include "mainwindow.h"
6 #include <iostream>
7 #include <cmath>
9 #include <QFileDialog>
10 #include <QMessageBox>
11 #include <QCloseEvent>
12 #include <QSettings>
13 #include <QtGlobal>
14 #include "ui_mainwindow.h"
15 #include "verstr.h"
17 #ifdef _WIN32
18 #include <windows.h>
19 #include <shlobj.h>
20 #endif
22 namespace {
24 static const struct {
25 char backend_name[16];
26 char full_string[32];
27 } backendList[] = {
28 #ifdef HAVE_JACK
29 { "jack", "JACK" },
30 #endif
31 #ifdef HAVE_PULSEAUDIO
32 { "pulse", "PulseAudio" },
33 #endif
34 #ifdef HAVE_ALSA
35 { "alsa", "ALSA" },
36 #endif
37 #ifdef HAVE_COREAUDIO
38 { "core", "CoreAudio" },
39 #endif
40 #ifdef HAVE_OSS
41 { "oss", "OSS" },
42 #endif
43 #ifdef HAVE_SOLARIS
44 { "solaris", "Solaris" },
45 #endif
46 #ifdef HAVE_SNDIO
47 { "sndio", "SoundIO" },
48 #endif
49 #ifdef HAVE_QSA
50 { "qsa", "QSA" },
51 #endif
52 #ifdef HAVE_WASAPI
53 { "wasapi", "WASAPI" },
54 #endif
55 #ifdef HAVE_DSOUND
56 { "dsound", "DirectSound" },
57 #endif
58 #ifdef HAVE_WINMM
59 { "winmm", "Windows Multimedia" },
60 #endif
61 #ifdef HAVE_PORTAUDIO
62 { "port", "PortAudio" },
63 #endif
64 #ifdef HAVE_OPENSL
65 { "opensl", "OpenSL" },
66 #endif
68 { "null", "Null Output" },
69 #ifdef HAVE_WAVE
70 { "wave", "Wave Writer" },
71 #endif
72 { "", "" }
75 static const struct NameValuePair {
76 const char name[64];
77 const char value[16];
78 } speakerModeList[] = {
79 { "Autodetect", "" },
80 { "Mono", "mono" },
81 { "Stereo", "stereo" },
82 { "Quadraphonic", "quad" },
83 { "5.1 Surround (Side)", "surround51" },
84 { "5.1 Surround (Rear)", "surround51rear" },
85 { "6.1 Surround", "surround61" },
86 { "7.1 Surround", "surround71" },
88 { "Ambisonic, 1st Order", "ambi1" },
89 { "Ambisonic, 2nd Order", "ambi2" },
90 { "Ambisonic, 3rd Order", "ambi3" },
92 { "", "" }
93 }, sampleTypeList[] = {
94 { "Autodetect", "" },
95 { "8-bit int", "int8" },
96 { "8-bit uint", "uint8" },
97 { "16-bit int", "int16" },
98 { "16-bit uint", "uint16" },
99 { "32-bit int", "int32" },
100 { "32-bit uint", "uint32" },
101 { "32-bit float", "float32" },
103 { "", "" }
104 }, resamplerList[] = {
105 { "Point", "point" },
106 { "Linear", "linear" },
107 { "Default (Linear)", "" },
108 { "Cubic Spline", "cubic" },
109 { "11th order Sinc (fast)", "fast_bsinc12" },
110 { "11th order Sinc", "bsinc12" },
111 { "23rd order Sinc (fast)", "fast_bsinc24" },
112 { "23rd order Sinc", "bsinc24" },
114 { "", "" }
115 }, stereoModeList[] = {
116 { "Autodetect", "" },
117 { "Speakers", "speakers" },
118 { "Headphones", "headphones" },
120 { "", "" }
121 }, stereoEncList[] = {
122 { "Default", "" },
123 { "Pan Pot", "panpot" },
124 { "UHJ", "uhj" },
126 { "", "" }
127 }, ambiFormatList[] = {
128 { "Default", "" },
129 { "AmbiX (ACN, SN3D)", "ambix" },
130 { "ACN, N3D", "acn+n3d" },
131 { "Furse-Malham", "fuma" },
133 { "", "" }
134 }, hrtfModeList[] = {
135 { "1st Order Ambisonic", "ambi1" },
136 { "2nd Order Ambisonic", "ambi2" },
137 { "Default (Full)", "" },
138 { "Full", "full" },
140 { "", "" }
143 static QString getDefaultConfigName()
145 #ifdef Q_OS_WIN32
146 static const char fname[] = "alsoft.ini";
147 auto get_appdata_path = []() noexcept -> QString
149 WCHAR buffer[MAX_PATH];
150 if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE)
151 return QString::fromWCharArray(buffer);
152 return QString();
154 QString base = get_appdata_path();
155 #else
156 static const char fname[] = "alsoft.conf";
157 QByteArray base = qgetenv("XDG_CONFIG_HOME");
158 if(base.isEmpty())
160 base = qgetenv("HOME");
161 if(base.isEmpty() == false)
162 base += "/.config";
164 #endif
165 if(base.isEmpty() == false)
166 return base +'/'+ fname;
167 return fname;
170 static QString getBaseDataPath()
172 #ifdef Q_OS_WIN32
173 auto get_appdata_path = []() noexcept -> QString
175 WCHAR buffer[MAX_PATH];
176 if(SHGetSpecialFolderPathW(nullptr, buffer, CSIDL_APPDATA, FALSE) != FALSE)
177 return QString::fromWCharArray(buffer);
178 return QString();
180 QString base = get_appdata_path();
181 #else
182 QByteArray base = qgetenv("XDG_DATA_HOME");
183 if(base.isEmpty())
185 base = qgetenv("HOME");
186 if(!base.isEmpty())
187 base += "/.local/share";
189 #endif
190 return base;
193 static QStringList getAllDataPaths(const QString &append)
195 QStringList list;
196 list.append(getBaseDataPath());
197 #ifdef Q_OS_WIN32
198 // TODO: Common AppData path
199 #else
200 QString paths = qgetenv("XDG_DATA_DIRS");
201 if(paths.isEmpty())
202 paths = "/usr/local/share/:/usr/share/";
203 list += paths.split(QChar(':'), QString::SkipEmptyParts);
204 #endif
205 QStringList::iterator iter = list.begin();
206 while(iter != list.end())
208 if(iter->isEmpty())
209 iter = list.erase(iter);
210 else
212 iter->append(append);
213 iter++;
216 return list;
219 template<size_t N>
220 static QString getValueFromName(const NameValuePair (&list)[N], const QString &str)
222 for(size_t i = 0;i < N-1;i++)
224 if(str == list[i].name)
225 return list[i].value;
227 return QString{};
230 template<size_t N>
231 static QString getNameFromValue(const NameValuePair (&list)[N], const QString &str)
233 for(size_t i = 0;i < N-1;i++)
235 if(str == list[i].value)
236 return list[i].name;
238 return QString{};
242 Qt::CheckState getCheckState(const QVariant &var)
244 if(var.isNull())
245 return Qt::PartiallyChecked;
246 if(var.toBool())
247 return Qt::Checked;
248 return Qt::Unchecked;
251 QString getCheckValue(const QCheckBox *checkbox)
253 const Qt::CheckState state{checkbox->checkState()};
254 if(state == Qt::Checked)
255 return QString{"true"};
256 if(state == Qt::Unchecked)
257 return QString{"false"};
258 return QString{};
263 MainWindow::MainWindow(QWidget *parent) :
264 QMainWindow(parent),
265 ui(new Ui::MainWindow),
266 mPeriodSizeValidator(nullptr),
267 mPeriodCountValidator(nullptr),
268 mSourceCountValidator(nullptr),
269 mEffectSlotValidator(nullptr),
270 mSourceSendValidator(nullptr),
271 mSampleRateValidator(nullptr),
272 mJackBufferValidator(nullptr),
273 mNeedsSave(false)
275 ui->setupUi(this);
277 for(int i = 0;speakerModeList[i].name[0];i++)
278 ui->channelConfigCombo->addItem(speakerModeList[i].name);
279 ui->channelConfigCombo->adjustSize();
280 for(int i = 0;sampleTypeList[i].name[0];i++)
281 ui->sampleFormatCombo->addItem(sampleTypeList[i].name);
282 ui->sampleFormatCombo->adjustSize();
283 for(int i = 0;stereoModeList[i].name[0];i++)
284 ui->stereoModeCombo->addItem(stereoModeList[i].name);
285 ui->stereoModeCombo->adjustSize();
286 for(int i = 0;stereoEncList[i].name[0];i++)
287 ui->stereoEncodingComboBox->addItem(stereoEncList[i].name);
288 ui->stereoEncodingComboBox->adjustSize();
289 for(int i = 0;ambiFormatList[i].name[0];i++)
290 ui->ambiFormatComboBox->addItem(ambiFormatList[i].name);
291 ui->ambiFormatComboBox->adjustSize();
293 int count;
294 for(count = 0;resamplerList[count].name[0];count++) {
296 ui->resamplerSlider->setRange(0, count-1);
298 for(count = 0;hrtfModeList[count].name[0];count++) {
300 ui->hrtfmodeSlider->setRange(0, count-1);
301 ui->hrtfStateComboBox->adjustSize();
303 #if !defined(HAVE_NEON) && !defined(HAVE_SSE)
304 ui->cpuExtDisabledLabel->move(ui->cpuExtDisabledLabel->x(), ui->cpuExtDisabledLabel->y() - 60);
305 #else
306 ui->cpuExtDisabledLabel->setVisible(false);
307 #endif
309 #ifndef HAVE_NEON
311 #ifndef HAVE_SSE4_1
312 #ifndef HAVE_SSE3
313 #ifndef HAVE_SSE2
314 #ifndef HAVE_SSE
315 ui->enableSSECheckBox->setVisible(false);
316 #endif /* !SSE */
317 ui->enableSSE2CheckBox->setVisible(false);
318 #endif /* !SSE2 */
319 ui->enableSSE3CheckBox->setVisible(false);
320 #endif /* !SSE3 */
321 ui->enableSSE41CheckBox->setVisible(false);
322 #endif /* !SSE4.1 */
323 ui->enableNeonCheckBox->setVisible(false);
325 #else /* !Neon */
327 #ifndef HAVE_SSE4_1
328 #ifndef HAVE_SSE3
329 #ifndef HAVE_SSE2
330 #ifndef HAVE_SSE
331 ui->enableNeonCheckBox->move(ui->enableNeonCheckBox->x(), ui->enableNeonCheckBox->y() - 30);
332 ui->enableSSECheckBox->setVisible(false);
333 #endif /* !SSE */
334 ui->enableSSE2CheckBox->setVisible(false);
335 #endif /* !SSE2 */
336 ui->enableSSE3CheckBox->setVisible(false);
337 #endif /* !SSE3 */
338 ui->enableSSE41CheckBox->setVisible(false);
339 #endif /* !SSE4.1 */
341 #endif
343 mPeriodSizeValidator = new QIntValidator{64, 8192, this};
344 ui->periodSizeEdit->setValidator(mPeriodSizeValidator);
345 mPeriodCountValidator = new QIntValidator{2, 16, this};
346 ui->periodCountEdit->setValidator(mPeriodCountValidator);
348 mSourceCountValidator = new QIntValidator{0, 4096, this};
349 ui->srcCountLineEdit->setValidator(mSourceCountValidator);
350 mEffectSlotValidator = new QIntValidator{0, 64, this};
351 ui->effectSlotLineEdit->setValidator(mEffectSlotValidator);
352 mSourceSendValidator = new QIntValidator{0, 16, this};
353 ui->srcSendLineEdit->setValidator(mSourceSendValidator);
354 mSampleRateValidator = new QIntValidator{8000, 192000, this};
355 ui->sampleRateCombo->lineEdit()->setValidator(mSampleRateValidator);
357 mJackBufferValidator = new QIntValidator{0, 8192, this};
358 ui->jackBufferSizeLine->setValidator(mJackBufferValidator);
360 connect(ui->actionLoad, &QAction::triggered, this, &MainWindow::loadConfigFromFile);
361 connect(ui->actionSave_As, &QAction::triggered, this, &MainWindow::saveConfigAsFile);
363 connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::showAboutPage);
365 connect(ui->closeCancelButton, &QPushButton::clicked, this, &MainWindow::cancelCloseAction);
366 connect(ui->applyButton, &QPushButton::clicked, this, &MainWindow::saveCurrentConfig);
368 auto qcb_cicstr = static_cast<void(QComboBox::*)(const QString&)>(&QComboBox::currentIndexChanged);
369 connect(ui->channelConfigCombo, qcb_cicstr, this, &MainWindow::enableApplyButton);
370 connect(ui->sampleFormatCombo, qcb_cicstr, this, &MainWindow::enableApplyButton);
371 connect(ui->stereoModeCombo, qcb_cicstr, this, &MainWindow::enableApplyButton);
372 connect(ui->sampleRateCombo, qcb_cicstr, this, &MainWindow::enableApplyButton);
373 connect(ui->sampleRateCombo, &QComboBox::editTextChanged, this, &MainWindow::enableApplyButton);
375 connect(ui->resamplerSlider, &QSlider::valueChanged, this, &MainWindow::updateResamplerLabel);
377 connect(ui->periodSizeSlider, &QSlider::valueChanged, this, &MainWindow::updatePeriodSizeEdit);
378 connect(ui->periodSizeEdit, &QLineEdit::editingFinished, this, &MainWindow::updatePeriodSizeSlider);
379 connect(ui->periodCountSlider, &QSlider::valueChanged, this, &MainWindow::updatePeriodCountEdit);
380 connect(ui->periodCountEdit, &QLineEdit::editingFinished, this, &MainWindow::updatePeriodCountSlider);
382 connect(ui->stereoEncodingComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
383 connect(ui->ambiFormatComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
384 connect(ui->outputLimiterCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
385 connect(ui->outputDitherCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
387 connect(ui->decoderHQModeCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
388 connect(ui->decoderDistCompCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
389 connect(ui->decoderNFEffectsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
390 auto qdsb_vcd = static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged);
391 connect(ui->decoderNFRefDelaySpinBox, qdsb_vcd, this, &MainWindow::enableApplyButton);
392 connect(ui->decoderQuadLineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
393 connect(ui->decoderQuadButton, &QPushButton::clicked, this, &MainWindow::selectQuadDecoderFile);
394 connect(ui->decoder51LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
395 connect(ui->decoder51Button, &QPushButton::clicked, this, &MainWindow::select51DecoderFile);
396 connect(ui->decoder61LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
397 connect(ui->decoder61Button, &QPushButton::clicked, this, &MainWindow::select61DecoderFile);
398 connect(ui->decoder71LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
399 connect(ui->decoder71Button, &QPushButton::clicked, this, &MainWindow::select71DecoderFile);
401 connect(ui->preferredHrtfComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
402 connect(ui->hrtfStateComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
403 connect(ui->hrtfmodeSlider, &QSlider::valueChanged, this, &MainWindow::updateHrtfModeLabel);
405 connect(ui->hrtfAddButton, &QPushButton::clicked, this, &MainWindow::addHrtfFile);
406 connect(ui->hrtfRemoveButton, &QPushButton::clicked, this, &MainWindow::removeHrtfFile);
407 connect(ui->hrtfFileList, &QListWidget::itemSelectionChanged, this, &MainWindow::updateHrtfRemoveButton);
408 connect(ui->defaultHrtfPathsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
410 connect(ui->srcCountLineEdit, &QLineEdit::editingFinished, this, &MainWindow::enableApplyButton);
411 connect(ui->srcSendLineEdit, &QLineEdit::editingFinished, this, &MainWindow::enableApplyButton);
412 connect(ui->effectSlotLineEdit, &QLineEdit::editingFinished, this, &MainWindow::enableApplyButton);
414 connect(ui->enableSSECheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
415 connect(ui->enableSSE2CheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
416 connect(ui->enableSSE3CheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
417 connect(ui->enableSSE41CheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
418 connect(ui->enableNeonCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
420 ui->enabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu);
421 connect(ui->enabledBackendList, &QListWidget::customContextMenuRequested, this, &MainWindow::showEnabledBackendMenu);
423 ui->disabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu);
424 connect(ui->disabledBackendList, &QListWidget::customContextMenuRequested, this, &MainWindow::showDisabledBackendMenu);
425 connect(ui->backendCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
427 connect(ui->defaultReverbComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
428 connect(ui->enableEaxReverbCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
429 connect(ui->enableStdReverbCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
430 connect(ui->enableAutowahCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
431 connect(ui->enableChorusCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
432 connect(ui->enableCompressorCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
433 connect(ui->enableDistortionCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
434 connect(ui->enableEchoCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
435 connect(ui->enableEqualizerCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
436 connect(ui->enableFlangerCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
437 connect(ui->enableFrequencyShifterCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
438 connect(ui->enableModulatorCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
439 connect(ui->enableDedicatedCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
440 connect(ui->enablePitchShifterCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
441 connect(ui->enableVocalMorpherCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
443 connect(ui->pulseAutospawnCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
444 connect(ui->pulseAllowMovesCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
445 connect(ui->pulseFixRateCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
446 connect(ui->pulseAdjLatencyCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
448 connect(ui->jackAutospawnCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
449 connect(ui->jackConnectPortsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
450 connect(ui->jackBufferSizeSlider, &QSlider::valueChanged, this, &MainWindow::updateJackBufferSizeEdit);
451 connect(ui->jackBufferSizeLine, &QLineEdit::editingFinished, this, &MainWindow::updateJackBufferSizeSlider);
453 connect(ui->alsaDefaultDeviceLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
454 connect(ui->alsaDefaultCaptureLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
455 connect(ui->alsaResamplerCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
456 connect(ui->alsaMmapCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
458 connect(ui->ossDefaultDeviceLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
459 connect(ui->ossPlaybackPushButton, &QPushButton::clicked, this, &MainWindow::selectOSSPlayback);
460 connect(ui->ossDefaultCaptureLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
461 connect(ui->ossCapturePushButton, &QPushButton::clicked, this, &MainWindow::selectOSSCapture);
463 connect(ui->solarisDefaultDeviceLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
464 connect(ui->solarisPlaybackPushButton, &QPushButton::clicked, this, &MainWindow::selectSolarisPlayback);
466 connect(ui->waveOutputLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
467 connect(ui->waveOutputButton, &QPushButton::clicked, this, &MainWindow::selectWaveOutput);
468 connect(ui->waveBFormatCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
470 ui->backendListWidget->setCurrentRow(0);
471 ui->tabWidget->setCurrentIndex(0);
473 for(int i = 1;i < ui->backendListWidget->count();i++)
474 ui->backendListWidget->setRowHidden(i, true);
475 for(int i = 0;backendList[i].backend_name[0];i++)
477 QList<QListWidgetItem*> items = ui->backendListWidget->findItems(
478 backendList[i].full_string, Qt::MatchFixedString);
479 foreach(QListWidgetItem *item, items)
480 item->setHidden(false);
483 loadConfig(getDefaultConfigName());
486 MainWindow::~MainWindow()
488 delete ui;
489 delete mPeriodSizeValidator;
490 delete mPeriodCountValidator;
491 delete mSourceCountValidator;
492 delete mEffectSlotValidator;
493 delete mSourceSendValidator;
494 delete mSampleRateValidator;
495 delete mJackBufferValidator;
498 void MainWindow::closeEvent(QCloseEvent *event)
500 if(!mNeedsSave)
501 event->accept();
502 else
504 QMessageBox::StandardButton btn = QMessageBox::warning(this,
505 tr("Apply changes?"), tr("Save changes before quitting?"),
506 QMessageBox::Save | QMessageBox::No | QMessageBox::Cancel);
507 if(btn == QMessageBox::Save)
508 saveCurrentConfig();
509 if(btn == QMessageBox::Cancel)
510 event->ignore();
511 else
512 event->accept();
516 void MainWindow::cancelCloseAction()
518 mNeedsSave = false;
519 close();
523 void MainWindow::showAboutPage()
525 QMessageBox::information(this, tr("About"),
526 tr("OpenAL Soft Configuration Utility.\nBuilt for OpenAL Soft library version ") +
527 GetVersionString());
531 QStringList MainWindow::collectHrtfs()
533 QStringList ret;
534 QStringList processed;
536 for(int i = 0;i < ui->hrtfFileList->count();i++)
538 QDir dir(ui->hrtfFileList->item(i)->text());
539 QStringList fnames = dir.entryList(QDir::Files | QDir::Readable, QDir::Name);
540 foreach(const QString &fname, fnames)
542 if(!fname.endsWith(".mhr", Qt::CaseInsensitive))
543 continue;
544 QString fullname{dir.absoluteFilePath(fname)};
545 if(processed.contains(fullname))
546 continue;
547 processed.push_back(fullname);
549 QString name{fname.left(fname.length()-4)};
550 if(!ret.contains(name))
551 ret.push_back(name);
552 else
554 size_t i{2};
555 do {
556 QString s = name+" #"+QString::number(i);
557 if(!ret.contains(s))
559 ret.push_back(s);
560 break;
562 ++i;
563 } while(1);
568 if(ui->defaultHrtfPathsCheckBox->isChecked())
570 QStringList paths = getAllDataPaths("/openal/hrtf");
571 foreach(const QString &name, paths)
573 QDir dir{name};
574 QStringList fnames{dir.entryList(QDir::Files | QDir::Readable, QDir::Name)};
575 foreach(const QString &fname, fnames)
577 if(!fname.endsWith(".mhr", Qt::CaseInsensitive))
578 continue;
579 QString fullname{dir.absoluteFilePath(fname)};
580 if(processed.contains(fullname))
581 continue;
582 processed.push_back(fullname);
584 QString name{fname.left(fname.length()-4)};
585 if(!ret.contains(name))
586 ret.push_back(name);
587 else
589 size_t i{2};
590 do {
591 QString s{name+" #"+QString::number(i)};
592 if(!ret.contains(s))
594 ret.push_back(s);
595 break;
597 ++i;
598 } while(1);
603 #ifdef ALSOFT_EMBED_HRTF_DATA
604 ret.push_back("Built-In HRTF");
605 #endif
607 return ret;
611 void MainWindow::loadConfigFromFile()
613 QString fname = QFileDialog::getOpenFileName(this, tr("Select Files"));
614 if(fname.isEmpty() == false)
615 loadConfig(fname);
618 void MainWindow::loadConfig(const QString &fname)
620 QSettings settings{fname, QSettings::IniFormat};
622 QString sampletype = settings.value("sample-type").toString();
623 ui->sampleFormatCombo->setCurrentIndex(0);
624 if(sampletype.isEmpty() == false)
626 QString str{getNameFromValue(sampleTypeList, sampletype)};
627 if(!str.isEmpty())
629 const int j{ui->sampleFormatCombo->findText(str)};
630 if(j > 0) ui->sampleFormatCombo->setCurrentIndex(j);
634 QString channelconfig{settings.value("channels").toString()};
635 ui->channelConfigCombo->setCurrentIndex(0);
636 if(channelconfig.isEmpty() == false)
638 QString str{getNameFromValue(speakerModeList, channelconfig)};
639 if(!str.isEmpty())
641 const int j{ui->channelConfigCombo->findText(str)};
642 if(j > 0) ui->channelConfigCombo->setCurrentIndex(j);
646 QString srate{settings.value("frequency").toString()};
647 if(srate.isEmpty())
648 ui->sampleRateCombo->setCurrentIndex(0);
649 else
651 ui->sampleRateCombo->lineEdit()->clear();
652 ui->sampleRateCombo->lineEdit()->insert(srate);
655 ui->srcCountLineEdit->clear();
656 ui->srcCountLineEdit->insert(settings.value("sources").toString());
657 ui->effectSlotLineEdit->clear();
658 ui->effectSlotLineEdit->insert(settings.value("slots").toString());
659 ui->srcSendLineEdit->clear();
660 ui->srcSendLineEdit->insert(settings.value("sends").toString());
662 QString resampler = settings.value("resampler").toString().trimmed();
663 ui->resamplerSlider->setValue(2);
664 ui->resamplerLabel->setText(resamplerList[2].name);
665 /* The "sinc4" and "sinc8" resamplers are no longer supported. Use "cubic"
666 * as a fallback.
668 if(resampler == "sinc4" || resampler == "sinc8")
669 resampler = "cubic";
670 /* The "bsinc" resampler name is an alias for "bsinc12". */
671 else if(resampler == "bsinc")
672 resampler = "bsinc12";
673 for(int i = 0;resamplerList[i].name[0];i++)
675 if(resampler == resamplerList[i].value)
677 ui->resamplerSlider->setValue(i);
678 ui->resamplerLabel->setText(resamplerList[i].name);
679 break;
683 QString stereomode = settings.value("stereo-mode").toString().trimmed();
684 ui->stereoModeCombo->setCurrentIndex(0);
685 if(stereomode.isEmpty() == false)
687 QString str{getNameFromValue(stereoModeList, stereomode)};
688 if(!str.isEmpty())
690 const int j{ui->stereoModeCombo->findText(str)};
691 if(j > 0) ui->stereoModeCombo->setCurrentIndex(j);
695 int periodsize{settings.value("period_size").toInt()};
696 ui->periodSizeEdit->clear();
697 if(periodsize >= 64)
699 ui->periodSizeEdit->insert(QString::number(periodsize));
700 updatePeriodSizeSlider();
703 int periodcount{settings.value("periods").toInt()};
704 ui->periodCountEdit->clear();
705 if(periodcount >= 2)
707 ui->periodCountEdit->insert(QString::number(periodcount));
708 updatePeriodCountSlider();
711 ui->outputLimiterCheckBox->setCheckState(getCheckState(settings.value("output-limiter")));
712 ui->outputDitherCheckBox->setCheckState(getCheckState(settings.value("dither")));
714 QString stereopan{settings.value("stereo-encoding").toString()};
715 ui->stereoEncodingComboBox->setCurrentIndex(0);
716 if(stereopan.isEmpty() == false)
718 QString str{getNameFromValue(stereoEncList, stereopan)};
719 if(!str.isEmpty())
721 const int j{ui->stereoEncodingComboBox->findText(str)};
722 if(j > 0) ui->stereoEncodingComboBox->setCurrentIndex(j);
726 QString ambiformat{settings.value("ambi-format").toString()};
727 ui->ambiFormatComboBox->setCurrentIndex(0);
728 if(ambiformat.isEmpty() == false)
730 QString str{getNameFromValue(ambiFormatList, ambiformat)};
731 if(!str.isEmpty())
733 const int j{ui->ambiFormatComboBox->findText(str)};
734 if(j > 0) ui->ambiFormatComboBox->setCurrentIndex(j);
738 bool hqmode{settings.value("decoder/hq-mode", true).toBool()};
739 ui->decoderHQModeCheckBox->setChecked(hqmode);
740 ui->decoderDistCompCheckBox->setCheckState(getCheckState(settings.value("decoder/distance-comp")));
741 ui->decoderNFEffectsCheckBox->setCheckState(getCheckState(settings.value("decoder/nfc")));
742 double refdelay{settings.value("decoder/nfc-ref-delay", 0.0).toDouble()};
743 ui->decoderNFRefDelaySpinBox->setValue(refdelay);
745 ui->decoderQuadLineEdit->setText(settings.value("decoder/quad").toString());
746 ui->decoder51LineEdit->setText(settings.value("decoder/surround51").toString());
747 ui->decoder61LineEdit->setText(settings.value("decoder/surround61").toString());
748 ui->decoder71LineEdit->setText(settings.value("decoder/surround71").toString());
750 QStringList disabledCpuExts{settings.value("disable-cpu-exts").toStringList()};
751 if(disabledCpuExts.size() == 1)
752 disabledCpuExts = disabledCpuExts[0].split(QChar(','));
753 for(QString &name : disabledCpuExts)
754 name = name.trimmed();
755 ui->enableSSECheckBox->setChecked(!disabledCpuExts.contains("sse", Qt::CaseInsensitive));
756 ui->enableSSE2CheckBox->setChecked(!disabledCpuExts.contains("sse2", Qt::CaseInsensitive));
757 ui->enableSSE3CheckBox->setChecked(!disabledCpuExts.contains("sse3", Qt::CaseInsensitive));
758 ui->enableSSE41CheckBox->setChecked(!disabledCpuExts.contains("sse4.1", Qt::CaseInsensitive));
759 ui->enableNeonCheckBox->setChecked(!disabledCpuExts.contains("neon", Qt::CaseInsensitive));
761 QString hrtfmode{settings.value("hrtf-mode").toString().trimmed()};
762 ui->hrtfmodeSlider->setValue(2);
763 ui->hrtfmodeLabel->setText(hrtfModeList[2].name);
764 /* The "basic" mode name is no longer supported, and "ambi3" is temporarily
765 * disabled. Use "ambi2" instead.
767 if(hrtfmode == "basic" || hrtfmode == "ambi3")
768 hrtfmode = "ambi2";
769 for(int i = 0;hrtfModeList[i].name[0];i++)
771 if(hrtfmode == hrtfModeList[i].value)
773 ui->hrtfmodeSlider->setValue(i);
774 ui->hrtfmodeLabel->setText(hrtfModeList[i].name);
775 break;
779 QStringList hrtf_paths{settings.value("hrtf-paths").toStringList()};
780 if(hrtf_paths.size() == 1)
781 hrtf_paths = hrtf_paths[0].split(QChar(','));
782 for(QString &name : hrtf_paths)
783 name = name.trimmed();
784 if(!hrtf_paths.empty() && !hrtf_paths.back().isEmpty())
785 ui->defaultHrtfPathsCheckBox->setCheckState(Qt::Unchecked);
786 else
788 hrtf_paths.removeAll(QString());
789 ui->defaultHrtfPathsCheckBox->setCheckState(Qt::Checked);
791 hrtf_paths.removeDuplicates();
792 ui->hrtfFileList->clear();
793 ui->hrtfFileList->addItems(hrtf_paths);
794 updateHrtfRemoveButton();
796 QString hrtfstate{settings.value("hrtf").toString().toLower()};
797 if(hrtfstate == "true")
798 ui->hrtfStateComboBox->setCurrentIndex(1);
799 else if(hrtfstate == "false")
800 ui->hrtfStateComboBox->setCurrentIndex(2);
801 else
802 ui->hrtfStateComboBox->setCurrentIndex(0);
804 ui->preferredHrtfComboBox->clear();
805 ui->preferredHrtfComboBox->addItem("- Any -");
806 if(ui->defaultHrtfPathsCheckBox->isChecked())
808 QStringList hrtfs{collectHrtfs()};
809 foreach(const QString &name, hrtfs)
810 ui->preferredHrtfComboBox->addItem(name);
813 QString defaulthrtf{settings.value("default-hrtf").toString()};
814 ui->preferredHrtfComboBox->setCurrentIndex(0);
815 if(defaulthrtf.isEmpty() == false)
817 int i{ui->preferredHrtfComboBox->findText(defaulthrtf)};
818 if(i > 0)
819 ui->preferredHrtfComboBox->setCurrentIndex(i);
820 else
822 i = ui->preferredHrtfComboBox->count();
823 ui->preferredHrtfComboBox->addItem(defaulthrtf);
824 ui->preferredHrtfComboBox->setCurrentIndex(i);
827 ui->preferredHrtfComboBox->adjustSize();
829 ui->enabledBackendList->clear();
830 ui->disabledBackendList->clear();
831 QStringList drivers{settings.value("drivers").toStringList()};
832 if(drivers.size() == 0)
833 ui->backendCheckBox->setChecked(true);
834 else
836 if(drivers.size() == 1)
837 drivers = drivers[0].split(QChar(','));
838 for(QString &name : drivers)
840 name = name.trimmed();
841 /* Convert "mmdevapi" references to "wasapi" for backwards
842 * compatibility.
844 if(name == "-mmdevapi")
845 name = "-wasapi";
846 else if(name == "mmdevapi")
847 name = "wasapi";
850 bool lastWasEmpty = false;
851 foreach(const QString &backend, drivers)
853 lastWasEmpty = backend.isEmpty();
854 if(lastWasEmpty) continue;
856 if(!backend.startsWith(QChar('-')))
857 for(int j = 0;backendList[j].backend_name[0];j++)
859 if(backend == backendList[j].backend_name)
861 ui->enabledBackendList->addItem(backendList[j].full_string);
862 break;
865 else if(backend.size() > 1)
867 QStringRef backendref{backend.rightRef(backend.size()-1)};
868 for(int j = 0;backendList[j].backend_name[0];j++)
870 if(backendref == backendList[j].backend_name)
872 ui->disabledBackendList->addItem(backendList[j].full_string);
873 break;
878 ui->backendCheckBox->setChecked(lastWasEmpty);
881 QString defaultreverb{settings.value("default-reverb").toString().toLower()};
882 ui->defaultReverbComboBox->setCurrentIndex(0);
883 if(defaultreverb.isEmpty() == false)
885 for(int i = 0;i < ui->defaultReverbComboBox->count();i++)
887 if(defaultreverb.compare(ui->defaultReverbComboBox->itemText(i).toLower()) == 0)
889 ui->defaultReverbComboBox->setCurrentIndex(i);
890 break;
895 QStringList excludefx{settings.value("excludefx").toStringList()};
896 if(excludefx.size() == 1)
897 excludefx = excludefx[0].split(QChar(','));
898 for(QString &name : excludefx)
899 name = name.trimmed();
900 ui->enableEaxReverbCheck->setChecked(!excludefx.contains("eaxreverb", Qt::CaseInsensitive));
901 ui->enableStdReverbCheck->setChecked(!excludefx.contains("reverb", Qt::CaseInsensitive));
902 ui->enableAutowahCheck->setChecked(!excludefx.contains("autowah", Qt::CaseInsensitive));
903 ui->enableChorusCheck->setChecked(!excludefx.contains("chorus", Qt::CaseInsensitive));
904 ui->enableCompressorCheck->setChecked(!excludefx.contains("compressor", Qt::CaseInsensitive));
905 ui->enableDistortionCheck->setChecked(!excludefx.contains("distortion", Qt::CaseInsensitive));
906 ui->enableEchoCheck->setChecked(!excludefx.contains("echo", Qt::CaseInsensitive));
907 ui->enableEqualizerCheck->setChecked(!excludefx.contains("equalizer", Qt::CaseInsensitive));
908 ui->enableFlangerCheck->setChecked(!excludefx.contains("flanger", Qt::CaseInsensitive));
909 ui->enableFrequencyShifterCheck->setChecked(!excludefx.contains("fshifter", Qt::CaseInsensitive));
910 ui->enableModulatorCheck->setChecked(!excludefx.contains("modulator", Qt::CaseInsensitive));
911 ui->enableDedicatedCheck->setChecked(!excludefx.contains("dedicated", Qt::CaseInsensitive));
912 ui->enablePitchShifterCheck->setChecked(!excludefx.contains("pshifter", Qt::CaseInsensitive));
913 ui->enableVocalMorpherCheck->setChecked(!excludefx.contains("vmorpher", Qt::CaseInsensitive));
915 ui->pulseAutospawnCheckBox->setCheckState(getCheckState(settings.value("pulse/spawn-server")));
916 ui->pulseAllowMovesCheckBox->setCheckState(getCheckState(settings.value("pulse/allow-moves")));
917 ui->pulseFixRateCheckBox->setCheckState(getCheckState(settings.value("pulse/fix-rate")));
918 ui->pulseAdjLatencyCheckBox->setCheckState(getCheckState(settings.value("pulse/adjust-latency")));
920 ui->jackAutospawnCheckBox->setCheckState(getCheckState(settings.value("jack/spawn-server")));
921 ui->jackConnectPortsCheckBox->setCheckState(getCheckState(settings.value("jack/connect-ports")));
922 ui->jackBufferSizeLine->setText(settings.value("jack/buffer-size", QString()).toString());
923 updateJackBufferSizeSlider();
925 ui->alsaDefaultDeviceLine->setText(settings.value("alsa/device", QString()).toString());
926 ui->alsaDefaultCaptureLine->setText(settings.value("alsa/capture", QString()).toString());
927 ui->alsaResamplerCheckBox->setCheckState(getCheckState(settings.value("alsa/allow-resampler")));
928 ui->alsaMmapCheckBox->setCheckState(getCheckState(settings.value("alsa/mmap")));
930 ui->ossDefaultDeviceLine->setText(settings.value("oss/device", QString()).toString());
931 ui->ossDefaultCaptureLine->setText(settings.value("oss/capture", QString()).toString());
933 ui->solarisDefaultDeviceLine->setText(settings.value("solaris/device", QString()).toString());
935 ui->waveOutputLine->setText(settings.value("wave/file", QString()).toString());
936 ui->waveBFormatCheckBox->setChecked(settings.value("wave/bformat", false).toBool());
938 ui->applyButton->setEnabled(false);
939 ui->closeCancelButton->setText(tr("Close"));
940 mNeedsSave = false;
943 void MainWindow::saveCurrentConfig()
945 saveConfig(getDefaultConfigName());
946 ui->applyButton->setEnabled(false);
947 ui->closeCancelButton->setText(tr("Close"));
948 mNeedsSave = false;
949 QMessageBox::information(this, tr("Information"),
950 tr("Applications using OpenAL need to be restarted for changes to take effect."));
953 void MainWindow::saveConfigAsFile()
955 QString fname{QFileDialog::getOpenFileName(this, tr("Select Files"))};
956 if(fname.isEmpty() == false)
958 saveConfig(fname);
959 ui->applyButton->setEnabled(false);
960 mNeedsSave = false;
964 void MainWindow::saveConfig(const QString &fname) const
966 QSettings settings{fname, QSettings::IniFormat};
968 /* HACK: Compound any stringlist values into a comma-separated string. */
969 QStringList allkeys{settings.allKeys()};
970 foreach(const QString &key, allkeys)
972 QStringList vals{settings.value(key).toStringList()};
973 if(vals.size() > 1)
974 settings.setValue(key, vals.join(QChar(',')));
977 settings.setValue("sample-type", getValueFromName(sampleTypeList, ui->sampleFormatCombo->currentText()));
978 settings.setValue("channels", getValueFromName(speakerModeList, ui->channelConfigCombo->currentText()));
980 uint rate{ui->sampleRateCombo->currentText().toUInt()};
981 if(rate <= 0)
982 settings.setValue("frequency", QString{});
983 else
984 settings.setValue("frequency", rate);
986 settings.setValue("period_size", ui->periodSizeEdit->text());
987 settings.setValue("periods", ui->periodCountEdit->text());
989 settings.setValue("sources", ui->srcCountLineEdit->text());
990 settings.setValue("slots", ui->effectSlotLineEdit->text());
992 settings.setValue("resampler", resamplerList[ui->resamplerSlider->value()].value);
994 settings.setValue("stereo-mode", getValueFromName(stereoModeList, ui->stereoModeCombo->currentText()));
995 settings.setValue("stereo-encoding", getValueFromName(stereoEncList, ui->stereoEncodingComboBox->currentText()));
996 settings.setValue("ambi-format", getValueFromName(ambiFormatList, ui->ambiFormatComboBox->currentText()));
998 settings.setValue("output-limiter", getCheckValue(ui->outputLimiterCheckBox));
999 settings.setValue("dither", getCheckValue(ui->outputDitherCheckBox));
1001 settings.setValue("decoder/hq-mode",
1002 ui->decoderHQModeCheckBox->isChecked() ? QString{/*"true"*/} : QString{"false"}
1004 settings.setValue("decoder/distance-comp", getCheckValue(ui->decoderDistCompCheckBox));
1005 settings.setValue("decoder/nfc", getCheckValue(ui->decoderNFEffectsCheckBox));
1006 double refdelay = ui->decoderNFRefDelaySpinBox->value();
1007 settings.setValue("decoder/nfc-ref-delay",
1008 (refdelay > 0.0) ? QString::number(refdelay) : QString{}
1011 settings.setValue("decoder/quad", ui->decoderQuadLineEdit->text());
1012 settings.setValue("decoder/surround51", ui->decoder51LineEdit->text());
1013 settings.setValue("decoder/surround61", ui->decoder61LineEdit->text());
1014 settings.setValue("decoder/surround71", ui->decoder71LineEdit->text());
1016 QStringList strlist;
1017 if(!ui->enableSSECheckBox->isChecked())
1018 strlist.append("sse");
1019 if(!ui->enableSSE2CheckBox->isChecked())
1020 strlist.append("sse2");
1021 if(!ui->enableSSE3CheckBox->isChecked())
1022 strlist.append("sse3");
1023 if(!ui->enableSSE41CheckBox->isChecked())
1024 strlist.append("sse4.1");
1025 if(!ui->enableNeonCheckBox->isChecked())
1026 strlist.append("neon");
1027 settings.setValue("disable-cpu-exts", strlist.join(QChar(',')));
1029 settings.setValue("hrtf-mode", hrtfModeList[ui->hrtfmodeSlider->value()].value);
1031 if(ui->hrtfStateComboBox->currentIndex() == 1)
1032 settings.setValue("hrtf", "true");
1033 else if(ui->hrtfStateComboBox->currentIndex() == 2)
1034 settings.setValue("hrtf", "false");
1035 else
1036 settings.setValue("hrtf", QString{});
1038 if(ui->preferredHrtfComboBox->currentIndex() == 0)
1039 settings.setValue("default-hrtf", QString{});
1040 else
1042 QString str{ui->preferredHrtfComboBox->currentText()};
1043 settings.setValue("default-hrtf", str);
1046 strlist.clear();
1047 strlist.reserve(ui->hrtfFileList->count());
1048 for(int i = 0;i < ui->hrtfFileList->count();i++)
1049 strlist.append(ui->hrtfFileList->item(i)->text());
1050 if(!strlist.empty() && ui->defaultHrtfPathsCheckBox->isChecked())
1051 strlist.append(QString{});
1052 settings.setValue("hrtf-paths", strlist.join(QChar{','}));
1054 strlist.clear();
1055 for(int i = 0;i < ui->enabledBackendList->count();i++)
1057 QString label{ui->enabledBackendList->item(i)->text()};
1058 for(int j = 0;backendList[j].backend_name[0];j++)
1060 if(label == backendList[j].full_string)
1062 strlist.append(backendList[j].backend_name);
1063 break;
1067 for(int i = 0;i < ui->disabledBackendList->count();i++)
1069 QString label{ui->disabledBackendList->item(i)->text()};
1070 for(int j = 0;backendList[j].backend_name[0];j++)
1072 if(label == backendList[j].full_string)
1074 strlist.append(QChar{'-'}+QString{backendList[j].backend_name});
1075 break;
1079 if(strlist.size() == 0 && !ui->backendCheckBox->isChecked())
1080 strlist.append("-all");
1081 else if(ui->backendCheckBox->isChecked())
1082 strlist.append(QString{});
1083 settings.setValue("drivers", strlist.join(QChar(',')));
1085 // TODO: Remove check when we can properly match global values.
1086 if(ui->defaultReverbComboBox->currentIndex() == 0)
1087 settings.setValue("default-reverb", QString{});
1088 else
1090 QString str{ui->defaultReverbComboBox->currentText().toLower()};
1091 settings.setValue("default-reverb", str);
1094 strlist.clear();
1095 if(!ui->enableEaxReverbCheck->isChecked())
1096 strlist.append("eaxreverb");
1097 if(!ui->enableStdReverbCheck->isChecked())
1098 strlist.append("reverb");
1099 if(!ui->enableAutowahCheck->isChecked())
1100 strlist.append("autowah");
1101 if(!ui->enableChorusCheck->isChecked())
1102 strlist.append("chorus");
1103 if(!ui->enableDistortionCheck->isChecked())
1104 strlist.append("distortion");
1105 if(!ui->enableCompressorCheck->isChecked())
1106 strlist.append("compressor");
1107 if(!ui->enableEchoCheck->isChecked())
1108 strlist.append("echo");
1109 if(!ui->enableEqualizerCheck->isChecked())
1110 strlist.append("equalizer");
1111 if(!ui->enableFlangerCheck->isChecked())
1112 strlist.append("flanger");
1113 if(!ui->enableFrequencyShifterCheck->isChecked())
1114 strlist.append("fshifter");
1115 if(!ui->enableModulatorCheck->isChecked())
1116 strlist.append("modulator");
1117 if(!ui->enableDedicatedCheck->isChecked())
1118 strlist.append("dedicated");
1119 if(!ui->enablePitchShifterCheck->isChecked())
1120 strlist.append("pshifter");
1121 if(!ui->enableVocalMorpherCheck->isChecked())
1122 strlist.append("vmorpher");
1123 settings.setValue("excludefx", strlist.join(QChar{','}));
1125 settings.setValue("pulse/spawn-server", getCheckValue(ui->pulseAutospawnCheckBox));
1126 settings.setValue("pulse/allow-moves", getCheckValue(ui->pulseAllowMovesCheckBox));
1127 settings.setValue("pulse/fix-rate", getCheckValue(ui->pulseFixRateCheckBox));
1128 settings.setValue("pulse/adjust-latency", getCheckValue(ui->pulseAdjLatencyCheckBox));
1130 settings.setValue("jack/spawn-server", getCheckValue(ui->jackAutospawnCheckBox));
1131 settings.setValue("jack/connect-ports", getCheckValue(ui->jackConnectPortsCheckBox));
1132 settings.setValue("jack/buffer-size", ui->jackBufferSizeLine->text());
1134 settings.setValue("alsa/device", ui->alsaDefaultDeviceLine->text());
1135 settings.setValue("alsa/capture", ui->alsaDefaultCaptureLine->text());
1136 settings.setValue("alsa/allow-resampler", getCheckValue(ui->alsaResamplerCheckBox));
1137 settings.setValue("alsa/mmap", getCheckValue(ui->alsaMmapCheckBox));
1139 settings.setValue("oss/device", ui->ossDefaultDeviceLine->text());
1140 settings.setValue("oss/capture", ui->ossDefaultCaptureLine->text());
1142 settings.setValue("solaris/device", ui->solarisDefaultDeviceLine->text());
1144 settings.setValue("wave/file", ui->waveOutputLine->text());
1145 settings.setValue("wave/bformat",
1146 ui->waveBFormatCheckBox->isChecked() ? QString{"true"} : QString{/*"false"*/}
1149 /* Remove empty keys
1150 * FIXME: Should only remove keys whose value matches the globally-specified value.
1152 allkeys = settings.allKeys();
1153 foreach(const QString &key, allkeys)
1155 QString str{settings.value(key).toString()};
1156 if(str == QString{})
1157 settings.remove(key);
1162 void MainWindow::enableApplyButton()
1164 if(!mNeedsSave)
1165 ui->applyButton->setEnabled(true);
1166 mNeedsSave = true;
1167 ui->closeCancelButton->setText(tr("Cancel"));
1171 void MainWindow::updateResamplerLabel(int num)
1173 ui->resamplerLabel->setText(resamplerList[num].name);
1174 enableApplyButton();
1178 void MainWindow::updatePeriodSizeEdit(int size)
1180 ui->periodSizeEdit->clear();
1181 if(size >= 64)
1182 ui->periodSizeEdit->insert(QString::number(size));
1183 enableApplyButton();
1186 void MainWindow::updatePeriodSizeSlider()
1188 int pos = ui->periodSizeEdit->text().toInt();
1189 if(pos >= 64)
1191 if(pos > 8192)
1192 pos = 8192;
1193 ui->periodSizeSlider->setSliderPosition(pos);
1195 enableApplyButton();
1198 void MainWindow::updatePeriodCountEdit(int count)
1200 ui->periodCountEdit->clear();
1201 if(count >= 2)
1202 ui->periodCountEdit->insert(QString::number(count));
1203 enableApplyButton();
1206 void MainWindow::updatePeriodCountSlider()
1208 int pos = ui->periodCountEdit->text().toInt();
1209 if(pos < 2)
1210 pos = 0;
1211 else if(pos > 16)
1212 pos = 16;
1213 ui->periodCountSlider->setSliderPosition(pos);
1214 enableApplyButton();
1218 void MainWindow::selectQuadDecoderFile()
1219 { selectDecoderFile(ui->decoderQuadLineEdit, "Select Quadraphonic Decoder");}
1220 void MainWindow::select51DecoderFile()
1221 { selectDecoderFile(ui->decoder51LineEdit, "Select 5.1 Surround Decoder");}
1222 void MainWindow::select61DecoderFile()
1223 { selectDecoderFile(ui->decoder61LineEdit, "Select 6.1 Surround Decoder");}
1224 void MainWindow::select71DecoderFile()
1225 { selectDecoderFile(ui->decoder71LineEdit, "Select 7.1 Surround Decoder");}
1226 void MainWindow::selectDecoderFile(QLineEdit *line, const char *caption)
1228 QString dir{line->text()};
1229 if(dir.isEmpty() || QDir::isRelativePath(dir))
1231 QStringList paths{getAllDataPaths("/openal/presets")};
1232 while(!paths.isEmpty())
1234 if(QDir{paths.last()}.exists())
1236 dir = paths.last();
1237 break;
1239 paths.removeLast();
1242 QString fname{QFileDialog::getOpenFileName(this, tr(caption),
1243 dir, tr("AmbDec Files (*.ambdec);;All Files (*.*)"))};
1244 if(!fname.isEmpty())
1246 line->setText(fname);
1247 enableApplyButton();
1252 void MainWindow::updateJackBufferSizeEdit(int size)
1254 ui->jackBufferSizeLine->clear();
1255 if(size > 0)
1256 ui->jackBufferSizeLine->insert(QString::number(1<<size));
1257 enableApplyButton();
1260 void MainWindow::updateJackBufferSizeSlider()
1262 int value{ui->jackBufferSizeLine->text().toInt()};
1263 auto pos = static_cast<int>(floor(log2(value) + 0.5));
1264 ui->jackBufferSizeSlider->setSliderPosition(pos);
1265 enableApplyButton();
1269 void MainWindow::updateHrtfModeLabel(int num)
1271 ui->hrtfmodeLabel->setText(hrtfModeList[num].name);
1272 enableApplyButton();
1276 void MainWindow::addHrtfFile()
1278 QString path{QFileDialog::getExistingDirectory(this, tr("Select HRTF Path"))};
1279 if(path.isEmpty() == false && !getAllDataPaths("/openal/hrtf").contains(path))
1281 ui->hrtfFileList->addItem(path);
1282 enableApplyButton();
1286 void MainWindow::removeHrtfFile()
1288 QList<QListWidgetItem*> selected{ui->hrtfFileList->selectedItems()};
1289 if(!selected.isEmpty())
1291 foreach(QListWidgetItem *item, selected)
1292 delete item;
1293 enableApplyButton();
1297 void MainWindow::updateHrtfRemoveButton()
1299 ui->hrtfRemoveButton->setEnabled(ui->hrtfFileList->selectedItems().size() != 0);
1302 void MainWindow::showEnabledBackendMenu(QPoint pt)
1304 QHash<QAction*,QString> actionMap;
1306 pt = ui->enabledBackendList->mapToGlobal(pt);
1308 QMenu ctxmenu;
1309 QAction *removeAction{ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove")};
1310 if(ui->enabledBackendList->selectedItems().size() == 0)
1311 removeAction->setEnabled(false);
1312 ctxmenu.addSeparator();
1313 for(size_t i = 0;backendList[i].backend_name[0];i++)
1315 QString backend{backendList[i].full_string};
1316 QAction *action{ctxmenu.addAction(QString("Add ")+backend)};
1317 actionMap[action] = backend;
1318 if(ui->enabledBackendList->findItems(backend, Qt::MatchFixedString).size() != 0 ||
1319 ui->disabledBackendList->findItems(backend, Qt::MatchFixedString).size() != 0)
1320 action->setEnabled(false);
1323 QAction *gotAction{ctxmenu.exec(pt)};
1324 if(gotAction == removeAction)
1326 QList<QListWidgetItem*> selected{ui->enabledBackendList->selectedItems()};
1327 foreach(QListWidgetItem *item, selected)
1328 delete item;
1329 enableApplyButton();
1331 else if(gotAction != nullptr)
1333 auto iter = actionMap.constFind(gotAction);
1334 if(iter != actionMap.cend())
1335 ui->enabledBackendList->addItem(iter.value());
1336 enableApplyButton();
1340 void MainWindow::showDisabledBackendMenu(QPoint pt)
1342 QHash<QAction*,QString> actionMap;
1344 pt = ui->disabledBackendList->mapToGlobal(pt);
1346 QMenu ctxmenu;
1347 QAction *removeAction{ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove")};
1348 if(ui->disabledBackendList->selectedItems().size() == 0)
1349 removeAction->setEnabled(false);
1350 ctxmenu.addSeparator();
1351 for(size_t i = 0;backendList[i].backend_name[0];i++)
1353 QString backend{backendList[i].full_string};
1354 QAction *action{ctxmenu.addAction(QString("Add ")+backend)};
1355 actionMap[action] = backend;
1356 if(ui->disabledBackendList->findItems(backend, Qt::MatchFixedString).size() != 0 ||
1357 ui->enabledBackendList->findItems(backend, Qt::MatchFixedString).size() != 0)
1358 action->setEnabled(false);
1361 QAction *gotAction{ctxmenu.exec(pt)};
1362 if(gotAction == removeAction)
1364 QList<QListWidgetItem*> selected{ui->disabledBackendList->selectedItems()};
1365 foreach(QListWidgetItem *item, selected)
1366 delete item;
1367 enableApplyButton();
1369 else if(gotAction != nullptr)
1371 auto iter = actionMap.constFind(gotAction);
1372 if(iter != actionMap.cend())
1373 ui->disabledBackendList->addItem(iter.value());
1374 enableApplyButton();
1378 void MainWindow::selectOSSPlayback()
1380 QString current{ui->ossDefaultDeviceLine->text()};
1381 if(current.isEmpty()) current = ui->ossDefaultDeviceLine->placeholderText();
1382 QString fname{QFileDialog::getOpenFileName(this, tr("Select Playback Device"), current)};
1383 if(!fname.isEmpty())
1385 ui->ossDefaultDeviceLine->setText(fname);
1386 enableApplyButton();
1390 void MainWindow::selectOSSCapture()
1392 QString current{ui->ossDefaultCaptureLine->text()};
1393 if(current.isEmpty()) current = ui->ossDefaultCaptureLine->placeholderText();
1394 QString fname{QFileDialog::getOpenFileName(this, tr("Select Capture Device"), current)};
1395 if(!fname.isEmpty())
1397 ui->ossDefaultCaptureLine->setText(fname);
1398 enableApplyButton();
1402 void MainWindow::selectSolarisPlayback()
1404 QString current{ui->solarisDefaultDeviceLine->text()};
1405 if(current.isEmpty()) current = ui->solarisDefaultDeviceLine->placeholderText();
1406 QString fname{QFileDialog::getOpenFileName(this, tr("Select Playback Device"), current)};
1407 if(!fname.isEmpty())
1409 ui->solarisDefaultDeviceLine->setText(fname);
1410 enableApplyButton();
1414 void MainWindow::selectWaveOutput()
1416 QString fname{QFileDialog::getSaveFileName(this, tr("Select Wave File Output"),
1417 ui->waveOutputLine->text(), tr("Wave Files (*.wav *.amb);;All Files (*.*)"))};
1418 if(!fname.isEmpty())
1420 ui->waveOutputLine->setText(fname);
1421 enableApplyButton();