drumstick 2.9.1
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-2024, 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
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 auto sampleRateValidator = new QDoubleValidator(8000.0, 96000.0, 1, this);
77 sampleRateValidator->setNotation(QDoubleValidator::StandardNotation);
78 sampleRateValidator->setLocale(QLocale::c());
79 ui->sampleRate->setValidator(sampleRateValidator);
80 auto gainValidator = new QDoubleValidator(0.1, 10.0, 2, this);
81 gainValidator->setNotation(QDoubleValidator::StandardNotation);
82 gainValidator->setLocale(QLocale::c());
83 ui->gain->setValidator(gainValidator);
84 auto polyphonyValidator = new QIntValidator(1, 65535, this);
85 ui->polyphony->setValidator(polyphonyValidator);
86 connect(ui->chorus_depth, &QAbstractSlider::valueChanged, this, [=](int val) {
87 setWidgetTip(ui->chorus_depth, QString::number(val / 10.0));
88 });
89 connect(ui->chorus_level, &QAbstractSlider::valueChanged, this, [=](int val) {
90 setWidgetTip(ui->chorus_level, QString::number(val / 10.0));
91 });
92 connect(ui->chorus_nr, &QAbstractSlider::valueChanged, this, [=](int val) {
93 setWidgetTip(ui->chorus_nr, QString::number(val));
94 });
95 connect(ui->chorus_speed, &QAbstractSlider::valueChanged, this, [=](int val) {
96 setWidgetTip(ui->chorus_speed, QString::number(val / 10.0));
97 });
98
99 connect(ui->reverb_damp, &QAbstractSlider::valueChanged, this, [=](int val) {
100 setWidgetTip(ui->reverb_damp, QString::number(val / 10.0));
101 });
102 connect(ui->reverb_level, &QAbstractSlider::valueChanged, this, [=](int val) {
103 setWidgetTip(ui->reverb_level, QString::number(val / 10.0));
104 });
105 connect(ui->reverb_size, &QAbstractSlider::valueChanged, this, [=](int val) {
106 setWidgetTip(ui->reverb_size, QString::number(val / 10.0));
107 });
108 connect(ui->reverb_width, &QAbstractSlider::valueChanged, this, [=](int val) {
109 setWidgetTip(ui->reverb_width, QString::number(val / 10.0));
110 });
111
113 m_driver = man.outputBackendByName("FluidSynth");
114 if (m_driver != nullptr) {
115 QVariant v = m_driver->property("audiodrivers");
116 if (v.isValid()) {
117 ui->audioDriver->blockSignals(true);
118 ui->audioDriver->clear();
119 ui->audioDriver->addItems(v.toStringList());
120 ui->audioDriver->blockSignals(false);
121 }
122 v = m_driver->property("soundfont");
123 if (v.isValid()) {
124 m_defSoundFont = v.toString();
125 }
126 }
127 ui->bufferTime->blockSignals(true);
128 ui->periodSize->blockSignals(true);
129 ui->periods->blockSignals(true);
130 //qDebug() << Q_FUNC_INFO;
131}
132
133FluidSettingsDialog::~FluidSettingsDialog()
134{
135 //qDebug() << Q_FUNC_INFO;
136 if (m_driver != nullptr) {
137 m_driver->close();
138 }
139 delete ui;
140}
141
142bool FluidSettingsDialog::checkRanges() const
143{
144 //qDebug() << Q_FUNC_INFO;
145 if (ui->gain->hasAcceptableInput()) {
146 ui->gain->deselect();
147 } else {
148 ui->gain->selectAll();
149 }
150 if (ui->polyphony->hasAcceptableInput()) {
151 ui->polyphony->deselect();
152 } else {
153 ui->polyphony->selectAll();
154 }
155 if (ui->sampleRate->hasAcceptableInput()) {
156 ui->sampleRate->deselect();
157 } else {
158 ui->sampleRate->selectAll();
159 }
160 return
161 ui->bufferTime->hasAcceptableInput() &&
162 ui->periodSize->hasAcceptableInput() &&
163 ui->periods->hasAcceptableInput() &&
164 ui->gain->hasAcceptableInput() &&
165 ui->polyphony->hasAcceptableInput() &&
166 ui->sampleRate->hasAcceptableInput();
167}
168
169void FluidSettingsDialog::accept()
170{
171 //qDebug() << Q_FUNC_INFO;
172 if (checkRanges()) {
173 writeSettings();
174 if (m_driver != nullptr) {
175 QString title;
176 QVariant varStatus = m_driver->property("status");
177 if (varStatus.isValid()) {
178 title = varStatus.toBool() ? tr("FluidSynth Initialized") : tr("FluidSynth Initialization Failed");
179 QVariant varDiag = m_driver->property("diagnostics");
180 if (varDiag.isValid()) {
181 QString text = varDiag.toStringList().join(QChar::LineFeed).trimmed();
182 if (varStatus.toBool()) {
183 if (!text.isEmpty()) {
184 QMessageBox::information(this, title, text);
185 }
186 } else {
187 QMessageBox::critical(this, title, text);
188 return;
189 }
190 }
191 }
192 }
193 QDialog::accept();
194 }
195}
196
197void FluidSettingsDialog::showEvent(QShowEvent *event)
198{
199 readSettings();
200 event->accept();
201}
202
203QString FluidSettingsDialog::defaultAudioDriver() const
204{
205 const QString QSTR_DEFAULT_AUDIODRIVER =
206#if defined(Q_OS_WIN)
207 QLatin1String("wasapi");
208#elif defined(Q_OS_OSX)
209 QLatin1String("coreaudio");
210#elif defined(Q_OS_LINUX)
211 QSTR_PULSEAUDIO;
212#else
213 QLatin1String("oss");
214#endif
215 return QSTR_DEFAULT_AUDIODRIVER;
216}
217
218void FluidSettingsDialog::chkDriverProperties(QSettings *settings)
219{
220 //qDebug() << Q_FUNC_INFO;
221 if (m_driver != nullptr) {
223 m_driver->close();
224 m_driver->initialize(settings);
225 m_driver->open(conn);
226
227 QVariant drivers = m_driver->property("audiodrivers");
228 if (drivers.isValid()) {
229 auto text = ui->audioDriver->currentText();
230 ui->audioDriver->blockSignals(true);
231 ui->audioDriver->clear();
232 ui->audioDriver->addItems(drivers.toStringList());
233 ui->audioDriver->setCurrentText(text);
234 ui->audioDriver->blockSignals(false);
235 }
236 ui->lblVersion->clear();
237 ui->lblVersion->setText(driverVersion());
238 QVariant varStatus = m_driver->property("status");
239 if (varStatus.isValid()) {
240 ui->lblStatus->clear();
241 ui->lblStatus->setText(varStatus.toBool() ? tr("Ready") : tr("Failed") );
242 ui->lblStatusIcon->setPixmap(varStatus.toBool() ? QPixmap(":/checked.png") : QPixmap(":/error.png") );
243 }
244 }
245}
246
247void FluidSettingsDialog::setWidgetTip(QWidget *w, const QString &tip)
248{
249 w->setToolTip(tip);
250 QToolTip::showText(w->parentWidget()->mapToGlobal(w->pos()), tip);
251}
252
253void drumstick::widgets::FluidSettingsDialog::initBuffer()
254{
255 if ((ui->audioDriver->currentText() == QSTR_PULSEAUDIO) && driverVersionLessThan_2_2_8()) {
256 //qDebug() << Q_FUNC_INFO << QSTR_PULSEAUDIO << driverVersion();
257 int bufferTime = ui->bufferTime->value();
258 int minBufTime = ui->bufferTime->minimum();
259 if (bufferTime < minBufTime) {
260 bufferTime = minBufTime;
261 }
262 ui->bufferTime->setValue( bufferTime );
263 bufferTimeChanged( bufferTime );
264 } else {
265 //qDebug() << Q_FUNC_INFO;
266 bufferSizeChanged();
267 }
268}
269
270QString FluidSettingsDialog::driverVersion() const
271{
272 static QString result;
273 if (m_driver != nullptr && result.isEmpty()) {
274 QVariant varVersion = m_driver->property("libversion");
275 if (varVersion.isValid()) {
276 result = varVersion.toString();
277 }
278 }
279 return result;
280}
281
282bool FluidSettingsDialog::driverVersionLessThan_2_2_8()
283{
284 static const QVersionNumber check_2_2_8(2, 2, 8);
285 QVersionNumber driverV = QVersionNumber::fromString(driverVersion());
286 return driverV < check_2_2_8;
287}
288
289void FluidSettingsDialog::readSettings()
290{
291 //qDebug() << Q_FUNC_INFO;
292 SettingsFactory settings;
293 settings->beginGroup(QSTR_PREFERENCES);
294 ui->audioDriver->setCurrentText( settings->value(QSTR_AUDIODRIVER, defaultAudioDriver()).toString() );
295 ui->bufferTime->setValue( settings->value(QSTR_BUFFERTIME, DEFAULT_BUFFERTIME).toInt() );
296 ui->periodSize->setValue( settings->value(QSTR_PERIODSIZE, DEFAULT_PERIODSIZE).toInt() );
297 ui->periods->setValue( settings->value(QSTR_PERIODS, DEFAULT_PERIODS).toInt() );
298 ui->sampleRate->setText( settings->value(QSTR_SAMPLERATE, DEFAULT_SAMPLERATE).toString() );
299 ui->gain->setText( settings->value(QSTR_GAIN, DEFAULT_GAIN).toString() );
300 ui->polyphony->setText( settings->value(QSTR_POLYPHONY, DEFAULT_POLYPHONY).toString() );
301 ui->soundFont->setText( settings->value(QSTR_INSTRUMENTSDEFINITION, m_defSoundFont).toString() );
302
303 ui->chorus_depth->setValue(settings->value(QSTR_CHORUS_DEPTH, DEFAULT_CHORUS_DEPTH).toFloat()
304 * 10);
305 ui->chorus_level->setValue(settings->value(QSTR_CHORUS_LEVEL, DEFAULT_CHORUS_LEVEL).toFloat()
306 * 10);
307 ui->chorus_nr->setValue(settings->value(QSTR_CHORUS_NR, DEFAULT_CHORUS_NR).toInt());
308 ui->chorus_speed->setValue(settings->value(QSTR_CHORUS_SPEED, DEFAULT_CHORUS_SPEED).toFloat()
309 * 10);
310
311 ui->reverb_damp->setValue(settings->value(QSTR_REVERB_DAMP, DEFAULT_REVERB_DAMP).toFloat() * 10);
312 ui->reverb_level->setValue(settings->value(QSTR_REVERB_LEVEL, DEFAULT_REVERB_LEVEL).toFloat()
313 * 10);
314 ui->reverb_size->setValue(settings->value(QSTR_REVERB_SIZE, DEFAULT_REVERB_SIZE).toFloat() * 10);
315 ui->reverb_width->setValue(settings->value(QSTR_REVERB_WIDTH, DEFAULT_REVERB_WIDTH).toFloat()
316 * 10);
317
318 ui->chorus->setChecked(settings->value(QSTR_CHORUS, DEFAULT_CHORUS).toInt() != 0);
319 ui->reverb->setChecked(settings->value(QSTR_REVERB, DEFAULT_REVERB).toInt() != 0);
320
321 settings->endGroup();
322
323 audioDriverChanged( ui->audioDriver->currentText() );
324 chkDriverProperties(settings.getQSettings());
325}
326
327void FluidSettingsDialog::writeSettings()
328{
329 //qDebug() << Q_FUNC_INFO;
330 SettingsFactory settings;
331 QString audioDriver;
332 QString soundFont(m_defSoundFont);
333 int bufferTime(DEFAULT_BUFFERTIME);
334 int periodSize(DEFAULT_PERIODSIZE);
335 int periods(DEFAULT_PERIODS);
336 double sampleRate(DEFAULT_SAMPLERATE);
337 int chorus(DEFAULT_CHORUS);
338 int reverb(DEFAULT_REVERB);
339 double gain(DEFAULT_GAIN);
340 int polyphony(DEFAULT_POLYPHONY);
341
342 double chorus_depth(DEFAULT_CHORUS_DEPTH);
343 double chorus_level(DEFAULT_CHORUS_LEVEL);
344 int chorus_nr(DEFAULT_CHORUS_NR);
345 double chorus_speed(DEFAULT_CHORUS_SPEED);
346
347 double reverb_damp(DEFAULT_REVERB_DAMP);
348 double reverb_level(DEFAULT_REVERB_LEVEL);
349 double reverb_size(DEFAULT_REVERB_SIZE);
350 double reverb_width(DEFAULT_REVERB_WIDTH);
351
352 audioDriver = ui->audioDriver->currentText();
353 if (audioDriver.isEmpty()) {
354 audioDriver = defaultAudioDriver();
355 }
356 soundFont = ui->soundFont->text();
357 bufferTime = ui->bufferTime->value();
358 periodSize = ui->periodSize->value();
359 periods = ui->periods->value();
360 sampleRate = ui->sampleRate->text().toDouble();
361 chorus = (ui->chorus->isChecked() ? 1 : 0);
362 reverb = (ui->reverb->isChecked() ? 1 : 0);
363 gain = ui->gain->text().toDouble();
364 polyphony = ui->polyphony->text().toInt();
365
366 chorus_depth = ui->chorus_depth->value() / 10.0;
367 chorus_level = ui->chorus_level->value() / 10.0;
368 chorus_nr = ui->chorus_nr->value();
369 chorus_speed = ui->chorus_speed->value() / 10.0;
370 reverb_damp = ui->reverb_damp->value() / 10.0;
371 reverb_level = ui->reverb_level->value() / 10.0;
372 reverb_size = ui->reverb_size->value() / 10.0;
373 reverb_width = ui->reverb_width->value() / 10.0;
374
375 settings->beginGroup(QSTR_PREFERENCES);
376 settings->setValue(QSTR_INSTRUMENTSDEFINITION, soundFont);
377 settings->setValue(QSTR_AUDIODRIVER, audioDriver);
378 settings->setValue(QSTR_BUFFERTIME, bufferTime);
379 settings->setValue(QSTR_PERIODSIZE, periodSize);
380 settings->setValue(QSTR_PERIODS, periods);
381 settings->setValue(QSTR_SAMPLERATE, sampleRate);
382 settings->setValue(QSTR_CHORUS, chorus);
383 settings->setValue(QSTR_REVERB, reverb);
384 settings->setValue(QSTR_GAIN, gain);
385 settings->setValue(QSTR_POLYPHONY, polyphony);
386 settings->setValue(QSTR_CHORUS_DEPTH, chorus_depth);
387 settings->setValue(QSTR_CHORUS_LEVEL, chorus_level);
388 settings->setValue(QSTR_CHORUS_NR, chorus_nr);
389 settings->setValue(QSTR_CHORUS_SPEED, chorus_speed);
390 settings->setValue(QSTR_REVERB_DAMP, reverb_damp);
391 settings->setValue(QSTR_REVERB_LEVEL, reverb_level);
392 settings->setValue(QSTR_REVERB_SIZE, reverb_size);
393 settings->setValue(QSTR_REVERB_WIDTH, reverb_width);
394 settings->endGroup();
395 settings->sync();
396
397 chkDriverProperties(settings.getQSettings());
398}
399
400void FluidSettingsDialog::restoreDefaults()
401{
402 //qDebug() << Q_FUNC_INFO;
403 ui->audioDriver->setCurrentText(defaultAudioDriver());
404 ui->bufferTime->setValue(DEFAULT_BUFFERTIME);
405 ui->periodSize->setValue(DEFAULT_PERIODSIZE);
406 ui->periods->setValue(DEFAULT_PERIODS);
407 ui->sampleRate->setText(QString::number(DEFAULT_SAMPLERATE));
408 ui->gain->setText(QString::number(DEFAULT_GAIN));
409 ui->polyphony->setText(QString::number(DEFAULT_POLYPHONY));
410 ui->soundFont->setText(m_defSoundFont);
411 ui->chorus_depth->setValue(DEFAULT_CHORUS_DEPTH * 10);
412 ui->chorus_level->setValue(DEFAULT_CHORUS_LEVEL * 10);
413 ui->chorus_nr->setValue(DEFAULT_CHORUS_NR);
414 ui->chorus_speed->setValue(DEFAULT_CHORUS_SPEED * 10);
415 ui->reverb_damp->setValue(DEFAULT_REVERB_DAMP * 10);
416 ui->reverb_level->setValue(DEFAULT_REVERB_LEVEL * 10);
417 ui->reverb_size->setValue(DEFAULT_REVERB_SIZE * 10);
418 ui->reverb_width->setValue(DEFAULT_REVERB_WIDTH * 10);
419 ui->chorus->setChecked(DEFAULT_CHORUS != 0);
420 ui->reverb->setChecked(DEFAULT_REVERB != 0);
421 initBuffer();
422}
423
424void FluidSettingsDialog::showFileDialog()
425{
426 QDir dir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR, QStandardPaths::LocateDirectory));
427 if (!dir.exists()) {
428 dir = QDir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR2, QStandardPaths::LocateDirectory));
429 }
430 QStringList fileNames
431 = QFileDialog::getOpenFileNames(this,
432 tr("Select SoundFont"),
433 dir.absolutePath(),
434 tr("SoundFont Files (*.sf2 *.sf3 *.dls)"));
435 if (!fileNames.isEmpty()) {
436 ui->soundFont->setText(fileNames.join(';'));
437 }
438}
439
440void FluidSettingsDialog::audioDriverChanged(const QString &text)
441{
442 //qDebug() << Q_FUNC_INFO << text;
443 if ((text == QSTR_PULSEAUDIO) && driverVersionLessThan_2_2_8()) {
444 ui->bufferTime->setDisabled(false);
445 ui->bufferTime->blockSignals(false);
446 ui->periodSize->setDisabled(true);
447 ui->periodSize->blockSignals(true);
448 ui->periods->setVisible(false);
449 ui->periods->setDisabled(true);
450 ui->periods->blockSignals(true);
451 } else {
452 ui->bufferTime->setDisabled(true);
453 ui->bufferTime->blockSignals(true);
454 ui->periodSize->setDisabled(false);
455 ui->periodSize->blockSignals(false);
456 ui->periods->setVisible(true);
457 ui->periods->setDisabled(false);
458 ui->periods->blockSignals(false);
459 }
460 initBuffer();
461}
462
463void FluidSettingsDialog::bufferTimeChanged(int value)
464{
465 double rate = ui->sampleRate->text().toDouble();
466 int size = qRound( value * rate / 1000.0 );
467 ui->periodSize->setValue( size );
468 ui->periods->setValue( ui->periods->minimum() );
469 //qDebug() << Q_FUNC_INFO << "time:" << value << "rate:" << rate << "size:" << size;
470}
471
472void FluidSettingsDialog::bufferSizeChanged()
473{
474 QString audioDriver = ui->audioDriver->currentText();
475 double rate = ui->sampleRate->text().toDouble();
476 int size = ui->periodSize->value();
477 if ((audioDriver != QSTR_PULSEAUDIO) || !driverVersionLessThan_2_2_8()) {
478 size *= ui->periods->value();
479 }
480 int ms = qRound( 1000.0 * size / rate );
481 ui->bufferTime->setValue(ms);
482 //qDebug() << Q_FUNC_INFO << "time:" << ms << "rate:" << rate << "size:" << size;
483}
484
485void FluidSettingsDialog::changeSoundFont(const QString& fileName)
486{
487 readSettings();
488 ui->soundFont->setText(fileName);
489 writeSettings();
490}
491
492} // namespace widgets
493} // 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.
QPair< QString, QVariant > MIDIConnection
MIDIConnection represents a connection identifier.
Drumstick common.
SettingsFactory class declaration.