drumstick  2.10.0
C++MIDIlibrariesusingQtobjects,idioms,andstyle.
backendmanager.cpp
Go to the documentation of this file.
1 /*
2  Drumstick RT (realtime MIDI In/Out)
3  Copyright (C) 2009-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 <QCoreApplication>
20 #include <QDir>
21 #include <QLibraryInfo>
22 #include <QPluginLoader>
23 #include <QtGlobal>
25 
31 namespace drumstick { namespace rt {
32 
50  class BackendManager::BackendManagerPrivate {
51  public:
52  QList<QPluginLoader *> m_loaders;
53  QList<MIDIInput *> m_inputsList;
54  QList<MIDIOutput *> m_outputsList;
55 
56  QString m_inputBackend{QLatin1String("Network")};
57 #if defined(Q_OS_LINUX)
58  QStringList m_outputBackends{QLatin1String("SonivoxEAS"),QLatin1String("FluidSynth"),QLatin1String("ALSA")};
59 #elif defined(Q_OS_DARWIN)
60  QStringList m_outputBackends{QLatin1String("DLS Synth"),QLatin1String("FluidSynth"),QLatin1String("CoreMIDI")};
61 #elif defined(Q_OS_WINDOWS)
62  QStringList m_outputBackends{QLatin1String("Windows MM"),QLatin1String("FluidSynth")};
63 #elif defined(Q_OS_UNIX)
64  QStringList m_outputBackends{QLatin1String("FluidSynth"),QLatin1String("OSS")};
65 #else
66  QStringList m_outputBackends{m_inputBackend};
67 #endif
68 
69  ~BackendManagerPrivate()
70  {
71  clearLists();
72  }
73 
74  void clearLists()
75  {
76  // qDebug() << Q_FUNC_INFO << "loaders:" << m_loaders.count()
77  // << "inputs:" << m_inputsList.count() << "outputs:" << m_outputsList.count();
78  while (!m_loaders.empty()) {
79  QPluginLoader* pluginLoader = m_loaders.takeFirst();
80  //qDebug() << "unloading:" << pluginLoader->fileName();
81  pluginLoader->unload();
82  delete pluginLoader;
83  }
84  m_inputsList.clear();
85  m_outputsList.clear();
86  m_loaders.clear();
87  }
88 
89  void appendDir(const QString &candidate, QStringList &result)
90  {
91  QDir checked(candidate.trimmed());
92  //qDebug() << Q_FUNC_INFO << candidate << "exists:" << checked.exists();
93  if (checked.exists() && !result.contains(checked.absolutePath())) {
94  result << checked.absolutePath();
95  }
96  }
97 
98  bool isLoaderNeeded(const QString &fileName)
99  {
100  auto it = std::find_if(m_loaders.constBegin(),
101  m_loaders.constEnd(),
102  [=](QPluginLoader *loader) {
103  return loader->fileName() == fileName;
104  });
105  return it == m_loaders.constEnd();
106  }
107  };
108 
113  : d{new BackendManagerPrivate}
114  {
115  //qDebug() << Q_FUNC_INFO;
116  QVariantMap defaultSettings {
117  { QSTR_DRUMSTICKRT_PUBLICNAMEIN, QStringLiteral("MIDI In")},
118  { QSTR_DRUMSTICKRT_PUBLICNAMEOUT, QStringLiteral("MIDI Out")}
119  };
120  refresh(defaultSettings);
121  }
122 
127  {
128  //qDebug() << Q_FUNC_INFO;
129  }
130 
136  {
137  QStringList result;
138  QString appPath = QCoreApplication::applicationDirPath() + QDir::separator();
139  #if defined(Q_OS_WIN)
140  d->appendDir( appPath + QSTR_DRUMSTICK, result );
141  d->appendDir( appPath + "../lib/" + QSTR_DRUMSTICK, result );
142  #else
143  #if defined(Q_OS_MAC)
144  d->appendDir( appPath + QStringLiteral("../PlugIns/") + QSTR_DRUMSTICK, result );
145  #endif // Linux, Unix...
146  QStringList libs;
147  libs << "../lib/";
148  #if defined(LIBSUFFIX)
149  QString libextra(QT_STRINGIFY(LIBSUFFIX));
150  if (QDir::isAbsolutePath(libextra)) {
151  d->appendDir( libextra + QDir::separator() + QSTR_DRUMSTICK, result );
152  } else {
153  libs << QString("../%1/").arg(libextra);
154  }
155  #endif
156  foreach(const QString& lib, libs) {
157  d->appendDir( appPath + lib + QSTR_DRUMSTICK, result );
158  }
159  #endif
160  d->appendDir( appPath + ".." + QDir::separator() + QSTR_DRUMSTICK, result );
161  QByteArray envdir = qgetenv(QSTR_DRUMSTICKRT.toLatin1());
162  //qDebug() << Q_FUNC_INFO << "envdir:" << envdir;
163  if(!envdir.isEmpty()) {
164  d->appendDir(QString(envdir), result );
165  }
166  d->appendDir( QDir::homePath() + QDir::separator() + QSTR_DRUMSTICK, result );
167 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
168  d->appendDir( QLibraryInfo::location(QLibraryInfo::PluginsPath) + QDir::separator() + QSTR_DRUMSTICK, result );
169 #else
170  d->appendDir( QLibraryInfo::path(QLibraryInfo::PluginsPath) + QDir::separator() + QSTR_DRUMSTICK, result );
171 #endif
172  foreach(const QString& path, QCoreApplication::libraryPaths()) {
173  d->appendDir( path + QDir::separator() + QSTR_DRUMSTICK, result );
174  }
175  return result;
176  }
177 
183  {
184  //qDebug() << Q_FUNC_INFO;
185  QVariantMap tmpMap;
186  settings->beginGroup(QSTR_DRUMSTICKRT_GROUP);
187  const QStringList allKeys = settings->allKeys();
188  //qDebug() << Q_FUNC_INFO << allKeys;
189  for(const auto &k : allKeys) {
190  tmpMap.insert(k, settings->value(k));
191  }
192  settings->endGroup();
193  refresh(tmpMap);
194  }
195 
201  void BackendManager::refresh(const QVariantMap &map)
202  {
203  QString name_in;
204  QString name_out;
205  QStringList names;
206  QStringList paths;
207 
208  d->appendDir(map.value(QSTR_DRUMSTICKRT_PATH).toString(), paths);
209  name_in = map.value(QSTR_DRUMSTICKRT_PUBLICNAMEIN).toString();
210  name_out = map.value(QSTR_DRUMSTICKRT_PUBLICNAMEOUT).toString();
211  names << map.value(QSTR_DRUMSTICKRT_EXCLUDED).toStringList();
212  names << (name_in.isEmpty() ? QStringLiteral("MIDI In") : name_in);
213  names << (name_out.isEmpty() ? QStringLiteral("MIDI Out") : name_out);
214  paths << defaultPaths();
215 
216  //qDebug() << Q_FUNC_INFO << "names:" << names << "paths:" << paths;
217 
218  // Dynamic backends
219  foreach(const QString& dir, paths) {
220  QDir pluginsDir(dir);
221  foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
222  auto absolutePath = pluginsDir.absoluteFilePath(fileName);
223  if (QLibrary::isLibrary(absolutePath) && d->isLoaderNeeded(absolutePath)) {
224  QPluginLoader *loader = new QPluginLoader(absolutePath);
225  //qDebug() << "plugin loader created:" << loader->fileName();
226  d->m_loaders << loader;
227  QObject *obj = loader->instance();
228  if (obj != nullptr) {
229  MIDIInput *input = qobject_cast<MIDIInput *>(obj);
230  if (input != nullptr && !d->m_inputsList.contains(input)) {
231  //qDebug() << "input plugin instantiated:" << name_in;
232  if (!name_in.isEmpty()) {
233  input->setPublicName(name_in);
234  }
235  input->setExcludedConnections(names);
236  d->m_inputsList << input;
237  } else {
238  MIDIOutput *output = qobject_cast<MIDIOutput *>(obj);
239  if (output != nullptr && !d->m_outputsList.contains(output)) {
240  //qDebug() << "output plugin instantiated:" << name_out;
241  if (!name_out.isEmpty()) {
242  output->setPublicName(name_out);
243  }
244  output->setExcludedConnections(names);
245  d->m_outputsList << output;
246  }
247  }
248  }
249  }
250  }
251  }
252 
253  // Static backends
254  foreach(QObject* obj, QPluginLoader::staticInstances()) {
255  if (obj != nullptr) {
256  MIDIInput *input = qobject_cast<MIDIInput*>(obj);
257  if (input != nullptr && !d->m_inputsList.contains(input)) {
258  if (!name_in.isEmpty()) {
259  input->setPublicName(name_in);
260  }
261  input->setExcludedConnections(names);
262  d->m_inputsList << input;
263  } else {
264  MIDIOutput *output = qobject_cast<MIDIOutput*>(obj);
265  if (output != nullptr && !d->m_outputsList.contains(output)) {
266  if (!name_out.isEmpty()) {
267  output->setPublicName(name_out);
268  }
269  output->setExcludedConnections(names);
270  d->m_outputsList << output;
271  }
272  }
273  }
274  }
275  }
276 
278  {
279  return d->m_inputsList;
280  }
281 
282  QList<MIDIOutput*> BackendManager::availableOutputs()
283  {
284  return d->m_outputsList;
285  }
286 
288  {
289  foreach (MIDIInput* i, d->m_inputsList) {
290  if (i->backendName() == name) {
291  return i;
292  }
293  }
294  return nullptr;
295  }
296 
298  {
299  foreach (MIDIOutput* i, d->m_outputsList) {
300  if (i->backendName() == name) {
301  return i;
302  }
303  }
304  return nullptr;
305  }
306 
308  {
309  QStringList names{name};
310  names << d->m_inputBackend;
311  names.removeDuplicates();
312  if (!names.isEmpty()) {
313  foreach(const QString& n, names) {
314  foreach(MIDIInput* input, d->m_inputsList) {
315  if (input->backendName() == n) {
316  return input;
317  }
318  }
319  }
320  }
321  return nullptr;
322  }
323 
325  {
326  QStringList names{name};
327  names << d->m_outputBackends;
328  names.removeDuplicates();
329  if (!names.isEmpty()) {
330  foreach(const QString& n, names) {
331  foreach(MIDIOutput* output, d->m_outputsList) {
332  if (output->backendName() == n) {
333  return output;
334  }
335  }
336  }
337  }
338  return nullptr;
339  }
340 
341  const QString BackendManager::QSTR_DRUMSTICK = QStringLiteral("drumstick2");
342  const QString BackendManager::QSTR_DRUMSTICK_VERSION = QStringLiteral(QT_STRINGIFY(VERSION));
343  const QString BackendManager::QSTR_DRUMSTICKRT = QStringLiteral("DRUMSTICKRT");
344  const QString BackendManager::QSTR_DRUMSTICKRT_GROUP = QStringLiteral("DrumstickRT");
345  const QString BackendManager::QSTR_DRUMSTICKRT_PUBLICNAMEIN = QStringLiteral("PublicNameIN");
346  const QString BackendManager::QSTR_DRUMSTICKRT_PUBLICNAMEOUT = QStringLiteral("PublicNameOUT");
347  const QString BackendManager::QSTR_DRUMSTICKRT_EXCLUDED = QStringLiteral("ExcludedNames");
348  const QString BackendManager::QSTR_DRUMSTICKRT_PATH = QStringLiteral("BackendsPath");
349 
355  {
356  return BackendManager::QSTR_DRUMSTICK_VERSION;
357  }
358 
359 } // namespace rt
360 } // namespace drumstick
virtual void setPublicName(QString name)=0
setPublicName
QString DRUMSTICK_RT_EXPORT drumstickLibraryVersion()
drumstickLibraryVersion provides the Drumstick version as an edited QString
QList< MIDIOutput * > availableOutputs()
availableOutputs
The QSettings class provides persistent platform-independent application settings.
QStringList defaultPaths()
defaultPaths
virtual void setExcludedConnections(QStringList conns)=0
setExcludedConnections
QList< MIDIInput * > availableInputs()
availableInputs
MIDI IN interface.
Definition: rtmidiinput.h:55
The QObject class is the base class of all Qt objects.
MIDIOutput * findOutput(QString name)
findOutput returns the backend corresponding to the provided name, or a suitable output instead...
void refresh(QSettings *settings=nullptr)
refresh the list of backends
BackendManager class declaration.
Drumstick common.
Definition: alsaclient.cpp:71
virtual void setExcludedConnections(QStringList conns)=0
setExcludedConnections
virtual ~BackendManager()
~BackendManager destructor
virtual QString backendName()=0
backendName
virtual QString backendName()=0
backendName
MIDIInput * findInput(QString name)
findInput returns the backend corresponding to the provided name, or a suitable input instead...
BackendManager()
BackendManager constructor.
MIDIOutput * outputBackendByName(const QString name)
outputBackendByName
MIDIInput * inputBackendByName(const QString name)
inputBackendByName
virtual void setPublicName(QString name)=0
setPublicName
MIDI OUT interface.
Definition: rtmidioutput.h:121