drumstick  2.9.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-2023, 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 <QPushButton>
23 #include <QStandardPaths>
24 #include <QToolButton>
25 #include <QMessageBox>
26 #include <QVersionNumber>
27 
28 #include "fluidsettingsdialog.h"
29 #include "ui_fluidsettingsdialog.h"
32 
38 namespace drumstick { namespace widgets {
39 
40 const QString FluidSettingsDialog::QSTR_PREFERENCES = QStringLiteral("FluidSynth");
41 const QString FluidSettingsDialog::QSTR_INSTRUMENTSDEFINITION = QStringLiteral("InstrumentsDefinition");
42 const QString FluidSettingsDialog::QSTR_DATADIR = QStringLiteral("soundfonts");
43 const QString FluidSettingsDialog::QSTR_DATADIR2 = QStringLiteral("sounds/sf2");
44 const QString FluidSettingsDialog::QSTR_AUDIODRIVER = QStringLiteral("AudioDriver");
45 const QString FluidSettingsDialog::QSTR_PERIODSIZE = QStringLiteral("PeriodSize");
46 const QString FluidSettingsDialog::QSTR_PERIODS = QStringLiteral("Periods");
47 const QString FluidSettingsDialog::QSTR_SAMPLERATE = QStringLiteral("SampleRate");
48 const QString FluidSettingsDialog::QSTR_CHORUS = QStringLiteral("Chorus");
49 const QString FluidSettingsDialog::QSTR_REVERB = QStringLiteral("Reverb");
50 const QString FluidSettingsDialog::QSTR_GAIN = QStringLiteral("Gain");
51 const QString FluidSettingsDialog::QSTR_POLYPHONY = QStringLiteral("Polyphony");
52 const QString FluidSettingsDialog::QSTR_BUFFERTIME = QStringLiteral("BufferTime");
53 const QString FluidSettingsDialog::QSTR_PULSEAUDIO = QStringLiteral("pulseaudio");
54 
55 FluidSettingsDialog::FluidSettingsDialog(QWidget *parent) :
56  QDialog(parent),
57  ui(new Ui::FluidSettingsDialog)
58 {
59  ui->setupUi(this);
60  connect(ui->audioDriver, &QComboBox::currentTextChanged, this, &FluidSettingsDialog::audioDriverChanged);
61  connect(ui->bufferTime, QOverload<int>::of(&QSpinBox::valueChanged), this, &FluidSettingsDialog::bufferTimeChanged);
62  connect(ui->periodSize, QOverload<int>::of(&QSpinBox::valueChanged), this, &FluidSettingsDialog::bufferSizeChanged);
63  connect(ui->periods, QOverload<int>::of(&QSpinBox::valueChanged), this, &FluidSettingsDialog::bufferSizeChanged);
64  connect(ui->btnFile, &QToolButton::clicked, this, &FluidSettingsDialog::showFileDialog);
65  connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked,
66  this, &FluidSettingsDialog::restoreDefaults);
67  auto sampleRateValidator = new QDoubleValidator(8000.0, 96000.0, 1, this);
68  sampleRateValidator->setNotation(QDoubleValidator::StandardNotation);
69  sampleRateValidator->setLocale(QLocale::c());
70  ui->sampleRate->setValidator(sampleRateValidator);
71  auto gainValidator = new QDoubleValidator(0.1, 10.0, 2, this);
72  gainValidator->setNotation(QDoubleValidator::StandardNotation);
73  gainValidator->setLocale(QLocale::c());
74  ui->gain->setValidator(gainValidator);
75  auto polyphonyValidator = new QIntValidator(1, 65535, this);
76  ui->polyphony->setValidator(polyphonyValidator);
77 
79  m_driver = man.outputBackendByName("FluidSynth");
80  if (m_driver != nullptr) {
81  QVariant v = m_driver->property("audiodrivers");
82  if (v.isValid()) {
83  ui->audioDriver->blockSignals(true);
84  ui->audioDriver->clear();
85  ui->audioDriver->addItems(v.toStringList());
86  ui->audioDriver->blockSignals(false);
87  }
88  v = m_driver->property("soundfont");
89  if (v.isValid()) {
90  m_defSoundFont = v.toString();
91  }
92  }
93  ui->bufferTime->blockSignals(true);
94  ui->periodSize->blockSignals(true);
95  ui->periods->blockSignals(true);
96  //qDebug() << Q_FUNC_INFO;
97 }
98 
99 FluidSettingsDialog::~FluidSettingsDialog()
100 {
101  //qDebug() << Q_FUNC_INFO;
102  if (m_driver != nullptr) {
103  m_driver->close();
104  }
105  delete ui;
106 }
107 
108 bool FluidSettingsDialog::checkRanges() const
109 {
110  //qDebug() << Q_FUNC_INFO;
111  if (ui->gain->hasAcceptableInput()) {
112  ui->gain->deselect();
113  } else {
114  ui->gain->selectAll();
115  }
116  if (ui->polyphony->hasAcceptableInput()) {
117  ui->polyphony->deselect();
118  } else {
119  ui->polyphony->selectAll();
120  }
121  if (ui->sampleRate->hasAcceptableInput()) {
122  ui->sampleRate->deselect();
123  } else {
124  ui->sampleRate->selectAll();
125  }
126  return
127  ui->bufferTime->hasAcceptableInput() &&
128  ui->periodSize->hasAcceptableInput() &&
129  ui->periods->hasAcceptableInput() &&
130  ui->gain->hasAcceptableInput() &&
131  ui->polyphony->hasAcceptableInput() &&
132  ui->sampleRate->hasAcceptableInput();
133 }
134 
135 void FluidSettingsDialog::accept()
136 {
137  //qDebug() << Q_FUNC_INFO;
138  if (checkRanges()) {
139  writeSettings();
140  if (m_driver != nullptr) {
141  QString title;
142  QVariant varStatus = m_driver->property("status");
143  if (varStatus.isValid()) {
144  title = varStatus.toBool() ? tr("FluidSynth Initialized") : tr("FluidSynth Initialization Failed");
145  QVariant varDiag = m_driver->property("diagnostics");
146  if (varDiag.isValid()) {
147  QString text = varDiag.toStringList().join(QChar::LineFeed).trimmed();
148  if (varStatus.toBool()) {
149  if (!text.isEmpty()) {
150  QMessageBox::information(this, title, text);
151  }
152  } else {
153  QMessageBox::critical(this, title, text);
154  return;
155  }
156  }
157  }
158  }
159  QDialog::accept();
160  }
161 }
162 
163 void FluidSettingsDialog::showEvent(QShowEvent *event)
164 {
165  readSettings();
166  event->accept();
167 }
168 
169 QString FluidSettingsDialog::defaultAudioDriver() const
170 {
171  const QString QSTR_DEFAULT_AUDIODRIVER =
172 #if defined(Q_OS_WIN)
173  QLatin1String("wasapi");
174 #elif defined(Q_OS_OSX)
175  QLatin1String("coreaudio");
176 #elif defined(Q_OS_LINUX)
177  QSTR_PULSEAUDIO;
178 #else
179  QLatin1String("oss");
180 #endif
181  return QSTR_DEFAULT_AUDIODRIVER;
182 }
183 
184 void FluidSettingsDialog::chkDriverProperties(QSettings *settings)
185 {
186  //qDebug() << Q_FUNC_INFO;
187  if (m_driver != nullptr) {
189  m_driver->close();
190  m_driver->initialize(settings);
191  m_driver->open(conn);
192 
193  QVariant drivers = m_driver->property("audiodrivers");
194  if (drivers.isValid()) {
195  auto text = ui->audioDriver->currentText();
196  ui->audioDriver->blockSignals(true);
197  ui->audioDriver->clear();
198  ui->audioDriver->addItems(drivers.toStringList());
199  ui->audioDriver->setCurrentText(text);
200  ui->audioDriver->blockSignals(false);
201  }
202  ui->lblVersion->clear();
203  ui->lblVersion->setText(driverVersion());
204  QVariant varStatus = m_driver->property("status");
205  if (varStatus.isValid()) {
206  ui->lblStatus->clear();
207  ui->lblStatus->setText(varStatus.toBool() ? tr("Ready") : tr("Failed") );
208  ui->lblStatusIcon->setPixmap(varStatus.toBool() ? QPixmap(":/checked.png") : QPixmap(":/error.png") );
209  }
210  }
211 }
212 
213 void drumstick::widgets::FluidSettingsDialog::initBuffer()
214 {
215  if ((ui->audioDriver->currentText() == QSTR_PULSEAUDIO) && driverVersionLessThan_2_2_8()) {
216  //qDebug() << Q_FUNC_INFO << QSTR_PULSEAUDIO << driverVersion();
217  int bufferTime = ui->bufferTime->value();
218  int minBufTime = ui->bufferTime->minimum();
219  if (bufferTime < minBufTime) {
220  bufferTime = minBufTime;
221  }
222  ui->bufferTime->setValue( bufferTime );
223  bufferTimeChanged( bufferTime );
224  } else {
225  //qDebug() << Q_FUNC_INFO;
226  bufferSizeChanged();
227  }
228 }
229 
230 QString FluidSettingsDialog::driverVersion() const
231 {
232  static QString result;
233  if (m_driver != nullptr && result.isEmpty()) {
234  QVariant varVersion = m_driver->property("libversion");
235  if (varVersion.isValid()) {
236  result = varVersion.toString();
237  }
238  }
239  return result;
240 }
241 
242 bool FluidSettingsDialog::driverVersionLessThan_2_2_8()
243 {
244  static const QVersionNumber check_2_2_8(2, 2, 8);
245  QVersionNumber driverV = QVersionNumber::fromString(driverVersion());
246  return driverV < check_2_2_8;
247 }
248 
249 void FluidSettingsDialog::readSettings()
250 {
251  //qDebug() << Q_FUNC_INFO;
252  SettingsFactory settings;
253  settings->beginGroup(QSTR_PREFERENCES);
254  ui->audioDriver->setCurrentText( settings->value(QSTR_AUDIODRIVER, defaultAudioDriver()).toString() );
255  ui->bufferTime->setValue( settings->value(QSTR_BUFFERTIME, DEFAULT_BUFFERTIME).toInt() );
256  ui->periodSize->setValue( settings->value(QSTR_PERIODSIZE, DEFAULT_PERIODSIZE).toInt() );
257  ui->periods->setValue( settings->value(QSTR_PERIODS, DEFAULT_PERIODS).toInt() );
258  ui->sampleRate->setText( settings->value(QSTR_SAMPLERATE, DEFAULT_SAMPLERATE).toString() );
259  ui->chorus->setChecked( settings->value(QSTR_CHORUS, DEFAULT_CHORUS).toInt() != 0 );
260  ui->reverb->setChecked( settings->value(QSTR_REVERB, DEFAULT_REVERB).toInt() != 0 );
261  ui->gain->setText( settings->value(QSTR_GAIN, DEFAULT_GAIN).toString() );
262  ui->polyphony->setText( settings->value(QSTR_POLYPHONY, DEFAULT_POLYPHONY).toString() );
263  ui->soundFont->setText( settings->value(QSTR_INSTRUMENTSDEFINITION, m_defSoundFont).toString() );
264  settings->endGroup();
265 
266  audioDriverChanged( ui->audioDriver->currentText() );
267  chkDriverProperties(settings.getQSettings());
268 }
269 
270 void FluidSettingsDialog::writeSettings()
271 {
272  //qDebug() << Q_FUNC_INFO;
273  SettingsFactory settings;
274  QString audioDriver;
275  QString soundFont(m_defSoundFont);
276  int bufferTime(DEFAULT_BUFFERTIME);
277  int periodSize(DEFAULT_PERIODSIZE);
278  int periods(DEFAULT_PERIODS);
279  double sampleRate(DEFAULT_SAMPLERATE);
280  int chorus(DEFAULT_CHORUS);
281  int reverb(DEFAULT_REVERB);
282  double gain(DEFAULT_GAIN);
283  int polyphony(DEFAULT_POLYPHONY);
284 
285  audioDriver = ui->audioDriver->currentText();
286  if (audioDriver.isEmpty()) {
287  audioDriver = defaultAudioDriver();
288  }
289  soundFont = ui->soundFont->text();
290  bufferTime = ui->bufferTime->value();
291  periodSize = ui->periodSize->value();
292  periods = ui->periods->value();
293  sampleRate = ui->sampleRate->text().toDouble();
294  chorus = (ui->chorus->isChecked() ? 1 : 0);
295  reverb = (ui->reverb->isChecked() ? 1 : 0);
296  gain = ui->gain->text().toDouble();
297  polyphony = ui->polyphony->text().toInt();
298 
299  settings->beginGroup(QSTR_PREFERENCES);
300  settings->setValue(QSTR_INSTRUMENTSDEFINITION, soundFont);
301  settings->setValue(QSTR_AUDIODRIVER, audioDriver);
302  settings->setValue(QSTR_BUFFERTIME, bufferTime);
303  settings->setValue(QSTR_PERIODSIZE, periodSize);
304  settings->setValue(QSTR_PERIODS, periods);
305  settings->setValue(QSTR_SAMPLERATE, sampleRate);
306  settings->setValue(QSTR_CHORUS, chorus);
307  settings->setValue(QSTR_REVERB, reverb);
308  settings->setValue(QSTR_GAIN, gain);
309  settings->setValue(QSTR_POLYPHONY, polyphony);
310  settings->endGroup();
311  settings->sync();
312 
313  chkDriverProperties(settings.getQSettings());
314 }
315 
316 void FluidSettingsDialog::restoreDefaults()
317 {
318  //qDebug() << Q_FUNC_INFO;
319  ui->audioDriver->setCurrentText( defaultAudioDriver() );
320  ui->bufferTime->setValue( DEFAULT_BUFFERTIME );
321  ui->periodSize->setValue( DEFAULT_PERIODSIZE );
322  ui->periods->setValue( DEFAULT_PERIODS );
323  ui->sampleRate->setText( QString::number( DEFAULT_SAMPLERATE ));
324  ui->chorus->setChecked( DEFAULT_CHORUS != 0 );
325  ui->reverb->setChecked( DEFAULT_REVERB != 0 );
326  ui->gain->setText( QString::number( DEFAULT_GAIN ) );
327  ui->polyphony->setText( QString::number( DEFAULT_POLYPHONY ));
328  ui->soundFont->setText( m_defSoundFont );
329  initBuffer();
330 }
331 
332 void FluidSettingsDialog::showFileDialog()
333 {
334  QDir dir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR, QStandardPaths::LocateDirectory));
335  if (!dir.exists()) {
336  dir = QDir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR2, QStandardPaths::LocateDirectory));
337  }
338  QString fileName = QFileDialog::getOpenFileName(this, tr("Select SoundFont"), dir.absolutePath(), tr("SoundFont Files (*.sf2 *.sf3 *.dls)"));
339  if (!fileName.isEmpty()) {
340  ui->soundFont->setText(fileName);
341  }
342 }
343 
344 void FluidSettingsDialog::audioDriverChanged(const QString &text)
345 {
346  //qDebug() << Q_FUNC_INFO << text;
347  if ((text == QSTR_PULSEAUDIO) && driverVersionLessThan_2_2_8()) {
348  ui->bufferTime->setDisabled(false);
349  ui->bufferTime->blockSignals(false);
350  ui->periodSize->setDisabled(true);
351  ui->periodSize->blockSignals(true);
352  ui->periods->setVisible(false);
353  ui->periods->setDisabled(true);
354  ui->periods->blockSignals(true);
355  } else {
356  ui->bufferTime->setDisabled(true);
357  ui->bufferTime->blockSignals(true);
358  ui->periodSize->setDisabled(false);
359  ui->periodSize->blockSignals(false);
360  ui->periods->setVisible(true);
361  ui->periods->setDisabled(false);
362  ui->periods->blockSignals(false);
363  }
364  initBuffer();
365 }
366 
367 void FluidSettingsDialog::bufferTimeChanged(int value)
368 {
369  double rate = ui->sampleRate->text().toDouble();
370  int size = qRound( value * rate / 1000.0 );
371  ui->periodSize->setValue( size );
372  ui->periods->setValue( ui->periods->minimum() );
373  //qDebug() << Q_FUNC_INFO << "time:" << value << "rate:" << rate << "size:" << size;
374 }
375 
376 void FluidSettingsDialog::bufferSizeChanged()
377 {
378  QString audioDriver = ui->audioDriver->currentText();
379  double rate = ui->sampleRate->text().toDouble();
380  int size = ui->periodSize->value();
381  if ((audioDriver != QSTR_PULSEAUDIO) || !driverVersionLessThan_2_2_8()) {
382  size *= ui->periods->value();
383  }
384  int ms = qRound( 1000.0 * size / rate );
385  ui->bufferTime->setValue(ms);
386  //qDebug() << Q_FUNC_INFO << "time:" << ms << "rate:" << rate << "size:" << size;
387 }
388 
389 void FluidSettingsDialog::changeSoundFont(const QString& fileName)
390 {
391  readSettings();
392  ui->soundFont->setText(fileName);
393  writeSettings();
394 }
395 
396 } // namespace widgets
397 } // 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
void DRUMSTICK_WIDGETS_EXPORT changeSoundFont(const QString driver, const QString fileName, QWidget *parent=nullptr)
Changes the sound font configuration Some RT output drivers accept soundfonts.
Drumstick common.
Definition: alsaclient.cpp:68
SettingsFactory class declaration.