Check MAX_RESAMPLER_PADDING properly to ensure it's large enough
[openal-soft.git] / utils / alsoft-config / mainwindow.cpp
blobf4f5fd456e1cc24890b4438c0333bd5edf897ec6
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 namespace {
19 static const struct {
20 char backend_name[16];
21 char full_string[32];
22 } backendList[] = {
23 #ifdef HAVE_JACK
24 { "jack", "JACK" },
25 #endif
26 #ifdef HAVE_PULSEAUDIO
27 { "pulse", "PulseAudio" },
28 #endif
29 #ifdef HAVE_ALSA
30 { "alsa", "ALSA" },
31 #endif
32 #ifdef HAVE_COREAUDIO
33 { "core", "CoreAudio" },
34 #endif
35 #ifdef HAVE_OSS
36 { "oss", "OSS" },
37 #endif
38 #ifdef HAVE_SOLARIS
39 { "solaris", "Solaris" },
40 #endif
41 #ifdef HAVE_SNDIO
42 { "sndio", "SoundIO" },
43 #endif
44 #ifdef HAVE_QSA
45 { "qsa", "QSA" },
46 #endif
47 #ifdef HAVE_WASAPI
48 { "wasapi", "WASAPI" },
49 #endif
50 #ifdef HAVE_DSOUND
51 { "dsound", "DirectSound" },
52 #endif
53 #ifdef HAVE_WINMM
54 { "winmm", "Windows Multimedia" },
55 #endif
56 #ifdef HAVE_PORTAUDIO
57 { "port", "PortAudio" },
58 #endif
59 #ifdef HAVE_OPENSL
60 { "opensl", "OpenSL" },
61 #endif
63 { "null", "Null Output" },
64 #ifdef HAVE_WAVE
65 { "wave", "Wave Writer" },
66 #endif
67 { "", "" }
70 static const struct NameValuePair {
71 const char name[64];
72 const char value[16];
73 } speakerModeList[] = {
74 { "Autodetect", "" },
75 { "Mono", "mono" },
76 { "Stereo", "stereo" },
77 { "Quadraphonic", "quad" },
78 { "5.1 Surround (Side)", "surround51" },
79 { "5.1 Surround (Rear)", "surround51rear" },
80 { "6.1 Surround", "surround61" },
81 { "7.1 Surround", "surround71" },
83 { "Ambisonic, 1st Order", "ambi1" },
84 { "Ambisonic, 2nd Order", "ambi2" },
85 { "Ambisonic, 3rd Order", "ambi3" },
87 { "", "" }
88 }, sampleTypeList[] = {
89 { "Autodetect", "" },
90 { "8-bit int", "int8" },
91 { "8-bit uint", "uint8" },
92 { "16-bit int", "int16" },
93 { "16-bit uint", "uint16" },
94 { "32-bit int", "int32" },
95 { "32-bit uint", "uint32" },
96 { "32-bit float", "float32" },
98 { "", "" }
99 }, resamplerList[] = {
100 { "Point", "point" },
101 { "Linear", "linear" },
102 { "Default (Linear)", "" },
103 { "Cubic Spline", "cubic" },
104 { "11th order Sinc (fast)", "fast_bsinc12" },
105 { "11th order Sinc", "bsinc12" },
106 { "23rd order Sinc (fast)", "fast_bsinc24" },
107 { "23rd order Sinc", "bsinc24" },
109 { "", "" }
110 }, stereoModeList[] = {
111 { "Autodetect", "" },
112 { "Speakers", "speakers" },
113 { "Headphones", "headphones" },
115 { "", "" }
116 }, stereoEncList[] = {
117 { "Default", "" },
118 { "Pan Pot", "panpot" },
119 { "UHJ", "uhj" },
121 { "", "" }
122 }, ambiFormatList[] = {
123 { "Default", "" },
124 { "AmbiX (ACN, SN3D)", "ambix" },
125 { "ACN, N3D", "acn+n3d" },
126 { "Furse-Malham", "fuma" },
128 { "", "" }
129 }, hrtfModeList[] = {
130 { "1st Order Ambisonic", "ambi1" },
131 { "2nd Order Ambisonic", "ambi2" },
132 { "3rd Order Ambisonic", "ambi3" },
133 { "Default (Full)", "" },
134 { "Full", "full" },
136 { "", "" }
139 static QString getDefaultConfigName()
141 #ifdef Q_OS_WIN32
142 static const char fname[] = "alsoft.ini";
143 QByteArray base = qgetenv("AppData");
144 #else
145 static const char fname[] = "alsoft.conf";
146 QByteArray base = qgetenv("XDG_CONFIG_HOME");
147 if(base.isEmpty())
149 base = qgetenv("HOME");
150 if(base.isEmpty() == false)
151 base += "/.config";
153 #endif
154 if(base.isEmpty() == false)
155 return base +'/'+ fname;
156 return fname;
159 static QString getBaseDataPath()
161 #ifdef Q_OS_WIN32
162 QByteArray base = qgetenv("AppData");
163 #else
164 QByteArray base = qgetenv("XDG_DATA_HOME");
165 if(base.isEmpty())
167 base = qgetenv("HOME");
168 if(!base.isEmpty())
169 base += "/.local/share";
171 #endif
172 return base;
175 static QStringList getAllDataPaths(const QString &append)
177 QStringList list;
178 list.append(getBaseDataPath());
179 #ifdef Q_OS_WIN32
180 // TODO: Common AppData path
181 #else
182 QString paths = qgetenv("XDG_DATA_DIRS");
183 if(paths.isEmpty())
184 paths = "/usr/local/share/:/usr/share/";
185 list += paths.split(QChar(':'), QString::SkipEmptyParts);
186 #endif
187 QStringList::iterator iter = list.begin();
188 while(iter != list.end())
190 if(iter->isEmpty())
191 iter = list.erase(iter);
192 else
194 iter->append(append);
195 iter++;
198 return list;
201 template<size_t N>
202 static QString getValueFromName(const NameValuePair (&list)[N], const QString &str)
204 for(size_t i = 0;i < N-1;i++)
206 if(str == list[i].name)
207 return list[i].value;
209 return QString{};
212 template<size_t N>
213 static QString getNameFromValue(const NameValuePair (&list)[N], const QString &str)
215 for(size_t i = 0;i < N-1;i++)
217 if(str == list[i].value)
218 return list[i].name;
220 return QString{};
224 Qt::CheckState getCheckState(const QVariant &var)
226 if(var.isNull())
227 return Qt::PartiallyChecked;
228 if(var.toBool())
229 return Qt::Checked;
230 return Qt::Unchecked;
233 QString getCheckValue(const QCheckBox *checkbox)
235 const Qt::CheckState state{checkbox->checkState()};
236 if(state == Qt::Checked)
237 return QString{"true"};
238 if(state == Qt::Unchecked)
239 return QString{"false"};
240 return QString{};
245 MainWindow::MainWindow(QWidget *parent) :
246 QMainWindow(parent),
247 ui(new Ui::MainWindow),
248 mPeriodSizeValidator(nullptr),
249 mPeriodCountValidator(nullptr),
250 mSourceCountValidator(nullptr),
251 mEffectSlotValidator(nullptr),
252 mSourceSendValidator(nullptr),
253 mSampleRateValidator(nullptr),
254 mJackBufferValidator(nullptr),
255 mNeedsSave(false)
257 ui->setupUi(this);
259 for(int i = 0;speakerModeList[i].name[0];i++)
260 ui->channelConfigCombo->addItem(speakerModeList[i].name);
261 ui->channelConfigCombo->adjustSize();
262 for(int i = 0;sampleTypeList[i].name[0];i++)
263 ui->sampleFormatCombo->addItem(sampleTypeList[i].name);
264 ui->sampleFormatCombo->adjustSize();
265 for(int i = 0;stereoModeList[i].name[0];i++)
266 ui->stereoModeCombo->addItem(stereoModeList[i].name);
267 ui->stereoModeCombo->adjustSize();
268 for(int i = 0;stereoEncList[i].name[0];i++)
269 ui->stereoEncodingComboBox->addItem(stereoEncList[i].name);
270 ui->stereoEncodingComboBox->adjustSize();
271 for(int i = 0;ambiFormatList[i].name[0];i++)
272 ui->ambiFormatComboBox->addItem(ambiFormatList[i].name);
273 ui->ambiFormatComboBox->adjustSize();
275 int count;
276 for(count = 0;resamplerList[count].name[0];count++) {
278 ui->resamplerSlider->setRange(0, count-1);
280 for(count = 0;hrtfModeList[count].name[0];count++) {
282 ui->hrtfmodeSlider->setRange(0, count-1);
283 ui->hrtfStateComboBox->adjustSize();
285 #if !defined(HAVE_NEON) && !defined(HAVE_SSE)
286 ui->cpuExtDisabledLabel->move(ui->cpuExtDisabledLabel->x(), ui->cpuExtDisabledLabel->y() - 60);
287 #else
288 ui->cpuExtDisabledLabel->setVisible(false);
289 #endif
291 #ifndef HAVE_NEON
293 #ifndef HAVE_SSE4_1
294 #ifndef HAVE_SSE3
295 #ifndef HAVE_SSE2
296 #ifndef HAVE_SSE
297 ui->enableSSECheckBox->setVisible(false);
298 #endif /* !SSE */
299 ui->enableSSE2CheckBox->setVisible(false);
300 #endif /* !SSE2 */
301 ui->enableSSE3CheckBox->setVisible(false);
302 #endif /* !SSE3 */
303 ui->enableSSE41CheckBox->setVisible(false);
304 #endif /* !SSE4.1 */
305 ui->enableNeonCheckBox->setVisible(false);
307 #else /* !Neon */
309 #ifndef HAVE_SSE4_1
310 #ifndef HAVE_SSE3
311 #ifndef HAVE_SSE2
312 #ifndef HAVE_SSE
313 ui->enableNeonCheckBox->move(ui->enableNeonCheckBox->x(), ui->enableNeonCheckBox->y() - 30);
314 ui->enableSSECheckBox->setVisible(false);
315 #endif /* !SSE */
316 ui->enableSSE2CheckBox->setVisible(false);
317 #endif /* !SSE2 */
318 ui->enableSSE3CheckBox->setVisible(false);
319 #endif /* !SSE3 */
320 ui->enableSSE41CheckBox->setVisible(false);
321 #endif /* !SSE4.1 */
323 #endif
325 mPeriodSizeValidator = new QIntValidator{64, 8192, this};
326 ui->periodSizeEdit->setValidator(mPeriodSizeValidator);
327 mPeriodCountValidator = new QIntValidator{2, 16, this};
328 ui->periodCountEdit->setValidator(mPeriodCountValidator);
330 mSourceCountValidator = new QIntValidator{0, 4096, this};
331 ui->srcCountLineEdit->setValidator(mSourceCountValidator);
332 mEffectSlotValidator = new QIntValidator{0, 64, this};
333 ui->effectSlotLineEdit->setValidator(mEffectSlotValidator);
334 mSourceSendValidator = new QIntValidator{0, 16, this};
335 ui->srcSendLineEdit->setValidator(mSourceSendValidator);
336 mSampleRateValidator = new QIntValidator{8000, 192000, this};
337 ui->sampleRateCombo->lineEdit()->setValidator(mSampleRateValidator);
339 mJackBufferValidator = new QIntValidator{0, 8192, this};
340 ui->jackBufferSizeLine->setValidator(mJackBufferValidator);
342 connect(ui->actionLoad, &QAction::triggered, this, &MainWindow::loadConfigFromFile);
343 connect(ui->actionSave_As, &QAction::triggered, this, &MainWindow::saveConfigAsFile);
345 connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::showAboutPage);
347 connect(ui->closeCancelButton, &QPushButton::clicked, this, &MainWindow::cancelCloseAction);
348 connect(ui->applyButton, &QPushButton::clicked, this, &MainWindow::saveCurrentConfig);
350 auto qcb_cicstr = static_cast<void(QComboBox::*)(const QString&)>(&QComboBox::currentIndexChanged);
351 connect(ui->channelConfigCombo, qcb_cicstr, this, &MainWindow::enableApplyButton);
352 connect(ui->sampleFormatCombo, qcb_cicstr, this, &MainWindow::enableApplyButton);
353 connect(ui->stereoModeCombo, qcb_cicstr, this, &MainWindow::enableApplyButton);
354 connect(ui->sampleRateCombo, qcb_cicstr, this, &MainWindow::enableApplyButton);
355 connect(ui->sampleRateCombo, &QComboBox::editTextChanged, this, &MainWindow::enableApplyButton);
357 connect(ui->resamplerSlider, &QSlider::valueChanged, this, &MainWindow::updateResamplerLabel);
359 connect(ui->periodSizeSlider, &QSlider::valueChanged, this, &MainWindow::updatePeriodSizeEdit);
360 connect(ui->periodSizeEdit, &QLineEdit::editingFinished, this, &MainWindow::updatePeriodSizeSlider);
361 connect(ui->periodCountSlider, &QSlider::valueChanged, this, &MainWindow::updatePeriodCountEdit);
362 connect(ui->periodCountEdit, &QLineEdit::editingFinished, this, &MainWindow::updatePeriodCountSlider);
364 connect(ui->stereoEncodingComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
365 connect(ui->ambiFormatComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
366 connect(ui->outputLimiterCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
367 connect(ui->outputDitherCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
369 connect(ui->decoderHQModeCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
370 connect(ui->decoderDistCompCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
371 connect(ui->decoderNFEffectsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
372 auto qdsb_vcd = static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged);
373 connect(ui->decoderNFRefDelaySpinBox, qdsb_vcd, this, &MainWindow::enableApplyButton);
374 connect(ui->decoderQuadLineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
375 connect(ui->decoderQuadButton, &QPushButton::clicked, this, &MainWindow::selectQuadDecoderFile);
376 connect(ui->decoder51LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
377 connect(ui->decoder51Button, &QPushButton::clicked, this, &MainWindow::select51DecoderFile);
378 connect(ui->decoder61LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
379 connect(ui->decoder61Button, &QPushButton::clicked, this, &MainWindow::select61DecoderFile);
380 connect(ui->decoder71LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
381 connect(ui->decoder71Button, &QPushButton::clicked, this, &MainWindow::select71DecoderFile);
383 connect(ui->preferredHrtfComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
384 connect(ui->hrtfStateComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
385 connect(ui->hrtfmodeSlider, &QSlider::valueChanged, this, &MainWindow::updateHrtfModeLabel);
387 connect(ui->hrtfAddButton, &QPushButton::clicked, this, &MainWindow::addHrtfFile);
388 connect(ui->hrtfRemoveButton, &QPushButton::clicked, this, &MainWindow::removeHrtfFile);
389 connect(ui->hrtfFileList, &QListWidget::itemSelectionChanged, this, &MainWindow::updateHrtfRemoveButton);
390 connect(ui->defaultHrtfPathsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
392 connect(ui->srcCountLineEdit, &QLineEdit::editingFinished, this, &MainWindow::enableApplyButton);
393 connect(ui->srcSendLineEdit, &QLineEdit::editingFinished, this, &MainWindow::enableApplyButton);
394 connect(ui->effectSlotLineEdit, &QLineEdit::editingFinished, this, &MainWindow::enableApplyButton);
396 connect(ui->enableSSECheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
397 connect(ui->enableSSE2CheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
398 connect(ui->enableSSE3CheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
399 connect(ui->enableSSE41CheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
400 connect(ui->enableNeonCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
402 ui->enabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu);
403 connect(ui->enabledBackendList, &QListWidget::customContextMenuRequested, this, &MainWindow::showEnabledBackendMenu);
405 ui->disabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu);
406 connect(ui->disabledBackendList, &QListWidget::customContextMenuRequested, this, &MainWindow::showDisabledBackendMenu);
407 connect(ui->backendCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
409 connect(ui->defaultReverbComboBox, qcb_cicstr, this, &MainWindow::enableApplyButton);
410 connect(ui->enableEaxReverbCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
411 connect(ui->enableStdReverbCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
412 connect(ui->enableAutowahCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
413 connect(ui->enableChorusCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
414 connect(ui->enableCompressorCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
415 connect(ui->enableDistortionCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
416 connect(ui->enableEchoCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
417 connect(ui->enableEqualizerCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
418 connect(ui->enableFlangerCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
419 connect(ui->enableFrequencyShifterCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
420 connect(ui->enableModulatorCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
421 connect(ui->enableDedicatedCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
422 connect(ui->enablePitchShifterCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
423 connect(ui->enableVocalMorpherCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
425 connect(ui->pulseAutospawnCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
426 connect(ui->pulseAllowMovesCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
427 connect(ui->pulseFixRateCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
428 connect(ui->pulseAdjLatencyCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
430 connect(ui->jackAutospawnCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
431 connect(ui->jackBufferSizeSlider, &QSlider::valueChanged, this, &MainWindow::updateJackBufferSizeEdit);
432 connect(ui->jackBufferSizeLine, &QLineEdit::editingFinished, this, &MainWindow::updateJackBufferSizeSlider);
434 connect(ui->alsaDefaultDeviceLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
435 connect(ui->alsaDefaultCaptureLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
436 connect(ui->alsaResamplerCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
437 connect(ui->alsaMmapCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
439 connect(ui->ossDefaultDeviceLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
440 connect(ui->ossPlaybackPushButton, &QPushButton::clicked, this, &MainWindow::selectOSSPlayback);
441 connect(ui->ossDefaultCaptureLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
442 connect(ui->ossCapturePushButton, &QPushButton::clicked, this, &MainWindow::selectOSSCapture);
444 connect(ui->solarisDefaultDeviceLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
445 connect(ui->solarisPlaybackPushButton, &QPushButton::clicked, this, &MainWindow::selectSolarisPlayback);
447 connect(ui->waveOutputLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
448 connect(ui->waveOutputButton, &QPushButton::clicked, this, &MainWindow::selectWaveOutput);
449 connect(ui->waveBFormatCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
451 ui->backendListWidget->setCurrentRow(0);
452 ui->tabWidget->setCurrentIndex(0);
454 for(int i = 1;i < ui->backendListWidget->count();i++)
455 ui->backendListWidget->setRowHidden(i, true);
456 for(int i = 0;backendList[i].backend_name[0];i++)
458 QList<QListWidgetItem*> items = ui->backendListWidget->findItems(
459 backendList[i].full_string, Qt::MatchFixedString
461 foreach(const QListWidgetItem *item, items)
462 ui->backendListWidget->setItemHidden(item, false);
465 loadConfig(getDefaultConfigName());
468 MainWindow::~MainWindow()
470 delete ui;
471 delete mPeriodSizeValidator;
472 delete mPeriodCountValidator;
473 delete mSourceCountValidator;
474 delete mEffectSlotValidator;
475 delete mSourceSendValidator;
476 delete mSampleRateValidator;
477 delete mJackBufferValidator;
480 void MainWindow::closeEvent(QCloseEvent *event)
482 if(!mNeedsSave)
483 event->accept();
484 else
486 QMessageBox::StandardButton btn = QMessageBox::warning(this,
487 tr("Apply changes?"), tr("Save changes before quitting?"),
488 QMessageBox::Save | QMessageBox::No | QMessageBox::Cancel);
489 if(btn == QMessageBox::Save)
490 saveCurrentConfig();
491 if(btn == QMessageBox::Cancel)
492 event->ignore();
493 else
494 event->accept();
498 void MainWindow::cancelCloseAction()
500 mNeedsSave = false;
501 close();
505 void MainWindow::showAboutPage()
507 QMessageBox::information(this, tr("About"),
508 tr("OpenAL Soft Configuration Utility.\nBuilt for OpenAL Soft library version ") +
509 GetVersionString());
513 QStringList MainWindow::collectHrtfs()
515 QStringList ret;
516 QStringList processed;
518 for(int i = 0;i < ui->hrtfFileList->count();i++)
520 QDir dir(ui->hrtfFileList->item(i)->text());
521 QStringList fnames = dir.entryList(QDir::Files | QDir::Readable, QDir::Name);
522 foreach(const QString &fname, fnames)
524 if(!fname.endsWith(".mhr", Qt::CaseInsensitive))
525 continue;
526 QString fullname{dir.absoluteFilePath(fname)};
527 if(processed.contains(fullname))
528 continue;
529 processed.push_back(fullname);
531 QString name{fname.left(fname.length()-4)};
532 if(!ret.contains(name))
533 ret.push_back(name);
534 else
536 size_t i{2};
537 do {
538 QString s = name+" #"+QString::number(i);
539 if(!ret.contains(s))
541 ret.push_back(s);
542 break;
544 ++i;
545 } while(1);
550 if(ui->defaultHrtfPathsCheckBox->isChecked())
552 QStringList paths = getAllDataPaths("/openal/hrtf");
553 foreach(const QString &name, paths)
555 QDir dir{name};
556 QStringList fnames{dir.entryList(QDir::Files | QDir::Readable, QDir::Name)};
557 foreach(const QString &fname, fnames)
559 if(!fname.endsWith(".mhr", Qt::CaseInsensitive))
560 continue;
561 QString fullname{dir.absoluteFilePath(fname)};
562 if(processed.contains(fullname))
563 continue;
564 processed.push_back(fullname);
566 QString name{fname.left(fname.length()-4)};
567 if(!ret.contains(name))
568 ret.push_back(name);
569 else
571 size_t i{2};
572 do {
573 QString s{name+" #"+QString::number(i)};
574 if(!ret.contains(s))
576 ret.push_back(s);
577 break;
579 ++i;
580 } while(1);
585 #ifdef ALSOFT_EMBED_HRTF_DATA
586 ret.push_back("Built-In 44100hz");
587 ret.push_back("Built-In 48000hz");
588 #endif
590 return ret;
594 void MainWindow::loadConfigFromFile()
596 QString fname = QFileDialog::getOpenFileName(this, tr("Select Files"));
597 if(fname.isEmpty() == false)
598 loadConfig(fname);
601 void MainWindow::loadConfig(const QString &fname)
603 QSettings settings{fname, QSettings::IniFormat};
605 QString sampletype = settings.value("sample-type").toString();
606 ui->sampleFormatCombo->setCurrentIndex(0);
607 if(sampletype.isEmpty() == false)
609 QString str{getNameFromValue(sampleTypeList, sampletype)};
610 if(!str.isEmpty())
612 const int j{ui->sampleFormatCombo->findText(str)};
613 if(j > 0) ui->sampleFormatCombo->setCurrentIndex(j);
617 QString channelconfig{settings.value("channels").toString()};
618 ui->channelConfigCombo->setCurrentIndex(0);
619 if(channelconfig.isEmpty() == false)
621 QString str{getNameFromValue(speakerModeList, channelconfig)};
622 if(!str.isEmpty())
624 const int j{ui->channelConfigCombo->findText(str)};
625 if(j > 0) ui->channelConfigCombo->setCurrentIndex(j);
629 QString srate{settings.value("frequency").toString()};
630 if(srate.isEmpty())
631 ui->sampleRateCombo->setCurrentIndex(0);
632 else
634 ui->sampleRateCombo->lineEdit()->clear();
635 ui->sampleRateCombo->lineEdit()->insert(srate);
638 ui->srcCountLineEdit->clear();
639 ui->srcCountLineEdit->insert(settings.value("sources").toString());
640 ui->effectSlotLineEdit->clear();
641 ui->effectSlotLineEdit->insert(settings.value("slots").toString());
642 ui->srcSendLineEdit->clear();
643 ui->srcSendLineEdit->insert(settings.value("sends").toString());
645 QString resampler = settings.value("resampler").toString().trimmed();
646 ui->resamplerSlider->setValue(2);
647 ui->resamplerLabel->setText(resamplerList[2].name);
648 /* The "sinc4" and "sinc8" resamplers are no longer supported. Use "cubic"
649 * as a fallback.
651 if(resampler == "sinc4" || resampler == "sinc8")
652 resampler = "cubic";
653 /* The "bsinc" resampler name is an alias for "bsinc12". */
654 else if(resampler == "bsinc")
655 resampler = "bsinc12";
656 for(int i = 0;resamplerList[i].name[0];i++)
658 if(resampler == resamplerList[i].value)
660 ui->resamplerSlider->setValue(i);
661 ui->resamplerLabel->setText(resamplerList[i].name);
662 break;
666 QString stereomode = settings.value("stereo-mode").toString().trimmed();
667 ui->stereoModeCombo->setCurrentIndex(0);
668 if(stereomode.isEmpty() == false)
670 QString str{getNameFromValue(stereoModeList, stereomode)};
671 if(!str.isEmpty())
673 const int j{ui->stereoModeCombo->findText(str)};
674 if(j > 0) ui->stereoModeCombo->setCurrentIndex(j);
678 int periodsize{settings.value("period_size").toInt()};
679 ui->periodSizeEdit->clear();
680 if(periodsize >= 64)
682 ui->periodSizeEdit->insert(QString::number(periodsize));
683 updatePeriodSizeSlider();
686 int periodcount{settings.value("periods").toInt()};
687 ui->periodCountEdit->clear();
688 if(periodcount >= 2)
690 ui->periodCountEdit->insert(QString::number(periodcount));
691 updatePeriodCountSlider();
694 ui->outputLimiterCheckBox->setCheckState(getCheckState(settings.value("output-limiter")));
695 ui->outputDitherCheckBox->setCheckState(getCheckState(settings.value("dither")));
697 QString stereopan{settings.value("stereo-encoding").toString()};
698 ui->stereoEncodingComboBox->setCurrentIndex(0);
699 if(stereopan.isEmpty() == false)
701 QString str{getNameFromValue(stereoEncList, stereopan)};
702 if(!str.isEmpty())
704 const int j{ui->stereoEncodingComboBox->findText(str)};
705 if(j > 0) ui->stereoEncodingComboBox->setCurrentIndex(j);
709 QString ambiformat{settings.value("ambi-format").toString()};
710 ui->ambiFormatComboBox->setCurrentIndex(0);
711 if(ambiformat.isEmpty() == false)
713 QString str{getNameFromValue(ambiFormatList, ambiformat)};
714 if(!str.isEmpty())
716 const int j{ui->ambiFormatComboBox->findText(str)};
717 if(j > 0) ui->ambiFormatComboBox->setCurrentIndex(j);
721 bool hqmode{settings.value("decoder/hq-mode", true).toBool()};
722 ui->decoderHQModeCheckBox->setChecked(hqmode);
723 ui->decoderDistCompCheckBox->setCheckState(getCheckState(settings.value("decoder/distance-comp")));
724 ui->decoderNFEffectsCheckBox->setCheckState(getCheckState(settings.value("decoder/nfc")));
725 double refdelay{settings.value("decoder/nfc-ref-delay", 0.0).toDouble()};
726 ui->decoderNFRefDelaySpinBox->setValue(refdelay);
728 ui->decoderQuadLineEdit->setText(settings.value("decoder/quad").toString());
729 ui->decoder51LineEdit->setText(settings.value("decoder/surround51").toString());
730 ui->decoder61LineEdit->setText(settings.value("decoder/surround61").toString());
731 ui->decoder71LineEdit->setText(settings.value("decoder/surround71").toString());
733 QStringList disabledCpuExts{settings.value("disable-cpu-exts").toStringList()};
734 if(disabledCpuExts.size() == 1)
735 disabledCpuExts = disabledCpuExts[0].split(QChar(','));
736 for(QString &name : disabledCpuExts)
737 name = name.trimmed();
738 ui->enableSSECheckBox->setChecked(!disabledCpuExts.contains("sse", Qt::CaseInsensitive));
739 ui->enableSSE2CheckBox->setChecked(!disabledCpuExts.contains("sse2", Qt::CaseInsensitive));
740 ui->enableSSE3CheckBox->setChecked(!disabledCpuExts.contains("sse3", Qt::CaseInsensitive));
741 ui->enableSSE41CheckBox->setChecked(!disabledCpuExts.contains("sse4.1", Qt::CaseInsensitive));
742 ui->enableNeonCheckBox->setChecked(!disabledCpuExts.contains("neon", Qt::CaseInsensitive));
744 QString hrtfmode{settings.value("hrtf-mode").toString().trimmed()};
745 ui->hrtfmodeSlider->setValue(3);
746 ui->hrtfmodeLabel->setText(hrtfModeList[3].name);
747 /* The "basic" mode name is no longer supported. Use "ambi2" instead. */
748 if(hrtfmode == "basic") hrtfmode = "ambi2";
749 for(int i = 0;hrtfModeList[i].name[0];i++)
751 if(hrtfmode == hrtfModeList[i].value)
753 ui->hrtfmodeSlider->setValue(i);
754 ui->hrtfmodeLabel->setText(hrtfModeList[i].name);
755 break;
759 QStringList hrtf_paths{settings.value("hrtf-paths").toStringList()};
760 if(hrtf_paths.size() == 1)
761 hrtf_paths = hrtf_paths[0].split(QChar(','));
762 for(QString &name : hrtf_paths)
763 name = name.trimmed();
764 if(!hrtf_paths.empty() && !hrtf_paths.back().isEmpty())
765 ui->defaultHrtfPathsCheckBox->setCheckState(Qt::Unchecked);
766 else
768 hrtf_paths.removeAll(QString());
769 ui->defaultHrtfPathsCheckBox->setCheckState(Qt::Checked);
771 hrtf_paths.removeDuplicates();
772 ui->hrtfFileList->clear();
773 ui->hrtfFileList->addItems(hrtf_paths);
774 updateHrtfRemoveButton();
776 QString hrtfstate{settings.value("hrtf").toString().toLower()};
777 if(hrtfstate == "true")
778 ui->hrtfStateComboBox->setCurrentIndex(1);
779 else if(hrtfstate == "false")
780 ui->hrtfStateComboBox->setCurrentIndex(2);
781 else
782 ui->hrtfStateComboBox->setCurrentIndex(0);
784 ui->preferredHrtfComboBox->clear();
785 ui->preferredHrtfComboBox->addItem("- Any -");
786 if(ui->defaultHrtfPathsCheckBox->isChecked())
788 QStringList hrtfs{collectHrtfs()};
789 foreach(const QString &name, hrtfs)
790 ui->preferredHrtfComboBox->addItem(name);
793 QString defaulthrtf{settings.value("default-hrtf").toString()};
794 ui->preferredHrtfComboBox->setCurrentIndex(0);
795 if(defaulthrtf.isEmpty() == false)
797 int i{ui->preferredHrtfComboBox->findText(defaulthrtf)};
798 if(i > 0)
799 ui->preferredHrtfComboBox->setCurrentIndex(i);
800 else
802 i = ui->preferredHrtfComboBox->count();
803 ui->preferredHrtfComboBox->addItem(defaulthrtf);
804 ui->preferredHrtfComboBox->setCurrentIndex(i);
807 ui->preferredHrtfComboBox->adjustSize();
809 ui->enabledBackendList->clear();
810 ui->disabledBackendList->clear();
811 QStringList drivers{settings.value("drivers").toStringList()};
812 if(drivers.size() == 0)
813 ui->backendCheckBox->setChecked(true);
814 else
816 if(drivers.size() == 1)
817 drivers = drivers[0].split(QChar(','));
818 for(QString &name : drivers)
820 name = name.trimmed();
821 /* Convert "mmdevapi" references to "wasapi" for backwards
822 * compatibility.
824 if(name == "-mmdevapi")
825 name = "-wasapi";
826 else if(name == "mmdevapi")
827 name = "wasapi";
830 bool lastWasEmpty = false;
831 foreach(const QString &backend, drivers)
833 lastWasEmpty = backend.isEmpty();
834 if(lastWasEmpty) continue;
836 if(!backend.startsWith(QChar('-')))
837 for(int j = 0;backendList[j].backend_name[0];j++)
839 if(backend == backendList[j].backend_name)
841 ui->enabledBackendList->addItem(backendList[j].full_string);
842 break;
845 else if(backend.size() > 1)
847 QStringRef backendref{backend.rightRef(backend.size()-1)};
848 for(int j = 0;backendList[j].backend_name[0];j++)
850 if(backendref == backendList[j].backend_name)
852 ui->disabledBackendList->addItem(backendList[j].full_string);
853 break;
858 ui->backendCheckBox->setChecked(lastWasEmpty);
861 QString defaultreverb{settings.value("default-reverb").toString().toLower()};
862 ui->defaultReverbComboBox->setCurrentIndex(0);
863 if(defaultreverb.isEmpty() == false)
865 for(int i = 0;i < ui->defaultReverbComboBox->count();i++)
867 if(defaultreverb.compare(ui->defaultReverbComboBox->itemText(i).toLower()) == 0)
869 ui->defaultReverbComboBox->setCurrentIndex(i);
870 break;
875 QStringList excludefx{settings.value("excludefx").toStringList()};
876 if(excludefx.size() == 1)
877 excludefx = excludefx[0].split(QChar(','));
878 for(QString &name : excludefx)
879 name = name.trimmed();
880 ui->enableEaxReverbCheck->setChecked(!excludefx.contains("eaxreverb", Qt::CaseInsensitive));
881 ui->enableStdReverbCheck->setChecked(!excludefx.contains("reverb", Qt::CaseInsensitive));
882 ui->enableAutowahCheck->setChecked(!excludefx.contains("autowah", Qt::CaseInsensitive));
883 ui->enableChorusCheck->setChecked(!excludefx.contains("chorus", Qt::CaseInsensitive));
884 ui->enableCompressorCheck->setChecked(!excludefx.contains("compressor", Qt::CaseInsensitive));
885 ui->enableDistortionCheck->setChecked(!excludefx.contains("distortion", Qt::CaseInsensitive));
886 ui->enableEchoCheck->setChecked(!excludefx.contains("echo", Qt::CaseInsensitive));
887 ui->enableEqualizerCheck->setChecked(!excludefx.contains("equalizer", Qt::CaseInsensitive));
888 ui->enableFlangerCheck->setChecked(!excludefx.contains("flanger", Qt::CaseInsensitive));
889 ui->enableFrequencyShifterCheck->setChecked(!excludefx.contains("fshifter", Qt::CaseInsensitive));
890 ui->enableModulatorCheck->setChecked(!excludefx.contains("modulator", Qt::CaseInsensitive));
891 ui->enableDedicatedCheck->setChecked(!excludefx.contains("dedicated", Qt::CaseInsensitive));
892 ui->enablePitchShifterCheck->setChecked(!excludefx.contains("pshifter", Qt::CaseInsensitive));
893 ui->enableVocalMorpherCheck->setChecked(!excludefx.contains("vmorpher", Qt::CaseInsensitive));
895 ui->pulseAutospawnCheckBox->setCheckState(getCheckState(settings.value("pulse/spawn-server")));
896 ui->pulseAllowMovesCheckBox->setCheckState(getCheckState(settings.value("pulse/allow-moves")));
897 ui->pulseFixRateCheckBox->setCheckState(getCheckState(settings.value("pulse/fix-rate")));
898 ui->pulseAdjLatencyCheckBox->setCheckState(getCheckState(settings.value("pulse/adjust-latency")));
900 ui->jackAutospawnCheckBox->setCheckState(getCheckState(settings.value("jack/spawn-server")));
901 ui->jackBufferSizeLine->setText(settings.value("jack/buffer-size", QString()).toString());
902 updateJackBufferSizeSlider();
904 ui->alsaDefaultDeviceLine->setText(settings.value("alsa/device", QString()).toString());
905 ui->alsaDefaultCaptureLine->setText(settings.value("alsa/capture", QString()).toString());
906 ui->alsaResamplerCheckBox->setCheckState(getCheckState(settings.value("alsa/allow-resampler")));
907 ui->alsaMmapCheckBox->setCheckState(getCheckState(settings.value("alsa/mmap")));
909 ui->ossDefaultDeviceLine->setText(settings.value("oss/device", QString()).toString());
910 ui->ossDefaultCaptureLine->setText(settings.value("oss/capture", QString()).toString());
912 ui->solarisDefaultDeviceLine->setText(settings.value("solaris/device", QString()).toString());
914 ui->waveOutputLine->setText(settings.value("wave/file", QString()).toString());
915 ui->waveBFormatCheckBox->setChecked(settings.value("wave/bformat", false).toBool());
917 ui->applyButton->setEnabled(false);
918 ui->closeCancelButton->setText(tr("Close"));
919 mNeedsSave = false;
922 void MainWindow::saveCurrentConfig()
924 saveConfig(getDefaultConfigName());
925 ui->applyButton->setEnabled(false);
926 ui->closeCancelButton->setText(tr("Close"));
927 mNeedsSave = false;
928 QMessageBox::information(this, tr("Information"),
929 tr("Applications using OpenAL need to be restarted for changes to take effect."));
932 void MainWindow::saveConfigAsFile()
934 QString fname{QFileDialog::getOpenFileName(this, tr("Select Files"))};
935 if(fname.isEmpty() == false)
937 saveConfig(fname);
938 ui->applyButton->setEnabled(false);
939 mNeedsSave = false;
943 void MainWindow::saveConfig(const QString &fname) const
945 QSettings settings{fname, QSettings::IniFormat};
947 /* HACK: Compound any stringlist values into a comma-separated string. */
948 QStringList allkeys{settings.allKeys()};
949 foreach(const QString &key, allkeys)
951 QStringList vals{settings.value(key).toStringList()};
952 if(vals.size() > 1)
953 settings.setValue(key, vals.join(QChar(',')));
956 settings.setValue("sample-type", getValueFromName(sampleTypeList, ui->sampleFormatCombo->currentText()));
957 settings.setValue("channels", getValueFromName(speakerModeList, ui->channelConfigCombo->currentText()));
959 uint rate{ui->sampleRateCombo->currentText().toUInt()};
960 if(rate <= 0)
961 settings.setValue("frequency", QString{});
962 else
963 settings.setValue("frequency", rate);
965 settings.setValue("period_size", ui->periodSizeEdit->text());
966 settings.setValue("periods", ui->periodCountEdit->text());
968 settings.setValue("sources", ui->srcCountLineEdit->text());
969 settings.setValue("slots", ui->effectSlotLineEdit->text());
971 settings.setValue("resampler", resamplerList[ui->resamplerSlider->value()].value);
973 settings.setValue("stereo-mode", getValueFromName(stereoModeList, ui->stereoModeCombo->currentText()));
974 settings.setValue("stereo-encoding", getValueFromName(stereoEncList, ui->stereoEncodingComboBox->currentText()));
975 settings.setValue("ambi-format", getValueFromName(ambiFormatList, ui->ambiFormatComboBox->currentText()));
977 settings.setValue("output-limiter", getCheckValue(ui->outputLimiterCheckBox));
978 settings.setValue("dither", getCheckValue(ui->outputDitherCheckBox));
980 settings.setValue("decoder/hq-mode",
981 ui->decoderHQModeCheckBox->isChecked() ? QString{/*"true"*/} : QString{"false"}
983 settings.setValue("decoder/distance-comp", getCheckValue(ui->decoderDistCompCheckBox));
984 settings.setValue("decoder/nfc", getCheckValue(ui->decoderNFEffectsCheckBox));
985 double refdelay = ui->decoderNFRefDelaySpinBox->value();
986 settings.setValue("decoder/nfc-ref-delay",
987 (refdelay > 0.0) ? QString::number(refdelay) : QString{}
990 settings.setValue("decoder/quad", ui->decoderQuadLineEdit->text());
991 settings.setValue("decoder/surround51", ui->decoder51LineEdit->text());
992 settings.setValue("decoder/surround61", ui->decoder61LineEdit->text());
993 settings.setValue("decoder/surround71", ui->decoder71LineEdit->text());
995 QStringList strlist;
996 if(!ui->enableSSECheckBox->isChecked())
997 strlist.append("sse");
998 if(!ui->enableSSE2CheckBox->isChecked())
999 strlist.append("sse2");
1000 if(!ui->enableSSE3CheckBox->isChecked())
1001 strlist.append("sse3");
1002 if(!ui->enableSSE41CheckBox->isChecked())
1003 strlist.append("sse4.1");
1004 if(!ui->enableNeonCheckBox->isChecked())
1005 strlist.append("neon");
1006 settings.setValue("disable-cpu-exts", strlist.join(QChar(',')));
1008 settings.setValue("hrtf-mode", hrtfModeList[ui->hrtfmodeSlider->value()].value);
1010 if(ui->hrtfStateComboBox->currentIndex() == 1)
1011 settings.setValue("hrtf", "true");
1012 else if(ui->hrtfStateComboBox->currentIndex() == 2)
1013 settings.setValue("hrtf", "false");
1014 else
1015 settings.setValue("hrtf", QString{});
1017 if(ui->preferredHrtfComboBox->currentIndex() == 0)
1018 settings.setValue("default-hrtf", QString{});
1019 else
1021 QString str{ui->preferredHrtfComboBox->currentText()};
1022 settings.setValue("default-hrtf", str);
1025 strlist.clear();
1026 strlist.reserve(ui->hrtfFileList->count());
1027 for(int i = 0;i < ui->hrtfFileList->count();i++)
1028 strlist.append(ui->hrtfFileList->item(i)->text());
1029 if(!strlist.empty() && ui->defaultHrtfPathsCheckBox->isChecked())
1030 strlist.append(QString{});
1031 settings.setValue("hrtf-paths", strlist.join(QChar{','}));
1033 strlist.clear();
1034 for(int i = 0;i < ui->enabledBackendList->count();i++)
1036 QString label{ui->enabledBackendList->item(i)->text()};
1037 for(int j = 0;backendList[j].backend_name[0];j++)
1039 if(label == backendList[j].full_string)
1041 strlist.append(backendList[j].backend_name);
1042 break;
1046 for(int i = 0;i < ui->disabledBackendList->count();i++)
1048 QString label{ui->disabledBackendList->item(i)->text()};
1049 for(int j = 0;backendList[j].backend_name[0];j++)
1051 if(label == backendList[j].full_string)
1053 strlist.append(QChar{'-'}+QString{backendList[j].backend_name});
1054 break;
1058 if(strlist.size() == 0 && !ui->backendCheckBox->isChecked())
1059 strlist.append("-all");
1060 else if(ui->backendCheckBox->isChecked())
1061 strlist.append(QString{});
1062 settings.setValue("drivers", strlist.join(QChar(',')));
1064 // TODO: Remove check when we can properly match global values.
1065 if(ui->defaultReverbComboBox->currentIndex() == 0)
1066 settings.setValue("default-reverb", QString{});
1067 else
1069 QString str{ui->defaultReverbComboBox->currentText().toLower()};
1070 settings.setValue("default-reverb", str);
1073 strlist.clear();
1074 if(!ui->enableEaxReverbCheck->isChecked())
1075 strlist.append("eaxreverb");
1076 if(!ui->enableStdReverbCheck->isChecked())
1077 strlist.append("reverb");
1078 if(!ui->enableAutowahCheck->isChecked())
1079 strlist.append("autowah");
1080 if(!ui->enableChorusCheck->isChecked())
1081 strlist.append("chorus");
1082 if(!ui->enableDistortionCheck->isChecked())
1083 strlist.append("distortion");
1084 if(!ui->enableCompressorCheck->isChecked())
1085 strlist.append("compressor");
1086 if(!ui->enableEchoCheck->isChecked())
1087 strlist.append("echo");
1088 if(!ui->enableEqualizerCheck->isChecked())
1089 strlist.append("equalizer");
1090 if(!ui->enableFlangerCheck->isChecked())
1091 strlist.append("flanger");
1092 if(!ui->enableFrequencyShifterCheck->isChecked())
1093 strlist.append("fshifter");
1094 if(!ui->enableModulatorCheck->isChecked())
1095 strlist.append("modulator");
1096 if(!ui->enableDedicatedCheck->isChecked())
1097 strlist.append("dedicated");
1098 if(!ui->enablePitchShifterCheck->isChecked())
1099 strlist.append("pshifter");
1100 if(!ui->enableVocalMorpherCheck->isChecked())
1101 strlist.append("vmorpher");
1102 settings.setValue("excludefx", strlist.join(QChar{','}));
1104 settings.setValue("pulse/spawn-server", getCheckValue(ui->pulseAutospawnCheckBox));
1105 settings.setValue("pulse/allow-moves", getCheckValue(ui->pulseAllowMovesCheckBox));
1106 settings.setValue("pulse/fix-rate", getCheckValue(ui->pulseFixRateCheckBox));
1107 settings.setValue("pulse/adjust-latency", getCheckValue(ui->pulseAdjLatencyCheckBox));
1109 settings.setValue("jack/spawn-server", getCheckValue(ui->jackAutospawnCheckBox));
1110 settings.setValue("jack/buffer-size", ui->jackBufferSizeLine->text());
1112 settings.setValue("alsa/device", ui->alsaDefaultDeviceLine->text());
1113 settings.setValue("alsa/capture", ui->alsaDefaultCaptureLine->text());
1114 settings.setValue("alsa/allow-resampler", getCheckValue(ui->alsaResamplerCheckBox));
1115 settings.setValue("alsa/mmap", getCheckValue(ui->alsaMmapCheckBox));
1117 settings.setValue("oss/device", ui->ossDefaultDeviceLine->text());
1118 settings.setValue("oss/capture", ui->ossDefaultCaptureLine->text());
1120 settings.setValue("solaris/device", ui->solarisDefaultDeviceLine->text());
1122 settings.setValue("wave/file", ui->waveOutputLine->text());
1123 settings.setValue("wave/bformat",
1124 ui->waveBFormatCheckBox->isChecked() ? QString{"true"} : QString{/*"false"*/}
1127 /* Remove empty keys
1128 * FIXME: Should only remove keys whose value matches the globally-specified value.
1130 allkeys = settings.allKeys();
1131 foreach(const QString &key, allkeys)
1133 QString str{settings.value(key).toString()};
1134 if(str == QString{})
1135 settings.remove(key);
1140 void MainWindow::enableApplyButton()
1142 if(!mNeedsSave)
1143 ui->applyButton->setEnabled(true);
1144 mNeedsSave = true;
1145 ui->closeCancelButton->setText(tr("Cancel"));
1149 void MainWindow::updateResamplerLabel(int num)
1151 ui->resamplerLabel->setText(resamplerList[num].name);
1152 enableApplyButton();
1156 void MainWindow::updatePeriodSizeEdit(int size)
1158 ui->periodSizeEdit->clear();
1159 if(size >= 64)
1160 ui->periodSizeEdit->insert(QString::number(size));
1161 enableApplyButton();
1164 void MainWindow::updatePeriodSizeSlider()
1166 int pos = ui->periodSizeEdit->text().toInt();
1167 if(pos >= 64)
1169 if(pos > 8192)
1170 pos = 8192;
1171 ui->periodSizeSlider->setSliderPosition(pos);
1173 enableApplyButton();
1176 void MainWindow::updatePeriodCountEdit(int count)
1178 ui->periodCountEdit->clear();
1179 if(count >= 2)
1180 ui->periodCountEdit->insert(QString::number(count));
1181 enableApplyButton();
1184 void MainWindow::updatePeriodCountSlider()
1186 int pos = ui->periodCountEdit->text().toInt();
1187 if(pos < 2)
1188 pos = 0;
1189 else if(pos > 16)
1190 pos = 16;
1191 ui->periodCountSlider->setSliderPosition(pos);
1192 enableApplyButton();
1196 void MainWindow::selectQuadDecoderFile()
1197 { selectDecoderFile(ui->decoderQuadLineEdit, "Select Quadraphonic Decoder");}
1198 void MainWindow::select51DecoderFile()
1199 { selectDecoderFile(ui->decoder51LineEdit, "Select 5.1 Surround Decoder");}
1200 void MainWindow::select61DecoderFile()
1201 { selectDecoderFile(ui->decoder61LineEdit, "Select 6.1 Surround Decoder");}
1202 void MainWindow::select71DecoderFile()
1203 { selectDecoderFile(ui->decoder71LineEdit, "Select 7.1 Surround Decoder");}
1204 void MainWindow::selectDecoderFile(QLineEdit *line, const char *caption)
1206 QString dir{line->text()};
1207 if(dir.isEmpty() || QDir::isRelativePath(dir))
1209 QStringList paths{getAllDataPaths("/openal/presets")};
1210 while(!paths.isEmpty())
1212 if(QDir{paths.last()}.exists())
1214 dir = paths.last();
1215 break;
1217 paths.removeLast();
1220 QString fname{QFileDialog::getOpenFileName(this, tr(caption),
1221 dir, tr("AmbDec Files (*.ambdec);;All Files (*.*)"))};
1222 if(!fname.isEmpty())
1224 line->setText(fname);
1225 enableApplyButton();
1230 void MainWindow::updateJackBufferSizeEdit(int size)
1232 ui->jackBufferSizeLine->clear();
1233 if(size > 0)
1234 ui->jackBufferSizeLine->insert(QString::number(1<<size));
1235 enableApplyButton();
1238 void MainWindow::updateJackBufferSizeSlider()
1240 int value{ui->jackBufferSizeLine->text().toInt()};
1241 auto pos = static_cast<int>(floor(log2(value) + 0.5));
1242 ui->jackBufferSizeSlider->setSliderPosition(pos);
1243 enableApplyButton();
1247 void MainWindow::updateHrtfModeLabel(int num)
1249 ui->hrtfmodeLabel->setText(hrtfModeList[num].name);
1250 enableApplyButton();
1254 void MainWindow::addHrtfFile()
1256 QString path{QFileDialog::getExistingDirectory(this, tr("Select HRTF Path"))};
1257 if(path.isEmpty() == false && !getAllDataPaths("/openal/hrtf").contains(path))
1259 ui->hrtfFileList->addItem(path);
1260 enableApplyButton();
1264 void MainWindow::removeHrtfFile()
1266 QList<QListWidgetItem*> selected{ui->hrtfFileList->selectedItems()};
1267 if(!selected.isEmpty())
1269 foreach(QListWidgetItem *item, selected)
1270 delete item;
1271 enableApplyButton();
1275 void MainWindow::updateHrtfRemoveButton()
1277 ui->hrtfRemoveButton->setEnabled(ui->hrtfFileList->selectedItems().size() != 0);
1280 void MainWindow::showEnabledBackendMenu(QPoint pt)
1282 QHash<QAction*,QString> actionMap;
1284 pt = ui->enabledBackendList->mapToGlobal(pt);
1286 QMenu ctxmenu;
1287 QAction *removeAction{ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove")};
1288 if(ui->enabledBackendList->selectedItems().size() == 0)
1289 removeAction->setEnabled(false);
1290 ctxmenu.addSeparator();
1291 for(size_t i = 0;backendList[i].backend_name[0];i++)
1293 QString backend{backendList[i].full_string};
1294 QAction *action{ctxmenu.addAction(QString("Add ")+backend)};
1295 actionMap[action] = backend;
1296 if(ui->enabledBackendList->findItems(backend, Qt::MatchFixedString).size() != 0 ||
1297 ui->disabledBackendList->findItems(backend, Qt::MatchFixedString).size() != 0)
1298 action->setEnabled(false);
1301 QAction *gotAction{ctxmenu.exec(pt)};
1302 if(gotAction == removeAction)
1304 QList<QListWidgetItem*> selected{ui->enabledBackendList->selectedItems()};
1305 foreach(QListWidgetItem *item, selected)
1306 delete item;
1307 enableApplyButton();
1309 else if(gotAction != nullptr)
1311 auto iter = actionMap.constFind(gotAction);
1312 if(iter != actionMap.cend())
1313 ui->enabledBackendList->addItem(iter.value());
1314 enableApplyButton();
1318 void MainWindow::showDisabledBackendMenu(QPoint pt)
1320 QHash<QAction*,QString> actionMap;
1322 pt = ui->disabledBackendList->mapToGlobal(pt);
1324 QMenu ctxmenu;
1325 QAction *removeAction{ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove")};
1326 if(ui->disabledBackendList->selectedItems().size() == 0)
1327 removeAction->setEnabled(false);
1328 ctxmenu.addSeparator();
1329 for(size_t i = 0;backendList[i].backend_name[0];i++)
1331 QString backend{backendList[i].full_string};
1332 QAction *action{ctxmenu.addAction(QString("Add ")+backend)};
1333 actionMap[action] = backend;
1334 if(ui->disabledBackendList->findItems(backend, Qt::MatchFixedString).size() != 0 ||
1335 ui->enabledBackendList->findItems(backend, Qt::MatchFixedString).size() != 0)
1336 action->setEnabled(false);
1339 QAction *gotAction{ctxmenu.exec(pt)};
1340 if(gotAction == removeAction)
1342 QList<QListWidgetItem*> selected{ui->disabledBackendList->selectedItems()};
1343 foreach(QListWidgetItem *item, selected)
1344 delete item;
1345 enableApplyButton();
1347 else if(gotAction != nullptr)
1349 auto iter = actionMap.constFind(gotAction);
1350 if(iter != actionMap.cend())
1351 ui->disabledBackendList->addItem(iter.value());
1352 enableApplyButton();
1356 void MainWindow::selectOSSPlayback()
1358 QString current{ui->ossDefaultDeviceLine->text()};
1359 if(current.isEmpty()) current = ui->ossDefaultDeviceLine->placeholderText();
1360 QString fname{QFileDialog::getOpenFileName(this, tr("Select Playback Device"), current)};
1361 if(!fname.isEmpty())
1363 ui->ossDefaultDeviceLine->setText(fname);
1364 enableApplyButton();
1368 void MainWindow::selectOSSCapture()
1370 QString current{ui->ossDefaultCaptureLine->text()};
1371 if(current.isEmpty()) current = ui->ossDefaultCaptureLine->placeholderText();
1372 QString fname{QFileDialog::getOpenFileName(this, tr("Select Capture Device"), current)};
1373 if(!fname.isEmpty())
1375 ui->ossDefaultCaptureLine->setText(fname);
1376 enableApplyButton();
1380 void MainWindow::selectSolarisPlayback()
1382 QString current{ui->solarisDefaultDeviceLine->text()};
1383 if(current.isEmpty()) current = ui->solarisDefaultDeviceLine->placeholderText();
1384 QString fname{QFileDialog::getOpenFileName(this, tr("Select Playback Device"), current)};
1385 if(!fname.isEmpty())
1387 ui->solarisDefaultDeviceLine->setText(fname);
1388 enableApplyButton();
1392 void MainWindow::selectWaveOutput()
1394 QString fname{QFileDialog::getSaveFileName(this, tr("Select Wave File Output"),
1395 ui->waveOutputLine->text(), tr("Wave Files (*.wav *.amb);;All Files (*.*)"))};
1396 if(!fname.isEmpty())
1398 ui->waveOutputLine->setText(fname);
1399 enableApplyButton();