drumstick 2.10.0
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 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_OSX)
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);
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(settings->value(QSTR_GAIN, DEFAULT_GAIN).toDouble());
269 ui->polyphony->setValue(settings->value(QSTR_POLYPHONY, DEFAULT_POLYPHONY).toInt());
270 ui->soundFont->setText( settings->value(QSTR_INSTRUMENTSDEFINITION, m_defSoundFont).toString() );
271
272 ui->chorus_depth->setValue(settings->value(QSTR_CHORUS_DEPTH, DEFAULT_CHORUS_DEPTH).toDouble()
273 * CHORUS_REVERB_VALUE_SCALE);
274 ui->chorus_level->setValue(settings->value(QSTR_CHORUS_LEVEL, DEFAULT_CHORUS_LEVEL).toDouble()
275 * CHORUS_REVERB_VALUE_SCALE);
276 ui->chorus_nr->setValue(settings->value(QSTR_CHORUS_NR, DEFAULT_CHORUS_NR).toInt());
277 ui->chorus_speed->setValue(settings->value(QSTR_CHORUS_SPEED, DEFAULT_CHORUS_SPEED).toDouble()
278 * CHORUS_REVERB_VALUE_SCALE);
279
280 ui->reverb_damp->setValue(settings->value(QSTR_REVERB_DAMP, DEFAULT_REVERB_DAMP).toDouble()
281 * CHORUS_REVERB_VALUE_SCALE);
282 ui->reverb_level->setValue(settings->value(QSTR_REVERB_LEVEL, DEFAULT_REVERB_LEVEL).toDouble()
283 * CHORUS_REVERB_VALUE_SCALE);
284 ui->reverb_size->setValue(settings->value(QSTR_REVERB_SIZE, DEFAULT_REVERB_SIZE).toDouble()
285 * CHORUS_REVERB_VALUE_SCALE);
286 ui->reverb_width->setValue(settings->value(QSTR_REVERB_WIDTH, DEFAULT_REVERB_WIDTH).toDouble()
287 * CHORUS_REVERB_VALUE_SCALE);
288
289 ui->chorus->setChecked(settings->value(QSTR_CHORUS, DEFAULT_CHORUS).toInt() != 0);
290 ui->reverb->setChecked(settings->value(QSTR_REVERB, DEFAULT_REVERB).toInt() != 0);
291
292 settings->endGroup();
293
294 audioDriverChanged( ui->audioDriver->currentText() );
295 chkDriverProperties(settings.getQSettings());
296}
297
298void FluidSettingsDialog::writeSettings()
299{
300 SettingsFactory settings;
301 QString audioDriver;
302 QString soundFont(m_defSoundFont);
303 int bufferTime(DEFAULT_BUFFERTIME);
304 int periodSize(DEFAULT_PERIODSIZE);
305 int periods(DEFAULT_PERIODS);
306 double sampleRate(DEFAULT_SAMPLERATE);
307 int chorus(DEFAULT_CHORUS);
308 int reverb(DEFAULT_REVERB);
309 double gain(DEFAULT_GAIN);
310 int polyphony(DEFAULT_POLYPHONY);
311
312 double chorus_depth(DEFAULT_CHORUS_DEPTH);
313 double chorus_level(DEFAULT_CHORUS_LEVEL);
314 int chorus_nr(DEFAULT_CHORUS_NR);
315 double chorus_speed(DEFAULT_CHORUS_SPEED);
316
317 double reverb_damp(DEFAULT_REVERB_DAMP);
318 double reverb_level(DEFAULT_REVERB_LEVEL);
319 double reverb_size(DEFAULT_REVERB_SIZE);
320 double reverb_width(DEFAULT_REVERB_WIDTH);
321
322 audioDriver = ui->audioDriver->currentText();
323 if (audioDriver.isEmpty()) {
324 audioDriver = defaultAudioDriver();
325 }
326 soundFont = ui->soundFont->text();
327 bufferTime = ui->bufferTime->value();
328 periodSize = ui->periodSize->value();
329 periods = ui->periods->value();
330 sampleRate = ui->sampleRate->currentText().toDouble();
331 chorus = (ui->chorus->isChecked() ? 1 : 0);
332 reverb = (ui->reverb->isChecked() ? 1 : 0);
333 gain = ui->gain->value();
334 polyphony = ui->polyphony->value();
335
336 chorus_depth = ui->chorus_depth->value() / CHORUS_REVERB_VALUE_SCALE;
337 chorus_level = ui->chorus_level->value() / CHORUS_REVERB_VALUE_SCALE;
338 chorus_nr = ui->chorus_nr->value();
339 chorus_speed = ui->chorus_speed->value() / CHORUS_REVERB_VALUE_SCALE;
340 reverb_damp = ui->reverb_damp->value() / CHORUS_REVERB_VALUE_SCALE;
341 reverb_level = ui->reverb_level->value() / CHORUS_REVERB_VALUE_SCALE;
342 reverb_size = ui->reverb_size->value() / CHORUS_REVERB_VALUE_SCALE;
343 reverb_width = ui->reverb_width->value() / CHORUS_REVERB_VALUE_SCALE;
344
345 settings->beginGroup(QSTR_PREFERENCES);
346 settings->setValue(QSTR_INSTRUMENTSDEFINITION, soundFont);
347 settings->setValue(QSTR_AUDIODRIVER, audioDriver);
348 settings->setValue(QSTR_BUFFERTIME, bufferTime);
349 settings->setValue(QSTR_PERIODSIZE, periodSize);
350 settings->setValue(QSTR_PERIODS, periods);
351 settings->setValue(QSTR_SAMPLERATE, sampleRate);
352 settings->setValue(QSTR_CHORUS, chorus);
353 settings->setValue(QSTR_REVERB, reverb);
354 settings->setValue(QSTR_GAIN, gain);
355 settings->setValue(QSTR_POLYPHONY, polyphony);
356 settings->setValue(QSTR_CHORUS_DEPTH, chorus_depth);
357 settings->setValue(QSTR_CHORUS_LEVEL, chorus_level);
358 settings->setValue(QSTR_CHORUS_NR, chorus_nr);
359 settings->setValue(QSTR_CHORUS_SPEED, chorus_speed);
360 settings->setValue(QSTR_REVERB_DAMP, reverb_damp);
361 settings->setValue(QSTR_REVERB_LEVEL, reverb_level);
362 settings->setValue(QSTR_REVERB_SIZE, reverb_size);
363 settings->setValue(QSTR_REVERB_WIDTH, reverb_width);
364 settings->endGroup();
365 settings->sync();
366
367 chkDriverProperties(settings.getQSettings());
368}
369
370void FluidSettingsDialog::restoreDefaults()
371{
372 ui->audioDriver->setCurrentText(defaultAudioDriver());
373 ui->bufferTime->setValue(DEFAULT_BUFFERTIME);
374 ui->periodSize->setValue(DEFAULT_PERIODSIZE);
375 ui->periods->setValue(DEFAULT_PERIODS);
376 ui->sampleRate->setCurrentText(QString::number(DEFAULT_SAMPLERATE));
377 ui->gain->setValue(DEFAULT_GAIN);
378 ui->polyphony->setValue(DEFAULT_POLYPHONY);
379 ui->soundFont->setText(m_defSoundFont);
380 ui->chorus_depth->setValue(DEFAULT_CHORUS_DEPTH * CHORUS_REVERB_VALUE_SCALE);
381 ui->chorus_level->setValue(DEFAULT_CHORUS_LEVEL * CHORUS_REVERB_VALUE_SCALE);
382 ui->chorus_nr->setValue(DEFAULT_CHORUS_NR);
383 ui->chorus_speed->setValue(DEFAULT_CHORUS_SPEED * CHORUS_REVERB_VALUE_SCALE);
384 ui->reverb_damp->setValue(DEFAULT_REVERB_DAMP * CHORUS_REVERB_VALUE_SCALE);
385 ui->reverb_level->setValue(DEFAULT_REVERB_LEVEL * CHORUS_REVERB_VALUE_SCALE);
386 ui->reverb_size->setValue(DEFAULT_REVERB_SIZE * CHORUS_REVERB_VALUE_SCALE);
387 ui->reverb_width->setValue(DEFAULT_REVERB_WIDTH * CHORUS_REVERB_VALUE_SCALE);
388 ui->chorus->setChecked(DEFAULT_CHORUS != 0);
389 ui->reverb->setChecked(DEFAULT_REVERB != 0);
390 initBuffer();
391}
392
393void FluidSettingsDialog::showFileDialog()
394{
395 QDir dir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR, QStandardPaths::LocateDirectory));
396 if (!dir.exists()) {
397 dir = QDir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR2, QStandardPaths::LocateDirectory));
398 }
399 QStringList fileNames
400 = QFileDialog::getOpenFileNames(this,
401 tr("Select SoundFont"),
402 dir.absolutePath(),
403 tr("SoundFont Files (*.sf2 *.sf3 *.dls)"));
404 if (!fileNames.isEmpty()) {
405 ui->soundFont->setText(fileNames.join(';'));
406 }
407}
408
409void FluidSettingsDialog::audioDriverChanged(const QString &text)
410{
411 if ((text == QSTR_PULSEAUDIO) && driverVersionLessThan_2_2_8()) {
412 ui->bufferTime->setDisabled(false);
413 ui->bufferTime->blockSignals(false);
414 ui->periodSize->setDisabled(true);
415 ui->periodSize->blockSignals(true);
416 ui->periods->setVisible(false);
417 ui->periods->setDisabled(true);
418 ui->periods->blockSignals(true);
419 } else {
420 ui->bufferTime->setDisabled(true);
421 ui->bufferTime->blockSignals(true);
422 ui->periodSize->setDisabled(false);
423 ui->periodSize->blockSignals(false);
424 ui->periods->setVisible(true);
425 ui->periods->setDisabled(false);
426 ui->periods->blockSignals(false);
427 }
428 initBuffer();
429}
430
431void FluidSettingsDialog::bufferTimeChanged(int value)
432{
433 double rate = ui->sampleRate->currentText().toDouble();
434 int size = qRound( value * rate / 1000.0 );
435 ui->periodSize->setValue( size );
436 ui->periods->setValue( ui->periods->minimum() );
437}
438
439void FluidSettingsDialog::bufferSizeChanged()
440{
441 QString audioDriver = ui->audioDriver->currentText();
442 double rate = ui->sampleRate->currentText().toDouble();
443 int size = ui->periodSize->value();
444 if ((audioDriver != QSTR_PULSEAUDIO) || !driverVersionLessThan_2_2_8()) {
445 size *= ui->periods->value();
446 }
447 int ms = qRound( 1000.0 * size / rate );
448 ui->bufferTime->setValue(ms);
449}
450
451void FluidSettingsDialog::changeSoundFont(const QString& fileName)
452{
453 readSettings();
454 ui->soundFont->setText(fileName);
455 writeSettings();
456}
457
458} // namespace widgets
459} // 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
virtual void close()=0
close the MIDI port
Declaration of the Fluidsynth configuration dialog.
QPair< QString, QVariant > MIDIConnection
MIDIConnection represents a connection identifier.
Definition: rtmidioutput.h:116
Drumstick common.
Definition: alsaclient.cpp:71
SettingsFactory class declaration.