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