drumstick  1.0.2
synthengine.cpp
1 /*
2  Drumstick RT (realtime MIDI In/Out)
3  Copyright (C) 2009-2015 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 along
16  with this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <QDir>
20 #include <QFileInfo>
21 #include <QSettings>
22 #include <QStandardPaths>
23 #include <QCoreApplication>
24 #include <QDebug>
25 #include "synthengine.h"
26 
27 const QString QSTR_PREFERENCES("FluidSynth");
28 const QString QSTR_INSTRUMENTSDEFINITION("InstrumentsDefinition");
29 const QString QSTR_DATADIR("soundfonts");
30 const QString QSTR_SOUNDFONT("default.sf2");
31 
32 const QString QSTR_AUDIODRIVER("AudioDriver");
33 const QString QSTR_PERIODSIZE("PeriodSize");
34 const QString QSTR_PERIODS("Periods");
35 const QString QSTR_SAMPLERATE("SampleRate");
36 const QString QSTR_CHORUS("Chorus");
37 const QString QSTR_REVERB("Reverb");
38 const QString QSTR_GAIN("Gain");
39 const QString QSTR_POLYPHONY("Polyphony");
40 
41 const QString QSTR_DEFAULT_AUDIODRIVER =
42 #if defined(Q_OS_LINUX)
43  QLatin1Literal("alsa");
44 #elif defined(Q_OS_WIN)
45  QLatin1Literal("dsound");
46 #elif defined(Q_OS_OSX)
47  QLatin1Literal("coreaudio");
48 #else
49  QLatin1Literal("oss");
50 #endif
51 const int DEFAULT_PERIODS =
52 #if defined(Q_OS_WIN)
53  4;
54 #else
55  3;
56 #endif
57 const int DEFAULT_PERIODSIZE = 512;
58 const double DEFAULT_SAMPLERATE = 48000.0;
59 const int DEFAULT_CHORUS = 0;
60 const int DEFAULT_REVERB = 0;
61 const double DEFAULT_GAIN = .4;
62 const int DEFAULT_POLYPHONY = 32;
63 
64 SynthEngine::SynthEngine(QObject *parent)
65  : QObject(parent),
66  m_settings(0),
67  m_synth(0),
68  m_driver(0)
69 { }
70 
71 SynthEngine::~SynthEngine()
72 {
73  uninitialize();
74 }
75 
76 void SynthEngine::uninitialize()
77 {
78  if (m_driver != 0) {
79  ::delete_fluid_audio_driver(m_driver);
80  m_driver = 0;
81  }
82  if (m_synth != 0) {
83  ::delete_fluid_synth(m_synth);
84  m_synth = 0;
85  }
86  if (m_settings != 0) {
87  ::delete_fluid_settings(m_settings);
88  m_settings = 0;
89  }
90 }
91 
92 void SynthEngine::initializeSynth(QSettings* settings)
93 {
94  QString fs_audiodriver = QSTR_DEFAULT_AUDIODRIVER;
95  int fs_periodSize = DEFAULT_PERIODSIZE;
96  int fs_periods = DEFAULT_PERIODS;
97  double fs_sampleRate = DEFAULT_SAMPLERATE;
98  int fs_chorus = DEFAULT_CHORUS;
99  int fs_reverb = DEFAULT_REVERB;
100  double fs_gain = DEFAULT_GAIN;
101  int fs_polyphony = DEFAULT_POLYPHONY;
102  if (settings != 0) {
103  settings->beginGroup(QSTR_PREFERENCES);
104  fs_audiodriver = settings->value(QSTR_AUDIODRIVER, QSTR_DEFAULT_AUDIODRIVER).toString();
105  fs_periodSize = settings->value(QSTR_PERIODSIZE, DEFAULT_PERIODSIZE).toInt();
106  fs_periods = settings->value(QSTR_PERIODS, DEFAULT_PERIODS).toInt();
107  fs_sampleRate = settings->value(QSTR_SAMPLERATE, DEFAULT_SAMPLERATE).toDouble();
108  fs_chorus = settings->value(QSTR_CHORUS, DEFAULT_CHORUS).toInt();
109  fs_reverb = settings->value(QSTR_REVERB, DEFAULT_REVERB).toInt();
110  fs_gain = settings->value(QSTR_GAIN, DEFAULT_GAIN).toDouble();
111  fs_polyphony = settings->value(QSTR_POLYPHONY, DEFAULT_POLYPHONY).toInt();
112  settings->endGroup();
113  }
114  uninitialize();
115  m_settings = ::new_fluid_settings();
116  ::fluid_settings_setstr(m_settings, "audio.driver", qPrintable(fs_audiodriver));
117  ::fluid_settings_setint(m_settings, "audio.period-size", fs_periodSize);
118  ::fluid_settings_setint(m_settings, "audio.periods", fs_periods);
119  ::fluid_settings_setnum(m_settings, "synth.sample-rate", fs_sampleRate);
120  ::fluid_settings_setint(m_settings, "synth.chorus.active", fs_chorus);
121  ::fluid_settings_setint(m_settings, "synth.reverb.active", fs_reverb);
122  ::fluid_settings_setnum(m_settings, "synth.gain", fs_gain);
123  ::fluid_settings_setint(m_settings, "synth.polyphony", fs_polyphony);
124  m_synth = ::new_fluid_synth(m_settings);
125  m_driver = ::new_fluid_audio_driver(m_settings, m_synth);
126 }
127 
128 void SynthEngine::setInstrument(int channel, int pgm)
129 {
130  ::fluid_synth_program_change(m_synth, channel, pgm);
131 }
132 
133 void SynthEngine::noteOn(int channel, int midiNote, int velocity)
134 {
135  ::fluid_synth_noteon(m_synth, channel, midiNote, velocity);
136 }
137 
138 void SynthEngine::noteOff(int channel, int midiNote, int /*velocity*/)
139 {
140  ::fluid_synth_noteoff(m_synth, channel, midiNote);
141 }
142 
143 void SynthEngine::loadSoundFont()
144 {
145  if (m_sfid != -1) {
146  ::fluid_synth_sfunload(m_synth, unsigned(m_sfid), 1);
147  }
148  m_sfid = ::fluid_synth_sfload(m_synth, qPrintable(m_soundFont), 1);
149 }
150 
151 void SynthEngine::initialize(QSettings *settings)
152 {
153  initializeSynth(settings);
154  scanSoundFonts();
155  loadSoundFont();
156  if (m_sfid < 0) {
157  m_soundFont = m_defSoundFont;
158  loadSoundFont();
159  }
160 }
161 
162 void SynthEngine::panic()
163 {
164  ::fluid_synth_system_reset(m_synth);
165 }
166 
167 void SynthEngine::controlChange(const int channel, const int midiCtl, const int value)
168 {
169  ::fluid_synth_cc(m_synth, channel, midiCtl, value);
170 }
171 
172 void SynthEngine::bender(const int channel, const int value)
173 {
174  ::fluid_synth_pitch_bend(m_synth, channel, value + 8192);
175 }
176 
177 void SynthEngine::setSoundFont(const QString &value)
178 {
179  if (value != m_soundFont) {
180  m_soundFont = value;
181  loadSoundFont();
182  }
183 }
184 
185 void SynthEngine::scanSoundFonts(const QDir &initialDir)
186 {
187  QDir dir(initialDir);
188  dir.setFilter(QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot);
189  dir.setSorting(QDir::Name);
190  QStringList filters;
191  filters << "*.sf2" << "*.SF2";
192  QFileInfoList entries= dir.entryInfoList(filters);
193  foreach(const QFileInfo &info, entries) {
194  QString name = info.absoluteFilePath();
195  if (info.isFile()) {
196  m_soundFontsList << name;
197  } else if (info.isDir()){
198  scanSoundFonts(name);
199  }
200  }
201 }
202 
203 void SynthEngine::scanSoundFonts()
204 {
205  m_soundFontsList.clear();
206  QStringList paths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
207 #if defined(Q_OS_OSX)
208  paths << (QCoreApplication::applicationDirPath() + QLatin1Literal("../Resources"));
209 #endif
210  foreach(const QString& p, paths) {
211  QDir d(p + QDir::separator() + QSTR_DATADIR);
212  if (d.exists()) {
213  scanSoundFonts(d);
214  }
215  }
216 }
217 
218 void SynthEngine::readSettings(QSettings *settings)
219 {
220  QDir dir;
221 #if defined(Q_OS_OSX)
222  dir = QDir(QCoreApplication::applicationDirPath() + QLatin1Literal("/../Resources"));
223 #elif defined(Q_OS_UNIX)
224  dir = QDir(QCoreApplication::applicationDirPath() + QLatin1Literal("/../share/soundfonts/"));
225 #else
226  dir = QDir(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QSTR_DATADIR, QStandardPaths::LocateDirectory));
227 #endif
228  QFileInfo sf2(dir, QSTR_SOUNDFONT);
229  if (sf2.exists()) {
230  m_defSoundFont = sf2.absoluteFilePath();
231  }
232  m_sfid = -1;
233  qDebug() << "defSoundFont:" << m_defSoundFont;
234  settings->beginGroup(QSTR_PREFERENCES);
235  m_soundFont = settings->value(QSTR_INSTRUMENTSDEFINITION, m_defSoundFont).toString();
236  settings->endGroup();
237 }
238 
239 void SynthEngine::close()
240 {
241  m_currentConnection.clear();
242  uninitialize();
243 }
244 
245 void SynthEngine::open()
246 {
247  m_currentConnection = QSTR_FLUIDSYNTH;
248 }
249 
The QObject class is the base class of all Qt objects.