From f211c192e8d902bb175c562bedbb1da49ec13a7b Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Wed, 16 Sep 2020 13:53:31 +0900 Subject: [PATCH 01/28] CircleCI: uninstall python@2 to prevent "brew install" errors --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 01a00d912d1..fe5b1f2fd7c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -179,8 +179,8 @@ jobs: - run: name: Install Homebrew dependencies command: | - # unlink Homebrew's python 2 to prevent an node-gyp error - brew unlink python@2 || true + # uninstall Homebrew's python 2 to prevent errors on brew install + brew uninstall python@2 || true brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio fltk qt5 carla - run: name: Install nodejs dependencies From af328003a05300eaad028d674f696eca7ed18719 Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 17 Sep 2020 17:23:35 +0200 Subject: [PATCH 02/28] Use valid Semver versions for pre-releases (#5636) * Fix ProjectVersion handling of pre-releases * Add workaround for old, non-standard version * Attempt to fix versioning * More consistent comments * Apply suggestions from code review - Set CompareType's underlying type to int and revert change to ProjectVersion::compare's parameters - Add "None" and "All" as names elements of CompareType enum - Preserve hyphens in prerelease identifiers - Pad invalid (too short) versions to prevent crashes or nasty behavior - Compare numeric identifiers to non-numeric ones correctly - Don't interpret identifiers of form "-#" as numeric (where '#' is any number of digits) - Add tests to ensure fixes in this commit work and won't regress in the future * CMAKE fixes from code review Co-authored-by: Tres Finocchiaro * Remove unnecessary changes to CMake logic * More const, more reference * Apply suggestions from code review Co-authored-by: Tres Finocchiaro --- CMakeLists.txt | 2 +- cmake/CMakeLists.txt | 2 +- cmake/modules/VersionInfo.cmake | 36 +++++- include/ProjectVersion.h | 24 ++-- src/core/ProjectVersion.cpp | 175 ++++++++++++-------------- tests/src/core/ProjectVersionTest.cpp | 30 +++++ 6 files changed, 153 insertions(+), 116 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ead95c32db..aaeec055f30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ IF(VERSION_STAGE) SET(VERSION "${VERSION}-${VERSION_STAGE}") ENDIF() IF(VERSION_BUILD) - SET(VERSION "${VERSION}.${VERSION_BUILD}") + SET(VERSION "${VERSION}-${VERSION_BUILD}") ENDIF() # Override version information for non-base builds diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index b27dec91e0f..833fad5819f 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -9,7 +9,7 @@ IF(VERSION_STAGE) SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${VERSION_STAGE}") ENDIF() IF(VERSION_BUILD) - SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}.${VERSION_BUILD}") + SET(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH}-${VERSION_BUILD}") ENDIF() SET(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME_UCASE}") SET(CPACK_SOURCE_GENERATOR "TBZ2") diff --git a/cmake/modules/VersionInfo.cmake b/cmake/modules/VersionInfo.cmake index cf6932cbbba..9571514a6eb 100644 --- a/cmake/modules/VersionInfo.cmake +++ b/cmake/modules/VersionInfo.cmake @@ -1,29 +1,56 @@ FIND_PACKAGE(Git) IF(GIT_FOUND AND NOT FORCE_VERSION) - # Look for git tag information (e.g. Tagged: "v1.0.0", Non-tagged: "v1.0.0-123-a1b2c3d") + SET(MAJOR_VERSION 0) + SET(MINOR_VERSION 0) + SET(PATCH_VERSION 0) + # Look for git tag information (e.g. Tagged: "v1.0.0", Untagged: "v1.0.0-123-a1b2c3d") + # Untagged format: [latest tag]-[number of commits]-[latest commit hash] EXECUTE_PROCESS( COMMAND "${GIT_EXECUTABLE}" describe --tags --match v[0-9].[0-9].[0-9]* OUTPUT_VARIABLE GIT_TAG WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 10 OUTPUT_STRIP_TRAILING_WHITESPACE) + # Read: TAG_LIST = GIT_TAG.split("-") STRING(REPLACE "-" ";" TAG_LIST "${GIT_TAG}") + # Read: TAG_LIST_LENGTH = TAG_LIST.length() LIST(LENGTH TAG_LIST TAG_LIST_LENGTH) + # Untagged versions contain at least 2 dashes, giving 3 strings on split. + # Hence, for untagged versions TAG_LIST_LENGTH = [dashes in latest tag] + 3. + # Corollary: if TAG_LIST_LENGTH <= 2, the version must be tagged. IF(TAG_LIST_LENGTH GREATER 0) + # Set FORCE_VERSION to TAG_LIST[0], strip any 'v's to get MAJ.MIN.PAT LIST(GET TAG_LIST 0 FORCE_VERSION) STRING(REPLACE "v" "" FORCE_VERSION "${FORCE_VERSION}") + # Split FORCE_VERSION on '.' and populate MAJOR/MINOR/PATCH_VERSION + STRING(REPLACE "." ";" MAJ_MIN_PAT "${FORCE_VERSION}") + LIST(GET MAJ_MIN_PAT 0 MAJOR_VERSION) + LIST(GET MAJ_MIN_PAT 1 MINOR_VERSION) + LIST(GET MAJ_MIN_PAT 2 PATCH_VERSION) ENDIF() + # 1 dash total: Dash in latest tag, no additional commits => pre-release IF(TAG_LIST_LENGTH EQUAL 2) LIST(GET TAG_LIST 1 VERSION_STAGE) SET(FORCE_VERSION "${FORCE_VERSION}-${VERSION_STAGE}") + # 2 dashes: Assume untagged with no dashes in latest tag name => stable + commits ELSEIF(TAG_LIST_LENGTH EQUAL 3) + # Get the number of commits and latest commit hash LIST(GET TAG_LIST 1 EXTRA_COMMITS) - SET(FORCE_VERSION "${FORCE_VERSION}.${EXTRA_COMMITS}") + LIST(GET TAG_LIST 2 COMMIT_HASH) + # Bump the patch version + MATH(EXPR PATCH_VERSION "${PATCH_VERSION}+1") + # Set the version to MAJOR.MINOR.PATCH-EXTRA_COMMITS+COMMIT_HASH + SET(FORCE_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}") + SET(FORCE_VERSION "${FORCE_VERSION}-${EXTRA_COMMITS}+${COMMIT_HASH}") + # 3 dashes: Assume untagged with 1 dash in latest tag name => pre-release + commits ELSEIF(TAG_LIST_LENGTH EQUAL 4) + # Get pre-release stage, number of commits, and latest commit hash LIST(GET TAG_LIST 1 VERSION_STAGE) LIST(GET TAG_LIST 2 EXTRA_COMMITS) - SET(FORCE_VERSION - "${FORCE_VERSION}-${VERSION_STAGE}.${EXTRA_COMMITS}") + LIST(GET TAG_LIST 3 COMMIT_HASH) + # Set the version to MAJOR.MINOR.PATCH-VERSION_STAGE.EXTRA_COMMITS+COMMIT_HASH + SET(FORCE_VERSION "${FORCE_VERSION}-${VERSION_STAGE}") + SET(FORCE_VERSION "${FORCE_VERSION}.${EXTRA_COMMITS}+${COMMIT_HASH}") ENDIF() ENDIF() @@ -74,4 +101,3 @@ MESSAGE("\n" "* Override version: -DFORCE_VERSION=x.x.x-x\n" "* Ignore Git information: -DFORCE_VERSION=internal\n" ) - diff --git a/include/ProjectVersion.h b/include/ProjectVersion.h index 5f7bf09b3c1..e938ccc81c7 100644 --- a/include/ProjectVersion.h +++ b/include/ProjectVersion.h @@ -28,6 +28,9 @@ #define PROJECT_VERSION_H #include +#include + +#include /*! \brief Version number parsing and comparison * @@ -36,16 +39,16 @@ class ProjectVersion { public: - enum CompareType { Major, Minor, Release, Stage, Build }; + enum CompareType : int { None = 0, Major=1, Minor=2, Release=3, Stage=4, Build=5, All = std::numeric_limits::max() }; + - ProjectVersion(QString version, CompareType c = Build); - ProjectVersion(const char * version, CompareType c = Build); + ProjectVersion(QString version, CompareType c = All); + ProjectVersion(const char * version, CompareType c = All); int getMajor() const { return m_major; } int getMinor() const { return m_minor; } - int getRelease() const { return m_release; } - QString getStage() const { return m_stage; } - int getBuild() const { return m_build; } + int getPatch() const { return m_patch; } + const QStringList& getLabels() const { return m_labels; } CompareType getCompareType() const { return m_compareType; } ProjectVersion setCompareType(CompareType compareType) { m_compareType = compareType; return * this; } @@ -54,11 +57,10 @@ class ProjectVersion private: QString m_version; - int m_major; - int m_minor; - int m_release; - QString m_stage; - int m_build; + int m_major = 0; + int m_minor = 0; + int m_patch = 0; + QStringList m_labels; CompareType m_compareType; } ; diff --git a/src/core/ProjectVersion.cpp b/src/core/ProjectVersion.cpp index ea97a6fec7d..6f3dcddbeb1 100644 --- a/src/core/ProjectVersion.cpp +++ b/src/core/ProjectVersion.cpp @@ -27,123 +27,105 @@ #include "ProjectVersion.h" -int parseMajor(QString & version) { - return version.section( '.', 0, 0 ).toInt(); -} - - - - -int parseMinor(QString & version) { - return version.section( '.', 1, 1 ).toInt(); -} - - - - -int parseRelease(QString & version) { - return version.section( '.', 2, 2 ).section( '-', 0, 0 ).toInt(); -} - - - - -QString parseStage(QString & version) { - return version.section( '.', 2, 2 ).section( '-', 1 ); -} - - - - -int parseBuild(QString & version) { - return version.section( '.', 3 ).toInt(); -} - ProjectVersion::ProjectVersion(QString version, CompareType c) : m_version(version), - m_major(parseMajor(m_version)), - m_minor(parseMinor(m_version)), - m_release(parseRelease(m_version)), - m_stage(parseStage(m_version)), - m_build(parseBuild(m_version)), m_compareType(c) { + // Version numbers may have build data, prefixed with a '+', + // but this mustn't affect version precedence in comparisons + QString metadataStripped = version.split("+").first(); + // They must have an obligatory initial segement, and may have + // optional identifiers prefaced by a '-'. Both parts affect precedence + QString obligatorySegment = metadataStripped.section('-', 0, 0); + QString prereleaseSegment = metadataStripped.section('-', 1); + + // The obligatory segment consists of three identifiers: MAJOR.MINOR.PATCH + QStringList mainVersion = obligatorySegment.split("."); + // HACK: Pad invalid versions in order to prevent crashes + while (mainVersion.size() < 3){ mainVersion.append("0"); } + m_major = mainVersion.at(0).toInt(); + m_minor = mainVersion.at(1).toInt(); + m_patch = mainVersion.at(2).toInt(); + + // Any # of optional pre-release identifiers may follow, separated by '.'s + if (!prereleaseSegment.isEmpty()){ m_labels = prereleaseSegment.split("."); } + + // HACK: Handle old (1.2.2 and earlier), non-standard versions of the form + // MAJOR.MINOR.PATCH.COMMITS, used for non-release builds from source. + if (mainVersion.size() >= 4 && m_major <= 1 && m_minor <= 2 && m_patch <= 2){ + // Drop the standard version identifiers. erase(a, b) removes [a,b) + mainVersion.erase(mainVersion.begin(), mainVersion.begin() + 3); + // Prepend the remaining identifiers as prerelease versions + m_labels = mainVersion + m_labels; + // Bump the patch version. x.y.z-a < x.y.z, but we want x.y.z.a > x.y.z + m_patch += 1; + } } -ProjectVersion::ProjectVersion(const char* version, CompareType c) : - m_version(QString(version)), - m_major(parseMajor(m_version)), - m_minor(parseMinor(m_version)), - m_release(parseRelease(m_version)), - m_stage(parseStage(m_version)), - m_build(parseBuild(m_version)), - m_compareType(c) +ProjectVersion::ProjectVersion(const char* version, CompareType c) : ProjectVersion(QString(version), c) { } +//! @param c Determines the number of identifiers to check when comparing int ProjectVersion::compare(const ProjectVersion & a, const ProjectVersion & b, CompareType c) { - if(a.getMajor() != b.getMajor()) - { - return a.getMajor() - b.getMajor(); - } - if(c == Major) - { - return 0; - } - - if(a.getMinor() != b.getMinor()) - { - return a.getMinor() - b.getMinor(); - } - if(c == Minor) - { - return 0; + // How many identifiers to compare before we consider the versions equal + const int limit = static_cast(c); + + // Use the value of limit to zero out identifiers we don't care about + int aMaj = 0, bMaj = 0, aMin = 0, bMin = 0, aPat = 0, bPat = 0; + if (limit >= 1){ aMaj = a.getMajor(); bMaj = b.getMajor(); } + if (limit >= 2){ aMin = a.getMinor(); bMin = b.getMinor(); } + if (limit >= 3){ aPat = a.getPatch(); bPat = b.getPatch(); } + + // Then we can compare as if we care about every identifier + if(aMaj != bMaj){ return aMaj - bMaj; } + if(aMin != bMin){ return aMin - bMin; } + if(aPat != bPat){ return aPat - bPat; } + + // Decide how many optional identifiers we care about + const int maxLabels = qMax(0, limit - 3); + const auto aLabels = a.getLabels().mid(0, maxLabels); + const auto bLabels = b.getLabels().mid(0, maxLabels); + + // We can only compare identifiers if both versions have them + const int commonLabels = qMin(aLabels.size(), bLabels.size()); + // If one version has optional labels and the other doesn't, + // the one without them is bigger + if (commonLabels == 0){ return bLabels.size() - aLabels.size(); } + + // Otherwise, compare as many labels as we can + for (int i = 0; i < commonLabels; i++){ + const QString& labelA = aLabels.at(i); + const QString& labelB = bLabels.at(i); + // If both labels are the same, skip + if (labelA == labelB){ continue; } + // Numeric and non-numeric identifiers compare differently + bool aIsNumeric = false, bIsNumeric = false; + const int numA = labelA.toInt(&aIsNumeric); + const int numB = labelB.toInt(&bIsNumeric); + // toInt reads '-x' as a negative number, semver says it's non-numeric + aIsNumeric &= !labelA.startsWith("-"); + bIsNumeric &= !labelB.startsWith("-"); + // If only one identifier is numeric, that one is smaller + if (aIsNumeric != bIsNumeric){ return aIsNumeric ? -1 : 1; } + // If both are numeric, compare as numbers + if (aIsNumeric && bIsNumeric){ return numA - numB; } + // Otherwise, compare lexically + return labelA.compare(labelB); } - if(a.getRelease() != b.getRelease()) - { - return a.getRelease() - b.getRelease(); - } - if(c == Release) - { - return 0; - } - - if(!(a.getStage().isEmpty() && b.getStage().isEmpty())) - { - // make sure 0.x.y > 0.x.y-alpha - if(a.getStage().isEmpty()) - { - return 1; - } - if(b.getStage().isEmpty()) - { - return -1; - } - - // 0.x.y-beta > 0.x.y-alpha - int cmp = QString::compare(a.getStage(), b.getStage()); - if(cmp) - { - return cmp; - } - } - if(c == Stage) - { - return 0; - } - - return a.getBuild() - b.getBuild(); + // If everything else matches, the version with more labels is bigger + return aLabels.size() - bLabels.size(); } @@ -153,6 +135,3 @@ int ProjectVersion::compare(ProjectVersion v1, ProjectVersion v2) { return compare(v1, v2, std::min(v1.getCompareType(), v2.getCompareType())); } - - - diff --git a/tests/src/core/ProjectVersionTest.cpp b/tests/src/core/ProjectVersionTest.cpp index 7c99727397f..e52088f6f8c 100644 --- a/tests/src/core/ProjectVersionTest.cpp +++ b/tests/src/core/ProjectVersionTest.cpp @@ -39,9 +39,39 @@ private slots: QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Minor) == "1.1.5"); QVERIFY( ! ( ProjectVersion("3.1.0", ProjectVersion::Minor) < "2.2.5" ) ); QVERIFY( ! ( ProjectVersion("2.5.0", ProjectVersion::Release) < "2.2.5" ) ); + //A pre-release version has lower precedence than a normal version QVERIFY(ProjectVersion("1.1.0") > "1.1.0-alpha"); + //But higher precedence than the previous version + QVERIFY(ProjectVersion("1.1.0-alpha") > "1.0.0"); + //Identifiers with letters or hyphens are compare lexically in ASCII sort order QVERIFY(ProjectVersion("1.1.0-alpha") < "1.1.0-beta"); QVERIFY(ProjectVersion("1.2.0-rc1") < "1.2.0-rc2"); + //Build metadata MUST be ignored when determining version precedence + QVERIFY(ProjectVersion("1.2.2") == "1.2.2+metadata"); + QVERIFY(ProjectVersion("1.0.0-alpha") < "1.0.0-alpha.1"); + QVERIFY(ProjectVersion("1.0.0-alpha.1") < "1.0.0-alpha.beta"); + QVERIFY(ProjectVersion("1.0.0-alpha.beta") < "1.0.0-beta"); + QVERIFY(ProjectVersion("1.0.0-beta.2") < "1.0.0-beta.11"); + //Test workaround for old, nonstandard version numbers + QVERIFY(ProjectVersion("1.2.2.42") == "1.2.3-42"); + QVERIFY(ProjectVersion("1.2.2.42") > "1.2.2.21"); + //Ensure that newer versions of the same format aren't upgraded + //in order to discourage use of incorrect versioning + QVERIFY(ProjectVersion("1.2.3.42") == "1.2.3"); + //CompareVersion "All" should compare every identifier + QVERIFY( + ProjectVersion("1.0.0-a.b.c.d.e.f.g.h.i.j.k.l", ProjectVersion::All) + < "1.0.0-a.b.c.d.e.f.g.h.i.j.k.m" + ); + //Prerelease identifiers may contain hyphens + QVERIFY(ProjectVersion("1.0.0-Alpha-1.2") > "1.0.0-Alpha-1.1"); + //We shouldn't crash on invalid versions + QVERIFY(ProjectVersion("1-invalid") == "1.0.0-invalid"); + QVERIFY(ProjectVersion("") == "0.0.0"); + //Numeric identifiers are smaller than non-numeric identiiers + QVERIFY(ProjectVersion("1.0.0-alpha") > "1.0.0-1"); + //An identifier of the form "-x" is non-numeric, not negative + QVERIFY(ProjectVersion("1.0.0-alpha.-1") > "1.0.0-alpha.1"); } } ProjectVersionTests; From 2bdd835a8ab03fec72ed7d4bac3a89077cbe2d62 Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 17 Sep 2020 18:24:54 +0200 Subject: [PATCH 03/28] Switch out mailing list for Discord Because as far as I can tell that's where most of the active devs are available. --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1061ecff600..b1bea70a0c6 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,6 @@ Information about what you can do and how can be found in the Before coding a new big feature, please _always_ [file an issue](https://github.com/LMMS/lmms/issues/new) for your idea and -suggestions about your feature and about the intended implementation on GitHub -or post to the LMMS developers mailinglist (lmms-devel@lists.sourceforge.net) -and wait for replies! Maybe there are different ideas, improvements, hints or +suggestions about your feature and about the intended implementation on GitHub, +or ask in one of the tech channels on Discord and wait for replies! Maybe there are different ideas, improvements, or hints, or maybe your feature is not welcome/needed at the moment. From 9e3f602194f43f3e7fa22640710459e54ffe3a29 Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 17 Sep 2020 18:40:55 +0200 Subject: [PATCH 04/28] Fix 'Version difference' dialogue showing for differences in patch and lower after semantic versioning PR --- src/core/DataFile.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 44cb920d844..dbd0c84b741 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -772,10 +772,10 @@ void DataFile::upgrade_0_4_0_rc2() void DataFile::upgrade_1_0_99() { jo_id_t last_assigned_id = 0; - + QList idList; findIds(documentElement(), idList); - + QDomNodeList list = elementsByTagName("ladspacontrols"); for(int i = 0; !list.item(i).isNull(); ++i) { @@ -791,22 +791,22 @@ void DataFile::upgrade_1_0_99() QDomElement me = createElement("data"); me.setAttribute("value", el.attribute("data")); me.setAttribute("scale_type", "log"); - + jo_id_t id; for(id = last_assigned_id + 1; idList.contains(id); id++) { } - + last_assigned_id = id; idList.append(id); me.setAttribute("id", id); el.appendChild(me); - + } } } - } + } } @@ -1047,7 +1047,7 @@ void DataFile::upgrade_1_3_0() QDomElement attribute = attributes.item( k ).toElement(); if( attribute.attribute( "name" ) == "file" && - ( attribute.attribute( "value" ) == "calf" || + ( attribute.attribute( "value" ) == "calf" || attribute.attribute( "value" ) == "calf.so" ) ) { attribute.setAttribute( "value", "veal" ); @@ -1311,7 +1311,7 @@ void DataFile::upgrade_1_3_0() }; iterate_ladspa_ports(effect, fn); } - + if( attribute.attribute( "name" ) == "plugin" && attribute.attribute( "value" ) == "StereoTools" ) { @@ -1486,15 +1486,14 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) if( root.hasAttribute( "creatorversion" ) ) { - // compareType defaults to Build,so it doesn't have to be set here + // compareType defaults to All, so it doesn't have to be set here ProjectVersion createdWith = root.attribute( "creatorversion" ); ProjectVersion openedWith = LMMS_VERSION; if ( createdWith != openedWith ) { - // only one compareType needs to be set, and we can compare on one line because setCompareType returns ProjectVersion - if( createdWith.setCompareType( ProjectVersion::Minor ) - != openedWith ) + if( createdWith.setCompareType( ProjectVersion::Minor ) != + openedWith.setCompareType( ProjectVersion::Minor ) ) { if( gui != nullptr && root.attribute( "type" ) == "song" ) { @@ -1536,7 +1535,7 @@ void findIds(const QDomElement& elem, QList& idList) idList.append(elem.attribute("id").toInt()); } QDomElement child = elem.firstChildElement(); - while(!child.isNull()) + while(!child.isNull()) { findIds(child, idList); child = child.nextSiblingElement(); From 1dab416c6cb9dd437829693bdabad5533b37271e Mon Sep 17 00:00:00 2001 From: IanCaio Date: Thu, 17 Sep 2020 19:45:46 -0300 Subject: [PATCH 05/28] Improvements to the upgrade routines (#5660) * First commit This commit starts the improvements on the upgrade methods. We are going to change both the DataFile and ConfigManager to use separate version values from LMMS release versions, so we can easily bump those versions for new upgrade routines. This first commit starts implementing a new version value for the ConfigManager. * Change code as per requested on review As requested, the "configVersion" method was replaced by "legacyConfigVersion" instead, which is only used to return a configuration version if none is present in the configuration file. The configuration version of the current build is stored in a local variable called CONFIG_VERSION, making version bumping easier. Uses a switch statement instead of if-else to be able to make use of case-cascading. TODO: - Change the CONFIG_VERSION variable to a unsigned int? - Start working on the DataFile.cpp. * Changes the upgrade logic on DataFile.cpp Starts refactoring the upgrade logic on DataFile.cpp. Now the "version" attribute is used to indicate which fileVersion we are loading. If the value of version is "1.0", we have a legacy file and use the legacyFileVersion method to retrieve the integer version using the LMMS version. If the value of version is an integer, we just read it. The integer indicates the position in a list of upgrade methods where we should start from and run the upgrade methods. The file version of the build is held in the FILE_VERSION const on DataFile.h. It HAS TO match the number of upgrade methods that we have on our list. One of the versions had 2 upgrade routines (upgrade_1_2_0_rc3 and upgrade_1_2_0_rc2_42). They were merged into a single one (upgrade_1_2_0_rc3) because they were both called from a single version check, meaning that they are both part of a single fileVersion. Two fatal errors were added (which can later be improved to show an error messagebox): One if the version attribute doesn't exist, and another one if we are using a FILE_VERSION that doesn't match the number of upgrade methods (to avoid mistakes while coding new upgrades later). The configVersion variables and methods were changed to use unsigned int instead of int. TODO: - Make the list of upgrade methods static. - Add debug messages for each upgrade routine for testing purposes. * Make method vector a static const variable On DataFile.cpp, we now use the vector list of upgrade methods as a static const variable, so it only has to be constructed once. * Reorganize vector lists Reorganize vector lists so they are more easily readable. Revert changes on upgrade method names from ConfigManager.cpp. * Makes the file version bumping automatic The file version bumping on DataFile.cpp is now automatic (using the size of the m_upgradeMethods vector as a reference). FILE_VERSION constant was removed, and with it the qFatal error when it doesn't match the size of the methods vector. * Improve formatting of version and upgrades lists Improves the formatting of the vector lists of upgrade routines and LMMS versions (2 upgrade routines per line and 3 LMMS versions per line). Adds a qWarning for every upgrade routine for testing purposes, plus a qWarning that tells the current fileVersion/configVersion when upgrade is called. Removes extra space characters after the opening bracket of ConfigManager::upgrade_1_1_91. * Changes ConfigManager to use a vector of methods Changes ConfigManager to use a vector of upgrade methods, just like DataFile. The new Config Version can be calculated automatically now, so the CONFIG_VERSION constant was removed. Corrects a small comment on Datafile.h. * Addresses Dom's review requests - Changes legacyConfigVersion and legacyFileVersion from const unsigned int to just unsigned int, since the const is irrelevant in this context. - Changes the type alias for upgrade methods to start with an uppercase as per the code style guidelines. Moves the aliasing of the type to the class declaration so it can be used on both the method and on the vector list declaration. - Changes the vector list names from m_upgradeMethods to UPGRADE_METHODS, so it's more visible they are a static constant. - Move the upgradeVersions list from the legacyFileVersion method to the DataFile class, as an static const called UPGRADE_VERSIONS. - Uses std::upper_bound instead of std::find_if for the legacyFileVersion method. * Uses type alias on vector assignment Uses the UpgradeMethod type alias when defining the upgrade methods vector from both ConfigManager and DataFile. * Removes debugging warnings Removes the qWarning() calls that were placed for debugging purposes. --- include/ConfigManager.h | 12 ++- include/DataFile.h | 18 ++-- src/core/ConfigManager.cpp | 67 ++++++++++--- src/core/DataFile.cpp | 201 ++++++++++++++++--------------------- 4 files changed, 165 insertions(+), 133 deletions(-) diff --git a/include/ConfigManager.h b/include/ConfigManager.h index de22d22af11..e5df02ff5e9 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -39,7 +39,6 @@ class LmmsCore; - const QString PROJECTS_PATH = "projects/"; const QString TEMPLATE_PATH = "templates/"; const QString PRESETS_PATH = "presets/"; @@ -55,6 +54,9 @@ const QString PORTABLE_MODE_FILE = "/portable_mode.txt"; class LMMS_EXPORT ConfigManager : public QObject { Q_OBJECT + + using UpgradeMethod = void(ConfigManager::*)(); + public: static inline ConfigManager * inst() { @@ -219,6 +221,10 @@ class LMMS_EXPORT ConfigManager : public QObject return m_version; } + // Used when the configversion attribute is not present in a configuration file. + // Returns the appropriate config file version based on the LMMS version. + unsigned int legacyConfigVersion(); + QString defaultVersion() const; @@ -270,6 +276,9 @@ class LMMS_EXPORT ConfigManager : public QObject void upgrade_1_1_91(); void upgrade(); + // List of all upgrade methods + static const std::vector UPGRADE_METHODS; + QString m_workingDir; QString m_dataDir; QString m_vstDir; @@ -286,6 +295,7 @@ class LMMS_EXPORT ConfigManager : public QObject QString m_backgroundPicFile; QString m_lmmsRcFile; QString m_version; + unsigned int m_configVersion; QStringList m_recentlyOpenedProjects; typedef QVector > stringPairVector; diff --git a/include/DataFile.h b/include/DataFile.h index 5890693da0a..38a1fa7631c 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -31,12 +31,16 @@ #include "lmms_export.h" #include "MemoryManager.h" +#include "ProjectVersion.h" class QTextStream; class LMMS_EXPORT DataFile : public QDomDocument { MM_OPERATORS + + using UpgradeMethod = void(DataFile::*)(); + public: enum Types { @@ -84,6 +88,8 @@ class LMMS_EXPORT DataFile : public QDomDocument return m_type; } + unsigned int legacyFileVersion(); + private: static Type type( const QString& typeName ); static QString typeName( Type type ); @@ -107,9 +113,13 @@ class LMMS_EXPORT DataFile : public QDomDocument void upgrade_1_1_0(); void upgrade_1_1_91(); void upgrade_1_2_0_rc3(); - void upgrade_1_2_0_rc2_42(); void upgrade_1_3_0(); + // List of all upgrade methods + static const std::vector UPGRADE_METHODS; + // List of ProjectVersions for the legacyFileVersion method + static const std::vector UPGRADE_VERSIONS; + void upgrade(); void loadData( const QByteArray & _data, const QString & _sourceFile ); @@ -125,14 +135,10 @@ class LMMS_EXPORT DataFile : public QDomDocument QDomElement m_content; QDomElement m_head; Type m_type; + unsigned int m_fileVersion; } ; -const int LDF_MAJOR_VERSION = 1; -const int LDF_MINOR_VERSION = 0; -const QString LDF_VERSION_STRING = QString::number( LDF_MAJOR_VERSION ) + "." + QString::number( LDF_MINOR_VERSION ); - - #endif diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 53262dac7df..5336654aef0 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -38,6 +38,11 @@ #include "lmmsversion.h" +// Vector with all the upgrade methods +const std::vector ConfigManager::UPGRADE_METHODS = { + &ConfigManager::upgrade_1_1_90 , &ConfigManager::upgrade_1_1_91 +}; + static inline QString ensureTrailingSlash(const QString & s ) { if(! s.isEmpty() && !s.endsWith('/') && !s.endsWith('\\')) @@ -51,7 +56,9 @@ static inline QString ensureTrailingSlash(const QString & s ) ConfigManager * ConfigManager::s_instanceOfMe = NULL; -ConfigManager::ConfigManager() : m_version(defaultVersion()) +ConfigManager::ConfigManager() : + m_version(defaultVersion()), + m_configVersion( UPGRADE_METHODS.size() ) { if (QFileInfo::exists(qApp->applicationDirPath() + PORTABLE_MODE_FILE)) { @@ -114,7 +121,7 @@ void ConfigManager::upgrade_1_1_90() void ConfigManager::upgrade_1_1_91() -{ +{ // rename displaydbv to displaydbfs if (!value("app", "displaydbv").isNull()) { setValue("app", "displaydbfs", value("app", "displaydbv")); @@ -131,17 +138,15 @@ void ConfigManager::upgrade() return; } - ProjectVersion createdWith = m_version; - - if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.90") - { - upgrade_1_1_90(); - } + // Runs all necessary upgrade methods + std::for_each( UPGRADE_METHODS.begin() + m_configVersion, UPGRADE_METHODS.end(), + [this](UpgradeMethod um) + { + (this->*um)(); + } + ); - if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.91") - { - upgrade_1_1_91(); - } + ProjectVersion createdWith = m_version; // Don't use old themes as they break the UI (i.e. 0.4 != 1.0, etc) if (createdWith.setCompareType(ProjectVersion::Minor) != LMMS_VERSION) @@ -151,6 +156,7 @@ void ConfigManager::upgrade() // Bump the version, now that we are upgraded m_version = LMMS_VERSION; + m_configVersion = UPGRADE_METHODS.size(); } QString ConfigManager::defaultVersion() const @@ -400,11 +406,23 @@ void ConfigManager::loadConfigFile(const QString & configFile) QDomNode node = root.firstChild(); - // Cache the config version for upgrade() + // Cache LMMS version if (!root.attribute("version").isNull()) { m_version = root.attribute("version"); } + // Get the version of the configuration file (for upgrade purposes) + if( root.attribute("configversion").isNull() ) + { + m_configVersion = legacyConfigVersion(); // No configversion attribute found + } + else + { + bool success; + m_configVersion = root.attribute("configversion").toUInt(&success); + if( !success ) qWarning("Config Version conversion failure."); + } + // create the settings-map out of the DOM while(!node.isNull()) { @@ -565,6 +583,7 @@ void ConfigManager::saveConfigFile() QDomElement lmms_config = doc.createElement("lmms"); lmms_config.setAttribute("version", m_version); + lmms_config.setAttribute("configversion", m_configVersion); doc.appendChild(lmms_config); for(settingsMap::iterator it = m_settings.begin(); @@ -673,3 +692,25 @@ void ConfigManager::initDevelopmentWorkingDir() cmakeCache.close(); } } + +// If configversion is not present, we will convert the LMMS version to the appropriate +// configuration file version for backwards compatibility. +unsigned int ConfigManager::legacyConfigVersion() +{ + ProjectVersion createdWith = m_version; + + createdWith.setCompareType(ProjectVersion::Build); + + if( createdWith < "1.1.90" ) + { + return 0; + } + else if( createdWith < "1.1.91" ) + { + return 1; + } + else + { + return 2; + } +} diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index dbd0c84b741..2ee74c41890 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -49,7 +49,28 @@ static void findIds(const QDomElement& elem, QList& idList); - +// Vector with all the upgrade methods +const std::vector DataFile::UPGRADE_METHODS = { + &DataFile::upgrade_0_2_1_20070501 , &DataFile::upgrade_0_2_1_20070508, + &DataFile::upgrade_0_3_0_rc2 , &DataFile::upgrade_0_3_0, + &DataFile::upgrade_0_4_0_20080104 , &DataFile::upgrade_0_4_0_20080118, + &DataFile::upgrade_0_4_0_20080129 , &DataFile::upgrade_0_4_0_20080409, + &DataFile::upgrade_0_4_0_20080607 , &DataFile::upgrade_0_4_0_20080622, + &DataFile::upgrade_0_4_0_beta1 , &DataFile::upgrade_0_4_0_rc2, + &DataFile::upgrade_1_0_99 , &DataFile::upgrade_1_1_0, + &DataFile::upgrade_1_1_91 , &DataFile::upgrade_1_2_0_rc3, + &DataFile::upgrade_1_3_0 +}; + +// Vector of all versions that have upgrade routines. +const std::vector DataFile::UPGRADE_VERSIONS = { + "0.2.1-20070501" , "0.2.1-20070508" , "0.3.0-rc2", + "0.3.0" , "0.4.0-20080104" , "0.4.0-20080118", + "0.4.0-20080129" , "0.4.0-20080409" , "0.4.0-20080607", + "0.4.0-20080622" , "0.4.0-beta1" , "0.4.0-rc2", + "1.0.99-0" , "1.1.0-0" , "1.1.91-0", + "1.2.0-rc3" , "1.3.0" +}; DataFile::typeDescStruct DataFile::s_types[DataFile::TypeCount] = @@ -71,11 +92,12 @@ DataFile::DataFile( Type type ) : QDomDocument( "lmms-project" ), m_content(), m_head(), - m_type( type ) + m_type( type ), + m_fileVersion( UPGRADE_METHODS.size() ) { appendChild( createProcessingInstruction("xml", "version=\"1.0\"")); QDomElement root = createElement( "lmms-project" ); - root.setAttribute( "version", LDF_VERSION_STRING ); + root.setAttribute( "version", m_fileVersion ); root.setAttribute( "type", typeName( type ) ); root.setAttribute( "creator", "LMMS" ); root.setAttribute( "creatorversion", LMMS_VERSION ); @@ -95,7 +117,8 @@ DataFile::DataFile( Type type ) : DataFile::DataFile( const QString & _fileName ) : QDomDocument(), m_content(), - m_head() + m_head(), + m_fileVersion( UPGRADE_METHODS.size() ) { QFile inFile( _fileName ); if( !inFile.open( QIODevice::ReadOnly ) ) @@ -873,31 +896,6 @@ void DataFile::upgrade_1_1_91() } -void DataFile::upgrade_1_2_0_rc3() -{ - // Upgrade from earlier bbtrack beat note behaviour of adding - // steps if a note is placed after the last step. - QDomNodeList bbtracks = elementsByTagName( "bbtrack" ); - for( int i = 0; !bbtracks.item( i ).isNull(); ++i ) - { - QDomNodeList patterns = bbtracks.item( i - ).toElement().elementsByTagName( - "pattern" ); - for( int j = 0; !patterns.item( j ).isNull(); ++j ) - { - int patternLength, steps; - QDomElement el = patterns.item( j ).toElement(); - if( el.attribute( "len" ) != "" ) - { - patternLength = el.attribute( "len" ).toInt(); - steps = patternLength / 12; - el.setAttribute( "steps", steps ); - } - } - } -} - - static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) { if( el.hasAttribute( "syncmode" ) ) @@ -930,8 +928,30 @@ static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) } -void DataFile::upgrade_1_2_0_rc2_42() +void DataFile::upgrade_1_2_0_rc3() { + // Upgrade from earlier bbtrack beat note behaviour of adding + // steps if a note is placed after the last step. + QDomNodeList bbtracks = elementsByTagName( "bbtrack" ); + for( int i = 0; !bbtracks.item( i ).isNull(); ++i ) + { + QDomNodeList patterns = bbtracks.item( i + ).toElement().elementsByTagName( + "pattern" ); + for( int j = 0; !patterns.item( j ).isNull(); ++j ) + { + int patternLength, steps; + QDomElement el = patterns.item( j ).toElement(); + if( el.attribute( "len" ) != "" ) + { + patternLength = el.attribute( "len" ).toInt(); + steps = patternLength / 12; + el.setAttribute( "steps", steps ); + } + } + } + + // DataFile::upgrade_1_2_0_rc2_42 QDomElement el = firstChildElement(); while ( !el.isNull() ) { @@ -1338,92 +1358,19 @@ void DataFile::upgrade_1_3_0() void DataFile::upgrade() { - ProjectVersion version = - documentElement().attribute( "creatorversion" ). - replace( "svn", "" ); - - if( version < "0.2.1-20070501" ) - { - upgrade_0_2_1_20070501(); - } - - if( version < "0.2.1-20070508" ) - { - upgrade_0_2_1_20070508(); - } - - if( version < "0.3.0-rc2" ) - { - upgrade_0_3_0_rc2(); - } - - if( version < "0.3.0" ) - { - upgrade_0_3_0(); - } - - if( version < "0.4.0-20080104" ) - { - upgrade_0_4_0_20080104(); - } - - if( version < "0.4.0-20080118" ) - { - upgrade_0_4_0_20080118(); - } - - if( version < "0.4.0-20080129" ) - { - upgrade_0_4_0_20080129(); - } - - if( version < "0.4.0-20080409" ) - { - upgrade_0_4_0_20080409(); - } - - if( version < "0.4.0-20080607" ) - { - upgrade_0_4_0_20080607(); - } - - if( version < "0.4.0-20080622" ) - { - upgrade_0_4_0_20080622(); - } + // Runs all necessary upgrade methods + std::for_each( UPGRADE_METHODS.begin() + m_fileVersion, UPGRADE_METHODS.end(), + [this](UpgradeMethod um) + { + (this->*um)(); + } + ); - if( version < "0.4.0-beta1" ) - { - upgrade_0_4_0_beta1(); - } - if( version < "0.4.0-rc2" ) - { - upgrade_0_4_0_rc2(); - } - if( version < "1.0.99-0" ) - { - upgrade_1_0_99(); - } - if( version < "1.1.0-0" ) - { - upgrade_1_1_0(); - } - if( version < "1.1.91-0" ) - { - upgrade_1_1_91(); - } - if( version < "1.2.0-rc3" ) - { - upgrade_1_2_0_rc3(); - upgrade_1_2_0_rc2_42(); - } - if( version < "1.3.0" ) - { - upgrade_1_3_0(); - } + // Bump the file version (which should be the size of the upgrade methods vector) + m_fileVersion = UPGRADE_METHODS.size(); // update document meta data - documentElement().setAttribute( "version", LDF_VERSION_STRING ); + documentElement().setAttribute( "version", m_fileVersion ); documentElement().setAttribute( "type", typeName( type() ) ); documentElement().setAttribute( "creator", "LMMS" ); documentElement().setAttribute( "creatorversion", LMMS_VERSION ); @@ -1483,6 +1430,20 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) m_type = type( root.attribute( "type" ) ); m_head = root.elementsByTagName( "head" ).item( 0 ).toElement(); + if( root.hasAttribute( "version" ) ) + { + if( root.attribute( "version" ) == "1.0" ){ + // The file versioning is now a unsigned int, not maj.min, so we use + // legacyFileVersion() to retrieve the appropriate version + m_fileVersion = legacyFileVersion(); + } + else + { + bool success; + m_fileVersion = root.attribute( "version" ).toUInt( &success ); + if( !success ) qWarning("File Version conversion failure."); + } + } if( root.hasAttribute( "creatorversion" ) ) { @@ -1541,3 +1502,17 @@ void findIds(const QDomElement& elem, QList& idList) child = child.nextSiblingElement(); } } + +unsigned int DataFile::legacyFileVersion() +{ + // Version of LMMs that created this project + ProjectVersion creator = + documentElement().attribute( "creatorversion" ). + replace( "svn", "" ); + + // Get an iterator pointing at the first upgrade we need to run (or at the end if there is no such upgrade) + auto firstRequiredUpgrade = std::upper_bound( UPGRADE_VERSIONS.begin(), UPGRADE_VERSIONS.end(), creator ); + + // Convert the iterator to an index, which is our file version (starting at 0) + return std::distance( UPGRADE_VERSIONS.begin(), firstRequiredUpgrade ); +} From 27a9f7711ee5e6f83648ec91625c027b876c2b0c Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Fri, 18 Sep 2020 15:55:44 +0900 Subject: [PATCH 06/28] LadspaManager: Skip empty entries when looking for plugins (#5677) This fixes crashes when starting LMMS from specific working directories. --- src/core/LadspaManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/LadspaManager.cpp b/src/core/LadspaManager.cpp index febbe5a9192..86acb661be5 100644 --- a/src/core/LadspaManager.cpp +++ b/src/core/LadspaManager.cpp @@ -59,6 +59,8 @@ LadspaManager::LadspaManager() for( QStringList::iterator it = ladspaDirectories.begin(); it != ladspaDirectories.end(); ++it ) { + // Skip empty entries as QDir will interpret it as the working directory + if ((*it).isEmpty()) { continue; } QDir directory( ( *it ) ); QFileInfoList list = directory.entryInfoList(); for( QFileInfoList::iterator file = list.begin(); From f6eeaba076af8cb1762a320c5c1b1810aee3e1fd Mon Sep 17 00:00:00 2001 From: Spekular Date: Fri, 18 Sep 2020 16:38:42 +0200 Subject: [PATCH 07/28] Suppress windows.h's min and max macros, allowing use of numeric_limits's max Co-authored-by: Dominic Clark --- src/core/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/main.cpp b/src/core/main.cpp index 897d07abf54..c83578a3d10 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -40,6 +40,7 @@ #include #ifdef LMMS_BUILD_WIN32 +#define NOMINMAX #include #endif @@ -437,7 +438,7 @@ int main( int argc, char * * argv ) { return noInputFileError(); } - + QFile f( QString::fromLocal8Bit( argv[i] ) ); f.open( QIODevice::ReadOnly ); QByteArray d = qCompress( f.readAll() ) ; From a6d0b460fcd13e9c5762bf8d6cbe92022695a0ea Mon Sep 17 00:00:00 2001 From: Spekular Date: Fri, 18 Sep 2020 18:48:39 +0200 Subject: [PATCH 08/28] Don't define NOMINMAX if it's already defined (#5678) Co-authored-by: Dominic Clark --- src/core/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/main.cpp b/src/core/main.cpp index c83578a3d10..9db0555222f 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -40,7 +40,9 @@ #include #ifdef LMMS_BUILD_WIN32 +#ifndef NOMINMAX #define NOMINMAX +#endif #include #endif From 2f37281d02b651766d94fe194d51984d343f4b1c Mon Sep 17 00:00:00 2001 From: DigArtRoks <69391149+DigArtRoks@users.noreply.github.com> Date: Mon, 21 Sep 2020 02:55:46 +0200 Subject: [PATCH 09/28] Fix for Mixer volume percentage labels are off by a factor of 100 (#5661) Add m_conversionFactor to the AutomatableModelView. This factor will be applied to the model->value when displaying it in the contextmenu of the control for the reset and copy actions. The factor will be applied when copying the value to the clipboard. When pasting from the clipboard, the value will be divided by the factor. Remove the model->displayValue() calls when updating the reset/copy/paste action text labels as this gives e.g. in the Equalizer the wrong action text for the Frequency knobs. In the Fader class, remove the m_displayConversion variable but rather use the new m_conversionFactor variable. Rewire the setDisplayConversion() function to set the m_conversionFactor to 1.0 or 100.0. Faders in FxMixer show now the correct context menu. Copying and pasting values between faders or even volume knobs in tracks shows consistent behavior. Other faders (like in Eq) show the old behavior. --- include/AutomatableModelView.h | 4 ++++ include/Fader.h | 5 ++--- src/gui/AutomatableModelView.cpp | 25 +++++++++++++++++------ src/gui/widgets/Fader.cpp | 35 ++++++++++---------------------- 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/include/AutomatableModelView.h b/include/AutomatableModelView.h index 1bcbd97d6fe..029ea16fba7 100644 --- a/include/AutomatableModelView.h +++ b/include/AutomatableModelView.h @@ -69,12 +69,16 @@ class LMMS_EXPORT AutomatableModelView : public ModelView void addDefaultActions( QMenu* menu ); + void setConversionFactor( float factor ); + float getConversionFactor(); + protected: virtual void mousePressEvent( QMouseEvent* event ); QString m_description; QString m_unit; + float m_conversionFactor; // Factor to be applied when the m_model->value is displayed } ; diff --git a/include/Fader.h b/include/Fader.h index bf1e7215e35..10f8c92ef05 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -99,7 +99,7 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView void setDisplayConversion( bool b ) { - m_displayConversion = b; + m_conversionFactor = b ? 100.0 : 1.0; } inline void setHintText( const QString & _txt_before, @@ -155,8 +155,7 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView QPixmap * m_back; QPixmap * m_leds; QPixmap * m_knob; - - bool m_displayConversion; + bool m_levelsDisplayedInDBFS; int m_moveStartPoint; diff --git a/src/gui/AutomatableModelView.cpp b/src/gui/AutomatableModelView.cpp index 039c75c9953..71f3ff8a3bb 100644 --- a/src/gui/AutomatableModelView.cpp +++ b/src/gui/AutomatableModelView.cpp @@ -42,7 +42,8 @@ static float floatFromClipboard(bool* ok=nullptr); AutomatableModelView::AutomatableModelView( ::Model* model, QWidget* _this ) : - ModelView( model, _this ) + ModelView( model, _this ), + m_conversionFactor( 1.0 ) { widget()->setAcceptDrops( true ); widget()->setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) ); @@ -56,14 +57,14 @@ void AutomatableModelView::addDefaultActions( QMenu* menu ) menu->addAction( embed::getIconPixmap( "reload" ), AutomatableModel::tr( "&Reset (%1%2)" ). - arg( model->displayValue( model->initValue() ) ). + arg( model->initValue() * m_conversionFactor ). arg( m_unit ), model, SLOT( reset() ) ); menu->addSeparator(); menu->addAction( embed::getIconPixmap( "edit_copy" ), AutomatableModel::tr( "&Copy value (%1%2)" ). - arg( model->displayValue( model->value() ) ). + arg( model->value() * m_conversionFactor ). arg( m_unit ), amvSlots, SLOT( copyToClipboard() ) ); @@ -71,7 +72,7 @@ void AutomatableModelView::addDefaultActions( QMenu* menu ) const float valueToPaste = floatFromClipboard(&canPaste); const QString pasteDesc = canPaste ? AutomatableModel::tr( "&Paste value (%1%2)"). - arg( model->displayValue( valueToPaste ) ). + arg( valueToPaste ). arg( m_unit ) : AutomatableModel::tr( "&Paste value"); QAction* pasteAction = menu->addAction( embed::getIconPixmap( "edit_paste" ), @@ -155,8 +156,20 @@ void AutomatableModelView::mousePressEvent( QMouseEvent* event ) } +void AutomatableModelView::setConversionFactor( float factor ) +{ + if( factor != 0.0 ) + { + m_conversionFactor = factor; + } +} +float AutomatableModelView::getConversionFactor() +{ + return m_conversionFactor; +} + AutomatableModelViewSlots::AutomatableModelViewSlots( AutomatableModelView* amv, QObject* parent ) : QObject(), @@ -243,7 +256,7 @@ void AutomatableModelViewSlots::unlinkAllModels() void AutomatableModelViewSlots::copyToClipboard() { QClipboard* clipboard = QApplication::clipboard(); - clipboard->setText(QString::number(m_amv->value())); + clipboard->setText(QString::number(m_amv->value() * m_amv->getConversionFactor())); } void AutomatableModelViewSlots::pasteFromClipboard() @@ -251,7 +264,7 @@ void AutomatableModelViewSlots::pasteFromClipboard() bool isNumber = false; const float number = floatFromClipboard(&isNumber); if (isNumber) { - m_amv->modelUntyped()->setValue(number); + m_amv->modelUntyped()->setValue(number / m_amv->getConversionFactor()); } } diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 1f0a13ec173..089964ec75e 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -72,7 +72,6 @@ Fader::Fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : m_persistentPeak_R( 0.0 ), m_fMinPeak( 0.01f ), m_fMaxPeak( 1.1 ), - m_displayConversion( true ), m_levelsDisplayedInDBFS(false), m_moveStartPoint( -1 ), m_startValue( 0 ), @@ -102,6 +101,8 @@ Fader::Fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : m_knob = s_knob; init(_model, _name); + + m_conversionFactor = 100.0; } @@ -114,7 +115,6 @@ Fader::Fader( FloatModel * model, const QString & name, QWidget * parent, QPixma m_persistentPeak_R( 0.0 ), m_fMinPeak( 0.01f ), m_fMaxPeak( 1.1 ), - m_displayConversion( false ), m_levelsDisplayedInDBFS(false), m_moveStartPoint( -1 ), m_startValue( 0 ), @@ -217,26 +217,13 @@ void Fader::mouseDoubleClickEvent( QMouseEvent* mouseEvent ) bool ok; float newValue; // TODO: dbV handling - if( m_displayConversion ) - { - newValue = QInputDialog::getDouble( this, tr( "Set value" ), - tr( "Please enter a new value between %1 and %2:" ). - arg( model()->minValue() * 100 ). - arg( model()->maxValue() * 100 ), - model()->getRoundedValue() * 100, - model()->minValue() * 100, - model()->maxValue() * 100, model()->getDigitCount(), &ok ) * 0.01f; - } - else - { - newValue = QInputDialog::getDouble( this, tr( "Set value" ), - tr( "Please enter a new value between %1 and %2:" ). - arg( model()->minValue() ). - arg( model()->maxValue() ), - model()->getRoundedValue(), - model()->minValue(), - model()->maxValue(), model()->getDigitCount(), &ok ); - } + newValue = QInputDialog::getDouble( this, tr( "Set value" ), + tr( "Please enter a new value between %1 and %2:" ). + arg( model()->minValue() * m_conversionFactor ). + arg( model()->maxValue() * m_conversionFactor ), + model()->getRoundedValue() * m_conversionFactor, + model()->minValue() * m_conversionFactor, + model()->maxValue() * m_conversionFactor, model()->getDigitCount(), &ok ) / m_conversionFactor; if( ok ) { @@ -330,14 +317,14 @@ void Fader::setPeak_R( float fPeak ) // update tooltip showing value and adjust position while changing fader value void Fader::updateTextFloat() { - if( ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() && m_displayConversion ) + if( ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() && m_conversionFactor == 100.0 ) { s_textFloat->setText( QString("Volume: %1 dBFS"). arg( ampToDbfs( model()->value() ), 3, 'f', 2 ) ); } else { - s_textFloat->setText( m_description + " " + QString("%1 ").arg( m_displayConversion ? model()->value() * 100 : model()->value() ) + " " + m_unit ); + s_textFloat->setText( m_description + " " + QString("%1 ").arg( model()->value() * m_conversionFactor ) + " " + m_unit ); } s_textFloat->moveGlobal( this, QPoint( width() - ( *m_knob ).width() - 5, knobPosY() - 46 ) ); } From 6d160fd77360ffa2bd32f2e6aaba8ca993439fe1 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Mon, 21 Sep 2020 09:04:44 +0200 Subject: [PATCH 10/28] basics: Change sampleFrame to use `std::array` (#5536) ... in order to make standard containers be able to store it. Required for #5532 (#4899) and the recording PR. This includes: * removing the `LocklessRingBuffer` specialization * passing samplerame in `StereoDelay::tick` as a reference Additional cleanups: * removing already unused typedef `sampleFrameA` * add some `const_cast` to make code more readable --- include/LocklessRingBuffer.h | 66 +++++----------------------------- include/lmms_basics.h | 8 ++--- plugins/Delay/StereoDelay.cpp | 2 +- plugins/Delay/StereoDelay.h | 2 +- src/core/Effect.cpp | 4 +-- src/core/SampleBuffer.cpp | 8 ++--- src/core/audio/AudioDevice.cpp | 4 +-- 7 files changed, 22 insertions(+), 72 deletions(-) diff --git a/include/LocklessRingBuffer.h b/include/LocklessRingBuffer.h index d313fd72288..617f7727a89 100644 --- a/include/LocklessRingBuffer.h +++ b/include/LocklessRingBuffer.h @@ -28,84 +28,36 @@ #include #include -#include "lmms_basics.h" -#include "lmms_export.h" #include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h" -//! A convenience layer for a realtime-safe and thread-safe multi-reader ring buffer library. +//! A convenience layer for a realtime-safe and thread-safe multi-reader ringbuffer template -class LocklessRingBufferBase +class LocklessRingBuffer { template friend class LocklessRingBufferReader; public: - LocklessRingBufferBase(std::size_t sz) : m_buffer(sz) + LocklessRingBuffer(std::size_t sz) : m_buffer(sz) { m_buffer.touch(); // reserve storage space before realtime operation starts } - ~LocklessRingBufferBase() {}; + ~LocklessRingBuffer() {}; std::size_t capacity() const {return m_buffer.maximum_eventual_write_space();} std::size_t free() const {return m_buffer.write_space();} void wakeAll() {m_notifier.wakeAll();} - -protected: - ringbuffer_t m_buffer; - QWaitCondition m_notifier; -}; - - -// The SampleFrameCopier is required because sampleFrame is just a two-element -// array and therefore does not have a copy constructor needed by std::copy. -class SampleFrameCopier -{ - const sampleFrame* m_src; -public: - SampleFrameCopier(const sampleFrame* src) : m_src(src) {} - void operator()(std::size_t src_offset, std::size_t count, sampleFrame* dest) - { - for (std::size_t i = src_offset; i < src_offset + count; i++, dest++) - { - (*dest)[0] = m_src[i][0]; - (*dest)[1] = m_src[i][1]; - } - } -}; - - -//! Standard ring buffer template for data types with copy constructor. -template -class LocklessRingBuffer : public LocklessRingBufferBase -{ -public: - LocklessRingBuffer(std::size_t sz) : LocklessRingBufferBase(sz) {}; - std::size_t write(const sampleFrame *src, std::size_t cnt, bool notify = false) { - std::size_t written = LocklessRingBufferBase::m_buffer.write(src, cnt); + std::size_t written = LocklessRingBuffer::m_buffer.write(src, cnt); // Let all waiting readers know new data are available. - if (notify) {LocklessRingBufferBase::m_notifier.wakeAll();} + if (notify) {LocklessRingBuffer::m_notifier.wakeAll();} return written; } -}; - -//! Specialized ring buffer template with write function modified to support sampleFrame. -template <> -class LocklessRingBuffer : public LocklessRingBufferBase -{ -public: - LocklessRingBuffer(std::size_t sz) : LocklessRingBufferBase(sz) {}; - - std::size_t write(const sampleFrame *src, std::size_t cnt, bool notify = false) - { - SampleFrameCopier copier(src); - std::size_t written = LocklessRingBufferBase::m_buffer.write_func(copier, cnt); - // Let all waiting readers know new data are available. - if (notify) {LocklessRingBufferBase::m_notifier.wakeAll();} - return written; - } +protected: + ringbuffer_t m_buffer; + QWaitCondition m_notifier; }; diff --git a/include/lmms_basics.h b/include/lmms_basics.h index 77a24649820..f114f362c15 100644 --- a/include/lmms_basics.h +++ b/include/lmms_basics.h @@ -32,6 +32,7 @@ #ifdef LMMS_HAVE_STDINT_H #include +#include #endif @@ -127,12 +128,9 @@ const ch_cnt_t SURROUND_CHANNELS = -typedef sample_t sampleFrame[DEFAULT_CHANNELS]; -typedef sample_t surroundSampleFrame[SURROUND_CHANNELS]; +using sampleFrame = std::array; +using surroundSampleFrame = std::array; #define ALIGN_SIZE 16 -#if __GNUC__ -typedef sample_t sampleFrameA[DEFAULT_CHANNELS] __attribute__((__aligned__(ALIGN_SIZE))); -#endif #define STRINGIFY(s) STR(s) diff --git a/plugins/Delay/StereoDelay.cpp b/plugins/Delay/StereoDelay.cpp index d25f6b52b18..f240b93547b 100644 --- a/plugins/Delay/StereoDelay.cpp +++ b/plugins/Delay/StereoDelay.cpp @@ -55,7 +55,7 @@ StereoDelay::~StereoDelay() -void StereoDelay::tick( sampleFrame frame ) +void StereoDelay::tick( sampleFrame& frame ) { m_writeIndex = ( m_writeIndex + 1 ) % ( int )m_maxLength; int readIndex = m_writeIndex - m_length; diff --git a/plugins/Delay/StereoDelay.h b/plugins/Delay/StereoDelay.h index a0dbdc22064..afea59b9af1 100644 --- a/plugins/Delay/StereoDelay.h +++ b/plugins/Delay/StereoDelay.h @@ -45,7 +45,7 @@ class StereoDelay m_feedback = feedback; } - void tick( sampleFrame frame ); + void tick( sampleFrame& frame ); void setSampleRate( int sampleRate ); private: diff --git a/src/core/Effect.cpp b/src/core/Effect.cpp index c842977532f..f1e32669e4f 100644 --- a/src/core/Effect.cpp +++ b/src/core/Effect.cpp @@ -203,8 +203,8 @@ void Effect::resample( int _i, const sampleFrame * _src_buf, } m_srcData[_i].input_frames = _frames; m_srcData[_i].output_frames = Engine::mixer()->framesPerPeriod(); - m_srcData[_i].data_in = (float *) _src_buf[0]; - m_srcData[_i].data_out = _dst_buf[0]; + m_srcData[_i].data_in = const_cast(_src_buf[0].data()); + m_srcData[_i].data_out = _dst_buf[0].data (); m_srcData[_i].src_ratio = (double) _dst_sr / _src_sr; m_srcData[_i].end_of_input = 0; int error; diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index cd943638d4f..368600752f0 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -693,8 +693,8 @@ bool SampleBuffer::play( sampleFrame * _ab, handleState * _state, // Generate output src_data.data_in = getSampleFragment( play_frame, fragment_size, _loopmode, &tmp, &is_backwards, - loopStartFrame, loopEndFrame, endFrame )[0]; - src_data.data_out = _ab[0]; + loopStartFrame, loopEndFrame, endFrame )->data (); + src_data.data_out = _ab->data (); src_data.input_frames = fragment_size; src_data.output_frames = _frames; src_data.src_ratio = 1.0 / freq_factor; @@ -1196,8 +1196,8 @@ SampleBuffer * SampleBuffer::resample( const sample_rate_t _src_sr, { SRC_DATA src_data; src_data.end_of_input = 1; - src_data.data_in = data[0]; - src_data.data_out = dst_buf[0]; + src_data.data_in = data->data (); + src_data.data_out = dst_buf->data (); src_data.input_frames = frames; src_data.output_frames = dst_frames; src_data.src_ratio = (double) _dst_sr / _src_sr; diff --git a/src/core/audio/AudioDevice.cpp b/src/core/audio/AudioDevice.cpp index f794602299b..a57fa2bd636 100644 --- a/src/core/audio/AudioDevice.cpp +++ b/src/core/audio/AudioDevice.cpp @@ -194,8 +194,8 @@ fpp_t AudioDevice::resample( const surroundSampleFrame * _src, } m_srcData.input_frames = _frames; m_srcData.output_frames = _frames; - m_srcData.data_in = (float *) _src[0]; - m_srcData.data_out = _dst[0]; + m_srcData.data_in = const_cast(_src[0].data()); + m_srcData.data_out = _dst[0].data (); m_srcData.src_ratio = (double) _dst_sr / _src_sr; m_srcData.end_of_input = 0; int error; From 9e401828aac306be4a66d03adbe903b758bcc269 Mon Sep 17 00:00:00 2001 From: Spekular Date: Mon, 21 Sep 2020 16:50:55 +0200 Subject: [PATCH 11/28] Don't give clips a hidden default name (Fix #5528) (#5621) * Automatic formatting changes * Give clips an empty name by default, display all names - Stop giving clips the same name as their parent track on creation - Stop hiding clip names that match the parent track name - Never rename clips on track rename - Never clear clip name when a clip is copied to another track - Create an upgrade routine to clear default names from old projects (< 1.3.0-alpha-1) - Bump version to 1.3.0-alpha-1 * Revert now-unnecessary version bump * Merge with master and fix conflicts * Formatting changes from review * Change weird for loop conditions * Properly revert AutomationPatter.h changes * Only clear names that match our parent track, be more generous with use of legacyFileVersion Co-authored-by: Hyunjin Song --- include/DataFile.h | 2 +- include/ProjectVersion.h | 1 + src/core/DataFile.cpp | 106 ++++++++++++++++++--------------- src/core/Track.cpp | 7 --- src/tracks/BBTrack.cpp | 5 +- src/tracks/InstrumentTrack.cpp | 13 +--- src/tracks/Pattern.cpp | 10 +--- 7 files changed, 66 insertions(+), 78 deletions(-) diff --git a/include/DataFile.h b/include/DataFile.h index 38a1fa7631c..5d6ead5adb3 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -114,6 +114,7 @@ class LMMS_EXPORT DataFile : public QDomDocument void upgrade_1_1_91(); void upgrade_1_2_0_rc3(); void upgrade_1_3_0(); + void upgrade_noHiddenClipNames(); // List of all upgrade methods static const std::vector UPGRADE_METHODS; @@ -141,4 +142,3 @@ class LMMS_EXPORT DataFile : public QDomDocument #endif - diff --git a/include/ProjectVersion.h b/include/ProjectVersion.h index e938ccc81c7..94ee9c6ba89 100644 --- a/include/ProjectVersion.h +++ b/include/ProjectVersion.h @@ -45,6 +45,7 @@ class ProjectVersion ProjectVersion(QString version, CompareType c = All); ProjectVersion(const char * version, CompareType c = All); + const QString& getVersion() const { return m_version; } int getMajor() const { return m_major; } int getMinor() const { return m_minor; } int getPatch() const { return m_patch; } diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 2ee74c41890..c1b48af38c8 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -59,7 +59,7 @@ const std::vector DataFile::UPGRADE_METHODS = { &DataFile::upgrade_0_4_0_beta1 , &DataFile::upgrade_0_4_0_rc2, &DataFile::upgrade_1_0_99 , &DataFile::upgrade_1_1_0, &DataFile::upgrade_1_1_91 , &DataFile::upgrade_1_2_0_rc3, - &DataFile::upgrade_1_3_0 + &DataFile::upgrade_1_3_0 , &DataFile::upgrade_noHiddenClipNames }; // Vector of all versions that have upgrade routines. @@ -1355,6 +1355,35 @@ void DataFile::upgrade_1_3_0() } } +void DataFile::upgrade_noHiddenClipNames() +{ + QDomNodeList tracks = elementsByTagName("track"); + + auto clearDefaultNames = [](QDomNodeList clips, QString trackName) + { + for (int j = 0; j < clips.size(); ++j) + { + QDomElement clip = clips.item(j).toElement(); + QString clipName = clip.attribute("name", ""); + if (clipName == trackName) { clip.setAttribute("name", ""); } + } + }; + + for (int i = 0; i < tracks.size(); ++i) + { + QDomElement track = tracks.item(i).toElement(); + QString trackName = track.attribute("name", ""); + + QDomNodeList instClips = track.elementsByTagName("pattern"); + QDomNodeList autoClips = track.elementsByTagName("automationpattern"); + QDomNodeList bbClips = track.elementsByTagName("bbtco"); + + clearDefaultNames(instClips, trackName); + clearDefaultNames(autoClips, trackName); + clearDefaultNames(bbClips, trackName); + } +} + void DataFile::upgrade() { @@ -1430,62 +1459,45 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) m_type = type( root.attribute( "type" ) ); m_head = root.elementsByTagName( "head" ).item( 0 ).toElement(); - if( root.hasAttribute( "version" ) ) + if (!root.hasAttribute("version") || root.attribute("version")=="1.0") { - if( root.attribute( "version" ) == "1.0" ){ - // The file versioning is now a unsigned int, not maj.min, so we use - // legacyFileVersion() to retrieve the appropriate version - m_fileVersion = legacyFileVersion(); - } - else - { - bool success; - m_fileVersion = root.attribute( "version" ).toUInt( &success ); - if( !success ) qWarning("File Version conversion failure."); - } + // The file versioning is now a unsigned int, not maj.min, so we use + // legacyFileVersion() to retrieve the appropriate version + m_fileVersion = legacyFileVersion(); + } + else + { + bool success; + m_fileVersion = root.attribute( "version" ).toUInt( &success ); + if( !success ) qWarning("File Version conversion failure."); } - if( root.hasAttribute( "creatorversion" ) ) + if (root.hasAttribute("creatorversion")) { // compareType defaults to All, so it doesn't have to be set here - ProjectVersion createdWith = root.attribute( "creatorversion" ); + ProjectVersion createdWith = root.attribute("creatorversion"); ProjectVersion openedWith = LMMS_VERSION; - if ( createdWith != openedWith ) - { - if( createdWith.setCompareType( ProjectVersion::Minor ) != - openedWith.setCompareType( ProjectVersion::Minor ) ) - { - if( gui != nullptr && root.attribute( "type" ) == "song" ) - { - TextFloat::displayMessage( - SongEditor::tr( "Version difference" ), - SongEditor::tr( - "This %1 was created with " - "LMMS %2." - ).arg( - _sourceFile.endsWith( ".mpt" ) ? - SongEditor::tr( "template" ) : - SongEditor::tr( "project" ) - ) - .arg( root.attribute( "creatorversion" ) ), - embed::getIconPixmap( "whatsthis", 24, 24 ), - 2500 - ); - } - } - - // the upgrade needs to happen after the warning as it updates the project version. - if( createdWith.setCompareType( ProjectVersion::Build ) - < openedWith ) - { - upgrade(); - } + if (createdWith < openedWith) { upgrade(); } + + if (createdWith.setCompareType(ProjectVersion::Minor) + != openedWith.setCompareType(ProjectVersion::Minor) + && gui != nullptr && root.attribute("type") == "song" + ){ + auto projectType = _sourceFile.endsWith(".mpt") ? + SongEditor::tr("template") : SongEditor::tr("project"); + + TextFloat::displayMessage( + SongEditor::tr("Version difference"), + SongEditor::tr("This %1 was created with LMMS %2") + .arg(projectType).arg(createdWith.getVersion()), + embed::getIconPixmap("whatsthis", 24, 24), + 2500 + ); } } - m_content = root.elementsByTagName( typeName( m_type ) ). - item( 0 ).toElement(); + m_content = root.elementsByTagName(typeName(m_type)).item(0).toElement(); } diff --git a/src/core/Track.cpp b/src/core/Track.cpp index e52270fdd83..c7f6d62b0b5 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1865,13 +1865,6 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, const QMimeData * md, { tco->selectViewOnCreate( true ); } - - //check tco name, if the same as source track name dont copy - QString sourceTrackName = outerTCOElement.attributeNode( "trackName" ).value(); - if( tco->name() == sourceTrackName ) - { - tco->setName( "" ); - } } AutomationPattern::resolveAllIDs(); diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index a779e2ea49b..ec6b4042004 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -290,10 +290,7 @@ void BBTCOView::openInBBEditor() -void BBTCOView::resetName() -{ - m_bbTCO->setName( m_bbTCO->getTrack()->name() ); -} +void BBTCOView::resetName() { m_bbTCO->setName(""); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 8d0cb24316e..5a51bb8aced 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -151,7 +151,7 @@ InstrumentTrack::~InstrumentTrack() autoAssignMidiDevice(false); s_autoAssignedTrack = NULL; } - + // kill all running notes and the iph silenceAllNotes( true ); @@ -530,17 +530,6 @@ void InstrumentTrack::deleteNotePluginData( NotePlayHandle* n ) void InstrumentTrack::setName( const QString & _new_name ) { - // when changing name of track, also change name of those patterns, - // which have the same name as the instrument-track - for( int i = 0; i < numOfTCOs(); ++i ) - { - Pattern* p = dynamic_cast( getTCO( i ) ); - if( ( p != NULL && p->name() == name() ) || p->name() == "" ) - { - p->setName( _new_name ); - } - } - Track::setName( _new_name ); m_midiPort.setName( name() ); m_audioPort.setName( name() ); diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 52259c70733..e5de8b83b68 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -56,7 +56,6 @@ Pattern::Pattern( InstrumentTrack * _instrument_track ) : m_patternType( BeatPattern ), m_steps( MidiTime::stepsPerBar() ) { - setName( _instrument_track->name() ); if( _instrument_track->trackContainer() == Engine::getBBTrackContainer() ) { @@ -647,10 +646,7 @@ void PatternView::setGhostInPianoRoll() -void PatternView::resetName() -{ - m_pat->setName( m_pat->m_instrumentTrack->name() ); -} +void PatternView::resetName() { m_pat->setName(""); } @@ -885,8 +881,8 @@ void PatternView::paintEvent( QPaintEvent * ) // Check whether we will paint a text box and compute its potential height // This is needed so we can paint the notes underneath it. - bool const isDefaultName = m_pat->name() == m_pat->instrumentTrack()->name(); - bool const drawTextBox = !beatPattern && !isDefaultName; + bool const drawName = !m_pat->name().isEmpty(); + bool const drawTextBox = !beatPattern && drawName; // TODO Warning! This might cause problems if TrackContentObjectView::paintTextLabel changes int textBoxHeight = 0; From 454e0472914550dce5e80d976283d56faaa55832 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 11 Jun 2020 08:19:44 +0200 Subject: [PATCH 12/28] Add lv2 packages to macOS CI --- .circleci/config.yml | 2 +- .travis/osx..install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fe5b1f2fd7c..408fef721a1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -181,7 +181,7 @@ jobs: command: | # uninstall Homebrew's python 2 to prevent errors on brew install brew uninstall python@2 || true - brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio fltk qt5 carla + brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio lilv lv2 stk fluid-synth portaudio fltk qt5 carla - run: name: Install nodejs dependencies command: npm install -g appdmg diff --git a/.travis/osx..install.sh b/.travis/osx..install.sh index 93d478c40f4..42bf66acab4 100755 --- a/.travis/osx..install.sh +++ b/.travis/osx..install.sh @@ -2,7 +2,7 @@ set -e -PACKAGES="cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio node fltk qt carla" +PACKAGES="cmake pkg-config libogg libvorbis lame libsndfile libsamplerate lilv lv2 jack sdl libgig libsoundio stk fluid-synth portaudio node fltk qt carla" if "${TRAVIS}"; then PACKAGES="$PACKAGES ccache" From c3c80159fd286c0fdd4670c2343ac33787099272 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 24 Sep 2020 09:45:31 +0200 Subject: [PATCH 13/28] Add missing include --- include/Lv2UridMap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/Lv2UridMap.h b/include/Lv2UridMap.h index f1ddb6253b7..39cfcc44fb2 100644 --- a/include/Lv2UridMap.h +++ b/include/Lv2UridMap.h @@ -31,6 +31,7 @@ #include #include // TODO: use semaphore, even though this is not realtime critical +#include #include #include From 6546857d213254b33feb2b075946292081ec2f52 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 24 Sep 2020 10:18:50 +0200 Subject: [PATCH 14/28] Move or remove unused slots --- plugins/Lv2Instrument/Lv2Instrument.cpp | 7 ------- plugins/Lv2Instrument/Lv2Instrument.h | 4 ---- src/gui/Lv2ViewBase.cpp | 7 +++++++ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 0f7534ac8c9..d5ba48787fb 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -271,13 +271,6 @@ void Lv2InsView::dropEvent(QDropEvent *_de) -void Lv2InsView::toggleUI() -{ -} - - - - void Lv2InsView::modelChanged() { Lv2ViewBase::modelChanged(castModel()); diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 6451d49cdcf..4fb2e1e34c3 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -111,10 +111,6 @@ class Lv2InsView : public InstrumentView, public Lv2ViewBase void dragEnterEvent(QDragEnterEvent *_dee) override; void dropEvent(QDropEvent *_de) override; -private slots: - void reloadPlugin(); - void toggleUI(); - private: void modelChanged() override; }; diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 9b9217e48b2..488705bcb66 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -202,6 +202,13 @@ Lv2ViewBase::~Lv2ViewBase() { +void Lv2ViewBase::toggleUI() +{ +} + + + + void Lv2ViewBase::toggleHelp(bool visible) { if (m_helpWindow) From e7adcbd8473a5c85ef3cbd6f8ebc249befcd1dec Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 27 Sep 2020 17:50:44 +0200 Subject: [PATCH 15/28] Update ringbuffer submodule --- src/3rdparty/ringbuffer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty/ringbuffer b/src/3rdparty/ringbuffer index 82ed7cfb9ad..ea00e1fc2b8 160000 --- a/src/3rdparty/ringbuffer +++ b/src/3rdparty/ringbuffer @@ -1 +1 @@ -Subproject commit 82ed7cfb9ad40467421d8b14ca1af0350e92613c +Subproject commit ea00e1fc2b821a0e06d78e8cc13ac06e6b4f72f5 From 173b1fadf8f8e709167d9481799bb741aec39212 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 28 Sep 2020 17:58:14 +0200 Subject: [PATCH 16/28] Improve control flow in Lv2 classes No functional change --- src/core/lv2/Lv2Ports.cpp | 10 ++- src/core/lv2/Lv2Proc.cpp | 132 ++++++++++++++++++++------------------ 2 files changed, 76 insertions(+), 66 deletions(-) diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index ae2d26d4971..48fe47f604d 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -125,6 +125,7 @@ std::vector Meta::get(const LilvPlugin *plugin, m_def = .0f; m_min = .0f; m_max = .0f; + m_type = Type::Unknown; if (isA(LV2_CORE__ControlPort)) { m_type = Type::Control; @@ -162,14 +163,17 @@ std::vector Meta::get(const LilvPlugin *plugin, } } else if (isA(LV2_CORE__AudioPort)) { m_type = Type::Audio; } - else if (isA(LV2_CORE__CVPort)) { + else if (isA(LV2_CORE__CVPort)) + { issue(badPortType, "cvPort"); m_type = Type::Cv; - } else { + } + + if(m_type == Type::Unknown) + { if (m_optional) { m_used = false; } else { issue(PluginIssueType::unknownPortType, portName); - m_type = Type::Unknown; } } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index ceb9b17123e..9046001ab13 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -311,78 +311,84 @@ void Lv2Proc::createPort(std::size_t portNum) const LilvPort* lilvPort = lilv_plugin_get_port_by_index(m_plugin, static_cast(portNum)); Lv2Ports::PortBase* port; - if (meta.m_type == Lv2Ports::Type::Control) + + switch (meta.m_type) { - Lv2Ports::Control* ctrl = new Lv2Ports::Control; - if (meta.m_flow == Lv2Ports::Flow::Input) + case Lv2Ports::Type::Control: { - AutoLilvNode node(lilv_port_get_name(m_plugin, lilvPort)); - QString dispName = lilv_node_as_string(node.get()); - switch (meta.m_vis) + Lv2Ports::Control* ctrl = new Lv2Ports::Control; + if (meta.m_flow == Lv2Ports::Flow::Input) { - case Lv2Ports::Vis::None: - { - // allow ~1000 steps - float stepSize = (meta.m_max - meta.m_min) / 1000.0f; - - // make multiples of 0.01 (or 0.1 for larger values) - float minStep = (stepSize >= 1.0f) ? 0.1f : 0.01f; - stepSize -= fmodf(stepSize, minStep); - stepSize = std::max(stepSize, minStep); - - ctrl->m_connectedModel.reset( - new FloatModel(meta.m_def, meta.m_min, meta.m_max, - stepSize, nullptr, dispName)); - break; - } - case Lv2Ports::Vis::Integer: - ctrl->m_connectedModel.reset( - new IntModel(static_cast(meta.m_def), - static_cast(meta.m_min), - static_cast(meta.m_max), - nullptr, dispName)); - break; - case Lv2Ports::Vis::Enumeration: + AutoLilvNode node(lilv_port_get_name(m_plugin, lilvPort)); + QString dispName = lilv_node_as_string(node.get()); + switch (meta.m_vis) { - ComboBoxModel* comboModel - = new ComboBoxModel( - nullptr, dispName); - LilvScalePoints* sps = - lilv_port_get_scale_points(m_plugin, lilvPort); - LILV_FOREACH(scale_points, i, sps) + case Lv2Ports::Vis::None: { - const LilvScalePoint* sp = lilv_scale_points_get(sps, i); - ctrl->m_scalePointMap.push_back(lilv_node_as_float( - lilv_scale_point_get_value(sp))); - comboModel->addItem( - lilv_node_as_string( - lilv_scale_point_get_label(sp))); + // allow ~1000 steps + float stepSize = (meta.m_max - meta.m_min) / 1000.0f; + + // make multiples of 0.01 (or 0.1 for larger values) + float minStep = (stepSize >= 1.0f) ? 0.1f : 0.01f; + stepSize -= fmodf(stepSize, minStep); + stepSize = std::max(stepSize, minStep); + + ctrl->m_connectedModel.reset( + new FloatModel(meta.m_def, meta.m_min, meta.m_max, + stepSize, nullptr, dispName)); + break; } - lilv_scale_points_free(sps); - ctrl->m_connectedModel.reset(comboModel); - break; + case Lv2Ports::Vis::Integer: + ctrl->m_connectedModel.reset( + new IntModel(static_cast(meta.m_def), + static_cast(meta.m_min), + static_cast(meta.m_max), + nullptr, dispName)); + break; + case Lv2Ports::Vis::Enumeration: + { + ComboBoxModel* comboModel + = new ComboBoxModel( + nullptr, dispName); + LilvScalePoints* sps = + lilv_port_get_scale_points(m_plugin, lilvPort); + LILV_FOREACH(scale_points, i, sps) + { + const LilvScalePoint* sp = lilv_scale_points_get(sps, i); + ctrl->m_scalePointMap.push_back(lilv_node_as_float( + lilv_scale_point_get_value(sp))); + comboModel->addItem( + lilv_node_as_string( + lilv_scale_point_get_label(sp))); + } + lilv_scale_points_free(sps); + ctrl->m_connectedModel.reset(comboModel); + break; + } + case Lv2Ports::Vis::Toggled: + ctrl->m_connectedModel.reset( + new BoolModel(static_cast(meta.m_def), + nullptr, dispName)); + break; } - case Lv2Ports::Vis::Toggled: - ctrl->m_connectedModel.reset( - new BoolModel(static_cast(meta.m_def), - nullptr, dispName)); - break; } + port = ctrl; + break; } - port = ctrl; - } - else if (meta.m_type == Lv2Ports::Type::Audio) - { - Lv2Ports::Audio* audio = - new Lv2Ports::Audio( - static_cast( - Engine::mixer()->framesPerPeriod()), - portIsSideChain(m_plugin, lilvPort), - portIsOptional(m_plugin, lilvPort) - ); - port = audio; - } else { - port = new Lv2Ports::Unknown; + case Lv2Ports::Type::Audio: + { + Lv2Ports::Audio* audio = + new Lv2Ports::Audio( + static_cast( + Engine::mixer()->framesPerPeriod()), + portIsSideChain(m_plugin, lilvPort), + portIsOptional(m_plugin, lilvPort) + ); + port = audio; + break; + } + default: + port = new Lv2Ports::Unknown; } // `meta` is of class `Lv2Ports::Meta` and `port` is of a child class From 31f79a22634d55bac42163ebb4600c075464ee7e Mon Sep 17 00:00:00 2001 From: Veratil Date: Mon, 16 Sep 2019 16:21:37 -0500 Subject: [PATCH 17/28] Add auto-highlight scale and key selection --- include/PianoRoll.h | 5 +- src/gui/editors/PianoRoll.cpp | 99 ++++++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 20 deletions(-) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 7a99e7b1f80..e3e4b312085 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -195,12 +195,13 @@ protected slots: void zoomingYChanged(); void quantizeChanged(); void noteLengthChanged(); + void keyChanged(); void quantizeNotes(); void updateSemiToneMarkerMenu(); void changeNoteEditMode( int i ); - void markSemiTone( int i ); + void markSemiTone(int i, bool fromMenu = true); void hidePattern( Pattern* pattern ); @@ -311,6 +312,7 @@ protected slots: ComboBoxModel m_zoomingYModel; ComboBoxModel m_quantizeModel; ComboBoxModel m_noteLenModel; + ComboBoxModel m_keyModel; ComboBoxModel m_scaleModel; ComboBoxModel m_chordModel; @@ -507,6 +509,7 @@ private slots: ComboBox * m_zoomingYComboBox; ComboBox * m_quantizeComboBox; ComboBox * m_noteLenComboBox; + ComboBox * m_keyComboBox; ComboBox * m_scaleComboBox; ComboBox * m_chordComboBox; QPushButton * m_clearGhostButton; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index c63225440dc..0eb049ddcae 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -374,6 +374,13 @@ PianoRoll::PianoRoll() : connect( &m_noteLenModel, SIGNAL( dataChanged() ), this, SLOT( noteLengthChanged() ) ); + // Set up key selection dropdown + m_keyModel.addItem(tr("No key")); + // Use piano roll note strings for key dropdown + for (int i = 0; i < 12; i++) { m_keyModel.addItem(s_noteStrings[i]); } + m_keyModel.setValue(0); // start with "No key" + connect(&m_keyModel, &ComboBoxModel::dataChanged, this, &PianoRoll::keyChanged); + // Set up scale model const InstrumentFunctionNoteStacking::ChordTable& chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance(); @@ -388,6 +395,8 @@ PianoRoll::PianoRoll() : } m_scaleModel.setValue( 0 ); + // connect scale change to key change so it auto-highlights with scale as well + connect(&m_scaleModel, &ComboBoxModel::dataChanged, this, &PianoRoll::keyChanged); // change can update m_semiToneMarkerMenu connect( &m_scaleModel, SIGNAL( dataChanged() ), this, SLOT( updateSemiToneMarkerMenu() ) ); @@ -485,11 +494,17 @@ void PianoRoll::changeNoteEditMode( int i ) } -void PianoRoll::markSemiTone( int i ) +void PianoRoll::markSemiTone(int i, bool fromMenu) { - const int key = m_pianoKeySelected; + const int key = fromMenu + ? getKey(mapFromGlobal(m_semiToneMarkerMenu->pos()).y()) + : m_keyModel.value() - 1; const InstrumentFunctionNoteStacking::Chord * chord = nullptr; + // if "No key" is selected, key is -1, unmark all semitones + // or if scale changed from toolbar to "No scale", unmark all semitones + if (!fromMenu && (key < 0 || m_scaleModel.value() == 0)) { i = stmaUnmarkAll; } + switch( static_cast( i ) ) { case stmaUnmarkAll: @@ -578,6 +593,8 @@ void PianoRoll::markSemiTone( int i ) std::sort( m_markedSemiTones.begin(), m_markedSemiTones.end(), std::greater() ); QList::iterator new_end = std::unique( m_markedSemiTones.begin(), m_markedSemiTones.end() ); m_markedSemiTones.erase( new_end, m_markedSemiTones.end() ); + // until we move the mouse the window won't update, force redraw + update(); } @@ -4139,6 +4156,10 @@ void PianoRoll::noteLengthChanged() update(); } +void PianoRoll::keyChanged() +{ + markSemiTone(stmaMarkCurrentScale, false); +} int PianoRoll::quantization() const { @@ -4380,6 +4401,12 @@ PianoRollWindow::PianoRollWindow() : m_noteLenComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); m_noteLenComboBox->setToolTip( tr( "Note length") ); + // setup key-stuff + m_keyComboBox = new ComboBox(m_toolBar); + m_keyComboBox->setModel(&m_editor->m_keyModel); + m_keyComboBox->setFixedSize(72, ComboBox::DEFAULT_HEIGHT); + m_keyComboBox->setToolTip(tr("Key")); + // setup scale-stuff QLabel * scale_lbl = new QLabel( m_toolBar ); scale_lbl->setPixmap( embed::getIconPixmap( "scale" ) ); @@ -4406,27 +4433,61 @@ PianoRollWindow::PianoRollWindow() : connect( m_clearGhostButton, SIGNAL( clicked() ), m_editor, SLOT( clearGhostPattern() ) ); connect( m_editor, SIGNAL( ghostPatternSet( bool ) ), this, SLOT( ghostPatternSet( bool ) ) ); - zoomAndNotesToolBar->addWidget( zoom_lbl ); - zoomAndNotesToolBar->addWidget( m_zoomingComboBox ); - - zoomAndNotesToolBar->addWidget(zoom_y_lbl); - zoomAndNotesToolBar->addWidget(m_zoomingYComboBox); - + // Wrap label icons and comboboxes in a single widget so when + // the window is resized smaller in width it hides both + QWidget * zoom_widget = new QWidget(); + QHBoxLayout * zoom_hbox = new QHBoxLayout(); + zoom_hbox->setContentsMargins(0, 0, 0, 0); + zoom_hbox->addWidget(zoom_lbl); + zoom_hbox->addWidget(m_zoomingComboBox); + zoom_widget->setLayout(zoom_hbox); + zoomAndNotesToolBar->addWidget(zoom_widget); + + QWidget * zoomY_widget = new QWidget(); + QHBoxLayout * zoomY_hbox = new QHBoxLayout(); + zoomY_hbox->setContentsMargins(0, 0, 0, 0); + zoomY_hbox->addWidget(zoom_y_lbl); + zoomY_hbox->addWidget(m_zoomingYComboBox); + zoomY_widget->setLayout(zoomY_hbox); + zoomAndNotesToolBar->addWidget(zoomY_widget); + + QWidget * quantize_widget = new QWidget(); + QHBoxLayout * quantize_hbox = new QHBoxLayout(); + quantize_hbox->setContentsMargins(0, 0, 0, 0); + quantize_hbox->addWidget(quantize_lbl); + quantize_hbox->addWidget(m_quantizeComboBox); + quantize_widget->setLayout(quantize_hbox); zoomAndNotesToolBar->addSeparator(); - zoomAndNotesToolBar->addWidget( quantize_lbl ); - zoomAndNotesToolBar->addWidget( m_quantizeComboBox ); - + zoomAndNotesToolBar->addWidget(quantize_widget); + + QWidget * note_widget = new QWidget(); + QHBoxLayout * note_hbox = new QHBoxLayout(); + note_hbox->setContentsMargins(0, 0, 0, 0); + note_hbox->addWidget(note_len_lbl); + note_hbox->addWidget(m_noteLenComboBox); + note_widget->setLayout(note_hbox); zoomAndNotesToolBar->addSeparator(); - zoomAndNotesToolBar->addWidget( note_len_lbl ); - zoomAndNotesToolBar->addWidget( m_noteLenComboBox ); - + zoomAndNotesToolBar->addWidget(note_widget); + + QWidget * scale_widget = new QWidget(); + QHBoxLayout * scale_hbox = new QHBoxLayout(); + scale_hbox->setContentsMargins(0, 0, 0, 0); + scale_hbox->addWidget(scale_lbl); + // Add the key selection between scale label and key + scale_hbox->addWidget(m_keyComboBox); + scale_hbox->addWidget(m_scaleComboBox); + scale_widget->setLayout(scale_hbox); zoomAndNotesToolBar->addSeparator(); - zoomAndNotesToolBar->addWidget( scale_lbl ); - zoomAndNotesToolBar->addWidget( m_scaleComboBox ); - + zoomAndNotesToolBar->addWidget(scale_widget); + + QWidget * chord_widget = new QWidget(); + QHBoxLayout * chord_hbox = new QHBoxLayout(); + chord_hbox->setContentsMargins(0, 0, 0, 0); + chord_hbox->addWidget(chord_lbl); + chord_hbox->addWidget(m_chordComboBox); + chord_widget->setLayout(chord_hbox); zoomAndNotesToolBar->addSeparator(); - zoomAndNotesToolBar->addWidget( chord_lbl ); - zoomAndNotesToolBar->addWidget( m_chordComboBox ); + zoomAndNotesToolBar->addWidget(chord_widget); zoomAndNotesToolBar->addSeparator(); zoomAndNotesToolBar->addWidget( m_clearGhostButton ); From cc87dfbb5256cd43d5c32efff8e3cd307e70168f Mon Sep 17 00:00:00 2001 From: Spekular Date: Wed, 30 Sep 2020 11:26:58 +0200 Subject: [PATCH 18/28] Fix relativeOrAbsolute bug with baseQDir in PathUtils.cpp (#5689) * Fix relativeOrAbsolute bug with baseQDir in PathUtils.cpp If `base` is `Absolute`, `baseQDir(base)` will point to the working directory. It will result in undefined behaviors. To fix this, update relativeOrAbsolute to explicitly return an absolute path when the target base is Absolute. Also make baseQDir return QDir::root() for Absolute base instead of QDir(""), because the latter represents the working directory. Co-authored-by: Hyunjin Song --- src/core/PathUtil.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/PathUtil.cpp b/src/core/PathUtil.cpp index ab81c49417c..5881db9f40f 100644 --- a/src/core/PathUtil.cpp +++ b/src/core/PathUtil.cpp @@ -36,7 +36,11 @@ namespace PathUtil return QDir::cleanPath(loc) + "/"; } - QDir baseQDir (const Base base) { return QDir(baseLocation(base)); } + QDir baseQDir (const Base base) + { + if (base == Base::Absolute) { return QDir::root(); } + return QDir(baseLocation(base)); + } QString basePrefix(const Base base) { @@ -123,6 +127,7 @@ namespace PathUtil { if (input.isEmpty()) { return input; } QString absolutePath = toAbsolute(input); + if (base == Base::Absolute) { return absolutePath; } QString relativePath = baseQDir(base).relativeFilePath(absolutePath); return relativePath.startsWith("..") ? absolutePath : relativePath; } From 89b13280de93ad2131c12dc234fa65cd10855996 Mon Sep 17 00:00:00 2001 From: thmueller64 <64359888+thmueller64@users.noreply.github.com> Date: Sat, 3 Oct 2020 07:05:55 +0200 Subject: [PATCH 19/28] LB302: Use consistent cutoff frequency on mulitple sample rates (#5618) --- plugins/lb302/lb302.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index cc671749db1..e9a518e3d0c 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -74,7 +74,7 @@ //#define engine::mixer()->processingSampleRate() 44100.0f - +const float sampleRateCutoff = 44100.0f; extern "C" { @@ -228,8 +228,11 @@ void lb302Filter3Pole::envRecalc() // e0 is adjusted for Hz and doesn't need ENVINC w = vcf_e0 + vcf_c0; k = (fs->cutoff > 0.975)?0.975:fs->cutoff; + // sampleRateCutoff should not be changed to anything dynamic that is outside the + // scope of lb302 (like e.g. the mixers sample rate) as this changes the filters cutoff + // behavior without any modification to its controls. kfco = 50.f + (k)*((2300.f-1600.f*(fs->envmod))+(w) * - (700.f+1500.f*(k)+(1500.f+(k)*(Engine::mixer()->processingSampleRate()/2.f-6000.f)) * + (700.f+1500.f*(k)+(1500.f+(k)*(sampleRateCutoff/2.f-6000.f)) * (fs->envmod)) ); //+iacc*(.3+.7*kfco*kenvmod)*kaccent*kaccurve*2000 From e5f1007ebbec5b9ae6d576712bf2ca4676d09fbf Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Mon, 6 Nov 2017 22:29:10 +0200 Subject: [PATCH 20/28] SampleTrack: Fix TCO not being played on the first tick (if it starts on tick 0) --- src/tracks/SampleTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index d73b62cbaff..80d41adea7f 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -674,7 +674,7 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, if( _start >= sTco->startPosition() && _start < sTco->endPosition() ) { - if( sTco->isPlaying() == false && _start > sTco->startPosition() + sTco->startTimeOffset() ) + if( sTco->isPlaying() == false && _start >= (sTco->startPosition() + sTco->startTimeOffset()) ) { auto bufferFramesPerTick = Engine::framesPerTick (sTco->sampleBuffer ()->sampleRate ()); f_cnt_t sampleStart = bufferFramesPerTick * ( _start - sTco->startPosition() - sTco->startTimeOffset() ); From 109a7c47356485ea6126f17f13747043e0272236 Mon Sep 17 00:00:00 2001 From: Spekular Date: Sat, 3 Oct 2020 21:31:13 +0200 Subject: [PATCH 21/28] Keyboard shortcuts to preview/add sounds from sidebar (#5427) - Extract file item preview start and end into new methods `previewFileItem` and `stopPreview`. - Add event handlers: - `keyPressEvent` to allow auto preview (on up/down arrow navigation), manual preview (space), and send to editors (enter) - `keyReleaseEvent` to end previews when preview key is released - `hideEvent` to end previews when switching sidebar tab or hiding sidebar - Functions that operate on a `FileItem` now take it as an argument instead of using a member variable - `getContextActions` provides menu items for sending clips to the song editor and BB editor with minimal duplicate code - Some formatting changes in affected code - Replace many instances of `NULL` with `nullptr` --- include/FileBrowser.h | 30 ++- src/gui/FileBrowser.cpp | 465 +++++++++++++++++++++++++--------------- 2 files changed, 314 insertions(+), 181 deletions(-) diff --git a/include/FileBrowser.h b/include/FileBrowser.h index d36eacf0775..a890506bdf3 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -63,9 +63,9 @@ class FileBrowser : public SideBarWidget private slots: void reloadTree( void ); - void expandItems( QTreeWidgetItem * item=NULL, QList expandedDirs = QList() ); + void expandItems( QTreeWidgetItem * item=nullptr, QList expandedDirs = QList() ); // call with item=NULL to filter the entire tree - bool filterItems( const QString & filter, QTreeWidgetItem * item=NULL ); + bool filterItems( const QString & filter, QTreeWidgetItem * item=nullptr ); void giveFocusToFilter(); private: @@ -105,29 +105,38 @@ class FileBrowserTreeWidget : public QTreeWidget void mousePressEvent( QMouseEvent * me ) override; void mouseMoveEvent( QMouseEvent * me ) override; void mouseReleaseEvent( QMouseEvent * me ) override; + void keyPressEvent( QKeyEvent * ke ) override; + void keyReleaseEvent( QKeyEvent * ke ) override; + void hideEvent( QHideEvent * he ) override; private: + //! Start a preview of a file item + void previewFileItem(FileItem* file); + //! If a preview is playing, stop it. + void stopPreview(); + void handleFile( FileItem * fi, InstrumentTrack * it ); - void openInNewInstrumentTrack( TrackContainer* tc ); + void openInNewInstrumentTrack( TrackContainer* tc, FileItem* item ); bool m_mousePressed; QPoint m_pressPos; + //! This should only be accessed or modified when m_pphMutex is held PlayHandle* m_previewPlayHandle; QMutex m_pphMutex; - FileItem * m_contextMenuItem; + QList getContextActions(FileItem* item, bool songEditor); private slots: void activateListItem( QTreeWidgetItem * item, int column ); - void openInNewInstrumentTrackBBE( void ); - void openInNewInstrumentTrackSE( void ); - void sendToActiveInstrumentTrack( void ); + void openInNewInstrumentTrack( FileItem* item, bool songEditor ); + bool openInNewSampleTrack( FileItem* item ); + void sendToActiveInstrumentTrack( FileItem* item ); void updateDirectory( QTreeWidgetItem * item ); - void openContainingFolder(); + void openContainingFolder( FileItem* item ); } ; @@ -234,6 +243,11 @@ class FileItem : public QTreeWidgetItem return( m_handling ); } + inline bool isTrack( void ) const + { + return m_handling == LoadAsPreset || m_handling == LoadByPlugin; + } + QString extension( void ); static QString extension( const QString & file ); diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index b37bf03f207..0524146069e 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -50,6 +50,7 @@ #include "PluginFactory.h" #include "PresetPreviewPlayHandle.h" #include "SamplePlayHandle.h" +#include "SampleTrack.h" #include "Song.h" #include "StringPairDrag.h" #include "TextFloat.h" @@ -173,7 +174,7 @@ void FileBrowser::reloadTree( void ) { addItems( *it ); } - expandItems(NULL, expandedDirs); + expandItems(nullptr, expandedDirs); m_filterEdit->setText( text ); filterItems( text ); } @@ -240,7 +241,7 @@ void FileBrowser::addItems(const QString & path ) { Directory * d = dynamic_cast( m_fileBrowserTreeWidget->topLevelItem( i ) ); - if( d == NULL || cur_file < d->text( 0 ) ) + if( d == nullptr || cur_file < d->text( 0 ) ) { // insert before item, we're done Directory *dd = new Directory( cur_file, path, @@ -300,13 +301,12 @@ void FileBrowser::addItems(const QString & path ) void FileBrowser::keyPressEvent(QKeyEvent * ke ) { - if( ke->key() == Qt::Key_F5 ) - { - reloadTree(); - } - else - { - ke->ignore(); + switch( ke->key() ){ + case Qt::Key_F5: + reloadTree(); + break; + default: + ke->ignore(); } } @@ -321,9 +321,8 @@ FileBrowserTreeWidget::FileBrowserTreeWidget(QWidget * parent ) : QTreeWidget( parent ), m_mousePressed( false ), m_pressPos(), - m_previewPlayHandle( NULL ), - m_pphMutex( QMutex::Recursive ), - m_contextMenuItem( NULL ) + m_previewPlayHandle( nullptr ), + m_pphMutex( QMutex::Recursive ) { setColumnCount( 1 ); headerItem()->setHidden( true ); @@ -338,6 +337,9 @@ FileBrowserTreeWidget::FileBrowserTreeWidget(QWidget * parent ) : } + + + QList FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) const { int numChildren = item ? item->childCount() : topLevelItemCount(); @@ -362,59 +364,157 @@ QList FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) con return dirs; } -void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e ) + + + +void FileBrowserTreeWidget::keyPressEvent(QKeyEvent * ke ) { - FileItem * f = dynamic_cast(itemAt(e->pos())); - if (f == nullptr) + // Shorter names for some commonly used properties of the event + const auto key = ke->key(); + const bool vertical = (key == Qt::Key_Up || key == Qt::Key_Down); + const bool horizontal = (key == Qt::Key_Left || key == Qt::Key_Right); + const bool insert = (key == Qt::Key_Enter || key == Qt::Key_Return); + const bool preview = (key == Qt::Key_Space); + + // First of all, forward all keypresses + QTreeWidget::keyPressEvent(ke); + // Then, ignore all autorepeats (they would spam new tracks or previews) + if (ke->isAutoRepeat()) { return; } + // We should stop any running previews before we do anything new + else if (vertical || horizontal || preview || insert) { stopPreview(); } + + // Try to get the currently selected item as a FileItem + FileItem * file = dynamic_cast(currentItem()); + // If it's null (folder, separator, etc.), there's nothing left for us to do + if (file == nullptr) { return; } + + // When moving to a new sound, preview it. Skip presets, they can play forever + if (vertical && file->type() == FileItem::SampleFile) { - return; + previewFileItem(file); + } + + // When enter is pressed, add the selected item... + if (insert) + { + // ...to the song editor by default, or to the BB editor if ctrl is held + bool songEditor = !(ke->modifiers() & Qt::ControlModifier); + // If shift is held, we send the item to a new sample track... + bool sampleTrack = ke->modifiers() & Qt::ShiftModifier; + // ...but only in the song editor. So, ctrl+shift enter does nothing + if (sampleTrack && songEditor){ openInNewSampleTrack(file); } + // Otherwise we send the item as a new instrument track + else if (!sampleTrack){ openInNewInstrumentTrack(file, songEditor); } } - if (f->handling() == FileItem::LoadAsPreset || f->handling() == FileItem::LoadByPlugin) + // When space is pressed, start a preview of the selected item + if (preview) { previewFileItem(file); } +} + + + + +void FileBrowserTreeWidget::keyReleaseEvent(QKeyEvent* ke) +{ + // Cancel previews when the space key is released + if (ke->key() == Qt::Key_Space && !ke->isAutoRepeat()) { stopPreview(); } +} + + + + +void FileBrowserTreeWidget::hideEvent(QHideEvent* he) +{ + // Cancel previews when the user switches tabs or hides the sidebar + stopPreview(); + QTreeWidget::hideEvent(he); +} + + + + +void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e ) +{ + FileItem * file = dynamic_cast( itemAt( e->pos() ) ); + if( file != nullptr && file->isTrack() ) { - // Set the member to the current FileItem so that it is available during the - // execution of the slots of the context menu we are about to create and execute. - m_contextMenuItem = f; - - QMenu contextMenu(this); - - contextMenu.addAction(tr("Send to active instrument-track"), - this, - SLOT(sendToActiveInstrumentTrack())); - contextMenu.addAction(tr("Open in new instrument-track/Song Editor"), - this, - SLOT(openInNewInstrumentTrackSE())); - contextMenu.addAction(tr("Open in new instrument-track/B+B Editor"), - this, - SLOT(openInNewInstrumentTrackBBE())); + QMenu contextMenu( this ); + + contextMenu.addAction( + tr( "Send to active instrument-track" ), + [=]{ sendToActiveInstrumentTrack(file); } + ); contextMenu.addSeparator(); - contextMenu.addAction(QIcon(embed::getIconPixmap("folder")), - tr("Open containing folder"), - this, - SLOT(openContainingFolder())); + contextMenu.addAction( + QIcon(embed::getIconPixmap("folder")), + tr("Open containing folder"), + [=]{ openContainingFolder(file); } + ); + + QAction* songEditorHeader = new QAction( tr("Song Editor"), nullptr ); + songEditorHeader->setDisabled(true); + contextMenu.addAction( songEditorHeader ); + contextMenu.addActions( getContextActions(file, true) ); - contextMenu.exec(e->globalPos()); + QAction* bbEditorHeader = new QAction( tr("BB Editor"), nullptr ); + bbEditorHeader->setDisabled(true); + contextMenu.addAction( bbEditorHeader ); + contextMenu.addActions( getContextActions(file, false) ); - // The context menu has been executed so we can reset this member back to nullptr. - m_contextMenuItem = nullptr; + // We should only show the menu if it contains items + if (!contextMenu.isEmpty()) { contextMenu.exec( e->globalPos() ); } } } -void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) +QList FileBrowserTreeWidget::getContextActions(FileItem* file, bool songEditor) { - QTreeWidget::mousePressEvent( me ); - if( me->button() != Qt::LeftButton ) + QList result = QList(); + const bool fileIsSample = file->type() == FileItem::SampleFile; + + QString instrumentAction = fileIsSample ? + tr("Send to new AudioFileProcessor instance") : + tr("Send to new instrument track"); + QString shortcutMod = songEditor ? "" : UI_CTRL_KEY + QString(" + "); + + QAction* toInstrument = new QAction( + instrumentAction + tr(" (%2Enter)").arg(shortcutMod), + nullptr + ); + connect(toInstrument, &QAction::triggered, + [=]{ openInNewInstrumentTrack(file, songEditor); }); + result.append(toInstrument); + + if (songEditor && fileIsSample) { - return; + QAction* toSampleTrack = new QAction( + tr("Send to new sample track (Shift + Enter)"), + nullptr + ); + connect(toSampleTrack, &QAction::triggered, + [=]{ openInNewSampleTrack(file); }); + result.append(toSampleTrack); } - QTreeWidgetItem * i = itemAt( me->pos() ); - if ( i ) + return result; +} + + + + +void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) +{ + // Forward the event + QTreeWidget::mousePressEvent(me); + // QTreeWidget handles right clicks for us, so we only care about left clicks + if(me->button() != Qt::LeftButton) { return; } + + QTreeWidgetItem * i = itemAt(me->pos()); + if (i) { // TODO: Restrict to visible selection // if ( _me->x() > header()->cellPos( header()->mapToActual( 0 ) ) @@ -428,68 +528,84 @@ void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) // } } - FileItem * f = dynamic_cast( i ); - if( f != NULL ) - { - m_pphMutex.lock(); - if( m_previewPlayHandle != NULL ) - { - Engine::mixer()->removePlayHandle( - m_previewPlayHandle ); - m_previewPlayHandle = NULL; - } + FileItem * f = dynamic_cast(i); + if(f != nullptr) { previewFileItem(f); } +} + + - // in special case of sample-files we do not care about - // handling() rather than directly creating a SamplePlayHandle - if( f->type() == FileItem::SampleFile ) + +void FileBrowserTreeWidget::previewFileItem(FileItem* file) +{ // TODO: We should do this work outside the event thread + // Lock the preview mutex + QMutexLocker previewLocker(&m_pphMutex); + // If something is already playing, stop it before we continue + stopPreview(); + + PlayHandle* newPPH = nullptr; + const QString fileName = file->fullName(); + const QString ext = file->extension(); + + // In special case of sample-files we do not care about + // handling() rather than directly creating a SamplePlayHandle + if (file->type() == FileItem::SampleFile) + { + TextFloat * tf = TextFloat::displayMessage( + tr("Loading sample"), + tr("Please wait, loading sample for preview..."), + embed::getIconPixmap("sample_file", 24, 24), 0); + // TODO: this can be removed once we do this outside the event thread + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + SamplePlayHandle* s = new SamplePlayHandle(fileName); + s->setDoneMayReturnTrue(false); + newPPH = s; + delete tf; + } + else if ( + (ext == "xiz" || ext == "sf2" || ext == "sf3" || + ext == "gig" || ext == "pat") + && !pluginFactory->pluginSupportingExtension(ext).isNull()) + { + const bool isPlugin = file->handling() == FileItem::LoadByPlugin; + newPPH = new PresetPreviewPlayHandle(fileName, isPlugin); + } + else if (file->type() != FileItem::VstPluginFile && file->isTrack()) + { + DataFile dataFile(fileName); + if (dataFile.validate(ext)) { - TextFloat * tf = TextFloat::displayMessage( - tr( "Loading sample" ), - tr( "Please wait, loading sample for " - "preview..." ), - embed::getIconPixmap( "sample_file", - 24, 24 ), 0 ); - qApp->processEvents( - QEventLoop::ExcludeUserInputEvents ); - SamplePlayHandle * s = new SamplePlayHandle( - f->fullName() ); - s->setDoneMayReturnTrue( false ); - m_previewPlayHandle = s; - delete tf; + const bool isPlugin = file->handling() == FileItem::LoadByPlugin; + newPPH = new PresetPreviewPlayHandle(fileName, isPlugin, &dataFile); } - else if ( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" -#ifdef LMMS_HAVE_LV2 - || f->extension() == "lv2" -#endif - ) && - ! pluginFactory->pluginSupportingExtension(f->extension()).info.isNull() ) + else { - m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin ); + QMessageBox::warning(0, tr ("Error"), + tr("%1 does not appear to be a valid %2 file") + .arg(fileName, ext), + QMessageBox::Ok, QMessageBox::NoButton); } - else if( f->type() != FileItem::VstPluginFile && - ( f->handling() == FileItem::LoadAsPreset || - f->handling() == FileItem::LoadByPlugin ) ) + } + + if (newPPH != nullptr) + { + if (Engine::mixer()->addPlayHandle(newPPH)) { - DataFile dataFile( f->fullName() ); - if( !dataFile.validate( f->extension() ) ) - { - QMessageBox::warning( 0, tr ( "Error" ), - tr( "%1 does not appear to be a valid %2 file" ).arg( f->fullName(), f->extension() ), - QMessageBox::Ok, QMessageBox::NoButton ); - m_pphMutex.unlock(); - return; - } - m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin, &dataFile ); + m_previewPlayHandle = newPPH; } - if( m_previewPlayHandle != NULL ) - { - if( !Engine::mixer()->addPlayHandle( - m_previewPlayHandle ) ) - { - m_previewPlayHandle = NULL; - } - } - m_pphMutex.unlock(); + else { m_previewPlayHandle = nullptr; } + } +} + + + + +void FileBrowserTreeWidget::stopPreview() +{ + QMutexLocker previewLocker(&m_pphMutex); + if (m_previewPlayHandle != nullptr) + { + Engine::mixer()->removePlayHandle(m_previewPlayHandle); + m_previewPlayHandle = nullptr; } } @@ -503,10 +619,10 @@ void FileBrowserTreeWidget::mouseMoveEvent( QMouseEvent * me ) QApplication::startDragDistance() ) { // make sure any playback is stopped - mouseReleaseEvent( NULL ); + mouseReleaseEvent( nullptr ); FileItem * f = dynamic_cast( itemAt( m_pressPos ) ); - if( f != NULL ) + if( f != nullptr ) { switch( f->type() ) { @@ -556,36 +672,30 @@ void FileBrowserTreeWidget::mouseReleaseEvent(QMouseEvent * me ) { m_mousePressed = false; - m_pphMutex.lock(); - if( m_previewPlayHandle != NULL ) + QMutexLocker previewLocker(&m_pphMutex); + + if (m_previewPlayHandle != nullptr) { - // if there're samples shorter than 3 seconds, we don't + // If less than 3 seconds remain of the sample, we don't // stop them if the user releases mouse-button... - if( m_previewPlayHandle->type() == PlayHandle::TypeSamplePlayHandle ) + if (m_previewPlayHandle->type() == PlayHandle::TypeSamplePlayHandle) { - SamplePlayHandle * s = dynamic_cast( - m_previewPlayHandle ); - if( s && s->totalFrames() - s->framesDone() <= - static_cast( Engine::mixer()-> - processingSampleRate() * 3 ) ) + SamplePlayHandle* s = dynamic_cast(m_previewPlayHandle); + auto second = static_cast(Engine::mixer()->processingSampleRate()); + if (s && s->totalFrames() - s->framesDone() <= second * 3) { - s->setDoneMayReturnTrue( true ); - m_previewPlayHandle = NULL; - m_pphMutex.unlock(); - return; + s->setDoneMayReturnTrue(true); } + else { stopPreview(); } } - Engine::mixer()->removePlayHandle( m_previewPlayHandle ); - m_previewPlayHandle = NULL; + else { stopPreview(); } } - m_pphMutex.unlock(); } - -void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it ) +void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it) { Engine::mixer()->requestChangeInModel(); switch( f->handling() ) @@ -601,7 +711,7 @@ void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it ) { const QString e = f->extension(); Instrument * i = it->instrument(); - if( i == NULL || + if( i == nullptr || !i->descriptor()->supportsFileType( e ) ) { PluginFactory::PluginInfoAndKey piakn = @@ -641,7 +751,7 @@ void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item, int column ) { FileItem * f = dynamic_cast( item ); - if( f == NULL ) + if( f == nullptr ) { return; } @@ -649,7 +759,7 @@ void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item, if( f->handling() == FileItem::LoadAsProject || f->handling() == FileItem::ImportAsProject ) { - handleFile( f, NULL ); + handleFile( f, nullptr ); } else if( f->handling() != FileItem::NotSupported ) { @@ -663,53 +773,66 @@ void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item, -void FileBrowserTreeWidget::openInNewInstrumentTrack( TrackContainer* tc ) +void FileBrowserTreeWidget::openInNewInstrumentTrack(TrackContainer* tc, FileItem* item) { - if( m_contextMenuItem->handling() == FileItem::LoadAsPreset || - m_contextMenuItem->handling() == FileItem::LoadByPlugin ) + if(item->isTrack()) { InstrumentTrack * it = dynamic_cast( - Track::create( Track::InstrumentTrack, tc ) ); - handleFile( m_contextMenuItem, it ); + Track::create(Track::InstrumentTrack, tc)); + handleFile(item, it); } } -void FileBrowserTreeWidget::openInNewInstrumentTrackBBE( void ) +void FileBrowserTreeWidget::openInNewInstrumentTrack(FileItem* item, bool songEditor) { - openInNewInstrumentTrack( Engine::getBBTrackContainer() ); + // Get the correct TrackContainer. Ternary doesn't compile here + TrackContainer* tc = Engine::getSong(); + if (!songEditor) { tc = Engine::getBBTrackContainer(); } + openInNewInstrumentTrack(tc, item); } -void FileBrowserTreeWidget::openInNewInstrumentTrackSE( void ) +bool FileBrowserTreeWidget::openInNewSampleTrack(FileItem* item) { - openInNewInstrumentTrack( Engine::getSong() ); + // Can't add non-samples to a sample track + if (item->type() != FileItem::SampleFile) { return false; } + + // Create a new sample track for this sample + SampleTrack* sampleTrack = static_cast( + Track::create(Track::SampleTrack, Engine::getSong())); + + // Add the sample clip to the track + Engine::mixer()->requestChangeInModel(); + SampleTCO* clip = static_cast(sampleTrack->createTCO(0)); + clip->setSampleFile(item->fullName()); + Engine::mixer()->doneChangeInModel(); + return true; } -void FileBrowserTreeWidget::openContainingFolder() + +void FileBrowserTreeWidget::openContainingFolder(FileItem* item) { - if (m_contextMenuItem) - { - // Delegate to QDesktopServices::openUrl with the directory of the selected file. Please note that - // this will only open the directory but not select the file as this is much more complicated due - // to different implementations that are needed for different platforms (Linux/Windows/MacOS). - - // Using QDesktopServices::openUrl seems to be the most simple cross platform way which uses - // functionality that's already available in Qt. - QFileInfo fileInfo(m_contextMenuItem->fullName()); - QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.dir().path())); - } + // Delegate to QDesktopServices::openUrl with the directory of the selected file. Please note that + // this will only open the directory but not select the file as this is much more complicated due + // to different implementations that are needed for different platforms (Linux/Windows/MacOS). + + // Using QDesktopServices::openUrl seems to be the most simple cross platform way which uses + // functionality that's already available in Qt. + QFileInfo fileInfo(item->fullName()); + QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.dir().path())); } -void FileBrowserTreeWidget::sendToActiveInstrumentTrack( void ) + +void FileBrowserTreeWidget::sendToActiveInstrumentTrack( FileItem* item ) { // get all windows opened in the workspace QList pl = @@ -724,9 +847,9 @@ void FileBrowserTreeWidget::sendToActiveInstrumentTrack( void ) InstrumentTrackWindow * itw = dynamic_cast( w.previous()->widget() ); - if( itw != NULL && itw->isHidden() == false ) + if( itw != nullptr && itw->isHidden() == false ) { - handleFile( m_contextMenuItem, itw->model() ); + handleFile( item, itw->model() ); break; } } @@ -738,7 +861,7 @@ void FileBrowserTreeWidget::sendToActiveInstrumentTrack( void ) void FileBrowserTreeWidget::updateDirectory(QTreeWidgetItem * item ) { Directory * dir = dynamic_cast( item ); - if( dir != NULL ) + if( dir != nullptr ) { dir->update(); } @@ -749,9 +872,9 @@ void FileBrowserTreeWidget::updateDirectory(QTreeWidgetItem * item ) -QPixmap * Directory::s_folderPixmap = NULL; -QPixmap * Directory::s_folderOpenedPixmap = NULL; -QPixmap * Directory::s_folderLockedPixmap = NULL; +QPixmap * Directory::s_folderPixmap = nullptr; +QPixmap * Directory::s_folderOpenedPixmap = nullptr; +QPixmap * Directory::s_folderLockedPixmap = nullptr; Directory::Directory(const QString & filename, const QString & path, @@ -780,19 +903,19 @@ Directory::Directory(const QString & filename, const QString & path, void Directory::initPixmaps( void ) { - if( s_folderPixmap == NULL ) + if( s_folderPixmap == nullptr ) { s_folderPixmap = new QPixmap( embed::getIconPixmap( "folder" ) ); } - if( s_folderOpenedPixmap == NULL ) + if( s_folderOpenedPixmap == nullptr ) { s_folderOpenedPixmap = new QPixmap( embed::getIconPixmap( "folder_opened" ) ); } - if( s_folderLockedPixmap == NULL ) + if( s_folderLockedPixmap == nullptr ) { s_folderLockedPixmap = new QPixmap( embed::getIconPixmap( "folder_locked" ) ); @@ -870,7 +993,7 @@ bool Directory::addItems(const QString & path ) { Directory * d = dynamic_cast( child( i ) ); - if( d == NULL || cur_file < d->text( 0 ) ) + if( d == nullptr || cur_file < d->text( 0 ) ) { // insert before item, we're done insertChild( i, new Directory( cur_file, @@ -928,13 +1051,13 @@ bool Directory::addItems(const QString & path ) -QPixmap * FileItem::s_projectFilePixmap = NULL; -QPixmap * FileItem::s_presetFilePixmap = NULL; -QPixmap * FileItem::s_sampleFilePixmap = NULL; -QPixmap * FileItem::s_soundfontFilePixmap = NULL; -QPixmap * FileItem::s_vstPluginFilePixmap = NULL; -QPixmap * FileItem::s_midiFilePixmap = NULL; -QPixmap * FileItem::s_unknownFilePixmap = NULL; +QPixmap * FileItem::s_projectFilePixmap = nullptr; +QPixmap * FileItem::s_presetFilePixmap = nullptr; +QPixmap * FileItem::s_sampleFilePixmap = nullptr; +QPixmap * FileItem::s_soundfontFilePixmap = nullptr; +QPixmap * FileItem::s_vstPluginFilePixmap = nullptr; +QPixmap * FileItem::s_midiFilePixmap = nullptr; +QPixmap * FileItem::s_unknownFilePixmap = nullptr; FileItem::FileItem(QTreeWidget * parent, const QString & name, @@ -962,43 +1085,43 @@ FileItem::FileItem(const QString & name, const QString & path ) : void FileItem::initPixmaps( void ) { - if( s_projectFilePixmap == NULL ) + if( s_projectFilePixmap == nullptr ) { s_projectFilePixmap = new QPixmap( embed::getIconPixmap( "project_file", 16, 16 ) ); } - if( s_presetFilePixmap == NULL ) + if( s_presetFilePixmap == nullptr ) { s_presetFilePixmap = new QPixmap( embed::getIconPixmap( "preset_file", 16, 16 ) ); } - if( s_sampleFilePixmap == NULL ) + if( s_sampleFilePixmap == nullptr ) { s_sampleFilePixmap = new QPixmap( embed::getIconPixmap( "sample_file", 16, 16 ) ); } - if ( s_soundfontFilePixmap == NULL ) + if ( s_soundfontFilePixmap == nullptr ) { s_soundfontFilePixmap = new QPixmap( embed::getIconPixmap( "soundfont_file", 16, 16 ) ); } - if ( s_vstPluginFilePixmap == NULL ) + if ( s_vstPluginFilePixmap == nullptr ) { s_vstPluginFilePixmap = new QPixmap( embed::getIconPixmap( "vst_plugin_file", 16, 16 ) ); } - if( s_midiFilePixmap == NULL ) + if( s_midiFilePixmap == nullptr ) { s_midiFilePixmap = new QPixmap( embed::getIconPixmap( "midi_file", 16, 16 ) ); } - if( s_unknownFilePixmap == NULL ) + if( s_unknownFilePixmap == nullptr ) { s_unknownFilePixmap = new QPixmap( embed::getIconPixmap( "unknown_file" ) ); @@ -1111,7 +1234,3 @@ QString FileItem::extension(const QString & file ) { return QFileInfo( file ).suffix().toLower(); } - - - - From 4efe0c842e127909494112ba648113b4209f91b6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 2 Oct 2020 09:36:22 +0000 Subject: [PATCH 22/28] Haiku build fix. related to ringbuffer, matching cmake settings to disable mlock for this platform. Haiku does not support tls model as well. --- CMakeLists.txt | 4 ++-- src/3rdparty/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aaeec055f30..a3c83c1a4b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -487,9 +487,9 @@ If(WANT_GIG) ENDIF(WANT_GIG) # check for pthreads -IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD) +IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD OR LMMS_BUILD_HAIKU) FIND_PACKAGE(Threads) -ENDIF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD) +ENDIF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD OR LMMS_BUILD_HAIKU) # check for sndio (roaraudio won't work yet) IF(WANT_SNDIO) diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index 808298e79ff..24d15609599 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -16,7 +16,7 @@ FILE(WRITE ${CMAKE_BINARY_DIR}/src/ringbuffer_export.h # Enable MLOCK support for ringbuffer if available INCLUDE(CheckIncludeFiles) CHECK_INCLUDE_FILES(sys/mman.h HAVE_SYS_MMAN) -IF(HAVE_SYS_MMAN) +IF(HAVE_SYS_MMAN AND NOT CMAKE_SYSTEM_NAME MATCHES "Haiku") SET(USE_MLOCK ON) ELSE() SET(USE_MLOCK OFF) From 7e986a83230b5f52a4cd3a917c99d69ac96f007a Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 3 Oct 2020 12:07:38 +0200 Subject: [PATCH 23/28] Compile LMMS using C++14 This replaces `set(CMAKE_CXX_STANDARD 14)` by `set(CMAKE_CXX_STANDARD 11)` wherever it is required. Wherever it is superseded by parental `CMakeLists.txt`, this command is now removed. --- plugins/CMakeLists.txt | 4 ++-- plugins/LadspaEffect/CMakeLists.txt | 2 -- plugins/LadspaEffect/calf/CMakeLists.txt | 2 -- plugins/vst_base/RemoteVstPlugin/CMakeLists.txt | 2 +- src/CMakeLists.txt | 4 ++-- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index a4e56921fea..dde913d56ef 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -2,8 +2,8 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") SET(CMAKE_DEBUG_POSTFIX "") -# Enable C++11 -SET(CMAKE_CXX_STANDARD 11) +# Enable C++14 +SET(CMAKE_CXX_STANDARD 14) IF(LMMS_BUILD_APPLE) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") diff --git a/plugins/LadspaEffect/CMakeLists.txt b/plugins/LadspaEffect/CMakeLists.txt index 5bfbc3284c8..951615ad4d0 100644 --- a/plugins/LadspaEffect/CMakeLists.txt +++ b/plugins/LadspaEffect/CMakeLists.txt @@ -4,8 +4,6 @@ BUILD_PLUGIN(ladspaeffect LadspaEffect.cpp LadspaControls.cpp LadspaControlDialo SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ladspa") -SET(CMAKE_CXX_STANDARD 11) - IF(WANT_CAPS) ADD_SUBDIRECTORY(caps) ENDIF(WANT_CAPS) diff --git a/plugins/LadspaEffect/calf/CMakeLists.txt b/plugins/LadspaEffect/calf/CMakeLists.txt index 4924169e410..f5fb632f6ad 100644 --- a/plugins/LadspaEffect/calf/CMakeLists.txt +++ b/plugins/LadspaEffect/calf/CMakeLists.txt @@ -1,8 +1,6 @@ # Note: # The last version of Calf that was LADSPA-capable is version 0.0.18.2 -SET(CMAKE_CXX_STANDARD 11) - # Parse version info from autoconf FILE(READ veal/configure.ac VERSION_FILE) STRING(REPLACE "[" ";" VERSION_FILE ${VERSION_FILE} ) diff --git a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt index f4023fd426c..aa77459b68a 100644 --- a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt +++ b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt @@ -55,7 +55,7 @@ if(WIN32) endif() if(IS_MINGW) - SET(CMAKE_REQUIRED_FLAGS "-std=c++11") + SET(CMAKE_REQUIRED_FLAGS "-std=c++14") CHECK_CXX_SOURCE_COMPILES(" #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3ebec349e60..0a63a28d5fc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,8 +9,8 @@ SET(LMMS_UIS "") SET(CMAKE_AUTOMOC ON) SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -# Enable C++11 -SET(CMAKE_CXX_STANDARD 11) +# Enable C++14 +SET(CMAKE_CXX_STANDARD 14) IF(LMMS_BUILD_APPLE) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") From 16db33f2bf1f1a68bbefa455d09e81f9b77cd374 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 3 Oct 2020 12:13:06 +0200 Subject: [PATCH 24/28] Use STL version of `std::make_shared` now As all is compiled with C++14 now, no need to use `std::make_shared` from stdshims.h now. --- plugins/DualFilter/DualFilterControls.cpp | 91 +++++++++++------------ plugins/monstro/Monstro.h | 53 +++++++------ src/core/InstrumentFunctions.cpp | 17 ++--- src/core/InstrumentSoundShaping.cpp | 47 ++++++------ src/core/RenderManager.cpp | 3 +- src/core/lv2/Lv2ControlBase.cpp | 3 +- src/gui/editors/PianoRoll.cpp | 7 +- 7 files changed, 107 insertions(+), 114 deletions(-) diff --git a/plugins/DualFilter/DualFilterControls.cpp b/plugins/DualFilter/DualFilterControls.cpp index 0992d478cb1..35cbaab3a94 100644 --- a/plugins/DualFilter/DualFilterControls.cpp +++ b/plugins/DualFilter/DualFilterControls.cpp @@ -32,7 +32,6 @@ #include "embed.h" #include "Engine.h" #include "Song.h" -#include "stdshims.h" DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : EffectControls( effect ), @@ -52,51 +51,51 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_res2Model( 0.5, BasicFilters<>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 2" ) ), m_gain2Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 2" ) ) { - m_filter1Model.addItem( tr( "Low-pass" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "Hi-pass" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "Band-pass csg" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "Band-pass czpg" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "Notch" ), make_unique( "filter_notch" ) ); - m_filter1Model.addItem( tr( "All-pass" ), make_unique( "filter_ap" ) ); - m_filter1Model.addItem( tr( "Moog" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "2x Low-pass" ), make_unique( "filter_2lp" ) ); - m_filter1Model.addItem( tr( "RC Low-pass 12 dB/oct" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "RC Band-pass 12 dB/oct" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "RC High-pass 12 dB/oct" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "RC Low-pass 24 dB/oct" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "RC Band-pass 24 dB/oct" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "RC High-pass 24 dB/oct" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "Vocal Formant" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "2x Moog" ), make_unique( "filter_2lp" ) ); - m_filter1Model.addItem( tr( "SV Low-pass" ), make_unique( "filter_lp" ) ); - m_filter1Model.addItem( tr( "SV Band-pass" ), make_unique( "filter_bp" ) ); - m_filter1Model.addItem( tr( "SV High-pass" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "SV Notch" ), make_unique( "filter_notch" ) ); - m_filter1Model.addItem( tr( "Fast Formant" ), make_unique( "filter_hp" ) ); - m_filter1Model.addItem( tr( "Tripole" ), make_unique( "filter_lp" ) ); - - m_filter2Model.addItem( tr( "Low-pass" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "Hi-pass" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "Band-pass csg" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "Band-pass czpg" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "Notch" ), make_unique( "filter_notch" ) ); - m_filter2Model.addItem( tr( "All-pass" ), make_unique( "filter_ap" ) ); - m_filter2Model.addItem( tr( "Moog" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "2x Low-pass" ), make_unique( "filter_2lp" ) ); - m_filter2Model.addItem( tr( "RC Low-pass 12 dB/oct" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "RC Band-pass 12 dB/oct" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "RC High-pass 12 dB/oct" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "RC Low-pass 24 dB/oct" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "RC Band-pass 24 dB/oct" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "RC High-pass 24 dB/oct" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "Vocal Formant" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "2x Moog" ), make_unique( "filter_2lp" ) ); - m_filter2Model.addItem( tr( "SV Low-pass" ), make_unique( "filter_lp" ) ); - m_filter2Model.addItem( tr( "SV Band-pass" ), make_unique( "filter_bp" ) ); - m_filter2Model.addItem( tr( "SV High-pass" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "SV Notch" ), make_unique( "filter_notch" ) ); - m_filter2Model.addItem( tr( "Fast Formant" ), make_unique( "filter_hp" ) ); - m_filter2Model.addItem( tr( "Tripole" ), make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "Hi-pass" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "Band-pass csg" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "Band-pass czpg" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "Notch" ), std::make_unique( "filter_notch" ) ); + m_filter1Model.addItem( tr( "All-pass" ), std::make_unique( "filter_ap" ) ); + m_filter1Model.addItem( tr( "Moog" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "2x Low-pass" ), std::make_unique( "filter_2lp" ) ); + m_filter1Model.addItem( tr( "RC Low-pass 12 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "RC Band-pass 12 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "RC High-pass 12 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "RC Low-pass 24 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "RC Band-pass 24 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "RC High-pass 24 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "Vocal Formant" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "2x Moog" ), std::make_unique( "filter_2lp" ) ); + m_filter1Model.addItem( tr( "SV Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filter1Model.addItem( tr( "SV Band-pass" ), std::make_unique( "filter_bp" ) ); + m_filter1Model.addItem( tr( "SV High-pass" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "SV Notch" ), std::make_unique( "filter_notch" ) ); + m_filter1Model.addItem( tr( "Fast Formant" ), std::make_unique( "filter_hp" ) ); + m_filter1Model.addItem( tr( "Tripole" ), std::make_unique( "filter_lp" ) ); + + m_filter2Model.addItem( tr( "Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "Hi-pass" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "Band-pass csg" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "Band-pass czpg" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "Notch" ), std::make_unique( "filter_notch" ) ); + m_filter2Model.addItem( tr( "All-pass" ), std::make_unique( "filter_ap" ) ); + m_filter2Model.addItem( tr( "Moog" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "2x Low-pass" ), std::make_unique( "filter_2lp" ) ); + m_filter2Model.addItem( tr( "RC Low-pass 12 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "RC Band-pass 12 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "RC High-pass 12 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "RC Low-pass 24 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "RC Band-pass 24 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "RC High-pass 24 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "Vocal Formant" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "2x Moog" ), std::make_unique( "filter_2lp" ) ); + m_filter2Model.addItem( tr( "SV Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filter2Model.addItem( tr( "SV Band-pass" ), std::make_unique( "filter_bp" ) ); + m_filter2Model.addItem( tr( "SV High-pass" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "SV Notch" ), std::make_unique( "filter_notch" ) ); + m_filter2Model.addItem( tr( "Fast Formant" ), std::make_unique( "filter_hp" ) ); + m_filter2Model.addItem( tr( "Tripole" ), std::make_unique( "filter_lp" ) ); connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateFilters() ) ); } diff --git a/plugins/monstro/Monstro.h b/plugins/monstro/Monstro.h index fc0cc13b06d..0bb1a8dccbb 100644 --- a/plugins/monstro/Monstro.h +++ b/plugins/monstro/Monstro.h @@ -39,7 +39,6 @@ #include "Oscillator.h" #include "lmms_math.h" #include "BandLimitedWave.h" -#include "stdshims.h" // // UI Macros @@ -310,35 +309,35 @@ class MonstroInstrument : public Instrument Q_OBJECT #define setwavemodel( name ) \ - name .addItem( tr( "Sine wave" ), make_unique( "sin" ) ); \ - name .addItem( tr( "Bandlimited Triangle wave" ), make_unique( "tri" ) ); \ - name .addItem( tr( "Bandlimited Saw wave" ), make_unique( "saw" ) ); \ - name .addItem( tr( "Bandlimited Ramp wave" ), make_unique( "ramp" ) ); \ - name .addItem( tr( "Bandlimited Square wave" ), make_unique( "sqr" ) ); \ - name .addItem( tr( "Bandlimited Moog saw wave" ), make_unique( "moog" ) ); \ - name .addItem( tr( "Soft square wave" ), make_unique( "sqrsoft" ) ); \ - name .addItem( tr( "Absolute sine wave" ), make_unique( "sinabs" ) ); \ - name .addItem( tr( "Exponential wave" ), make_unique( "exp" ) ); \ - name .addItem( tr( "White noise" ), make_unique( "noise" ) ); \ - name .addItem( tr( "Digital Triangle wave" ), make_unique( "tri" ) ); \ - name .addItem( tr( "Digital Saw wave" ), make_unique( "saw" ) ); \ - name .addItem( tr( "Digital Ramp wave" ), make_unique( "ramp" ) ); \ - name .addItem( tr( "Digital Square wave" ), make_unique( "sqr" ) ); \ - name .addItem( tr( "Digital Moog saw wave" ), make_unique( "moog" ) ); + name .addItem( tr( "Sine wave" ), std::make_unique( "sin" ) ); \ + name .addItem( tr( "Bandlimited Triangle wave" ), std::make_unique( "tri" ) ); \ + name .addItem( tr( "Bandlimited Saw wave" ), std::make_unique( "saw" ) ); \ + name .addItem( tr( "Bandlimited Ramp wave" ), std::make_unique( "ramp" ) ); \ + name .addItem( tr( "Bandlimited Square wave" ), std::make_unique( "sqr" ) ); \ + name .addItem( tr( "Bandlimited Moog saw wave" ), std::make_unique( "moog" ) ); \ + name .addItem( tr( "Soft square wave" ), std::make_unique( "sqrsoft" ) ); \ + name .addItem( tr( "Absolute sine wave" ), std::make_unique( "sinabs" ) ); \ + name .addItem( tr( "Exponential wave" ), std::make_unique( "exp" ) ); \ + name .addItem( tr( "White noise" ), std::make_unique( "noise" ) ); \ + name .addItem( tr( "Digital Triangle wave" ), std::make_unique( "tri" ) ); \ + name .addItem( tr( "Digital Saw wave" ), std::make_unique( "saw" ) ); \ + name .addItem( tr( "Digital Ramp wave" ), std::make_unique( "ramp" ) ); \ + name .addItem( tr( "Digital Square wave" ), std::make_unique( "sqr" ) ); \ + name .addItem( tr( "Digital Moog saw wave" ), std::make_unique( "moog" ) ); #define setlfowavemodel( name ) \ - name .addItem( tr( "Sine wave" ), make_unique( "sin" ) ); \ - name .addItem( tr( "Triangle wave" ), make_unique( "tri" ) ); \ - name .addItem( tr( "Saw wave" ), make_unique( "saw" ) ); \ - name .addItem( tr( "Ramp wave" ), make_unique( "ramp" ) ); \ - name .addItem( tr( "Square wave" ), make_unique( "sqr" ) ); \ - name .addItem( tr( "Moog saw wave" ), make_unique( "moog" ) ); \ - name .addItem( tr( "Soft square wave" ), make_unique( "sqrsoft" ) ); \ - name .addItem( tr( "Abs. sine wave" ), make_unique( "sinabs" ) ); \ - name .addItem( tr( "Exponential wave" ), make_unique( "exp" ) ); \ - name .addItem( tr( "Random" ), make_unique( "rand" ) ); \ - name .addItem( tr( "Random smooth" ), make_unique( "rand" ) ); + name .addItem( tr( "Sine wave" ), std::make_unique( "sin" ) ); \ + name .addItem( tr( "Triangle wave" ), std::make_unique( "tri" ) ); \ + name .addItem( tr( "Saw wave" ), std::make_unique( "saw" ) ); \ + name .addItem( tr( "Ramp wave" ), std::make_unique( "ramp" ) ); \ + name .addItem( tr( "Square wave" ), std::make_unique( "sqr" ) ); \ + name .addItem( tr( "Moog saw wave" ), std::make_unique( "moog" ) ); \ + name .addItem( tr( "Soft square wave" ), std::make_unique( "sqrsoft" ) ); \ + name .addItem( tr( "Abs. sine wave" ), std::make_unique( "sinabs" ) ); \ + name .addItem( tr( "Exponential wave" ), std::make_unique( "exp" ) ); \ + name .addItem( tr( "Random" ), std::make_unique( "rand" ) ); \ + name .addItem( tr( "Random smooth" ), std::make_unique( "rand" ) ); public: MonstroInstrument( InstrumentTrack * _instrument_track ); diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 70e2c5e806e..c8ec24d6e45 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -30,7 +30,6 @@ #include "InstrumentTrack.h" #include "Mixer.h" #include "PresetPreviewPlayHandle.h" -#include "stdshims.h" InstrumentFunctionNoteStacking::ChordTable::Init InstrumentFunctionNoteStacking::ChordTable::s_initTable[] = @@ -316,16 +315,16 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) : m_arpModel.addItem( chord_table[i].getName() ); } - m_arpDirectionModel.addItem( tr( "Up" ), make_unique( "arp_up" ) ); - m_arpDirectionModel.addItem( tr( "Down" ), make_unique( "arp_down" ) ); - m_arpDirectionModel.addItem( tr( "Up and down" ), make_unique( "arp_up_and_down" ) ); - m_arpDirectionModel.addItem( tr( "Down and up" ), make_unique( "arp_up_and_down" ) ); - m_arpDirectionModel.addItem( tr( "Random" ), make_unique( "arp_random" ) ); + m_arpDirectionModel.addItem( tr( "Up" ), std::make_unique( "arp_up" ) ); + m_arpDirectionModel.addItem( tr( "Down" ), std::make_unique( "arp_down" ) ); + m_arpDirectionModel.addItem( tr( "Up and down" ), std::make_unique( "arp_up_and_down" ) ); + m_arpDirectionModel.addItem( tr( "Down and up" ), std::make_unique( "arp_up_and_down" ) ); + m_arpDirectionModel.addItem( tr( "Random" ), std::make_unique( "arp_random" ) ); m_arpDirectionModel.setInitValue( ArpDirUp ); - m_arpModeModel.addItem( tr( "Free" ), make_unique( "arp_free" ) ); - m_arpModeModel.addItem( tr( "Sort" ), make_unique( "arp_sort" ) ); - m_arpModeModel.addItem( tr( "Sync" ), make_unique( "arp_sync" ) ); + m_arpModeModel.addItem( tr( "Free" ), std::make_unique( "arp_free" ) ); + m_arpModeModel.addItem( tr( "Sort" ), std::make_unique( "arp_sort" ) ); + m_arpModeModel.addItem( tr( "Sync" ), std::make_unique( "arp_sync" ) ); } diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 2c221cdcc01..b9e32427a87 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -33,7 +33,6 @@ #include "Instrument.h" #include "InstrumentTrack.h" #include "Mixer.h" -#include "stdshims.h" const float CUT_FREQ_MULTIPLIER = 6000.0f; @@ -78,28 +77,28 @@ InstrumentSoundShaping::InstrumentSoundShaping( tr( targetNames[i][2] ) ); } - m_filterModel.addItem( tr( "Low-pass" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "Hi-pass" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "Band-pass csg" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "Band-pass czpg" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "Notch" ), make_unique( "filter_notch" ) ); - m_filterModel.addItem( tr( "All-pass" ), make_unique( "filter_ap" ) ); - m_filterModel.addItem( tr( "Moog" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "2x Low-pass" ), make_unique( "filter_2lp" ) ); - m_filterModel.addItem( tr( "RC Low-pass 12 dB/oct" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "RC Band-pass 12 dB/oct" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "RC High-pass 12 dB/oct" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "RC Low-pass 24 dB/oct" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "RC Band-pass 24 dB/oct" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "RC High-pass 24 dB/oct" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "Vocal Formant" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "2x Moog" ), make_unique( "filter_2lp" ) ); - m_filterModel.addItem( tr( "SV Low-pass" ), make_unique( "filter_lp" ) ); - m_filterModel.addItem( tr( "SV Band-pass" ), make_unique( "filter_bp" ) ); - m_filterModel.addItem( tr( "SV High-pass" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "SV Notch" ), make_unique( "filter_notch" ) ); - m_filterModel.addItem( tr( "Fast Formant" ), make_unique( "filter_hp" ) ); - m_filterModel.addItem( tr( "Tripole" ), make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "Hi-pass" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "Band-pass csg" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "Band-pass czpg" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "Notch" ), std::make_unique( "filter_notch" ) ); + m_filterModel.addItem( tr( "All-pass" ), std::make_unique( "filter_ap" ) ); + m_filterModel.addItem( tr( "Moog" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "2x Low-pass" ), std::make_unique( "filter_2lp" ) ); + m_filterModel.addItem( tr( "RC Low-pass 12 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "RC Band-pass 12 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "RC High-pass 12 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "RC Low-pass 24 dB/oct" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "RC Band-pass 24 dB/oct" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "RC High-pass 24 dB/oct" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "Vocal Formant" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "2x Moog" ), std::make_unique( "filter_2lp" ) ); + m_filterModel.addItem( tr( "SV Low-pass" ), std::make_unique( "filter_lp" ) ); + m_filterModel.addItem( tr( "SV Band-pass" ), std::make_unique( "filter_bp" ) ); + m_filterModel.addItem( tr( "SV High-pass" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "SV Notch" ), std::make_unique( "filter_notch" ) ); + m_filterModel.addItem( tr( "Fast Formant" ), std::make_unique( "filter_hp" ) ); + m_filterModel.addItem( tr( "Tripole" ), std::make_unique( "filter_lp" ) ); } @@ -161,7 +160,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, if( n->m_filter == nullptr ) { - n->m_filter = make_unique>( Engine::mixer()->processingSampleRate() ); + n->m_filter = std::make_unique>( Engine::mixer()->processingSampleRate() ); } n->m_filter->setFilterType( m_filterModel.value() ); diff --git a/src/core/RenderManager.cpp b/src/core/RenderManager.cpp index 69255442cd4..62b2788888c 100644 --- a/src/core/RenderManager.cpp +++ b/src/core/RenderManager.cpp @@ -29,7 +29,6 @@ #include "Song.h" #include "BBTrackContainer.h" #include "BBTrack.h" -#include "stdshims.h" RenderManager::RenderManager( @@ -140,7 +139,7 @@ void RenderManager::renderProject() void RenderManager::render(QString outputPath) { - m_activeRenderer = make_unique( + m_activeRenderer = std::make_unique( m_qualitySettings, m_outputSettings, m_format, diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 3f50325e7d2..cc986dafed0 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -31,7 +31,6 @@ #include "Engine.h" #include "Lv2Manager.h" #include "Lv2Proc.h" -#include "stdshims.h" @@ -54,7 +53,7 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) : int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo while (channelsLeft > 0) { - std::unique_ptr newOne = make_unique(m_plugin, that); + std::unique_ptr newOne = std::make_unique(m_plugin, that); if (newOne->isValid()) { channelsLeft -= std::max( diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 0eb049ddcae..3ce684ecef8 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -61,7 +61,6 @@ #include "MainWindow.h" #include "Pattern.h" #include "SongEditor.h" -#include "stdshims.h" #include "StepRecorderWidget.h" #include "TextFloat.h" #include "TimeLineWidget.h" @@ -352,7 +351,7 @@ PianoRoll::PianoRoll() : // Set up note length model m_noteLenModel.addItem( tr( "Last note" ), - make_unique( "edit_draw" ) ); + std::make_unique( "edit_draw" ) ); const QString pixmaps[] = { "whole", "half", "quarter", "eighth", "sixteenth", "thirtysecond", "triplethalf", "tripletquarter", "tripleteighth", @@ -360,12 +359,12 @@ PianoRoll::PianoRoll() : for( int i = 0; i < NUM_EVEN_LENGTHS; ++i ) { - auto loader = make_unique( "note_" + pixmaps[i] ); + auto loader = std::make_unique( "note_" + pixmaps[i] ); m_noteLenModel.addItem( "1/" + QString::number( 1 << i ), ::move(loader) ); } for( int i = 0; i < NUM_TRIPLET_LENGTHS; ++i ) { - auto loader = make_unique( "note_" + pixmaps[i+NUM_EVEN_LENGTHS] ); + auto loader = std::make_unique( "note_" + pixmaps[i+NUM_EVEN_LENGTHS] ); m_noteLenModel.addItem( "1/" + QString::number( (1 << i) * 3 ), ::move(loader) ); } m_noteLenModel.setValue( 0 ); From 783db3e457a112d2348facac96d151f3c4e1ed5d Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 3 Oct 2020 12:14:12 +0200 Subject: [PATCH 25/28] Remove unused `make_shared` from stdshims.h --- include/stdshims.h | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/include/stdshims.h b/include/stdshims.h index c12254e1506..dc89a1b0933 100644 --- a/include/stdshims.h +++ b/include/stdshims.h @@ -4,33 +4,9 @@ #ifndef STDSHIMS_H #define STDSHIMS_H -#include #include #include -#if (__cplusplus >= 201402L || _MSC_VER) -#ifndef _MSC_VER -#warning "This part of this file should now be removed! The functions it provides are part of the C++14 standard." -#endif -using std::make_unique; - -#else - -/// Shim for http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique -template -std::unique_ptr make_unique(Args&&... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} - -//! Overload for the case a deleter should be specified -template -std::unique_ptr make_unique(Args&&... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} -#endif - #if (__cplusplus >= 201703L || _MSC_VER >= 1914) #ifndef _MSC_VER #warning "This part of this file should now be removed! The functions it provides are part of the C++17 standard." From 8939b149e38daa17073834c0255085bdff2470fa Mon Sep 17 00:00:00 2001 From: allejok96 Date: Sat, 3 Oct 2020 23:01:52 +0200 Subject: [PATCH 26/28] Add insert/remove bar buttons in Song editor (fix #5602) --- data/themes/default/insert_bar.png | Bin 0 -> 545 bytes data/themes/default/remove_bar.png | Bin 0 -> 559 bytes include/SongEditor.h | 3 +++ src/gui/editors/SongEditor.cpp | 7 +++++++ 4 files changed, 10 insertions(+) create mode 100644 data/themes/default/insert_bar.png create mode 100644 data/themes/default/remove_bar.png diff --git a/data/themes/default/insert_bar.png b/data/themes/default/insert_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..c7b7ddc790764bbfff9a416bb31e0bc74e58521a GIT binary patch literal 545 zcmV++0^a?JP)EX>4Tx04R}tkv&MmKpe$iQ>9WW4rUN>$WWauh)QwPDionYs1;guFuC*#nzSS- zE{=k0!NHHks)LKOt`4q(Aou~|}?mh0_0YbCFbgO3q&<)#6 zClgXOwf;?j{slqVm!4L_2fw?u3R9C_QX~QNzBtauC=l8OS`EkfK6aee2@re+u8fYq+5~1kNpEzt z=n*im4P0DzG<6TS+yRE3YKp12Qjn%lC;;zg^i4Tn@D>QKdA+swaryvcsH@ZsaBv8W z6)Ah&=iPnXz5RQp-QN#&d~&Fq?Nwp`000JJOGiWi000000Qp0^e*gdgM@d9MR5;6H zV4yN!{Qv*|f2tYA$jHb*jDe)6VWPH|M-3b`kkmvyC|&p;3TPIFER?v6E_s=S(pZJL jjApS+b^!}>5zPSrZEGkI|9N*T00000NkvXXu0mjf^FQcB literal 0 HcmV?d00001 diff --git a/data/themes/default/remove_bar.png b/data/themes/default/remove_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..46845e5991cd0681691d368fb5d007eecf350f58 GIT binary patch literal 559 zcmV+~0?_@5P)EX>4Tx04R}tkv&MmKpe$iTSX}q2Q!Ft$WWauh>GZ@RVYG*P%E_RU~=gfG-*gu zTpR`0f`cE6RRhT)2yyBK+|nA zolJzBx-kgE(v zjs;YqL3aJ%fAG6ot1vO{B}EcI_lx6v3R-^Y&AJOM(_z?I(iR~x|eC+YRJ z7CQp^w}Ff6wx;X>mpj1VlOdb3D}`tV3kBf)jJ_!c4BP_YHLthUK29Hi40V;d0S*p< zks@WU`@Fliv$ucGwEFu2kl}K(fyyVU00006VoOIv00000008+zyMF)x010qNS#tmY z4c7nw4c7reD4Tcy002KpL_t(I%VS`mGGP4w|Nnog8OF%S$UuyNq^Mz{wwFf@95s;C xL_H+D%tDSPd|7dDxDXZrLo*#xzksDS002pJ7#&uIR?7eY002ovPDHLkV1iAR@p1qF literal 0 HcmV?d00001 diff --git a/include/SongEditor.h b/include/SongEditor.h index 7e0fe986a8c..a4394fc569c 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -210,6 +210,9 @@ protected slots: ComboBox * m_zoomingComboBox; ComboBox * m_snappingComboBox; QLabel* m_snapSizeLabel; + + QAction* m_insertBarAction; + QAction* m_removeBarAction; }; #endif diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 492de0e0092..8829bedfd22 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -955,6 +955,13 @@ SongEditorWindow::SongEditorWindow(Song* song) : DropToolBar *timeLineToolBar = addDropToolBarToTop(tr("Timeline controls")); m_editor->m_timeLine->addToolButtons(timeLineToolBar); + DropToolBar *insertActionsToolBar = addDropToolBarToTop(tr("Bar insert controls")); + m_insertBarAction = new QAction(embed::getIconPixmap("insert_bar"), tr("Insert bar"), this); + m_removeBarAction = new QAction(embed::getIconPixmap("remove_bar"), tr("Remove bar"), this); + insertActionsToolBar->addAction( m_insertBarAction ); + insertActionsToolBar->addAction( m_removeBarAction ); + connect(m_insertBarAction, SIGNAL(triggered()), song, SLOT(insertBar())); + connect(m_removeBarAction, SIGNAL(triggered()), song, SLOT(removeBar())); DropToolBar *zoomToolBar = addDropToolBarToTop(tr("Zoom controls")); From b558865ca40e0d8660e9ea833b5ac1eee3178e49 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 4 Oct 2020 21:45:00 +0200 Subject: [PATCH 27/28] PluginIssue: Add too MIDI in/out channels --- include/PluginIssue.h | 2 ++ src/core/PluginIssue.cpp | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/include/PluginIssue.h b/include/PluginIssue.h index c009458056e..00c90b756f1 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -36,6 +36,8 @@ enum PluginIssueType unknownPortType, tooManyInputChannels, tooManyOutputChannels, + tooManyMidiInputChannels, + tooManyMidiOutputChannels, noOutputChannel, portHasNoDef, portHasNoMin, diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index 4a8b2ee5b4e..8e1938e5685 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -38,6 +38,10 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) return "too many audio input channels"; case tooManyOutputChannels: return "too many audio output channels"; + case tooManyMidiInputChannels: + return "too many MIDI input channels"; + case tooManyMidiOutputChannels: + return "too many MIDI output channels"; case noOutputChannel: return "no audio output channel"; case portHasNoDef: From 3fa4b98a9ec73a9ced691e08f71d46fd18c99760 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 4 Oct 2020 21:47:47 +0200 Subject: [PATCH 28/28] Remove redundant LV2Ports::Audio::m_optional The same info is already stored in the `Lv2Ports::Meta` base class. --- include/Lv2Ports.h | 2 +- src/core/lv2/Lv2Ports.cpp | 4 ++-- src/core/lv2/Lv2Proc.cpp | 6 ++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index c9e6c16c855..567e1ddb585 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -162,7 +162,7 @@ struct Cv : public VisitablePort struct Audio : public VisitablePort { - Audio(std::size_t bufferSize, bool isSidechain, bool isOptional); + Audio(std::size_t bufferSize, bool isSidechain); //! Copy buffer passed by LMMS into our ports //! @param channel channel index into each sample frame diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 48fe47f604d..f2fac5744d0 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -201,8 +201,8 @@ QString PortBase::uri() const -Audio::Audio(std::size_t bufferSize, bool isSidechain, bool isOptional) - : m_buffer(bufferSize), m_sidechain(isSidechain), m_optional(isOptional) +Audio::Audio(std::size_t bufferSize, bool isSidechain) + : m_buffer(bufferSize), m_sidechain(isSidechain) { } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 9046001ab13..26593428b2c 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -56,8 +56,7 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, bool portMustBeUsed = !portIsSideChain(plugin, lilv_plugin_get_port_by_index(plugin, portNum)) && - !portIsOptional(plugin, - lilv_plugin_get_port_by_index(plugin, portNum)); + !meta.m_optional; if (meta.m_type == Lv2Ports::Type::Audio && portMustBeUsed) ++audioChannels[meta.m_flow == Lv2Ports::Flow::Output ? outCount : inCount]; @@ -381,8 +380,7 @@ void Lv2Proc::createPort(std::size_t portNum) new Lv2Ports::Audio( static_cast( Engine::mixer()->framesPerPeriod()), - portIsSideChain(m_plugin, lilvPort), - portIsOptional(m_plugin, lilvPort) + portIsSideChain(m_plugin, lilvPort) ); port = audio; break;