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