diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index bcdaf48..97b095a 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,70 +1,100 @@ - - - - + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + - - - + + + - + + - - - - - - - - - - diff --git a/build-android.sh b/build-android.sh index c0d2559..e013955 100755 --- a/build-android.sh +++ b/build-android.sh @@ -1,15 +1,17 @@ #!/bin/bash -cmake ../ -DCMAKE_TOOLCHAIN_FILE=/usr/share/ECM/toolchain/Android.cmake -DCMAKE_BUILD_TYPE=Release -DECM_ADDITIONAL_FIND_ROOT_PATH=$Qt5_android -DCMAKE_INSTALL_PREFIX=../import -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -DCMAKE_SYSROOT=/data/android-ndk-r10e/platforms/android-17/arch-arm -DCMAKE_ANDROID_API=17 +#cmake ../ -DCMAKE_TOOLCHAIN_FILE=/usr/share/ECM/toolchain/Android.cmake -DCMAKE_BUILD_TYPE=Release -DECM_ADDITIONAL_FIND_ROOT_PATH=$Qt5_android -DCMAKE_INSTALL_PREFIX=../import -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -DCMAKE_SYSROOT=$CMAKE_SYSROOT -DCMAKE_ANDROID_API=29 -DQTANDROID_EXPORTED_TARGET=minuet -DANDROID_APK_DIR=../android/ -DANDROID_ABI=arm64-v8a # A diretiva -DQTANDROID_EXPORTED_TARGET esta gerando erro na execucao do cmake: nao encontra o cmake do Qt5Core -#cmake -DCMAKE_TOOLCHAIN_FILE=/usr/share/ECM/toolchain/Android.cmake -DCMAKE_BUILD_TYPE=Release -DECM_ADDITIONAL_FIND_ROOT_PATH="${Qt5_android}" -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -DQTANDROID_EXPORTED_TARGET=minuet -DANDROID_APK_DIR=../android/ -DCMAKE_SYSROOT=/data/android-ndk-r10e/platforms/android-17/arch-arm/ ../ +#cmake -DCMAKE_TOOLCHAIN_FILE=/usr/share/ECM/toolchain/Android.cmake -DCMAKE_BUILD_TYPE=Release -DECM_ADDITIONAL_FIND_ROOT_PATH="${Qt5_android}" -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -DQTANDROID_EXPORTED_TARGET=minuet -DANDROID_APK_DIR=../android/ -DCMAKE_SYSROOT=/data/android-ndk-r10e/platforms/android-29/arch-arm/ ../ + +cmake ../ -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} -DCMAKE_SYSROOT=$CMAKE_SYSROOT -DCMAKE_ANDROID_API=21 -DANDROID_PLATFORM=21 -DANDROID_ABI=arm64-v8a -DECM_DIR=/usr/share/ECM/cmake/ -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH -DCMAKE_PREFIX_PATH=$Qt5_android make mkdir -p "${INSTALL_DIR}"/share mkdir -p "${INSTALL_DIR}"/lib/qml make install/strip -make create-apk-minuet +make minuet-apk diff --git a/build-csound-android.sh b/build-csound-android.sh new file mode 100755 index 0000000..e9acc82 --- /dev/null +++ b/build-csound-android.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +cd ../Csound/Android/ +export NDK_MODULE_PATH=$PWD/../../Csound-build/android/ +export ANDROID_NDK_ROOT=$ANDROID_NDK +./downloadDependencies.sh +sh build-all.sh diff --git a/cmake/qt-android-deployment.json.in b/cmake/qt-android-deployment.json.in new file mode 100644 index 0000000..858dad0 --- /dev/null +++ b/cmake/qt-android-deployment.json.in @@ -0,0 +1,20 @@ +{ + "description": "This file is generated by qmake to be read by androiddeployqt and should not be modified by hand.", + "qt": "@QT5_INSTALL_PREFIX@", + "sdk": "$ENV{ANDROID_SDK_ROOT}", + "sdkBuildToolsRevision": "@APK_SDK_BUILD_TOOLS_VERSION@", + "ndk": "@CMAKE_ANDROID_NDK@", + "toolchain-prefix": "@APK_NDK_TOOLCHAIN_PREFIX@", + "tool-prefix": "@APK_NDK_TOOLCHAIN_PREFIX@", + "toolchain-version": "@CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION@", + "ndk-host": "@ANDROID_HOST_TAG@", + "target-architecture": "@CMAKE_ANDROID_ARCH_ABI@", + "android-package-source-directory": "@APK_ANDROID_EXTRA_FILES@", + "android-extra-libs": "${APK_EXTRA_LIBS}", + "android-version-name": "1.2.3", + "android-version-code": "0010020031", + "qml-root-path": "@APK_QML_ROOT_PATH@", + "stdcpp-path": "/data/teste-livro/android-ndk-r20/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so", + "useLLVM": true, + "application-binary": "@APK_TARGET_OUTPUT_FILENAME@" +} diff --git a/cmake/qt-android-mk-apk.cmake b/cmake/qt-android-mk-apk.cmake new file mode 100644 index 0000000..fe646c8 --- /dev/null +++ b/cmake/qt-android-mk-apk.cmake @@ -0,0 +1,113 @@ +include(CMakeParseArguments) + +set(QT_ANDROID_MK_APK_DIR ${CMAKE_CURRENT_LIST_DIR}) + +function(qt_android_build_apk) + set(options) + set(oneValueArgs + TARGET PACKAGE_NAME ANDROID_EXTRA_FILES QML_ROOT_PATH + SDK_BUILD_TOOLS_VERSION EXTRA_LIBS) + set(multiValueArgs) + cmake_parse_arguments(APK "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Gather required variables to create the configuration file. + + find_package(Qt5 COMPONENTS Core REQUIRED) + # Qt5Core_DIR now points to $Qt5InstallPrefix/lib/cmake/Qt5Core, so + # we get the parent directory three times: + get_filename_component(QT5_INSTALL_PREFIX "${Qt5Core_DIR}/../../.." ABSOLUTE) + message("Qt5 installed in ${QT5_INSTALL_PREFIX}") + + # Adjust QML root path if not set: + if(NOT APK_QML_ROOT_PATH) + set(APK_QML_ROOT_PATH $) + endif() + + # Get the toolchain prefix, i.e. the folder name within the + # toolchains/ folder without the compiler version + # APK_NDK_TOOLCHAIN_PREFIX + file(RELATIVE_PATH APK_NDK_TOOLCHAIN_PREFIX ${CMAKE_ANDROID_NDK} ${CMAKE_CXX_COMPILER}) + string(REPLACE "/" ";" APK_NDK_TOOLCHAIN_PREFIX ${APK_NDK_TOOLCHAIN_PREFIX}) + list(GET APK_NDK_TOOLCHAIN_PREFIX 1 APK_NDK_TOOLCHAIN_PREFIX) + string(REPLACE "clang" "" NEW_CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION ${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}) + if(NOT NEW_CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION STREQUAL "") + string(LENGTH "-${NEW_CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}" VERSION_LENGTH) + string(LENGTH "${APK_NDK_TOOLCHAIN_PREFIX}" FOLDER_LENGTH) + math(EXPR PREFIX_LENGTH ${FOLDER_LENGTH}-${VERSION_LENGTH}) + string(SUBSTRING "${APK_NDK_TOOLCHAIN_PREFIX}" 0 ${PREFIX_LENGTH} APK_NDK_TOOLCHAIN_PREFIX) + endif() + + # Get path to the target: + set(APK_TARGET_OUTPUT_FILENAME $) + + # Get Android SDK build tools version: + if(NOT APK_SDK_BUILD_TOOLS_VERSION) + file(GLOB sdk_versions RELATIVE $ENV{ANDROID_SDK_ROOT}/build-tools + $ENV{ANDROID_SDK_ROOT}/build-tools/*) + list(GET sdk_versions -1 APK_SDK_BUILD_TOOLS_VERSION) + endif() + + # Step 1: Create an intermediate config file. At this point, + # the generator expressions will we use are not yet resolved. + configure_file( + ${QT_ANDROID_MK_APK_DIR}/qt-android-deployment.json.in + ${CMAKE_CURRENT_BINARY_DIR}/${APK_TARGET}-config.json.pre) + + # Step 2: Run file(CONFIGURE ...) to create the final config JSON + # with generator expressions resolved: + file( + GENERATE + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${APK_TARGET}-config.json + INPUT ${CMAKE_CURRENT_BINARY_DIR}/${APK_TARGET}-config.json.pre) + + # Step 3: Create a custom target which will build our APK: + set(APK_DIR ${CMAKE_CURRENT_BINARY_DIR}/${APK_TARGET}-apk-build) + if(NOT APK_ANDROID_EXTRA_FILES) + set( + APK_ANDROID_EXTRA_FILES + ${QT5_INSTALL_PREFIX}/src/android/templates/) + endif() + if(JAVA_HOME) + set(ANDROIDDEPLOYQT_EXTRA_ARGS + ${ANDROIDDEPLOYQT_EXTRA_ARGS} --jdk '${JAVA_HOME}') + endif() + if(${CMAKE_BUILD_TYPE} STREQUAL "Release") + set(ANDROIDDEPLOYQT_EXTRA_ARGS + ${ANDROIDDEPLOYQT_EXTRA_ARGS} --release) + set(APK_FILENAME ${APK_TARGET}-apk-build-release-unsigned.apk) + else() + set(APK_FILENAME ${APK_TARGET}-apk-build-debug.apk) + endif() + add_custom_target( + ${APK_TARGET}-apk + COMMAND ${CMAKE_COMMAND} -E remove_directory ${APK_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${QT5_INSTALL_PREFIX}/src/android/templates/ + ${APK_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${APK_ANDROID_EXTRA_FILES}/ + ${APK_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${APK_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI} + COMMAND ${CMAKE_COMMAND} -E copy + ${APK_TARGET_OUTPUT_FILENAME} + ${APK_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI} + COMMAND ${QT5_INSTALL_PREFIX}/bin/androiddeployqt + --verbose + --output ${APK_DIR} + --input ${CMAKE_CURRENT_BINARY_DIR}/${APK_TARGET}-config.json + --deployment bundled + --gradle + ${ANDROIDDEPLOYQT_EXTRA_ARGS} + ) + + # Step 4: Create a custom target which pushes the created APK onto + # the device. + add_custom_target( + ${APK_TARGET}-apk-install + COMMAND $ENV{ANDROID_SDK_ROOT}/platform-tools/adb install -r + ${APK_DIR}/build/outputs/apk/debug/${APK_FILENAME} + DEPENDS + ${APK_TARGET}-apk + ) +endfunction() diff --git a/configure-csound-android.sh b/configure-csound-android.sh new file mode 100755 index 0000000..6f00acf --- /dev/null +++ b/configure-csound-android.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +mkdir android +cd android +git clone https://bitbucket.org/kunstmusik/libsndfile-android.git +cd .. diff --git a/set-android-env.sh b/set-android-env.sh index c82e150..a0685e6 100755 --- a/set-android-env.sh +++ b/set-android-env.sh @@ -1,9 +1,9 @@ -export BASE_DIR=/data -export ANDROID_NDK=$BASE_DIR/android-ndk-r10e -export ANDROID_SDK_ROOT=$BASE_DIR/sdk-tools-linux-4333796 -export Qt5_android=$BASE_DIR/Qt/5.11.2/android_armv7 -export PATH=$BASE_DIR/sdk-tools-linux-4333796/platform-tools/:$PATH +export BASE_DIR=/data/teste-livro +export ANDROID_NDK=$BASE_DIR/android-ndk-r20 +export ANDROID_SDK_ROOT=$BASE_DIR/android-sdk-4333796 +export Qt5_android=$BASE_DIR/Qt/5.13.2/android_arm64_v8a +export PATH=$ANDROID_SDK_ROOT/platform-tools/:$PATH export ANT=/usr/bin/ant export JAVA_HOME=/usr/lib/jvm/java-8-openjdk/ -export INSTALL_DIR=/home/pdv/install-dir/usr -export CMAKE_SYSROOT=$BASE_DIR/android-ndk-r10e/platforms/android-17/arch-arm/ +export INSTALL_DIR=../install +export CMAKE_SYSROOT=$ANDROID_NDK/platforms/android-21/arch-arm64 diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 52f5f2b..5eb0a31 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -1,73 +1,85 @@ set(minuet_SRCS main.cpp core.cpp uicontroller.cpp plugincontroller.cpp exercisecontroller.cpp ) qt5_add_resources(minuet_SRCS qml.qrc) IF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") set(minuet_ICONS_PNG ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-apps-minuet.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-apps-minuet.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/32-apps-minuet.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/48-apps-minuet.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/64-apps-minuet.png ${CMAKE_CURRENT_SOURCE_DIR}/icons/128-apps-minuet.png ) set(minuet_ICONS_SVG ${CMAKE_CURRENT_SOURCE_DIR}/icons/sc-apps-minuet.svgz ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-apps-minuet.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-apps-minuet.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/32-apps-minuet.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/48-apps-minuet.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/64-apps-minuet.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/128-apps-minuet.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-actions-minuet-scales.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-actions-minuet-intervals.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-actions-minuet-chords.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/16-actions-minuet-rhythms.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-actions-minuet-scales.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-actions-minuet-intervals.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-actions-minuet-chords.svg ${CMAKE_CURRENT_SOURCE_DIR}/icons/22-actions-minuet-rhythms.svg ) ecm_add_app_icon(minuet_SRCS ICONS ${minuet_ICONS_PNG}) ecm_install_icons(ICONS ${minuet_ICONS_PNG} ${minuet_ICONS_SVG} DESTINATION ${ICON_INSTALL_DIR} THEME hicolor) ENDIF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") -add_executable(minuet ${minuet_SRCS}) +if(ANDROID) + include(../../cmake/qt-android-mk-apk.cmake) + add_library(minuet SHARED ${minuet_SRCS}) + qt_android_build_apk( + TARGET ${PROJECT_NAME} + PACKAGE_NAME org.kde.minuet + QML_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR} + ANDROID_EXTRA_FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../android + EXTRA_LIBS ../plugins/csoundsoundcontroller/libminuetcsoundsoundcontroller.so,../interfaces/libminuetinterfaces.so,../../src/plugins/csoundsoundcontroller/Csound-prefix/src/Csound/Android/CsoundAndroid/libs/arm64-v8a/libcsoundandroid.so,../../src/plugins/csoundsoundcontroller/Csound-prefix/src/Csound/Android/CsoundAndroid/libs/arm64-v8a/libsndfile.so,../../src/plugins/csoundsoundcontroller/Csound-prefix/src/Csound/Android/pluginlibs/libfluidsynth/libs/arm64-v8a/libfluidOpcodes.so,../../src/plugins/csoundsoundcontroller/Csound-prefix/src/Csound/Android/CsoundAndroid/libs/arm64-v8a/libc++_shared.so + ) +else() + add_executable(minuet ${minuet_SRCS}) +endif() target_link_libraries(minuet Qt5::Core Qt5::Gui Qt5::Qml Qt5::Quick Qt5::QuickControls2 Qt5::Svg Minuet::Interfaces ) -IF(${CMAKE_SYSTEM_NAME} MATCHES "Android") +if(ANDROID) target_link_libraries(minuet Minuet::CsoundSoundController ) -ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Android") +endif() IF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") target_link_libraries(minuet KF5::CoreAddons KF5::I18n KF5::Crash ) ENDIF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") IF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") install(TARGETS minuet ${INSTALL_TARGETS_DEFAULT_ARGS}) install(PROGRAMS org.kde.minuet.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install(TARGETS minuet RUNTIME DESTINATION bin) ENDIF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") diff --git a/src/app/exercisecontroller.cpp b/src/app/exercisecontroller.cpp index 5d78ab5..14e5e15 100644 --- a/src/app/exercisecontroller.cpp +++ b/src/app/exercisecontroller.cpp @@ -1,252 +1,253 @@ /**************************************************************************** ** ** 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; } QJsonArray ExerciseController::exercises() const { return m_exercises[QStringLiteral("exercises")].toArray(); } bool ExerciseController::mergeJsonFiles(const QString directoryName, QJsonObject &targetObject, bool applyDefinitionsFlag, QString commonKey, QString mergeKey) { -#if defined(Q_OS_ANDROID) QStringList jsonDirs; - jsonDirs += "/data/data/org.kde.minuet/qt-reserved-files/share/minuet/" + directoryName; +#if defined(Q_OS_ANDROID) + jsonDirs << "assets:/data/" + directoryName; #elif defined(Q_OS_WIN) - QStringList jsonDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("minuet/") + directoryName, QStandardPaths::LocateDirectory); + jsonDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("minuet/") + directoryName, QStandardPaths::LocateDirectory); #else - QStringList jsonDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, directoryName, QStandardPaths::LocateDirectory); + jsonDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, directoryName, QStandardPaths::LocateDirectory); #endif foreach (const QString &jsonDirString, jsonDirs) { QDir jsonDir(jsonDirString); foreach (const QString &json, jsonDir.entryList(QDir::Files)) { + if (!json.endsWith(".json")) break; 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 += 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/main.cpp b/src/app/main.cpp index 57ea415..7292787 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -1,74 +1,84 @@ /**************************************************************************** ** ** 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 #include "core.h" #if !defined(Q_OS_ANDROID) #include #include #include #endif #include +#include #include #include +#include + +#include int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication application(argc, argv); #if !defined(Q_OS_ANDROID) KCrash::initialize(); KLocalizedString::setApplicationDomain("minuet"); KAboutData aboutData( QStringLiteral("minuet"), i18n("Minuet"), QStringLiteral(MINUET_VERSION_STRING), i18n("A KDE application for music education"), KAboutLicense::GPL, i18n("(c) 2016, Sandro S. Andrade (sandroandrade@kde.org)")); aboutData.addAuthor("Sandro S. Andrade", i18n("Developer"), QStringLiteral("sandroandrade@kde.org")); aboutData.addAuthor("Ayush Shah", i18n("Developer"), QStringLiteral("1595ayush@gmail.com")); aboutData.addAuthor("Alessandro Longo", i18n("Minuet Icon Designer"), QStringLiteral("alessandro.longo@kdemail.net")); #endif application.setWindowIcon(QIcon(QStringLiteral(":/minuet.png"))); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); #if !defined(Q_OS_ANDROID) aboutData.setupCommandLine(&parser); #endif parser.process(application); #if !defined(Q_OS_ANDROID) aboutData.processCommandLine(&parser); KAboutData::setApplicationData(aboutData); #endif +#if defined(Q_OS_ANDROID) + if (!QFile("/data/data/org.kde.minuet/files/sf_GMbank.sf2").exists()) { + if(QFile("assets:/share/sf_GMbank.sf2").copy("/data/data/org.kde.minuet/files/sf_GMbank.sf2")) + qDebug() << "COPIED " << QFileInfo("/data/data/org.kde.minuet/files/sf_GMbank.sf2").size() << "b soundfound file to /data/data/org.kde.minuet/files/sf_GMbank.sf2"; + } +#endif Minuet::Core::initialize(); return application.exec(); } diff --git a/src/plugins/csoundsoundcontroller/CMakeLists.txt b/src/plugins/csoundsoundcontroller/CMakeLists.txt index 588e6ad..f0d7b14 100644 --- a/src/plugins/csoundsoundcontroller/CMakeLists.txt +++ b/src/plugins/csoundsoundcontroller/CMakeLists.txt @@ -1,49 +1,41 @@ include(ExternalProject) -ExternalProject_Add(CsoundForAndroid - URL https://github.com/csound/csound/releases/download/6.11.0/Csound6.11.0-Android.zip - URL_HASH MD5=f2aeb39b5f227ae19c92f31b23d56342 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" -) - ExternalProject_Add(Csound GIT_REPOSITORY https://github.com/csound/csound.git GIT_TAG 6.11.0 - CONFIGURE_COMMAND "" - BUILD_COMMAND "" + CONFIGURE_COMMAND "${CMAKE_SOURCE_DIR}/configure-csound-android.sh" + BUILD_COMMAND "${CMAKE_SOURCE_DIR}/build-csound-android.sh" INSTALL_COMMAND "" ) -set(CSOUND_ANDROID_LIBS_DIR ${CMAKE_CURRENT_BINARY_DIR}/CsoundForAndroid-prefix/src/CsoundForAndroid/CsoundForAndroid/CsoundAndroid/src/main/jniLibs/armeabi-v7a) +set(CSOUND_ANDROID_LIBS_DIR ${CMAKE_CURRENT_BINARY_DIR}/Csound-prefix/src/Csound/Android/CsoundAndroid/libs/arm64-v8a/) set(CSOUND_ROOT ${CMAKE_CURRENT_BINARY_DIR}/Csound-prefix/src/Csound/) set(CSOUND_ANDROID ${CSOUND_ANDROID_LIBS_DIR}/libcsoundandroid.so) set(CSOUND_SHARED ${CSOUND_ANDROID_LIBS_DIR}/libc++_shared.so) set(CSOUND_SND ${CSOUND_ANDROID_LIBS_DIR}/libsndfile.so) set(CSOUND_LIBS ${CSOUND_ANDROID} ${CSOUND_SHARED} ${CSOUND_SND}) -include_directories(${CSOUND_ROOT}/include) -include_directories(${CSOUND_ROOT}/Android/CsoundAndroid/jni/) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/Csound-prefix/src/Csound/include) +include_directories(${CMAKE_CURRENT_BINARY_DIR}/Csound-prefix/src/Csound/Android/CsoundAndroid/jni) set(csoundsoundcontroller_PLUGIN_SRCS csengine.cpp csoundsoundcontroller.cpp ) add_library(minuetcsoundsoundcontroller ${csoundsoundcontroller_PLUGIN_SRCS}) add_library(Minuet::CsoundSoundController ALIAS minuetcsoundsoundcontroller) target_link_libraries(minuetcsoundsoundcontroller Qt5::Core Minuet::Interfaces ${CSOUND_LIBS} ) -add_dependencies(minuetcsoundsoundcontroller CsoundForAndroid Csound) +add_dependencies(minuetcsoundsoundcontroller Csound) IF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") install(TARGETS minuetcsoundsoundcontroller DESTINATION ${PLUGIN_INSTALL_DIR}/minuet/) ENDIF(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Android") diff --git a/src/plugins/csoundsoundcontroller/csengine.cpp b/src/plugins/csoundsoundcontroller/csengine.cpp index 408f898..a0c7f94 100644 --- a/src/plugins/csoundsoundcontroller/csengine.cpp +++ b/src/plugins/csoundsoundcontroller/csengine.cpp @@ -1,49 +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 . ** ****************************************************************************/ #include "csengine.h" #include #include #include #include #include +#include CsEngine::CsEngine() { m_fileName = (char *)"./template.csd"; } void CsEngine::run() { cs.setOpenSlCallbacks(); // for android audio to work - cs.Compile(m_fileName); - cs.Start(); - cs.Perform(); - cs.Cleanup(); - cs.Reset(); - cs.Stop(); + QFile file(m_fileName); + file.open(QIODevice::ReadOnly); + qDebug() << "Template: "; + while (!file.atEnd()) + qDebug() << file.readLine(); + file.close(); + qWarning() << "Dir " << QDir::currentPath() << " contains sf_GMbank.sf2? " << QDir::current().entryList(QDir::Files).contains("sf_GMbank.sf2"); + qWarning() << "Dir " << QCoreApplication::applicationDirPath() << " contains libfluidOpcodes.so? " << QDir(QCoreApplication::applicationDirPath()).entryList(QDir::Files).contains("libfluidOpcodes.so"); + qDebug() << "READING " << QDir::currentPath() + "/template.csd"; + qDebug() << "SOUNDFONT EXISTS? " << QFile("/data/data/org.kde.minuet/files/sf_GMbank.sf2").exists() << " " << QFileInfo("/data/data/org.kde.minuet/files/sf_GMbank.sf2").size() << "b"; + if (cs.Compile(m_fileName)) { + cs.Start(); + cs.Perform(); + cs.Cleanup(); + cs.Reset(); + cs.Stop(); + } } void CsEngine::stop() { - cs.Stop(); +// cs.Stop(); } diff --git a/src/plugins/csoundsoundcontroller/csoundsoundcontroller.cpp b/src/plugins/csoundsoundcontroller/csoundsoundcontroller.cpp index 4698bdc..bdd8773 100644 --- a/src/plugins/csoundsoundcontroller/csoundsoundcontroller.cpp +++ b/src/plugins/csoundsoundcontroller/csoundsoundcontroller.cpp @@ -1,210 +1,234 @@ /**************************************************************************** ** ** 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 "csoundsoundcontroller.h" #include "csengine.h" #include #include #include Q_DECLARE_LOGGING_CATEGORY(MINUETANDROID) CsoundSoundController::CsoundSoundController(QObject *parent): Minuet::ISoundController(parent), m_csoundEngine(new CsEngine) { qmlRegisterType("org.kde.minuet", 1, 0, "CsoundSoundController"); openExerciseFile(); // setQuestionLabel("new question"); } void CsoundSoundController::openExerciseFile() { QStringList templateList; templateList.append(QStringLiteral("assets:/share/template.csd")); - templateList.append(QStringLiteral("assets:/share/template_rhythm.csd")); - +// templateList.append(QStringLiteral("assets:/share/template_rhythm.csd")); + + qDebug() << "DIRPATH: " << QCoreApplication::applicationDirPath(); + qDebug() << "ASSETSPATH: " << QDir("assets:/").canonicalPath(); + qDebug() << "QStandardPaths::ApplicationsLocation: " << QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation); + qDebug() << "QStandardPaths::GenericDataLocation: " << QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); + qDebug() << "QStandardPaths::AppDataLocation: " << QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); + qDebug() << "QStandardPaths::AppLocalDataLocation: " << QStandardPaths::standardLocations(QStandardPaths::AppLocalDataLocation); + qDebug() << "QStandardPaths::AppConfigLocation: " << QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation); + QDir appDir(QCoreApplication::applicationDirPath()); + qDebug() << "Contains libfluidOpcodes.so? " << appDir.entryList(QDir::Files).contains("libfluidOpcodes.so"); + qDebug() << "DIRPATH: " << appDir << " Contents: " << appDir.entryList(QDir::AllEntries); + appDir.cdUp(); + qDebug() << "DIRPATH: " << appDir << " Contents: " << appDir.entryList(QDir::AllEntries); + appDir.cdUp(); + qDebug() << "DIRPATH: " << appDir << " Contents: " << appDir.entryList(QDir::AllEntries); + QDir reserved("/data/data/org.kde.minuet/qt-reserved-files/"); + qDebug() << "RESERVED: " << reserved << " Contents: " << reserved.entryList(QDir::AllEntries); + + QDirIterator it("/", QDirIterator::Subdirectories); + while (it.hasNext()) + qDebug() << "FILE: " << it.next(); foreach (const QString &templateString, templateList) { QFile sfile(templateString); if (!sfile.open(QIODevice::ReadOnly | QIODevice::Text)) return; QTextStream in(&sfile); QString lineData; QString tempBeginLine; QString tempEndLine; while (!in.atEnd()) { lineData = in.readLine(); + lineData.replace("APPDIRPATH", QCoreApplication::applicationDirPath()); tempBeginLine = tempBeginLine + lineData + "\n"; if (lineData.contains("")) { m_begLine.append(tempBeginLine); break; } } while (!in.atEnd()) { lineData = in.readLine(); tempEndLine += lineData + "\n"; } m_endLine.append(tempEndLine); } + qDebug() << "m_begLine: " << m_begLine; + qDebug() << "m_endLine: " << m_endLine; } void CsoundSoundController::appendEvent(QList midiNotes, QList barStartInfo, QString playMode) { //TODO : use grantlee processing or any other text template library int templateNumber = playMode == "rhythm" ? 1:0; QString content; QString fifthParam = QStringLiteral("100"); QFile m_csdFileOpen(QStringLiteral("./template.csd")); + qDebug() << "CREATING " << QDir::currentPath() + "/template.csd"; if(!m_csdFileOpen.isOpen()) { m_csdFileOpen.open(QIODevice::ReadWrite | QIODevice::Text); } m_csdFileOpen.resize(0); if (playMode == "rhythm") { QString wave = QStringLiteral("f 1 0 16384 10 1\n\n"); content += wave; fifthParam = QStringLiteral(""); } for(int i=0; i midiNotes; QList barStartInfo; if (m_playMode == "rhythm") { for(int k = 0; k < 4; ++k) { midiNotes.append(80); barStartInfo.append(barStart++); } } int exerciseOptionsSize = selectedExerciseOptions.size(); for (int i = 0; i < exerciseOptionsSize; ++i) { QString sequence = selectedExerciseOptions[i].toObject()[QStringLiteral("sequence")].toString(); unsigned int chosenRootNote = selectedExerciseOptions[i].toObject()[QStringLiteral("rootNote")].toString().toInt(); if (m_playMode != "rhythm") { midiNotes.append(chosenRootNote); barStartInfo.append(barStart); unsigned int j = 1; foreach(const QString &additionalNote, sequence.split(' ')) { midiNotes.append(chosenRootNote+additionalNote.toInt()); barStartInfo.append((m_playMode == "scale") ? barStart+j:barStart); ++j; } barStart++; } else { midiNotes.append(80); barStartInfo.append(barStart); foreach(QString additionalNote, sequence.split(' ')) { // krazy:exclude=foreach midiNotes.append(37); barStartInfo.append(barStart); float dotted = 1; if (additionalNote.endsWith('.')) { dotted = 1.5; additionalNote.chop(1); } barStart += dotted*1*(4.0/additionalNote.toInt()); } } } if (m_playMode == "rhythm") { midiNotes.append(80); barStartInfo.append(barStart); } appendEvent(midiNotes, barStartInfo, m_playMode); } void CsoundSoundController::prepareFromMidiFile(const QString &fileName) { Q_UNUSED(fileName) } void CsoundSoundController::play() { m_csoundEngine->start(); setState(PlayingState); } void CsoundSoundController::pause() { } void CsoundSoundController::stop() { m_csoundEngine->stop(); setState(StoppedState); } void CsoundSoundController::reset() { } void CsoundSoundController::setPitch (qint8 pitch) { Q_UNUSED(pitch) } void CsoundSoundController::setVolume (quint8 volume) { Q_UNUSED(volume) } void CsoundSoundController::setTempo (quint8 tempo) { Q_UNUSED(tempo) } //#include "moc_csoundsoundcontroller.cpp" diff --git a/src/plugins/csoundsoundcontroller/template.csd b/src/plugins/csoundsoundcontroller/template.csd new file mode 100644 index 0000000..61878a0 --- /dev/null +++ b/src/plugins/csoundsoundcontroller/template.csd @@ -0,0 +1,42 @@ + + +; Select audio/midi flags here according to platform +--opcode-lib=/data/data/org.kde.minuet/qt-reserved-files/share/libfluidOpcodes.so +-odac ;;;realtime audio out and realtime midi in +;-iadc ;;;uncomment -iadc if realtime audio input is needed too +; For Non-realtime ouput leave only the line below: +;-o fluidNote.wav -W ;;; for file output any platform + + + +sr = 44100 +ksmps = 32 +nchnls = 2 +0dbfs = 1 + +giengine fluidEngine +; soundfont path to manual/examples +isfnum fluidLoad "/data/data/org.kde.minuet/qt-reserved-files/share/sf_GMbank.sf2", giengine, 1 + fluidProgramSelect giengine, 1, isfnum, 0, 0 + +instr 1 + + mididefault 60, p3 + midinoteonkey p4, p5 +ikey init p4 +ivel init p5 + fluidNote giengine, 1, ikey, ivel + +endin + +instr 99 + +imvol init 7 +asigl, asigr fluidOut giengine + outs asigl*imvol, asigr*imvol + +endin + + + + diff --git a/src/plugins/csoundsoundcontroller/template_rhythm.csd b/src/plugins/csoundsoundcontroller/template_rhythm.csd new file mode 100644 index 0000000..d88e9a8 --- /dev/null +++ b/src/plugins/csoundsoundcontroller/template_rhythm.csd @@ -0,0 +1,34 @@ + + +; Select audio/midi flags here according to platform +;;;RT audio out, midi in, note=p4 and velocity=p5 +--opcode-lib=/data/data/org.kde.minuet/qt-reserved-files/share/libfluidOpcodes.so +-odac +;-+rtmidi=virtual -M0d --midi-key=4 --midi-velocity-amp=5 +;-iadc ;;;uncomment -iadc if RT audio input is needed too +; For Non-realtime ouput leave only the line below: +; -o cpsmidinn.wav -W ;;; for file output any platform + + + +sr = 44100 +ksmps = 32 +nchnls = 2 +0dbfs = 1 + +massign 0, 1 ;assign all midi to instr. 1 + +instr 1 ;play virtual keyboard + +inote = p4 +icps = cpsmidinn(inote) +asig pluck 0.5, inote, inote*0.81, 1, 3, .5 +out asig +print icps +endin + + + + + +