mainwindow.cpp 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427
  1. #include "config.h"
  2. #include "mainwindow.h"
  3. #include <array>
  4. #include <cmath>
  5. #include <iostream>
  6. #include <QFileDialog>
  7. #include <QMessageBox>
  8. #include <QCloseEvent>
  9. #include <QSettings>
  10. #include <QtGlobal>
  11. #include "ui_mainwindow.h"
  12. #include "verstr.h"
  13. #ifdef _WIN32
  14. #include <windows.h>
  15. #include <shlobj.h>
  16. #endif
  17. #include "almalloc.h"
  18. #include "alspan.h"
  19. namespace {
  20. struct BackendNamePair {
  21. /* NOLINTBEGIN(*-avoid-c-arrays) */
  22. char backend_name[16];
  23. char full_string[32];
  24. /* NOLINTEND(*-avoid-c-arrays) */
  25. };
  26. constexpr std::array backendList{
  27. #ifdef HAVE_PIPEWIRE
  28. BackendNamePair{ "pipewire", "PipeWire" },
  29. #endif
  30. #ifdef HAVE_PULSEAUDIO
  31. BackendNamePair{ "pulse", "PulseAudio" },
  32. #endif
  33. #ifdef HAVE_ALSA
  34. BackendNamePair{ "alsa", "ALSA" },
  35. #endif
  36. #ifdef HAVE_JACK
  37. BackendNamePair{ "jack", "JACK" },
  38. #endif
  39. #ifdef HAVE_COREAUDIO
  40. BackendNamePair{ "core", "CoreAudio" },
  41. #endif
  42. #ifdef HAVE_OSS
  43. BackendNamePair{ "oss", "OSS" },
  44. #endif
  45. #ifdef HAVE_SOLARIS
  46. BackendNamePair{ "solaris", "Solaris" },
  47. #endif
  48. #ifdef HAVE_SNDIO
  49. BackendNamePair{ "sndio", "SndIO" },
  50. #endif
  51. #ifdef HAVE_WASAPI
  52. BackendNamePair{ "wasapi", "WASAPI" },
  53. #endif
  54. #ifdef HAVE_DSOUND
  55. BackendNamePair{ "dsound", "DirectSound" },
  56. #endif
  57. #ifdef HAVE_WINMM
  58. BackendNamePair{ "winmm", "Windows Multimedia" },
  59. #endif
  60. #ifdef HAVE_PORTAUDIO
  61. BackendNamePair{ "port", "PortAudio" },
  62. #endif
  63. #ifdef HAVE_OPENSL
  64. BackendNamePair{ "opensl", "OpenSL" },
  65. #endif
  66. BackendNamePair{ "null", "Null Output" },
  67. #ifdef HAVE_WAVE
  68. BackendNamePair{ "wave", "Wave Writer" },
  69. #endif
  70. };
  71. struct NameValuePair {
  72. /* NOLINTBEGIN(*-avoid-c-arrays) */
  73. const char name[64];
  74. const char value[16];
  75. /* NOLINTEND(*-avoid-c-arrays) */
  76. };
  77. constexpr std::array speakerModeList{
  78. NameValuePair{ "Autodetect", "" },
  79. NameValuePair{ "Mono", "mono" },
  80. NameValuePair{ "Stereo", "stereo" },
  81. NameValuePair{ "Quadraphonic", "quad" },
  82. NameValuePair{ "5.1 Surround", "surround51" },
  83. NameValuePair{ "6.1 Surround", "surround61" },
  84. NameValuePair{ "7.1 Surround", "surround71" },
  85. NameValuePair{ "3D7.1 Surround", "surround3d71" },
  86. NameValuePair{ "Ambisonic, 1st Order", "ambi1" },
  87. NameValuePair{ "Ambisonic, 2nd Order", "ambi2" },
  88. NameValuePair{ "Ambisonic, 3rd Order", "ambi3" },
  89. };
  90. constexpr std::array sampleTypeList{
  91. NameValuePair{ "Autodetect", "" },
  92. NameValuePair{ "8-bit int", "int8" },
  93. NameValuePair{ "8-bit uint", "uint8" },
  94. NameValuePair{ "16-bit int", "int16" },
  95. NameValuePair{ "16-bit uint", "uint16" },
  96. NameValuePair{ "32-bit int", "int32" },
  97. NameValuePair{ "32-bit uint", "uint32" },
  98. NameValuePair{ "32-bit float", "float32" },
  99. };
  100. constexpr std::array resamplerList{
  101. NameValuePair{ "Point", "point" },
  102. NameValuePair{ "Linear", "linear" },
  103. NameValuePair{ "Cubic Spline", "spline" },
  104. NameValuePair{ "4-point Gaussian", "gaussian" },
  105. NameValuePair{ "Default (4-point Gaussian)", "" },
  106. NameValuePair{ "11th order Sinc (fast)", "fast_bsinc12" },
  107. NameValuePair{ "11th order Sinc", "bsinc12" },
  108. NameValuePair{ "23rd order Sinc (fast)", "fast_bsinc24" },
  109. NameValuePair{ "23rd order Sinc", "bsinc24" },
  110. };
  111. constexpr std::array stereoModeList{
  112. NameValuePair{ "Autodetect", "" },
  113. NameValuePair{ "Speakers", "speakers" },
  114. NameValuePair{ "Headphones", "headphones" },
  115. };
  116. constexpr std::array stereoEncList{
  117. NameValuePair{ "Default", "" },
  118. NameValuePair{ "Basic", "panpot" },
  119. NameValuePair{ "UHJ", "uhj" },
  120. NameValuePair{ "Binaural", "hrtf" },
  121. };
  122. constexpr std::array ambiFormatList{
  123. NameValuePair{ "Default", "" },
  124. NameValuePair{ "AmbiX (ACN, SN3D)", "ambix" },
  125. NameValuePair{ "Furse-Malham", "fuma" },
  126. NameValuePair{ "ACN, N3D", "acn+n3d" },
  127. NameValuePair{ "ACN, FuMa", "acn+fuma" },
  128. };
  129. constexpr std::array hrtfModeList{
  130. NameValuePair{ "1st Order Ambisonic", "ambi1" },
  131. NameValuePair{ "2nd Order Ambisonic", "ambi2" },
  132. NameValuePair{ "3rd Order Ambisonic", "ambi3" },
  133. NameValuePair{ "Default (Full)", "" },
  134. NameValuePair{ "Full", "full" },
  135. };
  136. QString getDefaultConfigName()
  137. {
  138. #ifdef Q_OS_WIN32
  139. const char *fname{"alsoft.ini"};
  140. auto get_appdata_path = []() noexcept -> QString
  141. {
  142. QString ret;
  143. WCHAR *buffer{};
  144. if(const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND,
  145. nullptr, &buffer)}; SUCCEEDED(hr))
  146. ret = QString::fromWCharArray(buffer);
  147. CoTaskMemFree(buffer);
  148. return ret;
  149. };
  150. QString base = get_appdata_path();
  151. #else
  152. const char *fname{"alsoft.conf"};
  153. QString base = qgetenv("XDG_CONFIG_HOME");
  154. if(base.isEmpty())
  155. {
  156. base = qgetenv("HOME");
  157. if(base.isEmpty() == false)
  158. base += "/.config";
  159. }
  160. #endif
  161. if(base.isEmpty() == false)
  162. return base +'/'+ fname;
  163. return fname;
  164. }
  165. QString getBaseDataPath()
  166. {
  167. #ifdef Q_OS_WIN32
  168. auto get_appdata_path = []() noexcept -> QString
  169. {
  170. QString ret;
  171. WCHAR *buffer{};
  172. if(const HRESULT hr{SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DONT_UNEXPAND,
  173. nullptr, &buffer)}; SUCCEEDED(hr))
  174. ret = QString::fromWCharArray(buffer);
  175. CoTaskMemFree(buffer);
  176. return ret;
  177. };
  178. QString base = get_appdata_path();
  179. #else
  180. QString base = qgetenv("XDG_DATA_HOME");
  181. if(base.isEmpty())
  182. {
  183. base = qgetenv("HOME");
  184. if(!base.isEmpty())
  185. base += "/.local/share";
  186. }
  187. #endif
  188. return base;
  189. }
  190. QStringList getAllDataPaths(const QString &append)
  191. {
  192. QStringList list;
  193. list.append(getBaseDataPath());
  194. #ifdef Q_OS_WIN32
  195. // TODO: Common AppData path
  196. #else
  197. QString paths = qgetenv("XDG_DATA_DIRS");
  198. if(paths.isEmpty())
  199. paths = "/usr/local/share/:/usr/share/";
  200. #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
  201. list += paths.split(QChar(':'), Qt::SkipEmptyParts);
  202. #else
  203. list += paths.split(QChar(':'), QString::SkipEmptyParts);
  204. #endif
  205. #endif
  206. QStringList::iterator iter = list.begin();
  207. while(iter != list.end())
  208. {
  209. if(iter->isEmpty())
  210. iter = list.erase(iter);
  211. else
  212. {
  213. iter->append(append);
  214. iter++;
  215. }
  216. }
  217. return list;
  218. }
  219. QString getValueFromName(const al::span<const NameValuePair> list, const QString &str)
  220. {
  221. for(size_t i{0};i < list.size();++i)
  222. {
  223. if(str == std::data(list[i].name))
  224. return std::data(list[i].value);
  225. }
  226. return QString{};
  227. }
  228. QString getNameFromValue(const al::span<const NameValuePair> list, const QString &str)
  229. {
  230. for(size_t i{0};i < list.size();++i)
  231. {
  232. if(str == std::data(list[i].value))
  233. return std::data(list[i].name);
  234. }
  235. return QString{};
  236. }
  237. Qt::CheckState getCheckState(const QVariant &var)
  238. {
  239. if(var.isNull())
  240. return Qt::PartiallyChecked;
  241. if(var.toBool())
  242. return Qt::Checked;
  243. return Qt::Unchecked;
  244. }
  245. QString getCheckValue(const QCheckBox *checkbox)
  246. {
  247. const Qt::CheckState state{checkbox->checkState()};
  248. if(state == Qt::Checked)
  249. return QStringLiteral("true");
  250. if(state == Qt::Unchecked)
  251. return QStringLiteral("false");
  252. return QString{};
  253. }
  254. }
  255. MainWindow::MainWindow(QWidget *parent) : QMainWindow{parent}
  256. , ui{std::make_unique<Ui::MainWindow>()}
  257. {
  258. ui->setupUi(this);
  259. for(auto &item : speakerModeList)
  260. ui->channelConfigCombo->addItem(std::data(item.name));
  261. ui->channelConfigCombo->adjustSize();
  262. for(auto &item : sampleTypeList)
  263. ui->sampleFormatCombo->addItem(std::data(item.name));
  264. ui->sampleFormatCombo->adjustSize();
  265. for(auto &item : stereoModeList)
  266. ui->stereoModeCombo->addItem(std::data(item.name));
  267. ui->stereoModeCombo->adjustSize();
  268. for(auto &item : stereoEncList)
  269. ui->stereoEncodingComboBox->addItem(std::data(item.name));
  270. ui->stereoEncodingComboBox->adjustSize();
  271. for(auto &item : ambiFormatList)
  272. ui->ambiFormatComboBox->addItem(std::data(item.name));
  273. ui->ambiFormatComboBox->adjustSize();
  274. ui->resamplerSlider->setRange(0, resamplerList.size()-1);
  275. ui->hrtfmodeSlider->setRange(0, hrtfModeList.size()-1);
  276. #if !defined(HAVE_NEON) && !defined(HAVE_SSE)
  277. ui->cpuExtDisabledLabel->move(ui->cpuExtDisabledLabel->x(), ui->cpuExtDisabledLabel->y() - 60);
  278. #else
  279. ui->cpuExtDisabledLabel->setVisible(false);
  280. #endif
  281. #ifndef HAVE_NEON
  282. #ifndef HAVE_SSE4_1
  283. #ifndef HAVE_SSE3
  284. #ifndef HAVE_SSE2
  285. #ifndef HAVE_SSE
  286. ui->enableSSECheckBox->setVisible(false);
  287. #endif /* !SSE */
  288. ui->enableSSE2CheckBox->setVisible(false);
  289. #endif /* !SSE2 */
  290. ui->enableSSE3CheckBox->setVisible(false);
  291. #endif /* !SSE3 */
  292. ui->enableSSE41CheckBox->setVisible(false);
  293. #endif /* !SSE4.1 */
  294. ui->enableNeonCheckBox->setVisible(false);
  295. #else /* !Neon */
  296. #ifndef HAVE_SSE4_1
  297. #ifndef HAVE_SSE3
  298. #ifndef HAVE_SSE2
  299. #ifndef HAVE_SSE
  300. ui->enableNeonCheckBox->move(ui->enableNeonCheckBox->x(), ui->enableNeonCheckBox->y() - 30);
  301. ui->enableSSECheckBox->setVisible(false);
  302. #endif /* !SSE */
  303. ui->enableSSE2CheckBox->setVisible(false);
  304. #endif /* !SSE2 */
  305. ui->enableSSE3CheckBox->setVisible(false);
  306. #endif /* !SSE3 */
  307. ui->enableSSE41CheckBox->setVisible(false);
  308. #endif /* !SSE4.1 */
  309. #endif
  310. #ifndef ALSOFT_EAX
  311. ui->enableEaxCheck->setChecked(Qt::Unchecked);
  312. ui->enableEaxCheck->setEnabled(false);
  313. ui->enableEaxCheck->setVisible(false);
  314. #endif
  315. mPeriodSizeValidator = std::make_unique<QIntValidator>(64, 8192, this);
  316. ui->periodSizeEdit->setValidator(mPeriodSizeValidator.get());
  317. mPeriodCountValidator = std::make_unique<QIntValidator>(2, 16, this);
  318. ui->periodCountEdit->setValidator(mPeriodCountValidator.get());
  319. mSourceCountValidator = std::make_unique<QIntValidator>(0, 4096, this);
  320. ui->srcCountLineEdit->setValidator(mSourceCountValidator.get());
  321. mEffectSlotValidator = std::make_unique<QIntValidator>(0, 64, this);
  322. ui->effectSlotLineEdit->setValidator(mEffectSlotValidator.get());
  323. mSourceSendValidator = std::make_unique<QIntValidator>(0, 16, this);
  324. ui->srcSendLineEdit->setValidator(mSourceSendValidator.get());
  325. mSampleRateValidator = std::make_unique<QIntValidator>(8000, 192000, this);
  326. ui->sampleRateCombo->lineEdit()->setValidator(mSampleRateValidator.get());
  327. mJackBufferValidator = std::make_unique<QIntValidator>(0, 8192, this);
  328. ui->jackBufferSizeLine->setValidator(mJackBufferValidator.get());
  329. connect(ui->actionLoad, &QAction::triggered, this, &MainWindow::loadConfigFromFile);
  330. connect(ui->actionSave_As, &QAction::triggered, this, &MainWindow::saveConfigAsFile);
  331. connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::showAboutPage);
  332. connect(ui->closeCancelButton, &QPushButton::clicked, this, &MainWindow::cancelCloseAction);
  333. connect(ui->applyButton, &QPushButton::clicked, this, &MainWindow::saveCurrentConfig);
  334. auto qcb_cicint = static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged);
  335. connect(ui->channelConfigCombo, qcb_cicint, this, &MainWindow::enableApplyButton);
  336. connect(ui->sampleFormatCombo, qcb_cicint, this, &MainWindow::enableApplyButton);
  337. connect(ui->stereoModeCombo, qcb_cicint, this, &MainWindow::enableApplyButton);
  338. connect(ui->sampleRateCombo, qcb_cicint, this, &MainWindow::enableApplyButton);
  339. connect(ui->sampleRateCombo, &QComboBox::editTextChanged, this, &MainWindow::enableApplyButton);
  340. connect(ui->resamplerSlider, &QSlider::valueChanged, this, &MainWindow::updateResamplerLabel);
  341. connect(ui->periodSizeSlider, &QSlider::valueChanged, this, &MainWindow::updatePeriodSizeEdit);
  342. connect(ui->periodSizeEdit, &QLineEdit::editingFinished, this, &MainWindow::updatePeriodSizeSlider);
  343. connect(ui->periodCountSlider, &QSlider::valueChanged, this, &MainWindow::updatePeriodCountEdit);
  344. connect(ui->periodCountEdit, &QLineEdit::editingFinished, this, &MainWindow::updatePeriodCountSlider);
  345. connect(ui->stereoEncodingComboBox, qcb_cicint, this, &MainWindow::enableApplyButton);
  346. connect(ui->ambiFormatComboBox, qcb_cicint, this, &MainWindow::enableApplyButton);
  347. connect(ui->outputLimiterCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  348. connect(ui->outputDitherCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  349. connect(ui->decoderHQModeCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  350. connect(ui->decoderDistCompCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  351. connect(ui->decoderNFEffectsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  352. auto qdsb_vcd = static_cast<void(QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged);
  353. connect(ui->decoderSpeakerDistSpinBox, qdsb_vcd, this, &MainWindow::enableApplyButton);
  354. connect(ui->decoderQuadLineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  355. connect(ui->decoderQuadButton, &QPushButton::clicked, this, &MainWindow::selectQuadDecoderFile);
  356. connect(ui->decoder51LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  357. connect(ui->decoder51Button, &QPushButton::clicked, this, &MainWindow::select51DecoderFile);
  358. connect(ui->decoder61LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  359. connect(ui->decoder61Button, &QPushButton::clicked, this, &MainWindow::select61DecoderFile);
  360. connect(ui->decoder71LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  361. connect(ui->decoder71Button, &QPushButton::clicked, this, &MainWindow::select71DecoderFile);
  362. connect(ui->decoder3D71LineEdit, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  363. connect(ui->decoder3D71Button, &QPushButton::clicked, this, &MainWindow::select3D71DecoderFile);
  364. connect(ui->preferredHrtfComboBox, qcb_cicint, this, &MainWindow::enableApplyButton);
  365. connect(ui->hrtfmodeSlider, &QSlider::valueChanged, this, &MainWindow::updateHrtfModeLabel);
  366. connect(ui->hrtfAddButton, &QPushButton::clicked, this, &MainWindow::addHrtfFile);
  367. connect(ui->hrtfRemoveButton, &QPushButton::clicked, this, &MainWindow::removeHrtfFile);
  368. connect(ui->hrtfFileList, &QListWidget::itemSelectionChanged, this, &MainWindow::updateHrtfRemoveButton);
  369. connect(ui->defaultHrtfPathsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  370. connect(ui->srcCountLineEdit, &QLineEdit::editingFinished, this, &MainWindow::enableApplyButton);
  371. connect(ui->srcSendLineEdit, &QLineEdit::editingFinished, this, &MainWindow::enableApplyButton);
  372. connect(ui->effectSlotLineEdit, &QLineEdit::editingFinished, this, &MainWindow::enableApplyButton);
  373. connect(ui->enableSSECheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  374. connect(ui->enableSSE2CheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  375. connect(ui->enableSSE3CheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  376. connect(ui->enableSSE41CheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  377. connect(ui->enableNeonCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  378. ui->enabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu);
  379. connect(ui->enabledBackendList, &QListWidget::customContextMenuRequested, this, &MainWindow::showEnabledBackendMenu);
  380. ui->disabledBackendList->setContextMenuPolicy(Qt::CustomContextMenu);
  381. connect(ui->disabledBackendList, &QListWidget::customContextMenuRequested, this, &MainWindow::showDisabledBackendMenu);
  382. connect(ui->backendCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  383. connect(ui->defaultReverbComboBox, qcb_cicint, this, &MainWindow::enableApplyButton);
  384. connect(ui->enableEaxReverbCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  385. connect(ui->enableStdReverbCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  386. connect(ui->enableAutowahCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  387. connect(ui->enableChorusCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  388. connect(ui->enableCompressorCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  389. connect(ui->enableDistortionCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  390. connect(ui->enableEchoCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  391. connect(ui->enableEqualizerCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  392. connect(ui->enableFlangerCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  393. connect(ui->enableFrequencyShifterCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  394. connect(ui->enableModulatorCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  395. connect(ui->enableDedicatedCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  396. connect(ui->enablePitchShifterCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  397. connect(ui->enableVocalMorpherCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  398. connect(ui->enableEaxCheck, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  399. connect(ui->pulseAutospawnCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  400. connect(ui->pulseAllowMovesCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  401. connect(ui->pulseFixRateCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  402. connect(ui->pulseAdjLatencyCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  403. connect(ui->pwireAssumeAudioCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  404. connect(ui->pwireRtMixCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  405. connect(ui->wasapiResamplerCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  406. connect(ui->jackAutospawnCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  407. connect(ui->jackConnectPortsCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  408. connect(ui->jackRtMixCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  409. connect(ui->jackBufferSizeSlider, &QSlider::valueChanged, this, &MainWindow::updateJackBufferSizeEdit);
  410. connect(ui->jackBufferSizeLine, &QLineEdit::editingFinished, this, &MainWindow::updateJackBufferSizeSlider);
  411. connect(ui->alsaDefaultDeviceLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  412. connect(ui->alsaDefaultCaptureLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  413. connect(ui->alsaResamplerCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  414. connect(ui->alsaMmapCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  415. connect(ui->ossDefaultDeviceLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  416. connect(ui->ossPlaybackPushButton, &QPushButton::clicked, this, &MainWindow::selectOSSPlayback);
  417. connect(ui->ossDefaultCaptureLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  418. connect(ui->ossCapturePushButton, &QPushButton::clicked, this, &MainWindow::selectOSSCapture);
  419. connect(ui->solarisDefaultDeviceLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  420. connect(ui->solarisPlaybackPushButton, &QPushButton::clicked, this, &MainWindow::selectSolarisPlayback);
  421. connect(ui->waveOutputLine, &QLineEdit::textChanged, this, &MainWindow::enableApplyButton);
  422. connect(ui->waveOutputButton, &QPushButton::clicked, this, &MainWindow::selectWaveOutput);
  423. connect(ui->waveBFormatCheckBox, &QCheckBox::stateChanged, this, &MainWindow::enableApplyButton);
  424. ui->backendListWidget->setCurrentRow(0);
  425. ui->tabWidget->setCurrentIndex(0);
  426. for(int i = 1;i < ui->backendListWidget->count();i++)
  427. ui->backendListWidget->setRowHidden(i, true);
  428. for(size_t i{0};i < backendList.size();++i)
  429. {
  430. QList<QListWidgetItem*> items = ui->backendListWidget->findItems(
  431. std::data(backendList[i].full_string), Qt::MatchFixedString);
  432. Q_FOREACH(QListWidgetItem *item, items)
  433. item->setHidden(false);
  434. }
  435. loadConfig(getDefaultConfigName());
  436. }
  437. MainWindow::~MainWindow() = default;
  438. void MainWindow::closeEvent(QCloseEvent *event)
  439. {
  440. if(!mNeedsSave)
  441. event->accept();
  442. else
  443. {
  444. QMessageBox::StandardButton btn = QMessageBox::warning(this,
  445. tr("Apply changes?"), tr("Save changes before quitting?"),
  446. QMessageBox::Save | QMessageBox::No | QMessageBox::Cancel);
  447. if(btn == QMessageBox::Save)
  448. saveCurrentConfig();
  449. if(btn == QMessageBox::Cancel)
  450. event->ignore();
  451. else
  452. event->accept();
  453. }
  454. }
  455. void MainWindow::cancelCloseAction()
  456. {
  457. mNeedsSave = false;
  458. close();
  459. }
  460. void MainWindow::showAboutPage()
  461. {
  462. QMessageBox::information(this, tr("About"),
  463. tr("OpenAL Soft Configuration Utility.\nBuilt for OpenAL Soft library version ") +
  464. GetVersionString());
  465. }
  466. QStringList MainWindow::collectHrtfs()
  467. {
  468. QStringList ret;
  469. QStringList processed;
  470. for(int i = 0;i < ui->hrtfFileList->count();i++)
  471. {
  472. QDir dir(ui->hrtfFileList->item(i)->text());
  473. QStringList fnames = dir.entryList(QDir::Files | QDir::Readable, QDir::Name);
  474. Q_FOREACH(const QString &fname, fnames)
  475. {
  476. if(!fname.endsWith(QStringLiteral(".mhr"), Qt::CaseInsensitive))
  477. continue;
  478. QString fullname{dir.absoluteFilePath(fname)};
  479. if(processed.contains(fullname))
  480. continue;
  481. processed.push_back(fullname);
  482. QString name{fname.left(fname.length()-4)};
  483. if(!ret.contains(name))
  484. ret.push_back(name);
  485. else
  486. {
  487. size_t i{2};
  488. do {
  489. QString s = name+" #"+QString::number(i);
  490. if(!ret.contains(s))
  491. {
  492. ret.push_back(s);
  493. break;
  494. }
  495. ++i;
  496. } while(true);
  497. }
  498. }
  499. }
  500. if(ui->defaultHrtfPathsCheckBox->isChecked())
  501. {
  502. QStringList paths = getAllDataPaths(QStringLiteral("/openal/hrtf"));
  503. Q_FOREACH(const QString &name, paths)
  504. {
  505. QDir dir{name};
  506. QStringList fnames{dir.entryList(QDir::Files | QDir::Readable, QDir::Name)};
  507. Q_FOREACH(const QString &fname, fnames)
  508. {
  509. if(!fname.endsWith(QStringLiteral(".mhr"), Qt::CaseInsensitive))
  510. continue;
  511. QString fullname{dir.absoluteFilePath(fname)};
  512. if(processed.contains(fullname))
  513. continue;
  514. processed.push_back(fullname);
  515. QString name{fname.left(fname.length()-4)};
  516. if(!ret.contains(name))
  517. ret.push_back(name);
  518. else
  519. {
  520. size_t i{2};
  521. do {
  522. QString s{name+" #"+QString::number(i)};
  523. if(!ret.contains(s))
  524. {
  525. ret.push_back(s);
  526. break;
  527. }
  528. ++i;
  529. } while(true);
  530. }
  531. }
  532. }
  533. #ifdef ALSOFT_EMBED_HRTF_DATA
  534. ret.push_back(QStringLiteral("Built-In HRTF"));
  535. #endif
  536. }
  537. return ret;
  538. }
  539. void MainWindow::loadConfigFromFile()
  540. {
  541. QString fname = QFileDialog::getOpenFileName(this, tr("Select Files"));
  542. if(fname.isEmpty() == false)
  543. loadConfig(fname);
  544. }
  545. void MainWindow::loadConfig(const QString &fname)
  546. {
  547. QSettings settings{fname, QSettings::IniFormat};
  548. QString sampletype{settings.value(QStringLiteral("sample-type")).toString()};
  549. ui->sampleFormatCombo->setCurrentIndex(0);
  550. if(sampletype.isEmpty() == false)
  551. {
  552. QString str{getNameFromValue(sampleTypeList, sampletype)};
  553. if(!str.isEmpty())
  554. {
  555. const int j{ui->sampleFormatCombo->findText(str)};
  556. if(j > 0) ui->sampleFormatCombo->setCurrentIndex(j);
  557. }
  558. }
  559. QString channelconfig{settings.value(QStringLiteral("channels")).toString()};
  560. ui->channelConfigCombo->setCurrentIndex(0);
  561. if(channelconfig.isEmpty() == false)
  562. {
  563. if(channelconfig == QStringLiteral("surround51rear"))
  564. channelconfig = QStringLiteral("surround51");
  565. QString str{getNameFromValue(speakerModeList, channelconfig)};
  566. if(!str.isEmpty())
  567. {
  568. const int j{ui->channelConfigCombo->findText(str)};
  569. if(j > 0) ui->channelConfigCombo->setCurrentIndex(j);
  570. }
  571. }
  572. QString srate{settings.value(QStringLiteral("frequency")).toString()};
  573. if(srate.isEmpty())
  574. ui->sampleRateCombo->setCurrentIndex(0);
  575. else
  576. {
  577. ui->sampleRateCombo->lineEdit()->clear();
  578. ui->sampleRateCombo->lineEdit()->insert(srate);
  579. }
  580. ui->srcCountLineEdit->clear();
  581. ui->srcCountLineEdit->insert(settings.value(QStringLiteral("sources")).toString());
  582. ui->effectSlotLineEdit->clear();
  583. ui->effectSlotLineEdit->insert(settings.value(QStringLiteral("slots")).toString());
  584. ui->srcSendLineEdit->clear();
  585. ui->srcSendLineEdit->insert(settings.value(QStringLiteral("sends")).toString());
  586. QString resampler = settings.value(QStringLiteral("resampler")).toString().trimmed();
  587. ui->resamplerSlider->setValue(2);
  588. ui->resamplerLabel->setText(std::data(resamplerList[2].name));
  589. /* "Cubic" is an alias for the 4-point gaussian resampler. The "sinc4" and
  590. * "sinc8" resamplers are unsupported, use "gaussian" as a fallback.
  591. */
  592. if(resampler == QLatin1String{"cubic"} || resampler == QLatin1String{"sinc4"}
  593. || resampler == QLatin1String{"sinc8"})
  594. resampler = QStringLiteral("gaussian");
  595. /* The "bsinc" resampler name is an alias for "bsinc12". */
  596. else if(resampler == QLatin1String{"bsinc"})
  597. resampler = QStringLiteral("bsinc12");
  598. for(int i = 0;resamplerList[i].name[0];i++)
  599. {
  600. if(resampler == std::data(resamplerList[i].value))
  601. {
  602. ui->resamplerSlider->setValue(i);
  603. ui->resamplerLabel->setText(std::data(resamplerList[i].name));
  604. break;
  605. }
  606. }
  607. QString stereomode{settings.value(QStringLiteral("stereo-mode")).toString().trimmed()};
  608. ui->stereoModeCombo->setCurrentIndex(0);
  609. if(stereomode.isEmpty() == false)
  610. {
  611. QString str{getNameFromValue(stereoModeList, stereomode)};
  612. if(!str.isEmpty())
  613. {
  614. const int j{ui->stereoModeCombo->findText(str)};
  615. if(j > 0) ui->stereoModeCombo->setCurrentIndex(j);
  616. }
  617. }
  618. int periodsize{settings.value("period_size").toInt()};
  619. ui->periodSizeEdit->clear();
  620. if(periodsize >= 64)
  621. {
  622. ui->periodSizeEdit->insert(QString::number(periodsize));
  623. updatePeriodSizeSlider();
  624. }
  625. int periodcount{settings.value("periods").toInt()};
  626. ui->periodCountEdit->clear();
  627. if(periodcount >= 2)
  628. {
  629. ui->periodCountEdit->insert(QString::number(periodcount));
  630. updatePeriodCountSlider();
  631. }
  632. ui->outputLimiterCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("output-limiter"))));
  633. ui->outputDitherCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("dither"))));
  634. QString stereopan{settings.value(QStringLiteral("stereo-encoding")).toString()};
  635. ui->stereoEncodingComboBox->setCurrentIndex(0);
  636. if(stereopan.isEmpty() == false)
  637. {
  638. QString str{getNameFromValue(stereoEncList, stereopan)};
  639. if(!str.isEmpty())
  640. {
  641. const int j{ui->stereoEncodingComboBox->findText(str)};
  642. if(j > 0) ui->stereoEncodingComboBox->setCurrentIndex(j);
  643. }
  644. }
  645. QString ambiformat{settings.value(QStringLiteral("ambi-format")).toString()};
  646. ui->ambiFormatComboBox->setCurrentIndex(0);
  647. if(ambiformat.isEmpty() == false)
  648. {
  649. QString str{getNameFromValue(ambiFormatList, ambiformat)};
  650. if(!str.isEmpty())
  651. {
  652. const int j{ui->ambiFormatComboBox->findText(str)};
  653. if(j > 0) ui->ambiFormatComboBox->setCurrentIndex(j);
  654. }
  655. }
  656. ui->decoderHQModeCheckBox->setChecked(getCheckState(settings.value(QStringLiteral("decoder/hq-mode"))));
  657. ui->decoderDistCompCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("decoder/distance-comp"))));
  658. ui->decoderNFEffectsCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("decoder/nfc"))));
  659. double speakerdist{settings.value(QStringLiteral("decoder/speaker-dist"), 1.0).toDouble()};
  660. ui->decoderSpeakerDistSpinBox->setValue(speakerdist);
  661. ui->decoderQuadLineEdit->setText(settings.value(QStringLiteral("decoder/quad")).toString());
  662. ui->decoder51LineEdit->setText(settings.value(QStringLiteral("decoder/surround51")).toString());
  663. ui->decoder61LineEdit->setText(settings.value(QStringLiteral("decoder/surround61")).toString());
  664. ui->decoder71LineEdit->setText(settings.value(QStringLiteral("decoder/surround71")).toString());
  665. ui->decoder3D71LineEdit->setText(settings.value(QStringLiteral("decoder/surround3d71")).toString());
  666. QStringList disabledCpuExts{settings.value(QStringLiteral("disable-cpu-exts")).toStringList()};
  667. if(disabledCpuExts.size() == 1)
  668. disabledCpuExts = disabledCpuExts[0].split(QChar(','));
  669. for(QString &name : disabledCpuExts)
  670. name = name.trimmed();
  671. ui->enableSSECheckBox->setChecked(!disabledCpuExts.contains(QStringLiteral("sse"), Qt::CaseInsensitive));
  672. ui->enableSSE2CheckBox->setChecked(!disabledCpuExts.contains(QStringLiteral("sse2"), Qt::CaseInsensitive));
  673. ui->enableSSE3CheckBox->setChecked(!disabledCpuExts.contains(QStringLiteral("sse3"), Qt::CaseInsensitive));
  674. ui->enableSSE41CheckBox->setChecked(!disabledCpuExts.contains(QStringLiteral("sse4.1"), Qt::CaseInsensitive));
  675. ui->enableNeonCheckBox->setChecked(!disabledCpuExts.contains(QStringLiteral("neon"), Qt::CaseInsensitive));
  676. QString hrtfmode{settings.value(QStringLiteral("hrtf-mode")).toString().trimmed()};
  677. ui->hrtfmodeSlider->setValue(2);
  678. ui->hrtfmodeLabel->setText(std::data(hrtfModeList[3].name));
  679. /* The "basic" mode name is no longer supported. Use "ambi2" instead. */
  680. if(hrtfmode == QLatin1String{"basic"})
  681. hrtfmode = QStringLiteral("ambi2");
  682. for(size_t i{0};i < hrtfModeList.size();++i)
  683. {
  684. if(hrtfmode == std::data(hrtfModeList[i].value))
  685. {
  686. ui->hrtfmodeSlider->setValue(static_cast<int>(i));
  687. ui->hrtfmodeLabel->setText(std::data(hrtfModeList[i].name));
  688. break;
  689. }
  690. }
  691. QStringList hrtf_paths{settings.value(QStringLiteral("hrtf-paths")).toStringList()};
  692. if(hrtf_paths.size() == 1)
  693. hrtf_paths = hrtf_paths[0].split(QChar(','));
  694. for(QString &name : hrtf_paths)
  695. name = name.trimmed();
  696. if(!hrtf_paths.empty() && !hrtf_paths.back().isEmpty())
  697. ui->defaultHrtfPathsCheckBox->setCheckState(Qt::Unchecked);
  698. else
  699. {
  700. hrtf_paths.removeAll(QString());
  701. ui->defaultHrtfPathsCheckBox->setCheckState(Qt::Checked);
  702. }
  703. hrtf_paths.removeDuplicates();
  704. ui->hrtfFileList->clear();
  705. ui->hrtfFileList->addItems(hrtf_paths);
  706. updateHrtfRemoveButton();
  707. ui->preferredHrtfComboBox->clear();
  708. ui->preferredHrtfComboBox->addItem(QStringLiteral("- Any -"));
  709. if(ui->defaultHrtfPathsCheckBox->isChecked())
  710. {
  711. QStringList hrtfs{collectHrtfs()};
  712. Q_FOREACH(const QString &name, hrtfs)
  713. ui->preferredHrtfComboBox->addItem(name);
  714. }
  715. QString defaulthrtf{settings.value(QStringLiteral("default-hrtf")).toString()};
  716. ui->preferredHrtfComboBox->setCurrentIndex(0);
  717. if(defaulthrtf.isEmpty() == false)
  718. {
  719. int i{ui->preferredHrtfComboBox->findText(defaulthrtf)};
  720. if(i > 0)
  721. ui->preferredHrtfComboBox->setCurrentIndex(i);
  722. else
  723. {
  724. i = ui->preferredHrtfComboBox->count();
  725. ui->preferredHrtfComboBox->addItem(defaulthrtf);
  726. ui->preferredHrtfComboBox->setCurrentIndex(i);
  727. }
  728. }
  729. ui->preferredHrtfComboBox->adjustSize();
  730. ui->enabledBackendList->clear();
  731. ui->disabledBackendList->clear();
  732. QStringList drivers{settings.value(QStringLiteral("drivers")).toStringList()};
  733. if(drivers.empty())
  734. ui->backendCheckBox->setChecked(true);
  735. else
  736. {
  737. if(drivers.size() == 1)
  738. drivers = drivers[0].split(QChar(','));
  739. for(QString &name : drivers)
  740. {
  741. name = name.trimmed();
  742. /* Convert "mmdevapi" references to "wasapi" for backwards
  743. * compatibility.
  744. */
  745. if(name == QLatin1String{"-mmdevapi"})
  746. name = QStringLiteral("-wasapi");
  747. else if(name == QLatin1String{"mmdevapi"})
  748. name = QStringLiteral("wasapi");
  749. }
  750. bool lastWasEmpty{false};
  751. Q_FOREACH(const QString &backend, drivers)
  752. {
  753. lastWasEmpty = backend.isEmpty();
  754. if(lastWasEmpty) continue;
  755. if(!backend.startsWith(QChar('-')))
  756. {
  757. for(size_t j{0};j < backendList.size();++j)
  758. {
  759. if(backend == std::data(backendList[j].backend_name))
  760. {
  761. ui->enabledBackendList->addItem(std::data(backendList[j].full_string));
  762. break;
  763. }
  764. }
  765. }
  766. else if(backend.size() > 1)
  767. {
  768. QStringRef backendref{backend.rightRef(backend.size()-1)};
  769. for(size_t j{0};j < backendList.size();++j)
  770. {
  771. if(backendref == std::data(backendList[j].backend_name))
  772. {
  773. ui->disabledBackendList->addItem(std::data(backendList[j].full_string));
  774. break;
  775. }
  776. }
  777. }
  778. }
  779. ui->backendCheckBox->setChecked(lastWasEmpty);
  780. }
  781. QString defaultreverb{settings.value(QStringLiteral("default-reverb")).toString().toLower()};
  782. ui->defaultReverbComboBox->setCurrentIndex(0);
  783. if(defaultreverb.isEmpty() == false)
  784. {
  785. for(int i = 0;i < ui->defaultReverbComboBox->count();i++)
  786. {
  787. if(defaultreverb.compare(ui->defaultReverbComboBox->itemText(i).toLower()) == 0)
  788. {
  789. ui->defaultReverbComboBox->setCurrentIndex(i);
  790. break;
  791. }
  792. }
  793. }
  794. QStringList excludefx{settings.value(QStringLiteral("excludefx")).toStringList()};
  795. if(excludefx.size() == 1)
  796. excludefx = excludefx[0].split(QChar(','));
  797. for(QString &name : excludefx)
  798. name = name.trimmed();
  799. ui->enableEaxReverbCheck->setChecked(!excludefx.contains(QStringLiteral("eaxreverb"), Qt::CaseInsensitive));
  800. ui->enableStdReverbCheck->setChecked(!excludefx.contains(QStringLiteral("reverb"), Qt::CaseInsensitive));
  801. ui->enableAutowahCheck->setChecked(!excludefx.contains(QStringLiteral("autowah"), Qt::CaseInsensitive));
  802. ui->enableChorusCheck->setChecked(!excludefx.contains(QStringLiteral("chorus"), Qt::CaseInsensitive));
  803. ui->enableCompressorCheck->setChecked(!excludefx.contains(QStringLiteral("compressor"), Qt::CaseInsensitive));
  804. ui->enableDistortionCheck->setChecked(!excludefx.contains(QStringLiteral("distortion"), Qt::CaseInsensitive));
  805. ui->enableEchoCheck->setChecked(!excludefx.contains(QStringLiteral("echo"), Qt::CaseInsensitive));
  806. ui->enableEqualizerCheck->setChecked(!excludefx.contains(QStringLiteral("equalizer"), Qt::CaseInsensitive));
  807. ui->enableFlangerCheck->setChecked(!excludefx.contains(QStringLiteral("flanger"), Qt::CaseInsensitive));
  808. ui->enableFrequencyShifterCheck->setChecked(!excludefx.contains(QStringLiteral("fshifter"), Qt::CaseInsensitive));
  809. ui->enableModulatorCheck->setChecked(!excludefx.contains(QStringLiteral("modulator"), Qt::CaseInsensitive));
  810. ui->enableDedicatedCheck->setChecked(!excludefx.contains(QStringLiteral("dedicated"), Qt::CaseInsensitive));
  811. ui->enablePitchShifterCheck->setChecked(!excludefx.contains(QStringLiteral("pshifter"), Qt::CaseInsensitive));
  812. ui->enableVocalMorpherCheck->setChecked(!excludefx.contains(QStringLiteral("vmorpher"), Qt::CaseInsensitive));
  813. if(ui->enableEaxCheck->isEnabled())
  814. ui->enableEaxCheck->setChecked(getCheckState(settings.value(QStringLiteral("eax/enable"))) != Qt::Unchecked);
  815. ui->pulseAutospawnCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("pulse/spawn-server"))));
  816. ui->pulseAllowMovesCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("pulse/allow-moves"))));
  817. ui->pulseFixRateCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("pulse/fix-rate"))));
  818. ui->pulseAdjLatencyCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("pulse/adjust-latency"))));
  819. ui->pwireAssumeAudioCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("pipewire/assume-audio"))));
  820. ui->pwireRtMixCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("pipewire/rt-mix"))));
  821. ui->wasapiResamplerCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("wasapi/allow-resampler"))));
  822. ui->jackAutospawnCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("jack/spawn-server"))));
  823. ui->jackConnectPortsCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("jack/connect-ports"))));
  824. ui->jackRtMixCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("jack/rt-mix"))));
  825. ui->jackBufferSizeLine->setText(settings.value(QStringLiteral("jack/buffer-size"), QString()).toString());
  826. updateJackBufferSizeSlider();
  827. ui->alsaDefaultDeviceLine->setText(settings.value(QStringLiteral("alsa/device"), QString()).toString());
  828. ui->alsaDefaultCaptureLine->setText(settings.value(QStringLiteral("alsa/capture"), QString()).toString());
  829. ui->alsaResamplerCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("alsa/allow-resampler"))));
  830. ui->alsaMmapCheckBox->setCheckState(getCheckState(settings.value(QStringLiteral("alsa/mmap"))));
  831. ui->ossDefaultDeviceLine->setText(settings.value(QStringLiteral("oss/device"), QString()).toString());
  832. ui->ossDefaultCaptureLine->setText(settings.value(QStringLiteral("oss/capture"), QString()).toString());
  833. ui->solarisDefaultDeviceLine->setText(settings.value(QStringLiteral("solaris/device"), QString()).toString());
  834. ui->waveOutputLine->setText(settings.value(QStringLiteral("wave/file"), QString()).toString());
  835. ui->waveBFormatCheckBox->setChecked(settings.value(QStringLiteral("wave/bformat"), false).toBool());
  836. ui->applyButton->setEnabled(false);
  837. ui->closeCancelButton->setText(tr("Close"));
  838. mNeedsSave = false;
  839. }
  840. void MainWindow::saveCurrentConfig()
  841. {
  842. saveConfig(getDefaultConfigName());
  843. ui->applyButton->setEnabled(false);
  844. ui->closeCancelButton->setText(tr("Close"));
  845. mNeedsSave = false;
  846. QMessageBox::information(this, tr("Information"),
  847. tr("Applications using OpenAL need to be restarted for changes to take effect."));
  848. }
  849. void MainWindow::saveConfigAsFile()
  850. {
  851. QString fname{QFileDialog::getOpenFileName(this, tr("Select Files"))};
  852. if(fname.isEmpty() == false)
  853. {
  854. saveConfig(fname);
  855. ui->applyButton->setEnabled(false);
  856. mNeedsSave = false;
  857. }
  858. }
  859. void MainWindow::saveConfig(const QString &fname) const
  860. {
  861. QSettings settings{fname, QSettings::IniFormat};
  862. /* HACK: Compound any stringlist values into a comma-separated string. */
  863. QStringList allkeys{settings.allKeys()};
  864. Q_FOREACH(const QString &key, allkeys)
  865. {
  866. QStringList vals{settings.value(key).toStringList()};
  867. if(vals.size() > 1)
  868. settings.setValue(key, vals.join(QChar(',')));
  869. }
  870. settings.setValue(QStringLiteral("sample-type"), getValueFromName(sampleTypeList, ui->sampleFormatCombo->currentText()));
  871. settings.setValue(QStringLiteral("channels"), getValueFromName(speakerModeList, ui->channelConfigCombo->currentText()));
  872. uint rate{ui->sampleRateCombo->currentText().toUInt()};
  873. if(rate <= 0)
  874. settings.setValue(QStringLiteral("frequency"), QString{});
  875. else
  876. settings.setValue(QStringLiteral("frequency"), rate);
  877. settings.setValue(QStringLiteral("period_size"), ui->periodSizeEdit->text());
  878. settings.setValue(QStringLiteral("periods"), ui->periodCountEdit->text());
  879. settings.setValue(QStringLiteral("sources"), ui->srcCountLineEdit->text());
  880. settings.setValue(QStringLiteral("slots"), ui->effectSlotLineEdit->text());
  881. settings.setValue(QStringLiteral("resampler"), std::data(resamplerList[ui->resamplerSlider->value()].value));
  882. settings.setValue(QStringLiteral("stereo-mode"), getValueFromName(stereoModeList, ui->stereoModeCombo->currentText()));
  883. settings.setValue(QStringLiteral("stereo-encoding"), getValueFromName(stereoEncList, ui->stereoEncodingComboBox->currentText()));
  884. settings.setValue(QStringLiteral("ambi-format"), getValueFromName(ambiFormatList, ui->ambiFormatComboBox->currentText()));
  885. settings.setValue(QStringLiteral("output-limiter"), getCheckValue(ui->outputLimiterCheckBox));
  886. settings.setValue(QStringLiteral("dither"), getCheckValue(ui->outputDitherCheckBox));
  887. settings.setValue(QStringLiteral("decoder/hq-mode"), getCheckValue(ui->decoderHQModeCheckBox));
  888. settings.setValue(QStringLiteral("decoder/distance-comp"), getCheckValue(ui->decoderDistCompCheckBox));
  889. settings.setValue(QStringLiteral("decoder/nfc"), getCheckValue(ui->decoderNFEffectsCheckBox));
  890. double speakerdist{ui->decoderSpeakerDistSpinBox->value()};
  891. settings.setValue(QStringLiteral("decoder/speaker-dist"),
  892. (speakerdist != 1.0) ? QString::number(speakerdist) : QString{}
  893. );
  894. settings.setValue(QStringLiteral("decoder/quad"), ui->decoderQuadLineEdit->text());
  895. settings.setValue(QStringLiteral("decoder/surround51"), ui->decoder51LineEdit->text());
  896. settings.setValue(QStringLiteral("decoder/surround61"), ui->decoder61LineEdit->text());
  897. settings.setValue(QStringLiteral("decoder/surround71"), ui->decoder71LineEdit->text());
  898. settings.setValue(QStringLiteral("decoder/surround3d71"), ui->decoder3D71LineEdit->text());
  899. QStringList strlist;
  900. if(!ui->enableSSECheckBox->isChecked())
  901. strlist.append(QStringLiteral("sse"));
  902. if(!ui->enableSSE2CheckBox->isChecked())
  903. strlist.append(QStringLiteral("sse2"));
  904. if(!ui->enableSSE3CheckBox->isChecked())
  905. strlist.append(QStringLiteral("sse3"));
  906. if(!ui->enableSSE41CheckBox->isChecked())
  907. strlist.append(QStringLiteral("sse4.1"));
  908. if(!ui->enableNeonCheckBox->isChecked())
  909. strlist.append(QStringLiteral("neon"));
  910. settings.setValue(QStringLiteral("disable-cpu-exts"), strlist.join(QChar(',')));
  911. settings.setValue(QStringLiteral("hrtf-mode"), std::data(hrtfModeList[ui->hrtfmodeSlider->value()].value));
  912. if(ui->preferredHrtfComboBox->currentIndex() == 0)
  913. settings.setValue(QStringLiteral("default-hrtf"), QString{});
  914. else
  915. {
  916. QString str{ui->preferredHrtfComboBox->currentText()};
  917. settings.setValue(QStringLiteral("default-hrtf"), str);
  918. }
  919. strlist.clear();
  920. strlist.reserve(ui->hrtfFileList->count());
  921. for(int i = 0;i < ui->hrtfFileList->count();i++)
  922. strlist.append(ui->hrtfFileList->item(i)->text());
  923. if(!strlist.empty() && ui->defaultHrtfPathsCheckBox->isChecked())
  924. strlist.append(QString{});
  925. settings.setValue(QStringLiteral("hrtf-paths"), strlist.join(QChar{','}));
  926. strlist.clear();
  927. for(int i = 0;i < ui->enabledBackendList->count();i++)
  928. {
  929. QString label{ui->enabledBackendList->item(i)->text()};
  930. for(size_t j{0};j < backendList.size();++j)
  931. {
  932. if(label == std::data(backendList[j].full_string))
  933. {
  934. strlist.append(std::data(backendList[j].backend_name));
  935. break;
  936. }
  937. }
  938. }
  939. for(int i = 0;i < ui->disabledBackendList->count();i++)
  940. {
  941. QString label{ui->disabledBackendList->item(i)->text()};
  942. for(size_t j{0};j < backendList.size();++j)
  943. {
  944. if(label == std::data(backendList[j].full_string))
  945. {
  946. strlist.append(QChar{'-'}+QString{std::data(backendList[j].backend_name)});
  947. break;
  948. }
  949. }
  950. }
  951. if(strlist.empty() && !ui->backendCheckBox->isChecked())
  952. strlist.append(QStringLiteral("-all"));
  953. else if(ui->backendCheckBox->isChecked())
  954. strlist.append(QString{});
  955. settings.setValue(QStringLiteral("drivers"), strlist.join(QChar(',')));
  956. // TODO: Remove check when we can properly match global values.
  957. if(ui->defaultReverbComboBox->currentIndex() == 0)
  958. settings.setValue(QStringLiteral("default-reverb"), QString{});
  959. else
  960. {
  961. QString str{ui->defaultReverbComboBox->currentText().toLower()};
  962. settings.setValue(QStringLiteral("default-reverb"), str);
  963. }
  964. strlist.clear();
  965. if(!ui->enableEaxReverbCheck->isChecked())
  966. strlist.append(QStringLiteral("eaxreverb"));
  967. if(!ui->enableStdReverbCheck->isChecked())
  968. strlist.append(QStringLiteral("reverb"));
  969. if(!ui->enableAutowahCheck->isChecked())
  970. strlist.append(QStringLiteral("autowah"));
  971. if(!ui->enableChorusCheck->isChecked())
  972. strlist.append(QStringLiteral("chorus"));
  973. if(!ui->enableDistortionCheck->isChecked())
  974. strlist.append(QStringLiteral("distortion"));
  975. if(!ui->enableCompressorCheck->isChecked())
  976. strlist.append(QStringLiteral("compressor"));
  977. if(!ui->enableEchoCheck->isChecked())
  978. strlist.append(QStringLiteral("echo"));
  979. if(!ui->enableEqualizerCheck->isChecked())
  980. strlist.append(QStringLiteral("equalizer"));
  981. if(!ui->enableFlangerCheck->isChecked())
  982. strlist.append(QStringLiteral("flanger"));
  983. if(!ui->enableFrequencyShifterCheck->isChecked())
  984. strlist.append(QStringLiteral("fshifter"));
  985. if(!ui->enableModulatorCheck->isChecked())
  986. strlist.append(QStringLiteral("modulator"));
  987. if(!ui->enableDedicatedCheck->isChecked())
  988. strlist.append(QStringLiteral("dedicated"));
  989. if(!ui->enablePitchShifterCheck->isChecked())
  990. strlist.append(QStringLiteral("pshifter"));
  991. if(!ui->enableVocalMorpherCheck->isChecked())
  992. strlist.append(QStringLiteral("vmorpher"));
  993. settings.setValue(QStringLiteral("excludefx"), strlist.join(QChar{','}));
  994. settings.setValue(QStringLiteral("eax/enable"),
  995. (!ui->enableEaxCheck->isEnabled() || ui->enableEaxCheck->isChecked())
  996. ? QString{/*"true"*/} : QStringLiteral("false"));
  997. settings.setValue(QStringLiteral("pipewire/assume-audio"), getCheckValue(ui->pwireAssumeAudioCheckBox));
  998. settings.setValue(QStringLiteral("pipewire/rt-mix"), getCheckValue(ui->pwireRtMixCheckBox));
  999. settings.setValue(QStringLiteral("wasapi/allow-resampler"), getCheckValue(ui->wasapiResamplerCheckBox));
  1000. settings.setValue(QStringLiteral("pulse/spawn-server"), getCheckValue(ui->pulseAutospawnCheckBox));
  1001. settings.setValue(QStringLiteral("pulse/allow-moves"), getCheckValue(ui->pulseAllowMovesCheckBox));
  1002. settings.setValue(QStringLiteral("pulse/fix-rate"), getCheckValue(ui->pulseFixRateCheckBox));
  1003. settings.setValue(QStringLiteral("pulse/adjust-latency"), getCheckValue(ui->pulseAdjLatencyCheckBox));
  1004. settings.setValue(QStringLiteral("jack/spawn-server"), getCheckValue(ui->jackAutospawnCheckBox));
  1005. settings.setValue(QStringLiteral("jack/connect-ports"), getCheckValue(ui->jackConnectPortsCheckBox));
  1006. settings.setValue(QStringLiteral("jack/rt-mix"), getCheckValue(ui->jackRtMixCheckBox));
  1007. settings.setValue(QStringLiteral("jack/buffer-size"), ui->jackBufferSizeLine->text());
  1008. settings.setValue(QStringLiteral("alsa/device"), ui->alsaDefaultDeviceLine->text());
  1009. settings.setValue(QStringLiteral("alsa/capture"), ui->alsaDefaultCaptureLine->text());
  1010. settings.setValue(QStringLiteral("alsa/allow-resampler"), getCheckValue(ui->alsaResamplerCheckBox));
  1011. settings.setValue(QStringLiteral("alsa/mmap"), getCheckValue(ui->alsaMmapCheckBox));
  1012. settings.setValue(QStringLiteral("oss/device"), ui->ossDefaultDeviceLine->text());
  1013. settings.setValue(QStringLiteral("oss/capture"), ui->ossDefaultCaptureLine->text());
  1014. settings.setValue(QStringLiteral("solaris/device"), ui->solarisDefaultDeviceLine->text());
  1015. settings.setValue(QStringLiteral("wave/file"), ui->waveOutputLine->text());
  1016. settings.setValue(QStringLiteral("wave/bformat"),
  1017. ui->waveBFormatCheckBox->isChecked() ? QStringLiteral("true") : QString{/*"false"*/}
  1018. );
  1019. /* Remove empty keys
  1020. * FIXME: Should only remove keys whose value matches the globally-specified value.
  1021. */
  1022. allkeys = settings.allKeys();
  1023. Q_FOREACH(const QString &key, allkeys)
  1024. {
  1025. QString str{settings.value(key).toString()};
  1026. if(str.isEmpty())
  1027. settings.remove(key);
  1028. }
  1029. }
  1030. void MainWindow::enableApplyButton()
  1031. {
  1032. if(!mNeedsSave)
  1033. ui->applyButton->setEnabled(true);
  1034. mNeedsSave = true;
  1035. ui->closeCancelButton->setText(tr("Cancel"));
  1036. }
  1037. void MainWindow::updateResamplerLabel(int num)
  1038. {
  1039. ui->resamplerLabel->setText(std::data(resamplerList[num].name));
  1040. enableApplyButton();
  1041. }
  1042. void MainWindow::updatePeriodSizeEdit(int size)
  1043. {
  1044. ui->periodSizeEdit->clear();
  1045. if(size >= 64)
  1046. ui->periodSizeEdit->insert(QString::number(size));
  1047. enableApplyButton();
  1048. }
  1049. void MainWindow::updatePeriodSizeSlider()
  1050. {
  1051. int pos = ui->periodSizeEdit->text().toInt();
  1052. if(pos >= 64)
  1053. {
  1054. if(pos > 8192)
  1055. pos = 8192;
  1056. ui->periodSizeSlider->setSliderPosition(pos);
  1057. }
  1058. enableApplyButton();
  1059. }
  1060. void MainWindow::updatePeriodCountEdit(int count)
  1061. {
  1062. ui->periodCountEdit->clear();
  1063. if(count >= 2)
  1064. ui->periodCountEdit->insert(QString::number(count));
  1065. enableApplyButton();
  1066. }
  1067. void MainWindow::updatePeriodCountSlider()
  1068. {
  1069. int pos = ui->periodCountEdit->text().toInt();
  1070. if(pos < 2)
  1071. pos = 0;
  1072. else if(pos > 16)
  1073. pos = 16;
  1074. ui->periodCountSlider->setSliderPosition(pos);
  1075. enableApplyButton();
  1076. }
  1077. void MainWindow::selectQuadDecoderFile()
  1078. { selectDecoderFile(ui->decoderQuadLineEdit, "Select Quadraphonic Decoder");}
  1079. void MainWindow::select51DecoderFile()
  1080. { selectDecoderFile(ui->decoder51LineEdit, "Select 5.1 Surround Decoder");}
  1081. void MainWindow::select61DecoderFile()
  1082. { selectDecoderFile(ui->decoder61LineEdit, "Select 6.1 Surround Decoder");}
  1083. void MainWindow::select71DecoderFile()
  1084. { selectDecoderFile(ui->decoder71LineEdit, "Select 7.1 Surround Decoder");}
  1085. void MainWindow::select3D71DecoderFile()
  1086. { selectDecoderFile(ui->decoder3D71LineEdit, "Select 3D7.1 Surround Decoder");}
  1087. void MainWindow::selectDecoderFile(QLineEdit *line, const char *caption)
  1088. {
  1089. QString dir{line->text()};
  1090. if(dir.isEmpty() || QDir::isRelativePath(dir))
  1091. {
  1092. QStringList paths{getAllDataPaths("/openal/presets")};
  1093. while(!paths.isEmpty())
  1094. {
  1095. if(QDir{paths.last()}.exists())
  1096. {
  1097. dir = paths.last();
  1098. break;
  1099. }
  1100. paths.removeLast();
  1101. }
  1102. }
  1103. QString fname{QFileDialog::getOpenFileName(this, tr(caption),
  1104. dir, tr("AmbDec Files (*.ambdec);;All Files (*.*)"))};
  1105. if(!fname.isEmpty())
  1106. {
  1107. line->setText(fname);
  1108. enableApplyButton();
  1109. }
  1110. }
  1111. void MainWindow::updateJackBufferSizeEdit(int size)
  1112. {
  1113. ui->jackBufferSizeLine->clear();
  1114. if(size > 0)
  1115. ui->jackBufferSizeLine->insert(QString::number(1<<size));
  1116. enableApplyButton();
  1117. }
  1118. void MainWindow::updateJackBufferSizeSlider()
  1119. {
  1120. int value{ui->jackBufferSizeLine->text().toInt()};
  1121. auto pos = static_cast<int>(floor(log2(value) + 0.5));
  1122. ui->jackBufferSizeSlider->setSliderPosition(pos);
  1123. enableApplyButton();
  1124. }
  1125. void MainWindow::updateHrtfModeLabel(int num)
  1126. {
  1127. ui->hrtfmodeLabel->setText(std::data(hrtfModeList[static_cast<uint>(num)].name));
  1128. enableApplyButton();
  1129. }
  1130. void MainWindow::addHrtfFile()
  1131. {
  1132. QString path{QFileDialog::getExistingDirectory(this, tr("Select HRTF Path"))};
  1133. if(path.isEmpty() == false && !getAllDataPaths(QStringLiteral("/openal/hrtf")).contains(path))
  1134. {
  1135. ui->hrtfFileList->addItem(path);
  1136. enableApplyButton();
  1137. }
  1138. }
  1139. void MainWindow::removeHrtfFile()
  1140. {
  1141. QList<gsl::owner<QListWidgetItem*>> selected{ui->hrtfFileList->selectedItems()};
  1142. if(!selected.isEmpty())
  1143. {
  1144. std::for_each(selected.begin(), selected.end(), std::default_delete<QListWidgetItem>{});
  1145. enableApplyButton();
  1146. }
  1147. }
  1148. void MainWindow::updateHrtfRemoveButton()
  1149. {
  1150. ui->hrtfRemoveButton->setEnabled(!ui->hrtfFileList->selectedItems().empty());
  1151. }
  1152. void MainWindow::showEnabledBackendMenu(QPoint pt)
  1153. {
  1154. QHash<QAction*,QString> actionMap;
  1155. pt = ui->enabledBackendList->mapToGlobal(pt);
  1156. QMenu ctxmenu;
  1157. QAction *removeAction{ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove")};
  1158. if(ui->enabledBackendList->selectedItems().empty())
  1159. removeAction->setEnabled(false);
  1160. ctxmenu.addSeparator();
  1161. for(size_t i{0};i < backendList.size();++i)
  1162. {
  1163. QString backend{std::data(backendList[i].full_string)};
  1164. QAction *action{ctxmenu.addAction(QString("Add ")+backend)};
  1165. actionMap[action] = backend;
  1166. if(!ui->enabledBackendList->findItems(backend, Qt::MatchFixedString).empty() ||
  1167. !ui->disabledBackendList->findItems(backend, Qt::MatchFixedString).empty())
  1168. action->setEnabled(false);
  1169. }
  1170. QAction *gotAction{ctxmenu.exec(pt)};
  1171. if(gotAction == removeAction)
  1172. {
  1173. QList<gsl::owner<QListWidgetItem*>> selected{ui->enabledBackendList->selectedItems()};
  1174. std::for_each(selected.begin(), selected.end(), std::default_delete<QListWidgetItem>{});
  1175. enableApplyButton();
  1176. }
  1177. else if(gotAction != nullptr)
  1178. {
  1179. auto iter = actionMap.constFind(gotAction);
  1180. if(iter != actionMap.cend())
  1181. ui->enabledBackendList->addItem(iter.value());
  1182. enableApplyButton();
  1183. }
  1184. }
  1185. void MainWindow::showDisabledBackendMenu(QPoint pt)
  1186. {
  1187. QHash<QAction*,QString> actionMap;
  1188. pt = ui->disabledBackendList->mapToGlobal(pt);
  1189. QMenu ctxmenu;
  1190. QAction *removeAction{ctxmenu.addAction(QIcon::fromTheme("list-remove"), "Remove")};
  1191. if(ui->disabledBackendList->selectedItems().empty())
  1192. removeAction->setEnabled(false);
  1193. ctxmenu.addSeparator();
  1194. for(size_t i{0};i < backendList.size();++i)
  1195. {
  1196. QString backend{std::data(backendList[i].full_string)};
  1197. QAction *action{ctxmenu.addAction(QString("Add ")+backend)};
  1198. actionMap[action] = backend;
  1199. if(!ui->disabledBackendList->findItems(backend, Qt::MatchFixedString).empty() ||
  1200. !ui->enabledBackendList->findItems(backend, Qt::MatchFixedString).empty())
  1201. action->setEnabled(false);
  1202. }
  1203. QAction *gotAction{ctxmenu.exec(pt)};
  1204. if(gotAction == removeAction)
  1205. {
  1206. QList<gsl::owner<QListWidgetItem*>> selected{ui->disabledBackendList->selectedItems()};
  1207. std::for_each(selected.begin(), selected.end(), std::default_delete<QListWidgetItem>{});
  1208. enableApplyButton();
  1209. }
  1210. else if(gotAction != nullptr)
  1211. {
  1212. auto iter = actionMap.constFind(gotAction);
  1213. if(iter != actionMap.cend())
  1214. ui->disabledBackendList->addItem(iter.value());
  1215. enableApplyButton();
  1216. }
  1217. }
  1218. void MainWindow::selectOSSPlayback()
  1219. {
  1220. QString current{ui->ossDefaultDeviceLine->text()};
  1221. if(current.isEmpty()) current = ui->ossDefaultDeviceLine->placeholderText();
  1222. QString fname{QFileDialog::getOpenFileName(this, tr("Select Playback Device"), current)};
  1223. if(!fname.isEmpty())
  1224. {
  1225. ui->ossDefaultDeviceLine->setText(fname);
  1226. enableApplyButton();
  1227. }
  1228. }
  1229. void MainWindow::selectOSSCapture()
  1230. {
  1231. QString current{ui->ossDefaultCaptureLine->text()};
  1232. if(current.isEmpty()) current = ui->ossDefaultCaptureLine->placeholderText();
  1233. QString fname{QFileDialog::getOpenFileName(this, tr("Select Capture Device"), current)};
  1234. if(!fname.isEmpty())
  1235. {
  1236. ui->ossDefaultCaptureLine->setText(fname);
  1237. enableApplyButton();
  1238. }
  1239. }
  1240. void MainWindow::selectSolarisPlayback()
  1241. {
  1242. QString current{ui->solarisDefaultDeviceLine->text()};
  1243. if(current.isEmpty()) current = ui->solarisDefaultDeviceLine->placeholderText();
  1244. QString fname{QFileDialog::getOpenFileName(this, tr("Select Playback Device"), current)};
  1245. if(!fname.isEmpty())
  1246. {
  1247. ui->solarisDefaultDeviceLine->setText(fname);
  1248. enableApplyButton();
  1249. }
  1250. }
  1251. void MainWindow::selectWaveOutput()
  1252. {
  1253. QString fname{QFileDialog::getSaveFileName(this, tr("Select Wave File Output"),
  1254. ui->waveOutputLine->text(), tr("Wave Files (*.wav *.amb);;All Files (*.*)"))};
  1255. if(!fname.isEmpty())
  1256. {
  1257. ui->waveOutputLine->setText(fname);
  1258. enableApplyButton();
  1259. }
  1260. }