drumstick 2.11.2
C++ MIDI libraries using Qt objects, idioms, and style.
fluidsettingsdialog.cpp
Go to the documentation of this file.
1/*
2 Drumstick MIDI Sequencer C++ library
3 Copyright (C) 2006-2026, Pedro Lopez-Cabanillas <plcl@users.sf.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include <QDir>
20#include <QFileDialog>
21#include <QFileInfo>
22#include <QMessageBox>
23#include <QPushButton>
24#include <QStandardPaths>
25#include <QToolButton>
26#include <QToolTip>
27#include <QVersionNumber>
28
29#include "fluidsettingsdialog.h"
30#include "ui_fluidsettingsdialog.h"
33
38
39namespace drumstick { namespace widgets {
40
41const QString FluidSettingsDialog::QSTR_PREFERENCES = QStringLiteral("FluidSynth");
42const QString FluidSettingsDialog::QSTR_INSTRUMENTSDEFINITION = QStringLiteral("InstrumentsDefinition");
43const QString FluidSettingsDialog::QSTR_DATADIR = QStringLiteral("soundfonts");
44const QString FluidSettingsDialog::QSTR_DATADIR2 = QStringLiteral("sounds/sf2");
45const QString FluidSettingsDialog::QSTR_AUDIODRIVER = QStringLiteral("AudioDriver");
46const QString FluidSettingsDialog::QSTR_PERIODSIZE = QStringLiteral("PeriodSize");
47const QString FluidSettingsDialog::QSTR_PERIODS = QStringLiteral("Periods");
48const QString FluidSettingsDialog::QSTR_SAMPLERATE = QStringLiteral("SampleRate");
49const QString FluidSettingsDialog::QSTR_CHORUS = QStringLiteral("Chorus");
50const QString FluidSettingsDialog::QSTR_REVERB = QStringLiteral("Reverb");
51const QString FluidSettingsDialog::QSTR_GAIN = QStringLiteral("Gain");
52const QString FluidSettingsDialog::QSTR_POLYPHONY = QStringLiteral("Polyphony");
53const QString FluidSettingsDialog::QSTR_BUFFERTIME = QStringLiteral("BufferTime");
54const QString FluidSettingsDialog::QSTR_PULSEAUDIO = QStringLiteral("pulseaudio");
55const QString FluidSettingsDialog::QSTR_CHORUS_DEPTH = QStringLiteral("chorus_depth");
56const QString FluidSettingsDialog::QSTR_CHORUS_LEVEL = QStringLiteral("chorus_level");
57const QString FluidSettingsDialog::QSTR_CHORUS_NR = QStringLiteral("chorus_nr");
58const QString FluidSettingsDialog::QSTR_CHORUS_SPEED = QStringLiteral("chorus_speed");
59const QString FluidSettingsDialog::QSTR_REVERB_DAMP = QStringLiteral("reverb_damp");
60const QString FluidSettingsDialog::QSTR_REVERB_LEVEL = QStringLiteral("reverb_level");
61const QString FluidSettingsDialog::QSTR_REVERB_SIZE = QStringLiteral("reverb_size");
62const QString FluidSettingsDialog::QSTR_REVERB_WIDTH = QStringLiteral("reverb_width");
63
64FluidSettingsDialog::FluidSettingsDialog(QWidget *parent) :
65 QDialog(parent),
66 ui(new Ui::FluidSettingsDialog)
67{
68 ui->setupUi(this);
69 connect(ui->audioDriver, &QComboBox::currentTextChanged, this, &FluidSettingsDialog::audioDriverChanged);
70 connect(ui->bufferTime, QOverload<int>::of(&QSpinBox::valueChanged), this, &FluidSettingsDialog::bufferTimeChanged);
71 connect(ui->periodSize, QOverload<int>::of(&QSpinBox::valueChanged), this, &FluidSettingsDialog::bufferSizeChanged);
72 connect(ui->periods, QOverload<int>::of(&QSpinBox::valueChanged), this, &FluidSettingsDialog::bufferSizeChanged);
73 connect(ui->btnFile, &QToolButton::clicked, this, &FluidSettingsDialog::showFileDialog);
74 connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked,
75 this, &FluidSettingsDialog::restoreDefaults);
76 connect(ui->chorus_depth, &QAbstractSlider::valueChanged, this, [=](int val) {
77 QString v = QString::number(val / CHORUS_REVERB_VALUE_SCALE, 'f', 2);
78 ui->label_CDepth->setText(v);
79 });
80 connect(ui->chorus_level, &QAbstractSlider::valueChanged, this, [=](int val) {
81 QString v = QString::number(val / CHORUS_REVERB_VALUE_SCALE, 'f', 2);
82 ui->label_CLevel->setText(v);
83 });
84 connect(ui->chorus_nr, &QAbstractSlider::valueChanged, this, [=](int val) {
85 QString v = QString::number(val);
86 ui->label_CNR->setText(v);
87 });
88 connect(ui->chorus_speed, &QAbstractSlider::valueChanged, this, [=](int val) {
89 QString v = QString::number(val / CHORUS_REVERB_VALUE_SCALE, 'f', 2);
90 ui->label_CSpeed->setText(v);
91 });
92 connect(ui->reverb_damp, &QAbstractSlider::valueChanged, this, [=](int val) {
93 QString v = QString::number(val / CHORUS_REVERB_VALUE_SCALE, 'f', 2);
94 ui->label_RDamp->setText(v);
95 });
96 connect(ui->reverb_level, &QAbstractSlider::valueChanged, this, [=](int val) {
97 QString v = QString::number(val / CHORUS_REVERB_VALUE_SCALE, 'f', 2);
98 ui->label_RLevel->setText(v);
99 });
100 connect(ui->reverb_size, &QAbstractSlider::valueChanged, this, [=](int val) {
101 QString v = QString::number(val / CHORUS_REVERB_VALUE_SCALE, 'f', 2);
102 ui->label_RSize->setText(v);
103 });
104 connect(ui->reverb_width, &QAbstractSlider::valueChanged, this, [=](int val) {
105 QString v = QString::number(val / CHORUS_REVERB_VALUE_SCALE, 'f', 2);
106 ui->label_RWidth->setText(v);
107 });
108
110 m_driver = man->outputBackendByName("FluidSynth");
111 if (m_driver != nullptr) {
112 QVariant v = m_driver->property("audiodrivers");
113 if (v.isValid()) {
114 ui->audioDriver->blockSignals(true);
115 ui->audioDriver->clear();
116 ui->audioDriver->addItems(v.toStringList());
117 ui->audioDriver->blockSignals(false);
118 }
119 v = m_driver->property("soundfont");
120 if (v.isValid()) {
121 m_defSoundFont = v.toString();
122 }
123 }
124 ui->bufferTime->blockSignals(true);
125 ui->periodSize->blockSignals(true);
126 ui->periods->blockSignals(true);
127}
128
129FluidSettingsDialog::~FluidSettingsDialog()
130{
131 if (m_driver != nullptr) {
132 m_driver->close();
133 }
134 delete ui;
135}
136
137bool FluidSettingsDialog::checkRanges() const
138{
139 return ui->bufferTime->hasAcceptableInput() && ui->periodSize->hasAcceptableInput()
140 && ui->periods->hasAcceptableInput();
141}
142
143void FluidSettingsDialog::accept()
144{
145 if (checkRanges()) {
146 writeSettings();
147 if (m_driver != nullptr) {
148 QString title;
149 QVariant varStatus = m_driver->property("status");
150 if (varStatus.isValid()) {
151 title = varStatus.toBool() ? tr("FluidSynth Initialized") : tr("FluidSynth Initialization Failed");
152 QVariant varDiag = m_driver->property("diagnostics");
153 if (varDiag.isValid()) {
154 QString text = varDiag.toStringList().join(QChar::LineFeed).trimmed();
155 if (varStatus.toBool()) {
156 if (!text.isEmpty()) {
157 QMessageBox::information(this, title, text);
158 }
159 } else {
160 QMessageBox::critical(this, title, text);
161 return;
162 }
163 }
164 }
165 }
166 QDialog::accept();
167 }
168}
169
170void FluidSettingsDialog::showEvent(QShowEvent *event)
171{
172 readSettings();
173 event->accept();
174}
175
176QString FluidSettingsDialog::defaultAudioDriver() const
177{
178 const QString QSTR_DEFAULT_AUDIODRIVER =
179#if defined(Q_OS_WIN)
180 QLatin1String("wasapi");
181#elif defined(Q_OS_MACOS)
182 QLatin1String("coreaudio");
183#elif defined(Q_OS_LINUX)
184 QSTR_PULSEAUDIO;
185#else
186 QLatin1String("oss");
187#endif
188 return QSTR_DEFAULT_AUDIODRIVER;
189}
190
191void FluidSettingsDialog::chkDriverProperties(QSettings *settings)
192{
193 if (m_driver != nullptr) {
195 m_driver->close();
196 m_driver->initialize(settings);
197 m_driver->open(conn);
198
199 QVariant drivers = m_driver->property("audiodrivers");
200 if (drivers.isValid()) {
201 auto text = ui->audioDriver->currentText();
202 ui->audioDriver->blockSignals(true);
203 ui->audioDriver->clear();
204 ui->audioDriver->addItems(drivers.toStringList());
205 ui->audioDriver->setCurrentText(text);
206 ui->audioDriver->blockSignals(false);
207 }
208 ui->lblVersion->clear();
209 ui->lblVersion->setText(driverVersion());
210 QVariant varStatus = m_driver->property("status");
211 if (varStatus.isValid()) {
212 ui->lblStatus->clear();
213 ui->lblStatus->setText(varStatus.toBool() ? tr("Ready") : tr("Failed") );
214 ui->lblStatusIcon->setPixmap(varStatus.toBool() ? QPixmap(":/checked.png") : QPixmap(":/error.png") );
215 }
216 }
217}
218
219void FluidSettingsDialog::setWidgetTip(QWidget *w, const QString &tip)
220{
221 w->setToolTip(tip);
222 QToolTip::showText(w->parentWidget()->mapToGlobal(w->pos()), tip, this);
223}
224
225void drumstick::widgets::FluidSettingsDialog::initBuffer()
226{
227 if ((ui->audioDriver->currentText() == QSTR_PULSEAUDIO) && driverVersionLessThan_2_2_8()) {
228 int bufferTime = ui->bufferTime->value();
229 int minBufTime = ui->bufferTime->minimum();
230 if (bufferTime < minBufTime) {
231 bufferTime = minBufTime;
232 }
233 ui->bufferTime->setValue( bufferTime );
234 bufferTimeChanged( bufferTime );
235 } else {
236 bufferSizeChanged();
237 }
238}
239
240QString FluidSettingsDialog::driverVersion() const
241{
242 static QString result;
243 if (m_driver != nullptr && result.isEmpty()) {
244 QVariant varVersion = m_driver->property("libversion");
245 if (varVersion.isValid()) {
246 result = varVersion.toString();
247 }
248 }
249 return result;
250}
251
252bool FluidSettingsDialog::driverVersionLessThan_2_2_8()
253{
254 static const QVersionNumber check_2_2_8(2, 2, 8);
255 QVersionNumber driverV = QVersionNumber::fromString(driverVersion());
256 return driverV < check_2_2_8;
257}
258
259void FluidSettingsDialog::readSettings()
260{
261 SettingsFactory settings;
262 settings->beginGroup(QSTR_PREFERENCES);
263 ui->audioDriver->setCurrentText( settings->value(QSTR_AUDIODRIVER, defaultAudioDriver()).toString() );
264 ui->bufferTime->setValue( settings->value(QSTR_BUFFERTIME, DEFAULT_BUFFERTIME).toInt() );
265 ui->periodSize->setValue( settings->value(QSTR_PERIODSIZE, DEFAULT_PERIODSIZE).toInt() );
266 ui->periods->setValue( settings->value(QSTR_PERIODS, DEFAULT_PERIODS).toInt() );
267 ui->sampleRate->setCurrentText(settings->value(QSTR_SAMPLERATE, DEFAULT_SAMPLERATE).toString());
268 ui->gain->setValue(
269 settings
270 ->value(QSTR_GAIN, m_driver ? m_driver->property("defaultGain").toReal() : DEFAULT_GAIN)
271 .toDouble());
272 ui->polyphony->setValue(settings->value(QSTR_POLYPHONY, DEFAULT_POLYPHONY).toInt());
273 ui->soundFont->setText( settings->value(QSTR_INSTRUMENTSDEFINITION, m_defSoundFont).toString() );
274
275 ui->chorus_depth->setValue(settings->value(QSTR_CHORUS_DEPTH, DEFAULT_CHORUS_DEPTH).toDouble()
276 * CHORUS_REVERB_VALUE_SCALE);
277 ui->chorus_level->setValue(settings->value(QSTR_CHORUS_LEVEL, DEFAULT_CHORUS_LEVEL).toDouble()
278 * CHORUS_REVERB_VALUE_SCALE);
279 ui->chorus_nr->setValue(settings->value(QSTR_CHORUS_NR, DEFAULT_CHORUS_NR).toInt());
280 ui->chorus_speed->setValue(settings->value(QSTR_CHORUS_SPEED, DEFAULT_CHORUS_SPEED).toDouble()
281 * CHORUS_REVERB_VALUE_SCALE);
282
283 ui->reverb_damp->setValue(settings->value(QSTR_REVERB_DAMP, DEFAULT_REVERB_DAMP).toDouble()
284 * CHORUS_REVERB_VALUE_SCALE);
285 ui->reverb_level->setValue(settings->value(QSTR_REVERB_LEVEL, DEFAULT_REVERB_LEVEL).toDouble()
286 * CHORUS_REVERB_VALUE_SCALE);
287 ui->reverb_size->setValue(settings->value(QSTR_REVERB_SIZE, DEFAULT_REVERB_SIZE).toDouble()
288 * CHORUS_REVERB_VALUE_SCALE);
289 ui->reverb_width->setValue(settings->value(QSTR_REVERB_WIDTH, DEFAULT_REVERB_WIDTH).toDouble()
290 * CHORUS_REVERB_VALUE_SCALE);
291
292 ui->chorus->setChecked(settings->value(QSTR_CHORUS, DEFAULT_CHORUS).toInt() != 0);
293 ui->reverb->setChecked(settings->value(QSTR_REVERB, DEFAULT_REVERB).toInt() != 0);
294
295 settings->endGroup();
296
297 audioDriverChanged( ui->audioDriver->currentText() );
298 chkDriverProperties(settings.getQSettings());
299}
300
301void FluidSettingsDialog::writeSettings()
302{
303 SettingsFactory settings;
304 QString audioDriver;
305 QString soundFont(m_defSoundFont);
306 int bufferTime(DEFAULT_BUFFERTIME);
307 int periodSize(DEFAULT_PERIODSIZE);
308 int periods(DEFAULT_PERIODS);
309 double sampleRate(DEFAULT_SAMPLERATE);
310 int chorus(DEFAULT_CHORUS);
311 int reverb(DEFAULT_REVERB);
312 double gain(m_driver ? m_driver->property("defaultGain").toReal() : DEFAULT_GAIN);
313 int polyphony(DEFAULT_POLYPHONY);
314
315 double chorus_depth(DEFAULT_CHORUS_DEPTH);
316 double chorus_level(DEFAULT_CHORUS_LEVEL);
317 int chorus_nr(DEFAULT_CHORUS_NR);
318 double chorus_speed(DEFAULT_CHORUS_SPEED);
319
320 double reverb_damp(DEFAULT_REVERB_DAMP);
321 double reverb_level(DEFAULT_REVERB_LEVEL);
322 double reverb_size(DEFAULT_REVERB_SIZE);
323 double reverb_width(DEFAULT_REVERB_WIDTH);
324
325 audioDriver = ui->audioDriver->currentText();
326 if (audioDriver.isEmpty()) {
327 audioDriver = defaultAudioDriver();
328 }
329 soundFont = ui->soundFont->text();
330 bufferTime = ui->bufferTime->value();
331 periodSize = ui->periodSize->value();
332 periods = ui->periods->value();
333 sampleRate = ui->sampleRate->currentText().toDouble();
334 chorus = (ui->chorus->isChecked() ? 1 : 0);
335 reverb = (ui->reverb->isChecked() ? 1 : 0);
336 gain = ui->gain->value();
337 polyphony = ui->polyphony->value();
338
339 chorus_depth = ui->chorus_depth->value() / CHORUS_REVERB_VALUE_SCALE;
340 chorus_level = ui->chorus_level->value() / CHORUS_REVERB_VALUE_SCALE;
341 chorus_nr = ui->chorus_nr->value();
342 chorus_speed = ui->chorus_speed->value() / CHORUS_REVERB_VALUE_SCALE;
343 reverb_damp = ui->reverb_damp->value() / CHORUS_REVERB_VALUE_SCALE;
344 reverb_level = ui->reverb_level->value() / CHORUS_REVERB_VALUE_SCALE;
345 reverb_size = ui->reverb_size->value() / CHORUS_REVERB_VALUE_SCALE;
346 reverb_width = ui->reverb_width->value() / CHORUS_REVERB_VALUE_SCALE;
347
348 settings->beginGroup(QSTR_PREFERENCES);
349 settings->setValue(QSTR_INSTRUMENTSDEFINITION, soundFont);
350 settings->setValue(QSTR_AUDIODRIVER, audioDriver);
351 settings->setValue(QSTR_BUFFERTIME, bufferTime);
352 settings->setValue(QSTR_PERIODSIZE, periodSize);
353 settings->setValue(QSTR_PERIODS, periods);
354 settings->setValue(QSTR_SAMPLERATE, sampleRate);
355 settings->setValue(QSTR_CHORUS, chorus);
356 settings->setValue(QSTR_REVERB, reverb);
357 settings->setValue(QSTR_GAIN, gain);
358 settings->setValue(QSTR_POLYPHONY, polyphony);
359 settings->setValue(QSTR_CHORUS_DEPTH, chorus_depth);
360 settings->setValue(QSTR_CHORUS_LEVEL, chorus_level);
361 settings->setValue(QSTR_CHORUS_NR, chorus_nr);
362 settings->setValue(QSTR_CHORUS_SPEED, chorus_speed);
363 settings->setValue(QSTR_REVERB_DAMP, reverb_damp);
364 settings->setValue(QSTR_REVERB_LEVEL, reverb_level);
365 settings->setValue(QSTR_REVERB_SIZE, reverb_size);
366 settings->setValue(QSTR_REVERB_WIDTH, reverb_width);
367 settings->endGroup();
368 settings->sync();
369
370 chkDriverProperties(settings.getQSettings());
371}
372
373void FluidSettingsDialog::restoreDefaults()
374{
375 ui->audioDriver->setCurrentText(defaultAudioDriver());
376 ui->bufferTime->setValue(DEFAULT_BUFFERTIME);
377 ui->periodSize->setValue(DEFAULT_PERIODSIZE);
378 ui->periods->setValue(DEFAULT_PERIODS);
379 ui->sampleRate->setCurrentText(QString::number(DEFAULT_SAMPLERATE));
380 ui->gain->setValue(m_driver ? m_driver->property("defaultGain").toReal() : DEFAULT_GAIN);
381 ui->polyphony->setValue(DEFAULT_POLYPHONY);
382 ui->soundFont->setText(m_defSoundFont);
383 ui->chorus_depth->setValue(DEFAULT_CHORUS_DEPTH * CHORUS_REVERB_VALUE_SCALE);
384 ui->chorus_level->setValue(DEFAULT_CHORUS_LEVEL * CHORUS_REVERB_VALUE_SCALE);
385 ui->chorus_nr->setValue(DEFAULT_CHORUS_NR);
386 ui->chorus_speed->setValue(DEFAULT_CHORUS_SPEED * CHORUS_REVERB_VALUE_SCALE);
387 ui->reverb_damp->setValue(DEFAULT_REVERB_DAMP * CHORUS_REVERB_VALUE_SCALE);
388 ui->reverb_level->setValue(DEFAULT_REVERB_LEVEL * CHORUS_REVERB_VALUE_SCALE);
389 ui->reverb_size->setValue(DEFAULT_REVERB_SIZE * CHORUS_REVERB_VALUE_SCALE);
390 ui->reverb_width->setValue(DEFAULT_REVERB_WIDTH * CHORUS_REVERB_VALUE_SCALE);
391 ui->chorus->setChecked(DEFAULT_CHORUS != 0);
392 ui->reverb->setChecked(DEFAULT_REVERB != 0);
393 initBuffer();
394}
395
396void FluidSettingsDialog::showFileDialog()
397{
398 QDir dir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR, QStandardPaths::LocateDirectory));
399 if (!dir.exists()) {
400 dir = QDir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR2, QStandardPaths::LocateDirectory));
401 }
402 QStringList fileNames
403 = QFileDialog::getOpenFileNames(this,
404 tr("Select SoundFont"),
405 dir.absolutePath(),
406 tr("SoundFont Files (*.sf2 *.sf3 *.dls)"));
407 if (!fileNames.isEmpty()) {
408 ui->soundFont->setText(fileNames.join(';'));
409 }
410}
411
412void FluidSettingsDialog::audioDriverChanged(const QString &text)
413{
414 if ((text == QSTR_PULSEAUDIO) && driverVersionLessThan_2_2_8()) {
415 ui->bufferTime->setDisabled(false);
416 ui->bufferTime->blockSignals(false);
417 ui->periodSize->setDisabled(true);
418 ui->periodSize->blockSignals(true);
419 ui->periods->setVisible(false);
420 ui->periods->setDisabled(true);
421 ui->periods->blockSignals(true);
422 } else {
423 ui->bufferTime->setDisabled(true);
424 ui->bufferTime->blockSignals(true);
425 ui->periodSize->setDisabled(false);
426 ui->periodSize->blockSignals(false);
427 ui->periods->setVisible(true);
428 ui->periods->setDisabled(false);
429 ui->periods->blockSignals(false);
430 }
431 initBuffer();
432}
433
434void FluidSettingsDialog::bufferTimeChanged(int value)
435{
436 double rate = ui->sampleRate->currentText().toDouble();
437 int size = qRound( value * rate / 1000.0 );
438 ui->periodSize->setValue( size );
439 ui->periods->setValue( ui->periods->minimum() );
440}
441
442void FluidSettingsDialog::bufferSizeChanged()
443{
444 QString audioDriver = ui->audioDriver->currentText();
445 double rate = ui->sampleRate->currentText().toDouble();
446 int size = ui->periodSize->value();
447 if ((audioDriver != QSTR_PULSEAUDIO) || !driverVersionLessThan_2_2_8()) {
448 size *= ui->periods->value();
449 }
450 int ms = qRound( 1000.0 * size / rate );
451 ui->bufferTime->setValue(ms);
452}
453
454void FluidSettingsDialog::changeSoundFont(const QString& fileName)
455{
456 readSettings();
457 ui->soundFont->setText(fileName);
458 writeSettings();
459}
460
461} // namespace widgets
462} // namespace drumstick
BackendManager class declaration.
The QSettings class provides persistent platform-independent application settings.
The BackendManager class manages lists of dynamic and static backends for applications based on drums...
MIDIOutput * outputBackendByName(const QString name)
outputBackendByName
Declaration of the Fluidsynth configuration dialog.
BackendManager DRUMSTICK_RT_EXPORT * lastBackendManagerInstance()
lastBackendManagerInstance provides the latest BackendManager instance, or a new instance if needed.
QPair< QString, QVariant > MIDIConnection
MIDIConnection represents a connection identifier.
Drumstick Widgets library MIDI related widgets and functions.
Drumstick common.
SettingsFactory class declaration.