1 // qsynthSetupForm.cpp
2 //
3 /****************************************************************************
4 Copyright (C) 2003-2021, rncbc aka Rui Nuno Capela. All rights reserved.
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 *****************************************************************************/
21
22 #include "qsynthAbout.h"
23 #include "qsynthSetupForm.h"
24
25 #include "qsynthEngine.h"
26
27 #include <QValidator>
28 #include <QHeaderView>
29 #include <QMessageBox>
30 #include <QFileDialog>
31 #include <QFontDialog>
32 #include <QFileInfo>
33 #include <QPixmap>
34 #include <QMenu>
35
36
37 // Our local parameter data struct.
38 struct qsynth_settings_data
39 {
40 qsynthSetup *pSetup;
41 QTreeWidget *pListView;
42 QTreeWidgetItem *pListItem;
43 QStringList options;
44 };
45
qsynth_settings_foreach_option(void * pvData,const char *,const char * pszOption)46 static void qsynth_settings_foreach_option (
47 #ifdef CONFIG_FLUID_SETTINGS_FOREACH_OPTION
48 void *pvData, const char *, const char *pszOption )
49 #else
50 void *pvData, char *, char *pszOption )
51 #endif
52 {
53 qsynth_settings_data *pData = (qsynth_settings_data *) pvData;
54
55 pData->options.append(pszOption);
56 }
57
qsynth_settings_foreach(void * pvData,const char * pszName,int iType)58 static void qsynth_settings_foreach (
59 #ifdef CONFIG_FLUID_SETTINGS_FOREACH
60 void *pvData, const char *pszName, int iType )
61 #else
62 void *pvData, char *pszName, int iType )
63 #endif
64 {
65 qsynth_settings_data *pData = (qsynth_settings_data *) pvData;
66 fluid_settings_t *pFluidSettings = (pData->pSetup)->fluid_settings();
67
68 // Add the new list item.
69 int iCol = 0;
70 pData->pListItem = new QTreeWidgetItem(pData->pListView, pData->pListItem);
71 (pData->pListItem)->setText(iCol++, pszName);
72
73 // Check for type...
74 QString qsType = "?";
75 switch (iType) {
76 case FLUID_NUM_TYPE: qsType = "num"; break;
77 case FLUID_INT_TYPE: qsType = "int"; break;
78 case FLUID_STR_TYPE: qsType = "str"; break;
79 case FLUID_SET_TYPE: qsType = "set"; break;
80 }
81 (pData->pListItem)->setText(iCol++, qsType);
82 /*
83 // Check for hints...
84 int iHints = ::fluid_settings_get_hints(pFluidSettings, pszName);
85 QString sHints = "";
86 if (iHints & FLUID_HINT_BOUNDED_BELOW)
87 sHints += " BOUNDED_BELOW ";
88 if (iHints & FLUID_HINT_BOUNDED_ABOVE)
89 sHints += " BOUNDED_ABOVE ";
90 if (iHints & FLUID_HINT_TOGGLED)
91 sHints += " TOGGLED ";
92 if (iHints & FLUID_HINT_SAMPLE_RATE)
93 sHints += " SAMPLE_RATE ";
94 if (iHints & FLUID_HINT_LOGARITHMIC)
95 sHints += " LOGARITHMIC ";
96 if (iHints & FLUID_HINT_LOGARITHMIC)
97 sHints += " INTEGER ";
98 if (iHints & FLUID_HINT_FILENAME)
99 sHints += " FILENAME ";
100 if (iHints & FLUID_HINT_OPTIONLIST)
101 sHints += " OPTIONLIST ";
102 */
103 bool bRealtime = (bool) ::fluid_settings_is_realtime(pFluidSettings, pszName);
104 (pData->pListItem)->setText(iCol++, (bRealtime ? "yes" : "no"));
105
106 switch (iType) {
107
108 case FLUID_NUM_TYPE:
109 {
110 #ifdef CONFIG_FLUID_SETTINGS_GETNUM_DEFAULT
111 double fDefault = 0.0f;
112 ::fluid_settings_getnum_default(pFluidSettings, pszName, &fDefault);
113 #else
114 const double fDefault = ::fluid_settings_getnum_default(pFluidSettings, pszName);
115 #endif
116 double fCurrent = 0.0;
117 double fRangeMin = 0.0;
118 double fRangeMax = 0.0;
119 ::fluid_settings_getnum(pFluidSettings, pszName, &fCurrent);
120 ::fluid_settings_getnum_range(pFluidSettings, pszName, &fRangeMin, &fRangeMax);
121 (pData->pListItem)->setText(iCol++, QString::number(fCurrent));
122 (pData->pListItem)->setText(iCol++, QString::number(fDefault));
123 (pData->pListItem)->setText(iCol++, QString::number(fRangeMin));
124 (pData->pListItem)->setText(iCol++, QString::number(fRangeMax));
125 break;
126 }
127
128 case FLUID_INT_TYPE:
129 {
130 #ifdef CONFIG_FLUID_SETTINGS_GETINT_DEFAULT
131 int iDefault = 0;
132 ::fluid_settings_getint_default(pFluidSettings, pszName, &iDefault);
133 #else
134 const int iDefault = ::fluid_settings_getint_default(pFluidSettings, pszName);
135 #endif
136 int iCurrent = 0;
137 int iRangeMin = 0;
138 int iRangeMax = 0;
139 ::fluid_settings_getint(pFluidSettings, pszName, &iCurrent);
140 ::fluid_settings_getint_range(pFluidSettings, pszName, &iRangeMin, &iRangeMax);
141 if (iRangeMin + iRangeMax < 2) {
142 iRangeMin = 0;
143 iRangeMax = 1;
144 }
145 (pData->pListItem)->setText(iCol++, QString::number(iCurrent));
146 (pData->pListItem)->setText(iCol++, QString::number(iDefault));
147 (pData->pListItem)->setText(iCol++, QString::number(iRangeMin));
148 (pData->pListItem)->setText(iCol++, QString::number(iRangeMax));
149 break;
150 }
151
152 case FLUID_STR_TYPE:
153 {
154 #ifdef CONFIG_FLUID_SETTINGS_GETSTR_DEFAULT
155 char *pszDefault = nullptr;
156 ::fluid_settings_getstr_default(pFluidSettings, pszName, &pszDefault);
157 #else
158 const char *pszDefault = ::fluid_settings_getstr_default(pFluidSettings, pszName);
159 #endif
160 char *pszCurrent = nullptr;
161 #ifdef CONFIG_FLUID_SETTINGS_DUPSTR
162 ::fluid_settings_dupstr(pFluidSettings, pszName, &pszCurrent);
163 #else
164 ::fluid_settings_getstr(pFluidSettings, pszName, &pszCurrent);
165 #endif
166 (pData->pListItem)->setText(iCol++, pszCurrent);
167 (pData->pListItem)->setText(iCol++, pszDefault);
168 (pData->pListItem)->setText(iCol++, QString());
169 (pData->pListItem)->setText(iCol++, QString());
170 #ifdef CONFIG_FLUID_SETTINGS_DUPSTR
171 ::free(pszCurrent);
172 #endif
173 break;
174 }}
175
176 // Check for options.
177 pData->options.clear();
178 ::fluid_settings_foreach_option(pFluidSettings, pszName, pvData, qsynth_settings_foreach_option);
179 (pData->pListItem)->setText(iCol++, pData->options.join(" "));
180 }
181
182
183 //----------------------------------------------------------------------------
184 // qsynthSetupForm -- UI wrapper form.
185
186 // Constructor.
qsynthSetupForm(QWidget * pParent)187 qsynthSetupForm::qsynthSetupForm ( QWidget *pParent )
188 : QDialog(pParent)
189 {
190 // Setup UI struct...
191 m_ui.setupUi(this);
192
193 // No settings descriptor initially (the caller will set it).
194 m_pSetup = nullptr;
195 m_pOptions = nullptr;
196
197 // Initialize dirty control state.
198 m_iDirtySetup = 0;
199 m_iDirtyCount = 0;
200
201 // Check for pixmaps.
202 m_pXpmSoundFont = new QPixmap(":/images/sfont1.png");
203
204 // Set dialog validators...
205 QRegularExpression rx("[\\w-]+");
206 m_ui.DisplayNameLineEdit->setValidator(new QRegularExpressionValidator(rx, m_ui.DisplayNameLineEdit));
207 m_ui.SampleRateComboBox->setValidator(new QIntValidator(m_ui.SampleRateComboBox));
208 m_ui.AudioBufSizeComboBox->setValidator(new QIntValidator(m_ui.AudioBufSizeComboBox));
209 m_ui.AudioBufCountComboBox->setValidator(new QIntValidator(m_ui.AudioBufCountComboBox));
210 m_ui.JackNameComboBox->setValidator(new QRegularExpressionValidator(rx, m_ui.JackNameComboBox));
211 m_ui.MidiNameComboBox->setValidator(new QRegularExpressionValidator(rx, m_ui.MidiNameComboBox));
212
213 // No sorting on soundfont stack list.
214 //m_ui.SoundFontListView->setSorting(-1);
215
216 // Soundfonts list view...
217 QHeaderView *pHeader = m_ui.SoundFontListView->header();
218 pHeader->setDefaultAlignment(Qt::AlignLeft);
219 // pHeader->setDefaultSectionSize(300);
220 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
221 // pHeader->setSectionResizeMode(QHeaderView::Custom);
222 pHeader->setSectionsMovable(false);
223 #else
224 // pHeader->setResizeMode(QHeaderView::Custom);
225 pHeader->setMovable(false);
226 #endif
227 pHeader->setStretchLastSection(true);
228
229 m_ui.SoundFontListView->resizeColumnToContents(0); // SFID.
230 pHeader->resizeSection(1, 300); // Name.
231 m_ui.SoundFontListView->resizeColumnToContents(2); // Offset.
232
233 // Settings list view...
234 pHeader = m_ui.SettingsListView->header();
235 pHeader->setDefaultAlignment(Qt::AlignLeft);
236 // pHeader->setDefaultSectionSize(160);
237 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
238 // pHeader->setSectionResizeMode(QHeaderView::Custom);
239 pHeader->setSectionsMovable(false);
240 #else
241 // pHeader->setResizeMode(QHeaderView::Custom);
242 pHeader->setMovable(false);
243 #endif
244 pHeader->setStretchLastSection(true);
245
246 pHeader->resizeSection(0, 160); // Name.
247 m_ui.SettingsListView->resizeColumnToContents(1); // Type.
248 m_ui.SettingsListView->resizeColumnToContents(2); // Realtime.
249 // m_ui.SettingsListView->resizeColumnToContents(3); // Current.
250 // m_ui.SettingsListView->resizeColumnToContents(4); // Default.
251 m_ui.SettingsListView->resizeColumnToContents(5); // Min.
252 m_ui.SettingsListView->resizeColumnToContents(6); // Max.
253 m_ui.SettingsListView->resizeColumnToContents(7); // Options.
254
255 // Try to restore old window positioning.
256 adjustSize();
257
258 // UI connections...
259 QObject::connect(m_ui.DisplayNameLineEdit,
260 SIGNAL(textChanged(const QString&)),
261 SLOT(nameChanged(const QString&)));
262 QObject::connect(m_ui.MidiInCheckBox,
263 SIGNAL(stateChanged(int)),
264 SLOT(settingsChanged()));
265 QObject::connect(m_ui.MidiDriverComboBox,
266 SIGNAL(activated(const QString&)),
267 SLOT(midiDriverChanged(const QString&)));
268 QObject::connect(m_ui.MidiDeviceComboBox,
269 SIGNAL(editTextChanged(const QString&)),
270 SLOT(settingsChanged()));
271 QObject::connect(m_ui.MidiChannelsSpinBox,
272 SIGNAL(valueChanged(int)),
273 SLOT(settingsChanged()));
274 QObject::connect(m_ui.VerboseCheckBox,
275 SIGNAL(stateChanged(int)),
276 SLOT(settingsChanged()));
277 QObject::connect(m_ui.MidiDumpCheckBox,
278 SIGNAL(stateChanged(int)),
279 SLOT(settingsChanged()));
280 QObject::connect(m_ui.MidiNameComboBox,
281 SIGNAL(editTextChanged(const QString&)),
282 SLOT(settingsChanged()));
283 QObject::connect(m_ui.MidiBankSelectComboBox,
284 SIGNAL(activated(const QString&)),
285 SLOT(settingsChanged()));
286 QObject::connect(m_ui.AudioDriverComboBox,
287 SIGNAL(activated(const QString&)),
288 SLOT(audioDriverChanged(const QString&)));
289 QObject::connect(m_ui.AudioDeviceComboBox,
290 SIGNAL(editTextChanged(const QString&)),
291 SLOT(settingsChanged()));
292 QObject::connect(m_ui.SampleFormatComboBox,
293 SIGNAL(activated(int)),
294 SLOT(settingsChanged()));
295 QObject::connect(m_ui.SampleRateComboBox,
296 SIGNAL(editTextChanged(const QString&)),
297 SLOT(settingsChanged()));
298 QObject::connect(m_ui.AudioBufSizeComboBox,
299 SIGNAL(editTextChanged(const QString&)),
300 SLOT(settingsChanged()));
301 QObject::connect(m_ui.AudioBufCountComboBox,
302 SIGNAL(editTextChanged(const QString&)),
303 SLOT(settingsChanged()));
304 QObject::connect(m_ui.AudioChannelsSpinBox,
305 SIGNAL(valueChanged(int)),
306 SLOT(settingsChanged()));
307 QObject::connect(m_ui.AudioGroupsSpinBox,
308 SIGNAL(valueChanged(int)),
309 SLOT(settingsChanged()));
310 QObject::connect(m_ui.PolyphonySpinBox,
311 SIGNAL(valueChanged(int)),
312 SLOT(settingsChanged()));
313 QObject::connect(m_ui.JackAutoConnectCheckBox,
314 SIGNAL(stateChanged(int)),
315 SLOT(settingsChanged()));
316 QObject::connect(m_ui.JackMultiCheckBox,
317 SIGNAL(stateChanged(int)),
318 SLOT(settingsChanged()));
319 QObject::connect(m_ui.JackNameComboBox,
320 SIGNAL(editTextChanged(const QString&)),
321 SLOT(settingsChanged()));
322 QObject::connect(m_ui.SoundFontListView,
323 SIGNAL(customContextMenuRequested(const QPoint&)),
324 SLOT(contextMenuRequested(const QPoint&)));
325 QObject::connect(m_ui.SoundFontListView,
326 SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
327 SLOT(stabilizeForm()));
328 QObject::connect(m_ui.SoundFontOpenPushButton,
329 SIGNAL(clicked()),
330 SLOT(openSoundFont()));
331 QObject::connect(m_ui.SoundFontRemovePushButton,
332 SIGNAL(clicked()),
333 SLOT(removeSoundFont()));
334 QObject::connect(m_ui.SoundFontEditPushButton,
335 SIGNAL(clicked()),
336 SLOT(editSoundFont()));
337 QObject::connect(m_ui.SoundFontMoveUpPushButton,
338 SIGNAL(clicked()),
339 SLOT(moveUpSoundFont()));
340 QObject::connect(m_ui.SoundFontMoveDownPushButton,
341 SIGNAL(clicked()),
342 SLOT(moveDownSoundFont()));
343 QObject::connect(m_ui.SoundFontListView->itemDelegate(),
344 SIGNAL(commitData(QWidget*)),
345 SLOT(itemRenamed()));
346 QObject::connect(m_ui.DialogButtonBox,
347 SIGNAL(accepted()),
348 SLOT(accept()));
349 QObject::connect(m_ui.DialogButtonBox,
350 SIGNAL(rejected()),
351 SLOT(reject()));
352 }
353
354
355 // Destructor.
~qsynthSetupForm(void)356 qsynthSetupForm::~qsynthSetupForm (void)
357 {
358 // Delete pixmaps.
359 delete m_pXpmSoundFont;
360 }
361
362
363 // A combo-box text item setter helper.
setComboBoxCurrentText(QComboBox * pComboBox,const QString & sText) const364 void qsynthSetupForm::setComboBoxCurrentText (
365 QComboBox *pComboBox, const QString& sText ) const
366 {
367 if (pComboBox->isEditable()) {
368 pComboBox->setEditText(sText);
369 } else {
370 int iIndex = pComboBox->findText(sText);
371 if (iIndex < 0) {
372 iIndex = 0;
373 if (!sText.isEmpty())
374 pComboBox->insertItem(0, sText);
375 }
376 pComboBox->setCurrentIndex(iIndex);
377 }
378 }
379
380
381 // Populate (setup) dialog controls from settings descriptors.
setup(qsynthOptions * pOptions,qsynthEngine * pEngine,bool bNew)382 void qsynthSetupForm::setup ( qsynthOptions *pOptions, qsynthEngine *pEngine, bool bNew )
383 {
384 // Check this first.
385 if (pOptions == nullptr || pEngine == nullptr)
386 return;
387
388 // Set reference descriptors.
389 m_pOptions = pOptions;
390 m_pSetup = pEngine->setup();
391
392 // Update caption.
393 setWindowTitle(pEngine->name() + " - " + tr("Setup"));
394
395 // Start clean?
396 m_iDirtyCount = 0;
397 if (bNew) {
398 m_pSetup->realize();
399 ++m_iDirtyCount;
400 }
401 // Avoid nested changes.
402 ++m_iDirtySetup;
403
404 // Display name.
405 m_ui.DisplayNameLineEdit->setText(m_pSetup->sDisplayName);
406
407 // Load Setttings view...
408 qsynth_settings_data data;
409 // Set data context.
410 data.pSetup = m_pSetup;
411 data.pListView = m_ui.SettingsListView;
412 data.pListItem = nullptr;
413 // And start filling it in...
414 ::fluid_settings_foreach(m_pSetup->fluid_settings(), &data, qsynth_settings_foreach);
415
416 // Midi Driver combobox options;
417 // check if intended MIDI driver is available.
418 data.options.clear();
419 char midi_driver[] = "midi.driver";
420 ::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
421 midi_driver, &data, qsynth_settings_foreach_option);
422 m_ui.MidiDriverComboBox->clear();
423 if (!data.options.contains(m_pSetup->sMidiDriver))
424 data.options.append(m_pSetup->sMidiDriver);
425 m_ui.MidiDriverComboBox->addItems(data.options);
426
427 // Audio Driver combobox options.
428 // check if intended Audio driver is available.
429 data.options.clear();
430 char audio_driver[] = "audio.driver";
431 ::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
432 audio_driver, &data, qsynth_settings_foreach_option);
433 m_ui.AudioDriverComboBox->clear();
434 if (!data.options.contains(m_pSetup->sAudioDriver))
435 data.options.append(m_pSetup->sAudioDriver);
436 m_ui.AudioDriverComboBox->addItems(data.options);
437
438 // Sample Format combobox options.
439 data.options.clear();
440 char audio_sample_fmt[] = "audio.sample-format";
441 ::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
442 audio_sample_fmt, &data, qsynth_settings_foreach_option);
443 m_ui.SampleFormatComboBox->clear();
444 m_ui.SampleFormatComboBox->addItems(data.options);
445
446 // Midi bank select combobox options
447 data.options.clear();
448 char midi_bank_select[] = "synth.midi-bank-select";
449 ::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
450 midi_bank_select, &data, qsynth_settings_foreach_option);
451 m_ui.MidiBankSelectComboBox->clear();
452 m_ui.MidiBankSelectComboBox->addItems(data.options);
453
454 // Midi settings...
455 m_ui.MidiInCheckBox->setChecked(m_pSetup->bMidiIn);
456 setComboBoxCurrentText(m_ui.MidiDriverComboBox,
457 m_pSetup->sMidiDriver);
458 updateMidiDevices(m_pSetup->sMidiDriver);
459
460 setComboBoxCurrentText(m_ui.MidiDeviceComboBox,
461 m_pSetup->sMidiDevice);
462 setComboBoxCurrentText(m_ui.MidiBankSelectComboBox,
463 m_pSetup->sMidiBankSelect);
464 m_ui.MidiChannelsSpinBox->setValue(m_pSetup->iMidiChannels);
465 m_ui.MidiDumpCheckBox->setChecked(m_pSetup->bMidiDump);
466 m_ui.VerboseCheckBox->setChecked(m_pSetup->bVerbose);
467 // ALSA client identifier.
468 m_ui.MidiNameComboBox->addItem(m_pSetup->sDisplayName);
469 setComboBoxCurrentText(m_ui.MidiNameComboBox,
470 bNew ? m_pSetup->sDisplayName : m_pSetup->sMidiName);
471
472 // Audio settings...
473 setComboBoxCurrentText(m_ui.AudioDriverComboBox,
474 m_pSetup->sAudioDriver);
475 updateAudioDevices(m_pSetup->sAudioDriver);
476
477 setComboBoxCurrentText(m_ui.AudioDeviceComboBox,
478 m_pSetup->sAudioDevice);
479 setComboBoxCurrentText(m_ui.SampleFormatComboBox,
480 m_pSetup->sSampleFormat);
481 setComboBoxCurrentText(m_ui.SampleRateComboBox,
482 QString::number(m_pSetup->fSampleRate));
483 setComboBoxCurrentText(m_ui.AudioBufSizeComboBox,
484 QString::number(m_pSetup->iAudioBufSize));
485 setComboBoxCurrentText(m_ui.AudioBufCountComboBox,
486 QString::number(m_pSetup->iAudioBufCount));
487 m_ui.AudioChannelsSpinBox->setValue(m_pSetup->iAudioChannels);
488 m_ui.AudioGroupsSpinBox->setValue(m_pSetup->iAudioGroups);
489 m_ui.PolyphonySpinBox->setValue(m_pSetup->iPolyphony);
490 m_ui.JackMultiCheckBox->setChecked(m_pSetup->bJackMulti);
491 m_ui.JackAutoConnectCheckBox->setChecked(m_pSetup->bJackAutoConnect);
492 // JACK client name...
493 QString sJackName;
494 if (!m_pSetup->sDisplayName.contains(QSYNTH_TITLE))
495 sJackName += QSYNTH_TITLE "_";
496 sJackName += m_pSetup->sDisplayName;
497 m_ui.JackNameComboBox->addItem(sJackName);
498 setComboBoxCurrentText(m_ui.JackNameComboBox,
499 bNew ? sJackName : m_pSetup->sJackName);
500
501 // Load the soundfonts view.
502 m_ui.SoundFontListView->clear();
503 m_ui.SoundFontListView->setUpdatesEnabled(false);
504 QTreeWidgetItem *pItem = nullptr;
505 if (pEngine->pSynth) {
506 // Load soundfont view from actual synth stack...
507 int cSoundFonts = ::fluid_synth_sfcount(pEngine->pSynth);
508 for (int i = cSoundFonts - 1; i >= 0; i--) {
509 fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(pEngine->pSynth, i);
510 if (pSoundFont) {
511 pItem = new QTreeWidgetItem(m_ui.SoundFontListView, pItem);
512 if (pItem) {
513 #ifdef CONFIG_FLUID_SFONT_GET_ID
514 const int iSFID = ::fluid_sfont_get_id(pSoundFont);
515 #else
516 const int iSFID = pSoundFont->id;
517 #endif
518 #ifdef CONFIG_FLUID_SFONT_GET_NAME
519 const QString sSFName = ::fluid_sfont_get_name(pSoundFont);
520 #else
521 const QString sSFName = pSoundFont->get_name(pSoundFont);
522 #endif
523 pItem->setIcon(0, *m_pXpmSoundFont);
524 pItem->setText(0, QString::number(iSFID));
525 pItem->setText(1, sSFName);
526 #ifdef CONFIG_FLUID_BANK_OFFSET
527 pItem->setText(2, QString::number(
528 ::fluid_synth_get_bank_offset(pEngine->pSynth, iSFID)));
529 pItem->setFlags(pItem->flags() | Qt::ItemIsEditable);
530 #endif
531 }
532 }
533 }
534 } else {
535 // Load soundfont view from configuration setup list...
536 int i = 0;
537 QStringListIterator iter(m_pSetup->soundfonts);
538 while (iter.hasNext()) {
539 pItem = new QTreeWidgetItem(m_ui.SoundFontListView, pItem);
540 if (pItem) {
541 pItem->setIcon(0, *m_pXpmSoundFont);
542 pItem->setText(0, QString::number(i));
543 pItem->setText(1, iter.next());
544 #ifdef CONFIG_FLUID_BANK_OFFSET
545 pItem->setText(2, m_pSetup->bankoffsets[i]);
546 pItem->setFlags(pItem->flags() | Qt::ItemIsEditable);
547 #endif
548 }
549 ++i;
550 }
551 }
552 m_ui.SoundFontListView->setUpdatesEnabled(true);
553 m_ui.SoundFontListView->update();
554
555 // Done.
556 m_iDirtySetup--;
557 stabilizeForm();
558 }
559
560
561 // Accept settings (OK button slot).
accept(void)562 void qsynthSetupForm::accept (void)
563 {
564 if (m_iDirtyCount > 0) {
565 // Save the soundfont view.
566 m_pSetup->soundfonts.clear();
567 m_pSetup->bankoffsets.clear();
568 const int iItemCount = m_ui.SoundFontListView->topLevelItemCount();
569 for (int i = 0; i < iItemCount; ++i) {
570 QTreeWidgetItem *pItem = m_ui.SoundFontListView->topLevelItem(i);
571 m_pSetup->soundfonts.append(pItem->text(1));
572 m_pSetup->bankoffsets.append(pItem->text(2));
573 }
574 // Will we have a setup renaming?
575 m_pSetup->sDisplayName = m_ui.DisplayNameLineEdit->text();
576 // Midi settings...
577 m_pSetup->bMidiIn = m_ui.MidiInCheckBox->isChecked();
578 m_pSetup->sMidiDriver = m_ui.MidiDriverComboBox->currentText();
579 m_pSetup->sMidiDevice = m_ui.MidiDeviceComboBox->currentText();
580 m_pSetup->iMidiChannels = m_ui.MidiChannelsSpinBox->value();
581 m_pSetup->sMidiBankSelect = m_ui.MidiBankSelectComboBox->currentText();
582 m_pSetup->bMidiDump = m_ui.MidiDumpCheckBox->isChecked();
583 m_pSetup->bVerbose = m_ui.VerboseCheckBox->isChecked();
584 m_pSetup->sMidiName = m_ui.MidiNameComboBox->currentText();
585 // Audio settings...
586 m_pSetup->sAudioDriver = m_ui.AudioDriverComboBox->currentText();
587 m_pSetup->sAudioDevice = m_ui.AudioDeviceComboBox->currentText();
588 m_pSetup->sSampleFormat = m_ui.SampleFormatComboBox->currentText();
589 m_pSetup->fSampleRate = m_ui.SampleRateComboBox->currentText().toDouble();
590 m_pSetup->iAudioBufSize = m_ui.AudioBufSizeComboBox->currentText().toInt();
591 m_pSetup->iAudioBufCount = m_ui.AudioBufCountComboBox->currentText().toInt();
592 m_pSetup->iAudioChannels = m_ui.AudioChannelsSpinBox->value();
593 m_pSetup->iAudioGroups = m_ui.AudioGroupsSpinBox->value();
594 m_pSetup->iPolyphony = m_ui.PolyphonySpinBox->value();
595 m_pSetup->bJackMulti = m_ui.JackMultiCheckBox->isChecked();
596 m_pSetup->bJackAutoConnect = m_ui.JackAutoConnectCheckBox->isChecked();
597 m_pSetup->sJackName = m_ui.JackNameComboBox->currentText();
598 // Reset dirty flag.
599 m_iDirtyCount = 0;
600 }
601
602 // Just go with dialog acceptance.
603 QDialog::accept();
604 }
605
606
607 // Reject settings (Cancel button slot).
reject(void)608 void qsynthSetupForm::reject (void)
609 {
610 bool bReject = true;
611
612 // Check if there's any pending changes...
613 if (m_iDirtyCount > 0) {
614 switch (QMessageBox::warning(this,
615 tr("Warning"),
616 tr("Some settings have been changed.") + "\n\n" +
617 tr("Do you want to apply the changes?"),
618 QMessageBox::Apply | QMessageBox::Discard | QMessageBox::Cancel)) {
619 case QMessageBox::Apply:
620 accept();
621 return;
622 case QMessageBox::Discard:
623 break;
624 default: // Cancel.
625 bReject = false;
626 }
627 }
628
629 if (bReject)
630 QDialog::reject();
631 }
632
633
634 // Dirty up engine display name.
nameChanged(const QString &)635 void qsynthSetupForm::nameChanged ( const QString& )
636 {
637 if (m_iDirtySetup > 0)
638 return;
639
640 int iItem;
641 const QString& sDisplayName = m_ui.DisplayNameLineEdit->text();
642
643 iItem = m_ui.MidiNameComboBox->findText(sDisplayName);
644 if (iItem >= 0) {
645 m_ui.MidiNameComboBox->removeItem(iItem);
646 m_ui.MidiNameComboBox->insertItem(0, sDisplayName);
647 }
648
649 iItem = m_ui.JackNameComboBox->findText(sDisplayName);
650 if (iItem >= 0) {
651 m_ui.JackNameComboBox->removeItem(iItem);
652 m_ui.JackNameComboBox->insertItem(0, sDisplayName);
653 }
654
655 settingsChanged();
656 }
657
658
659 // Fill MIDI device options.
updateMidiDevices(const QString & sMidiDriver)660 void qsynthSetupForm::updateMidiDevices ( const QString& sMidiDriver )
661 {
662 qsynth_settings_data data;
663 data.pSetup = m_pSetup;
664 data.pListView = nullptr;
665 data.pListItem = nullptr;
666
667 // MIDI Device combobox options;
668 QString sOldText = m_ui.MidiDeviceComboBox->currentText();
669 m_ui.MidiDeviceComboBox->clear();
670 QString sName = "midi." + sMidiDriver + ".device";
671 data.options.clear();
672 ::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
673 sName.toLocal8Bit().data(), &data, qsynth_settings_foreach_option);
674
675 m_ui.MidiDeviceComboBox->addItems(data.options);
676 }
677
678
midiDriverChanged(const QString & sMidiDriver)679 void qsynthSetupForm::midiDriverChanged ( const QString& sMidiDriver )
680 {
681 if (m_iDirtySetup > 0)
682 return;
683
684 QString sMidiDevice = m_ui.MidiDeviceComboBox->currentText();
685 updateMidiDevices(sMidiDriver);
686 setComboBoxCurrentText(m_ui.MidiDeviceComboBox, sMidiDevice);
687
688 settingsChanged();
689 }
690
691
692 // Fill audio device options.
updateAudioDevices(const QString & sAudioDriver)693 void qsynthSetupForm::updateAudioDevices ( const QString& sAudioDriver )
694 {
695 qsynth_settings_data data;
696 data.pSetup = m_pSetup;
697 data.pListView = nullptr;
698 data.pListItem = nullptr;
699
700 // Audio Device combobox options;
701 QString sOldText = m_ui.AudioDeviceComboBox->currentText();
702 m_ui.AudioDeviceComboBox->clear();
703 QString sName = "audio." + sAudioDriver + ".device";
704 data.options.clear();
705 ::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
706 sName.toLocal8Bit().data(), &data, qsynth_settings_foreach_option);
707
708 m_ui.AudioDeviceComboBox->addItems(data.options);
709 }
710
audioDriverChanged(const QString & sAudioDriver)711 void qsynthSetupForm::audioDriverChanged ( const QString& sAudioDriver )
712 {
713 if (m_iDirtySetup > 0)
714 return;
715
716 const QString& sAudioDevice = m_ui.AudioDeviceComboBox->currentText();
717 updateAudioDevices(sAudioDriver);
718 setComboBoxCurrentText(m_ui.AudioDeviceComboBox, sAudioDevice);
719
720 settingsChanged();
721 }
722
723
724 // Dirty up settings.
settingsChanged(void)725 void qsynthSetupForm::settingsChanged (void)
726 {
727 if (m_iDirtySetup > 0)
728 return;
729
730 m_iDirtyCount++;
731 stabilizeForm();
732 }
733
734
735 // Stabilize current form state.
stabilizeForm(void)736 void qsynthSetupForm::stabilizeForm (void)
737 {
738 bool bEnabled = m_ui.MidiInCheckBox->isChecked();
739
740 const bool bAlsaEnabled = (m_ui.MidiDriverComboBox->currentText() == "alsa_seq");
741 const bool bCoreMidiEnabled = (m_ui.MidiDriverComboBox->currentText() == "coremidi");
742 m_ui.MidiDriverTextLabel->setEnabled(bEnabled);
743 m_ui.MidiDriverComboBox->setEnabled(bEnabled);
744 m_ui.MidiDeviceTextLabel->setEnabled(bEnabled && !bAlsaEnabled);
745 m_ui.MidiDeviceComboBox->setEnabled(bEnabled && !bAlsaEnabled);
746 m_ui.MidiChannelsTextLabel->setEnabled(bEnabled);
747 m_ui.MidiChannelsSpinBox->setEnabled(bEnabled);
748 m_ui.MidiDumpCheckBox->setEnabled(bEnabled);
749 m_ui.VerboseCheckBox->setEnabled(bEnabled);
750 m_ui.MidiBankSelectTextLabel->setEnabled(bEnabled);
751 m_ui.MidiBankSelectComboBox->setEnabled(bEnabled);
752 m_ui.MidiNameTextLabel->setEnabled(bEnabled && (bAlsaEnabled | bCoreMidiEnabled));
753 m_ui.MidiNameComboBox->setEnabled(bEnabled && (bAlsaEnabled | bCoreMidiEnabled));
754
755 const bool bJackEnabled = (m_ui.AudioDriverComboBox->currentText() == "jack");
756 const bool bJackMultiEnabled = m_ui.JackMultiCheckBox->isChecked();
757 m_ui.AudioDeviceTextLabel->setEnabled(!bJackEnabled);
758 m_ui.AudioDeviceComboBox->setEnabled(!bJackEnabled);
759 m_ui.JackMultiCheckBox->setEnabled(bJackEnabled);
760 m_ui.JackAutoConnectCheckBox->setEnabled(bJackEnabled);
761 m_ui.JackNameTextLabel->setEnabled(bJackEnabled);
762 m_ui.JackNameComboBox->setEnabled(bJackEnabled);
763 if (bJackEnabled) {
764 m_ui.AudioChannelsTextLabel->setEnabled(bJackMultiEnabled);
765 m_ui.AudioChannelsSpinBox->setEnabled(bJackMultiEnabled);
766 m_ui.AudioChannelsSpinBox->setSingleStep(2);
767 m_ui.AudioChannelsSpinBox->setMinimum(2);
768 m_ui.AudioGroupsTextLabel->setEnabled(bJackMultiEnabled);
769 m_ui.AudioGroupsSpinBox->setEnabled(bJackMultiEnabled);
770 // m_ui.AudioGroupsSpinBox->setSingleStep(2);
771 // m_ui.AudioGroupsSpinBox->setMinimum(2);
772 }
773
774 m_ui.SoundFontOpenPushButton->setEnabled(true);
775 QTreeWidgetItem *pSelectedItem = m_ui.SoundFontListView->currentItem();
776 if (pSelectedItem) {
777 const int iItem = m_ui.SoundFontListView->indexOfTopLevelItem(pSelectedItem);
778 const int iItemCount = m_ui.SoundFontListView->topLevelItemCount();
779 m_ui.SoundFontEditPushButton->setEnabled(
780 pSelectedItem->flags() & Qt::ItemIsEditable);
781 m_ui.SoundFontRemovePushButton->setEnabled(true);
782 m_ui.SoundFontMoveUpPushButton->setEnabled(iItem > 0);
783 m_ui.SoundFontMoveDownPushButton->setEnabled(iItem < iItemCount - 1);
784 } else {
785 m_ui.SoundFontRemovePushButton->setEnabled(false);
786 m_ui.SoundFontEditPushButton->setEnabled(false);
787 m_ui.SoundFontMoveUpPushButton->setEnabled(false);
788 m_ui.SoundFontMoveDownPushButton->setEnabled(false);
789 }
790
791 bEnabled = (m_iDirtyCount > 0);
792 if (bEnabled && m_pSetup) {
793 const QString& sDisplayName = m_ui.DisplayNameLineEdit->text();
794 if (sDisplayName != m_pSetup->sDisplayName) {
795 bEnabled = (!m_pOptions->engines.contains(sDisplayName));
796 if (bEnabled)
797 bEnabled = (sDisplayName != (m_pOptions->defaultSetup())->sDisplayName);
798 }
799 }
800 m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(bEnabled);
801 }
802
803
804 // Soundfont view context menu handler.
contextMenuRequested(const QPoint & pos)805 void qsynthSetupForm::contextMenuRequested ( const QPoint& pos )
806 {
807 int iItem = 0;
808 int iItemCount = 0;
809 QTreeWidgetItem *pItem = m_ui.SoundFontListView->itemAt(pos);
810 if (pItem == nullptr)
811 pItem = m_ui.SoundFontListView->currentItem();
812 if (pItem) {
813 iItem = m_ui.SoundFontListView->indexOfTopLevelItem(pItem);
814 iItemCount = m_ui.SoundFontListView->topLevelItemCount();
815 }
816
817 // Build the soundfont context menu...
818 QMenu menu(this);
819 QAction *pAction;
820
821 pAction = menu.addAction(
822 QIcon(":/images/open1.png"),
823 tr("Open..."), this, SLOT(openSoundFont()));
824 menu.addSeparator();
825 bool bEnabled = (pItem != nullptr);
826 pAction = menu.addAction(
827 QIcon(":/images/edit1.png"),
828 tr("Edit"), this, SLOT(editSoundFont()));
829 pAction->setEnabled(
830 (bEnabled && (pItem->flags() & Qt::ItemIsEditable)));
831 pAction = menu.addAction(
832 QIcon(":/images/remove1.png"),
833 tr("Remove"), this, SLOT(removeSoundFont()));
834 pAction->setEnabled(bEnabled);
835 menu.addSeparator();
836 pAction = menu.addAction(
837 QIcon(":/images/up1.png"),
838 tr("Move Up"), this, SLOT(moveUpSoundFont()));
839 pAction->setEnabled(bEnabled && iItem > 0);
840 pAction = menu.addAction(
841 QIcon(":/images/down1.png"),
842 tr("Move Down"), this, SLOT(moveDownSoundFont()));
843 pAction->setEnabled(bEnabled && iItem < iItemCount - 1);
844
845 menu.exec((m_ui.SoundFontListView->viewport())->mapToGlobal(pos));
846 }
847
848
849 // Refresh the soundfont view ids.
refreshSoundFonts(void)850 void qsynthSetupForm::refreshSoundFonts (void)
851 {
852 m_ui.SoundFontListView->setUpdatesEnabled(false);
853 const int iItemCount = m_ui.SoundFontListView->topLevelItemCount();
854 for (int i = 0; i < iItemCount; ++i) {
855 QTreeWidgetItem *pItem = m_ui.SoundFontListView->topLevelItem(i);
856 pItem->setText(0, QString::number(i + 1));
857 }
858 m_ui.SoundFontListView->setUpdatesEnabled(true);
859 m_ui.SoundFontListView->update();
860 }
861
862
863 // Browse for a soundfont file on the filesystem.
openSoundFont(void)864 void qsynthSetupForm::openSoundFont (void)
865 {
866 QStringList soundfonts = QFileDialog::getOpenFileNames(this,
867 tr("Soundfont files"), m_pOptions->sSoundFontDir,
868 tr("Soundfont files") + " (*.sf2 *.SF2 *.sf3 *.SF3)");
869
870 QTreeWidgetItem *pItem = nullptr;
871
872 // For avery selected soundfont to load...
873 QStringListIterator iter(soundfonts);
874 while (iter.hasNext()) {
875 const QString& sSoundFont = iter.next();
876 // Is it a soundfont file...
877 if (::fluid_is_soundfont(sSoundFont.toLocal8Bit().data())) {
878 // Check if not already there...
879 if (!m_ui.SoundFontListView->findItems(
880 sSoundFont, Qt::MatchExactly, 1).isEmpty() &&
881 QMessageBox::warning(this,
882 tr("Warning"),
883 tr("Soundfont file already on list") + ":\n\n" +
884 "\"" + sSoundFont + "\"\n\n" +
885 tr("Add anyway?"),
886 QMessageBox::Ok | QMessageBox::Cancel)
887 == QMessageBox::Cancel) {
888 continue;
889 }
890 // Start inserting in the current selected or last item...
891 if (pItem == nullptr) {
892 pItem = m_ui.SoundFontListView->currentItem();
893 if (pItem)
894 pItem->setSelected(false);
895 }
896 // New item on the block :-)
897 pItem = new QTreeWidgetItem(m_ui.SoundFontListView, pItem);
898 if (pItem) {
899 pItem->setIcon(0, *m_pXpmSoundFont);
900 pItem->setText(1, sSoundFont);
901 #ifdef CONFIG_FLUID_BANK_OFFSET
902 pItem->setText(2, "0");
903 pItem->setFlags(pItem->flags() | Qt::ItemIsEditable);
904 #endif
905 pItem->setSelected(true);
906 m_ui.SoundFontListView->setCurrentItem(pItem);
907 m_pOptions->sSoundFontDir = QFileInfo(sSoundFont).dir().absolutePath();
908 ++m_iDirtyCount;
909 }
910 } else {
911 QMessageBox::critical(this,
912 tr("Error"),
913 tr("Failed to add soundfont file") + ":\n\n" +
914 "\"" + sSoundFont + "\"\n\n" +
915 tr("Please, check for a valid soundfont file."),
916 QMessageBox::Cancel);
917 }
918 }
919
920 refreshSoundFonts();
921 stabilizeForm();
922 }
923
924
925 // Edit current selected soundfont.
editSoundFont(void)926 void qsynthSetupForm::editSoundFont (void)
927 {
928 QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
929 if (pItem)
930 m_ui.SoundFontListView->editItem(pItem, 2);
931
932 stabilizeForm();
933 }
934
935
936 // Remove current selected soundfont.
removeSoundFont(void)937 void qsynthSetupForm::removeSoundFont (void)
938 {
939 QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
940 if (pItem) {
941 ++m_iDirtyCount;
942 delete pItem;
943 }
944
945 refreshSoundFonts();
946 stabilizeForm();
947 }
948
949
950 // Move current selected soundfont one position up.
moveUpSoundFont(void)951 void qsynthSetupForm::moveUpSoundFont (void)
952 {
953 QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
954 if (pItem) {
955 int iItem = m_ui.SoundFontListView->indexOfTopLevelItem(pItem);
956 if (iItem > 0) {
957 pItem->setSelected(false);
958 pItem = m_ui.SoundFontListView->takeTopLevelItem(iItem);
959 m_ui.SoundFontListView->insertTopLevelItem(iItem - 1, pItem);
960 pItem->setSelected(true);
961 m_ui.SoundFontListView->setCurrentItem(pItem);
962 ++m_iDirtyCount;
963 }
964 }
965
966 refreshSoundFonts();
967 stabilizeForm();
968 }
969
970
971 // Move current selected soundfont one position down.
moveDownSoundFont(void)972 void qsynthSetupForm::moveDownSoundFont (void)
973 {
974 QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
975 if (pItem) {
976 const int iItemCount = m_ui.SoundFontListView->topLevelItemCount();
977 int iItem = m_ui.SoundFontListView->indexOfTopLevelItem(pItem);
978 if (iItem < iItemCount - 1) {
979 pItem->setSelected(false);
980 pItem = m_ui.SoundFontListView->takeTopLevelItem(iItem);
981 m_ui.SoundFontListView->insertTopLevelItem(iItem + 1, pItem);
982 pItem->setSelected(true);
983 m_ui.SoundFontListView->setCurrentItem(pItem);
984 m_iDirtyCount++;
985 }
986 }
987
988 refreshSoundFonts();
989 stabilizeForm();
990 }
991
992
993 // Check soundfont bank offset edit.
itemRenamed(void)994 void qsynthSetupForm::itemRenamed (void)
995 {
996 QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
997 if (pItem) {
998 const int iBankOffset = pItem->text(2).toInt();
999 if (iBankOffset < 0 || iBankOffset > 16384)
1000 pItem->setText(2, QString::number(0));
1001 m_iDirtyCount++;
1002 }
1003
1004 stabilizeForm();
1005 }
1006
1007
1008 // end of qsynthSetupForm.cpp
1009