drumstick  2.5.0
fluidsettingsdialog.cpp
Go to the documentation of this file.
1 /*
2  Drumstick MIDI Sequencer C++ library
3  Copyright (C) 2006-2021, 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 
27 #include "fluidsettingsdialog.h"
28 #include "ui_fluidsettingsdialog.h"
31 
37 namespace drumstick { namespace widgets {
38 
39 const QString FluidSettingsDialog::QSTR_PREFERENCES = QStringLiteral("FluidSynth");
40 const QString FluidSettingsDialog::QSTR_INSTRUMENTSDEFINITION = QStringLiteral("InstrumentsDefinition");
41 const QString FluidSettingsDialog::QSTR_DATADIR = QStringLiteral("soundfonts");
42 const QString FluidSettingsDialog::QSTR_DATADIR2 = QStringLiteral("sounds/sf2");
43 const QString FluidSettingsDialog::QSTR_SOUNDFONT = QStringLiteral("default.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  }
89  ui->bufferTime->blockSignals(true);
90  ui->periodSize->blockSignals(true);
91  ui->periods->blockSignals(true);
92  //qDebug() << Q_FUNC_INFO;
93 }
94 
95 FluidSettingsDialog::~FluidSettingsDialog()
96 {
97  //qDebug() << Q_FUNC_INFO;
98  if (m_driver != nullptr) {
99  m_driver->close();
100  }
101  delete ui;
102 }
103 
104 bool FluidSettingsDialog::checkRanges() const
105 {
106  //qDebug() << Q_FUNC_INFO;
107  if (ui->gain->hasAcceptableInput()) {
108  ui->gain->deselect();
109  } else {
110  ui->gain->selectAll();
111  }
112  if (ui->polyphony->hasAcceptableInput()) {
113  ui->polyphony->deselect();
114  } else {
115  ui->polyphony->selectAll();
116  }
117  if (ui->sampleRate->hasAcceptableInput()) {
118  ui->sampleRate->deselect();
119  } else {
120  ui->sampleRate->selectAll();
121  }
122  return
123  ui->bufferTime->hasAcceptableInput() &&
124  ui->periodSize->hasAcceptableInput() &&
125  ui->periods->hasAcceptableInput() &&
126  ui->gain->hasAcceptableInput() &&
127  ui->polyphony->hasAcceptableInput() &&
128  ui->sampleRate->hasAcceptableInput();
129 }
130 
131 void FluidSettingsDialog::accept()
132 {
133  //qDebug() << Q_FUNC_INFO;
134  if (checkRanges()) {
135  writeSettings();
136  if (m_driver != nullptr) {
137  QString title;
138  QVariant varStatus = m_driver->property("status");
139  if (varStatus.isValid()) {
140  title = varStatus.toBool() ? tr("FluidSynth Initialized") : tr("FluidSynth Initialization Failed");
141  QVariant varDiag = m_driver->property("diagnostics");
142  if (varDiag.isValid()) {
143  QString text = varDiag.toStringList().join(QChar::LineFeed).trimmed();
144  if (varStatus.toBool()) {
145  if (!text.isEmpty()) {
146  QMessageBox::information(this, title, text);
147  }
148  } else {
149  QMessageBox::critical(this, title, text);
150  return;
151  }
152  }
153  }
154  }
155  QDialog::accept();
156  }
157 }
158 
159 void FluidSettingsDialog::showEvent(QShowEvent *event)
160 {
161  readSettings();
162  event->accept();
163 }
164 
165 QString FluidSettingsDialog::defaultAudioDriver() const
166 {
167  const QString QSTR_DEFAULT_AUDIODRIVER =
168 #if defined(Q_OS_WIN)
169  QLatin1String("wasapi");
170 #elif defined(Q_OS_OSX)
171  QLatin1String("coreaudio");
172 #elif defined(Q_OS_LINUX)
173  QSTR_PULSEAUDIO;
174 #else
175  QLatin1String("oss");
176 #endif
177  return QSTR_DEFAULT_AUDIODRIVER;
178 }
179 
180 void FluidSettingsDialog::chkDriverProperties(QSettings *settings)
181 {
182  //qDebug() << Q_FUNC_INFO;
183  if (m_driver != nullptr) {
185  m_driver->close();
186  m_driver->initialize(settings);
187  m_driver->open(conn);
188 
189  QVariant drivers = m_driver->property("audiodrivers");
190  if (drivers.isValid()) {
191  auto text = ui->audioDriver->currentText();
192  ui->audioDriver->blockSignals(true);
193  ui->audioDriver->clear();
194  ui->audioDriver->addItems(drivers.toStringList());
195  ui->audioDriver->setCurrentText(text);
196  ui->audioDriver->blockSignals(false);
197  }
198  QVariant varVersion = m_driver->property("libversion");
199  if (varVersion.isValid()) {
200  ui->lblVersion->clear();
201  ui->lblVersion->setText(varVersion.toString());
202  }
203  QVariant varStatus = m_driver->property("status");
204  if (varStatus.isValid()) {
205  ui->lblStatus->clear();
206  ui->lblStatus->setText(varStatus.toBool() ? tr("Ready") : tr("Failed") );
207  ui->lblStatusIcon->setPixmap(varStatus.toBool() ? QPixmap(":/checked.png") : QPixmap(":/error.png") );
208  }
209  }
210 }
211 
212 void drumstick::widgets::FluidSettingsDialog::initBuffer()
213 {
214  if (ui->audioDriver->currentText() == QSTR_PULSEAUDIO) {
215  //qDebug() << Q_FUNC_INFO << QSTR_PULSEAUDIO;
216  int bufferTime = ui->bufferTime->value();
217  int minBufTime = ui->bufferTime->minimum();
218  if (qEnvironmentVariableIsSet("PULSE_LATENCY_MSEC")) {
219  bufferTime = qEnvironmentVariable("PULSE_LATENCY_MSEC").toInt();
220  }
221  if (bufferTime < minBufTime) {
222  bufferTime = minBufTime;
223  }
224  ui->bufferTime->setValue( bufferTime );
225  bufferTimeChanged( bufferTime );
226  } else {
227  //qDebug() << Q_FUNC_INFO;
228  bufferSizeChanged();
229  }
230 }
231 
232 void FluidSettingsDialog::readSettings()
233 {
234  //qDebug() << Q_FUNC_INFO;
235  SettingsFactory settings;
236  QString fs_defSoundFont = QSTR_SOUNDFONT;
237  QDir dir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR, QStandardPaths::LocateDirectory));
238  if (!dir.exists()) {
239  dir = QDir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR2, QStandardPaths::LocateDirectory));
240  }
241  QFileInfo sf2(dir, QSTR_SOUNDFONT);
242  if (sf2.exists()) {
243  fs_defSoundFont = sf2.absoluteFilePath();
244  }
245 
246  settings->beginGroup(QSTR_PREFERENCES);
247  ui->audioDriver->setCurrentText( settings->value(QSTR_AUDIODRIVER, defaultAudioDriver()).toString() );
248  ui->bufferTime->setValue( settings->value(QSTR_BUFFERTIME, DEFAULT_BUFFERTIME).toInt() );
249  ui->periodSize->setValue( settings->value(QSTR_PERIODSIZE, DEFAULT_PERIODSIZE).toInt() );
250  ui->periods->setValue( settings->value(QSTR_PERIODS, DEFAULT_PERIODS).toInt() );
251  ui->sampleRate->setText( settings->value(QSTR_SAMPLERATE, DEFAULT_SAMPLERATE).toString() );
252  ui->chorus->setChecked( settings->value(QSTR_CHORUS, DEFAULT_CHORUS).toInt() != 0 );
253  ui->reverb->setChecked( settings->value(QSTR_REVERB, DEFAULT_REVERB).toInt() != 0 );
254  ui->gain->setText( settings->value(QSTR_GAIN, DEFAULT_GAIN).toString() );
255  ui->polyphony->setText( settings->value(QSTR_POLYPHONY, DEFAULT_POLYPHONY).toString() );
256  ui->soundFont->setText( settings->value(QSTR_INSTRUMENTSDEFINITION, fs_defSoundFont).toString() );
257  settings->endGroup();
258 
259  audioDriverChanged( ui->audioDriver->currentText() );
260  chkDriverProperties(settings.getQSettings());
261 }
262 
263 void FluidSettingsDialog::writeSettings()
264 {
265  //qDebug() << Q_FUNC_INFO;
266  SettingsFactory settings;
267  QString audioDriver;
268  QString soundFont(QSTR_SOUNDFONT);
269  int bufferTime(DEFAULT_BUFFERTIME);
270  int periodSize(DEFAULT_PERIODSIZE);
271  int periods(DEFAULT_PERIODS);
272  double sampleRate(DEFAULT_SAMPLERATE);
273  int chorus(DEFAULT_CHORUS);
274  int reverb(DEFAULT_REVERB);
275  double gain(DEFAULT_GAIN);
276  int polyphony(DEFAULT_POLYPHONY);
277 
278  audioDriver = ui->audioDriver->currentText();
279  if (audioDriver.isEmpty()) {
280  audioDriver = defaultAudioDriver();
281  }
282  soundFont = ui->soundFont->text();
283  bufferTime = ui->bufferTime->value();
284  periodSize = ui->periodSize->value();
285  periods = ui->periods->value();
286  sampleRate = ui->sampleRate->text().toDouble();
287  chorus = (ui->chorus->isChecked() ? 1 : 0);
288  reverb = (ui->reverb->isChecked() ? 1 : 0);
289  gain = ui->gain->text().toDouble();
290  polyphony = ui->polyphony->text().toInt();
291 
292  settings->beginGroup(QSTR_PREFERENCES);
293  settings->setValue(QSTR_INSTRUMENTSDEFINITION, soundFont);
294  settings->setValue(QSTR_AUDIODRIVER, audioDriver);
295  settings->setValue(QSTR_BUFFERTIME, bufferTime);
296  settings->setValue(QSTR_PERIODSIZE, periodSize);
297  settings->setValue(QSTR_PERIODS, periods);
298  settings->setValue(QSTR_SAMPLERATE, sampleRate);
299  settings->setValue(QSTR_CHORUS, chorus);
300  settings->setValue(QSTR_REVERB, reverb);
301  settings->setValue(QSTR_GAIN, gain);
302  settings->setValue(QSTR_POLYPHONY, polyphony);
303  settings->endGroup();
304  settings->sync();
305 
306  if ( audioDriver == QSTR_PULSEAUDIO ) {
307  qputenv("PULSE_LATENCY_MSEC", QByteArray::number( bufferTime ) );
308  }
309  chkDriverProperties(settings.getQSettings());
310 }
311 
312 void FluidSettingsDialog::restoreDefaults()
313 {
314  //qDebug() << Q_FUNC_INFO;
315  ui->audioDriver->setCurrentText( defaultAudioDriver() );
316  ui->bufferTime->setValue( DEFAULT_BUFFERTIME );
317  ui->periodSize->setValue( DEFAULT_PERIODSIZE );
318  ui->periods->setValue( DEFAULT_PERIODS );
319  ui->sampleRate->setText( QString::number( DEFAULT_SAMPLERATE ));
320  ui->chorus->setChecked( DEFAULT_CHORUS != 0 );
321  ui->reverb->setChecked( DEFAULT_REVERB != 0 );
322  ui->gain->setText( QString::number( DEFAULT_GAIN ) );
323  ui->polyphony->setText( QString::number( DEFAULT_POLYPHONY ));
324  ui->soundFont->setText( QSTR_SOUNDFONT );
325  initBuffer();
326 }
327 
328 void FluidSettingsDialog::showFileDialog()
329 {
330  QDir dir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR, QStandardPaths::LocateDirectory));
331  if (!dir.exists()) {
332  dir = QDir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR2, QStandardPaths::LocateDirectory));
333  }
334  QString fileName = QFileDialog::getOpenFileName(this, tr("Select SoundFont"), dir.absolutePath(), tr("SoundFont Files (*.sf2)"));
335  if (!fileName.isEmpty()) {
336  ui->soundFont->setText(fileName);
337  }
338 }
339 
340 void FluidSettingsDialog::audioDriverChanged(const QString &text)
341 {
342  //qDebug() << Q_FUNC_INFO << text;
343  if (text == QSTR_PULSEAUDIO) {
344  ui->bufferTime->setDisabled(false);
345  ui->bufferTime->blockSignals(false);
346  ui->periodSize->setDisabled(true);
347  ui->periodSize->blockSignals(true);
348  ui->periods->setDisabled(true);
349  ui->periods->blockSignals(true);
350  } else {
351  ui->bufferTime->setDisabled(true);
352  ui->bufferTime->blockSignals(true);
353  ui->periodSize->setDisabled(false);
354  ui->periodSize->blockSignals(false);
355  ui->periods->setDisabled(false);
356  ui->periods->blockSignals(false);
357  }
358  initBuffer();
359 }
360 
361 void FluidSettingsDialog::bufferTimeChanged(int value)
362 {
363  double rate = ui->sampleRate->text().toDouble();
364  int size = qRound( value * rate / 1000.0 );
365  ui->periodSize->setValue( size );
366  ui->periods->setValue( ui->periods->minimum() );
367  //qDebug() << Q_FUNC_INFO << "time:" << value << "rate:" << rate << "size:" << size;
368 }
369 
370 void FluidSettingsDialog::bufferSizeChanged()
371 {
372  QString audioDriver = ui->audioDriver->currentText();
373  double rate = ui->sampleRate->text().toDouble();
374  int size = ui->periodSize->value();
375  if (audioDriver != QSTR_PULSEAUDIO) {
376  size *= ui->periods->value();
377  }
378  int ms = qRound( 1000.0 * size / rate );
379  ui->bufferTime->setValue(ms);
380  //qDebug() << Q_FUNC_INFO << "time:" << ms << "rate:" << rate << "size:" << size;
381 }
382 
383 void FluidSettingsDialog::changeSoundFont(const QString& fileName)
384 {
385  readSettings();
386  ui->soundFont->setText(fileName);
387  writeSettings();
388 }
389 
390 } // namespace widgets
391 } // 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.