diff --git a/src/app/core.cpp b/src/app/core.cpp index be5eb0f..ad3669d 100644 --- a/src/app/core.cpp +++ b/src/app/core.cpp @@ -1,91 +1,106 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #include "core.h" +#include + #include "uicontroller.h" #include "plugincontroller.h" #include "exercisecontroller.h" #include namespace Minuet { Core::~Core() { } bool Core::initialize() { if (m_self) return true; + qRegisterMetaType("State"); + qmlRegisterInterface("ISoundController"); + qmlRegisterUncreatableType("org.kde.minuet.isoundcontroller", 1, 0, "ISoundController", "ISoundController cannot be instantiated"); + m_self = new Core; return true; } IPluginController *Core::pluginController() { return m_pluginController; } ISoundController *Core::soundController() { return m_soundController; } IExerciseController *Core::exerciseController() { return m_exerciseController; } IUiController *Core::uiController() { return m_uiController; } void Core::setSoundController(ISoundController *soundController) { if (m_soundController != soundController) { m_soundController = soundController; emit soundControllerChanged(m_soundController); } } Core::Core(QObject *parent) : ICore(parent), m_soundController(0) { m_pluginController = new PluginController(this); - ((PluginController *)m_pluginController)->initialize(this); + if (!((PluginController *)m_pluginController)->initialize(this)) { + qCritical() << m_pluginController->errorString(); + exit(-1); + } m_exerciseController = new ExerciseController(this); - ((ExerciseController *)m_exerciseController)->initialize(this); + if (!((ExerciseController *)m_exerciseController)->initialize(this)) { + qCritical() << m_exerciseController->errorString(); + exit(-2); + } m_uiController = new UiController(this); - ((UiController *)m_uiController)->initialize(this); + if (!((UiController *)m_uiController)->initialize(this)) { + qCritical() << m_uiController->errorString(); + exit(-3); + } } } diff --git a/src/app/exercisecontroller.cpp b/src/app/exercisecontroller.cpp index e14179e..ebe7337 100644 --- a/src/app/exercisecontroller.cpp +++ b/src/app/exercisecontroller.cpp @@ -1,252 +1,252 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #include "exercisecontroller.h" #if !defined(Q_OS_ANDROID) #include #endif #include #include #include #include #include namespace Minuet { ExerciseController::ExerciseController(QObject *parent) : IExerciseController(parent), m_chosenRootNote(0) { m_exercises["exercises"] = QJsonArray(); m_definitions["definitions"] = QJsonArray(); } ExerciseController::~ExerciseController() { } bool ExerciseController::initialize(Core *core) { Q_UNUSED(core) + m_errorString.clear(); bool definitionsMerge = mergeJsonFiles("definitions", m_definitions); bool exercisesMerge = mergeJsonFiles("exercises", m_exercises, true, "name", "children"); // QFile file("merged-exercises.json"); // file.open(QIODevice::WriteOnly); // file.write(QJsonDocument(m_exercises).toJson()); // file.close(); return definitionsMerge & exercisesMerge; } +QString ExerciseController::errorString() const +{ + return m_errorString; +} + void ExerciseController::randomlySelectExerciseOptions() { while (!m_selectedExerciseOptions.isEmpty()) m_selectedExerciseOptions.removeFirst(); qsrand(QDateTime::currentDateTimeUtc().toTime_t()); int minNote = INT_MAX; int maxNote = INT_MIN; quint8 numberOfSelectedOptions = m_currentExercise[QStringLiteral("numberOfSelectedOptions")].toInt(); for (quint8 i = 0; i < numberOfSelectedOptions; ++i) { QJsonArray exerciseOptions = QJsonObject::fromVariantMap(m_currentExercise)[QStringLiteral("options")].toArray(); quint8 chosenExerciseOption = qrand() % exerciseOptions.size(); QString sequence = exerciseOptions[chosenExerciseOption].toObject()[QStringLiteral("sequence")].toString(); foreach(const QString &additionalNote, sequence.split(' ')) { int note = additionalNote.toInt(); if (note > maxNote) maxNote = note; if (note < minNote) minNote = note; } if (m_currentExercise["playMode"].toString() != "rhythm") { QStringList exerciseRoots = m_currentExercise["root"].toString().split('.'); quint8 exerciseMinRoot = exerciseRoots.first().toInt(); quint8 exerciseMaxRoot = exerciseRoots.last().toInt(); do m_chosenRootNote = exerciseMinRoot + qrand() % (exerciseMaxRoot - exerciseMinRoot); while (m_chosenRootNote + maxNote > 108 || m_chosenRootNote + minNote < 21); } QJsonObject jsonObject = exerciseOptions[chosenExerciseOption].toObject(); jsonObject["rootNote"] = QString::number(m_chosenRootNote); exerciseOptions[chosenExerciseOption] = jsonObject; m_selectedExerciseOptions.append(exerciseOptions[chosenExerciseOption]); } emit selectedExerciseOptionsChanged(m_selectedExerciseOptions); } unsigned int ExerciseController::chosenRootNote() { return m_chosenRootNote; } -QString ExerciseController::errorString() const -{ - return m_errorString; -} - QJsonArray ExerciseController::exercises() const { return m_exercises[QStringLiteral("exercises")].toArray(); } bool ExerciseController::mergeJsonFiles(const QString directoryName, QJsonObject &targetObject, bool applyDefinitionsFlag, QString commonKey, QString mergeKey) { - m_errorString.clear(); #if defined(Q_OS_ANDROID) QStringList jsonDirs; jsonDirs += "/data/data/org.kde.minuet/qt-reserved-files/share/minuet/" + directoryName; #elif defined(Q_OS_LINUX) QStringList jsonDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, directoryName, QStandardPaths::LocateDirectory); #elif defined(Q_OS_WIN) QStringList jsonDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("minuet/") + directoryName, QStandardPaths::LocateDirectory); #endif foreach (const QString &jsonDirString, jsonDirs) { QDir jsonDir(jsonDirString); foreach (const QString &json, jsonDir.entryList(QDir::Files)) { QFile jsonFile(jsonDir.absoluteFilePath(json)); if (!jsonFile.open(QIODevice::ReadOnly)) { #if !defined(Q_OS_ANDROID) m_errorString = i18n("Could not open JSON file \"%1\".", jsonDir.absoluteFilePath(json)); #else m_errorString = QStringLiteral("Couldn't open json file \"%1\".").arg(jsonDir.absoluteFilePath(json)); #endif return false; } QJsonParseError error; QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonFile.readAll(), &error); if (error.error != QJsonParseError::NoError) { - m_errorString = error.errorString(); + m_errorString += QStringLiteral("Error when parsing JSON file '%1'. ").arg(jsonDir.absoluteFilePath(json)); jsonFile.close(); return false; } else { QJsonObject jsonObject = jsonDocument.object(); if (applyDefinitionsFlag) jsonObject[directoryName] = applyDefinitions(jsonObject[directoryName].toArray(), m_definitions[QStringLiteral("definitions")].toArray()); targetObject[directoryName] = mergeJsonArrays(targetObject[directoryName].toArray(), jsonObject[directoryName].toArray(), commonKey, mergeKey); } jsonFile.close(); } } return true; } QJsonArray ExerciseController::applyDefinitions(QJsonArray exercises, QJsonArray definitions, QJsonObject collectedProperties) { QJsonArray::const_iterator exercisesBegin = exercises.constBegin(); QJsonArray::const_iterator exercisesEnd = exercises.constEnd(); for (QJsonArray::ConstIterator i1 = exercisesBegin; i1 < exercisesEnd; ++i1) { if (i1->isObject()) { QJsonObject exerciseObject = i1->toObject(); QJsonArray filteredDefinitions = definitions; QStringList exerciseObjectKeys = exerciseObject.keys(); if (exerciseObjectKeys.contains(QStringLiteral("and-tags")) && exerciseObject[QStringLiteral("and-tags")].isArray()) filterDefinitions(filteredDefinitions, exerciseObject, "and-tags", AndFiltering); if (exerciseObjectKeys.contains(QStringLiteral("or-tags")) && exerciseObject[QStringLiteral("or-tags")].isArray()) filterDefinitions(filteredDefinitions, exerciseObject, "or-tags", OrFiltering); if (exerciseObjectKeys.contains(QStringLiteral("children"))) { foreach(const QString &key, exerciseObjectKeys) if (key != "name" && key != "children" && key != "and-tags" && key != "or-tags" && !key.startsWith('_')) { collectedProperties.insert(key, exerciseObject[key]); exerciseObject.remove(key); } exerciseObject[QStringLiteral("children")] = applyDefinitions(exerciseObject[QStringLiteral("children")].toArray(), filteredDefinitions, collectedProperties); } else { foreach(const QString &key, collectedProperties.keys()) if (!exerciseObject.contains(key)) exerciseObject.insert(key, collectedProperties[key]); exerciseObject.insert("options", filteredDefinitions); } exercises[i1-exercisesBegin] = exerciseObject; } } return exercises; } void ExerciseController::filterDefinitions(QJsonArray &definitions, QJsonObject &exerciseObject, const QString &filterTagsKey, DefinitionFilteringMode definitionFilteringMode) { QJsonArray filterTags = exerciseObject[filterTagsKey].toArray(); exerciseObject.remove(filterTagsKey); for (QJsonArray::Iterator i2 = definitions.begin(); i2 < definitions.end(); ++i2) { bool remove = (definitionFilteringMode == AndFiltering) ? false:true; QJsonArray::const_iterator filterTagsEnd = filterTags.constEnd(); for (QJsonArray::ConstIterator i3 = filterTags.constBegin(); i3 < filterTagsEnd; ++i3) { QJsonArray tagArray = i2->toObject()["tags"].toArray(); if (definitionFilteringMode == AndFiltering && !tagArray.contains(*i3)) { remove = true; break; } if (definitionFilteringMode == OrFiltering && tagArray.contains(*i3)) remove = false; } if (remove) { i2 = definitions.erase(i2); i2--; } } } QJsonArray ExerciseController::mergeJsonArrays(QJsonArray oldFile, QJsonArray newFile, QString commonKey, QString mergeKey) { QJsonArray::const_iterator newFileEnd = newFile.constEnd();; for (QJsonArray::ConstIterator i1 = newFile.constBegin(); i1 < newFileEnd; ++i1) { if (i1->isObject()) { QJsonArray::ConstIterator i2; QJsonArray::const_iterator oldFileEnd = oldFile.constEnd(); for (i2 = oldFile.constBegin(); i2 < oldFileEnd; ++i2) { QJsonObject newFileObject = i1->toObject(); QJsonObject oldFileObject = i2->toObject(); if (i2->isObject() && i1->isObject() && !commonKey.isEmpty() && oldFileObject[commonKey] == newFileObject[commonKey]) { QJsonObject jsonObject = oldFile[i2-oldFile.constBegin()].toObject(); jsonObject[mergeKey] = mergeJsonArrays(oldFileObject[mergeKey].toArray(), newFileObject[mergeKey].toArray(), commonKey, mergeKey); oldFile[i2-oldFile.constBegin()] = jsonObject; break; } } if (i2 == oldFile.constEnd()) oldFile.append(*i1); } } return oldFile; } } diff --git a/src/app/exercisecontroller.h b/src/app/exercisecontroller.h index 86f4763..5ababaf 100644 --- a/src/app/exercisecontroller.h +++ b/src/app/exercisecontroller.h @@ -1,72 +1,72 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #ifndef MINUET_EXERCISECONTROLLER_H #define MINUET_EXERCISECONTROLLER_H #include #include #include namespace Minuet { class Core; class ExerciseController : public IExerciseController { Q_OBJECT public: explicit ExerciseController(QObject *parent= 0); virtual ~ExerciseController(); bool initialize(Core *core); + virtual QString errorString() const; Q_INVOKABLE unsigned int chosenRootNote(); - QString errorString() const; virtual QJsonArray exercises() const override; public Q_SLOTS: virtual void randomlySelectExerciseOptions(); private: bool mergeJsonFiles(const QString directoryName, QJsonObject &targetObject, bool applyDefinitionsFlag = false, QString commonKey = "", QString mergeKey = ""); QJsonArray applyDefinitions(QJsonArray exercises, QJsonArray definitions, QJsonObject collectedProperties = QJsonObject()); enum DefinitionFilteringMode { AndFiltering = 0, OrFiltering }; void filterDefinitions(QJsonArray &definitions, QJsonObject &exerciseObject, const QString &filterTagsKey, DefinitionFilteringMode definitionFilteringMode); QJsonArray mergeJsonArrays(QJsonArray oldFile, QJsonArray newFile, QString commonKey = "", QString mergeKey = ""); QJsonObject m_exercises; QJsonObject m_definitions; unsigned int m_chosenRootNote; QString m_errorString; }; } #endif // MINUET_EXERCISECONTROLLER_H diff --git a/src/app/plugincontroller.cpp b/src/app/plugincontroller.cpp index ecd6cad..304d944 100644 --- a/src/app/plugincontroller.cpp +++ b/src/app/plugincontroller.cpp @@ -1,96 +1,106 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #include "plugincontroller.h" #include "core.h" #if defined(Q_OS_ANDROID) #include "../plugins/csoundsoundcontroller/csoundsoundcontroller.h" #endif #include #include #if !defined(Q_OS_ANDROID) #include #endif #include namespace Minuet { PluginController::PluginController(QObject *parent) : IPluginController(parent) { #if !defined(Q_OS_ANDROID) m_plugins = KPluginLoader::findPlugins(QStringLiteral("minuet"), [&](const KPluginMetaData &meta) { if (!meta.serviceTypes().contains(QStringLiteral("Minuet/Plugin"))) { - qDebug() << "Plugin" << meta.fileName() << "is installed into the minuet plugin directory, but does not have" + qWarning() << "Plugin" << meta.fileName() << "is installed into the minuet plugin directory, but does not have" " \"Minuet/Plugin\" set as the service type. This plugin will not be loaded."; return false; } return true; }); #endif } PluginController::~PluginController() { #if !defined(Q_OS_ANDROID) qDeleteAll(m_loadedPlugins.values().begin(), m_loadedPlugins.values().end()); m_loadedPlugins.clear(); #endif } bool PluginController::initialize(Core *core) { + m_errorString.clear(); #if !defined(Q_OS_ANDROID) + ISoundController *soundController = 0; foreach (const KPluginMetaData &pluginMetaData, m_plugins) { if (m_loadedPlugins.value(pluginMetaData)) continue; KPluginLoader loader(pluginMetaData.fileName()); IPlugin *plugin = qobject_cast(loader.instance()); if (plugin) { m_loadedPlugins.insert(pluginMetaData, plugin); - ISoundController *soundController = 0; if (!core->soundController() && (soundController = qobject_cast(plugin))) { - qDebug() << "Setting soundcontroller to" << soundController->metaObject()->className(); + qInfo() << "Setting soundcontroller to" << soundController->metaObject()->className(); core->setSoundController(soundController); } } } + if (!soundController) { + m_errorString = QStringLiteral("Could not find a suitable SoundController plugin!"); + return false; + } #else ISoundController *soundController = 0; if (!core->soundController() && (soundController = new CsoundSoundController)) { - qDebug() << "Setting soundcontroller to" << soundController->metaObject()->className(); + qInfo() << "Setting soundcontroller to" << soundController->metaObject()->className(); core->setSoundController(soundController); } #endif return true; } +QString PluginController::errorString() const +{ + return m_errorString; +} + } diff --git a/src/app/plugincontroller.h b/src/app/plugincontroller.h index f867030..19c6c89 100644 --- a/src/app/plugincontroller.h +++ b/src/app/plugincontroller.h @@ -1,59 +1,61 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #ifndef MINUET_PLUGINCONTROLLER_H #define MINUET_PLUGINCONTROLLER_H #include #ifndef Q_OS_ANDROID #include #endif namespace Minuet { class Core; class IPlugin; class PluginController : public IPluginController { Q_OBJECT public: PluginController(QObject *parent = 0); ~PluginController() override; - + bool initialize(Core *core); + virtual QString errorString() const; private: #ifndef Q_OS_ANDROID QVector m_plugins; typedef QHash InfoToPluginMap; InfoToPluginMap m_loadedPlugins; #endif + QString m_errorString; }; } #endif diff --git a/src/app/uicontroller.cpp b/src/app/uicontroller.cpp index d5d36f8..15cd207 100644 --- a/src/app/uicontroller.cpp +++ b/src/app/uicontroller.cpp @@ -1,64 +1,70 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #include "uicontroller.h" #include "core.h" #include #include #include #ifndef Q_OS_ANDROID #include #endif namespace Minuet { UiController::UiController(QObject *parent) : IUiController(parent) { } UiController::~UiController() { } bool UiController::initialize(Core *core) { + m_errorString.clear(); QQmlApplicationEngine *engine = new QQmlApplicationEngine(this); QQmlContext *rootContext = engine->rootContext(); rootContext->setContextProperty(QStringLiteral("core"), core); #ifndef Q_OS_ANDROID rootContext->setContextObject(new KLocalizedContext(engine)); #else rootContext->setContextObject(new DummyAndroidLocalizer(engine)); #endif engine->load(QUrl("qrc:/Main.qml")); return true; } +QString UiController::errorString() const +{ + return m_errorString; +} + } diff --git a/src/app/uicontroller.h b/src/app/uicontroller.h index fec8342..a6149c3 100644 --- a/src/app/uicontroller.h +++ b/src/app/uicontroller.h @@ -1,68 +1,72 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #ifndef MINUET_UICONTROLLER_H #define MINUET_UICONTROLLER_H #include #ifdef Q_OS_ANDROID #include class DummyAndroidLocalizer : public QObject { Q_OBJECT public: explicit DummyAndroidLocalizer(QObject *parent = 0) : QObject(parent) { } Q_INVOKABLE QString i18n (const QString &message, const QVariant &p1=QVariant(), const QVariant &p2=QVariant(), const QVariant &p3=QVariant(), const QVariant &p4=QVariant(), const QVariant &p5=QVariant(), const QVariant &p6=QVariant(), const QVariant &p7=QVariant(), const QVariant &p8=QVariant(), const QVariant &p9=QVariant(), const QVariant &p10=QVariant()) const { return message; } Q_INVOKABLE QString i18nc (const QString &context, const QString &message, const QVariant &p1=QVariant(), const QVariant &p2=QVariant(), const QVariant &p3=QVariant(), const QVariant &p4=QVariant(), const QVariant &p5=QVariant(), const QVariant &p6=QVariant(), const QVariant &p7=QVariant(), const QVariant &p8=QVariant(), const QVariant &p9=QVariant(), const QVariant &p10=QVariant()) const { return message; } }; #endif namespace Minuet { class Core; class UiController : public IUiController { Q_OBJECT public: UiController(QObject *parent = 0); ~UiController() override; bool initialize(Core *core); + virtual QString errorString() const; + +private: + QString m_errorString; }; } #endif diff --git a/src/interfaces/iexercisecontroller.h b/src/interfaces/iexercisecontroller.h index ea227ba..c117563 100644 --- a/src/interfaces/iexercisecontroller.h +++ b/src/interfaces/iexercisecontroller.h @@ -1,66 +1,68 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #ifndef MINUET_IEXERCISECONTROLLER_H #define MINUET_IEXERCISECONTROLLER_H #include #include #include #include #include namespace Minuet { class MINUETINTERFACES_EXPORT IExerciseController : public QObject { Q_OBJECT Q_PROPERTY(QJsonArray exercises READ exercises) Q_PROPERTY(QVariantMap currentExercise MEMBER m_currentExercise WRITE setCurrentExercise NOTIFY currentExerciseChanged) Q_PROPERTY(QJsonArray selectedExerciseOptions READ selectedExerciseOptions NOTIFY selectedExerciseOptionsChanged) public: virtual ~IExerciseController() override; + + virtual QString errorString() const = 0; virtual QJsonArray exercises() const = 0; void setCurrentExercise(QVariantMap currentExercise); QJsonArray selectedExerciseOptions() const; public Q_SLOTS: virtual void randomlySelectExerciseOptions() = 0; Q_SIGNALS: void currentExerciseChanged(QVariantMap newCurrentExercise); void selectedExerciseOptionsChanged(QJsonArray newSelectedExerciseOptions); protected: explicit IExerciseController(QObject *parent = 0); QVariantMap m_currentExercise; QJsonArray m_selectedExerciseOptions; }; } #endif diff --git a/src/interfaces/iplugincontroller.h b/src/interfaces/iplugincontroller.h index 3adbc7a..2ad4a97 100644 --- a/src/interfaces/iplugincontroller.h +++ b/src/interfaces/iplugincontroller.h @@ -1,46 +1,48 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #ifndef MINUET_IPLUGINCONTROLLER_H #define MINUET_IPLUGINCONTROLLER_H #include #include namespace Minuet { class MINUETINTERFACES_EXPORT IPluginController : public QObject { Q_OBJECT public: virtual ~IPluginController() override; + + virtual QString errorString() const = 0; protected: explicit IPluginController(QObject *parent = 0); }; } #endif diff --git a/src/interfaces/isoundcontroller.cpp b/src/interfaces/isoundcontroller.cpp index b570a2f..5e9078a 100644 --- a/src/interfaces/isoundcontroller.cpp +++ b/src/interfaces/isoundcontroller.cpp @@ -1,72 +1,66 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #include "isoundcontroller.h" -#include - namespace Minuet { ISoundController::ISoundController(QObject *parent) : IPlugin(parent) { - qRegisterMetaType("State"); - qmlRegisterInterface("ISoundController"); - qmlRegisterUncreatableType("org.kde.minuet.isoundcontroller", 1, 0, "ISoundController", "ISoundController cannot be instantiated"); - setPlaybackLabel(QStringLiteral("00:00.00")); setState(StoppedState); } ISoundController::~ISoundController() { } ISoundController::State ISoundController::state() const { return m_state; } QString ISoundController::playbackLabel() const { return m_playbackLabel; } void ISoundController::setPlaybackLabel(const QString &playbackLabel) { if (m_playbackLabel != playbackLabel) { m_playbackLabel = playbackLabel; emit playbackLabelChanged(m_playbackLabel); } } void ISoundController::setState(State state) { if (m_state != state) { m_state = state; emit stateChanged(m_state); } } } diff --git a/src/interfaces/iuicontroller.h b/src/interfaces/iuicontroller.h index 7e31bf9..dbc0a7b 100644 --- a/src/interfaces/iuicontroller.h +++ b/src/interfaces/iuicontroller.h @@ -1,46 +1,48 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #ifndef MINUET_IUICONTROLLER_H #define MINUET_IUICONTROLLER_H #include #include namespace Minuet { class MINUETINTERFACES_EXPORT IUiController : public QObject { Q_OBJECT public: virtual ~IUiController() override; + + virtual QString errorString() const = 0; protected: explicit IUiController(QObject *parent = 0); }; } #endif diff --git a/src/plugins/fluidsynthsoundcontroller/fluidsynthsoundcontroller.cpp b/src/plugins/fluidsynthsoundcontroller/fluidsynthsoundcontroller.cpp index 8be38b8..ae1e725 100644 --- a/src/plugins/fluidsynthsoundcontroller/fluidsynthsoundcontroller.cpp +++ b/src/plugins/fluidsynthsoundcontroller/fluidsynthsoundcontroller.cpp @@ -1,246 +1,246 @@ /**************************************************************************** ** ** Copyright (C) 2016 by Sandro S. Andrade ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as ** published by the Free Software Foundation; either version 2 of ** the License or (at your option) version 3 or any later version ** accepted by the membership of KDE e.V. (or its successor approved ** by the membership of KDE e.V.), which shall act as a proxy ** defined in Section 14 of version 3 of the license. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . ** ****************************************************************************/ #include "fluidsynthsoundcontroller.h" #include #include #include #include #include unsigned int FluidSynthSoundController::m_initialTime = 0; FluidSynthSoundController::FluidSynthSoundController(QObject *parent) : Minuet::ISoundController(parent), m_audioDriver(0), m_sequencer(0), m_song(0) { m_tempo = 60; m_settings = new_fluid_settings(); fluid_settings_setstr(m_settings, "synth.reverb.active", "no"); fluid_settings_setstr(m_settings, "synth.chorus.active", "no"); m_synth = new_fluid_synth(m_settings); fluid_synth_cc(m_synth, 1, 100, 0); #ifdef Q_OS_LINUX int fluid_res = fluid_synth_sfload(m_synth, QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("soundfonts/GeneralUser-v1.47.sf2")).toLatin1(), 1); #endif #ifdef Q_OS_WIN int fluid_res = fluid_synth_sfload(m_synth, QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("minuet/soundfonts/GeneralUser-v1.47.sf2")).toLatin1(), 1); #endif if (fluid_res == FLUID_FAILED) - qDebug() << "Error when loading soundfont!"; + qCritical() << "Error when loading soundfont!"; resetEngine(); } FluidSynthSoundController::~FluidSynthSoundController() { deleteEngine(); if (m_synth) delete_fluid_synth(m_synth); if (m_settings) delete_fluid_settings(m_settings); } void FluidSynthSoundController::setPitch(qint8 pitch) { m_pitch = pitch; fluid_synth_cc(m_synth, 1, 101, 0); fluid_synth_cc(m_synth, 1, 6, 12); float accurate_pitch = (m_pitch + 12) * (2.0 / 3) * 1024; fluid_synth_pitch_bend(m_synth, 1, qMin(qRound(accurate_pitch), 16 * 1024 - 1)); } void FluidSynthSoundController::setVolume(quint8 volume) { m_volume = volume; fluid_synth_cc(m_synth, 1, 7, m_volume * 127 / 200); } void FluidSynthSoundController::setTempo (quint8 tempo) { m_tempo = tempo; } void FluidSynthSoundController::prepareFromExerciseOptions(QJsonArray selectedExerciseOptions) { QList *song = new QList; m_song.reset(song); if (m_playMode == "rhythm") for (int i = 0; i < 4; ++i) appendEvent(9, 80, 127, 1000*(60.0/m_tempo)); for (int i = 0; i < selectedExerciseOptions.size(); ++i) { QString sequence = selectedExerciseOptions[i].toObject()[QStringLiteral("sequence")].toString(); unsigned int chosenRootNote = selectedExerciseOptions[i].toObject()[QStringLiteral("rootNote")].toString().toInt(); if (m_playMode != "rhythm") { appendEvent(1, chosenRootNote, 127, 1000*(60.0/m_tempo)); foreach(const QString &additionalNote, sequence.split(' ')) appendEvent(1, chosenRootNote + additionalNote.toInt(), 127, ((m_playMode == "scale") ? 1000:4000)*(60.0/m_tempo)); } else { //appendEvent(9, 80, 127, 1000*(60.0/m_tempo)); foreach(QString additionalNote, sequence.split(' ')) { // krazy:exclude=foreach float dotted = 1; if (additionalNote.endsWith('.')) { dotted = 1.5; additionalNote.chop(1); } unsigned int duration = dotted*1000*(60.0/m_tempo)*(4.0/additionalNote.toInt()); appendEvent(9, 37, 127, duration); } } } //if (m_playMode == "rhythm") // appendEvent(9, 80, 127, 1000*(60.0/m_tempo)); fluid_event_t *event = new_fluid_event(); fluid_event_set_source(event, -1); fluid_event_all_notes_off(event, 1); m_song->append(event); } void FluidSynthSoundController::prepareFromMidiFile(const QString &fileName) { Q_UNUSED(fileName) } void FluidSynthSoundController::play() { if (!m_song.data()) return; if (m_state != PlayingState) { unsigned int now = fluid_sequencer_get_tick(m_sequencer); foreach(fluid_event_t *event, *m_song.data()) { if (fluid_event_get_type(event) != FLUID_SEQ_ALLNOTESOFF || m_playMode != "chord") { fluid_event_set_dest(event, m_synthSeqID); fluid_sequencer_send_at(m_sequencer, event, now, 1); } fluid_event_set_dest(event, m_callbackSeqID); fluid_sequencer_send_at(m_sequencer, event, now, 1); now += (m_playMode == "rhythm") ? fluid_event_get_duration(event): (m_playMode == "scale") ? 1000*(60.0/m_tempo):0; } setState(PlayingState); } } void FluidSynthSoundController::pause() { } void FluidSynthSoundController::stop() { if (m_state != StoppedState) { fluid_event_t *event = new_fluid_event(); fluid_event_set_source(event, -1); fluid_event_all_notes_off(event, 1); fluid_event_set_dest(event, m_synthSeqID); fluid_sequencer_send_now(m_sequencer, event); resetEngine(); } } void FluidSynthSoundController::reset() { stop(); m_song.reset(0); } void FluidSynthSoundController::appendEvent(int channel, short key, short velocity, unsigned int duration) { fluid_event_t *event = new_fluid_event(); fluid_event_set_source(event, -1); fluid_event_note(event, channel, key, velocity, duration); m_song->append(event); } void FluidSynthSoundController::sequencerCallback(unsigned int time, fluid_event_t *event, fluid_sequencer_t *seq, void *data) { Q_UNUSED(seq); // This is safe! FluidSynthSoundController *soundController = reinterpret_cast(data); int eventType = fluid_event_get_type(event); switch (eventType) { case FLUID_SEQ_NOTE: { if (m_initialTime == 0) m_initialTime = time; double adjustedTime = (time - m_initialTime)/1000.0; int mins = adjustedTime / 60; int secs = ((int)adjustedTime) % 60; int cnts = 100*(adjustedTime-qFloor(adjustedTime)); static QChar fill('0'); soundController->setPlaybackLabel(QStringLiteral("%1:%2.%3").arg(mins, 2, 10, fill).arg(secs, 2, 10, fill).arg(cnts, 2, 10, fill)); break; } case FLUID_SEQ_ALLNOTESOFF: { m_initialTime = 0; soundController->setPlaybackLabel(QStringLiteral("00:00.00")); soundController->setState(StoppedState); break; } } } void FluidSynthSoundController::resetEngine() { deleteEngine(); #ifdef Q_OS_LINUX fluid_settings_setstr(m_settings, "audio.driver", "pulseaudio"); #endif #ifdef Q_OS_WIN fluid_settings_setstr(m_settings, "audio.driver", "dsound"); #endif m_audioDriver = new_fluid_audio_driver(m_settings, m_synth); if (!m_audioDriver) { fluid_settings_setstr(m_settings, "audio.driver", "alsa"); m_audioDriver = new_fluid_audio_driver(m_settings, m_synth); } if (!m_audioDriver) { - qDebug() << "Couldn't start audio driver!"; + qCritical() << "Couldn't start audio driver!"; } m_sequencer = new_fluid_sequencer2(0); m_synthSeqID = fluid_sequencer_register_fluidsynth(m_sequencer, m_synth); m_callbackSeqID = fluid_sequencer_register_client (m_sequencer, "Minuet Fluidsynth Sound Controller", &FluidSynthSoundController::sequencerCallback, this); m_initialTime = 0; setPlaybackLabel(QStringLiteral("00:00.00")); setState(StoppedState); } void FluidSynthSoundController::deleteEngine() { if (m_sequencer) delete_fluid_sequencer(m_sequencer); if (m_audioDriver) delete_fluid_audio_driver(m_audioDriver); }