From 20c83e50213fcb831ec3629d0397c3ba9cb204e6 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Mon, 5 Jun 2023 20:09:30 +0200 Subject: [PATCH 01/45] Fix automated base notes and their automations (#6548) Fix all base notes that are used in automations and their corresponding automation values. Base notes that are automated are stored as elements in the save file whereas non-automated base notes are stored as attributes. So far the method `upgrade_extendedNoteRange` only upgraded the non-automated base notes that are stored in attributes. This commit fixes the automated ones which are stored in elements. The fix works as follows: * Collect all base note elements. * Store their ids in a set so that we can later identify automations that reference them. * Collect all automation pattern and check if they reference a base note. * Adjust the values and out values of all automations that reference base notes. Note: for many older files the out values will be introduced by the upgrade `method upgrade_automationNodes` and do not appear in the files themselves! --- src/core/DataFile.cpp | 73 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 2e7b21e8b..a39db430b 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -1723,6 +1724,71 @@ void DataFile::upgrade_extendedNoteRange() } } } + + // Fix the base notes that are used in automations and the automations that use them. + + // First fix the base notes used in automations and collect their ids while doing so. + // Base notes that are used in automations appear as elements in the document. + // The ids are used later to find the automations that automate these corrected base + // notes so that we can correct the automation values as well. + std::set baseNoteIds; + + QDomNodeList baseNotes = elementsByTagName("basenote"); + for (int j = 0; j < baseNotes.size(); ++j) + { + QDomElement baseNote = baseNotes.item(j).toElement(); + if (!baseNote.isNull()) + { + if (baseNote.hasAttribute("value")) + { + int const value = baseNote.attribute("value").toInt(); + baseNote.setAttribute("value", value + 12); + } + + // The ids of base notes are of type jo_id_t which are in fact uint32_t. + // So let's just use these here to save some casting. + unsigned int const id = baseNote.attribute("id").toUInt(); + baseNoteIds.insert(id); + } + } + + // Now collect all automation patterns and correct all their automations that + // use the corrected base notes. + QDomNodeList automationPatterns = elementsByTagName("automationpattern"); + for (int j = 0; j < automationPatterns.size(); ++j) + { + QDomElement automationPattern = automationPatterns.item(j).toElement(); + if (!automationPattern.isNull()) + { + // Iterate the objects. These contain the ids of the automated objects. + QDomElement object = automationPattern.firstChildElement("object"); + while(!object.isNull()) { + unsigned int const id = object.attribute("id").toUInt(); + if (baseNoteIds.find(id) != baseNoteIds.end()) + { + // The automation pattern belongs to a corrected base note. + // Collect all time elements to correct their values and out + // values. + QDomElement time = automationPattern.firstChildElement("time"); + while(!time.isNull()) { + // Value is in fact a float but if we save automations for + // base notes we in fact save integer values. + int const value = time.attribute("value").toInt(); + time.setAttribute("value", value + 12); + + // The method "upgrade_automationNodes" adds some attributes + // with the name "outValue". We have to correct these as well. + int const outValue = time.attribute("outValue").toInt(); + time.setAttribute("outValue", outValue + 12); + + time = time.nextSiblingElement("time"); + } + } + + object = object.nextSiblingElement("object"); + } + } + } } else { @@ -1837,8 +1903,11 @@ void DataFile::upgrade_bbTcoRename() void DataFile::upgrade() { // Runs all necessary upgrade methods - std::size_t max = std::min(static_cast(m_fileVersion), UPGRADE_METHODS.size()); - std::for_each( UPGRADE_METHODS.begin() + max, UPGRADE_METHODS.end(), + size_t const upgradedVersion = static_cast(m_fileVersion); + size_t const numberOfVersions = UPGRADE_METHODS.size(); + std::size_t offsetToUpgradeStart = std::min(upgradedVersion, numberOfVersions); + auto upgradeMethodIt = UPGRADE_VERSIONS.begin() + offsetToUpgradeStart; + std::for_each( UPGRADE_METHODS.begin() + offsetToUpgradeStart, UPGRADE_METHODS.end(), [this](UpgradeMethod um) { (this->*um)(); From 9f34c5cfa336a03d78a8783829357e15cd84107b Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Mon, 5 Jun 2023 20:20:34 +0200 Subject: [PATCH 02/45] Remove debug code from DataFile::upgrade Remove debug code from DataFile::upgrade which was accidentally committed. --- src/core/DataFile.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index a39db430b..68058fb7f 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1903,11 +1903,8 @@ void DataFile::upgrade_bbTcoRename() void DataFile::upgrade() { // Runs all necessary upgrade methods - size_t const upgradedVersion = static_cast(m_fileVersion); - size_t const numberOfVersions = UPGRADE_METHODS.size(); - std::size_t offsetToUpgradeStart = std::min(upgradedVersion, numberOfVersions); - auto upgradeMethodIt = UPGRADE_VERSIONS.begin() + offsetToUpgradeStart; - std::for_each( UPGRADE_METHODS.begin() + offsetToUpgradeStart, UPGRADE_METHODS.end(), + std::size_t max = std::min(static_cast(m_fileVersion), UPGRADE_METHODS.size()); + std::for_each( UPGRADE_METHODS.begin() + max, UPGRADE_METHODS.end(), [this](UpgradeMethod um) { (this->*um)(); From 02fef122ae5158658be142d962c09889ef1c3d1a Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Thu, 8 Jun 2023 10:48:02 +0200 Subject: [PATCH 03/45] Extend fix for mixed automation tracks/patterns (#6548) Move the fix for the automated base notes to the code that fixes the non-automated base notes. Improve the fix for automation tracks and patterns by potentially splitting them into two tracks: * The original track which is adjusted to only contain patterns with targets that are not base notes. * A cloned track that only contains patterns with base note targets. This is done by iterating over all automation tracks and checking which types of automations they contain: * Base note automations * Automations of other targets The result for each automation track are then evaluated as follows. * If an automation track does not contain any base note automations it is kept as it is, i.e. nothing is done. * If an automation track only contains patterns with base note automations its patterns are corrected in place. * If an automation track contains patterns with base note automations and other targets then the track is duplicated so that we can split it as described above. This split and correction is done on a per pattern level. Patterns that would become empty are removed. Cloned tracks will keep the same names and attributes as their original tracks. TODOs ------ * Base notes are read as integers, corrected by an integer value of 12 and then stored back as an integer. In some files base notes have float values, i.e. if the file was stored after the base notes have been automated linearly. * B&B tracks are not corrected although they can contain instruments with (automated) base notes! * Nested for-loops with "i" as their running variables. (Fix in a separate commit) --- src/core/DataFile.cpp | 337 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 276 insertions(+), 61 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 68058fb7f..e8d4c9941 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1670,6 +1670,76 @@ void DataFile::upgrade_automationNodes() } } +/** + * @brief Used by the helper function that analyzes automation patterns. + */ +struct PatternAnalysisResult +{ + PatternAnalysisResult(bool hasBaseNoteAutomations, bool hasNonBaseNoteAutomations) + { + this->hasBaseNoteAutomations = hasBaseNoteAutomations; + this->hasNonBaseNoteAutomations = hasNonBaseNoteAutomations; + } + bool hasBaseNoteAutomations; + bool hasNonBaseNoteAutomations; +}; + +/** + * @brief Helper function that checks for an automation pattern if it contains automation for + * targets that are base notes and/or other targets. + * @param automationPattern The automation pattern to be checked. + * @param automatedBaseNoteIds A set of id of automated base notes that are used in the check. + * @return A struct that contains the results. + */ +static PatternAnalysisResult analyzeAutomationPattern(QDomElement const & automationPattern, std::set const & automatedBaseNoteIds) +{ + bool hasBaseNoteAutomations = false; + bool hasNonBaseNoteAutomations = false; + + // Iterate the objects. These contain the ids of the automated objects. + QDomElement object = automationPattern.firstChildElement("object"); + while(!object.isNull()) + { + unsigned int const id = object.attribute("id").toUInt(); + + // Check if the automated object is a base note. + if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end()) + { + hasBaseNoteAutomations = true; + } + else + { + hasNonBaseNoteAutomations = true; + } + + object = object.nextSiblingElement("object"); + } + + return PatternAnalysisResult(hasBaseNoteAutomations, hasNonBaseNoteAutomations); +} + +/** + * @brief Helper method that fixes the values and out values for an automation pattern. + * @param automationPattern The automation pattern to be fixed. + */ +static void fixAutomationPattern(QDomElement & automationPattern) +{ + QDomElement time = automationPattern.firstChildElement("time"); + while(!time.isNull()) + { + // Automation patterns can automate base notes as floats + // so we read and correct them as floats here. + float const value = time.attribute("value").toFloat(); + time.setAttribute("value", value + 12.); + + // The method "upgrade_automationNodes" adds some attributes + // with the name "outValue". We have to correct these as well. + float const outValue = time.attribute("outValue").toFloat(); + time.setAttribute("outValue", outValue + 12.); + + time = time.nextSiblingElement("time"); + }; +} /** \brief Note range has been extended to match MIDI specification * @@ -1680,37 +1750,100 @@ void DataFile::upgrade_extendedNoteRange() { auto affected = [](const QDomElement& instrument) { - return instrument.attribute("name") == "zynaddsubfx" || - instrument.attribute("name") == "vestige" || - instrument.attribute("name") == "lv2instrument" || - instrument.attribute("name") == "carlapatchbay" || - instrument.attribute("name") == "carlarack"; + assert(instrument.hasAttribute("name")); + QString const name = instrument.attribute("name"); + + return name == "zynaddsubfx" || + name == "vestige" || name == "lv2instrument" || + name == "carlapatchbay" || name == "carlarack"; }; if (!elementsByTagName("song").item(0).isNull()) { + // This set will later contain all ids of automated base notes. They are + // used to find out which automation patterns must to be corrected, i.e. to + // check if an automation pattern has one or more base notes as its target. + std::set automatedBaseNoteIds; + // Dealing with a project file, go through all the tracks QDomNodeList tracks = elementsByTagName("track"); - for (int i = 0; !tracks.item(i).isNull(); i++) + for (int i = 0; i < tracks.size(); ++i) { - // Ignore BB container tracks - if (tracks.item(i).toElement().attribute("type").toInt() == 1) { continue; } + QDomElement currentTrack = tracks.item(i).toElement(); + if (!currentTrack.hasAttribute("type")) + { + continue; + } + Track::TrackTypes const trackType = static_cast(currentTrack.attribute("type").toInt()); + + // Ignore BB container tracks + if (trackType == Track::PatternTrack) + { + continue; + } + + QDomNodeList instruments = currentTrack.elementsByTagName("instrument"); + + if (instruments.isEmpty()) + { + continue; + } + + assert(instruments.size() < 2 && "More than one instrument found in a track!"); - QDomNodeList instruments = tracks.item(i).toElement().elementsByTagName("instrument"); - if (instruments.isEmpty()) { continue; } QDomElement instrument = instruments.item(0).toElement(); + + if (instrument.isNull()) + { + continue; + } + // Raise the base note of every instrument by 12 to compensate for the change // of A4 key code from 57 to 69. This ensures that notes are labeled correctly. - instrument.parentNode().toElement().setAttribute( - "basenote", - instrument.parentNode().toElement().attribute("basenote").toInt() + 12); + QDomElement instrumentParent = instrument.parentNode().toElement(); + + // Correct the base note of the instrument. Base notes which are automated are + // stored as elements. Non-automated base notes are stored as attributes. + if (instrumentParent.hasAttribute("basenote")) + { + // TODO Base notes can have float values in the save file! This might need to be changed! + int const currentBaseNote = instrumentParent.attribute("basenote").toInt(); + instrumentParent.setAttribute("basenote", currentBaseNote + 12); + } + else + { + // Check if the instrument track has an automated base note. + // Correct the value of the base note and collect their ids while doing so. + // The ids are used later to find the automations that automate these corrected base + // notes so that we can correct the automation values as well. + QDomNodeList baseNotes = instrumentParent.elementsByTagName("basenote"); + for (int j = 0; j < baseNotes.size(); ++j) + { + QDomElement baseNote = baseNotes.item(j).toElement(); + if (!baseNote.isNull()) + { + if (baseNote.hasAttribute("value")) + { + // TODO Base notes can have float values in the save file! This might need to be changed! + int const value = baseNote.attribute("value").toInt(); + baseNote.setAttribute("value", value + 12); + } + + // The ids of base notes are of type jo_id_t which are in fact uint32_t. + // So let's just use these here to save some casting. + unsigned int const id = baseNote.attribute("id").toUInt(); + automatedBaseNoteIds.insert(id); + } + } + } + // Raise the pitch of all notes in patterns assigned to instruments not affected // by #1857 by an octave. This negates the base note change for normal instruments, // but leaves the MIDI-based instruments sounding an octave lower, preserving their // pitch in existing projects. if (!affected(instrument)) { - QDomNodeList patterns = tracks.item(i).toElement().elementsByTagName("pattern"); + QDomNodeList patterns = currentTrack.elementsByTagName("pattern"); for (int i = 0; !patterns.item(i).isNull(); i++) { QDomNodeList notes = patterns.item(i).toElement().elementsByTagName("note"); @@ -1725,69 +1858,151 @@ void DataFile::upgrade_extendedNoteRange() } } - // Fix the base notes that are used in automations and the automations that use them. + // Now fix all the automation tracks. + // We have to collect the tracks that we need to duplicate and cannot do this in-place + // because if we did the iteration might never stop. + std::vector tracksToDuplicate; + tracksToDuplicate.reserve(tracks.size()); - // First fix the base notes used in automations and collect their ids while doing so. - // Base notes that are used in automations appear as elements in the document. - // The ids are used later to find the automations that automate these corrected base - // notes so that we can correct the automation values as well. - std::set baseNoteIds; - - QDomNodeList baseNotes = elementsByTagName("basenote"); - for (int j = 0; j < baseNotes.size(); ++j) + // Iterate the tracks again. This time work on all automation tracks. + for (int i = 0; i < tracks.size(); ++i) { - QDomElement baseNote = baseNotes.item(j).toElement(); - if (!baseNote.isNull()) + QDomElement currentTrack = tracks.item(i).toElement(); + if (currentTrack.attribute("type").toInt() != Track::AutomationTrack) { - if (baseNote.hasAttribute("value")) - { - int const value = baseNote.attribute("value").toInt(); - baseNote.setAttribute("value", value + 12); - } + continue; + } - // The ids of base notes are of type jo_id_t which are in fact uint32_t. - // So let's just use these here to save some casting. - unsigned int const id = baseNote.attribute("id").toUInt(); - baseNoteIds.insert(id); + // Check each track for the types of automations it contains in its patterns. + bool containsPatternsWithBaseNoteTargets = false; + bool containsPatternsWithNonBaseNoteTargets = false; + + QDomElement automationPattern = currentTrack.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) + { + auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); + containsPatternsWithBaseNoteTargets |= analysis.hasBaseNoteAutomations; + containsPatternsWithNonBaseNoteTargets |= analysis.hasNonBaseNoteAutomations; + + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + + if (!containsPatternsWithBaseNoteTargets) + { + // No base notes are automated by this automation track so we have nothing to do + continue; + } + else + { + if (!containsPatternsWithNonBaseNoteTargets) + { + // Only base note targets. This means we can simply keep the track and fix it. + automationPattern = currentTrack.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) + { + fixAutomationPattern(automationPattern); + + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + } + else + { + // The automation track has automations for base notes and other targets in its patterns. + // We will later need to duplicate/split the track. + tracksToDuplicate.push_back(currentTrack); + } } } - // Now collect all automation patterns and correct all their automations that - // use the corrected base notes. - QDomNodeList automationPatterns = elementsByTagName("automationpattern"); - for (int j = 0; j < automationPatterns.size(); ++j) + // Now fix the tracks that need duplication/splitting + for (QDomElement & track : tracksToDuplicate) { - QDomElement automationPattern = automationPatterns.item(j).toElement(); - if (!automationPattern.isNull()) + // First clone the original track + QDomNode cloneOfTrack = track.cloneNode(); + + // Now that we have the original and the clone we can manipulate both of them. + // The original track will keep only patterns without base note automations. + // Note: for the original track these might also be automation patterns without + // any targets. We will keep these because they might have been saved by the users + // like this. + QDomElement automationPattern = track.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) { - // Iterate the objects. These contain the ids of the automated objects. - QDomElement object = automationPattern.firstChildElement("object"); - while(!object.isNull()) { - unsigned int const id = object.attribute("id").toUInt(); - if (baseNoteIds.find(id) != baseNoteIds.end()) + auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); + if (!analysis.hasBaseNoteAutomations) + { + // This pattern has no base note automations. Leave it alone. + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + else if (!analysis.hasNonBaseNoteAutomations) + { + // The pattern only has base note automations. Remove it completely as it would become empty. + QDomElement patternToRemove = automationPattern; + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + track.removeChild(patternToRemove); + } + else + { + // The pattern itself is mixed. Remove the base note objects. + QDomElement object = automationPattern.firstChildElement("object"); + while(!object.isNull()) { - // The automation pattern belongs to a corrected base note. - // Collect all time elements to correct their values and out - // values. - QDomElement time = automationPattern.firstChildElement("time"); - while(!time.isNull()) { - // Value is in fact a float but if we save automations for - // base notes we in fact save integer values. - int const value = time.attribute("value").toInt(); - time.setAttribute("value", value + 12); + unsigned int const id = object.attribute("id").toUInt(); - // The method "upgrade_automationNodes" adds some attributes - // with the name "outValue". We have to correct these as well. - int const outValue = time.attribute("outValue").toInt(); - time.setAttribute("outValue", outValue + 12); - - time = time.nextSiblingElement("time"); + if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end()) + { + QDomElement objectToRemove = object; + object = object.nextSiblingElement("object"); + automationPattern.removeChild(objectToRemove); + } + else + { + object = object.nextSiblingElement("object"); } } - object = object.nextSiblingElement("object"); + automationPattern = automationPattern.nextSiblingElement("automationpattern"); } } + + // The clone will only keep non-empty patterns with base note automations and the values of the patterns will be corrected. + automationPattern = cloneOfTrack.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) + { + auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); + if (analysis.hasBaseNoteAutomations) + { + // This pattern has base note automations. Remove all other ones and fix the pattern. + QDomElement object = automationPattern.firstChildElement("object"); + while(!object.isNull()) + { + unsigned int const id = object.attribute("id").toUInt(); + + if (automatedBaseNoteIds.find(id) == automatedBaseNoteIds.end()) + { + QDomElement objectToRemove = object; + object = object.nextSiblingElement("object"); + automationPattern.removeChild(objectToRemove); + } + else + { + object = object.nextSiblingElement("object"); + } + } + + fixAutomationPattern(automationPattern); + + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + else + { + // The pattern has no base note automations. Remove it completely. + QDomElement patternToRemove = automationPattern; + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + cloneOfTrack.removeChild(patternToRemove); + } + } + track.parentNode().appendChild(cloneOfTrack); } } else From 502e95d5b4cf5dac0d02c49793154c0ec165e66b Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Thu, 8 Jun 2023 11:09:38 +0200 Subject: [PATCH 04/45] Try to fix GitHub builds The GitHub builds seem to need the explicit include which for some reason is not needed on my developer machine. --- src/core/DataFile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index e8d4c9941..b3884c9e0 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include From cf83b52783e172aaddc2befb01df97b463f2ef9d Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Thu, 8 Jun 2023 11:27:04 +0200 Subject: [PATCH 05/45] Fix nested for loops with identical variables (#6548) Fix the problem with the nested for loops that all had variables called "i" by extracting a helper method with a corrected nesting. Due the extraction we do not have to care anymore if the correction is running beneath another for loop with potentially the same variable names. --- src/core/DataFile.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index b3884c9e0..009a8fe81 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1719,6 +1719,23 @@ static PatternAnalysisResult analyzeAutomationPattern(QDomElement const & automa return PatternAnalysisResult(hasBaseNoteAutomations, hasNonBaseNoteAutomations); } +static void fixNotePatterns(QDomNodeList & patterns) +{ + for (int i = 0; i < patterns.size(); ++i) + { + QDomNodeList notes = patterns.item(i).toElement().elementsByTagName("note"); + for (int j = 0; j < notes.size(); ++j) + { + QDomElement note = notes.item(j).toElement(); + if (note.hasAttribute("key")) + { + int const currentKey = note.attribute("key").toInt(); + note.setAttribute("key", currentKey + 12); + } + } + } +} + /** * @brief Helper method that fixes the values and out values for an automation pattern. * @param automationPattern The automation pattern to be fixed. @@ -1845,17 +1862,7 @@ void DataFile::upgrade_extendedNoteRange() if (!affected(instrument)) { QDomNodeList patterns = currentTrack.elementsByTagName("pattern"); - for (int i = 0; !patterns.item(i).isNull(); i++) - { - QDomNodeList notes = patterns.item(i).toElement().elementsByTagName("note"); - for (int i = 0; !notes.item(i).isNull(); i++) - { - notes.item(i).toElement().setAttribute( - "key", - notes.item(i).toElement().attribute("key").toInt() + 12 - ); - } - } + fixNotePatterns(patterns); } } From 4f01094d9865ee5e4f929b903d38d1f2686804bf Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Thu, 8 Jun 2023 14:25:20 +0200 Subject: [PATCH 06/45] Fix base notes of B+B tracks (#6548) Fix the base notes and automations of B+B tracks. This fixes for example the problem that when a TripleOscillator had been put into a B+B track, e.g. with version 1.2 that it sounded too high when being loaded with a current master version. The cause seems to be that the notes of the instrument pattern are corrected/transposed too often (likely due to the collection of all tracks starting from the song/document root). The multiple correction of notes is fixed by traversing the song structure in a more consise way. First all track containers of the song are collected and for each of them the tracks are collected. These tracks are then fixed one by one. B+B tracks are getting a special handling while doing so. It is assumed that a B+B track cannot have other B+B tracks inside and therefore all sub tracks of the B+B track are searched for so that they can be fixed recursively. Refactor some more functionality into static helper methods: * Everything beneath a track is now fixed by the helper method `fixTrack`. * The fix of the base notes of the instruments themselves and the extraction of the ids of automated base notes has been moved into `fixInstrumentBaseNoteAndCollectIds`. * The lambda `affected` has been converted into a static method because it is must be accessible by one of the static helper methods. * The code for fixing the automation tracks of a song has been moved into the helper function `fixAutomationTracks`. --- src/core/DataFile.cpp | 473 ++++++++++++++++++++++-------------------- 1 file changed, 252 insertions(+), 221 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 009a8fe81..2d28a3090 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1736,6 +1736,51 @@ static void fixNotePatterns(QDomNodeList & patterns) } } +static void fixInstrumentBaseNoteAndCollectIds(QDomElement & instrument, std::set & automatedBaseNoteIds) +{ + // Raise the base note of every instrument by 12 to compensate for the change + // of A4 key code from 57 to 69. This ensures that notes are labeled correctly. + QDomElement instrumentParent = instrument.parentNode().toElement(); + + // Correct the base note of the instrument. Base notes which are automated are + // stored as elements. Non-automated base notes are stored as attributes. + if (instrumentParent.hasAttribute("basenote")) + { + // TODO Base notes can have float values in the save file! This might need to be changed! + int const currentBaseNote = instrumentParent.attribute("basenote").toInt(); + instrumentParent.setAttribute("basenote", currentBaseNote + 12); + } + else + { + // Check if the instrument track has an automated base note. + // Correct the value of the base note and collect their ids while doing so. + // The ids are used later to find the automations that automate these corrected base + // notes so that we can correct the automation values as well. + QDomNodeList baseNotes = instrumentParent.elementsByTagName("basenote"); + for (int j = 0; j < baseNotes.size(); ++j) + { + QDomElement baseNote = baseNotes.item(j).toElement(); + if (!baseNote.isNull()) + { + if (baseNote.hasAttribute("value")) + { + // Base notes can have float values in the save file, e.g. if the file + // is saved after a linear automation has run on the base note. Therefore + // it is fixed here as a float even if the nominal values of base notes + // are integers. + float const value = baseNote.attribute("value").toFloat(); + baseNote.setAttribute("value", value + 12); + } + + // The ids of base notes are of type jo_id_t which are in fact uint32_t. + // So let's just use these here to save some casting. + unsigned int const id = baseNote.attribute("id").toUInt(); + automatedBaseNoteIds.insert(id); + } + } + } +} + /** * @brief Helper method that fixes the values and out values for an automation pattern. * @param automationPattern The automation pattern to be fixed. @@ -1759,101 +1804,45 @@ static void fixAutomationPattern(QDomElement & automationPattern) }; } -/** \brief Note range has been extended to match MIDI specification - * - * The non-standard note range previously affected all MIDI-based instruments - * except OpulenZ, and made them sound an octave lower than they should (#1857). - */ -void DataFile::upgrade_extendedNoteRange() +static bool affected(QDomElement & instrument) { - auto affected = [](const QDomElement& instrument) + assert(instrument.hasAttribute("name")); + QString const name = instrument.attribute("name"); + + return name == "zynaddsubfx" || + name == "vestige" || name == "lv2instrument" || + name == "carlapatchbay" || name == "carlarack"; +} + +static void fixTrack(QDomElement & track, std::set & automatedBaseNoteIds) +{ + if (!track.hasAttribute("type")) { - assert(instrument.hasAttribute("name")); - QString const name = instrument.attribute("name"); + return; + } - return name == "zynaddsubfx" || - name == "vestige" || name == "lv2instrument" || - name == "carlapatchbay" || name == "carlarack"; - }; + Track::TrackTypes const trackType = static_cast(track.attribute("type").toInt()); - if (!elementsByTagName("song").item(0).isNull()) + // BB tracks need special handling because they container a track container of their own + if (trackType == Track::PatternTrack) { - // This set will later contain all ids of automated base notes. They are - // used to find out which automation patterns must to be corrected, i.e. to - // check if an automation pattern has one or more base notes as its target. - std::set automatedBaseNoteIds; - - // Dealing with a project file, go through all the tracks - QDomNodeList tracks = elementsByTagName("track"); - for (int i = 0; i < tracks.size(); ++i) + // Assuming that a BB track cannot contain another BB track here... + QDomNodeList subTracks = track.elementsByTagName("track"); + for (int i = 0; i < subTracks.size(); ++i) { - QDomElement currentTrack = tracks.item(i).toElement(); - if (!currentTrack.hasAttribute("type")) - { - continue; - } - Track::TrackTypes const trackType = static_cast(currentTrack.attribute("type").toInt()); + QDomElement subTrack = subTracks.item(i).toElement(); + fixTrack(subTrack, automatedBaseNoteIds); + } + } + else + { + QDomNodeList instruments = track.elementsByTagName("instrument"); - // Ignore BB container tracks - if (trackType == Track::PatternTrack) - { - continue; - } + for (int i = 0; i < instruments.size(); ++i) + { + QDomElement instrument = instruments.item(i).toElement(); - QDomNodeList instruments = currentTrack.elementsByTagName("instrument"); - - if (instruments.isEmpty()) - { - continue; - } - - assert(instruments.size() < 2 && "More than one instrument found in a track!"); - - QDomElement instrument = instruments.item(0).toElement(); - - if (instrument.isNull()) - { - continue; - } - - // Raise the base note of every instrument by 12 to compensate for the change - // of A4 key code from 57 to 69. This ensures that notes are labeled correctly. - QDomElement instrumentParent = instrument.parentNode().toElement(); - - // Correct the base note of the instrument. Base notes which are automated are - // stored as elements. Non-automated base notes are stored as attributes. - if (instrumentParent.hasAttribute("basenote")) - { - // TODO Base notes can have float values in the save file! This might need to be changed! - int const currentBaseNote = instrumentParent.attribute("basenote").toInt(); - instrumentParent.setAttribute("basenote", currentBaseNote + 12); - } - else - { - // Check if the instrument track has an automated base note. - // Correct the value of the base note and collect their ids while doing so. - // The ids are used later to find the automations that automate these corrected base - // notes so that we can correct the automation values as well. - QDomNodeList baseNotes = instrumentParent.elementsByTagName("basenote"); - for (int j = 0; j < baseNotes.size(); ++j) - { - QDomElement baseNote = baseNotes.item(j).toElement(); - if (!baseNote.isNull()) - { - if (baseNote.hasAttribute("value")) - { - // TODO Base notes can have float values in the save file! This might need to be changed! - int const value = baseNote.attribute("value").toInt(); - baseNote.setAttribute("value", value + 12); - } - - // The ids of base notes are of type jo_id_t which are in fact uint32_t. - // So let's just use these here to save some casting. - unsigned int const id = baseNote.attribute("id").toUInt(); - automatedBaseNoteIds.insert(id); - } - } - } + fixInstrumentBaseNoteAndCollectIds(instrument, automatedBaseNoteIds); // Raise the pitch of all notes in patterns assigned to instruments not affected // by #1857 by an octave. This negates the base note change for normal instruments, @@ -1861,159 +1850,201 @@ void DataFile::upgrade_extendedNoteRange() // pitch in existing projects. if (!affected(instrument)) { - QDomNodeList patterns = currentTrack.elementsByTagName("pattern"); + QDomNodeList patterns = track.elementsByTagName("pattern"); fixNotePatterns(patterns); } } + } +} - // Now fix all the automation tracks. - // We have to collect the tracks that we need to duplicate and cannot do this in-place - // because if we did the iteration might never stop. - std::vector tracksToDuplicate; - tracksToDuplicate.reserve(tracks.size()); +static void fixAutomationTracks(QDomElement & song, std::set const & automatedBaseNoteIds) +{ + // Now fix all the automation tracks. + QDomNodeList tracks = song.elementsByTagName("track"); - // Iterate the tracks again. This time work on all automation tracks. - for (int i = 0; i < tracks.size(); ++i) + // We have to collect the tracks that we need to duplicate and cannot do this in-place + // because if we did the iteration might never stop. + std::vector tracksToDuplicate; + tracksToDuplicate.reserve(tracks.size()); + + // Iterate the tracks again. This time work on all automation tracks. + for (int i = 0; i < tracks.size(); ++i) + { + QDomElement currentTrack = tracks.item(i).toElement(); + if (currentTrack.attribute("type").toInt() != Track::AutomationTrack) { - QDomElement currentTrack = tracks.item(i).toElement(); - if (currentTrack.attribute("type").toInt() != Track::AutomationTrack) - { - continue; - } - - // Check each track for the types of automations it contains in its patterns. - bool containsPatternsWithBaseNoteTargets = false; - bool containsPatternsWithNonBaseNoteTargets = false; - - QDomElement automationPattern = currentTrack.firstChildElement("automationpattern"); - while (!automationPattern.isNull()) - { - auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); - containsPatternsWithBaseNoteTargets |= analysis.hasBaseNoteAutomations; - containsPatternsWithNonBaseNoteTargets |= analysis.hasNonBaseNoteAutomations; - - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - } - - if (!containsPatternsWithBaseNoteTargets) - { - // No base notes are automated by this automation track so we have nothing to do - continue; - } - else - { - if (!containsPatternsWithNonBaseNoteTargets) - { - // Only base note targets. This means we can simply keep the track and fix it. - automationPattern = currentTrack.firstChildElement("automationpattern"); - while (!automationPattern.isNull()) - { - fixAutomationPattern(automationPattern); - - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - } - } - else - { - // The automation track has automations for base notes and other targets in its patterns. - // We will later need to duplicate/split the track. - tracksToDuplicate.push_back(currentTrack); - } - } + continue; } - // Now fix the tracks that need duplication/splitting - for (QDomElement & track : tracksToDuplicate) + // Check each track for the types of automations it contains in its patterns. + bool containsPatternsWithBaseNoteTargets = false; + bool containsPatternsWithNonBaseNoteTargets = false; + + QDomElement automationPattern = currentTrack.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) { - // First clone the original track - QDomNode cloneOfTrack = track.cloneNode(); + auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); + containsPatternsWithBaseNoteTargets |= analysis.hasBaseNoteAutomations; + containsPatternsWithNonBaseNoteTargets |= analysis.hasNonBaseNoteAutomations; - // Now that we have the original and the clone we can manipulate both of them. - // The original track will keep only patterns without base note automations. - // Note: for the original track these might also be automation patterns without - // any targets. We will keep these because they might have been saved by the users - // like this. - QDomElement automationPattern = track.firstChildElement("automationpattern"); - while (!automationPattern.isNull()) + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + + if (!containsPatternsWithBaseNoteTargets) + { + // No base notes are automated by this automation track so we have nothing to do + continue; + } + else + { + if (!containsPatternsWithNonBaseNoteTargets) { - auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); - if (!analysis.hasBaseNoteAutomations) + // Only base note targets. This means we can simply keep the track and fix it. + automationPattern = currentTrack.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) { - // This pattern has no base note automations. Leave it alone. - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - } - else if (!analysis.hasNonBaseNoteAutomations) - { - // The pattern only has base note automations. Remove it completely as it would become empty. - QDomElement patternToRemove = automationPattern; - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - track.removeChild(patternToRemove); - } - else - { - // The pattern itself is mixed. Remove the base note objects. - QDomElement object = automationPattern.firstChildElement("object"); - while(!object.isNull()) - { - unsigned int const id = object.attribute("id").toUInt(); - - if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end()) - { - QDomElement objectToRemove = object; - object = object.nextSiblingElement("object"); - automationPattern.removeChild(objectToRemove); - } - else - { - object = object.nextSiblingElement("object"); - } - } - - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - } - } - - // The clone will only keep non-empty patterns with base note automations and the values of the patterns will be corrected. - automationPattern = cloneOfTrack.firstChildElement("automationpattern"); - while (!automationPattern.isNull()) - { - auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); - if (analysis.hasBaseNoteAutomations) - { - // This pattern has base note automations. Remove all other ones and fix the pattern. - QDomElement object = automationPattern.firstChildElement("object"); - while(!object.isNull()) - { - unsigned int const id = object.attribute("id").toUInt(); - - if (automatedBaseNoteIds.find(id) == automatedBaseNoteIds.end()) - { - QDomElement objectToRemove = object; - object = object.nextSiblingElement("object"); - automationPattern.removeChild(objectToRemove); - } - else - { - object = object.nextSiblingElement("object"); - } - } - fixAutomationPattern(automationPattern); automationPattern = automationPattern.nextSiblingElement("automationpattern"); } - else - { - // The pattern has no base note automations. Remove it completely. - QDomElement patternToRemove = automationPattern; - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - cloneOfTrack.removeChild(patternToRemove); - } } - track.parentNode().appendChild(cloneOfTrack); + else + { + // The automation track has automations for base notes and other targets in its patterns. + // We will later need to duplicate/split the track. + tracksToDuplicate.push_back(currentTrack); + } } } - else + + // Now fix the tracks that need duplication/splitting + for (QDomElement & track : tracksToDuplicate) + { + // First clone the original track + QDomNode cloneOfTrack = track.cloneNode(); + + // Now that we have the original and the clone we can manipulate both of them. + // The original track will keep only patterns without base note automations. + // Note: for the original track these might also be automation patterns without + // any targets. We will keep these because they might have been saved by the users + // like this. + QDomElement automationPattern = track.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) + { + auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); + if (!analysis.hasBaseNoteAutomations) + { + // This pattern has no base note automations. Leave it alone. + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + else if (!analysis.hasNonBaseNoteAutomations) + { + // The pattern only has base note automations. Remove it completely as it would become empty. + QDomElement patternToRemove = automationPattern; + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + track.removeChild(patternToRemove); + } + else + { + // The pattern itself is mixed. Remove the base note objects. + QDomElement object = automationPattern.firstChildElement("object"); + while(!object.isNull()) + { + unsigned int const id = object.attribute("id").toUInt(); + + if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end()) + { + QDomElement objectToRemove = object; + object = object.nextSiblingElement("object"); + automationPattern.removeChild(objectToRemove); + } + else + { + object = object.nextSiblingElement("object"); + } + } + + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + } + + // The clone will only keep non-empty patterns with base note automations + // and the values of the patterns will be corrected. + automationPattern = cloneOfTrack.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) + { + auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); + if (analysis.hasBaseNoteAutomations) + { + // This pattern has base note automations. Remove all other ones and fix the pattern. + QDomElement object = automationPattern.firstChildElement("object"); + while(!object.isNull()) + { + unsigned int const id = object.attribute("id").toUInt(); + + if (automatedBaseNoteIds.find(id) == automatedBaseNoteIds.end()) + { + QDomElement objectToRemove = object; + object = object.nextSiblingElement("object"); + automationPattern.removeChild(objectToRemove); + } + else + { + object = object.nextSiblingElement("object"); + } + } + + fixAutomationPattern(automationPattern); + + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + else + { + // The pattern has no base note automations. Remove it completely. + QDomElement patternToRemove = automationPattern; + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + cloneOfTrack.removeChild(patternToRemove); + } + } + track.parentNode().appendChild(cloneOfTrack); + } +} + +/** \brief Note range has been extended to match MIDI specification + * + * The non-standard note range previously affected all MIDI-based instruments + * except OpulenZ, and made them sound an octave lower than they should (#1857). + */ +void DataFile::upgrade_extendedNoteRange() +{ + QDomElement song = documentElement().firstChildElement("song"); + while (!song.isNull()) + { + // This set will later contain all ids of automated base notes. They are + // used to find out which automation patterns must to be corrected, i.e. to + // check if an automation pattern has one or more base notes as its target. + std::set automatedBaseNoteIds; + + QDomElement trackContainer = song.firstChildElement("trackcontainer"); + while (!trackContainer.isNull()) + { + QDomElement track = trackContainer.firstChildElement("track"); + while (!track.isNull()) + { + fixTrack(track, automatedBaseNoteIds); + + track = track.nextSiblingElement("track"); + } + + trackContainer = trackContainer.nextSiblingElement("trackcontainer"); + } + + fixAutomationTracks(song, automatedBaseNoteIds); + + song = song.nextSiblingElement("song"); + }; + + if (elementsByTagName("song").item(0).isNull()) { // Dealing with a preset, not a song QDomNodeList presets = elementsByTagName("instrumenttrack"); From c53f3d981b9710bb1601e9213f925d95bdd9bd72 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Tue, 11 Jul 2023 12:52:32 -0400 Subject: [PATCH 07/45] Bump cmt submodule (#6151) * Bump cmt submodule Closes #6006 --- plugins/LadspaEffect/cmt/cmt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/LadspaEffect/cmt/cmt b/plugins/LadspaEffect/cmt/cmt index f7c25ed4e..6e6e291fb 160000 --- a/plugins/LadspaEffect/cmt/cmt +++ b/plugins/LadspaEffect/cmt/cmt @@ -1 +1 @@ -Subproject commit f7c25ed4ef7f4d7efb1bcd4229d25595d4f1ce55 +Subproject commit 6e6e291fbad1138c808860ba3f140a963b52fa58 From a9d49d4ff70642c958380a0b2e4fc6180c1a1877 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Sat, 29 Jul 2023 12:22:32 +0800 Subject: [PATCH 08/45] CMakeLists: use flags for Apple correctly (#6784) --- plugins/CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index cad7c00b4..9a71be4b8 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -5,7 +5,7 @@ SET(CMAKE_DEBUG_POSTFIX "") # Enable C++17 SET(CMAKE_CXX_STANDARD 17) -IF(LMMS_BUILD_APPLE) +IF(LMMS_BUILD_APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") ENDIF() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bd543779f..23923f616 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,7 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) # Enable C++17 SET(CMAKE_CXX_STANDARD 17) -IF(LMMS_BUILD_APPLE) +IF(LMMS_BUILD_APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") ENDIF() From 086e86490f26ace682e01e4109043faaccbf08bf Mon Sep 17 00:00:00 2001 From: Madadog <53168336+Madadog@users.noreply.github.com> Date: Sun, 30 Jul 2023 21:27:05 +1000 Subject: [PATCH 09/45] Fix scale highlighting during vertical zooming (#6761) Vertically zooming the piano roll caused highlighted semitones to drift from the actual note positions. This 2-line fix ensures the marked semitones are aligned with the grid lines and notes at all allowed vertical zoom levels. --- src/gui/editors/PianoRoll.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index adc1997b9..3ec008ecb 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -3355,11 +3355,11 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) for(x = 0; x < m_markedSemiTones.size(); ++x) { const int key_num = m_markedSemiTones.at(x); - const int y = keyAreaBottom() + 5 - m_keyLineHeight * + const int y = keyAreaBottom() - 1 - m_keyLineHeight * (key_num - m_startKey + 1); if(y > keyAreaBottom()) { break; } p.fillRect(m_whiteKeyWidth + 1, - y - m_keyLineHeight / 2, + y, width() - 10, m_keyLineHeight + 1, m_markedSemitoneColor); From 62a8c68736008e83e1be99c32e9c0748c74ec582 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sun, 30 Jul 2023 23:42:53 -0400 Subject: [PATCH 10/45] Fix apple install script for zyn interface (#6782) * Fix apple install script for zyn interface * Add codesigning support to prevent crash --- cmake/apple/install_apple.sh.in | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/cmake/apple/install_apple.sh.in b/cmake/apple/install_apple.sh.in index df9300712..fc27d78b3 100644 --- a/cmake/apple/install_apple.sh.in +++ b/cmake/apple/install_apple.sh.in @@ -35,8 +35,6 @@ cd "$APP" find . -type f -print0 | xargs -0 chmod u+w lmmsbin="MacOS/@CMAKE_PROJECT_NAME@" -zynlib="lib/lmms/libzynaddsubfx.so" -zynfmk="Frameworks/libZynAddSubFxCore.dylib" zynbin="MacOS/RemoteZynAddSubFx" # Move lmms binary @@ -44,15 +42,6 @@ mv "$APP/Contents/bin/@CMAKE_PROJECT_NAME@" "$APP/Contents/$lmmsbin" # Fix zyn linking mv "$APP/Contents/lib/lmms/RemoteZynAddSubFx" "$APP/Contents/$zynbin" -mv "$APP/Contents/lib/lmms/libZynAddSubFxCore.dylib" "$APP/Contents/$zynfmk" - -install_name_tool -change @rpath/libZynAddSubFxCore.dylib \ - @loader_path/../$zynfmk \ - "$APP/Contents/$zynbin" - -install_name_tool -change @rpath/libZynAddSubFxCore.dylib \ - @loader_path/../../$zynfmk \ - "$APP/Contents/$zynlib" # Replace @rpath with @loader_path for Carla # See also plugins/CarlaBase/CMakeLists.txt @@ -67,7 +56,6 @@ install_name_tool -change @rpath/libcarlabase.dylib \ # Link lmms binary _executables="${_executables} -executable=$APP/Contents/$zynbin" -_executables="${_executables} -executable=$APP/Contents/$zynfmk" # Build a list of shared objects in target/lib/lmms for file in "$APP/Contents/lib/lmms/"*.so; do @@ -109,4 +97,8 @@ done # Cleanup rm -rf "$APP/Contents/bin" + +# Codesign +codesign --force --deep --sign - "$APP" + echo -e "\nFinished.\n\n" From 57e7fdc5334f9089269296176f9184563bfcdef1 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Mon, 31 Jul 2023 12:38:35 +0800 Subject: [PATCH 11/45] MidiApple.cpp: fix getName to allow build with GCC (#6791) Fixes: https://github.com/LMMS/lmms/issues/6785 --- src/core/midi/MidiApple.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/midi/MidiApple.cpp b/src/core/midi/MidiApple.cpp index 01836c50b..4105f18f1 100644 --- a/src/core/midi/MidiApple.cpp +++ b/src/core/midi/MidiApple.cpp @@ -403,7 +403,7 @@ void MidiApple::midiInClose( MIDIEndpointRef reference ) -char *getName( MIDIObjectRef &object ) +char *getName( const MIDIObjectRef &object ) { // Returns the name of a given MIDIObjectRef as char * CFStringRef name = nullptr; From 2ded48598f5db03461b09ee87b2514d24e6de1fe Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Thu, 3 Aug 2023 11:05:27 +0800 Subject: [PATCH 12/45] versioninfo.h: improve recognition of compiler and platforms (#6795) --- include/versioninfo.h | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/include/versioninfo.h b/include/versioninfo.h index f224b62c5..7495299c2 100644 --- a/include/versioninfo.h +++ b/include/versioninfo.h @@ -3,15 +3,17 @@ #include "lmms_basics.h" -#ifdef __GNUC__ +#if defined(__GNUC__) constexpr const char* LMMS_BUILDCONF_COMPILER_VERSION = "GCC " __VERSION__; +#elif defined(__clang__) +constexpr const char* LMMS_BUILDCONF_COMPILER_VERSION = "Clang " __clang_version__; #elif defined(_MSC_VER) constexpr const char* LMMS_BUILDCONF_COMPILER_VERSION = "MSVC " LMMS_STRINGIFY(_MSC_FULL_VER); #else constexpr const char* LMMS_BUILDCONF_COMPILER_VERSION = "unknown compiler"; #endif -#ifdef LMMS_HOST_X86 +#if defined(LMMS_HOST_X86) constexpr const char* LMMS_BUILDCONF_MACHINE = "i386"; #elif defined(LMMS_HOST_X86_64) constexpr const char* LMMS_BUILDCONF_MACHINE = "x86_64"; @@ -23,32 +25,28 @@ constexpr const char* LMMS_BUILDCONF_MACHINE = "arm64"; constexpr const char* LMMS_BUILDCONF_MACHINE = "riscv32"; #elif defined(LMMS_HOST_RISCV64) constexpr const char* LMMS_BUILDCONF_MACHINE = "riscv64"; +#elif defined(LMMS_HOST_PPC32) +constexpr const char* LMMS_BUILDCONF_MACHINE = "ppc"; +#elif defined(LMMS_HOST_PPC64) +constexpr const char* LMMS_BUILDCONF_MACHINE = "ppc64"; #else constexpr const char* LMMS_BUILDCONF_MACHINE = "unknown processor"; #endif -#ifdef LMMS_BUILD_LINUX +#if defined(LMMS_BUILD_LINUX) constexpr const char* LMMS_BUILDCONF_PLATFORM = "Linux"; -#endif - -#ifdef LMMS_BUILD_APPLE +#elif defined(LMMS_BUILD_APPLE) constexpr const char* LMMS_BUILDCONF_PLATFORM = "OS X"; -#endif - -#ifdef LMMS_BUILD_OPENBSD +#elif defined(LMMS_BUILD_OPENBSD) constexpr const char* LMMS_BUILDCONF_PLATFORM = "OpenBSD"; -#endif - -#ifdef LMMS_BUILD_FREEBSD +#elif defined(LMMS_BUILD_FREEBSD) constexpr const char* LMMS_BUILDCONF_PLATFORM = "FreeBSD"; -#endif - -#ifdef LMMS_BUILD_WIN32 +#elif defined(LMMS_BUILD_WIN32) constexpr const char* LMMS_BUILDCONF_PLATFORM = "win32"; -#endif - -#ifdef LMMS_BUILD_HAIKU +#elif defined(LMMS_BUILD_HAIKU) constexpr const char* LMMS_BUILDCONF_PLATFORM = "Haiku"; +#else +constexpr const char* LMMS_BUILDCONF_PLATFORM = "unknown platform"; #endif #endif // LMMS_VERSION_INFO_H From bc99728534c90fba25c5deedef1568dc5d9d6e62 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Wed, 2 Aug 2023 23:06:18 -0400 Subject: [PATCH 13/45] Consolidate and simplify CMAKE_POLICY entries (#6780) Simplify CMake Policies Per https://github.com/LMMS/lmms/pull/6758#discussion_r1271346366 --- CMakeLists.txt | 13 +------------ cmake/modules/DefineInstallVar.cmake | 2 +- cmake/modules/InstallDependencies.cmake | 4 ++-- plugins/CarlaBase/CMakeLists.txt | 8 -------- 4 files changed, 4 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c5de064a5..a68b788d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,18 +6,7 @@ SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules" ${CMAKE_MODULE_PATH}) SET(LMMS_BINARY_DIR ${CMAKE_BINARY_DIR}) SET(LMMS_SOURCE_DIR ${CMAKE_SOURCE_DIR}) -IF(COMMAND CMAKE_POLICY) - CMAKE_POLICY(SET CMP0005 NEW) - CMAKE_POLICY(SET CMP0003 NEW) - IF (CMAKE_MAJOR_VERSION GREATER 2) - CMAKE_POLICY(SET CMP0026 NEW) - CMAKE_POLICY(SET CMP0045 NEW) - CMAKE_POLICY(SET CMP0050 OLD) - ENDIF() - CMAKE_POLICY(SET CMP0020 NEW) - CMAKE_POLICY(SET CMP0057 NEW) -ENDIF(COMMAND CMAKE_POLICY) - +# CMAKE_POLICY Section # Import of windows.h breaks min()/max() ADD_DEFINITIONS(-DNOMINMAX) diff --git a/cmake/modules/DefineInstallVar.cmake b/cmake/modules/DefineInstallVar.cmake index b13cb1d52..0ca8fa429 100644 --- a/cmake/modules/DefineInstallVar.cmake +++ b/cmake/modules/DefineInstallVar.cmake @@ -24,7 +24,7 @@ function(DEFINE_INSTALL_VAR) endif() else() if(VAR_GENERATOR_EXPRESSION) - cmake_policy(SET CMP0087 NEW) + cmake_policy(SET CMP0087 NEW) # install(CODE) and install(SCRIPT) support generator expressions. endif() install(CODE "set(\"${VAR_NAME}\" \"${VAR_CONTENT}\")") endif() diff --git a/cmake/modules/InstallDependencies.cmake b/cmake/modules/InstallDependencies.cmake index 791041bb2..167a93f35 100644 --- a/cmake/modules/InstallDependencies.cmake +++ b/cmake/modules/InstallDependencies.cmake @@ -1,8 +1,8 @@ include(GetPrerequisites) include(CMakeParseArguments) -CMAKE_POLICY(SET CMP0011 NEW) -CMAKE_POLICY(SET CMP0057 NEW) +# Project's cmake_minimum_required doesn't always propagate +cmake_policy(SET CMP0057 NEW) # Support new if() IN_LIST operator. function(make_absolute var) get_filename_component(abs "${${var}}" ABSOLUTE BASE_DIR "${CMAKE_INSTALL_PREFIX}") diff --git a/plugins/CarlaBase/CMakeLists.txt b/plugins/CarlaBase/CMakeLists.txt index d4f7939c9..8f9e14dd1 100644 --- a/plugins/CarlaBase/CMakeLists.txt +++ b/plugins/CarlaBase/CMakeLists.txt @@ -1,11 +1,3 @@ -# For MacOS, use "OLD" RPATH install_name behavior -# This can be changed to "NEW" safely if install_apple.sh.in -# is updated to relink libcarlabase.dylib. MacOS 10.8 uses -# cmake 3.9.6, so this can be done at any time. -IF(NOT CMAKE_VERSION VERSION_LESS 3.9) - CMAKE_POLICY(SET CMP0068 OLD) -ENDIF() - # If Carla was not provided by the system, make a dummy library instead if(LMMS_HAVE_WEAKCARLA) SET(CARLA_INCLUDE_DIRS From 97c6ae1aa0eb0c7cd6c4b735f1cd667aa8a60510 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sat, 5 Aug 2023 22:20:23 +0200 Subject: [PATCH 14/45] Lv2: Improve control visualization (#6799) Previously, the Lv2 controls were checked for control types in this order: integer? enum? toggle? generic? This is bad because it goes from generic (integer) to specific (toggle), and then defaults to most generic. The order should be specific (toggle) to generic (integer, generic), which is fixed by this PR. This solves that controls which are marked "toggle" and "integer" were being displayed as a spinbox (#6798). Additional improvement: Only show search bar for more than 5 controls, previously more than 2 (#6798). --- src/core/lv2/Lv2Ports.cpp | 8 ++++---- src/gui/ControlLayout.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 1ce1d6956..2faee067d 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -114,12 +114,12 @@ std::vector Meta::get(const LilvPlugin *plugin, m_optional = hasProperty(LV2_CORE__connectionOptional); - m_vis = hasProperty(LV2_CORE__integer) - ? Vis::Integer // WARNING: this may still be changed below + m_vis = hasProperty(LV2_CORE__toggled) + ? Vis::Toggled : hasProperty(LV2_CORE__enumeration) ? Vis::Enumeration - : hasProperty(LV2_CORE__toggled) - ? Vis::Toggled + : hasProperty(LV2_CORE__integer) + ? Vis::Integer // WARNING: this may still be changed below : Vis::Generic; if (isA(LV2_CORE__InputPort)) { m_flow = Flow::Input; } diff --git a/src/gui/ControlLayout.cpp b/src/gui/ControlLayout.cpp index 7e0976c94..5e9a21101 100644 --- a/src/gui/ControlLayout.cpp +++ b/src/gui/ControlLayout.cpp @@ -258,8 +258,8 @@ int ControlLayout::doLayout(const QRect &rect, bool testOnly) const if (first) { // for the search bar, only show it if there are at least - // two control widgets (i.e. at least 3 widgets) - if (m_itemMap.size() > 2) { wid->show(); } + // five control widgets (i.e. at least 6 widgets) + if (m_itemMap.size() > 5) { wid->show(); } else { wid->hide(); } } else { wid->show(); } From 353221a02ca64e4809629cedfd56ddae4fe33229 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sat, 5 Aug 2023 19:29:46 -0400 Subject: [PATCH 15/45] Allow Apple install step to fail CI (#6796) Fail CI if Apple install step fails --- cmake/install/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/install/CMakeLists.txt b/cmake/install/CMakeLists.txt index 5ee731d16..3f6e3624e 100644 --- a/cmake/install/CMakeLists.txt +++ b/cmake/install/CMakeLists.txt @@ -48,5 +48,9 @@ endif() IF(LMMS_BUILD_APPLE) INSTALL(CODE "EXECUTE_PROCESS(COMMAND chmod u+x ${CMAKE_BINARY_DIR}/install_apple.sh)") - INSTALL(CODE "EXECUTE_PROCESS(COMMAND ${CMAKE_BINARY_DIR}/install_apple.sh)") + INSTALL(CODE "EXECUTE_PROCESS(COMMAND ${CMAKE_BINARY_DIR}/install_apple.sh RESULT_VARIABLE EXIT_CODE) + IF(NOT EXIT_CODE EQUAL 0) + MESSAGE(FATAL_ERROR \"Execution of install_apple.sh failed\") + ENDIF() + ") ENDIF() From dac1f773470123ee0d4f9708db165021f2e00a75 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Fri, 11 Aug 2023 17:55:26 +0200 Subject: [PATCH 16/45] Code review changes (#6548) Add a space after some while loops. Fix a typo in a comment. --- src/core/DataFile.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 2d28a3090..81bdc1fe2 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1699,7 +1699,7 @@ static PatternAnalysisResult analyzeAutomationPattern(QDomElement const & automa // Iterate the objects. These contain the ids of the automated objects. QDomElement object = automationPattern.firstChildElement("object"); - while(!object.isNull()) + while (!object.isNull()) { unsigned int const id = object.attribute("id").toUInt(); @@ -1788,7 +1788,7 @@ static void fixInstrumentBaseNoteAndCollectIds(QDomElement & instrument, std::se static void fixAutomationPattern(QDomElement & automationPattern) { QDomElement time = automationPattern.firstChildElement("time"); - while(!time.isNull()) + while (!time.isNull()) { // Automation patterns can automate base notes as floats // so we read and correct them as floats here. @@ -1823,7 +1823,7 @@ static void fixTrack(QDomElement & track, std::set & automatedBase Track::TrackTypes const trackType = static_cast(track.attribute("type").toInt()); - // BB tracks need special handling because they container a track container of their own + // BB tracks need special handling because they contain a track container of their own if (trackType == Track::PatternTrack) { // Assuming that a BB track cannot contain another BB track here... @@ -1948,7 +1948,7 @@ static void fixAutomationTracks(QDomElement & song, std::set const { // The pattern itself is mixed. Remove the base note objects. QDomElement object = automationPattern.firstChildElement("object"); - while(!object.isNull()) + while (!object.isNull()) { unsigned int const id = object.attribute("id").toUInt(); @@ -1978,7 +1978,7 @@ static void fixAutomationTracks(QDomElement & song, std::set const { // This pattern has base note automations. Remove all other ones and fix the pattern. QDomElement object = automationPattern.firstChildElement("object"); - while(!object.isNull()) + while (!object.isNull()) { unsigned int const id = object.attribute("id").toUInt(); From 5335f6d8c6274f02957d8d7b4a0f7a8f1ebc6055 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Fri, 11 Aug 2023 18:33:35 +0200 Subject: [PATCH 17/45] Separate class for extended note range upgrade (#6548) Extract the code that upgrades the extended note range into its own class. This hides the helper functions that are related to the upgrade from the other upgrade methods in DataFile.cpp. The header file is put into the src/core directory as it is not part of the public interface and therefore should not be included in the "global" include directory. The whole thing is just an implementation detail of the upgrade in DataFile.cpp. --- src/core/CMakeLists.txt | 2 + src/core/DataFile.cpp | 391 +---------------------- src/core/UpgradeExtendedNoteRange.cpp | 431 ++++++++++++++++++++++++++ src/core/UpgradeExtendedNoteRange.h | 47 +++ 4 files changed, 483 insertions(+), 388 deletions(-) create mode 100644 src/core/UpgradeExtendedNoteRange.cpp create mode 100644 src/core/UpgradeExtendedNoteRange.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b8809ed78..319882af2 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -77,6 +77,8 @@ set(LMMS_SRCS core/ToolPlugin.cpp core/Track.cpp core/TrackContainer.cpp + core/UpgradeExtendedNoteRange.h + core/UpgradeExtendedNoteRange.cpp core/Clip.cpp core/ValueBuffer.cpp core/VstSyncController.cpp diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 81bdc1fe2..2361b1926 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -28,8 +28,6 @@ #include #include -#include -#include #include #include @@ -49,6 +47,7 @@ #include "TextFloat.h" #include "Track.h" #include "PathUtil.h" +#include "UpgradeExtendedNoteRange.h" #include "lmmsversion.h" @@ -1671,344 +1670,6 @@ void DataFile::upgrade_automationNodes() } } -/** - * @brief Used by the helper function that analyzes automation patterns. - */ -struct PatternAnalysisResult -{ - PatternAnalysisResult(bool hasBaseNoteAutomations, bool hasNonBaseNoteAutomations) - { - this->hasBaseNoteAutomations = hasBaseNoteAutomations; - this->hasNonBaseNoteAutomations = hasNonBaseNoteAutomations; - } - bool hasBaseNoteAutomations; - bool hasNonBaseNoteAutomations; -}; - -/** - * @brief Helper function that checks for an automation pattern if it contains automation for - * targets that are base notes and/or other targets. - * @param automationPattern The automation pattern to be checked. - * @param automatedBaseNoteIds A set of id of automated base notes that are used in the check. - * @return A struct that contains the results. - */ -static PatternAnalysisResult analyzeAutomationPattern(QDomElement const & automationPattern, std::set const & automatedBaseNoteIds) -{ - bool hasBaseNoteAutomations = false; - bool hasNonBaseNoteAutomations = false; - - // Iterate the objects. These contain the ids of the automated objects. - QDomElement object = automationPattern.firstChildElement("object"); - while (!object.isNull()) - { - unsigned int const id = object.attribute("id").toUInt(); - - // Check if the automated object is a base note. - if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end()) - { - hasBaseNoteAutomations = true; - } - else - { - hasNonBaseNoteAutomations = true; - } - - object = object.nextSiblingElement("object"); - } - - return PatternAnalysisResult(hasBaseNoteAutomations, hasNonBaseNoteAutomations); -} - -static void fixNotePatterns(QDomNodeList & patterns) -{ - for (int i = 0; i < patterns.size(); ++i) - { - QDomNodeList notes = patterns.item(i).toElement().elementsByTagName("note"); - for (int j = 0; j < notes.size(); ++j) - { - QDomElement note = notes.item(j).toElement(); - if (note.hasAttribute("key")) - { - int const currentKey = note.attribute("key").toInt(); - note.setAttribute("key", currentKey + 12); - } - } - } -} - -static void fixInstrumentBaseNoteAndCollectIds(QDomElement & instrument, std::set & automatedBaseNoteIds) -{ - // Raise the base note of every instrument by 12 to compensate for the change - // of A4 key code from 57 to 69. This ensures that notes are labeled correctly. - QDomElement instrumentParent = instrument.parentNode().toElement(); - - // Correct the base note of the instrument. Base notes which are automated are - // stored as elements. Non-automated base notes are stored as attributes. - if (instrumentParent.hasAttribute("basenote")) - { - // TODO Base notes can have float values in the save file! This might need to be changed! - int const currentBaseNote = instrumentParent.attribute("basenote").toInt(); - instrumentParent.setAttribute("basenote", currentBaseNote + 12); - } - else - { - // Check if the instrument track has an automated base note. - // Correct the value of the base note and collect their ids while doing so. - // The ids are used later to find the automations that automate these corrected base - // notes so that we can correct the automation values as well. - QDomNodeList baseNotes = instrumentParent.elementsByTagName("basenote"); - for (int j = 0; j < baseNotes.size(); ++j) - { - QDomElement baseNote = baseNotes.item(j).toElement(); - if (!baseNote.isNull()) - { - if (baseNote.hasAttribute("value")) - { - // Base notes can have float values in the save file, e.g. if the file - // is saved after a linear automation has run on the base note. Therefore - // it is fixed here as a float even if the nominal values of base notes - // are integers. - float const value = baseNote.attribute("value").toFloat(); - baseNote.setAttribute("value", value + 12); - } - - // The ids of base notes are of type jo_id_t which are in fact uint32_t. - // So let's just use these here to save some casting. - unsigned int const id = baseNote.attribute("id").toUInt(); - automatedBaseNoteIds.insert(id); - } - } - } -} - -/** - * @brief Helper method that fixes the values and out values for an automation pattern. - * @param automationPattern The automation pattern to be fixed. - */ -static void fixAutomationPattern(QDomElement & automationPattern) -{ - QDomElement time = automationPattern.firstChildElement("time"); - while (!time.isNull()) - { - // Automation patterns can automate base notes as floats - // so we read and correct them as floats here. - float const value = time.attribute("value").toFloat(); - time.setAttribute("value", value + 12.); - - // The method "upgrade_automationNodes" adds some attributes - // with the name "outValue". We have to correct these as well. - float const outValue = time.attribute("outValue").toFloat(); - time.setAttribute("outValue", outValue + 12.); - - time = time.nextSiblingElement("time"); - }; -} - -static bool affected(QDomElement & instrument) -{ - assert(instrument.hasAttribute("name")); - QString const name = instrument.attribute("name"); - - return name == "zynaddsubfx" || - name == "vestige" || name == "lv2instrument" || - name == "carlapatchbay" || name == "carlarack"; -} - -static void fixTrack(QDomElement & track, std::set & automatedBaseNoteIds) -{ - if (!track.hasAttribute("type")) - { - return; - } - - Track::TrackTypes const trackType = static_cast(track.attribute("type").toInt()); - - // BB tracks need special handling because they contain a track container of their own - if (trackType == Track::PatternTrack) - { - // Assuming that a BB track cannot contain another BB track here... - QDomNodeList subTracks = track.elementsByTagName("track"); - for (int i = 0; i < subTracks.size(); ++i) - { - QDomElement subTrack = subTracks.item(i).toElement(); - fixTrack(subTrack, automatedBaseNoteIds); - } - } - else - { - QDomNodeList instruments = track.elementsByTagName("instrument"); - - for (int i = 0; i < instruments.size(); ++i) - { - QDomElement instrument = instruments.item(i).toElement(); - - fixInstrumentBaseNoteAndCollectIds(instrument, automatedBaseNoteIds); - - // Raise the pitch of all notes in patterns assigned to instruments not affected - // by #1857 by an octave. This negates the base note change for normal instruments, - // but leaves the MIDI-based instruments sounding an octave lower, preserving their - // pitch in existing projects. - if (!affected(instrument)) - { - QDomNodeList patterns = track.elementsByTagName("pattern"); - fixNotePatterns(patterns); - } - } - } -} - -static void fixAutomationTracks(QDomElement & song, std::set const & automatedBaseNoteIds) -{ - // Now fix all the automation tracks. - QDomNodeList tracks = song.elementsByTagName("track"); - - // We have to collect the tracks that we need to duplicate and cannot do this in-place - // because if we did the iteration might never stop. - std::vector tracksToDuplicate; - tracksToDuplicate.reserve(tracks.size()); - - // Iterate the tracks again. This time work on all automation tracks. - for (int i = 0; i < tracks.size(); ++i) - { - QDomElement currentTrack = tracks.item(i).toElement(); - if (currentTrack.attribute("type").toInt() != Track::AutomationTrack) - { - continue; - } - - // Check each track for the types of automations it contains in its patterns. - bool containsPatternsWithBaseNoteTargets = false; - bool containsPatternsWithNonBaseNoteTargets = false; - - QDomElement automationPattern = currentTrack.firstChildElement("automationpattern"); - while (!automationPattern.isNull()) - { - auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); - containsPatternsWithBaseNoteTargets |= analysis.hasBaseNoteAutomations; - containsPatternsWithNonBaseNoteTargets |= analysis.hasNonBaseNoteAutomations; - - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - } - - if (!containsPatternsWithBaseNoteTargets) - { - // No base notes are automated by this automation track so we have nothing to do - continue; - } - else - { - if (!containsPatternsWithNonBaseNoteTargets) - { - // Only base note targets. This means we can simply keep the track and fix it. - automationPattern = currentTrack.firstChildElement("automationpattern"); - while (!automationPattern.isNull()) - { - fixAutomationPattern(automationPattern); - - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - } - } - else - { - // The automation track has automations for base notes and other targets in its patterns. - // We will later need to duplicate/split the track. - tracksToDuplicate.push_back(currentTrack); - } - } - } - - // Now fix the tracks that need duplication/splitting - for (QDomElement & track : tracksToDuplicate) - { - // First clone the original track - QDomNode cloneOfTrack = track.cloneNode(); - - // Now that we have the original and the clone we can manipulate both of them. - // The original track will keep only patterns without base note automations. - // Note: for the original track these might also be automation patterns without - // any targets. We will keep these because they might have been saved by the users - // like this. - QDomElement automationPattern = track.firstChildElement("automationpattern"); - while (!automationPattern.isNull()) - { - auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); - if (!analysis.hasBaseNoteAutomations) - { - // This pattern has no base note automations. Leave it alone. - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - } - else if (!analysis.hasNonBaseNoteAutomations) - { - // The pattern only has base note automations. Remove it completely as it would become empty. - QDomElement patternToRemove = automationPattern; - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - track.removeChild(patternToRemove); - } - else - { - // The pattern itself is mixed. Remove the base note objects. - QDomElement object = automationPattern.firstChildElement("object"); - while (!object.isNull()) - { - unsigned int const id = object.attribute("id").toUInt(); - - if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end()) - { - QDomElement objectToRemove = object; - object = object.nextSiblingElement("object"); - automationPattern.removeChild(objectToRemove); - } - else - { - object = object.nextSiblingElement("object"); - } - } - - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - } - } - - // The clone will only keep non-empty patterns with base note automations - // and the values of the patterns will be corrected. - automationPattern = cloneOfTrack.firstChildElement("automationpattern"); - while (!automationPattern.isNull()) - { - auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); - if (analysis.hasBaseNoteAutomations) - { - // This pattern has base note automations. Remove all other ones and fix the pattern. - QDomElement object = automationPattern.firstChildElement("object"); - while (!object.isNull()) - { - unsigned int const id = object.attribute("id").toUInt(); - - if (automatedBaseNoteIds.find(id) == automatedBaseNoteIds.end()) - { - QDomElement objectToRemove = object; - object = object.nextSiblingElement("object"); - automationPattern.removeChild(objectToRemove); - } - else - { - object = object.nextSiblingElement("object"); - } - } - - fixAutomationPattern(automationPattern); - - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - } - else - { - // The pattern has no base note automations. Remove it completely. - QDomElement patternToRemove = automationPattern; - automationPattern = automationPattern.nextSiblingElement("automationpattern"); - cloneOfTrack.removeChild(patternToRemove); - } - } - track.parentNode().appendChild(cloneOfTrack); - } -} /** \brief Note range has been extended to match MIDI specification * @@ -2017,54 +1678,8 @@ static void fixAutomationTracks(QDomElement & song, std::set const */ void DataFile::upgrade_extendedNoteRange() { - QDomElement song = documentElement().firstChildElement("song"); - while (!song.isNull()) - { - // This set will later contain all ids of automated base notes. They are - // used to find out which automation patterns must to be corrected, i.e. to - // check if an automation pattern has one or more base notes as its target. - std::set automatedBaseNoteIds; - - QDomElement trackContainer = song.firstChildElement("trackcontainer"); - while (!trackContainer.isNull()) - { - QDomElement track = trackContainer.firstChildElement("track"); - while (!track.isNull()) - { - fixTrack(track, automatedBaseNoteIds); - - track = track.nextSiblingElement("track"); - } - - trackContainer = trackContainer.nextSiblingElement("trackcontainer"); - } - - fixAutomationTracks(song, automatedBaseNoteIds); - - song = song.nextSiblingElement("song"); - }; - - if (elementsByTagName("song").item(0).isNull()) - { - // Dealing with a preset, not a song - QDomNodeList presets = elementsByTagName("instrumenttrack"); - if (presets.item(0).isNull()) { return; } - QDomElement preset = presets.item(0).toElement(); - // Common correction for all instrument presets (make base notes match the new MIDI range). - // NOTE: Many older presets do not have any basenote defined, assume they were "made for 57". - // (Specifying a default like this also happens to fix a FileBrowser bug where previews of presets - // with undefined basenote always play with the basenote inherited from previously played preview.) - int oldBase = preset.attribute("basenote", "57").toInt(); - preset.setAttribute("basenote", oldBase + 12); - // Extra correction for Zyn, VeSTige, LV2 and Carla (to preserve the original buggy behavior). - QDomNodeList instruments = presets.item(0).toElement().elementsByTagName("instrument"); - if (instruments.isEmpty()) { return; } - QDomElement instrument = instruments.item(0).toElement(); - if (affected(instrument)) - { - preset.setAttribute("basenote", preset.attribute("basenote").toInt() + 12); - } - } + auto root = documentElement(); + UpgradeExtendedNoteRange upgradeExtendedNoteRange(root); } diff --git a/src/core/UpgradeExtendedNoteRange.cpp b/src/core/UpgradeExtendedNoteRange.cpp new file mode 100644 index 000000000..aab47f6b1 --- /dev/null +++ b/src/core/UpgradeExtendedNoteRange.cpp @@ -0,0 +1,431 @@ +/* + * UpgradeExtendedNoteRange.cpp - Upgrades the extended note range + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "UpgradeExtendedNoteRange.h" + +#include "Track.h" + +#include + +#include + +namespace lmms +{ + +/** + * @brief Used by the helper function that analyzes automation patterns. + */ +struct PatternAnalysisResult +{ + PatternAnalysisResult(bool hasBaseNoteAutomations, bool hasNonBaseNoteAutomations) + { + this->hasBaseNoteAutomations = hasBaseNoteAutomations; + this->hasNonBaseNoteAutomations = hasNonBaseNoteAutomations; + } + bool hasBaseNoteAutomations; + bool hasNonBaseNoteAutomations; +}; + +/** + * @brief Helper function that checks for an automation pattern if it contains automation for + * targets that are base notes and/or other targets. + * @param automationPattern The automation pattern to be checked. + * @param automatedBaseNoteIds A set of id of automated base notes that are used in the check. + * @return A struct that contains the results. + */ +static PatternAnalysisResult analyzeAutomationPattern(QDomElement const & automationPattern, std::set const & automatedBaseNoteIds) +{ + bool hasBaseNoteAutomations = false; + bool hasNonBaseNoteAutomations = false; + + // Iterate the objects. These contain the ids of the automated objects. + QDomElement object = automationPattern.firstChildElement("object"); + while (!object.isNull()) + { + unsigned int const id = object.attribute("id").toUInt(); + + // Check if the automated object is a base note. + if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end()) + { + hasBaseNoteAutomations = true; + } + else + { + hasNonBaseNoteAutomations = true; + } + + object = object.nextSiblingElement("object"); + } + + return PatternAnalysisResult(hasBaseNoteAutomations, hasNonBaseNoteAutomations); +} + +static void fixNotePatterns(QDomNodeList & patterns) +{ + for (int i = 0; i < patterns.size(); ++i) + { + QDomNodeList notes = patterns.item(i).toElement().elementsByTagName("note"); + for (int j = 0; j < notes.size(); ++j) + { + QDomElement note = notes.item(j).toElement(); + if (note.hasAttribute("key")) + { + int const currentKey = note.attribute("key").toInt(); + note.setAttribute("key", currentKey + 12); + } + } + } +} + +static void fixInstrumentBaseNoteAndCollectIds(QDomElement & instrument, std::set & automatedBaseNoteIds) +{ + // Raise the base note of every instrument by 12 to compensate for the change + // of A4 key code from 57 to 69. This ensures that notes are labeled correctly. + QDomElement instrumentParent = instrument.parentNode().toElement(); + + // Correct the base note of the instrument. Base notes which are automated are + // stored as elements. Non-automated base notes are stored as attributes. + if (instrumentParent.hasAttribute("basenote")) + { + // TODO Base notes can have float values in the save file! This might need to be changed! + int const currentBaseNote = instrumentParent.attribute("basenote").toInt(); + instrumentParent.setAttribute("basenote", currentBaseNote + 12); + } + else + { + // Check if the instrument track has an automated base note. + // Correct the value of the base note and collect their ids while doing so. + // The ids are used later to find the automations that automate these corrected base + // notes so that we can correct the automation values as well. + QDomNodeList baseNotes = instrumentParent.elementsByTagName("basenote"); + for (int j = 0; j < baseNotes.size(); ++j) + { + QDomElement baseNote = baseNotes.item(j).toElement(); + if (!baseNote.isNull()) + { + if (baseNote.hasAttribute("value")) + { + // Base notes can have float values in the save file, e.g. if the file + // is saved after a linear automation has run on the base note. Therefore + // it is fixed here as a float even if the nominal values of base notes + // are integers. + float const value = baseNote.attribute("value").toFloat(); + baseNote.setAttribute("value", value + 12); + } + + // The ids of base notes are of type jo_id_t which are in fact uint32_t. + // So let's just use these here to save some casting. + unsigned int const id = baseNote.attribute("id").toUInt(); + automatedBaseNoteIds.insert(id); + } + } + } +} + +/** + * @brief Helper method that fixes the values and out values for an automation pattern. + * @param automationPattern The automation pattern to be fixed. + */ +static void fixAutomationPattern(QDomElement & automationPattern) +{ + QDomElement time = automationPattern.firstChildElement("time"); + while (!time.isNull()) + { + // Automation patterns can automate base notes as floats + // so we read and correct them as floats here. + float const value = time.attribute("value").toFloat(); + time.setAttribute("value", value + 12.); + + // The method "upgrade_automationNodes" adds some attributes + // with the name "outValue". We have to correct these as well. + float const outValue = time.attribute("outValue").toFloat(); + time.setAttribute("outValue", outValue + 12.); + + time = time.nextSiblingElement("time"); + }; +} + +static bool affected(QDomElement & instrument) +{ + assert(instrument.hasAttribute("name")); + QString const name = instrument.attribute("name"); + + return name == "zynaddsubfx" || + name == "vestige" || name == "lv2instrument" || + name == "carlapatchbay" || name == "carlarack"; +} + +static void fixTrack(QDomElement & track, std::set & automatedBaseNoteIds) +{ + if (!track.hasAttribute("type")) + { + return; + } + + Track::TrackTypes const trackType = static_cast(track.attribute("type").toInt()); + + // BB tracks need special handling because they contain a track container of their own + if (trackType == Track::PatternTrack) + { + // Assuming that a BB track cannot contain another BB track here... + QDomNodeList subTracks = track.elementsByTagName("track"); + for (int i = 0; i < subTracks.size(); ++i) + { + QDomElement subTrack = subTracks.item(i).toElement(); + fixTrack(subTrack, automatedBaseNoteIds); + } + } + else + { + QDomNodeList instruments = track.elementsByTagName("instrument"); + + for (int i = 0; i < instruments.size(); ++i) + { + QDomElement instrument = instruments.item(i).toElement(); + + fixInstrumentBaseNoteAndCollectIds(instrument, automatedBaseNoteIds); + + // Raise the pitch of all notes in patterns assigned to instruments not affected + // by #1857 by an octave. This negates the base note change for normal instruments, + // but leaves the MIDI-based instruments sounding an octave lower, preserving their + // pitch in existing projects. + if (!affected(instrument)) + { + QDomNodeList patterns = track.elementsByTagName("pattern"); + fixNotePatterns(patterns); + } + } + } +} + +static void fixAutomationTracks(QDomElement & song, std::set const & automatedBaseNoteIds) +{ + // Now fix all the automation tracks. + QDomNodeList tracks = song.elementsByTagName("track"); + + // We have to collect the tracks that we need to duplicate and cannot do this in-place + // because if we did the iteration might never stop. + std::vector tracksToDuplicate; + tracksToDuplicate.reserve(tracks.size()); + + // Iterate the tracks again. This time work on all automation tracks. + for (int i = 0; i < tracks.size(); ++i) + { + QDomElement currentTrack = tracks.item(i).toElement(); + if (currentTrack.attribute("type").toInt() != Track::AutomationTrack) + { + continue; + } + + // Check each track for the types of automations it contains in its patterns. + bool containsPatternsWithBaseNoteTargets = false; + bool containsPatternsWithNonBaseNoteTargets = false; + + QDomElement automationPattern = currentTrack.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) + { + auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); + containsPatternsWithBaseNoteTargets |= analysis.hasBaseNoteAutomations; + containsPatternsWithNonBaseNoteTargets |= analysis.hasNonBaseNoteAutomations; + + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + + if (!containsPatternsWithBaseNoteTargets) + { + // No base notes are automated by this automation track so we have nothing to do + continue; + } + else + { + if (!containsPatternsWithNonBaseNoteTargets) + { + // Only base note targets. This means we can simply keep the track and fix it. + automationPattern = currentTrack.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) + { + fixAutomationPattern(automationPattern); + + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + } + else + { + // The automation track has automations for base notes and other targets in its patterns. + // We will later need to duplicate/split the track. + tracksToDuplicate.push_back(currentTrack); + } + } + } + + // Now fix the tracks that need duplication/splitting + for (QDomElement & track : tracksToDuplicate) + { + // First clone the original track + QDomNode cloneOfTrack = track.cloneNode(); + + // Now that we have the original and the clone we can manipulate both of them. + // The original track will keep only patterns without base note automations. + // Note: for the original track these might also be automation patterns without + // any targets. We will keep these because they might have been saved by the users + // like this. + QDomElement automationPattern = track.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) + { + auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); + if (!analysis.hasBaseNoteAutomations) + { + // This pattern has no base note automations. Leave it alone. + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + else if (!analysis.hasNonBaseNoteAutomations) + { + // The pattern only has base note automations. Remove it completely as it would become empty. + QDomElement patternToRemove = automationPattern; + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + track.removeChild(patternToRemove); + } + else + { + // The pattern itself is mixed. Remove the base note objects. + QDomElement object = automationPattern.firstChildElement("object"); + while (!object.isNull()) + { + unsigned int const id = object.attribute("id").toUInt(); + + if (automatedBaseNoteIds.find(id) != automatedBaseNoteIds.end()) + { + QDomElement objectToRemove = object; + object = object.nextSiblingElement("object"); + automationPattern.removeChild(objectToRemove); + } + else + { + object = object.nextSiblingElement("object"); + } + } + + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + } + + // The clone will only keep non-empty patterns with base note automations + // and the values of the patterns will be corrected. + automationPattern = cloneOfTrack.firstChildElement("automationpattern"); + while (!automationPattern.isNull()) + { + auto const analysis = analyzeAutomationPattern(automationPattern, automatedBaseNoteIds); + if (analysis.hasBaseNoteAutomations) + { + // This pattern has base note automations. Remove all other ones and fix the pattern. + QDomElement object = automationPattern.firstChildElement("object"); + while (!object.isNull()) + { + unsigned int const id = object.attribute("id").toUInt(); + + if (automatedBaseNoteIds.find(id) == automatedBaseNoteIds.end()) + { + QDomElement objectToRemove = object; + object = object.nextSiblingElement("object"); + automationPattern.removeChild(objectToRemove); + } + else + { + object = object.nextSiblingElement("object"); + } + } + + fixAutomationPattern(automationPattern); + + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + } + else + { + // The pattern has no base note automations. Remove it completely. + QDomElement patternToRemove = automationPattern; + automationPattern = automationPattern.nextSiblingElement("automationpattern"); + cloneOfTrack.removeChild(patternToRemove); + } + } + track.parentNode().appendChild(cloneOfTrack); + } +} + + +UpgradeExtendedNoteRange::UpgradeExtendedNoteRange(QDomElement & domElement) : + m_domElement(domElement) +{ +} + +void UpgradeExtendedNoteRange::upgrade() +{ + QDomElement song = m_domElement.firstChildElement("song"); + while (!song.isNull()) + { + // This set will later contain all ids of automated base notes. They are + // used to find out which automation patterns must to be corrected, i.e. to + // check if an automation pattern has one or more base notes as its target. + std::set automatedBaseNoteIds; + + QDomElement trackContainer = song.firstChildElement("trackcontainer"); + while (!trackContainer.isNull()) + { + QDomElement track = trackContainer.firstChildElement("track"); + while (!track.isNull()) + { + fixTrack(track, automatedBaseNoteIds); + + track = track.nextSiblingElement("track"); + } + + trackContainer = trackContainer.nextSiblingElement("trackcontainer"); + } + + fixAutomationTracks(song, automatedBaseNoteIds); + + song = song.nextSiblingElement("song"); + }; + + if (m_domElement.elementsByTagName("song").item(0).isNull()) + { + // Dealing with a preset, not a song + QDomNodeList presets = m_domElement.elementsByTagName("instrumenttrack"); + if (presets.item(0).isNull()) { return; } + QDomElement preset = presets.item(0).toElement(); + // Common correction for all instrument presets (make base notes match the new MIDI range). + // NOTE: Many older presets do not have any basenote defined, assume they were "made for 57". + // (Specifying a default like this also happens to fix a FileBrowser bug where previews of presets + // with undefined basenote always play with the basenote inherited from previously played preview.) + int oldBase = preset.attribute("basenote", "57").toInt(); + preset.setAttribute("basenote", oldBase + 12); + // Extra correction for Zyn, VeSTige, LV2 and Carla (to preserve the original buggy behavior). + QDomNodeList instruments = presets.item(0).toElement().elementsByTagName("instrument"); + if (instruments.isEmpty()) { return; } + QDomElement instrument = instruments.item(0).toElement(); + if (affected(instrument)) + { + preset.setAttribute("basenote", preset.attribute("basenote").toInt() + 12); + } + } +} + +} diff --git a/src/core/UpgradeExtendedNoteRange.h b/src/core/UpgradeExtendedNoteRange.h new file mode 100644 index 000000000..66f95419a --- /dev/null +++ b/src/core/UpgradeExtendedNoteRange.h @@ -0,0 +1,47 @@ +/* + * UpgradeExtendedNoteRange.h - Upgrades the extended note range + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#pragma once + +#ifndef LMMS_UPGRADEEXTENDEDNOTERANGE_H +#define LMMS_UPGRADEEXTENDEDNOTERANGE_H + + +class QDomElement; + +namespace lmms +{ + +class UpgradeExtendedNoteRange +{ +public: + UpgradeExtendedNoteRange(QDomElement & domElement); + + void upgrade(); + +private: + QDomElement & m_domElement; +}; + +} + +#endif // LMMS_UPGRADEEXTENDEDNOTERANGE_H From 5a6799314abf4cca8e8280eefef0f5ca5ca854fc Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Fri, 11 Aug 2023 18:44:13 +0200 Subject: [PATCH 18/45] Fix builds (#6548) Add the comment "// namespace lmms" to the closing scope of the lmms namespaces. The "scripted-checks" are quite strict. Perhaps they should be renamed to "strict-checks". ;) Include "cassert" in UpgradeExtendedNoteRange.cpp to hopefully fix the mingw builds. --- src/core/UpgradeExtendedNoteRange.cpp | 4 +++- src/core/UpgradeExtendedNoteRange.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/UpgradeExtendedNoteRange.cpp b/src/core/UpgradeExtendedNoteRange.cpp index aab47f6b1..41cbc5c26 100644 --- a/src/core/UpgradeExtendedNoteRange.cpp +++ b/src/core/UpgradeExtendedNoteRange.cpp @@ -27,6 +27,8 @@ #include #include +#include + namespace lmms { @@ -428,4 +430,4 @@ void UpgradeExtendedNoteRange::upgrade() } } -} +} // namespace lmms diff --git a/src/core/UpgradeExtendedNoteRange.h b/src/core/UpgradeExtendedNoteRange.h index 66f95419a..ae444e82a 100644 --- a/src/core/UpgradeExtendedNoteRange.h +++ b/src/core/UpgradeExtendedNoteRange.h @@ -42,6 +42,6 @@ private: QDomElement & m_domElement; }; -} +} // namespace lmms #endif // LMMS_UPGRADEEXTENDEDNOTERANGE_H From e87d44b42ca3a29bbc0ade8313deb6271cd5e166 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Sat, 12 Aug 2023 11:54:26 -0300 Subject: [PATCH 19/45] Fixes bug from issue #6002 (#6803) * Fixes bug from issue 6002 Since the refactoring of the Copy/Paste features, it was not possible to paste inside the BBEditor (now Pattern Editor), as reported on issue #6002. The reason for that is better explained in the issue discussion. This PR fixes this by allowing pasting inside the Pattern Editor when the clipboard has a single Clip, which is what was allowed on the previous workflow, while at the same time avoiding the unexpected behavior of pasting multiple Clips that might invade the positions reserved for different Pattern Tracks. --- src/gui/tracks/TrackContentWidget.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/gui/tracks/TrackContentWidget.cpp b/src/gui/tracks/TrackContentWidget.cpp index 74ab016ea..26107c1ae 100644 --- a/src/gui/tracks/TrackContentWidget.cpp +++ b/src/gui/tracks/TrackContentWidget.cpp @@ -325,8 +325,7 @@ bool TrackContentWidget::canPasteSelection( TimePos clipPos, const QMimeData* md QString value = decodeValue( md ); // We can only paste into tracks of the same type - if( type != ( "clip_" + QString::number( t->type() ) ) || - m_trackView->trackContainerView()->fixedClips() == true ) + if (type != ("clip_" + QString::number(t->type()))) { return false; } @@ -360,6 +359,14 @@ bool TrackContentWidget::canPasteSelection( TimePos clipPos, const QMimeData* md QDomElement clipParent = dataFile.content().firstChildElement("clips"); QDomNodeList clipNodes = clipParent.childNodes(); + // If we are pasting into the PatternEditor, only a single Clip is allowed to be pasted + // so we don't have the unexpected behavior of pasting on different PatternTracks + if (m_trackView->trackContainerView()->fixedClips() == true && + clipNodes.length() > 1) + { + return false; + } + // Determine if all the Clips will land on a valid track for( int i = 0; i < clipNodes.length(); i++ ) { From 98c5e6af5d0241f458d6d6f5545ad76ca8b36485 Mon Sep 17 00:00:00 2001 From: consolegrl <5942029+consolegrl@users.noreply.github.com> Date: Fri, 18 Aug 2023 10:26:06 -0400 Subject: [PATCH 20/45] fix: Issue 6807: Marked notes bleed into note... (#6812) Issue was caused by an obiwan (off-by-one) The 'if (y > limit)' test was broken by an incorrect inequality, should be >=, and a graphical adjustment made previously in the 'y = ...' statement. I perserved the graphical adjustment and fixed the test to be 'if (y >= limit - 1) { break; }' --- src/gui/editors/PianoRoll.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 3ec008ecb..337b36b36 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -3357,7 +3357,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int key_num = m_markedSemiTones.at(x); const int y = keyAreaBottom() - 1 - m_keyLineHeight * (key_num - m_startKey + 1); - if(y > keyAreaBottom()) { break; } + if(y >= keyAreaBottom() - 1) { break; } p.fillRect(m_whiteKeyWidth + 1, y, width() - 10, From b62a272d01dab2af66acdcf15c8ea77405e61f8e Mon Sep 17 00:00:00 2001 From: Dalton Messmer <33463986+messmerd@users.noreply.github.com> Date: Sat, 19 Aug 2023 02:47:26 -0400 Subject: [PATCH 21/45] Fix segfault when clicking "Auto Detect" in Connection Settings window (#6811) --- src/gui/modals/ControllerConnectionDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/modals/ControllerConnectionDialog.cpp b/src/gui/modals/ControllerConnectionDialog.cpp index 06f6a5708..6396c7ccb 100644 --- a/src/gui/modals/ControllerConnectionDialog.cpp +++ b/src/gui/modals/ControllerConnectionDialog.cpp @@ -406,7 +406,7 @@ void ControllerConnectionDialog::userSelected() void ControllerConnectionDialog::autoDetectToggled() { - if( m_midiAutoDetect.value() ) + if (m_midiAutoDetect.value() && m_midiController) { m_midiController->reset(); } From 793f096c4f6c01c9f48328530fe05320892835d1 Mon Sep 17 00:00:00 2001 From: Bimal Poudel Date: Sun, 20 Aug 2023 00:52:23 -0600 Subject: [PATCH 22/45] Update DummyCarla.cpp to use CARLA_PLUGIN_EXPORT (#6814) --- plugins/CarlaBase/DummyCarla.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/plugins/CarlaBase/DummyCarla.cpp b/plugins/CarlaBase/DummyCarla.cpp index 8fd9ef6d7..572bdf4bc 100644 --- a/plugins/CarlaBase/DummyCarla.cpp +++ b/plugins/CarlaBase/DummyCarla.cpp @@ -2,12 +2,16 @@ #define BUILDING_CARLA #include "CarlaNativePlugin.h" -CARLA_EXPORT const char* carla_get_library_filename() { return nullptr; } -CARLA_EXPORT const char* carla_get_library_folder() { return nullptr; } -CARLA_EXPORT const NativePluginDescriptor* carla_get_native_rack_plugin() { return nullptr; } -CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay_plugin() { return nullptr; } -CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay16_plugin() { return nullptr; } -CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay32_plugin() { return nullptr; } -CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay64_plugin() { return nullptr; } -CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay_cv_plugin() { return nullptr; } -CARLA_EXPORT CarlaBackend::CarlaEngine* carla_get_native_plugin_engine(const NativePluginDescriptor* desc, NativePluginHandle handle) { return nullptr; } +#ifndef CARLA_PLUGIN_EXPORT +#define CARLA_PLUGIN_EXPORT CARLA_EXPORT +#endif + +CARLA_PLUGIN_EXPORT const char* carla_get_library_filename() { return nullptr; } +CARLA_PLUGIN_EXPORT const char* carla_get_library_folder() { return nullptr; } +CARLA_PLUGIN_EXPORT const NativePluginDescriptor* carla_get_native_rack_plugin() { return nullptr; } +CARLA_PLUGIN_EXPORT const NativePluginDescriptor* carla_get_native_patchbay_plugin() { return nullptr; } +CARLA_PLUGIN_EXPORT const NativePluginDescriptor* carla_get_native_patchbay16_plugin() { return nullptr; } +CARLA_PLUGIN_EXPORT const NativePluginDescriptor* carla_get_native_patchbay32_plugin() { return nullptr; } +CARLA_PLUGIN_EXPORT const NativePluginDescriptor* carla_get_native_patchbay64_plugin() { return nullptr; } +CARLA_PLUGIN_EXPORT const NativePluginDescriptor* carla_get_native_patchbay_cv_plugin() { return nullptr; } +CARLA_PLUGIN_EXPORT CarlaBackend::CarlaEngine* carla_get_native_plugin_engine(const NativePluginDescriptor* desc, NativePluginHandle handle) { return nullptr; } From 5cbf2c5287d62b018927f58d88b54c4462f551fb Mon Sep 17 00:00:00 2001 From: Dalton Messmer <33463986+messmerd@users.noreply.github.com> Date: Mon, 21 Aug 2023 23:07:09 -0400 Subject: [PATCH 23/45] Fix LMMS plugin issues (#6670) * Fix Organic automated harmonic parameter loading * Fix Kicker automated end distortion parameter loading * Fix AudioFileProcessor automated interpolation parameter loading * Fix Vibed automated volume parameter loading * Improve coding style/formatting * Fix #6671 * Fix Vibed memory leaks * Refactor Vibed instrument * Fix build * Try to fix build again * Revert previous commit * Replace more raw pointers with smart pointers * Remove unused files * Minor changes * Update plugins/Vibed/Vibed.cpp Co-authored-by: Kevin Zander * Implement suggestions from review * Minor changes * Only check plugin data pointer * Refactor NineButtonSelector * Fix memory leaks during heavy tempo automation * Fix build * Use s_stringCount * Replace some vectors with arrays * Use array instead of switch * Allow compiler to generate move assignment operator * Fix loading of old automated detune values * Fix member variable names * Address review comments --------- Co-authored-by: Kevin Zander --- .../AudioFileProcessor/AudioFileProcessor.cpp | 87 +- .../AudioFileProcessor/AudioFileProcessor.h | 12 +- plugins/BitInvader/BitInvader.cpp | 3 +- plugins/FreeBoy/FreeBoy.cpp | 2 +- plugins/GigPlayer/GigPlayer.cpp | 4 +- plugins/Kicker/Kicker.cpp | 84 +- plugins/Kicker/Kicker.h | 11 +- plugins/Lb302/Lb302.cpp | 2 +- plugins/Monstro/Monstro.cpp | 2 +- plugins/Nes/Nes.cpp | 2 +- plugins/Organic/Organic.cpp | 76 +- plugins/Organic/Organic.h | 11 +- plugins/Patman/Patman.cpp | 2 +- plugins/Sf2Player/Sf2Player.cpp | 4 +- plugins/Sfxr/Sfxr.cpp | 6 +- plugins/Sid/SidInstrument.cpp | 4 +- plugins/Stk/Mallets/Mallets.cpp | 2 +- plugins/TripleOscillator/TripleOscillator.cpp | 2 +- plugins/Vibed/CMakeLists.txt | 5 +- plugins/Vibed/NineButtonSelector.cpp | 234 +----- plugins/Vibed/NineButtonSelector.h | 89 +- plugins/Vibed/StringContainer.cpp | 108 --- plugins/Vibed/StringContainer.h | 86 -- plugins/Vibed/Vibed.cpp | 794 +++++++----------- plugins/Vibed/Vibed.h | 121 +-- plugins/Vibed/VibratingString.cpp | 150 ++-- plugins/Vibed/VibratingString.h | 299 +++---- plugins/Watsyn/Watsyn.cpp | 2 +- plugins/Xpressive/Xpressive.cpp | 2 +- 29 files changed, 781 insertions(+), 1425 deletions(-) delete mode 100644 plugins/Vibed/StringContainer.cpp delete mode 100644 plugins/Vibed/StringContainer.h diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index 2e2d7163b..2243683ce 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -1,5 +1,5 @@ /* - * AudioFileProcessor.cpp - instrument for using audio-files + * AudioFileProcessor.cpp - instrument for using audio files * * Copyright (c) 2004-2014 Tobias Doerffel * @@ -47,7 +47,6 @@ #include "embed.h" #include "plugin_export.h" - namespace lmms { @@ -205,74 +204,70 @@ void AudioFileProcessor::deleteNotePluginData( NotePlayHandle * _n ) -void AudioFileProcessor::saveSettings( QDomDocument & _doc, - QDomElement & _this ) +void AudioFileProcessor::saveSettings(QDomDocument& doc, QDomElement& elem) { - _this.setAttribute( "src", m_sampleBuffer.audioFile() ); - if( m_sampleBuffer.audioFile() == "" ) + elem.setAttribute("src", m_sampleBuffer.audioFile()); + if (m_sampleBuffer.audioFile().isEmpty()) { QString s; - _this.setAttribute( "sampledata", - m_sampleBuffer.toBase64( s ) ); + elem.setAttribute("sampledata", m_sampleBuffer.toBase64(s)); } - m_reverseModel.saveSettings( _doc, _this, "reversed" ); - m_loopModel.saveSettings( _doc, _this, "looped" ); - m_ampModel.saveSettings( _doc, _this, "amp" ); - m_startPointModel.saveSettings( _doc, _this, "sframe" ); - m_endPointModel.saveSettings( _doc, _this, "eframe" ); - m_loopPointModel.saveSettings( _doc, _this, "lframe" ); - m_stutterModel.saveSettings( _doc, _this, "stutter" ); - m_interpolationModel.saveSettings( _doc, _this, "interp" ); - + m_reverseModel.saveSettings(doc, elem, "reversed"); + m_loopModel.saveSettings(doc, elem, "looped"); + m_ampModel.saveSettings(doc, elem, "amp"); + m_startPointModel.saveSettings(doc, elem, "sframe"); + m_endPointModel.saveSettings(doc, elem, "eframe"); + m_loopPointModel.saveSettings(doc, elem, "lframe"); + m_stutterModel.saveSettings(doc, elem, "stutter"); + m_interpolationModel.saveSettings(doc, elem, "interp"); } -void AudioFileProcessor::loadSettings( const QDomElement & _this ) +void AudioFileProcessor::loadSettings(const QDomElement& elem) { - if( _this.attribute( "src" ) != "" ) + if (!elem.attribute("src").isEmpty()) { - setAudioFile( _this.attribute( "src" ), false ); + setAudioFile(elem.attribute("src"), false); - QString absolutePath = PathUtil::toAbsolute( m_sampleBuffer.audioFile() ); - if ( !QFileInfo( absolutePath ).exists() ) + QString absolutePath = PathUtil::toAbsolute(m_sampleBuffer.audioFile()); + if (!QFileInfo(absolutePath).exists()) { - QString message = tr( "Sample not found: %1" ).arg( m_sampleBuffer.audioFile() ); - - Engine::getSong()->collectError( message ); + QString message = tr("Sample not found: %1").arg(m_sampleBuffer.audioFile()); + Engine::getSong()->collectError(message); } } - else if( _this.attribute( "sampledata" ) != "" ) + else if (!elem.attribute("sampledata").isEmpty()) { - m_sampleBuffer.loadFromBase64( _this.attribute( "srcdata" ) ); + m_sampleBuffer.loadFromBase64(elem.attribute("srcdata")); } - m_loopModel.loadSettings( _this, "looped" ); - m_ampModel.loadSettings( _this, "amp" ); - m_endPointModel.loadSettings( _this, "eframe" ); - m_startPointModel.loadSettings( _this, "sframe" ); + m_loopModel.loadSettings(elem, "looped"); + m_ampModel.loadSettings(elem, "amp"); + m_endPointModel.loadSettings(elem, "eframe"); + m_startPointModel.loadSettings(elem, "sframe"); // compat code for not having a separate loopback point - if (_this.hasAttribute("lframe") || !(_this.firstChildElement("lframe").isNull())) + if (elem.hasAttribute("lframe") || !elem.firstChildElement("lframe").isNull()) { - m_loopPointModel.loadSettings( _this, "lframe" ); + m_loopPointModel.loadSettings(elem, "lframe"); } else { - m_loopPointModel.loadSettings( _this, "sframe" ); + m_loopPointModel.loadSettings(elem, "sframe"); } - m_reverseModel.loadSettings( _this, "reversed" ); + m_reverseModel.loadSettings(elem, "reversed"); - m_stutterModel.loadSettings( _this, "stutter" ); - if( _this.hasAttribute( "interp" ) ) + m_stutterModel.loadSettings(elem, "stutter"); + if (elem.hasAttribute("interp") || !elem.firstChildElement("interp").isNull()) { - m_interpolationModel.loadSettings( _this, "interp" ); + m_interpolationModel.loadSettings(elem, "interp"); } else { - m_interpolationModel.setValue( 1 ); //linear by default + m_interpolationModel.setValue(1.0f); // linear by default } pointChanged(); @@ -686,14 +681,12 @@ void AudioFileProcessorView::sampleUpdated() void AudioFileProcessorView::openAudioFile() { - QString af = castModel()->m_sampleBuffer. - openAudioFile(); - if( af != "" ) - { - castModel()->setAudioFile( af ); - Engine::getSong()->setModified(); - m_waveView->updateSampleRange(); - } + QString af = castModel()->m_sampleBuffer.openAudioFile(); + if (af.isEmpty()) { return; } + + castModel()->setAudioFile(af); + Engine::getSong()->setModified(); + m_waveView->updateSampleRange(); } diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.h b/plugins/AudioFileProcessor/AudioFileProcessor.h index b80954577..6c696784a 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.h +++ b/plugins/AudioFileProcessor/AudioFileProcessor.h @@ -23,9 +23,8 @@ * */ - -#ifndef AUDIO_FILE_PROCESSOR_H -#define AUDIO_FILE_PROCESSOR_H +#ifndef LMMS_AUDIO_FILE_PROCESSOR_H +#define LMMS_AUDIO_FILE_PROCESSOR_H #include @@ -61,9 +60,8 @@ public: sampleFrame * _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; - void saveSettings( QDomDocument & _doc, - QDomElement & _parent ) override; - void loadSettings( const QDomElement & _this ) override; + void saveSettings(QDomDocument& doc, QDomElement& elem) override; + void loadSettings(const QDomElement& elem) override; void loadFile( const QString & _file ) override; @@ -303,4 +301,4 @@ private: } // namespace lmms -#endif +#endif // LMMS_AUDIO_FILE_PROCESSOR_H diff --git a/plugins/BitInvader/BitInvader.cpp b/plugins/BitInvader/BitInvader.cpp index 947c6f5d6..d89383560 100644 --- a/plugins/BitInvader/BitInvader.cpp +++ b/plugins/BitInvader/BitInvader.cpp @@ -274,9 +274,8 @@ QString BitInvader::nodeName() const void BitInvader::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { - if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == nullptr ) + if (!_n->m_pluginData) { - float factor; if( !m_normalize.value() ) { diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index 1fd3f2513..c26cd79c4 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -250,7 +250,7 @@ void FreeBoyInstrument::playNote(NotePlayHandle* nph, sampleFrame* workingBuffer int data = 0; int freq = nph->frequency(); - if ( tfp == 0 ) + if (!nph->m_pluginData) { auto papu = new GbApuWrapper{}; papu->setSampleRate(samplerate, CLOCK_RATE); diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 24073ceea..42d64cf07 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -293,8 +293,6 @@ void GigInstrument::playNote( NotePlayHandle * _n, sampleFrame * ) { const float LOG440 = 2.643452676f; - const f_cnt_t tfp = _n->totalFramesPlayed(); - int midiNote = (int) floor( 12.0 * ( log2( _n->unpitchedFrequency() ) - LOG440 ) - 4.0 ); // out of range? @@ -303,7 +301,7 @@ void GigInstrument::playNote( NotePlayHandle * _n, sampleFrame * ) return; } - if( tfp == 0 ) + if (!_n->m_pluginData) { auto pluginData = new GIGPluginData; pluginData->midiNote = midiNote; diff --git a/plugins/Kicker/Kicker.cpp b/plugins/Kicker/Kicker.cpp index 7d4c6fbb0..29f7dc8f1 100644 --- a/plugins/Kicker/Kicker.cpp +++ b/plugins/Kicker/Kicker.cpp @@ -23,11 +23,10 @@ * */ - +#include "Kicker.h" #include -#include "Kicker.h" #include "AudioEngine.h" #include "Engine.h" #include "InstrumentTrack.h" @@ -85,65 +84,64 @@ KickerInstrument::KickerInstrument( InstrumentTrack * _instrument_track ) : -void KickerInstrument::saveSettings( QDomDocument & _doc, - QDomElement & _this ) +void KickerInstrument::saveSettings(QDomDocument& doc, QDomElement& elem) { - m_startFreqModel.saveSettings( _doc, _this, "startfreq" ); - m_endFreqModel.saveSettings( _doc, _this, "endfreq" ); - m_decayModel.saveSettings( _doc, _this, "decay" ); - m_distModel.saveSettings( _doc, _this, "dist" ); - m_distEndModel.saveSettings( _doc, _this, "distend" ); - m_gainModel.saveSettings( _doc, _this, "gain" ); - m_envModel.saveSettings( _doc, _this, "env" ); - m_noiseModel.saveSettings( _doc, _this, "noise" ); - m_clickModel.saveSettings( _doc, _this, "click" ); - m_slopeModel.saveSettings( _doc, _this, "slope" ); - m_startNoteModel.saveSettings( _doc, _this, "startnote" ); - m_endNoteModel.saveSettings( _doc, _this, "endnote" ); - m_versionModel.saveSettings( _doc, _this, "version" ); + m_startFreqModel.saveSettings(doc, elem, "startfreq"); + m_endFreqModel.saveSettings(doc, elem, "endfreq"); + m_decayModel.saveSettings(doc, elem, "decay"); + m_distModel.saveSettings(doc, elem, "dist"); + m_distEndModel.saveSettings(doc, elem, "distend"); + m_gainModel.saveSettings(doc, elem, "gain"); + m_envModel.saveSettings(doc, elem, "env"); + m_noiseModel.saveSettings(doc, elem, "noise"); + m_clickModel.saveSettings(doc, elem, "click"); + m_slopeModel.saveSettings(doc, elem, "slope"); + m_startNoteModel.saveSettings(doc, elem, "startnote"); + m_endNoteModel.saveSettings(doc, elem, "endnote"); + m_versionModel.saveSettings(doc, elem, "version"); } -void KickerInstrument::loadSettings( const QDomElement & _this ) +void KickerInstrument::loadSettings(const QDomElement& elem) { - m_versionModel.loadSettings( _this, "version" ); + m_versionModel.loadSettings(elem, "version"); - m_startFreqModel.loadSettings( _this, "startfreq" ); - m_endFreqModel.loadSettings( _this, "endfreq" ); - m_decayModel.loadSettings( _this, "decay" ); - m_distModel.loadSettings( _this, "dist" ); - if( _this.hasAttribute( "distend" ) ) + m_startFreqModel.loadSettings(elem, "startfreq"); + m_endFreqModel.loadSettings(elem, "endfreq"); + m_decayModel.loadSettings(elem, "decay"); + m_distModel.loadSettings(elem, "dist"); + if (elem.hasAttribute("distend") || !elem.firstChildElement("distend").isNull()) { - m_distEndModel.loadSettings( _this, "distend" ); + m_distEndModel.loadSettings(elem, "distend"); } else { - m_distEndModel.setValue( m_distModel.value() ); + m_distEndModel.setValue(m_distModel.value()); } - m_gainModel.loadSettings( _this, "gain" ); - m_envModel.loadSettings( _this, "env" ); - m_noiseModel.loadSettings( _this, "noise" ); - m_clickModel.loadSettings( _this, "click" ); - m_slopeModel.loadSettings( _this, "slope" ); - m_startNoteModel.loadSettings( _this, "startnote" ); - if( m_versionModel.value() < 1 ) + m_gainModel.loadSettings(elem, "gain"); + m_envModel.loadSettings(elem, "env"); + m_noiseModel.loadSettings(elem, "noise"); + m_clickModel.loadSettings(elem, "click"); + m_slopeModel.loadSettings(elem, "slope"); + m_startNoteModel.loadSettings(elem, "startnote"); + if (m_versionModel.value() < 1) { - m_startNoteModel.setValue( false ); + m_startNoteModel.setValue(false); } - m_endNoteModel.loadSettings( _this, "endnote" ); + m_endNoteModel.loadSettings(elem, "endnote"); // Try to maintain backwards compatibility - if( !_this.hasAttribute( "version" ) ) + if (!elem.hasAttribute("version")) { - m_startNoteModel.setValue( false ); - m_decayModel.setValue( m_decayModel.value() * 1.33f ); - m_envModel.setValue( 1.0f ); - m_slopeModel.setValue( 1.0f ); - m_clickModel.setValue( 0.0f ); + m_startNoteModel.setValue(false); + m_decayModel.setValue(m_decayModel.value() * 1.33f); + m_envModel.setValue(1.0f); + m_slopeModel.setValue(1.0f); + m_clickModel.setValue(0.0f); } - m_versionModel.setValue( KICKER_PRESET_VERSION ); + m_versionModel.setValue(KICKER_PRESET_VERSION); } @@ -165,7 +163,7 @@ void KickerInstrument::playNote( NotePlayHandle * _n, const float decfr = m_decayModel.value() * Engine::audioEngine()->processingSampleRate() / 1000.0f; const f_cnt_t tfp = _n->totalFramesPlayed(); - if ( tfp == 0 ) + if (!_n->m_pluginData) { _n->m_pluginData = new SweepOsc( DistFX( m_distModel.value(), diff --git a/plugins/Kicker/Kicker.h b/plugins/Kicker/Kicker.h index 22413c4db..cef732adb 100644 --- a/plugins/Kicker/Kicker.h +++ b/plugins/Kicker/Kicker.h @@ -23,9 +23,8 @@ * */ - -#ifndef KICKER_H -#define KICKER_H +#ifndef LMMS_KICKER_H +#define LMMS_KICKER_H #include "AutomatableModel.h" #include "Instrument.h" @@ -60,8 +59,8 @@ public: sampleFrame * _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; - void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; - void loadSettings( const QDomElement & _this ) override; + void saveSettings(QDomDocument& doc, QDomElement& elem) override; + void loadSettings(const QDomElement& elem) override; QString nodeName() const override; @@ -135,4 +134,4 @@ private: } // namespace lmms -#endif +#endif // LMMS_KICKER_H diff --git a/plugins/Lb302/Lb302.cpp b/plugins/Lb302/Lb302.cpp index 140b84189..c0e477dcc 100644 --- a/plugins/Lb302/Lb302.cpp +++ b/plugins/Lb302/Lb302.cpp @@ -746,7 +746,7 @@ void Lb302Synth::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) void Lb302Synth::processNote( NotePlayHandle * _n ) { /// Start a new note. - if( _n->m_pluginData != this ) + if (_n->m_pluginData != this) { m_playingNote = _n; new_freq = true; diff --git a/plugins/Monstro/Monstro.cpp b/plugins/Monstro/Monstro.cpp index 3a9737fdb..10dbf5b1f 100644 --- a/plugins/Monstro/Monstro.cpp +++ b/plugins/Monstro/Monstro.cpp @@ -1030,7 +1030,7 @@ void MonstroInstrument::playNote( NotePlayHandle * _n, const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); - if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == nullptr ) + if (!_n->m_pluginData) { _n->m_pluginData = new MonstroSynth( this, _n ); } diff --git a/plugins/Nes/Nes.cpp b/plugins/Nes/Nes.cpp index d47ebd728..4260bca1a 100644 --- a/plugins/Nes/Nes.cpp +++ b/plugins/Nes/Nes.cpp @@ -550,7 +550,7 @@ void NesInstrument::playNote( NotePlayHandle * n, sampleFrame * workingBuffer ) const fpp_t frames = n->framesLeftForCurrentPeriod(); const f_cnt_t offset = n->noteOffset(); - if ( n->totalFramesPlayed() == 0 || n->m_pluginData == nullptr ) + if (!n->m_pluginData) { auto nes = new NesObject(this, Engine::audioEngine()->processingSampleRate(), n); n->m_pluginData = nes; diff --git a/plugins/Organic/Organic.cpp b/plugins/Organic/Organic.cpp index 6c2cde773..476b24961 100644 --- a/plugins/Organic/Organic.cpp +++ b/plugins/Organic/Organic.cpp @@ -22,13 +22,10 @@ * */ - #include "Organic.h" - #include - #include "Engine.h" #include "AudioEngine.h" #include "InstrumentTrack.h" @@ -38,7 +35,6 @@ #include "PixmapButton.h" #include "embed.h" - #include "plugin_export.h" namespace lmms @@ -159,61 +155,59 @@ OrganicInstrument::~OrganicInstrument() -void OrganicInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) +void OrganicInstrument::saveSettings(QDomDocument& doc, QDomElement& elem) { - _this.setAttribute( "num_osc", QString::number( m_numOscillators ) ); - m_fx1Model.saveSettings( _doc, _this, "foldback" ); - m_volModel.saveSettings( _doc, _this, "vol" ); + elem.setAttribute("num_osc", QString::number(m_numOscillators)); + m_fx1Model.saveSettings(doc, elem, "foldback"); + m_volModel.saveSettings(doc, elem, "vol"); - for( int i = 0; i < m_numOscillators; ++i ) + for (int i = 0; i < m_numOscillators; ++i) { - QString is = QString::number( i ); - m_osc[i]->m_volModel.saveSettings( _doc, _this, "vol" + is ); - m_osc[i]->m_panModel.saveSettings( _doc, _this, "pan" + is ); - m_osc[i]->m_harmModel.saveSettings( _doc, _this, "newharmonic" + is ); - - m_osc[i]->m_detuneModel.saveSettings( _doc, _this, "newdetune" - + is ); - m_osc[i]->m_oscModel.saveSettings( _doc, _this, "wavetype" - + is ); + const auto is = QString::number(i); + m_osc[i]->m_volModel.saveSettings(doc, elem, "vol" + is); + m_osc[i]->m_panModel.saveSettings(doc, elem, "pan" + is); + m_osc[i]->m_harmModel.saveSettings(doc, elem, "newharmonic" + is); + m_osc[i]->m_detuneModel.saveSettings(doc, elem, "newdetune" + is); + m_osc[i]->m_oscModel.saveSettings(doc, elem, "wavetype" + is); } } -void OrganicInstrument::loadSettings( const QDomElement & _this ) +void OrganicInstrument::loadSettings(const QDomElement& elem) { -// m_numOscillators = _this.attribute( "num_osc" ). - // toInt(); - - for( int i = 0; i < m_numOscillators; ++i ) + for (int i = 0; i < m_numOscillators; ++i) { - QString is = QString::number( i ); - m_osc[i]->m_volModel.loadSettings( _this, "vol" + is ); - if( _this.hasAttribute( "detune" + is ) ) - { - m_osc[i]->m_detuneModel.setValue( _this.attribute( "detune" ).toInt() * 12 ); - } - else - { - m_osc[i]->m_detuneModel.loadSettings( _this, "newdetune" + is ); - } - m_osc[i]->m_panModel.loadSettings( _this, "pan" + is ); - m_osc[i]->m_oscModel.loadSettings( _this, "wavetype" + is ); + const auto is = QString::number(i); - if( _this.hasAttribute( "newharmonic" + is ) ) + m_osc[i]->m_volModel.loadSettings(elem, "vol" + is); + + if (elem.hasAttribute("detune" + is) || !elem.firstChildElement("detune" + is).isNull()) { - m_osc[i]->m_harmModel.loadSettings( _this, "newharmonic" + is ); + m_osc[i]->m_detuneModel.loadSettings(elem, "detune" + is); + m_osc[i]->m_detuneModel.setValue(m_osc[i]->m_detuneModel.value() * 12); // compat } else { - m_osc[i]->m_harmModel.setValue( static_cast( i ) ); + m_osc[i]->m_detuneModel.loadSettings(elem, "newdetune" + is); + } + + m_osc[i]->m_panModel.loadSettings(elem, "pan" + is); + m_osc[i]->m_oscModel.loadSettings(elem, "wavetype" + is); + + if (elem.hasAttribute("newharmonic" + is) || !elem.firstChildElement("newharmonic" + is).isNull()) + { + m_osc[i]->m_harmModel.loadSettings(elem, "newharmonic" + is); + } + else + { + m_osc[i]->m_harmModel.setValue(static_cast(i)); } } - m_volModel.loadSettings( _this, "vol" ); - m_fx1Model.loadSettings( _this, "foldback" ); + m_volModel.loadSettings(elem, "vol"); + m_fx1Model.loadSettings(elem, "foldback"); } @@ -231,7 +225,7 @@ void OrganicInstrument::playNote( NotePlayHandle * _n, const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); - if( _n->totalFramesPlayed() == 0 || _n->m_pluginData == nullptr ) + if (!_n->m_pluginData) { auto oscs_l = std::array{}; auto oscs_r = std::array{}; diff --git a/plugins/Organic/Organic.h b/plugins/Organic/Organic.h index 057a4a5b4..6c53e84ec 100644 --- a/plugins/Organic/Organic.h +++ b/plugins/Organic/Organic.h @@ -22,9 +22,8 @@ * */ - -#ifndef ORGANIC_H -#define ORGANIC_H +#ifndef LMMS_ORGANIC_H +#define LMMS_ORGANIC_H #include @@ -131,8 +130,8 @@ public: void deleteNotePluginData( NotePlayHandle * _n ) override; - void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; - void loadSettings( const QDomElement & _this ) override; + void saveSettings(QDomDocument& doc, QDomElement& elem) override; + void loadSettings(const QDomElement& elem) override; QString nodeName() const override; @@ -239,4 +238,4 @@ protected slots: } // namespace lmms -#endif +#endif // LMMS_ORGANIC_H diff --git a/plugins/Patman/Patman.cpp b/plugins/Patman/Patman.cpp index a8f75db60..7bcd46dcb 100644 --- a/plugins/Patman/Patman.cpp +++ b/plugins/Patman/Patman.cpp @@ -144,7 +144,7 @@ void PatmanInstrument::playNote( NotePlayHandle * _n, const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); - if( !_n->m_pluginData ) + if (!_n->m_pluginData) { selectSample( _n ); } diff --git a/plugins/Sf2Player/Sf2Player.cpp b/plugins/Sf2Player/Sf2Player.cpp index ab3729f7d..ef4f884ca 100644 --- a/plugins/Sf2Player/Sf2Player.cpp +++ b/plugins/Sf2Player/Sf2Player.cpp @@ -663,8 +663,6 @@ void Sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * ) return; } - const f_cnt_t tfp = _n->totalFramesPlayed(); - int masterPitch = instrumentTrack()->useMasterPitchModel()->value() ? Engine::getSong()->masterPitch() : 0; int baseNote = instrumentTrack()->baseNoteModel()->value(); int midiNote = _n->midiKey() - baseNote + DefaultBaseKey + masterPitch; @@ -675,7 +673,7 @@ void Sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * ) return; } - if (tfp == 0) + if (!_n->m_pluginData) { const int baseVelocity = instrumentTrack()->midiPort()->baseVelocity(); diff --git a/plugins/Sfxr/Sfxr.cpp b/plugins/Sfxr/Sfxr.cpp index d97217446..33f588521 100644 --- a/plugins/Sfxr/Sfxr.cpp +++ b/plugins/Sfxr/Sfxr.cpp @@ -446,9 +446,9 @@ void SfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe { float currentSampleRate = Engine::audioEngine()->processingSampleRate(); - fpp_t frameNum = _n->framesLeftForCurrentPeriod(); - const f_cnt_t offset = _n->noteOffset(); - if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == nullptr ) + fpp_t frameNum = _n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = _n->noteOffset(); + if (!_n->m_pluginData) { _n->m_pluginData = new SfxrSynth( this ); } diff --git a/plugins/Sid/SidInstrument.cpp b/plugins/Sid/SidInstrument.cpp index 6e7473492..3b41d8972 100644 --- a/plugins/Sid/SidInstrument.cpp +++ b/plugins/Sid/SidInstrument.cpp @@ -297,12 +297,10 @@ static int sid_fillbuffer(unsigned char* sidreg, SID *sid, int tdelta, short *pt void SidInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { - const f_cnt_t tfp = _n->totalFramesPlayed(); - const int clockrate = C64_PAL_CYCLES_PER_SEC; const int samplerate = Engine::audioEngine()->processingSampleRate(); - if ( tfp == 0 ) + if (!_n->m_pluginData) { SID *sid = new SID(); sid->set_sampling_parameters( clockrate, SAMPLE_FAST, samplerate ); diff --git a/plugins/Stk/Mallets/Mallets.cpp b/plugins/Stk/Mallets/Mallets.cpp index 8aa71d62e..be144e764 100644 --- a/plugins/Stk/Mallets/Mallets.cpp +++ b/plugins/Stk/Mallets/Mallets.cpp @@ -286,7 +286,7 @@ void MalletsInstrument::playNote( NotePlayHandle * _n, int p = m_presetsModel.value(); const float freq = _n->frequency(); - if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == nullptr ) + if (!_n->m_pluginData) { // If newer projects, adjust velocity to within stk's limits float velocityAdjust = diff --git a/plugins/TripleOscillator/TripleOscillator.cpp b/plugins/TripleOscillator/TripleOscillator.cpp index 25baea208..c66247541 100644 --- a/plugins/TripleOscillator/TripleOscillator.cpp +++ b/plugins/TripleOscillator/TripleOscillator.cpp @@ -308,7 +308,7 @@ QString TripleOscillator::nodeName() const void TripleOscillator::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { - if( _n->totalFramesPlayed() == 0 || _n->m_pluginData == nullptr ) + if (!_n->m_pluginData) { auto oscs_l = std::array{}; auto oscs_r = std::array{}; diff --git a/plugins/Vibed/CMakeLists.txt b/plugins/Vibed/CMakeLists.txt index 39e2ce57c..1cffeccfc 100644 --- a/plugins/Vibed/CMakeLists.txt +++ b/plugins/Vibed/CMakeLists.txt @@ -1,3 +1,6 @@ INCLUDE(BuildPlugin) -BUILD_PLUGIN(vibedstrings Vibed.cpp NineButtonSelector.cpp StringContainer.cpp VibratingString.cpp Vibed.h NineButtonSelector.h StringContainer.h VibratingString.h MOCFILES Vibed.h NineButtonSelector.h EMBEDDED_RESOURCES *.png) +BUILD_PLUGIN(vibedstrings Vibed.cpp NineButtonSelector.cpp VibratingString.cpp + Vibed.h NineButtonSelector.h VibratingString.h + MOCFILES Vibed.h NineButtonSelector.h + EMBEDDED_RESOURCES *.png) diff --git a/plugins/Vibed/NineButtonSelector.cpp b/plugins/Vibed/NineButtonSelector.cpp index 431dbec8b..3a68e5ee6 100644 --- a/plugins/Vibed/NineButtonSelector.cpp +++ b/plugins/Vibed/NineButtonSelector.cpp @@ -3,7 +3,7 @@ * * Copyright (c) 2006-2007 Danny McRae * Copyright (c) 2009 Tobias Doerffel - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -23,237 +23,69 @@ * */ - #include "NineButtonSelector.h" #include "CaptionMenu.h" -#include "PixmapButton.h" namespace lmms::gui { -NineButtonSelector::NineButtonSelector( QPixmap _button0_on, - QPixmap _button0_off, - QPixmap _button1_on, - QPixmap _button1_off, - QPixmap _button2_on, - QPixmap _button2_off, - QPixmap _button3_on, - QPixmap _button3_off, - QPixmap _button4_on, - QPixmap _button4_off, - QPixmap _button5_on, - QPixmap _button5_off, - QPixmap _button6_on, - QPixmap _button6_off, - QPixmap _button7_on, - QPixmap _button7_off, - QPixmap _button8_on, - QPixmap _button8_off, - int _default, - int _x, int _y, - QWidget * _parent ): - QWidget( _parent ), - IntModelView( new NineButtonSelectorModel(0, 8, _default, nullptr, - QString(), true ), this ) +NineButtonSelector::NineButtonSelector(std::array onOffIcons, int defaultButton, int x, int y, QWidget* parent) : + QWidget{parent}, + IntModelView{new NineButtonSelectorModel{defaultButton, 0, 8, nullptr, QString{}, true}, this} { - setFixedSize( 50, 50 ); - move( _x, _y ); + setFixedSize(50, 50); + move(x, y); - m_button = new PixmapButton( this, nullptr ); - m_button->move( 1, 1 ); - m_button->setActiveGraphic( _button0_on ); - m_button->setInactiveGraphic( _button0_off ); - m_button->setChecked( false ); - connect( m_button, SIGNAL ( clicked () ), - this, SLOT ( button0Clicked() ) ); - m_buttons.append( m_button ); - - m_button = new PixmapButton( this, nullptr ); - m_button->move( 18, 1 ); - m_button->setActiveGraphic( _button1_on ); - m_button->setInactiveGraphic( _button1_off ); - m_button->setChecked( false ); - connect( m_button, SIGNAL ( clicked () ), - this, SLOT ( button1Clicked() ) ); - m_buttons.append( m_button ); - - m_button = new PixmapButton( this, nullptr ); - m_button->move( 35, 1 ); - m_button->setActiveGraphic( _button2_on ); - m_button->setInactiveGraphic( _button2_off ); - m_button->setChecked( false ); - connect( m_button, SIGNAL ( clicked () ), - this, SLOT ( button2Clicked() ) ); - m_buttons.append( m_button ); - - m_button = new PixmapButton( this, nullptr ); - m_button->move( 1, 18 ); - m_button->setActiveGraphic( _button3_on ); - m_button->setInactiveGraphic( _button3_off ); - m_button->setChecked( false ); - connect( m_button, SIGNAL ( clicked () ), - this, SLOT ( button3Clicked() ) ); - m_buttons.append( m_button ); - - m_button = new PixmapButton( this, nullptr ); - m_button->move( 18, 18 ); - m_button->setActiveGraphic( _button4_on ); - m_button->setInactiveGraphic( _button4_off ); - m_button->setChecked( false ); - connect( m_button, SIGNAL ( clicked () ), - this, SLOT ( button4Clicked() ) ); - m_buttons.append( m_button ); - - m_button = new PixmapButton( this, nullptr ); - m_button->move( 35, 18 ); - m_button->setActiveGraphic( _button5_on ); - m_button->setInactiveGraphic( _button5_off ); - m_button->setChecked( false ); - connect( m_button, SIGNAL ( clicked () ), - this, SLOT ( button5Clicked() ) ); - m_buttons.append( m_button ); - - m_button = new PixmapButton( this, nullptr ); - m_button->move( 1, 35 ); - m_button->setActiveGraphic( _button6_on ); - m_button->setInactiveGraphic( _button6_off ); - m_button->setChecked( false ); - connect( m_button, SIGNAL ( clicked () ), - this, SLOT ( button6Clicked() ) ); - m_buttons.append( m_button ); - - m_button = new PixmapButton( this, nullptr ); - m_button->move( 18, 35 ); - m_button->setActiveGraphic( _button7_on ); - m_button->setInactiveGraphic( _button7_off ); - m_button->setChecked( false ); - connect( m_button, SIGNAL ( clicked () ), - this, SLOT ( button7Clicked() ) ); - m_buttons.append( m_button ); - - m_button = new PixmapButton( this, nullptr ); - m_button->move( 35, 35 ); - m_button->setActiveGraphic( _button8_on ); - m_button->setInactiveGraphic( _button8_off ); - m_button->setChecked( false ); - connect( m_button, SIGNAL ( clicked () ), - this, SLOT ( button8Clicked() ) ); - m_buttons.append( m_button ); - - m_lastBtn = m_buttons[_default]; - m_lastBtn->setChecked( true ); -} - - -NineButtonSelector::~ NineButtonSelector() -{ - for( int i = 0; i < 9; i++ ) + for (int i = 0; i < 9; ++i) { - delete m_buttons[i]; + m_buttons[i] = std::make_unique(this, nullptr); + const int buttonX = 1 + (i % 3) * 17; + const int buttonY = 1 + (i / 3) * 17; + m_buttons[i]->move(buttonX, buttonY); + m_buttons[i]->setActiveGraphic(onOffIcons[i * 2]); + m_buttons[i]->setInactiveGraphic(onOffIcons[(i * 2) + 1]); + m_buttons[i]->setChecked(false); + connect(m_buttons[i].get(), &PixmapButton::clicked, this, [=](){ this->buttonClicked(i); }); } + + m_lastBtn = m_buttons[defaultButton].get(); + m_lastBtn->setChecked(true); } - - - -void NineButtonSelector::button0Clicked() +void NineButtonSelector::buttonClicked(int id) { - setSelected( 0 ); -} - - - - -void NineButtonSelector::button1Clicked() -{ - setSelected( 1 ); -} - - - - -void NineButtonSelector::button2Clicked() -{ - setSelected( 2 ); -} - - - - -void NineButtonSelector::button3Clicked() -{ - setSelected( 3 ); -} - - - - -void NineButtonSelector::button4Clicked() -{ - setSelected( 4 ); -} - - - - -void NineButtonSelector::button5Clicked() -{ - setSelected( 5 ); -} - - - - -void NineButtonSelector::button6Clicked() -{ - setSelected( 6 ); -} - - - - -void NineButtonSelector::button7Clicked() -{ - setSelected( 7 ); -} - - - - -void NineButtonSelector::button8Clicked() -{ - setSelected( 8 ); + setSelected(id); } void NineButtonSelector::modelChanged() { - updateButton( model()->value() ); + updateButton(model()->value()); } -void NineButtonSelector::setSelected( int _new_button ) +void NineButtonSelector::setSelected(int newButton) { - model()->setValue(_new_button); - updateButton( _new_button ); + model()->setValue(newButton); + updateButton(newButton); } -void NineButtonSelector::updateButton( int _new_button ) +void NineButtonSelector::updateButton(int newButton) { - m_lastBtn->setChecked( false ); + m_lastBtn->setChecked(false); m_lastBtn->update(); - m_lastBtn = m_buttons[_new_button]; - m_lastBtn->setChecked( true ); + m_lastBtn = m_buttons[newButton].get(); + m_lastBtn->setChecked(true); m_lastBtn->update(); - - emit NineButtonSelection( _new_button ); + + emit NineButtonSelection(newButton); } -void NineButtonSelector::contextMenuEvent( QContextMenuEvent * ) +void NineButtonSelector::contextMenuEvent(QContextMenuEvent*) { - CaptionMenu contextMenu( windowTitle(), this ); - contextMenu.exec( QCursor::pos() ); + CaptionMenu contextMenu{windowTitle(), this}; + contextMenu.exec(QCursor::pos()); } diff --git a/plugins/Vibed/NineButtonSelector.h b/plugins/Vibed/NineButtonSelector.h index 9c768e996..78ae7c3d1 100644 --- a/plugins/Vibed/NineButtonSelector.h +++ b/plugins/Vibed/NineButtonSelector.h @@ -3,7 +3,7 @@ * * Copyright (c) 2006-2007 Danny McRae * Copyright (c) 2009 Tobias Doerffel - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -22,87 +22,56 @@ * Boston, MA 02110-1301 USA. * */ -#ifndef _NINE_BUTTON_SELECTOR_H -#define _NINE_BUTTON_SELECTOR_H +#ifndef LMMS_GUI_NINE_BUTTON_SELECTOR_H +#define LMMS_GUI_NINE_BUTTON_SELECTOR_H + +#include +#include #include + #include "AutomatableModelView.h" +#include "PixmapButton.h" namespace lmms { -class graphModel; -} -namespace lmms::gui + +namespace gui { -class Knob; -class PixmapButton; - - -class NineButtonSelector: public QWidget , public IntModelView +class NineButtonSelector : public QWidget, public IntModelView { Q_OBJECT - public: - NineButtonSelector( QPixmap _button0_on, - QPixmap _button0_off, - QPixmap _button1_on, - QPixmap _button1_off, - QPixmap _button2_on, - QPixmap _button2_off, - QPixmap _button3_on, - QPixmap _button3_off, - QPixmap _button4_on, - QPixmap _button4_off, - QPixmap _button5_on, - QPixmap _button5_off, - QPixmap _button6_on, - QPixmap _button6_off, - QPixmap _button7_on, - QPixmap _button7_off, - QPixmap _button8_on, - QPixmap _button8_off, - int _default, - int _x, int _y, - QWidget * _parent); - ~NineButtonSelector() override; - -// inline int getSelected() { -// return( castModel()->value() ); -// }; + NineButtonSelector(std::array onOffIcons, int defaultButton, int x, int y, QWidget* parent); + ~NineButtonSelector() override = default; protected: - void setSelected( int _new_button ); - + void setSelected(int newButton); + public slots: - void button0Clicked(); - void button1Clicked(); - void button2Clicked(); - void button3Clicked(); - void button4Clicked(); - void button5Clicked(); - void button6Clicked(); - void button7Clicked(); - void button8Clicked(); - void contextMenuEvent( QContextMenuEvent * ) override; - + void buttonClicked(int id); + void contextMenuEvent(QContextMenuEvent*) override; + signals: - void NineButtonSelection( int ); - + void NineButtonSelection(int); + private: void modelChanged() override; - void updateButton( int ); + void updateButton(int); - QList m_buttons; - PixmapButton * m_button; - PixmapButton * m_lastBtn; + std::array, 9> m_buttons; + PixmapButton* m_lastBtn; +}; -} ; + +} // namespace gui using NineButtonSelectorModel = IntModel; -} // namespace lmms::gui -#endif +} // namespace lmms + +#endif // LMMS_GUI_NINE_BUTTON_SELECTOR_H diff --git a/plugins/Vibed/StringContainer.cpp b/plugins/Vibed/StringContainer.cpp deleted file mode 100644 index 36dee6da0..000000000 --- a/plugins/Vibed/StringContainer.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * StringContainer.cpp - contains a collection of strings - * - * Copyright (c) 2006 Danny McRae - * - * This file is part of LMMS - https://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#include "StringContainer.h" - -namespace lmms -{ - - -StringContainer::StringContainer(const float _pitch, - const sample_rate_t _sample_rate, - const int _buffer_length, - const int _strings ) : - m_pitch( _pitch ), - m_sampleRate( _sample_rate ), - m_bufferLength( _buffer_length ) -{ - for( int i = 0; i < _strings; i++ ) - { - m_exists.append( false ); - } -} - - - - -void StringContainer::addString(int _harm, - const float _pick, - const float _pickup, - const float * _impulse, - const float _randomize, - const float _string_loss, - const float _detune, - const int _oversample, - const bool _state, - const int _id ) -{ - float harm; - switch( _harm ) - { - case 0: - harm = 0.25f; - break; - case 1: - harm = 0.5f; - break; - case 2: - harm = 1.0f; - break; - case 3: - harm = 2.0f; - break; - case 4: - harm = 3.0f; - break; - case 5: - harm = 4.0f; - break; - case 6: - harm = 5.0f; - break; - case 7: - harm = 6.0f; - break; - case 8: - harm = 7.0f; - break; - default: - harm = 1.0f; - } - - m_strings.append( new VibratingString( m_pitch * harm, - _pick, - _pickup, - const_cast(_impulse), - m_bufferLength, - m_sampleRate, - _oversample, - _randomize, - _string_loss, - _detune, - _state ) ); - m_exists[_id] = true; -} - - -} // namespace lmms \ No newline at end of file diff --git a/plugins/Vibed/StringContainer.h b/plugins/Vibed/StringContainer.h deleted file mode 100644 index 96408c56a..000000000 --- a/plugins/Vibed/StringContainer.h +++ /dev/null @@ -1,86 +0,0 @@ -/* StringContainer.h - contains a collection of strings - * - * Copyright (c) 2006 Danny McRae - * - * This file is part of LMMS - https://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#ifndef _STRING_CONTAINER_H -#define _STRING_CONTAINER_H - -#include - -#include "VibratingString.h" -#include "MemoryManager.h" - -namespace lmms -{ - - -class StringContainer -{ - MM_OPERATORS -public: - StringContainer(const float _pitch, - const sample_rate_t _sample_rate, - const int _buffer_length, - const int _strings = 9 ); - - void addString( int _harm, - const float _pick, - const float _pickup, - const float * _impluse, - const float _randomize, - const float _string_loss, - const float _detune, - const int _oversample, - const bool _state, - const int _id ); - - bool exists( int _id ) const - { - return m_exists[_id]; - } - - ~StringContainer() - { - int strings = m_strings.count(); - for( int i = 0; i < strings; i++ ) - { - delete m_strings[i]; - } - } - - float getStringSample( int _string ) - { - return m_strings[_string]->nextSample(); - } - -private: - QVector m_strings; - const float m_pitch; - const sample_rate_t m_sampleRate; - const int m_bufferLength; - QVector m_exists; -} ; - - -} // namespace lmms - -#endif diff --git a/plugins/Vibed/Vibed.cpp b/plugins/Vibed/Vibed.cpp index 9f119aa6e..014ab1429 100644 --- a/plugins/Vibed/Vibed.cpp +++ b/plugins/Vibed/Vibed.cpp @@ -22,21 +22,20 @@ * */ +#include "Vibed.h" +#include +#include #include -#include "Vibed.h" #include "AudioEngine.h" #include "Engine.h" -#include "Graph.h" #include "InstrumentTrack.h" -#include "Knob.h" -#include "LedCheckBox.h" #include "NotePlayHandle.h" -#include "PixmapButton.h" +#include "VibratingString.h" +#include "MemoryManager.h" #include "base64.h" #include "CaptionMenu.h" -#include "StringContainer.h" #include "volume.h" #include "Song.h" @@ -52,628 +51,472 @@ extern "C" Plugin::Descriptor PLUGIN_EXPORT vibedstrings_plugin_descriptor = { - LMMS_STRINGIFY( PLUGIN_NAME ), + LMMS_STRINGIFY(PLUGIN_NAME), "Vibed", - QT_TRANSLATE_NOOP( "PluginBrowser", - "Vibrating string modeler" ), + QT_TRANSLATE_NOOP("PluginBrowser", "Vibrating string modeler"), "Danny McRae ", 0x0100, Plugin::Instrument, - new PluginPixmapLoader( "logo" ), - nullptr, + new PluginPixmapLoader("logo"), nullptr, + nullptr }; } -Vibed::Vibed( InstrumentTrack * _instrumentTrack ) : - Instrument( _instrumentTrack, &vibedstrings_plugin_descriptor ) +class Vibed::StringContainer { + MM_OPERATORS +public: + StringContainer(float pitch, sample_rate_t sampleRate, int bufferLength) : + m_pitch(pitch), m_sampleRate(sampleRate), m_bufferLength(bufferLength) {} - FloatModel * knob; - BoolModel * led; - gui::NineButtonSelectorModel * harmonic; - graphModel * graphTmp; + ~StringContainer() = default; - for( int harm = 0; harm < 9; harm++ ) + void addString(int harm, float pick, float pickup, const float* impulse, float randomize, + float stringLoss, float detune, int oversample, bool state, int id) { - knob = new FloatModel( DefaultVolume, MinVolume, MaxVolume, - 1.0f, this, tr( "String %1 volume" ).arg( harm+1 ) ); - m_volumeKnobs.append( knob ); + constexpr auto octave = std::array{0.25f, 0.5f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f}; + assert(harm >= 0 && harm < octave.size()); - knob = new FloatModel( 0.0f, 0.0f, 0.05f, 0.001f, this, - tr( "String %1 stiffness" ).arg( harm+1 ) ); - m_stiffnessKnobs.append( knob ); + m_strings[id] = VibratingString{m_pitch * octave[harm], pick, pickup, impulse, m_bufferLength, + m_sampleRate, oversample, randomize, stringLoss, detune, state}; - knob = new FloatModel( 0.0f, 0.0f, 0.05f, 0.005f, this, - tr( "Pick %1 position" ).arg( harm+1 ) ); - m_pickKnobs.append( knob ); + m_exists[id] = true; + } - knob = new FloatModel( 0.05f, 0.0f, 0.05f, 0.005f, this, - tr( "Pickup %1 position" ).arg( harm+1 ) ); - m_pickupKnobs.append( knob ); + bool exists(int id) const { return m_exists[id]; } - knob = new FloatModel( 0.0f, -1.0f, 1.0f, 0.01f, this, - tr( "String %1 panning" ).arg( harm+1 ) ); - m_panKnobs.append( knob ); + sample_t getStringSample(int id) { return m_strings[id].nextSample(); } - knob = new FloatModel( 0.0f, -0.1f, 0.1f, 0.001f, this, - tr( "String %1 detune" ).arg( harm+1 ) ); - m_detuneKnobs.append( knob ); +private: + const float m_pitch; + const sample_rate_t m_sampleRate; + const int m_bufferLength; + std::array m_strings{}; + std::array m_exists{}; +}; - knob = new FloatModel( 0.0f, 0.0f, 0.75f, 0.01f, this, - tr( "String %1 fuzziness" ).arg( harm+1 ) ); - m_randomKnobs.append( knob ); - - knob = new FloatModel( 1, 1, 16, 1, this, - tr( "String %1 length" ).arg( harm+1 ) ); - m_lengthKnobs.append( knob ); - - led = new BoolModel( false, this, - tr( "Impulse %1" ).arg( harm+1 ) ); - m_impulses.append( led ); - - led = new BoolModel( harm==0, this, - tr( "String %1" ).arg( harm+1 ) ); - m_powerButtons.append( led ); - - harmonic = new gui::NineButtonSelectorModel( 2, 0, 8, this ); - m_harmonics.append( harmonic ); - - graphTmp = new graphModel( -1.0, 1.0, __sampleLength, this ); - graphTmp->setWaveToSine(); - - m_graphs.append( graphTmp ); +Vibed::Vibed(InstrumentTrack* instrumentTrack) : + Instrument(instrumentTrack, &vibedstrings_plugin_descriptor) +{ + for (int harm = 0; harm < s_stringCount; ++harm) + { + m_volumeModels[harm] = std::make_unique( + DefaultVolume, MinVolume, MaxVolume, 1.0f, this, tr("String %1 volume").arg(harm + 1)); + m_stiffnessModels[harm] = std::make_unique( + 0.0f, 0.0f, 0.05f, 0.001f, this, tr("String %1 stiffness").arg(harm + 1)); + m_pickModels[harm] = std::make_unique( + 0.0f, 0.0f, 0.05f, 0.005f, this, tr("Pick %1 position").arg(harm + 1)); + m_pickupModels[harm] = std::make_unique( + 0.05f, 0.0f, 0.05f, 0.005f, this, tr("Pickup %1 position").arg( harm + 1)); + m_panModels[harm] = std::make_unique( + 0.0f, -1.0f, 1.0f, 0.01f, this, tr("String %1 panning").arg(harm + 1)); + m_detuneModels[harm] = std::make_unique( + 0.0f, -0.1f, 0.1f, 0.001f, this, tr("String %1 detune").arg(harm + 1)); + m_randomModels[harm] = std::make_unique( + 0.0f, 0.0f, 0.75f, 0.01f, this, tr("String %1 fuzziness").arg(harm + 1)); + m_lengthModels[harm] = std::make_unique( + 1, 1, 16, 1, this, tr("String %1 length").arg(harm + 1)); + m_impulseModels[harm] = std::make_unique(false, this, tr("Impulse %1").arg(harm + 1)); + m_powerModels[harm] = std::make_unique(harm == 0, this, tr("String %1").arg(harm + 1)); + m_harmonicModels[harm] = std::make_unique(2, 0, 8, this); + m_graphModels[harm] = std::make_unique(-1.0, 1.0, s_sampleLength, this); + m_graphModels[harm]->setWaveToSine(); } } - - - -void Vibed::saveSettings( QDomDocument & _doc, QDomElement & _this ) +void Vibed::saveSettings(QDomDocument& doc, QDomElement& elem) { - - QString name; - // Save plugin version - _this.setAttribute( "version", "0.1" ); - - for( int i = 0; i < 9; i++ ) + elem.setAttribute("version", "0.2"); + + for (int i = 0; i < s_stringCount; ++i) { - name = "active" + QString::number( i ); - _this.setAttribute( name, QString::number( - m_powerButtons[i]->value() ) ); + const auto is = QString::number(i); - if( m_powerButtons[i]->value() ) - { - name = "volume" + QString::number( i ); - m_volumeKnobs[i]->saveSettings( _doc, _this, name ); - - name = "stiffness" + QString::number( i ); - m_stiffnessKnobs[i]->saveSettings( _doc, _this, name ); + elem.setAttribute("active" + is, QString::number(m_powerModels[i]->value())); - name = "pick" + QString::number( i ); - m_pickKnobs[i]->saveSettings( _doc, _this, name ); + m_volumeModels[i]->saveSettings(doc, elem, "volume" + is); + m_stiffnessModels[i]->saveSettings(doc, elem, "stiffness" + is); + m_pickModels[i]->saveSettings(doc, elem, "pick" + is); + m_pickupModels[i]->saveSettings(doc, elem, "pickup" + is); + m_harmonicModels[i]->saveSettings(doc, elem, "octave" + is); + m_lengthModels[i]->saveSettings(doc, elem, "length" + is); + m_panModels[i]->saveSettings(doc, elem, "pan" + is); + m_detuneModels[i]->saveSettings(doc, elem, "detune" + is); + m_randomModels[i]->saveSettings(doc, elem, "slap" + is); + m_impulseModels[i]->saveSettings(doc, elem, "impulse" + is); - name = "pickup" + QString::number( i ); - m_pickupKnobs[i]->saveSettings( _doc, _this, name ); + QString sampleString; + base64::encode((const char*)m_graphModels[i]->samples(), s_sampleLength * sizeof(float), sampleString); - name = "octave" + QString::number( i ); - m_harmonics[i]->saveSettings( _doc, _this, name ); - - name = "length" + QString::number( i ); - m_lengthKnobs[i]->saveSettings( _doc, _this, name ); - - name = "pan" + QString::number( i ); - m_panKnobs[i]->saveSettings( _doc, _this, name ); - - name = "detune" + QString::number( i ); - m_detuneKnobs[i]->saveSettings( _doc, _this, name ); - - name = "slap" + QString::number( i ); - m_randomKnobs[i]->saveSettings( _doc, _this, name ); - - name = "impulse" + QString::number( i ); - m_impulses[i]->saveSettings( _doc, _this, name ); - - QString sampleString; - base64::encode( - (const char *)m_graphs[i]->samples(), - __sampleLength * sizeof(float), - sampleString ); - name = "graph" + QString::number( i ); - _this.setAttribute( name, sampleString ); - } + elem.setAttribute("graph" + is, sampleString); } - } - - -void Vibed::loadSettings( const QDomElement & _this ) +void Vibed::loadSettings(const QDomElement& elem) { - - QString name; - - for( int i = 0; i < 9; i++ ) + // Load plugin version + bool newVersion = false; + if (elem.hasAttribute("version")) { - name = "active" + QString::number( i ); - m_powerButtons[i]->setValue( _this.attribute( name ).toInt() ); - - if( m_powerButtons[i]->value() && - _this.hasAttribute( "volume" + QString::number( i ) ) ) + newVersion = elem.attribute("version").toFloat() >= 0.2f; + } + + for (int i = 0; i < s_stringCount; ++i) + { + const auto is = QString::number(i); + + m_powerModels[i]->setValue(elem.attribute("active" + is).toInt()); + + // Version 0.2 saves/loads all instrument data unconditionally + const bool hasVolumeAttr = elem.hasAttribute("volume" + is) || !elem.firstChildElement("volume" + is).isNull(); + if (newVersion || (m_powerModels[i]->value() && hasVolumeAttr)) { - name = "volume" + QString::number( i ); - m_volumeKnobs[i]->loadSettings( _this, name ); - - name = "stiffness" + QString::number( i ); - m_stiffnessKnobs[i]->loadSettings( _this, name ); - - name = "pick" + QString::number( i ); - m_pickKnobs[i]->loadSettings( _this, name ); - - name = "pickup" + QString::number( i ); - m_pickupKnobs[i]->loadSettings( _this, name ); - - name = "octave" + QString::number( i ); - m_harmonics[i]->loadSettings( _this, name ); - - name = "length" + QString::number( i ); - m_lengthKnobs[i]->loadSettings( _this, name ); - - name = "pan" + QString::number( i ); - m_panKnobs[i]->loadSettings( _this, name ); - - name = "detune" + QString::number( i ); - m_detuneKnobs[i]->loadSettings( _this, name ); - - name = "slap" + QString::number( i ); - m_randomKnobs[i]->loadSettings( _this, name ); - - name = "impulse" + QString::number( i ); - m_impulses[i]->loadSettings( _this, name ); + m_volumeModels[i]->loadSettings(elem, "volume" + is); + m_stiffnessModels[i]->loadSettings(elem, "stiffness" + is); + m_pickModels[i]->loadSettings(elem, "pick" + is); + m_pickupModels[i]->loadSettings(elem, "pickup" + is); + m_harmonicModels[i]->loadSettings(elem, "octave" + is); + m_lengthModels[i]->loadSettings(elem, "length" + is); + m_panModels[i]->loadSettings(elem, "pan" + is); + m_detuneModels[i]->loadSettings(elem, "detune" + is); + m_randomModels[i]->loadSettings(elem, "slap" + is); + m_impulseModels[i]->loadSettings(elem, "impulse" + is); int size = 0; - float * shp = 0; - base64::decode( _this.attribute( "graph" + - QString::number( i ) ), - &shp, - &size ); - // TODO: check whether size == 128 * sizeof( float ), - // otherwise me might and up in a segfault - m_graphs[i]->setSamples( shp ); - delete[] shp; - + float* shp = nullptr; + base64::decode(elem.attribute("graph" + is), &shp, &size); - // TODO: do one of the following to avoid - // "uninitialized" wave-shape-buttongroup - // - activate random-wave-shape-button here - // - make wave-shape-buttons simple toggle-buttons - // instead of checkable buttons - // - save and restore selected wave-shape-button + assert(size == 128 * sizeof(float)); + m_graphModels[i]->setSamples(shp); + delete[] shp; } } - -// update(); } - - - QString Vibed::nodeName() const { - return( vibedstrings_plugin_descriptor.name ); + return vibedstrings_plugin_descriptor.name; } - - - -void Vibed::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) +void Vibed::playNote(NotePlayHandle* n, sampleFrame* workingBuffer) { - if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == nullptr ) + if (!n->m_pluginData) { - _n->m_pluginData = new StringContainer( _n->frequency(), - Engine::audioEngine()->processingSampleRate(), - __sampleLength ); - - for( int i = 0; i < 9; ++i ) + const auto newContainer = new StringContainer{n->frequency(), + Engine::audioEngine()->processingSampleRate(), s_sampleLength}; + + n->m_pluginData = newContainer; + + for (int i = 0; i < s_stringCount; ++i) { - if( m_powerButtons[i]->value() ) + if (m_powerModels[i]->value()) { - static_cast( - _n->m_pluginData )->addString( - m_harmonics[i]->value(), - m_pickKnobs[i]->value(), - m_pickupKnobs[i]->value(), - m_graphs[i]->samples(), - m_randomKnobs[i]->value(), - m_stiffnessKnobs[i]->value(), - m_detuneKnobs[i]->value(), - static_cast( - m_lengthKnobs[i]->value() ), - m_impulses[i]->value(), - i ); + newContainer->addString( + m_harmonicModels[i]->value(), + m_pickModels[i]->value(), + m_pickupModels[i]->value(), + m_graphModels[i]->samples(), + m_randomModels[i]->value(), + m_stiffnessModels[i]->value(), + m_detuneModels[i]->value(), + static_cast(m_lengthModels[i]->value()), + m_impulseModels[i]->value(), + i); } } } - const fpp_t frames = _n->framesLeftForCurrentPeriod(); - const f_cnt_t offset = _n->noteOffset(); - auto ps = static_cast(_n->m_pluginData); + const fpp_t frames = n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = n->noteOffset(); + auto ps = static_cast(n->m_pluginData); - for( fpp_t i = offset; i < frames + offset; ++i ) + for (fpp_t i = offset; i < frames + offset; ++i) { - _working_buffer[i][0] = 0.0f; - _working_buffer[i][1] = 0.0f; - int s = 0; - for( int string = 0; string < 9; ++string ) + workingBuffer[i][0] = 0.0f; + workingBuffer[i][1] = 0.0f; + for (int str = 0; str < s_stringCount; ++str) { - if( ps->exists( string ) ) + if (ps->exists(str)) { // pan: 0 -> left, 1 -> right - const float pan = ( m_panKnobs[string]->value() + 1 ) / 2.0f; - const sample_t sample = ps->getStringSample( s ) * m_volumeKnobs[string]->value() / 100.0f; - _working_buffer[i][0] += ( 1.0f - pan ) * sample; - _working_buffer[i][1] += pan * sample; - s++; + const float pan = (m_panModels[str]->value() + 1) / 2.0f; + const sample_t sample = ps->getStringSample(str) * m_volumeModels[str]->value() / 100.0f; + workingBuffer[i][0] += (1.0f - pan) * sample; + workingBuffer[i][1] += pan * sample; } } } - instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); + instrumentTrack()->processAudioBuffer(workingBuffer, frames + offset, n); } - - - -void Vibed::deleteNotePluginData( NotePlayHandle * _n ) +void Vibed::deleteNotePluginData(NotePlayHandle* n) { - delete static_cast( _n->m_pluginData ); + delete static_cast(n->m_pluginData); } - - - -gui::PluginView * Vibed::instantiateView( QWidget * _parent ) +gui::PluginView* Vibed::instantiateView(QWidget* parent) { - return( new gui::VibedView( this, _parent ) ); + return new gui::VibedView(this, parent); } - namespace gui { -VibedView::VibedView( Instrument * _instrument, - QWidget * _parent ) : - InstrumentViewFixedSize( _instrument, _parent ) +VibedView::VibedView(Instrument* instrument, QWidget* parent) : + InstrumentViewFixedSize(instrument, parent), + m_volumeKnob(knobBright_26, this), + m_stiffnessKnob(knobBright_26, this), + m_pickKnob(knobBright_26, this), + m_pickupKnob(knobBright_26, this), + m_panKnob(knobBright_26, this), + m_detuneKnob(knobBright_26, this), + m_randomKnob(knobBright_26, this), + m_lengthKnob(knobBright_26, this), + m_graph(this), + m_impulse("", this), + m_power("", this, tr("Enable waveform")), + m_smoothBtn(this, tr("Smooth waveform")), + m_normalizeBtn(this, tr("Normalize waveform")), + m_sinWaveBtn(this, tr("Sine wave")), + m_triangleWaveBtn(this, tr("Triangle wave")), + m_sawWaveBtn(this, tr("Saw wave")), + m_sqrWaveBtn(this, tr("Square wave")), + m_whiteNoiseWaveBtn(this, tr("White noise")), + m_usrWaveBtn(this, tr("User-defined wave")) { - setAutoFillBackground( true ); + setAutoFillBackground(true); QPalette pal; - pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( - "artwork" ) ); - setPalette( pal ); - - m_volumeKnob = new Knob( knobBright_26, this ); - m_volumeKnob->setVolumeKnob( true ); - m_volumeKnob->move( 103, 142 ); - m_volumeKnob->setHintText( tr( "String volume:" ), "" ); + pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); + setPalette(pal); - m_stiffnessKnob = new Knob( knobBright_26, this ); - m_stiffnessKnob->move( 129, 142 ); - m_stiffnessKnob->setHintText( tr( "String stiffness:" ) - , "" ); - - - m_pickKnob = new Knob( knobBright_26, this ); - m_pickKnob->move( 153, 142 ); - m_pickKnob->setHintText( tr( "Pick position:" ), "" ); + m_volumeKnob.setVolumeKnob(true); + m_volumeKnob.move(103, 142); + m_volumeKnob.setHintText(tr("String volume:"), ""); - m_pickupKnob = new Knob( knobBright_26, this ); - m_pickupKnob->move( 177, 142 ); - m_pickupKnob->setHintText( tr( "Pickup position:" ) - , "" ); + m_stiffnessKnob.move(129, 142); + m_stiffnessKnob.setHintText(tr("String stiffness:"), ""); - m_panKnob = new Knob( knobBright_26, this ); - m_panKnob->move( 105, 187 ); - m_panKnob->setHintText( tr( "String panning:" ), "" ); - - m_detuneKnob = new Knob( knobBright_26, this ); - m_detuneKnob->move( 150, 187 ); - m_detuneKnob->setHintText( tr( "String detune:" ), "" ); + m_pickKnob.move(153, 142); + m_pickKnob.setHintText(tr("Pick position:"), ""); - m_randomKnob = new Knob( knobBright_26, this ); - m_randomKnob->move( 194, 187 ); - m_randomKnob->setHintText( tr( "String fuzziness:" ) - , "" ); + m_pickupKnob.move(177, 142); + m_pickupKnob.setHintText(tr("Pickup position:"), ""); - m_lengthKnob = new Knob( knobBright_26, this ); - m_lengthKnob->move( 23, 193 ); - m_lengthKnob->setHintText( tr( "String length:" ) - , "" ); + m_panKnob.move(105, 187); + m_panKnob.setHintText(tr("String panning:"), ""); - m_impulse = new LedCheckBox( "", this ); - m_impulse->move( 23, 94 ); - m_impulse->setToolTip( - tr( "Impulse" ) ); + m_detuneKnob.move(150, 187); + m_detuneKnob.setHintText(tr("String detune:"), ""); - m_harmonic = new NineButtonSelector( - PLUGIN_NAME::getIconPixmap( "button_-2_on" ), - PLUGIN_NAME::getIconPixmap( "button_-2_off" ), - PLUGIN_NAME::getIconPixmap( "button_-1_on" ), - PLUGIN_NAME::getIconPixmap( "button_-1_off" ), - PLUGIN_NAME::getIconPixmap( "button_f_on" ), - PLUGIN_NAME::getIconPixmap( "button_f_off" ), - PLUGIN_NAME::getIconPixmap( "button_2_on" ), - PLUGIN_NAME::getIconPixmap( "button_2_off" ), - PLUGIN_NAME::getIconPixmap( "button_3_on" ), - PLUGIN_NAME::getIconPixmap( "button_3_off" ), - PLUGIN_NAME::getIconPixmap( "button_4_on" ), - PLUGIN_NAME::getIconPixmap( "button_4_off" ), - PLUGIN_NAME::getIconPixmap( "button_5_on" ), - PLUGIN_NAME::getIconPixmap( "button_5_off" ), - PLUGIN_NAME::getIconPixmap( "button_6_on" ), - PLUGIN_NAME::getIconPixmap( "button_6_off" ), - PLUGIN_NAME::getIconPixmap( "button_7_on" ), - PLUGIN_NAME::getIconPixmap( "button_7_off" ), + m_randomKnob.move(194, 187); + m_randomKnob.setHintText(tr("String fuzziness:"), ""); + + m_lengthKnob.move(23, 193); + m_lengthKnob.setHintText(tr("String length:"), ""); + + m_graph.setWindowTitle(tr("Impulse Editor")); + m_graph.setForeground(PLUGIN_NAME::getIconPixmap("wavegraph4")); + m_graph.move(76, 21); + m_graph.resize(132, 104); + + m_impulse.move(23, 94); + m_impulse.setToolTip(tr("Impulse")); + + m_power.move(212, 130); + m_power.setToolTip(tr("Enable/disable string")); + + m_harmonic = std::make_unique( + std::array{ + PLUGIN_NAME::getIconPixmap("button_-2_on"), + PLUGIN_NAME::getIconPixmap("button_-2_off"), + PLUGIN_NAME::getIconPixmap("button_-1_on"), + PLUGIN_NAME::getIconPixmap("button_-1_off"), + PLUGIN_NAME::getIconPixmap("button_f_on"), + PLUGIN_NAME::getIconPixmap("button_f_off"), + PLUGIN_NAME::getIconPixmap("button_2_on"), + PLUGIN_NAME::getIconPixmap("button_2_off"), + PLUGIN_NAME::getIconPixmap("button_3_on"), + PLUGIN_NAME::getIconPixmap("button_3_off"), + PLUGIN_NAME::getIconPixmap("button_4_on"), + PLUGIN_NAME::getIconPixmap("button_4_off"), + PLUGIN_NAME::getIconPixmap("button_5_on"), + PLUGIN_NAME::getIconPixmap("button_5_off"), + PLUGIN_NAME::getIconPixmap("button_6_on"), + PLUGIN_NAME::getIconPixmap("button_6_off"), + PLUGIN_NAME::getIconPixmap("button_7_on"), + PLUGIN_NAME::getIconPixmap("button_7_off")}, 2, 21, 127, - this ); + this); - m_harmonic->setWindowTitle( tr( "Octave" ) ); - + m_harmonic->setWindowTitle(tr("Octave")); - m_stringSelector = new NineButtonSelector( - PLUGIN_NAME::getIconPixmap( "button_1_on" ), - PLUGIN_NAME::getIconPixmap( "button_1_off" ), - PLUGIN_NAME::getIconPixmap( "button_2_on" ), - PLUGIN_NAME::getIconPixmap( "button_2_off" ), - PLUGIN_NAME::getIconPixmap( "button_3_on" ), - PLUGIN_NAME::getIconPixmap( "button_3_off" ), - PLUGIN_NAME::getIconPixmap( "button_4_on" ), - PLUGIN_NAME::getIconPixmap( "button_4_off" ), - PLUGIN_NAME::getIconPixmap( "button_5_on" ), - PLUGIN_NAME::getIconPixmap( "button_5_off" ), - PLUGIN_NAME::getIconPixmap( "button_6_on" ), - PLUGIN_NAME::getIconPixmap( "button_6_off" ), - PLUGIN_NAME::getIconPixmap( "button_7_on" ), - PLUGIN_NAME::getIconPixmap( "button_7_off" ), - PLUGIN_NAME::getIconPixmap( "button_8_on" ), - PLUGIN_NAME::getIconPixmap( "button_8_off" ), - PLUGIN_NAME::getIconPixmap( "button_9_on" ), - PLUGIN_NAME::getIconPixmap( "button_9_off" ), - 0, - 21, 39, - this); + m_stringSelector = std::make_unique( + std::array{ + PLUGIN_NAME::getIconPixmap("button_1_on"), + PLUGIN_NAME::getIconPixmap("button_1_off"), + PLUGIN_NAME::getIconPixmap("button_2_on"), + PLUGIN_NAME::getIconPixmap("button_2_off"), + PLUGIN_NAME::getIconPixmap("button_3_on"), + PLUGIN_NAME::getIconPixmap("button_3_off"), + PLUGIN_NAME::getIconPixmap("button_4_on"), + PLUGIN_NAME::getIconPixmap("button_4_off"), + PLUGIN_NAME::getIconPixmap("button_5_on"), + PLUGIN_NAME::getIconPixmap("button_5_off"), + PLUGIN_NAME::getIconPixmap("button_6_on"), + PLUGIN_NAME::getIconPixmap("button_6_off"), + PLUGIN_NAME::getIconPixmap("button_7_on"), + PLUGIN_NAME::getIconPixmap("button_7_off"), + PLUGIN_NAME::getIconPixmap("button_8_on"), + PLUGIN_NAME::getIconPixmap("button_8_off"), + PLUGIN_NAME::getIconPixmap("button_9_on"), + PLUGIN_NAME::getIconPixmap("button_9_off")}, + 0, + 21, 39, + this); - m_graph = new Graph( this ); - m_graph->setWindowTitle( tr( "Impulse Editor" ) ); - m_graph->setForeground( PLUGIN_NAME::getIconPixmap( "wavegraph4" ) ); - m_graph->move( 76, 21 ); - m_graph->resize(132, 104); - - - m_power = new LedCheckBox( "", this, tr( "Enable waveform" ) ); - m_power->move( 212, 130 ); - m_power->setToolTip( - tr( "Enable/disable string" ) ); - - // String selector is not a part of the model - m_stringSelector->setWindowTitle( tr( "String" ) ); + m_stringSelector->setWindowTitle(tr("String")); - connect( m_stringSelector, SIGNAL( NineButtonSelection( int ) ), - this, SLOT( showString( int ) ) ); + connect(m_stringSelector.get(), SIGNAL(NineButtonSelection(int)), this, SLOT(showString(int))); - showString( 0 ); + showString(0); - m_sinWaveBtn = new PixmapButton( this, tr( "Sine wave" ) ); - m_sinWaveBtn->move( 212, 24 ); - m_sinWaveBtn->setActiveGraphic( embed::getIconPixmap( - "sin_wave_active" ) ); - m_sinWaveBtn->setInactiveGraphic( embed::getIconPixmap( - "sin_wave_inactive" ) ); - m_sinWaveBtn->setToolTip( - tr( "Sine wave" ) ); - connect( m_sinWaveBtn, SIGNAL (clicked () ), - this, SLOT ( sinWaveClicked() ) ); + m_smoothBtn.move(79, 129); + m_smoothBtn.setActiveGraphic(PLUGIN_NAME::getIconPixmap("smooth_active")); + m_smoothBtn.setInactiveGraphic(PLUGIN_NAME::getIconPixmap("smooth_inactive")); + m_smoothBtn.setChecked(false); + m_smoothBtn.setToolTip(tr("Smooth waveform")); + connect(&m_smoothBtn, SIGNAL(clicked()), this, SLOT(smoothClicked())); - - m_triangleWaveBtn = new PixmapButton( this, tr( "Triangle wave" ) ); - m_triangleWaveBtn->move( 212, 41 ); - m_triangleWaveBtn->setActiveGraphic( - embed::getIconPixmap( "triangle_wave_active" ) ); - m_triangleWaveBtn->setInactiveGraphic( - embed::getIconPixmap( "triangle_wave_inactive" ) ); - m_triangleWaveBtn->setToolTip( - tr( "Triangle wave" ) ); - connect( m_triangleWaveBtn, SIGNAL ( clicked () ), - this, SLOT ( triangleWaveClicked() ) ); + m_normalizeBtn.move(96, 129); + m_normalizeBtn.setActiveGraphic(PLUGIN_NAME::getIconPixmap("normalize_active")); + m_normalizeBtn.setInactiveGraphic(PLUGIN_NAME::getIconPixmap("normalize_inactive")); + m_normalizeBtn.setChecked(false); + m_normalizeBtn.setToolTip(tr("Normalize waveform")); + connect(&m_normalizeBtn, SIGNAL(clicked()), this, SLOT(normalizeClicked())); - - m_sawWaveBtn = new PixmapButton( this, tr( "Saw wave" ) ); - m_sawWaveBtn->move( 212, 58 ); - m_sawWaveBtn->setActiveGraphic( embed::getIconPixmap( - "saw_wave_active" ) ); - m_sawWaveBtn->setInactiveGraphic( embed::getIconPixmap( - "saw_wave_inactive" ) ); - m_sawWaveBtn->setToolTip( - tr( "Saw wave" ) ); - connect( m_sawWaveBtn, SIGNAL (clicked () ), - this, SLOT ( sawWaveClicked() ) ); + m_sinWaveBtn.move(212, 24); + m_sinWaveBtn.setActiveGraphic(embed::getIconPixmap("sin_wave_active")); + m_sinWaveBtn.setInactiveGraphic(embed::getIconPixmap("sin_wave_inactive")); + m_sinWaveBtn.setToolTip(tr("Sine wave")); + connect(&m_sinWaveBtn, SIGNAL(clicked()), this, SLOT(sinWaveClicked())); - - m_sqrWaveBtn = new PixmapButton( this, tr( "Square wave" ) ); - m_sqrWaveBtn->move( 212, 75 ); - m_sqrWaveBtn->setActiveGraphic( embed::getIconPixmap( - "square_wave_active" ) ); - m_sqrWaveBtn->setInactiveGraphic( embed::getIconPixmap( - "square_wave_inactive" ) ); - m_sqrWaveBtn->setToolTip( - tr( "Square wave" ) ); - connect( m_sqrWaveBtn, SIGNAL ( clicked () ), - this, SLOT ( sqrWaveClicked() ) ); + m_triangleWaveBtn.move(212, 41); + m_triangleWaveBtn.setActiveGraphic(embed::getIconPixmap("triangle_wave_active")); + m_triangleWaveBtn.setInactiveGraphic(embed::getIconPixmap("triangle_wave_inactive")); + m_triangleWaveBtn.setToolTip(tr("Triangle wave")); + connect(&m_triangleWaveBtn, SIGNAL(clicked()), this, SLOT(triangleWaveClicked())); - - m_whiteNoiseWaveBtn = new PixmapButton( this, tr( "White noise" ) ); - m_whiteNoiseWaveBtn->move( 212, 92 ); - m_whiteNoiseWaveBtn->setActiveGraphic( - embed::getIconPixmap( "white_noise_wave_active" ) ); - m_whiteNoiseWaveBtn->setInactiveGraphic( - embed::getIconPixmap( "white_noise_wave_inactive" ) ); - m_whiteNoiseWaveBtn->setToolTip( - tr( "White noise" ) ); - connect( m_whiteNoiseWaveBtn, SIGNAL ( clicked () ), - this, SLOT ( noiseWaveClicked() ) ); + m_sawWaveBtn.move(212, 58); + m_sawWaveBtn.setActiveGraphic(embed::getIconPixmap("saw_wave_active")); + m_sawWaveBtn.setInactiveGraphic(embed::getIconPixmap("saw_wave_inactive")); + m_sawWaveBtn.setToolTip(tr("Saw wave")); + connect(&m_sawWaveBtn, SIGNAL(clicked()), this, SLOT(sawWaveClicked())); - - m_usrWaveBtn = new PixmapButton( this, tr( "User-defined wave" ) ); - m_usrWaveBtn->move( 212, 109 ); - m_usrWaveBtn->setActiveGraphic( embed::getIconPixmap( - "usr_wave_active" ) ); - m_usrWaveBtn->setInactiveGraphic( embed::getIconPixmap( - "usr_wave_inactive" ) ); - m_usrWaveBtn->setToolTip( - tr( "User-defined wave" ) ); - connect( m_usrWaveBtn, SIGNAL ( clicked () ), - this, SLOT ( usrWaveClicked() ) ); + m_sqrWaveBtn.move(212, 75); + m_sqrWaveBtn.setActiveGraphic(embed::getIconPixmap("square_wave_active")); + m_sqrWaveBtn.setInactiveGraphic(embed::getIconPixmap("square_wave_inactive")); + m_sqrWaveBtn.setToolTip(tr("Square wave")); + connect(&m_sqrWaveBtn, SIGNAL(clicked()), this, SLOT(sqrWaveClicked())); + m_whiteNoiseWaveBtn.move(212, 92); + m_whiteNoiseWaveBtn.setActiveGraphic(embed::getIconPixmap("white_noise_wave_active")); + m_whiteNoiseWaveBtn.setInactiveGraphic(embed::getIconPixmap("white_noise_wave_inactive")); + m_whiteNoiseWaveBtn.setToolTip(tr("White noise")); + connect(&m_whiteNoiseWaveBtn, SIGNAL(clicked()), this, SLOT(noiseWaveClicked())); - m_smoothBtn = new PixmapButton( this, tr( "Smooth waveform" ) ); - m_smoothBtn->move( 79, 129 ); - m_smoothBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( - "smooth_active" ) ); - m_smoothBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( - "smooth_inactive" ) ); - m_smoothBtn->setChecked( false ); - m_smoothBtn->setToolTip( - tr( "Smooth waveform" ) ); - connect( m_smoothBtn, SIGNAL ( clicked () ), - this, SLOT ( smoothClicked() ) ); - - m_normalizeBtn = new PixmapButton( this, tr( "Normalize waveform" ) ); - m_normalizeBtn->move( 96, 129 ); - m_normalizeBtn->setActiveGraphic( PLUGIN_NAME::getIconPixmap( - "normalize_active" ) ); - m_normalizeBtn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( - "normalize_inactive" ) ); - m_normalizeBtn->setChecked( false ); - m_normalizeBtn->setToolTip( - tr( "Normalize waveform" ) ); - - connect( m_normalizeBtn, SIGNAL ( clicked () ), - this, SLOT ( normalizeClicked() ) ); - + m_usrWaveBtn.move(212, 109); + m_usrWaveBtn.setActiveGraphic(embed::getIconPixmap("usr_wave_active")); + m_usrWaveBtn.setInactiveGraphic(embed::getIconPixmap("usr_wave_inactive")); + m_usrWaveBtn.setToolTip(tr("User-defined wave")); + connect(&m_usrWaveBtn, SIGNAL(clicked()),this, SLOT(usrWaveClicked())); } - - - void VibedView::modelChanged() { - showString( 0 ); + showString(0); } - - - -void VibedView::showString( int _string ) +void VibedView::showString(int str) { auto v = castModel(); - m_pickKnob->setModel( v->m_pickKnobs[_string] ); - m_pickupKnob->setModel( v->m_pickupKnobs[_string] ); - m_stiffnessKnob->setModel( v->m_stiffnessKnobs[_string] ); - m_volumeKnob->setModel( v->m_volumeKnobs[_string] ); - m_panKnob->setModel( v->m_panKnobs[_string] ); - m_detuneKnob->setModel( v->m_detuneKnobs[_string] ); - m_randomKnob->setModel( v->m_randomKnobs[_string] ); - m_lengthKnob->setModel( v->m_lengthKnobs[_string] ); - m_graph->setModel( v->m_graphs[_string] ); - m_impulse->setModel( v->m_impulses[_string] ); - m_harmonic->setModel( v->m_harmonics[_string] ); - m_power->setModel( v->m_powerButtons[_string] ); - + m_pickKnob.setModel(v->m_pickModels[str].get()); + m_pickupKnob.setModel(v->m_pickupModels[str].get()); + m_stiffnessKnob.setModel(v->m_stiffnessModels[str].get()); + m_volumeKnob.setModel(v->m_volumeModels[str].get()); + m_panKnob.setModel(v->m_panModels[str].get()); + m_detuneKnob.setModel(v->m_detuneModels[str].get()); + m_randomKnob.setModel(v->m_randomModels[str].get()); + m_lengthKnob.setModel(v->m_lengthModels[str].get()); + m_graph.setModel(v->m_graphModels[str].get()); + m_impulse.setModel(v->m_impulseModels[str].get()); + m_harmonic->setModel(v->m_harmonicModels[str].get()); + m_power.setModel(v->m_powerModels[str].get()); } - - - void VibedView::sinWaveClicked() { - m_graph->model()->setWaveToSine(); + m_graph.model()->setWaveToSine(); Engine::getSong()->setModified(); } - - void VibedView::triangleWaveClicked() { - m_graph->model()->setWaveToTriangle(); + m_graph.model()->setWaveToTriangle(); Engine::getSong()->setModified(); } - - void VibedView::sawWaveClicked() { - m_graph->model()->setWaveToSaw(); + m_graph.model()->setWaveToSaw(); Engine::getSong()->setModified(); } - - void VibedView::sqrWaveClicked() { - m_graph->model()->setWaveToSquare(); + m_graph.model()->setWaveToSquare(); Engine::getSong()->setModified(); } - - void VibedView::noiseWaveClicked() { - m_graph->model()->setWaveToNoise(); + m_graph.model()->setWaveToNoise(); Engine::getSong()->setModified(); } - - void VibedView::usrWaveClicked() { - QString fileName = m_graph->model()->setWaveToUser(); - m_usrWaveBtn->setToolTip(fileName); + QString fileName = m_graph.model()->setWaveToUser(); + m_usrWaveBtn.setToolTip(fileName); Engine::getSong()->setModified(); } - - void VibedView::smoothClicked() { - m_graph->model()->smooth(); + m_graph.model()->smooth(); Engine::getSong()->setModified(); } - - void VibedView::normalizeClicked() { - m_graph->model()->normalize(); + m_graph.model()->normalize(); Engine::getSong()->setModified(); } - - - -void VibedView::contextMenuEvent( QContextMenuEvent * ) +void VibedView::contextMenuEvent(QContextMenuEvent*) { - - CaptionMenu contextMenu( model()->displayName(), this ); - contextMenu.exec( QCursor::pos() ); - + CaptionMenu contextMenu(model()->displayName(), this); + contextMenu.exec(QCursor::pos()); } @@ -683,12 +526,11 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* m, void*) { - return( new Vibed( static_cast( m ) ) ); + return new Vibed(static_cast(m)); } - } diff --git a/plugins/Vibed/Vibed.h b/plugins/Vibed/Vibed.h index 75f92157c..308ae64d5 100644 --- a/plugins/Vibed/Vibed.h +++ b/plugins/Vibed/Vibed.h @@ -2,7 +2,7 @@ * Vibed.h - combination of PluckedStringSynth and BitInvader * * Copyright (c) 2006-2008 Danny McRae - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -21,12 +21,20 @@ * Boston, MA 02110-1301 USA. * */ -#ifndef _VIBED_H -#define _VIBED_H + +#ifndef LMMS_VIBED_H +#define LMMS_VIBED_H #include "Instrument.h" #include "InstrumentView.h" #include "NineButtonSelector.h" +#include "Knob.h" +#include "LedCheckBox.h" +#include "Graph.h" +#include "PixmapButton.h" + +#include +#include namespace lmms { @@ -46,45 +54,42 @@ class Vibed : public Instrument { Q_OBJECT public: - Vibed( InstrumentTrack * _instrument_track ); + Vibed(InstrumentTrack* instrumentTrack); + ~Vibed() override = default; - void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; - void deleteNotePluginData( NotePlayHandle * _n ) override; + void playNote(NotePlayHandle* n, sampleFrame* workingBuffer) override; + void deleteNotePluginData(NotePlayHandle* n) override; - - void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; - void loadSettings( const QDomElement & _this ) override; + void saveSettings(QDomDocument& doc, QDomElement& elem) override; + void loadSettings(const QDomElement& elem) override; QString nodeName() const override; - Flags flags() const override - { - return IsNotBendable; - } - - - gui::PluginView* instantiateView( QWidget * _parent ) override; + Flags flags() const override { return IsNotBendable; } + gui::PluginView* instantiateView(QWidget* parent) override; private: - QList m_pickKnobs; - QList m_pickupKnobs; - QList m_stiffnessKnobs; - QList m_volumeKnobs; - QList m_panKnobs; - QList m_detuneKnobs; - QList m_randomKnobs; - QList m_lengthKnobs; - QList m_powerButtons; - QList m_graphs; - QList m_impulses; - QList m_harmonics; + class StringContainer; - static const int __sampleLength = 128; + static constexpr int s_sampleLength = 128; + static constexpr int s_stringCount = 9; + + std::array, s_stringCount> m_pickModels; + std::array, s_stringCount> m_pickupModels; + std::array, s_stringCount> m_stiffnessModels; + std::array, s_stringCount> m_volumeModels; + std::array, s_stringCount> m_panModels; + std::array, s_stringCount> m_detuneModels; + std::array, s_stringCount> m_randomModels; + std::array, s_stringCount> m_lengthModels; + std::array, s_stringCount> m_powerModels; + std::array, s_stringCount> m_graphModels; + std::array, s_stringCount> m_impulseModels; + std::array, s_stringCount> m_harmonicModels; friend class gui::VibedView; -} ; +}; namespace gui @@ -95,13 +100,12 @@ class VibedView : public InstrumentViewFixedSize { Q_OBJECT public: - VibedView( Instrument * _instrument, - QWidget * _parent ); + VibedView(Instrument* instrument, QWidget* parent); ~VibedView() override = default; public slots: - void showString( int _string ); - void contextMenuEvent( QContextMenuEvent * ) override; + void showString(int str); + void contextMenuEvent(QContextMenuEvent*) override; protected slots: void sinWaveClicked(); @@ -116,35 +120,32 @@ protected slots: private: void modelChanged() override; - // String-related - Knob * m_pickKnob; - Knob * m_pickupKnob; - Knob * m_stiffnessKnob; - Knob * m_volumeKnob; - Knob * m_panKnob; - Knob * m_detuneKnob; - Knob * m_randomKnob; - Knob * m_lengthKnob; - Graph * m_graph; - NineButtonSelector * m_harmonic; - LedCheckBox * m_impulse; - LedCheckBox * m_power; + Knob m_volumeKnob; + Knob m_stiffnessKnob; + Knob m_pickKnob; + Knob m_pickupKnob; + Knob m_panKnob; + Knob m_detuneKnob; + Knob m_randomKnob; + Knob m_lengthKnob; + Graph m_graph; + LedCheckBox m_impulse; + LedCheckBox m_power; + std::unique_ptr m_harmonic; // Not in model - NineButtonSelector * m_stringSelector; - PixmapButton * m_smoothBtn; - PixmapButton * m_normalizeBtn; + std::unique_ptr m_stringSelector; + PixmapButton m_smoothBtn; + PixmapButton m_normalizeBtn; // From impulse editor - PixmapButton * m_sinWaveBtn; - PixmapButton * m_triangleWaveBtn; - PixmapButton * m_sqrWaveBtn; - PixmapButton * m_sawWaveBtn; - PixmapButton * m_whiteNoiseWaveBtn; - PixmapButton * m_usrWaveBtn; - - + PixmapButton m_sinWaveBtn; + PixmapButton m_triangleWaveBtn; + PixmapButton m_sawWaveBtn; + PixmapButton m_sqrWaveBtn; + PixmapButton m_whiteNoiseWaveBtn; + PixmapButton m_usrWaveBtn; }; @@ -152,4 +153,4 @@ private: } // namespace lmms -#endif +#endif // LMMS_VIBED_H diff --git a/plugins/Vibed/VibratingString.cpp b/plugins/Vibed/VibratingString.cpp index e33ef0582..44ade3c3a 100644 --- a/plugins/Vibed/VibratingString.cpp +++ b/plugins/Vibed/VibratingString.cpp @@ -1,8 +1,8 @@ /* - * vibrating_sring.h - model of a vibrating string lifted from pluckedSynth + * VibratingString.cpp - model of a vibrating string lifted from pluckedSynth * * Copyright (c) 2006-2008 Danny McRae - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -21,93 +21,66 @@ * Boston, MA 02110-1301 USA. * */ -#include #include "VibratingString.h" #include "interpolation.h" #include "AudioEngine.h" #include "Engine.h" +#include +#include + namespace lmms { -VibratingString::VibratingString( float _pitch, - float _pick, - float _pickup, - float * _impulse, - int _len, - sample_rate_t _sample_rate, - int _oversample, - float _randomize, - float _string_loss, - float _detune, - bool _state ) : - m_oversample( 2 * _oversample / (int)( _sample_rate / - Engine::audioEngine()->baseSampleRate() ) ), - m_randomize( _randomize ), - m_stringLoss( 1.0f - _string_loss ), - m_state( 0.1f ) +VibratingString::VibratingString(float pitch, float pick, float pickup, const float* impulse, int len, + sample_rate_t sampleRate, int oversample, float randomize, float stringLoss, float detune, bool state) : + m_oversample{2 * oversample / static_cast(sampleRate / Engine::audioEngine()->baseSampleRate())}, + m_randomize{randomize}, + m_stringLoss{1.0f - stringLoss}, + m_choice{static_cast(m_oversample * static_cast(std::rand()) / RAND_MAX)}, + m_state{0.1f}, + m_outsamp{std::make_unique(m_oversample)} { - m_outsamp = new sample_t[m_oversample]; - int string_length; - - string_length = static_cast( m_oversample * _sample_rate / - _pitch ) + 1; - string_length += static_cast( string_length * -_detune ); + int stringLength = static_cast(m_oversample * sampleRate / pitch) + 1; + stringLength += static_cast(stringLength * -detune); - int pick = static_cast( ceil( string_length * _pick ) ); - - if( ! _state ) + const int pickInt = static_cast(std::ceil(stringLength * pick)); + + if (!state) { - m_impulse = new float[string_length]; - resample( _impulse, _len, string_length ); + m_impulse = std::make_unique(stringLength); + resample(impulse, len, stringLength); } else - { - m_impulse = new float[_len]; - for( int i = 0; i < _len; i++ ) - { - m_impulse[i] = _impulse[i]; - } + { + m_impulse = std::make_unique(len); + std::copy_n(impulse, len, m_impulse.get()); } - - m_toBridge = VibratingString::initDelayLine( string_length, pick ); - m_fromBridge = VibratingString::initDelayLine( string_length, pick ); - - VibratingString::setDelayLine( m_toBridge, pick, - m_impulse, _len, 0.5f, - _state ); - VibratingString::setDelayLine( m_fromBridge, pick, - m_impulse, _len, 0.5f, - _state); - - m_choice = static_cast( m_oversample * - static_cast( rand() ) / RAND_MAX ); - - m_pickupLoc = static_cast( _pickup * string_length ); + m_toBridge = VibratingString::initDelayLine(stringLength); + m_fromBridge = VibratingString::initDelayLine(stringLength); + + VibratingString::setDelayLine(m_toBridge.get(), pickInt, m_impulse.get(), len, 0.5f, state); + VibratingString::setDelayLine(m_fromBridge.get(), pickInt, m_impulse.get(), len, 0.5f, state); + + m_pickupLoc = static_cast(pickup * stringLength); } - - - -VibratingString::delayLine * VibratingString::initDelayLine( int _len, - int _pick ) +std::unique_ptr VibratingString::initDelayLine(int len) { - auto dl = new VibratingString::delayLine[_len]; - dl->length = _len; - if( _len > 0 ) + auto dl = std::make_unique(); + dl->length = len; + if (len > 0) { - dl->data = new sample_t[_len]; + dl->data = std::make_unique(len); float r; float offset = 0.0f; - for( int i = 0; i < dl->length; i++ ) + for (int i = 0; i < dl->length; ++i) { - r = static_cast( rand() ) / - RAND_MAX; - offset = ( m_randomize / 2.0f - - m_randomize ) * r; + r = static_cast(std::rand()) / RAND_MAX; + offset = (m_randomize / 2.0f - m_randomize) * r; dl->data[i] = offset; } } @@ -116,46 +89,25 @@ VibratingString::delayLine * VibratingString::initDelayLine( int _len, dl->data = nullptr; } - dl->pointer = dl->data; - dl->end = dl->data + _len - 1; + dl->pointer = dl->data.get(); + dl->end = dl->data.get() + len - 1; - return( dl ); + return dl; } - - - -void VibratingString::freeDelayLine( delayLine * _dl ) +void VibratingString::resample(const float* src, f_cnt_t srcFrames, f_cnt_t dstFrames) { - if( _dl ) + for (f_cnt_t frame = 0; frame < dstFrames; ++frame) { - delete[] _dl->data; - delete[] _dl; - } -} - - - - -void VibratingString::resample( float *_src, f_cnt_t _src_frames, - f_cnt_t _dst_frames ) -{ - for( f_cnt_t frame = 0; frame < _dst_frames; ++frame ) - { - const float src_frame_float = frame * - (float) _src_frames / - _dst_frames; - const float frac_pos = src_frame_float - - static_cast( src_frame_float ); - const f_cnt_t src_frame = qBound( - 1, static_cast( src_frame_float ), - _src_frames - 3 ); + const float srcFrameFloat = frame * static_cast(srcFrames) / dstFrames; + const float fracPos = srcFrameFloat - static_cast(srcFrameFloat); + const f_cnt_t srcFrame = std::clamp(static_cast(srcFrameFloat), 1, srcFrames - 3); m_impulse[frame] = cubicInterpolate( - _src[src_frame - 1], - _src[src_frame + 0], - _src[src_frame + 1], - _src[src_frame + 2], - frac_pos ); + src[srcFrame - 1], + src[srcFrame + 0], + src[srcFrame + 1], + src[srcFrame + 2], + fracPos); } } diff --git a/plugins/Vibed/VibratingString.h b/plugins/Vibed/VibratingString.h index ed7cbe38c..efdaec282 100644 --- a/plugins/Vibed/VibratingString.h +++ b/plugins/Vibed/VibratingString.h @@ -2,7 +2,7 @@ * VibratingString.h - model of a vibrating string lifted from pluckedSynth * * Copyright (c) 2006-2007 Danny McRae - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -21,10 +21,12 @@ * Boston, MA 02110-1301 USA. * */ -#ifndef _VIBRATING_STRING_H -#define _VIBRATING_STRING_H -#include +#ifndef LMMS_VIBRATING_STRING_H +#define LMMS_VIBRATING_STRING_H + +#include +#include #include "lmms_basics.h" @@ -34,244 +36,219 @@ namespace lmms class VibratingString { - public: - VibratingString( float _pitch, - float _pick, - float _pickup, - float * impluse, - int _len, - sample_rate_t _sample_rate, - int _oversample, - float _randomize, - float _string_loss, - float _detune, - bool _state ); - - inline ~VibratingString() - { - delete[] m_outsamp; - delete[] m_impulse; - VibratingString::freeDelayLine( m_fromBridge ); - VibratingString::freeDelayLine( m_toBridge ); - } + VibratingString() = default; + VibratingString(float pitch, float pick, float pickup, const float* impulse, int len, + sample_rate_t sampleRate, int oversample, float randomize, float stringLoss, float detune, bool state); + ~VibratingString() = default; - inline sample_t nextSample() - { + VibratingString(const VibratingString&) = delete; + VibratingString& operator=(const VibratingString&) = delete; + VibratingString(VibratingString&&) noexcept = delete; + VibratingString& operator=(VibratingString&&) noexcept = default; + + sample_t nextSample() + { sample_t ym0; sample_t ypM; - for( int i = 0; i < m_oversample; i++) + for (int i = 0; i < m_oversample; ++i) { // Output at pickup position - m_outsamp[i] = fromBridgeAccess( m_fromBridge, - m_pickupLoc ); - m_outsamp[i] += toBridgeAccess( m_toBridge, - m_pickupLoc ); - + m_outsamp[i] = fromBridgeAccess(m_fromBridge.get(), m_pickupLoc); + m_outsamp[i] += toBridgeAccess(m_toBridge.get(), m_pickupLoc); + // Sample traveling into "bridge" - ym0 = toBridgeAccess( m_toBridge, 1 ); + ym0 = toBridgeAccess(m_toBridge.get(), 1); // Sample to "nut" - ypM = fromBridgeAccess( m_fromBridge, - m_fromBridge->length - 2 ); + ypM = fromBridgeAccess(m_fromBridge.get(), m_fromBridge->length - 2); // String state update // Decrement pointer and then update - fromBridgeUpdate( m_fromBridge, - -bridgeReflection( ym0 ) ); + fromBridgeUpdate(m_fromBridge.get(), -bridgeReflection(ym0)); // Update and then increment pointer - toBridgeUpdate( m_toBridge, -ypM ); + toBridgeUpdate(m_toBridge.get(), -ypM); } - return( m_outsamp[m_choice] ); + + return m_outsamp[m_choice]; } private: - struct delayLine + struct DelayLine { - sample_t * data; + std::unique_ptr data; int length; - sample_t * pointer; - sample_t * end; - } ; + sample_t* pointer; + sample_t* end; + }; - delayLine * m_fromBridge; - delayLine * m_toBridge; + std::unique_ptr m_fromBridge; + std::unique_ptr m_toBridge; int m_pickupLoc; int m_oversample; float m_randomize; float m_stringLoss; - - float * m_impulse; + + std::unique_ptr m_impulse; int m_choice; float m_state; - - sample_t * m_outsamp; - delayLine * initDelayLine( int _len, int _pick ); - static void freeDelayLine( delayLine * _dl ); - void resample( float *_src, f_cnt_t _src_frames, f_cnt_t _dst_frames ); - - /* setDelayLine initializes the string with an impulse at the pick + std::unique_ptr m_outsamp; + + std::unique_ptr initDelayLine(int len); + void resample(const float* src, f_cnt_t srcFrames, f_cnt_t dstFrames); + + /** + * setDelayLine initializes the string with an impulse at the pick * position unless the impulse is longer than the string, in which - * case the impulse gets truncated. */ - inline void setDelayLine( delayLine * _dl, - int _pick, - const float * _values, - int _len, - float _scale, - bool _state ) + * case the impulse gets truncated. + */ + void setDelayLine(DelayLine* dl, int pick, const float* values, int len, float scale, bool state) { float r; float offset; - - if( ! _state ) + + if (!state) { - for( int i = 0; i < _pick; i++ ) + for (int i = 0; i < pick; ++i) { - r = static_cast( rand() ) / - RAND_MAX; - offset = ( m_randomize / 2.0f - - m_randomize ) * r; - _dl->data[i] = _scale * - _values[_dl->length - i - 1] + - offset; + r = static_cast(std::rand()) / RAND_MAX; + offset = (m_randomize / 2.0f - m_randomize) * r; + dl->data[i] = scale * values[dl->length - i - 1] + offset; } - for( int i = _pick; i < _dl->length; i++ ) + for (int i = pick; i < dl->length; ++i) { - r = static_cast( rand() ) / - RAND_MAX; - offset = ( m_randomize / 2.0f - - m_randomize ) * r; - _dl->data[i] = _scale * - _values[i - _pick] + offset ; + r = static_cast(std::rand()) / RAND_MAX; + offset = (m_randomize / 2.0f - m_randomize) * r; + dl->data[i] = scale * values[i - pick] + offset; } } else { - if( _len + _pick > _dl->length ) + if (len + pick > dl->length) { - for( int i = _pick; i < _dl->length; i++ ) + for (int i = pick; i < dl->length; ++i) { - r = static_cast( rand() ) / - RAND_MAX; - offset = ( m_randomize / 2.0f - - m_randomize ) * r; - _dl->data[i] = _scale * - _values[i-_pick] + - offset; + r = static_cast(std::rand()) / RAND_MAX; + offset = (m_randomize / 2.0f - m_randomize) * r; + dl->data[i] = scale * values[i - pick] + offset; } } else { - for( int i = 0; i < _len; i++ ) + for (int i = 0; i < len; ++i) { - r = static_cast( rand() ) / - RAND_MAX; - offset = ( m_randomize / 2.0f - - m_randomize ) * r; - _dl->data[i+_pick] = _scale * - _values[i] + - offset; + r = static_cast(std::rand()) / RAND_MAX; + offset = (m_randomize / 2.0f - m_randomize) * r; + dl->data[i+pick] = scale * values[i] + offset; } } } } - /* toBridgeUpdate(dl, insamp); - * Places "nut-reflected" sample from upper delay-line into - * current lower delay-line pointer position (which represents - * x = 0 position). The pointer is then incremented (i.e. the - * wave travels one sample to the left), turning the previous - * position into an "effective" x = L position for the next - * iteration. */ - inline void toBridgeUpdate( delayLine * _dl, sample_t _insamp ) + /** + * toBridgeUpdate(dl, insamp); + * Places "nut-reflected" sample from upper delay-line into + * current lower delay-line pointer position (which represents + * x = 0 position). The pointer is then incremented (i.e. the + * wave travels one sample to the left), turning the previous + * position into an "effective" x = L position for the next + * iteration. + */ + void toBridgeUpdate(DelayLine* dl, sample_t insamp) { - sample_t * ptr = _dl->pointer; - *ptr = _insamp * m_stringLoss; + sample_t* ptr = dl->pointer; + *ptr = insamp * m_stringLoss; ++ptr; - if( ptr > _dl->end ) + if (ptr > dl->end) { - ptr = _dl->data; + ptr = dl->data.get(); } - _dl->pointer = ptr; + dl->pointer = ptr; } - /* fromBridgeUpdate(dl, insamp); - * Decrements current upper delay-line pointer position (i.e. - * the wave travels one sample to the right), moving it to the - * "effective" x = 0 position for the next iteration. The - * "bridge-reflected" sample from lower delay-line is then placed - * into this position. */ - inline void fromBridgeUpdate( delayLine * _dl, - sample_t _insamp ) + /** + * fromBridgeUpdate(dl, insamp); + * Decrements current upper delay-line pointer position (i.e. + * the wave travels one sample to the right), moving it to the + * "effective" x = 0 position for the next iteration. The + * "bridge-reflected" sample from lower delay-line is then placed + * into this position. + */ + void fromBridgeUpdate(DelayLine* dl, sample_t insamp) { - sample_t * ptr = _dl->pointer; + sample_t* ptr = dl->pointer; --ptr; - if( ptr < _dl->data ) + if (ptr < dl->data.get()) { - ptr = _dl->end; + ptr = dl->end; } - *ptr = _insamp * m_stringLoss; - _dl->pointer = ptr; + *ptr = insamp * m_stringLoss; + dl->pointer = ptr; } - /* dlAccess(dl, position); - * Returns sample "position" samples into delay-line's past. - * Position "0" points to the most recently inserted sample. */ - static inline sample_t dlAccess( delayLine * _dl, int _position ) + /** + * dlAccess(dl, position); + * Returns sample "position" samples into delay-line's past. + * Position "0" points to the most recently inserted sample. + */ + static sample_t dlAccess(DelayLine* dl, int position) { - sample_t * outpos = _dl->pointer + _position; - while( outpos < _dl->data ) + sample_t* outpos = dl->pointer + position; + while (outpos < dl->data.get()) { - outpos += _dl->length; + outpos += dl->length; } - while( outpos > _dl->end ) + while (outpos > dl->end) { - outpos -= _dl->length; + outpos -= dl->length; } - return( *outpos ); + return *outpos; } /* - * Right-going delay line: - * -->---->---->--- - * x=0 - * (pointer) - * Left-going delay line: - * --<----<----<--- - * x=0 - * (pointer) - */ + * Right-going delay line: + * -->---->---->--- + * x=0 + * (pointer) + * Left-going delay line: + * --<----<----<--- + * x=0 + * (pointer) + */ - /* fromBridgeAccess(dl, position); - * Returns spatial sample at position "position", where position zero - * is equal to the current upper delay-line pointer position (x = 0). - * In a right-going delay-line, position increases to the right, and - * delay increases to the right => left = past and right = future. */ - static inline sample_t fromBridgeAccess( delayLine * _dl, - int _position ) + /** + * fromBridgeAccess(dl, position); + * Returns spatial sample at position "position", where position zero + * is equal to the current upper delay-line pointer position (x = 0). + * In a right-going delay-line, position increases to the right, and + * delay increases to the right => left = past and right = future. + */ + static sample_t fromBridgeAccess(DelayLine* dl, int position) { - return( dlAccess( _dl, _position ) ); + return dlAccess(dl, position); } - /* toBridgeAccess(dl, position); - * Returns spatial sample at position "position", where position zero - * is equal to the current lower delay-line pointer position (x = 0). - * In a left-going delay-line, position increases to the right, and - * delay DEcreases to the right => left = future and right = past. */ - static inline sample_t toBridgeAccess( delayLine * _dl, int _position ) + /** + * toBridgeAccess(dl, position); + * Returns spatial sample at position "position", where position zero + * is equal to the current lower delay-line pointer position (x = 0). + * In a left-going delay-line, position increases to the right, and + * delay DEcreases to the right => left = future and right = past. + */ + static sample_t toBridgeAccess(DelayLine* dl, int position) { - return( dlAccess( _dl, _position ) ); + return dlAccess(dl, position); } - inline sample_t bridgeReflection( sample_t _insamp ) + sample_t bridgeReflection(sample_t insamp) { - return( m_state = ( m_state + _insamp ) * 0.5 ); + m_state = (m_state + insamp) * 0.5; + return m_state; } - -} ; +}; } // namespace lmms -#endif +#endif // LMMS_VIBRATING_STRING_H diff --git a/plugins/Watsyn/Watsyn.cpp b/plugins/Watsyn/Watsyn.cpp index 9ceaaddb8..7916eb45f 100644 --- a/plugins/Watsyn/Watsyn.cpp +++ b/plugins/Watsyn/Watsyn.cpp @@ -329,7 +329,7 @@ WatsynInstrument::WatsynInstrument( InstrumentTrack * _instrument_track ) : void WatsynInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { - if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == nullptr ) + if (!_n->m_pluginData) { auto w = new WatsynObject(&A1_wave[0], &A2_wave[0], &B1_wave[0], &B2_wave[0], m_amod.value(), m_bmod.value(), Engine::audioEngine()->processingSampleRate(), _n, Engine::audioEngine()->framesPerPeriod(), this); diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index 18ac8c052..3cc5564e6 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -201,7 +201,7 @@ void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) { m_A2=m_parameterA2.value(); m_A3=m_parameterA3.value(); - if (nph->totalFramesPlayed() == 0 || nph->m_pluginData == nullptr) { + if (!nph->m_pluginData) { auto exprO1 = new ExprFront(m_outputExpression[0].constData(), Engine::audioEngine()->processingSampleRate()); // give the "last" function a whole second From 2ca05d025cba536b5fc42ecf0ada9661ed8891f5 Mon Sep 17 00:00:00 2001 From: saker Date: Mon, 21 Aug 2023 23:08:18 -0400 Subject: [PATCH 24/45] Bump mingw-std-threads submodule (#6817) * Bump submodule and generate std headers * Commit submodule update * Downgrade to earlier commit Can be upgraded once a proper C++20 MinGW compiler is available for the docker images. * Downgrade to the correct commit * Append mingw_stdthreads to EXTRA_LIBRARIES Currently for this CMake version, it seems that there is no support to link other targets to object libraries. I'll add it to EXTRA_LIBRARIES instead. * Add LMMS_ prefix to USE_MINGW_STD_THREADS * Use built-in MINGW CMake variable * Use lowercase rather than uppercase --- src/3rdparty/CMakeLists.txt | 6 ++++++ src/3rdparty/mingw-std-threads | 2 +- src/CMakeLists.txt | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index 7a6b88775..a95332a07 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -7,6 +7,12 @@ ADD_SUBDIRECTORY(hiir) ADD_SUBDIRECTORY(rpmalloc) ADD_SUBDIRECTORY(weakjack) +if(MINGW) + option(MINGW_STDTHREADS_GENERATE_STDHEADERS "" ON) + add_subdirectory(mingw-std-threads) + set(LMMS_USE_MINGW_STD_THREADS ON PARENT_SCOPE) +endif() + # The lockless ring buffer library is compiled as part of the core SET(RINGBUFFER_DIR "${CMAKE_SOURCE_DIR}/src/3rdparty/ringbuffer/") SET(RINGBUFFER_DIR ${RINGBUFFER_DIR} PARENT_SCOPE) diff --git a/src/3rdparty/mingw-std-threads b/src/3rdparty/mingw-std-threads index 10665829d..6c2061b7d 160000 --- a/src/3rdparty/mingw-std-threads +++ b/src/3rdparty/mingw-std-threads @@ -1 +1 @@ -Subproject commit 10665829daaedc28629e5e9b014fe498c20d73f2 +Subproject commit 6c2061b7da41d6aa1b2162ff4383ec3ece864bc6 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 23923f616..f483d8b41 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -170,6 +170,10 @@ if(LMMS_HAVE_MP3LAME) list(APPEND EXTRA_LIBRARIES mp3lame::mp3lame) endif() +if(LMMS_USE_MINGW_STD_THREADS) + list(APPEND EXTRA_LIBRARIES mingw_stdthreads) +endif() + SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} ${CMAKE_THREAD_LIBS_INIT} ${QT_LIBRARIES} From 9a0add49fb61f1bbac603e7e181221b7cef81c1b Mon Sep 17 00:00:00 2001 From: saker Date: Mon, 21 Aug 2023 23:08:56 -0400 Subject: [PATCH 25/45] Core Refactor: Replace ``QVector`` with ``std::vector`` (#6477) * Replace QVector with std::vector in AudioEngine * Replace QVector with std::vector in AudioEngineWorkerThread * Replace QVector with std::vector in AudioJack * Replace QVector with std::vector in AutomatableModel * Replace QVector with std::vector in AutomationClip * Replace QVector with std::vector in AutomationEditor * Replace QVector with std::vector in ConfigManager * Replace QVector with std::vector in Controller * Replace QVector with std::vector in ControllerConnection * Replace QVector with std::vector in EffectChain * Replace QVector with std::vector in EnvelopeAndLfoParameters * Replace QVector with std::vector in InstrumentFunctions * Replace QVector with std::vector in MidiClient * Replace QVector with std::vector in Mixer * Replace QVector with std::vector in Note * Replace QVector with std::vector in PeakController * Replace QVector with std::vector in PianoRoll * Replace QVector with std::vector in PluginFactory * Replace QVector with std::vector in RenderManager * Replace QVector with std::vector in StepRecorder * Replace QVector with std::vector in Track * Replace QVector with std::vector in TrackContainer * Replace QVector with std::vector in Song * Adapt QVector to std::vector changes in ControllerConnectionDialog * Phase 2: Use std::abs in panning.h Without this, the QVector changes will make the code not compile. * Phase 2: Replace QVector with std::vector in PeakControllerEffect * Phase 2: Replace QVector with std::vector in AutomatableModel * Phase 2: Replace QVector with std::vector in AutomationClip * Phase 2: Replace QVector with std::vector in ControllerConnection * Phase 2: Replace QVector with std::vector in EffectChain * Phase 2: Replace QVector with std::vector in Mixer * Phase 2: Replace QVector with std::vector in PeakController * Phase 2: Replace QVector with std::vector in RenderManager * Phase 2: Replace QVector with std::vector in Song * Phase 2: Replace QVector with std::vector in StepRecorder * Phase 2: Replace QVector with std::vector in Track * Phase 2: Replace QVector with std::vector in TrackContainer * Phase 2: Adapt QVector changes in EffectRackView * Phase 2: Adapt QVector changes in AutomationClipView * Phase 2: Adapt QVector changes in ClipView * Phase 2: Adapt QVector changes in AutomationEditor * Phase 2: Adapt QVector changes in PianoRoll * Phase 2: Adapt QVector changes in TrackContainerView * Phase 2: Adapt QVector changes in TrackContentWidget * Phase 2: Adapt QVector changes in InstrumentTrack * Phase 2: Adapt QVector changes in MidiClip * Phase 2: Adapt QVector changes in SampleTrack * Fix segmentation fault in ConfigManager::value * Fix unintended faulty std::vector insert in AutomationClip::resolveAllIDs * Resolve trailing whitespace in src/core/StepRecorder.cpp Co-authored-by: Hyunjin Song * Use std::next and std::prev in EffectChain::moveUp/moveDown * Introduce static "combineAllTracks" function in AutomationClip * Adjust variable name in Song::automatedValuesAt * Adjust removal of long step notes in StepRecorder::removeNotesReleasedForTooLong * Iterate over m_chords by const reference in src/core/InstrumentFunctions.cpp Co-authored-by: Hyunjin Song * Fix StepRecorder::removeNotesReleasedForTooLong again * Combine the ConfigManager::value overloads using std::optional * Revise StepRecorder::removeNotesReleasedForTooLong * Remove redundant std::optional in ConfigManager::value * Remove trailing whitespace in ConfigManager::value * Fix: Prevent incorrect use of std::distance when element not found * Chore: Remove trailing whitespace in edited files * Only set the id attribute if the controller was found Co-authored-by: Hyunjin Song * Remove extra indents from 84b8fe8a559855ed263b74cc582eab3655250c5f * Fix compilation issues * Add LMMS_ prefix for header guard in Track.h * Undo changes made to MixerView::deleteUnusedChannels * Simplify code to handle failure of finding tracks Co-authored-by: Hyunjin Song * Split ternary operator into separate if statement Co-authored-by: Hyunjin Song * Undo changes to indentation in MixerRoute * Do general clean-up Some of the changes made: + Use auto where benefical + Fix bug in AutomatableModel::globalAutomationValueAt (for loop should be looping over clips variable, not clipsInRange) + Undo out of focus whitespace changes * Always assign to m_steps regardless if clip is found or not Even when the clip is not found (i.e., currentClip is -1), m_steps still gets assigned to. * Insert at the end of tracks vector in src/core/Mixer.cpp Co-authored-by: Dominic Clark * Insert at the end of tracks vector in src/core/Mixer.cpp (2) Co-authored-by: Dominic Clark * Remove redundant template parameter * Use std::array for zoomLevels --------- Co-authored-by: Hyunjin Song Co-authored-by: Dominic Clark --- include/AudioEngine.h | 6 +- include/AudioEngineWorkerThread.h | 4 +- include/AudioJack.h | 4 +- include/AutomatableModel.h | 2 +- include/AutomationClip.h | 15 ++++- include/AutomationEditor.h | 3 +- include/ConfigManager.h | 11 ++-- include/Controller.h | 2 +- include/ControllerConnection.h | 5 +- include/EffectChain.h | 2 +- include/EnvelopeAndLfoParameters.h | 2 +- include/InstrumentFunctions.h | 8 ++- include/MidiClient.h | 4 +- include/Mixer.h | 4 +- include/Note.h | 4 +- include/PeakController.h | 2 +- include/PianoRoll.h | 9 +-- include/PluginFactory.h | 4 +- include/RenderManager.h | 4 +- include/StepRecorder.h | 4 +- include/Track.h | 5 +- include/TrackContainer.h | 2 +- include/panning.h | 4 +- .../PeakControllerEffect.cpp | 10 +-- src/core/AudioEngine.cpp | 4 +- src/core/AutomatableModel.cpp | 24 +++---- src/core/AutomationClip.cpp | 62 ++++++++++--------- src/core/ConfigManager.cpp | 18 +----- src/core/Controller.cpp | 12 ++-- src/core/ControllerConnection.cpp | 17 +++-- src/core/EffectChain.cpp | 24 +++---- src/core/InstrumentFunctions.cpp | 30 ++++----- src/core/Mixer.cpp | 60 +++++++++++------- src/core/PeakController.cpp | 23 +++---- src/core/RenderManager.cpp | 4 +- src/core/Song.cpp | 18 +++--- src/core/StepRecorder.cpp | 30 +++++---- src/core/Track.cpp | 8 +-- src/core/TrackContainer.cpp | 14 ++--- src/core/midi/MidiClient.cpp | 3 +- src/gui/clips/AutomationClipView.cpp | 4 +- src/gui/clips/ClipView.cpp | 9 ++- src/gui/editors/AutomationEditor.cpp | 2 +- src/gui/editors/PianoRoll.cpp | 39 ++++++------ src/gui/editors/TrackContainerView.cpp | 4 +- src/gui/modals/ControllerConnectionDialog.cpp | 6 +- src/gui/tracks/TrackContentWidget.cpp | 6 +- src/tracks/InstrumentTrack.cpp | 2 +- src/tracks/MidiClip.cpp | 44 ++++++------- 49 files changed, 314 insertions(+), 273 deletions(-) diff --git a/include/AudioEngine.h b/include/AudioEngine.h index d0f269d87..dec8f2592 100644 --- a/include/AudioEngine.h +++ b/include/AudioEngine.h @@ -32,10 +32,10 @@ #endif #include -#include #include #include +#include #include "lmms_basics.h" #include "LocklessList.h" @@ -416,7 +416,7 @@ private: bool m_renderOnly; - QVector m_audioPorts; + std::vector m_audioPorts; fpp_t m_framesPerPeriod; @@ -430,7 +430,7 @@ private: surroundSampleFrame * m_outputBufferWrite; // worker thread stuff - QVector m_workers; + std::vector m_workers; int m_numWorkers; // playhandle stuff diff --git a/include/AudioEngineWorkerThread.h b/include/AudioEngineWorkerThread.h index 112930808..16de6ff6f 100644 --- a/include/AudioEngineWorkerThread.h +++ b/include/AudioEngineWorkerThread.h @@ -100,9 +100,9 @@ public: JobQueue::OperationMode _opMode = JobQueue::Static ) { resetJobQueue( _opMode ); - for( typename T::ConstIterator it = _vec.begin(); it != _vec.end(); ++it ) + for (const auto& job : _vec) { - addJob( *it ); + addJob(job); } } diff --git a/include/AudioJack.h b/include/AudioJack.h index 2ef0f665c..164258e5f 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -35,7 +35,7 @@ #endif #include -#include +#include #include "AudioDevice.h" #include "AudioDeviceSetupWidget.h" @@ -117,7 +117,7 @@ private: std::atomic m_stopped; std::atomic m_midiClient; - QVector m_outputPorts; + std::vector m_outputPorts; jack_default_audio_sample_t * * m_tempOutBufs; surroundSampleFrame * m_outBuf; diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 1e6d5eda3..2b93a514c 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -78,7 +78,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject Q_OBJECT MM_OPERATORS public: - using AutoModelVector = QVector; + using AutoModelVector = std::vector; enum ScaleType { diff --git a/include/AutomationClip.h b/include/AutomationClip.h index 10c714c3d..fc6a26d0e 100644 --- a/include/AutomationClip.h +++ b/include/AutomationClip.h @@ -62,7 +62,7 @@ public: } ; using timeMap = QMap; - using objectVector = QVector>; + using objectVector = std::vector>; using TimemapIterator = timeMap::const_iterator; @@ -167,7 +167,7 @@ public: static bool isAutomated( const AutomatableModel * _m ); - static QVector clipsForModel( const AutomatableModel * _m ); + static std::vector clipsForModel(const AutomatableModel* _m); static AutomationClip * globalAutomationClip( AutomatableModel * _m ); static void resolveAllIDs(); @@ -190,6 +190,15 @@ private: void generateTangents(timeMap::iterator it, int numToGenerate); float valueAt( timeMap::const_iterator v, int offset ) const; + /** + * @brief + * This function combines the song tracks, pattern store tracks, + * and the global automation track all in one vector. + * + * @return std::vector + */ + static std::vector combineAllTracks(); + // Mutex to make methods involving automation clips thread safe // Mutable so we can lock it from const objects #if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) @@ -199,7 +208,7 @@ private: #endif AutomationTrack * m_autoTrack; - QVector m_idsToResolve; + std::vector m_idsToResolve; objectVector m_objects; timeMap m_timeMap; // actual values timeMap m_oldTimeMap; // old values for storing the values before setDragValue() is called. diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index c2ab98092..a1c4d694f 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -27,6 +27,7 @@ #define LMMS_GUI_AUTOMATION_EDITOR_H #include +#include #include "Editor.h" @@ -180,7 +181,7 @@ private: ComboBoxModel m_zoomingYModel; ComboBoxModel m_quantizeModel; - static const QVector m_zoomXLevels; + static const std::array m_zoomXLevels; FloatModel * m_tensionModel; diff --git a/include/ConfigManager.h b/include/ConfigManager.h index eee069681..f6239c297 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -30,9 +30,9 @@ #include #include #include -#include #include +#include #include "lmms_export.h" @@ -239,11 +239,8 @@ public: void addRecentlyOpenedProject(const QString & _file); - const QString & value(const QString & cls, - const QString & attribute) const; - const QString & value(const QString & cls, - const QString & attribute, - const QString & defaultVal) const; + QString value(const QString& cls, const QString& attribute, const QString& defaultVal = "") const; + void setValue(const QString & cls, const QString & attribute, const QString & value); void deleteValue(const QString & cls, const QString & attribute); @@ -302,7 +299,7 @@ private: unsigned int m_configVersion; QStringList m_recentlyOpenedProjects; - using stringPairVector = QVector>; + using stringPairVector = std::vector>; using settingsMap = QMap; settingsMap m_settings; diff --git a/include/Controller.h b/include/Controller.h index 0a5eb0f36..b176b1f74 100644 --- a/include/Controller.h +++ b/include/Controller.h @@ -45,7 +45,7 @@ class ControllerDialog; } // namespace gui -using ControllerVector = QVector; +using ControllerVector = std::vector; class LMMS_EXPORT Controller : public Model, public JournallingObject { diff --git a/include/ControllerConnection.h b/include/ControllerConnection.h index 4473b68e0..b60687123 100644 --- a/include/ControllerConnection.h +++ b/include/ControllerConnection.h @@ -30,12 +30,13 @@ #define LMMS_CONTROLLER_CONNECTION_H #include -#include #include "Controller.h" #include "JournallingObject.h" #include "ValueBuffer.h" +#include + namespace lmms { @@ -46,7 +47,7 @@ namespace gui class ControllerConnectionDialog; } -using ControllerConnectionVector = QVector; +using ControllerConnectionVector = std::vector; class LMMS_EXPORT ControllerConnection : public QObject, public JournallingObject { diff --git a/include/EffectChain.h b/include/EffectChain.h index 57cf8d547..f9482174e 100644 --- a/include/EffectChain.h +++ b/include/EffectChain.h @@ -69,7 +69,7 @@ public: private: - using EffectList = QVector; + using EffectList = std::vector; EffectList m_effects; BoolModel m_enabledModel; diff --git a/include/EnvelopeAndLfoParameters.h b/include/EnvelopeAndLfoParameters.h index a713a90f3..02abd07e3 100644 --- a/include/EnvelopeAndLfoParameters.h +++ b/include/EnvelopeAndLfoParameters.h @@ -25,7 +25,7 @@ #ifndef LMMS_ENVELOPE_AND_LFO_PARAMETERS_H #define LMMS_ENVELOPE_AND_LFO_PARAMETERS_H -#include +#include #include "JournallingObject.h" #include "AutomatableModel.h" diff --git a/include/InstrumentFunctions.h b/include/InstrumentFunctions.h index f62b74e9a..61d625d83 100644 --- a/include/InstrumentFunctions.h +++ b/include/InstrumentFunctions.h @@ -119,7 +119,7 @@ public: }; - struct ChordTable : public QVector + struct ChordTable { private: ChordTable(); @@ -131,6 +131,7 @@ public: }; static std::array s_initTable; + std::vector m_chords; public: static const ChordTable & getInstance() @@ -150,6 +151,11 @@ public: { return getByName( name, false ); } + + const std::vector& chords() const + { + return m_chords; + } }; diff --git a/include/MidiClient.h b/include/MidiClient.h index 3a64f709c..1eb04402b 100644 --- a/include/MidiClient.h +++ b/include/MidiClient.h @@ -26,7 +26,7 @@ #define LMMS_MIDI_CLIENT_H #include -#include +#include #include "MidiEvent.h" @@ -111,7 +111,7 @@ public: static MidiClient * openMidiClient(); protected: - QVector m_midiPorts; + std::vector m_midiPorts; } ; diff --git a/include/Mixer.h b/include/Mixer.h index 6589836c4..35787a414 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -39,7 +39,7 @@ namespace lmms class MixerRoute; -using MixerRouteVector = QVector; +using MixerRouteVector = std::vector; class MixerChannel : public ThreadableJob { @@ -219,7 +219,7 @@ public: private: // the mixer channels in the mixer. index 0 is always master. - QVector m_mixerChannels; + std::vector m_mixerChannels; // make sure we have at least num channels void allocateChannelsTo(int num); diff --git a/include/Note.h b/include/Note.h index 15aeaa2ce..dd261b3af 100644 --- a/include/Note.h +++ b/include/Note.h @@ -27,7 +27,7 @@ #define LMMS_NOTE_H #include -#include +#include #include "volume.h" #include "panning.h" @@ -249,7 +249,7 @@ private: DetuningHelper * m_detuning; }; -using NoteVector = QVector; +using NoteVector = std::vector; struct NoteBounds { diff --git a/include/PeakController.h b/include/PeakController.h index 357e2a95d..de9da3b1c 100644 --- a/include/PeakController.h +++ b/include/PeakController.h @@ -36,7 +36,7 @@ namespace lmms class PeakControllerEffect; -using PeakControllerEffectVector = QVector; +using PeakControllerEffectVector = std::vector; class LMMS_EXPORT PeakController : public Controller { diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 6100792d5..03b93d816 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -27,9 +27,10 @@ #ifndef LMMS_GUI_PIANO_ROLL_H #define LMMS_GUI_PIANO_ROLL_H -#include #include +#include + #include "Editor.h" #include "ComboBoxModel.h" #include "SerializingObject.h" @@ -291,7 +292,7 @@ private: PositionLine * m_positionLine; - QVector m_nemStr; // gui names of each edit mode + std::vector m_nemStr; // gui names of each edit mode QMenu * m_noteEditMenu; // when you right click below the key area QList m_markedSemiTones; @@ -358,8 +359,8 @@ private: ComboBoxModel m_chordModel; ComboBoxModel m_snapModel; - static const QVector m_zoomLevels; - static const QVector m_zoomYLevels; + static const std::vector m_zoomLevels; + static const std::vector m_zoomYLevels; MidiClip* m_midiClip; NoteVector m_ghostNotes; diff --git a/include/PluginFactory.h b/include/PluginFactory.h index 001ea190a..10c76e4ee 100644 --- a/include/PluginFactory.h +++ b/include/PluginFactory.h @@ -27,12 +27,12 @@ #include #include +#include #include #include #include #include -#include #include "lmms_export.h" #include "Plugin.h" @@ -99,7 +99,7 @@ private: PluginInfoList m_pluginInfos; QMap m_pluginByExt; - QVector m_garbage; //!< cleaned up at destruction + std::vector m_garbage; //!< cleaned up at destruction QHash m_errors; diff --git a/include/RenderManager.h b/include/RenderManager.h index 5240e96a8..43f696f14 100644 --- a/include/RenderManager.h +++ b/include/RenderManager.h @@ -78,8 +78,8 @@ private: std::unique_ptr m_activeRenderer; - QVector m_tracksToRender; - QVector m_unmuted; + std::vector m_tracksToRender; + std::vector m_unmuted; } ; diff --git a/include/StepRecorder.h b/include/StepRecorder.h index 456e69a69..55435617c 100644 --- a/include/StepRecorder.h +++ b/include/StepRecorder.h @@ -59,7 +59,7 @@ class StepRecorder : public QObject void setCurrentMidiClip(MidiClip* newMidiClip); void setStepsLength(const TimePos& newLength); - QVector getCurStepNotes(); + std::vector getCurStepNotes(); bool isRecording() const { @@ -142,7 +142,7 @@ class StepRecorder : public QObject QElapsedTimer releasedTimer; } ; - QVector m_curStepNotes; // contains the current recorded step notes (i.e. while user still press the notes; before they are applied to the clip) + std::vector m_curStepNotes; // contains the current recorded step notes (i.e. while user still press the notes; before they are applied to the clip) StepNote* findCurStepNote(const int key); diff --git a/include/Track.h b/include/Track.h index ff3c1ae00..000d564f7 100644 --- a/include/Track.h +++ b/include/Track.h @@ -25,7 +25,8 @@ #ifndef LMMS_TRACK_H #define LMMS_TRACK_H -#include +#include + #include #include "AutomatableModel.h" @@ -69,7 +70,7 @@ class LMMS_EXPORT Track : public Model, public JournallingObject mapPropertyFromModel(bool,isMuted,setMuted,m_mutedModel); mapPropertyFromModel(bool,isSolo,setSolo,m_soloModel); public: - using clipVector = QVector; + using clipVector = std::vector; enum TrackTypes { diff --git a/include/TrackContainer.h b/include/TrackContainer.h index f99273ba2..8739a9e9f 100644 --- a/include/TrackContainer.h +++ b/include/TrackContainer.h @@ -49,7 +49,7 @@ class LMMS_EXPORT TrackContainer : public Model, public JournallingObject { Q_OBJECT public: - using TrackList = QVector; + using TrackList = std::vector; enum TrackContainerTypes { PatternContainer, diff --git a/include/panning.h b/include/panning.h index 56ca04eee..0fd74d1cc 100644 --- a/include/panning.h +++ b/include/panning.h @@ -31,6 +31,8 @@ #include "Midi.h" #include "volume.h" +#include + namespace lmms { @@ -40,7 +42,7 @@ inline StereoVolumeVector panningToVolumeVector( panning_t _p, { StereoVolumeVector v = { { _scale, _scale } }; const float pf = _p / 100.0f; - v.vol[_p >= PanningCenter ? 0 : 1] *= 1.0f - qAbs( pf ); + v.vol[_p >= PanningCenter ? 0 : 1] *= 1.0f - std::abs(pf); return v; } diff --git a/plugins/PeakControllerEffect/PeakControllerEffect.cpp b/plugins/PeakControllerEffect/PeakControllerEffect.cpp index d32a22320..bd99631b7 100644 --- a/plugins/PeakControllerEffect/PeakControllerEffect.cpp +++ b/plugins/PeakControllerEffect/PeakControllerEffect.cpp @@ -76,7 +76,7 @@ PeakControllerEffect::PeakControllerEffect( { Engine::getSong()->addController( m_autoController ); } - PeakController::s_effects.append( this ); + PeakController::s_effects.push_back(this); } @@ -84,11 +84,11 @@ PeakControllerEffect::PeakControllerEffect( PeakControllerEffect::~PeakControllerEffect() { - int idx = PeakController::s_effects.indexOf( this ); - if( idx >= 0 ) + auto it = std::find(PeakController::s_effects.begin(), PeakController::s_effects.end(), this); + if (it != PeakController::s_effects.end()) { - PeakController::s_effects.remove( idx ); - Engine::getSong()->removeController( m_autoController ); + PeakController::s_effects.erase(it); + Engine::getSong()->removeController(m_autoController); } } diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index a91e88f38..91a16ed12 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -419,7 +419,7 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer() } // STAGE 2: process effects of all instrument- and sampletracks - AudioEngineWorkerThread::fillJobQueue >( m_audioPorts ); + AudioEngineWorkerThread::fillJobQueue(m_audioPorts); AudioEngineWorkerThread::startAndWaitForJobs(); @@ -663,7 +663,7 @@ void AudioEngine::removeAudioPort(AudioPort * port) { requestChangeInModel(); - QVector::Iterator it = std::find(m_audioPorts.begin(), m_audioPorts.end(), port); + auto it = std::find(m_audioPorts.begin(), m_audioPorts.end(), port); if (it != m_audioPorts.end()) { m_audioPorts.erase(it); diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 378ccd91e..fa4697c69 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -71,7 +71,7 @@ AutomatableModel::~AutomatableModel() { while( m_linkedModels.empty() == false ) { - m_linkedModels.last()->unlinkModel( this ); + m_linkedModels.back()->unlinkModel(this); m_linkedModels.erase( m_linkedModels.end() - 1 ); } @@ -473,7 +473,8 @@ float AutomatableModel::fittedValue( float value ) const void AutomatableModel::linkModel( AutomatableModel* model ) { - if( !m_linkedModels.contains( model ) && model != this ) + auto containsModel = std::find(m_linkedModels.begin(), m_linkedModels.end(), model) != m_linkedModels.end(); + if (!containsModel && model != this) { m_linkedModels.push_back( model ); @@ -490,7 +491,7 @@ void AutomatableModel::linkModel( AutomatableModel* model ) void AutomatableModel::unlinkModel( AutomatableModel* model ) { - AutoModelVector::Iterator it = std::find( m_linkedModels.begin(), m_linkedModels.end(), model ); + auto it = std::find(m_linkedModels.begin(), m_linkedModels.end(), model); if( it != m_linkedModels.end() ) { m_linkedModels.erase( it ); @@ -504,7 +505,8 @@ void AutomatableModel::unlinkModel( AutomatableModel* model ) void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* model2 ) { - if (!model1->m_linkedModels.contains( model2 ) && model1 != model2) + auto model1ContainsModel2 = std::find(model1->m_linkedModels.begin(), model1->m_linkedModels.end(), model2) != model1->m_linkedModels.end(); + if (!model1ContainsModel2 && model1 != model2) { // copy data model1->m_value = model2->m_value; @@ -588,7 +590,7 @@ float AutomatableModel::controllerValue( int frameOffset ) const return v; } - AutomatableModel* lm = m_linkedModels.first(); + AutomatableModel* lm = m_linkedModels.front(); if (lm->controllerConnection() && lm->useControllerValue()) { return fittedValue( lm->controllerValue( frameOffset ) ); @@ -649,7 +651,7 @@ ValueBuffer * AutomatableModel::valueBuffer() AutomatableModel* lm = nullptr; if (hasLinkedModels()) { - lm = m_linkedModels.first(); + lm = m_linkedModels.front(); } if (lm && lm->controllerConnection() && lm->useControllerValue() && lm->controllerConnection()->getController()->isSampleExact()) @@ -721,8 +723,8 @@ void AutomatableModel::reset() float AutomatableModel::globalAutomationValueAt( const TimePos& time ) { // get clips that connect to this model - QVector clips = AutomationClip::clipsForModel( this ); - if( clips.isEmpty() ) + auto clips = AutomationClip::clipsForModel(this); + if (clips.empty()) { // if no such clips exist, return current value return m_value; @@ -731,17 +733,17 @@ float AutomatableModel::globalAutomationValueAt( const TimePos& time ) { // of those clips: // find the clips which overlap with the time position - QVector clipsInRange; + std::vector clipsInRange; for (const auto& clip : clips) { int s = clip->startPosition(); int e = clip->endPosition(); - if (s <= time && e >= time) { clipsInRange += clip; } + if (s <= time && e >= time) { clipsInRange.push_back(clip); } } AutomationClip * latestClip = nullptr; - if( ! clipsInRange.isEmpty() ) + if (!clipsInRange.empty()) { // if there are more than one overlapping clips, just use the first one because // multiple clip behaviour is undefined anyway diff --git a/src/core/AutomationClip.cpp b/src/core/AutomationClip.cpp index c031c5a41..7a161c355 100644 --- a/src/core/AutomationClip.cpp +++ b/src/core/AutomationClip.cpp @@ -120,19 +120,19 @@ bool AutomationClip::addObject( AutomatableModel * _obj, bool _search_dup ) { QMutexLocker m(&m_clipMutex); - if( _search_dup && m_objects.contains(_obj) ) + if (_search_dup && std::find(m_objects.begin(), m_objects.end(), _obj) != m_objects.end()) { return false; } // the automation track is unconnected and there is nothing in the track - if( m_objects.isEmpty() && hasAutomation() == false ) + if (m_objects.empty() && hasAutomation() == false) { // then initialize first value putValue( TimePos(0), _obj->inverseScaledValue( _obj->value() ), false ); } - m_objects += _obj; + m_objects.push_back(_obj); connect( _obj, SIGNAL(destroyed(lmms::jo_id_t)), this, SLOT(objectDestroyed(lmms::jo_id_t)), @@ -184,7 +184,7 @@ const AutomatableModel * AutomationClip::firstObject() const QMutexLocker m(&m_clipMutex); AutomatableModel* model; - if (!m_objects.isEmpty() && (model = m_objects.first()) != nullptr) + if (!m_objects.empty() && (model = m_objects.front()) != nullptr) { return model; } @@ -380,11 +380,11 @@ void AutomationClip::removeNodes(const int tick0, const int tick1) // Make a list of TimePos with nodes to be removed // because we can't simply remove the nodes from // the timeMap while we are iterating it. - QVector nodesToRemove; + std::vector nodesToRemove; for (auto it = m_timeMap.lowerBound(start), endIt = m_timeMap.upperBound(end); it != endIt; ++it) { - nodesToRemove.append(POS(it)); + nodesToRemove.push_back(POS(it)); } for (auto node: nodesToRemove) @@ -831,7 +831,7 @@ void AutomationClip::loadSettings( const QDomElement & _this ) } else if( element.tagName() == "object" ) { - m_idsToResolve << element.attribute( "id" ).toInt(); + m_idsToResolve.push_back(element.attribute("id").toInt()); } } @@ -865,9 +865,9 @@ QString AutomationClip::name() const { return Clip::name(); } - if( !m_objects.isEmpty() && m_objects.first() != nullptr ) + if (!m_objects.empty() && m_objects.front() != nullptr) { - return m_objects.first()->fullDisplayName(); + return m_objects.front()->fullDisplayName(); } return tr( "Drag a control while pressing <%1>" ).arg(UI_CTRL_KEY); } @@ -888,12 +888,8 @@ gui::ClipView * AutomationClip::createView( gui::TrackView * _tv ) bool AutomationClip::isAutomated( const AutomatableModel * _m ) { - TrackContainer::TrackList l; - l += Engine::getSong()->tracks(); - l += Engine::patternStore()->tracks(); - l += Engine::getSong()->globalAutomationTrack(); - - for (const auto& track : l) + auto l = combineAllTracks(); + for (const auto track : l) { if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack) { @@ -921,16 +917,13 @@ bool AutomationClip::isAutomated( const AutomatableModel * _m ) * @brief returns a list of all the automation clips that are connected to a specific model * @param _m the model we want to look for */ -QVector AutomationClip::clipsForModel( const AutomatableModel * _m ) +std::vector AutomationClip::clipsForModel(const AutomatableModel* _m) { - QVector clips; - TrackContainer::TrackList tracks; - tracks += Engine::getSong()->tracks(); - tracks += Engine::patternStore()->tracks(); - tracks += Engine::getSong()->globalAutomationTrack(); + std::vector clips; + auto l = combineAllTracks(); // go through all tracks... - for (const auto& track : tracks) + for (const auto track : l) { // we want only automation tracks... if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack ) @@ -953,7 +946,7 @@ QVector AutomationClip::clipsForModel( const AutomatableModel } } // if the clips is connected to the model, add it to the list - if( has_object ) { clips += a; } + if (has_object) { clips.push_back(a); } } } } @@ -989,9 +982,7 @@ AutomationClip * AutomationClip::globalAutomationClip( void AutomationClip::resolveAllIDs() { - TrackContainer::TrackList l = Engine::getSong()->tracks() + - Engine::patternStore()->tracks(); - l += Engine::getSong()->globalAutomationTrack(); + auto l = combineAllTracks(); for (const auto& track : l) { if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack) @@ -1060,10 +1051,9 @@ void AutomationClip::objectDestroyed( jo_id_t _id ) // when switching samplerate) and real deletions because in the latter // case we had to remove ourselves if we're the global automation // clip of the destroyed object - m_idsToResolve += _id; + m_idsToResolve.push_back(_id); - for( objectVector::Iterator objIt = m_objects.begin(); - objIt != m_objects.end(); objIt++ ) + for (auto objIt = m_objects.begin(); objIt != m_objects.end(); objIt++) { Q_ASSERT( !(*objIt).isNull() ); if( (*objIt)->id() == _id ) @@ -1173,4 +1163,18 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate) } } +std::vector AutomationClip::combineAllTracks() +{ + std::vector combinedTrackList; + + auto& songTracks = Engine::getSong()->tracks(); + auto& patternStoreTracks = Engine::patternStore()->tracks(); + + combinedTrackList.insert(combinedTrackList.end(), songTracks.begin(), songTracks.end()); + combinedTrackList.insert(combinedTrackList.end(), patternStoreTracks.begin(), patternStoreTracks.end()); + combinedTrackList.push_back(Engine::getSong()->globalAutomationTrack()); + + return combinedTrackList; +} + } // namespace lmms diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index bfe9e31f1..c647a27e6 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -334,10 +334,9 @@ void ConfigManager::addRecentlyOpenedProject(const QString & file) -const QString & ConfigManager::value(const QString & cls, - const QString & attribute) const +QString ConfigManager::value(const QString& cls, const QString& attribute, const QString& defaultVal) const { - if(m_settings.contains(cls)) + if (m_settings.find(cls) != m_settings.end()) { for (const auto& setting : m_settings[cls]) { @@ -347,18 +346,7 @@ const QString & ConfigManager::value(const QString & cls, } } } - static QString empty; - return empty; -} - - - -const QString & ConfigManager::value(const QString & cls, - const QString & attribute, - const QString & defaultVal) const -{ - const QString & val = value(cls, attribute); - return val.isEmpty() ? defaultVal : val; + return defaultVal; } diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index aee6481cc..e7031f20c 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -25,8 +25,8 @@ */ #include -#include +#include #include "AudioEngine.h" #include "ControllerConnection.h" @@ -40,7 +40,7 @@ namespace lmms long Controller::s_periods = 0; -QVector Controller::s_controllers; +std::vector Controller::s_controllers; @@ -55,7 +55,7 @@ Controller::Controller( ControllerTypes _type, Model * _parent, { if( _type != DummyController && _type != MidiController ) { - s_controllers.append( this ); + s_controllers.push_back(this); // Determine which name to use for ( uint i=s_controllers.size(); ; i++ ) { @@ -86,10 +86,10 @@ Controller::Controller( ControllerTypes _type, Model * _parent, Controller::~Controller() { - int idx = s_controllers.indexOf( this ); - if( idx >= 0 ) + auto it = std::find(s_controllers.begin(), s_controllers.end(), this); + if (it != s_controllers.end()) { - s_controllers.remove( idx ); + s_controllers.erase(it); } m_valueBuffer.clear(); diff --git a/src/core/ControllerConnection.cpp b/src/core/ControllerConnection.cpp index c65cd8ae9..a4477c563 100644 --- a/src/core/ControllerConnection.cpp +++ b/src/core/ControllerConnection.cpp @@ -53,7 +53,7 @@ ControllerConnection::ControllerConnection(Controller * _controller) : m_controller = Controller::create( Controller::DummyController, nullptr ); } - s_connections.append( this ); + s_connections.push_back(this); } @@ -64,7 +64,7 @@ ControllerConnection::ControllerConnection( int _controllerId ) : m_controllerId( _controllerId ), m_ownsController( false ) { - s_connections.append( this ); + s_connections.push_back(this); } @@ -76,7 +76,10 @@ ControllerConnection::~ControllerConnection() { m_controller->removeConnection( this ); } - s_connections.remove( s_connections.indexOf( this ) ); + + auto it = std::find(s_connections.begin(), s_connections.end(), this); + if (it != s_connections.end()) { s_connections.erase(it); }; + if( m_ownsController ) { delete m_controller; @@ -186,10 +189,12 @@ void ControllerConnection::saveSettings( QDomDocument & _doc, QDomElement & _thi } else { - int id = Engine::getSong()->controllers().indexOf( m_controller ); - if( id >= 0 ) + const auto& controllers = Engine::getSong()->controllers(); + const auto it = std::find(controllers.begin(), controllers.end(), m_controller); + if (it != controllers.end()) { - _this.setAttribute( "id", id ); + const int id = std::distance(controllers.begin(), it); + _this.setAttribute("id", id); } } } diff --git a/src/core/EffectChain.cpp b/src/core/EffectChain.cpp index 024ddab64..b07a7227b 100644 --- a/src/core/EffectChain.cpp +++ b/src/core/EffectChain.cpp @@ -56,7 +56,7 @@ EffectChain::~EffectChain() void EffectChain::saveSettings( QDomDocument & _doc, QDomElement & _this ) { m_enabledModel.saveSettings( _doc, _this, "enabled" ); - _this.setAttribute( "numofeffects", m_effects.count() ); + _this.setAttribute("numofeffects", static_cast(m_effects.size())); for( Effect* effect : m_effects) { @@ -121,7 +121,7 @@ void EffectChain::loadSettings( const QDomElement & _this ) void EffectChain::appendEffect( Effect * _effect ) { Engine::audioEngine()->requestChangeInModel(); - m_effects.append( _effect ); + m_effects.push_back(_effect); Engine::audioEngine()->doneChangeInModel(); m_enabledModel.setValue( true ); @@ -136,7 +136,7 @@ void EffectChain::removeEffect( Effect * _effect ) { Engine::audioEngine()->requestChangeInModel(); - Effect ** found = std::find( m_effects.begin(), m_effects.end(), _effect ); + auto found = std::find(m_effects.begin(), m_effects.end(), _effect); if( found == m_effects.end() ) { Engine::audioEngine()->doneChangeInModel(); @@ -146,7 +146,7 @@ void EffectChain::removeEffect( Effect * _effect ) Engine::audioEngine()->doneChangeInModel(); - if( m_effects.isEmpty() ) + if (m_effects.empty()) { m_enabledModel.setValue( false ); } @@ -159,10 +159,10 @@ void EffectChain::removeEffect( Effect * _effect ) void EffectChain::moveDown( Effect * _effect ) { - if( _effect != m_effects.last() ) + if (_effect != m_effects.back()) { - int i = m_effects.indexOf(_effect); - std::swap(m_effects[i + 1], m_effects[i]); + auto it = std::find(m_effects.begin(), m_effects.end(), _effect); + std::swap(*std::next(it), *it); } } @@ -171,10 +171,10 @@ void EffectChain::moveDown( Effect * _effect ) void EffectChain::moveUp( Effect * _effect ) { - if( _effect != m_effects.first() ) + if (_effect != m_effects.front()) { - int i = m_effects.indexOf(_effect); - std::swap(m_effects[i - 1], m_effects[i]); + auto it = std::find(m_effects.begin(), m_effects.end(), _effect); + std::swap(*std::prev(it), *it); } } @@ -228,9 +228,9 @@ void EffectChain::clear() Engine::audioEngine()->requestChangeInModel(); - while( m_effects.count() ) + while (m_effects.size()) { - Effect * e = m_effects[m_effects.count() - 1]; + auto e = m_effects[m_effects.size() - 1]; m_effects.pop_back(); delete e; } diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 06e56666c..d2fea9dbd 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -177,12 +177,11 @@ bool InstrumentFunctionNoteStacking::Chord::hasSemiTone( int8_t semi_tone ) cons -InstrumentFunctionNoteStacking::ChordTable::ChordTable() : - QVector() +InstrumentFunctionNoteStacking::ChordTable::ChordTable() { for (const auto& chord : s_initTable) { - push_back(Chord(chord.m_name, chord.m_semiTones)); + m_chords.emplace_back(chord.m_name, chord.m_semiTones); } } @@ -191,10 +190,12 @@ InstrumentFunctionNoteStacking::ChordTable::ChordTable() : const InstrumentFunctionNoteStacking::Chord & InstrumentFunctionNoteStacking::ChordTable::getByName( const QString & name, bool is_scale ) const { - for( int i = 0; i < size(); i++ ) + for (const auto& chord : m_chords) { - if( at( i ).getName() == name && is_scale == at( i ).isScale() ) - return at( i ); + if (chord.getName() == name && is_scale == chord.isScale()) + { + return chord; + } } static Chord empty; @@ -211,9 +212,10 @@ InstrumentFunctionNoteStacking::InstrumentFunctionNoteStacking( Model * _parent m_chordRangeModel( 1.0f, 1.0f, 9.0f, 1.0f, this, tr( "Chord range" ) ) { const ChordTable & chord_table = ChordTable::getInstance(); - for( int i = 0; i < chord_table.size(); ++i ) + + for (const auto& chord : chord_table.chords()) { - m_chordsModel.addItem( chord_table[i].getName() ); + m_chordsModel.addItem(chord.getName()); } } @@ -244,10 +246,10 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n ) const int sub_note_key_base = base_note_key + octave_cnt * KeysPerOctave; // process all notes in the chord - for( int i = 0; i < chord_table[selected_chord].size(); ++i ) + for( int i = 0; i < chord_table.chords()[selected_chord].size(); ++i ) { // add interval to sub-note-key - const int sub_note_key = sub_note_key_base + (int) chord_table[selected_chord][i]; + const int sub_note_key = sub_note_key_base + (int) chord_table.chords()[selected_chord][i]; // maybe we're out of range -> let's get outta // here! if( sub_note_key > NumKeys ) @@ -309,9 +311,9 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) : m_arpModeModel( this, tr( "Arpeggio mode" ) ) { const InstrumentFunctionNoteStacking::ChordTable & chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance(); - for( int i = 0; i < chord_table.size(); ++i ) + for (auto& chord : chord_table.chords()) { - m_arpModel.addItem( chord_table[i].getName() ); + m_arpModel.addItem(chord.getName()); } m_arpDirectionModel.addItem( tr( "Up" ), std::make_unique( "arp_up" ) ); @@ -362,7 +364,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) } const InstrumentFunctionNoteStacking::ChordTable & chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance(); - const int cur_chord_size = chord_table[selected_arp].size(); + const int cur_chord_size = chord_table.chords()[selected_arp].size(); const int range = static_cast(cur_chord_size * m_arpRangeModel.value() * m_arpRepeatsModel.value()); const int total_range = range * cnphv.size(); @@ -485,7 +487,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) // now calculate final key for our arp-note const int sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size ) * - KeysPerOctave + chord_table[selected_arp][cur_arp_idx % cur_chord_size]; + KeysPerOctave + chord_table.chords()[selected_arp][cur_arp_idx % cur_chord_size]; // range-checking if( sub_note_key >= NumKeys || diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index b2a9f9e3f..dba5c334e 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -201,9 +201,9 @@ Mixer::Mixer() : Mixer::~Mixer() { - while( ! m_mixerRoutes.isEmpty() ) + while (!m_mixerRoutes.empty()) { - deleteChannelSend( m_mixerRoutes.first() ); + deleteChannelSend(m_mixerRoutes.front()); } while( m_mixerChannels.size() ) { @@ -293,8 +293,11 @@ void Mixer::deleteChannel( int index ) // go through every instrument and adjust for the channel index change TrackContainer::TrackList tracks; - tracks += Engine::getSong()->tracks(); - tracks += Engine::patternStore()->tracks(); + + auto& songTracks = Engine::getSong()->tracks(); + auto& patternStoreTracks = Engine::patternStore()->tracks(); + tracks.insert(tracks.end(), songTracks.begin(), songTracks.end()); + tracks.insert(tracks.end(), patternStoreTracks.begin(), patternStoreTracks.end()); for( Track* t : tracks ) { @@ -335,13 +338,13 @@ void Mixer::deleteChannel( int index ) MixerChannel * ch = m_mixerChannels[index]; // delete all of this channel's sends and receives - while( ! ch->m_sends.isEmpty() ) + while (!ch->m_sends.empty()) { - deleteChannelSend( ch->m_sends.first() ); + deleteChannelSend(ch->m_sends.front()); } - while( ! ch->m_receives.isEmpty() ) + while (!ch->m_receives.empty()) { - deleteChannelSend( ch->m_receives.first() ); + deleteChannelSend(ch->m_receives.front()); } // if m_lastSoloed was our index, reset it @@ -350,7 +353,7 @@ void Mixer::deleteChannel( int index ) else if (m_lastSoloed > index) { --m_lastSoloed; } // actually delete the channel - m_mixerChannels.remove(index); + m_mixerChannels.erase(m_mixerChannels.begin() + index); delete ch; for( int i = index; i < m_mixerChannels.size(); ++i ) @@ -477,13 +480,13 @@ MixerRoute * Mixer::createRoute( MixerChannel * from, MixerChannel * to, float a auto route = new MixerRoute(from, to, amount); // add us to from's sends - from->m_sends.append( route ); + from->m_sends.push_back(route); // add us to to's receives - to->m_receives.append( route ); + to->m_receives.push_back(route); // add us to mixer's list - Engine::mixer()->m_mixerRoutes.append( route ); + Engine::mixer()->m_mixerRoutes.push_back(route); Engine::audioEngine()->doneChangeInModel(); return route; @@ -512,12 +515,22 @@ void Mixer::deleteChannelSend( mix_ch_t fromChannel, mix_ch_t toChannel ) void Mixer::deleteChannelSend( MixerRoute * route ) { Engine::audioEngine()->requestChangeInModel(); + + auto removeFromMixerRoute = [route](MixerRouteVector& routeVec) + { + auto it = std::find(routeVec.begin(), routeVec.end(), route); + if (it != routeVec.end()) { routeVec.erase(it); } + }; + // remove us from from's sends - route->sender()->m_sends.remove( route->sender()->m_sends.indexOf( route ) ); + removeFromMixerRoute(route->sender()->m_sends); + // remove us from to's receives - route->receiver()->m_receives.remove( route->receiver()->m_receives.indexOf( route ) ); + removeFromMixerRoute(route->receiver()->m_receives); + // remove us from mixer's list - Engine::mixer()->m_mixerRoutes.remove( Engine::mixer()->m_mixerRoutes.indexOf( route ) ); + removeFromMixerRoute(Engine::mixer()->m_mixerRoutes); + delete route; Engine::audioEngine()->doneChangeInModel(); } @@ -714,9 +727,9 @@ void Mixer::clearChannel(mix_ch_t index) if( index > 0) { // delete existing sends - while( ! ch->m_sends.isEmpty() ) + while (!ch->m_sends.empty()) { - deleteChannelSend( ch->m_sends.first() ); + deleteChannelSend(ch->m_sends.front()); } // add send to master @@ -724,9 +737,9 @@ void Mixer::clearChannel(mix_ch_t index) } // delete receives - while( ! ch->m_receives.isEmpty() ) + while (!ch->m_receives.empty()) { - deleteChannelSend( ch->m_receives.first() ); + deleteChannelSend(ch->m_receives.front()); } } @@ -835,15 +848,18 @@ void Mixer::validateChannelName( int index, int oldIndex ) bool Mixer::isChannelInUse(int index) { // check if the index mixer channel receives audio from any other channel - if (!m_mixerChannels[index]->m_receives.isEmpty()) + if (!m_mixerChannels[index]->m_receives.empty()) { return true; } // check if the destination mixer channel on any instrument or sample track is the index mixer channel TrackContainer::TrackList tracks; - tracks += Engine::getSong()->tracks(); - tracks += Engine::patternStore()->tracks(); + + auto& songTracks = Engine::getSong()->tracks(); + auto& patternStoreTracks = Engine::patternStore()->tracks(); + tracks.insert(tracks.end(), songTracks.begin(), songTracks.end()); + tracks.insert(tracks.end(), patternStoreTracks.begin(), patternStoreTracks.end()); for (const auto t : tracks) { diff --git a/src/core/PeakController.cpp b/src/core/PeakController.cpp index a9215325e..af0e7e69d 100644 --- a/src/core/PeakController.cpp +++ b/src/core/PeakController.cpp @@ -161,12 +161,11 @@ void PeakController::loadSettings( const QDomElement & _this ) effectId = m_loadCount++; } - PeakControllerEffectVector::Iterator i; - for( i = s_effects.begin(); i != s_effects.end(); ++i ) + for (const auto& effect : s_effects) { - if( (*i)->m_effectId == effectId ) + if (effect->m_effectId == effectId) { - m_peakEffect = *i; + m_peakEffect = effect; return; } } @@ -190,16 +189,14 @@ PeakController * PeakController::getControllerBySetting(const QDomElement & _thi { int effectId = _this.attribute( "effectId" ).toInt(); - PeakControllerEffectVector::Iterator i; - //Backward compatibility for bug in <= 0.4.15 . For >= 1.0.0 , //foundCount should always be 1 because m_effectId is initialized with rand() int foundCount = 0; if( m_buggedFile == false ) { - for( i = s_effects.begin(); i != s_effects.end(); ++i ) + for (const auto& effect : s_effects) { - if( (*i)->m_effectId == effectId ) + if (effect->m_effectId == effectId) { foundCount++; } @@ -208,9 +205,9 @@ PeakController * PeakController::getControllerBySetting(const QDomElement & _thi { m_buggedFile = true; int newEffectId = 0; - for( i = s_effects.begin(); i != s_effects.end(); ++i ) + for (const auto& effect : s_effects) { - (*i)->m_effectId = newEffectId++; + effect->m_effectId = newEffectId++; } QMessageBox msgBox; msgBox.setIcon( QMessageBox::Information ); @@ -231,11 +228,11 @@ PeakController * PeakController::getControllerBySetting(const QDomElement & _thi } m_getCount++; //NB: m_getCount should be increased even m_buggedFile is false - for( i = s_effects.begin(); i != s_effects.end(); ++i ) + for (const auto& effect : s_effects) { - if( (*i)->m_effectId == effectId ) + if (effect->m_effectId == effectId) { - return (*i)->controller(); + return effect->controller(); } } diff --git a/src/core/RenderManager.cpp b/src/core/RenderManager.cpp index 56b4ce9d7..8c031b970 100644 --- a/src/core/RenderManager.cpp +++ b/src/core/RenderManager.cpp @@ -69,7 +69,7 @@ void RenderManager::renderNextTrack() { m_activeRenderer.reset(); - if( m_tracksToRender.isEmpty() ) + if (m_tracksToRender.empty()) { // nothing left to render restoreMutedState(); @@ -169,7 +169,7 @@ void RenderManager::render(QString outputPath) // Unmute all tracks that were muted while rendering tracks void RenderManager::restoreMutedState() { - while( !m_unmuted.isEmpty() ) + while (!m_unmuted.empty()) { Track* restoreTrack = m_unmuted.back(); m_unmuted.pop_back(); diff --git a/src/core/Song.cpp b/src/core/Song.cpp index fef66f193..6f4c5212e 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -840,7 +840,9 @@ bpm_t Song::getTempo() AutomatedValueMap Song::automatedValuesAt(TimePos time, int clipNum) const { - return TrackContainer::automatedValuesFromTracks(TrackList{m_globalAutomationTrack} << tracks(), time, clipNum); + auto trackList = TrackList{m_globalAutomationTrack}; + trackList.insert(trackList.end(), tracks().begin(), tracks().end()); + return TrackContainer::automatedValuesFromTracks(trackList, time, clipNum); } @@ -1326,8 +1328,7 @@ void Song::restoreControllerStates( const QDomElement & element ) else { // Fix indices to ensure correct connections - m_controllers.append(Controller::create( - Controller::DummyController, this)); + m_controllers.push_back(Controller::create(Controller::DummyController, this)); } node = node.nextSibling(); @@ -1445,9 +1446,10 @@ void Song::setProjectFileName(QString const & projectFileName) void Song::addController( Controller * controller ) { - if( controller && !m_controllers.contains( controller ) ) + bool containsController = std::find(m_controllers.begin(), m_controllers.end(), controller) != m_controllers.end(); + if (controller && !containsController) { - m_controllers.append( controller ); + m_controllers.push_back(controller); emit controllerAdded( controller ); this->setModified(); @@ -1459,10 +1461,10 @@ void Song::addController( Controller * controller ) void Song::removeController( Controller * controller ) { - int index = m_controllers.indexOf( controller ); - if( index != -1 ) + auto it = std::find(m_controllers.begin(), m_controllers.end(), controller); + if (it != m_controllers.end()) { - m_controllers.remove( index ); + m_controllers.erase(it); emit controllerRemoved( controller ); delete controller; diff --git a/src/core/StepRecorder.cpp b/src/core/StepRecorder.cpp index e8f31f644..9c2a216ae 100644 --- a/src/core/StepRecorder.cpp +++ b/src/core/StepRecorder.cpp @@ -91,7 +91,7 @@ void StepRecorder::notePressed(const Note & n) StepNote* stepNote = findCurStepNote(n.key()); if(stepNote == nullptr) { - m_curStepNotes.append(new StepNote(Note(m_curStepLength, m_curStepStartPos, n.key(), n.getVolume(), n.getPanning()))); + m_curStepNotes.push_back(new StepNote(Note(m_curStepLength, m_curStepStartPos, n.key(), n.getVolume(), n.getPanning()))); m_pianoRoll.update(); } else if (stepNote->isReleased()) @@ -175,15 +175,15 @@ void StepRecorder::setStepsLength(const TimePos& newLength) updateWidget(); } -QVector StepRecorder::getCurStepNotes() +std::vector StepRecorder::getCurStepNotes() { - QVector notes; + std::vector notes; if(m_isStepInProgress) { - for(StepNote* stepNote: m_curStepNotes) + for (StepNote* stepNote: m_curStepNotes) { - notes.append(&stepNote->m_note); + notes.push_back(&stepNote->m_note); } } @@ -288,18 +288,13 @@ void StepRecorder::removeNotesReleasedForTooLong() int nextTimout = std::numeric_limits::max(); bool notesRemoved = false; - QMutableVectorIterator itr(m_curStepNotes); - while (itr.hasNext()) + for (const auto& stepNote : m_curStepNotes) { - StepNote* stepNote = itr.next(); - - if(stepNote->isReleased()) + if (stepNote->isReleased()) { const int timeSinceReleased = stepNote->timeSinceReleased(); // capture value to avoid wraparound when calculting nextTimout if (timeSinceReleased >= REMOVE_RELEASED_NOTE_TIME_THRESHOLD_MS) { - delete stepNote; - itr.remove(); notesRemoved = true; } else @@ -309,6 +304,17 @@ void StepRecorder::removeNotesReleasedForTooLong() } } + m_curStepNotes.erase(std::remove_if(m_curStepNotes.begin(), m_curStepNotes.end(), [](auto stepNote) + { + bool shouldRemove = stepNote->isReleased() && stepNote->timeSinceReleased() >= REMOVE_RELEASED_NOTE_TIME_THRESHOLD_MS; + if (shouldRemove) + { + delete stepNote; + } + + return shouldRemove; + }), m_curStepNotes.end()); + if(notesRemoved) { m_pianoRoll.update(); diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 084f302f8..33b9c8ef3 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -84,9 +84,9 @@ Track::~Track() lock(); emit destroyedTrack(); - while( !m_clips.isEmpty() ) + while (!m_clips.empty()) { - delete m_clips.last(); + delete m_clips.back(); } m_trackContainer->removeTrack( this ); @@ -365,9 +365,9 @@ void Track::removeClip( Clip * clip ) /*! \brief Remove all Clips from this track */ void Track::deleteClips() { - while( ! m_clips.isEmpty() ) + while (!m_clips.empty()) { - delete m_clips.first(); + delete m_clips.front(); } } diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index 0347773ed..33c695f48 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -196,14 +196,14 @@ void TrackContainer::removeTrack( Track * _track ) // After checking that index != -1, we need to upgrade the lock to a write locker before changing m_tracks. // But since Qt offers no function to promote a read lock to a write lock, we must start with the write locker. QWriteLocker lockTracksAccess(&m_tracksMutex); - int index = m_tracks.indexOf( _track ); - if( index != -1 ) + auto it = std::find(m_tracks.begin(), m_tracks.end(), _track); + if (it != m_tracks.end()) { // If the track is solo, all other tracks are muted. Change this before removing the solo track: if (_track->isSolo()) { _track->setSolo(false); } - m_tracks.remove( index ); + m_tracks.erase(it); lockTracksAccess.unlock(); if( Engine::getSong() ) @@ -226,9 +226,9 @@ void TrackContainer::updateAfterTrackAdd() void TrackContainer::clearAllTracks() { //m_tracksMutex.lockForWrite(); - while( !m_tracks.isEmpty() ) + while (!m_tracks.empty()) { - delete m_tracks.first(); + delete m_tracks.front(); } //m_tracksMutex.unlock(); } @@ -240,7 +240,7 @@ bool TrackContainer::isEmpty() const { for (const auto& track : m_tracks) { - if (!track->getClips().isEmpty()) + if (!track->getClips().empty()) { return false; } @@ -275,7 +275,7 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra track->getClipsInRange(clips, 0, time); } else { Q_ASSERT(track->numOfClips() > clipNum); - clips << track->getClip(clipNum); + clips.push_back(track->getClip(clipNum)); } default: break; diff --git a/src/core/midi/MidiClient.cpp b/src/core/midi/MidiClient.cpp index 78691de6f..030384c5e 100644 --- a/src/core/midi/MidiClient.cpp +++ b/src/core/midi/MidiClient.cpp @@ -71,8 +71,7 @@ void MidiClient::removePort( MidiPort* port ) return; } - QVector::Iterator it = - std::find( m_midiPorts.begin(), m_midiPorts.end(), port ); + auto it = std::find(m_midiPorts.begin(), m_midiPorts.end(), port); if( it != m_midiPorts.end() ) { m_midiPorts.erase( it ); diff --git a/src/gui/clips/AutomationClipView.cpp b/src/gui/clips/AutomationClipView.cpp index 6fa91efdb..a5d415a2e 100644 --- a/src/gui/clips/AutomationClipView.cpp +++ b/src/gui/clips/AutomationClipView.cpp @@ -191,10 +191,10 @@ void AutomationClipView::constructContextMenu( QMenu * _cm ) _cm->addAction( embed::getIconPixmap( "flip_x" ), tr( "Flip Horizontally (Visible)" ), this, SLOT(flipX())); - if( !m_clip->m_objects.isEmpty() ) + if (!m_clip->m_objects.empty()) { _cm->addSeparator(); - auto m = new QMenu(tr("%1 Connections").arg(m_clip->m_objects.count()), _cm); + auto m = new QMenu(tr("%1 Connections").arg(m_clip->m_objects.size()), _cm); for (const auto& object : m_clip->m_objects) { if (object) diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index 8c4f704bc..871466a55 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -545,7 +545,7 @@ DataFile ClipView::createClipDataFiles( { // Insert into the dom under the "clips" element Track* clipTrack = clipView->m_trackView->getTrack(); - int trackIndex = tc->tracks().indexOf( clipTrack ); + int trackIndex = std::distance(tc->tracks().begin(), std::find(tc->tracks().begin(), tc->tracks().end(), clipTrack)); QDomElement clipElement = dataFile.createElement("clip"); clipElement.setAttribute( "trackIndex", trackIndex ); clipElement.setAttribute( "trackType", clipTrack->type() ); @@ -557,12 +557,15 @@ DataFile ClipView::createClipDataFiles( dataFile.content().appendChild( clipParent ); // Add extra metadata needed for calculations later - int initialTrackIndex = tc->tracks().indexOf( t ); - if( initialTrackIndex < 0 ) + + const auto initialTrackIt = std::find(tc->tracks().begin(), tc->tracks().end(), t); + if (initialTrackIt != tc->tracks().end()) { printf("Failed to find selected track in the TrackContainer.\n"); return dataFile; } + + const int initialTrackIndex = std::distance(tc->tracks().begin(), initialTrackIt); QDomElement metadata = dataFile.createElement( "copyMetadata" ); // initialTrackIndex is the index of the track that was touched metadata.setAttribute( "initialTrackIndex", initialTrackIndex ); diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index db56557a4..1289c5626 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -72,7 +72,7 @@ QPixmap * AutomationEditor::s_toolMove = nullptr; QPixmap * AutomationEditor::s_toolYFlip = nullptr; QPixmap * AutomationEditor::s_toolXFlip = nullptr; -const QVector AutomationEditor::m_zoomXLevels = +const std::array AutomationEditor::m_zoomXLevels = { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f }; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 337b36b36..753dfe77f 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -148,15 +148,14 @@ std::array PianoRoll::prKeyOrder const int DEFAULT_PR_PPB = DEFAULT_CELL_WIDTH * DefaultStepsPerBar; -const QVector PianoRoll::m_zoomLevels = +const std::vector PianoRoll::m_zoomLevels = {0.125f, 0.25f, 0.5f, 1.0f, 1.5f, 2.0f, 4.0f, 8.0f}; -const QVector PianoRoll::m_zoomYLevels = +const std::vector PianoRoll::m_zoomYLevels = {0.25f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 4.0f}; PianoRoll::PianoRoll() : - m_nemStr( QVector() ), m_noteEditMenu( nullptr ), m_semiToneMarkerMenu( nullptr ), m_zoomingModel(), @@ -406,7 +405,7 @@ PianoRoll::PianoRoll() : InstrumentFunctionNoteStacking::ChordTable::getInstance(); m_scaleModel.addItem( tr("No scale") ); - for( const InstrumentFunctionNoteStacking::Chord& chord : chord_table ) + for (const InstrumentFunctionNoteStacking::Chord& chord : chord_table.chords()) { if( chord.isScale() ) { @@ -423,7 +422,7 @@ PianoRoll::PianoRoll() : // Set up chord model m_chordModel.addItem( tr("No chord") ); - for( const InstrumentFunctionNoteStacking::Chord& chord : chord_table ) + for (const InstrumentFunctionNoteStacking::Chord& chord : chord_table.chords()) { if( ! chord.isScale() ) { @@ -775,7 +774,7 @@ void PianoRoll::fitNoteLengths(bool fill) { if (!fill) { break; } // Last notes stretch to end of last bar - length = notes.last()->endPos().nextFullBar() * TimePos::ticksPerBar() - note->pos(); + length = notes.back()->endPos().nextFullBar() * TimePos::ticksPerBar() - note->pos(); } else { @@ -1241,11 +1240,11 @@ void PianoRoll::shiftPos(NoteVector notes, int amount) { m_midiClip->addJournalCheckPoint(); - if (notes.isEmpty()) { + if (notes.empty()) { return; } - auto leftMostPos = notes.first()->pos(); + auto leftMostPos = notes.front()->pos(); //Limit leftwards shifts to prevent moving left of clip start auto shiftAmount = (leftMostPos > -amount) ? amount : -leftMostPos; if (shiftAmount == 0) { return; } @@ -1645,7 +1644,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) if (note) { - n.append(note); + n.push_back(note); updateKnifePos(me); @@ -1723,7 +1722,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) const NoteVector & notes = m_midiClip->notes(); // will be our iterator in the following loop - NoteVector::ConstIterator it = notes.begin()+notes.size()-1; + auto it = notes.begin() + notes.size() - 1; // loop through whole note-vector... for( int i = 0; i < notes.size(); ++i ) @@ -1846,9 +1845,9 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) auto selectedNotes = getSelectedNotes(); - m_moveBoundaryLeft = selectedNotes.first()->pos().getTicks(); - m_moveBoundaryRight = selectedNotes.first()->endPos(); - m_moveBoundaryBottom = selectedNotes.first()->key(); + m_moveBoundaryLeft = selectedNotes.front()->pos().getTicks(); + m_moveBoundaryRight = selectedNotes.front()->endPos(); + m_moveBoundaryBottom = selectedNotes.front()->key(); m_moveBoundaryTop = m_moveBoundaryBottom; //Figure out the bounding box of all the selected notes @@ -2036,7 +2035,7 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) { if( i->withinRange( ticks_start, ticks_end ) || ( i->selected() && !altPressed ) ) { - nv += i; + nv.push_back(i); } } // make sure we're on a note @@ -2054,8 +2053,8 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) if( dist < closest_dist ) { closest = i; closest_dist = dist; } } // ... then remove all notes from the vector that aren't on the same exact time - NoteVector::Iterator it = nv.begin(); - while( it != nv.end() ) + auto it = nv.begin(); + while (it != nv.end()) { const Note *note = *it; if( note->pos().getTicks() != closest->pos().getTicks() ) @@ -2517,7 +2516,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) bool altPressed = me->modifiers() & Qt::AltModifier; // We iterate from last note in MIDI clip to the first, // chronologically - NoteVector::ConstIterator it = notes.begin()+notes.size()-1; + auto it = notes.begin() + notes.size() - 1; for( int i = 0; i < notes.size(); ++i ) { Note* n = *it; @@ -2579,7 +2578,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) const NoteVector & notes = m_midiClip->notes(); // will be our iterator in the following loop - NoteVector::ConstIterator it = notes.begin()+notes.size()-1; + auto it = notes.begin() + notes.size() - 1; // loop through whole note-vector... for( int i = 0; i < notes.size(); ++i ) @@ -2656,7 +2655,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) const NoteVector & notes = m_midiClip->notes(); // will be our iterator in the following loop - NoteVector::ConstIterator it = notes.begin(); + auto it = notes.begin(); // loop through whole note-vector... while( it != notes.end() ) @@ -3789,7 +3788,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) { if( i->withinRange( ticks_start, ticks_end ) || ( i->selected() && !altPressed ) ) { - nv += i; + nv.push_back(i); } } if( nv.size() > 0 ) diff --git a/src/gui/editors/TrackContainerView.cpp b/src/gui/editors/TrackContainerView.cpp index 38a6a36d5..83017fb1a 100644 --- a/src/gui/editors/TrackContainerView.cpp +++ b/src/gui/editors/TrackContainerView.cpp @@ -199,8 +199,8 @@ void TrackContainerView::moveTrackView( TrackView * trackView, int indexTo ) Track * track = m_tc->m_tracks[indexFrom]; - m_tc->m_tracks.remove( indexFrom ); - m_tc->m_tracks.insert( indexTo, track ); + m_tc->m_tracks.erase(m_tc->m_tracks.begin() + indexFrom); + m_tc->m_tracks.insert(m_tc->m_tracks.begin() + indexTo, track); m_trackViews.move( indexFrom, indexTo ); realignTracks(); diff --git a/src/gui/modals/ControllerConnectionDialog.cpp b/src/gui/modals/ControllerConnectionDialog.cpp index 6396c7ccb..55c0b476f 100644 --- a/src/gui/modals/ControllerConnectionDialog.cpp +++ b/src/gui/modals/ControllerConnectionDialog.cpp @@ -258,10 +258,12 @@ ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent, } else { - int idx = Engine::getSong()->controllers().indexOf( cc->getController() ); + auto& controllers = Engine::getSong()->controllers(); + auto it = std::find(controllers.begin(), controllers.end(), cc->getController()); - if( idx >= 0 ) + if (it != controllers.end()) { + int idx = std::distance(it, controllers.begin()); m_userGroupBox->model()->setValue( true ); m_userController->model()->setValue( idx ); } diff --git a/src/gui/tracks/TrackContentWidget.cpp b/src/gui/tracks/TrackContentWidget.cpp index 26107c1ae..ca7123bda 100644 --- a/src/gui/tracks/TrackContentWidget.cpp +++ b/src/gui/tracks/TrackContentWidget.cpp @@ -345,7 +345,8 @@ bool TrackContentWidget::canPasteSelection( TimePos clipPos, const QMimeData* md // Get the current track's index const TrackContainer::TrackList tracks = t->trackContainer()->tracks(); - const int currentTrackIndex = tracks.indexOf( t ); + const auto currentTrackIt = std::find(tracks.begin(), tracks.end(), t); + const int currentTrackIndex = currentTrackIt != tracks.end() ? std::distance(tracks.begin(), currentTrackIt) : -1; // Don't paste if we're on the same bar and allowSameBar is false auto sourceTrackContainerId = metadata.attributeNode( "trackContainerId" ).value().toUInt(); @@ -443,7 +444,8 @@ bool TrackContentWidget::pasteSelection( TimePos clipPos, const QMimeData * md, // Snap the mouse position to the beginning of the dropped bar, in ticks const TrackContainer::TrackList tracks = getTrack()->trackContainer()->tracks(); - const int currentTrackIndex = tracks.indexOf( getTrack() ); + const auto currentTrackIt = std::find(tracks.begin(), tracks.end(), getTrack()); + const int currentTrackIndex = currentTrackIt != tracks.end() ? std::distance(tracks.begin(), currentTrackIt) : -1; bool wasSelection = m_trackView->trackContainerView()->rubberBand()->selectedObjects().count(); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 7eb6bba11..1d2546288 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -739,7 +739,7 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames, // get all notes from the given clip... const NoteVector & notes = c->notes(); // ...and set our index to zero - NoteVector::ConstIterator nit = notes.begin(); + auto nit = notes.begin(); // very effective algorithm for playing notes that are // posated within the current sample-frame diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index 08e76ae59..52a532028 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -115,11 +115,11 @@ void MidiClip::resizeToFirstTrack() { if (track != m_instrumentTrack) { - unsigned int currentClip = m_instrumentTrack-> - getClips().indexOf(this); - m_steps = static_cast - (track->getClip(currentClip)) - ->m_steps; + const auto& instrumentTrackClips = m_instrumentTrack->getClips(); + const auto currentClipIt = std::find(instrumentTrackClips.begin(), instrumentTrackClips.end(), this); + unsigned int currentClip = currentClipIt != instrumentTrackClips.end() ? + std::distance(instrumentTrackClips.begin(), currentClipIt) : -1; + m_steps = static_cast(track->getClip(currentClip))->m_steps; } break; } @@ -218,17 +218,14 @@ Note * MidiClip::addNote( const Note & _new_note, const bool _quant_pos ) void MidiClip::removeNote( Note * _note_to_del ) { instrumentTrack()->lock(); - NoteVector::Iterator it = m_notes.begin(); - while( it != m_notes.end() ) + + m_notes.erase(std::remove_if(m_notes.begin(), m_notes.end(), [&](Note* note) { - if( *it == _note_to_del ) - { - delete *it; - m_notes.erase( it ); - break; - } - ++it; - } + auto shouldRemove = note == _note_to_del; + if (shouldRemove) { delete note; } + return shouldRemove; + }), m_notes.end()); + instrumentTrack()->unlock(); checkType(); @@ -354,15 +351,13 @@ void MidiClip::setType( MidiClipTypes _new_clip_type ) void MidiClip::checkType() { - NoteVector::Iterator it = m_notes.begin(); - while( it != m_notes.end() ) + for (auto& note : m_notes) { - if( ( *it )->length() > 0 ) + if (note->length() > 0) { - setType( MelodyClip ); + setType(MelodyClip); return; } - ++it; } setType( BeatClip ); } @@ -395,9 +390,9 @@ void MidiClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "steps", m_steps ); // now save settings of all notes - for (const auto& note : m_notes) + for (auto& note : m_notes) { - note->saveState( _doc, _this ); + note->saveState(_doc, _this); } } @@ -477,9 +472,10 @@ MidiClip * MidiClip::nextMidiClip() const MidiClip * MidiClip::adjacentMidiClipByOffset(int offset) const { - QVector clips = m_instrumentTrack->getClips(); + std::vector clips = m_instrumentTrack->getClips(); int clipNum = m_instrumentTrack->getClipNum(this); - return dynamic_cast(clips.value(clipNum + offset, nullptr)); + if (clipNum < 0 || clipNum > clips.size() - 1) { return nullptr; } + return dynamic_cast(clips[clipNum + offset]); } From da14de92fe6f7332206f1b0995f816b8fd54d9cf Mon Sep 17 00:00:00 2001 From: saker Date: Wed, 23 Aug 2023 16:52:35 -0400 Subject: [PATCH 26/45] Fix regressions in #6477 (#6826) * Fix if statement in ClipView * Move controllers.begin() to be the first argument It was the second argument, which means it could've returned negatives for random access iterators. --- src/gui/clips/ClipView.cpp | 2 +- src/gui/modals/ControllerConnectionDialog.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index 871466a55..8a190e2a7 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -559,7 +559,7 @@ DataFile ClipView::createClipDataFiles( // Add extra metadata needed for calculations later const auto initialTrackIt = std::find(tc->tracks().begin(), tc->tracks().end(), t); - if (initialTrackIt != tc->tracks().end()) + if (initialTrackIt == tc->tracks().end()) { printf("Failed to find selected track in the TrackContainer.\n"); return dataFile; diff --git a/src/gui/modals/ControllerConnectionDialog.cpp b/src/gui/modals/ControllerConnectionDialog.cpp index 55c0b476f..040ed9e0f 100644 --- a/src/gui/modals/ControllerConnectionDialog.cpp +++ b/src/gui/modals/ControllerConnectionDialog.cpp @@ -263,7 +263,7 @@ ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent, if (it != controllers.end()) { - int idx = std::distance(it, controllers.begin()); + int idx = std::distance(controllers.begin(), it); m_userGroupBox->model()->setValue( true ); m_userController->model()->setValue( idx ); } From d6cf417a4d07c3f7c36bbfe881d5883c3a68aba0 Mon Sep 17 00:00:00 2001 From: superpaik <68785450+superpaik@users.noreply.github.com> Date: Thu, 24 Aug 2023 18:02:26 +0200 Subject: [PATCH 27/45] Change zoom in SongEditor to a Slider Zoom (#6664) Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> Co-authored-by: Alex --- data/themes/classic/horizontal_slider.png | Bin 0 -> 1444 bytes data/themes/classic/style.css | 19 ++ data/themes/default/horizontal_slider.png | Bin 0 -> 1065 bytes data/themes/default/style.css | 19 ++ include/SongEditor.h | 17 +- src/gui/clips/ClipView.cpp | 9 +- src/gui/editors/SongEditor.cpp | 239 ++++++++++++---------- src/gui/editors/TimeLineWidget.cpp | 22 +- 8 files changed, 198 insertions(+), 127 deletions(-) create mode 100644 data/themes/classic/horizontal_slider.png create mode 100644 data/themes/default/horizontal_slider.png diff --git a/data/themes/classic/horizontal_slider.png b/data/themes/classic/horizontal_slider.png new file mode 100644 index 0000000000000000000000000000000000000000..49d16b9d869e01d2380a90b5615d9cc6d2d10fb0 GIT binary patch literal 1444 zcmeAS@N?(olHy`uVBq!ia0vp^Ahr|-8<6aKa#jsUNtU=qlmzFem6RtIr7}3Cer{{GxPyLrY6bkQqisxQ#zd*q`*i1nqJTosPzr0uz ztlrnx$}_LHBrz{J)zigR321^|W@d_&p`oR#i;*(J3ovn(~mttdZN0qkX~Ox$j9!f75< zZwhX=IOEi-4|I$^C}NQ!8YToxJs>7L*#bH6grAxROzlO$WZnEmY$^i-lZ2;>V@SoV zmf+oew*mxY<7Y3=$WUt8BC=3vnvzKV;rbSZ9}H4I`j^}?Q3!0fbm78>U(B=Cw7gi< z+rs3!X;Fl-0E+;B^IYrXb%x#(8E+S-_nkj8w_2w$cm6|rwWpsey(cYMy&!W|fsNcj z1D>esufOVZHC>v2DM5zEOOB7-Yu)v1rf*fYQQ8&BLd^T`zc)~l;1oNp+L6U0drX3L z*Im6OL7EJu+7cR*ISrz^qb^wXj*HC4Yj-|Yy#6+5pca?!lI zJzu(xKVGQtZAGTkiuWP24)6v9if}!$IQ7&>b)ulTN>A07s=WnPa}{Qv4Jt5^irrc? z^YLogIEVGelqb#gV-E>!t+1)9i`*r*D71qvGmuN z2C;uX&pREjo1`CgnDrFb`moikoi0rGKFQ@OSR~x?Q4m;gplo-m*HWi_nKS=n#+{!S zgqd~@CT#_BK)kuMo0Rp&*QJQnm@9$ZoLr%x%eaKW_!7<`*!r_zN0-$o!)78&q Iol`;+08;c5761SM literal 0 HcmV?d00001 diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 2880fe661..d1f4d0588 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -418,6 +418,25 @@ lmms--gui--AutomatableSlider::handle:vertical { margin: -4px -12px -2px; } +/* main horizontal sliders (zoom) */ + +lmms--gui--AutomatableSlider::groove:horizontal { + background: rgba(0,0,0, 128); + border: 1px inset rgba(100,100,100, 64); + border-radius: 2px; + height: 2px; + margin: 2px; +} + +lmms--gui--AutomatableSlider::handle:horizontal { + background: none; + border-image: url(resources:horizontal_slider.png); + width: 10px; + height: 26px; + border-radius: 2px; + margin: -12px -2px; +} + /* about dialog */ QTabWidget, QTabWidget QWidget { background: #5b6571; diff --git a/data/themes/default/horizontal_slider.png b/data/themes/default/horizontal_slider.png new file mode 100644 index 0000000000000000000000000000000000000000..24f6098e517df6a8ae5c0372b117241437e2f4bb GIT binary patch literal 1065 zcmah|O-K|`9G_Z{TD7Q4W>UileNemevAg5Ur&gz*_o#fI`gKPx9w__ zU?|oh^ddnN9V#OFIw~G4>e3ZS(Ht+OBytTs)q>{O&hZbqPeALtJ(lSWDfKhR+RcSH&2m<9;MDiQcTK@gMOoJ z*a6+c9T{zSKofPUw2EPwWfeIVQaFVZ7*1ftgeWT&Zqjm*YH6UtH8!}) zrd%oR02M(e1L0t$dtLnyLAM{;WW1Xv51%nC9TwapUTMopfn%HpdcuJwS*nd>{1xBR zo`|u0f)5K}xm8rK`k0_3;%rEeg*ekHa1pA^)&G(Af4MXkLwk+=qtTTsJUyPdKDPL< zKKQ`G)9&E0zN)s*;U{r3DaSI!_qkuIt1Z4$#Dlk06P=?oS*Gp7_^vyXQ&Wp~CMRb0 zHqi%s)zwFRo%f^{Z)SJa3F2(s^_5j1jC_B2U~GAuPlAtbNTku zZP{DfZ@4}FsV#T?XYZTT*Jo!_`LPA!^w`Ly<^96g$VBGHg-;LfjX&vJ{<-LzZv5 m_zoomLevels; - bool m_scrollBack; bool m_smoothScroll; @@ -158,14 +158,14 @@ private: QPoint m_mousePos; int m_rubberBandStartTrackview; TimePos m_rubberbandStartTimePos; - int m_currentZoomingValue; + int m_rubberbandPixelsPerBar; //!< pixels per bar when selection starts int m_trackHeadWidth; bool m_selectRegion; friend class SongEditorWindow; signals: - void zoomingValueChanged( float ); + void pixelsPerBarChanged(float); } ; @@ -213,7 +213,7 @@ private: QAction* m_selectModeAction; QAction* m_crtlAction; - ComboBox * m_zoomingComboBox; + AutomatableSlider * m_zoomingSlider; ComboBox * m_snappingComboBox; QLabel* m_snapSizeLabel; @@ -221,7 +221,6 @@ private: QAction* m_removeBarAction; }; - } // namespace gui } // namespace lmms diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index 8a190e2a7..b78605906 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -125,7 +125,7 @@ ClipView::ClipView( Clip * clip, connect( m_clip, SIGNAL(lengthChanged()), this, SLOT(updateLength())); - connect( getGUI()->songEditor()->m_editor->zoomingModel(), SIGNAL(dataChanged()), this, SLOT(updateLength())); + connect(getGUI()->songEditor()->m_editor, &SongEditor::pixelsPerBarChanged, this, &ClipView::updateLength); connect( m_clip, SIGNAL(positionChanged()), this, SLOT(updatePosition())); connect( m_clip, SIGNAL(destroyedClip()), this, SLOT(close())); @@ -314,10 +314,9 @@ void ClipView::updateLength() } else { - setFixedWidth( - static_cast( m_clip->length() * pixelsPerBar() / - TimePos::ticksPerBar() ) + 1 /*+ - BORDER_WIDTH * 2-1*/ ); + // this std::max function is needed for clips that do not start or end on the beat, otherwise, they "disappear" when zooming to min + // 3 is the minimun width needed to make a clip visible + setFixedWidth(std::max(static_cast(m_clip->length() * pixelsPerBar() / TimePos::ticksPerBar() + 1), 3)); } m_trackView->trackContainerView()->update(); } diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 3e62cc238..939c1fdf4 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -24,11 +24,14 @@ #include "SongEditor.h" +#include + #include #include #include #include #include +#include #include #include "ActionGroup.h" @@ -57,14 +60,24 @@ namespace lmms::gui { +namespace +{ + +constexpr int MIN_PIXELS_PER_BAR = 2; +constexpr int MAX_PIXELS_PER_BAR = 400; +constexpr int ZOOM_STEPS = 200; + +constexpr std::array SNAP_SIZES{8.f, 4.f, 2.f, 1.f, 1/2.f, 1/4.f, 1/8.f, 1/16.f}; +constexpr std::array PROPORTIONAL_SNAP_SIZES{64.f, 32.f, 16.f, 8.f, 4.f, 2.f, 1.f, 1/2.f, 1/4.f, 1/8.f, 1/16.f, 1/32.f, 1/64.f}; + +} + -const QVector SongEditor::m_zoomLevels = - { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f }; SongEditor::SongEditor( Song * song ) : TrackContainerView( song ), m_song( song ), - m_zoomingModel(new ComboBoxModel()), + m_zoomingModel(new IntModel(calculateZoomSliderValue(DEFAULT_PIXELS_PER_BAR), 0, ZOOM_STEPS, nullptr, tr("Zoom"))), m_snappingModel(new ComboBoxModel()), m_proportionalSnap( false ), m_scrollBack( false ), @@ -75,7 +88,7 @@ SongEditor::SongEditor( Song * song ) : m_mousePos(), m_rubberBandStartTrackview(0), m_rubberbandStartTimePos(0), - m_currentZoomingValue(m_zoomingModel->value()), + m_rubberbandPixelsPerBar(DEFAULT_PIXELS_PER_BAR), m_trackHeadWidth(ConfigManager::inst()->value("ui", "compacttrackbuttons").toInt()==1 ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT : DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH), @@ -83,6 +96,7 @@ SongEditor::SongEditor( Song * song ) : { m_zoomingModel->setParent(this); m_snappingModel->setParent(this); + m_timeLine = new TimeLineWidget( m_trackHeadWidth, 32, pixelsPerBar(), m_song->m_playPos[Song::Mode_PlaySong], @@ -103,12 +117,15 @@ SongEditor::SongEditor( Song * song ) : m_positionLine = new PositionLine(this); static_cast( layout() )->insertWidget( 1, m_timeLine ); - + connect( m_song, SIGNAL(playbackStateChanged()), m_positionLine, SLOT(update())); - connect( this, SIGNAL(zoomingValueChanged(float)), - m_positionLine, SLOT(zoomChange(float))); - + + // When zoom changes, update position line + // But we must convert pixels per bar to a zoom factor where 1.0 is 100% + connect(this, &SongEditor::pixelsPerBarChanged, m_positionLine, + [this]() { m_positionLine->zoomChange(pixelsPerBar() / float(DEFAULT_PIXELS_PER_BAR)); }); + // Ensure loop markers snap to same increments as clips. Zoom & proportional // snap changes are handled in zoomingChanged() and toggleProportionalSnap() connect(m_snappingModel, &ComboBoxModel::dataChanged, @@ -171,8 +188,8 @@ SongEditor::SongEditor( Song * song ) : SLOT(hideMasterVolumeFloat())); m_mvsStatus = new TextFloat; - m_mvsStatus->setTitle( tr( "Master volume" ) ); - m_mvsStatus->setPixmap( embed::getIconPixmap( "master_volume" ) ); + m_mvsStatus->setTitle(tr("Master volume")); + m_mvsStatus->setPixmap(embed::getIconPixmap("master_volume")); getGUI()->mainWindow()->addWidgetToToolBar( master_vol_lbl ); getGUI()->mainWindow()->addWidgetToToolBar( m_masterVolumeSlider ); @@ -240,33 +257,23 @@ SongEditor::SongEditor( Song * song ) : connect(contentWidget()->verticalScrollBar(), SIGNAL(valueChanged(int)),this, SLOT(updateRubberband())); connect(m_timeLine, SIGNAL(selectionFinished()), this, SLOT(stopSelectRegion())); + //zoom connects + connect(m_zoomingModel, SIGNAL(dataChanged()), this, SLOT(zoomingChanged())); - //Set up zooming model - for( float const & zoomLevel : m_zoomLevels ) + // Set up snapping model + for (float bars : SNAP_SIZES) { - m_zoomingModel->addItem( QString( "%1\%" ).arg( zoomLevel * 100 ) ); - } - m_zoomingModel->setInitValue( - m_zoomingModel->findText( "100%" ) ); - connect( m_zoomingModel, SIGNAL(dataChanged()), - this, SLOT(zoomingChanged())); - connect( m_zoomingModel, SIGNAL(dataChanged()), - m_positionLine, SLOT(update())); - - //Set up snapping model, 2^i - for ( int i = 3; i >= -4; i-- ) - { - if ( i > 0 ) + if (bars > 1.0f) { - m_snappingModel->addItem( QString( "%1 Bars").arg( 1 << i ) ); + m_snappingModel->addItem(QString("%1 Bars").arg(bars)); } - else if ( i == 0 ) + else if (bars == 1.0f) { m_snappingModel->addItem( "1 Bar" ); } else { - m_snappingModel->addItem( QString( "1/%1 Bar" ).arg( 1 << (-i) ) ); + m_snappingModel->addItem(QString("1/%1 Bar").arg(1 / bars)); } } m_snappingModel->setInitValue( m_snappingModel->findText( "1 Bar" ) ); @@ -291,42 +298,40 @@ void SongEditor::loadSettings( const QDomElement& element ) +/*! \brief Return grid size as number of bars */ float SongEditor::getSnapSize() const { - // 1 Bar is the third value in the snapping dropdown - int val = -m_snappingModel->value() + 3; + float snapSize = SNAP_SIZES[m_snappingModel->value()]; + // If proportional snap is on, we snap to finer values when zoomed in if (m_proportionalSnap) { - val = val - m_zoomingModel->value() + 3; + // Finds the closest available snap size + const float optimalSize = snapSize * DEFAULT_PIXELS_PER_BAR / pixelsPerBar(); + return *std::min_element(PROPORTIONAL_SNAP_SIZES.begin(), PROPORTIONAL_SNAP_SIZES.end(), [optimalSize](float a, float b) + { + return std::abs(a - optimalSize) < std::abs(b - optimalSize); + }); } - val = std::max(val, -6); // -6 gives 1/64th bar snapping. Lower values cause crashing. - if ( val >= 0 ){ - return 1 << val; - } - else { - return 1.0 / ( 1 << -val ); - } + return snapSize; } QString SongEditor::getSnapSizeString() const { - int val = -m_snappingModel->value() + 3; - val = val - m_zoomingModel->value() + 3; - val = std::max(val, -6); // -6 gives 1/64th bar snapping. Lower values cause crashing. + float bars = getSnapSize(); - if ( val >= 0 ){ - int bars = 1 << val; - if ( bars == 1 ) { return QString("1 Bar"); } - else - { - return QString( "%1 Bars" ).arg(bars); - } + if (bars < 1) + { + return QString(tr("1/%1 Bar")).arg(round(1 / bars)); } - else { - int div = ( 1 << -val ); - return QString( "1/%1 Bar" ).arg(div); + else if (bars >= 2) + { + return QString(tr("%1 Bars")).arg(bars); + } + else + { + return QString("1 Bar"); } } @@ -367,7 +372,7 @@ void SongEditor::selectRegionFromPixels(int xStart, int xEnd) //we save the position of scrollbars, mouse position and zooming level m_origin = QPoint(xStart, 0); m_scrollPos = QPoint(m_leftRightScroll->value(), contentWidget()->verticalScrollBar()->value()); - m_currentZoomingValue = zoomingModel()->value(); + m_rubberbandPixelsPerBar = pixelsPerBar(); //calculate the song position where the mouse was clicked m_rubberbandStartTimePos = TimePos((xStart - m_trackHeadWidth) @@ -399,10 +404,9 @@ void SongEditor::updateRubberband() int originX = m_origin.x(); //take care of the zooming - if (m_currentZoomingValue != m_zoomingModel->value()) + if (m_rubberbandPixelsPerBar != pixelsPerBar()) { - originX = m_trackHeadWidth + (originX - m_trackHeadWidth) - * m_zoomLevels[m_zoomingModel->value()] / m_zoomLevels[m_currentZoomingValue]; + originX = m_trackHeadWidth + (originX - m_trackHeadWidth) * pixelsPerBar() / m_rubberbandPixelsPerBar; } //take care of the scrollbar position @@ -524,9 +528,13 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) { selectAllClips( false ); } + else if (ke->key() == Qt::Key_0 && ke->modifiers() & Qt::ControlModifier) + { + m_zoomingModel->reset(); + } else { - QWidget::keyPressEvent( ke ); + QWidget::keyPressEvent(ke); } } @@ -535,35 +543,24 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) void SongEditor::wheelEvent( QWheelEvent * we ) { - if( we->modifiers() & Qt::ControlModifier ) + if ((we->modifiers() & Qt::ControlModifier) && (position(we).x() > m_trackHeadWidth)) { - int z = m_zoomingModel->value(); - - if(we->angleDelta().y() > 0) - { - z++; - } - else if(we->angleDelta().y() < 0) - { - z--; - } - z = qBound( 0, z, m_zoomingModel->size() - 1 ); - - int x = position(we).x() - m_trackHeadWidth; // bar based on the mouse x-position where the scroll wheel was used int bar = x / pixelsPerBar(); - // what would be the bar in the new zoom level on the very same mouse x - int newBar = x / DEFAULT_PIXELS_PER_BAR / m_zoomLevels[z]; - // scroll so the bar "selected" by the mouse x doesn't move on the screen + + // move zoom slider (pixelsPerBar will change automatically) + int step = we->modifiers() & Qt::ShiftModifier ? 1 : 5; + // when Alt is pressed, wheelEvent returns delta for x coordinate (mimics horizontal mouse wheel) + int direction = (we->angleDelta().y() + we->angleDelta().x()) > 0 ? 1 : -1; + m_zoomingModel->incValue(step * direction); + + // scroll to zooming around cursor's bar + int newBar = static_cast(x / pixelsPerBar()); m_leftRightScroll->setValue(m_leftRightScroll->value() + bar - newBar); - // update combobox with zooming-factor - m_zoomingModel->setValue( z ); - // update timeline - m_song->m_playPos[Song::Mode_PlaySong].m_timeLine-> - setPixelsPerBar( pixelsPerBar() ); + m_song->m_playPos[Song::Mode_PlaySong].m_timeLine->setPixelsPerBar(pixelsPerBar()); // and make sure, all Clip's are resized and relocated realignTracks(); } @@ -612,7 +609,7 @@ void SongEditor::mousePressEvent(QMouseEvent *me) //we save the position of scrollbars, mouse position and zooming level m_scrollPos = QPoint(m_leftRightScroll->value(), contentWidget()->verticalScrollBar()->value()); m_origin = contentWidget()->mapFromParent(QPoint(me->pos().x(), me->pos().y())); - m_currentZoomingValue = zoomingModel()->value(); + m_rubberbandPixelsPerBar = pixelsPerBar(); //paint the rubberband rubberBand()->setEnabled(true); @@ -655,12 +652,12 @@ void SongEditor::setMasterVolume( int new_val ) { updateMasterVolumeFloat( new_val ); - if( !m_mvsStatus->isVisible() && !m_song->m_loadingProject + if (!m_mvsStatus->isVisible() && !m_song->m_loadingProject && m_masterVolumeSlider->showStatus() ) { - m_mvsStatus->moveGlobal( m_masterVolumeSlider, + m_mvsStatus->moveGlobal(m_masterVolumeSlider, QPoint( m_masterVolumeSlider->width() + 2, -2 ) ); - m_mvsStatus->setVisibilityTimeOut( 1000 ); + m_mvsStatus->setVisibilityTimeOut(1000); } Engine::audioEngine()->setMasterGain( new_val / 100.0f ); } @@ -670,7 +667,7 @@ void SongEditor::setMasterVolume( int new_val ) void SongEditor::showMasterVolumeFloat( void ) { - m_mvsStatus->moveGlobal( m_masterVolumeSlider, + m_mvsStatus->moveGlobal(m_masterVolumeSlider, QPoint( m_masterVolumeSlider->width() + 2, -2 ) ); m_mvsStatus->show(); updateMasterVolumeFloat( m_song->m_masterVolumeModel.value() ); @@ -681,7 +678,7 @@ void SongEditor::showMasterVolumeFloat( void ) void SongEditor::updateMasterVolumeFloat( int new_val ) { - m_mvsStatus->setText( tr( "Value: %1%" ).arg( new_val ) ); + m_mvsStatus->setText(tr("Value: %1%").arg(new_val)); } @@ -837,17 +834,50 @@ void SongEditor::updatePositionLine() +//! Convert zoom slider's value to bar width in pixels +int SongEditor::calculatePixelsPerBar() const +{ + // What we need to raise 2 by to get MIN_PIXELS_PER_BAR and MAX_PIXELS_PER_BAR + static const double minExp = std::log2(MIN_PIXELS_PER_BAR); + static const double maxExp = std::log2(MAX_PIXELS_PER_BAR); + static const double stepsInv = 1 / static_cast(ZOOM_STEPS) * (maxExp - minExp); + double exponent = m_zoomingModel->value() * stepsInv + minExp; + + double ppb = std::exp2(exponent); + + return static_cast(std::round(ppb)); +} + + + + +//! Convert bar width in pixels to zoom slider value +int SongEditor::calculateZoomSliderValue(int pixelsPerBar) const +{ + // What we need to raise 2 by to get MIN_PIXELS_PER_BAR and MAX_PIXELS_PER_BAR + static const double minExp = std::log2(MIN_PIXELS_PER_BAR); + static const double maxExp = std::log2(MAX_PIXELS_PER_BAR); + double exponent = std::log2(pixelsPerBar); + + double sliderValue = (exponent - minExp) / (maxExp - minExp) * ZOOM_STEPS; + + return static_cast(std::round(sliderValue)); +} + + + + void SongEditor::zoomingChanged() { - setPixelsPerBar( m_zoomLevels[m_zoomingModel->value()] * DEFAULT_PIXELS_PER_BAR ); + int ppb = calculatePixelsPerBar(); + setPixelsPerBar(ppb); - m_song->m_playPos[Song::Mode_PlaySong].m_timeLine-> - setPixelsPerBar( pixelsPerBar() ); + m_song->m_playPos[Song::Mode_PlaySong].m_timeLine->setPixelsPerBar(ppb); realignTracks(); updateRubberband(); m_timeLine->setSnapSize(getSnapSize()); - - emit zoomingValueChanged( m_zoomLevels[m_zoomingModel->value()] ); + + emit pixelsPerBarChanged(ppb); } @@ -899,14 +929,6 @@ int SongEditor::indexOfTrackView(const TrackView *tv) -ComboBoxModel *SongEditor::zoomingModel() const -{ - return m_zoomingModel; -} - - - - ComboBoxModel *SongEditor::snappingModel() const { return m_snappingModel; @@ -991,16 +1013,19 @@ SongEditorWindow::SongEditorWindow(Song* song) : auto zoom_lbl = new QLabel(m_toolBar); zoom_lbl->setPixmap( embed::getIconPixmap( "zoom" ) ); - //Set up zooming-stuff - m_zoomingComboBox = new ComboBox( m_toolBar ); - m_zoomingComboBox->setFixedSize( 80, ComboBox::DEFAULT_HEIGHT ); - m_zoomingComboBox->move( 580, 4 ); - m_zoomingComboBox->setModel(m_editor->m_zoomingModel); - m_zoomingComboBox->setToolTip(tr("Horizontal zooming")); - connect(m_editor->zoomingModel(), SIGNAL(dataChanged()), this, SLOT(updateSnapLabel())); + // Set slider zoom + m_zoomingSlider = new AutomatableSlider(m_toolBar, tr("Zoom")); + m_zoomingSlider->setModel(m_editor->m_zoomingModel); + m_zoomingSlider->setOrientation(Qt::Horizontal); + m_zoomingSlider->setPageStep(1); + m_zoomingSlider->setFocusPolicy(Qt::NoFocus); + m_zoomingSlider->setFixedSize(100, 26); + m_zoomingSlider->setToolTip(tr("Zoom")); + m_zoomingSlider->setContextMenuPolicy(Qt::NoContextMenu); + connect(m_editor->m_zoomingModel, SIGNAL(dataChanged()), this, SLOT(updateSnapLabel())); zoomToolBar->addWidget( zoom_lbl ); - zoomToolBar->addWidget( m_zoomingComboBox ); + zoomToolBar->addWidget(m_zoomingSlider); DropToolBar *snapToolBar = addDropToolBarToTop(tr("Snap controls")); auto snap_lbl = new QLabel(m_toolBar); @@ -1034,7 +1059,7 @@ SongEditorWindow::SongEditorWindow(Song* song) : QSize SongEditorWindow::sizeHint() const { - return {720, 300}; + return {900, 300}; } void SongEditorWindow::updateSnapLabel(){ @@ -1140,3 +1165,5 @@ void SongEditorWindow::adjustUiAfterProjectLoad() } // namespace lmms::gui + + diff --git a/src/gui/editors/TimeLineWidget.cpp b/src/gui/editors/TimeLineWidget.cpp index 12b6a11f5..b051fa79b 100644 --- a/src/gui/editors/TimeLineWidget.cpp +++ b/src/gui/editors/TimeLineWidget.cpp @@ -22,23 +22,29 @@ * */ +#include "TimeLineWidget.h" + +#include #include -#include #include #include +#include #include - -#include "TimeLineWidget.h" #include "embed.h" -#include "NStateButton.h" #include "GuiApplication.h" +#include "NStateButton.h" #include "TextFloat.h" namespace lmms::gui { +namespace +{ + constexpr int MIN_BAR_LABEL_DISTANCE = 35; +} + QPixmap * TimeLineWidget::s_posMarkerPixmap = nullptr; @@ -270,12 +276,14 @@ void TimeLineWidget::paintEvent( QPaintEvent * ) int const x = m_xOffset + s_posMarkerPixmap->width() / 2 - ( ( static_cast( m_begin * m_ppb ) / TimePos::ticksPerBar() ) % static_cast( m_ppb ) ); + // Double the interval between bar numbers until they are far enough appart + int barLabelInterval = 1; + while (barLabelInterval * m_ppb < MIN_BAR_LABEL_DISTANCE) { barLabelInterval *= 2; } + for( int i = 0; x + i * m_ppb < width(); ++i ) { ++barNumber; - if( ( barNumber - 1 ) % - qMax( 1, qRound( 1.0f / 3.0f * - TimePos::ticksPerBar() / m_ppb ) ) == 0 ) + if ((barNumber - 1) % barLabelInterval == 0) { const int cx = x + qRound( i * m_ppb ); p.setPen( barLineColor ); From 3aed361b82dba6246119c8fc9d929fd9a779a647 Mon Sep 17 00:00:00 2001 From: saker Date: Thu, 24 Aug 2023 12:28:34 -0400 Subject: [PATCH 28/45] Core: Replace global Qt declarations with standard equivalents (#6821) * Replace qAbs with std::abs * Replace qBound with std::clamp * Replace qMax with std::max * Replace qMin with std::min * Replace qRound with std::round * Replace qgetenv with std::getenv --------- Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> --- include/AutomatableModel.h | 3 +- include/BasicFilters.h | 149 ++++++++++++-------------- include/ComboBoxModel.h | 4 +- include/Controller.h | 2 +- include/DspEffectLibrary.h | 4 +- include/Note.h | 2 +- include/lmms_math.h | 10 +- src/core/AudioEngine.cpp | 6 +- src/core/AudioEngineProfiler.cpp | 2 +- src/core/AutomatableModel.cpp | 10 +- src/core/AutomationClip.cpp | 10 +- src/core/BandLimitedWave.cpp | 6 +- src/core/Clip.cpp | 2 +- src/core/ConfigManager.cpp | 8 +- src/core/EnvelopeAndLfoParameters.cpp | 18 ++-- src/core/Instrument.cpp | 4 +- src/core/InstrumentSoundShaping.cpp | 2 +- src/core/LfoController.cpp | 2 +- src/core/MixHelpers.cpp | 2 +- src/core/Mixer.cpp | 4 +- src/core/Note.cpp | 16 +-- src/core/NotePlayHandle.cpp | 6 +- src/core/PatternStore.cpp | 4 +- src/core/PluginFactory.cpp | 3 +- src/core/ProjectVersion.cpp | 4 +- src/core/RemotePlugin.cpp | 10 +- src/core/SampleBuffer.cpp | 28 ++--- src/core/SampleClip.cpp | 2 +- src/core/TimePos.cpp | 2 +- src/core/TrackContainer.cpp | 2 +- src/core/audio/AudioAlsa.cpp | 10 +- src/core/audio/AudioFileFlac.cpp | 2 +- src/core/audio/AudioJack.cpp | 12 +-- src/core/audio/AudioOss.cpp | 6 +- src/core/audio/AudioPortAudio.cpp | 14 +-- src/core/audio/AudioPulseAudio.cpp | 6 +- src/core/audio/AudioSdl.cpp | 10 +- src/core/audio/AudioSndio.cpp | 6 +- src/core/audio/AudioSoundIo.cpp | 6 +- src/core/midi/MidiApple.cpp | 4 +- src/tracks/InstrumentTrack.cpp | 4 +- src/tracks/MidiClip.cpp | 8 +- 42 files changed, 204 insertions(+), 211 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 2b93a514c..292da3bec 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -27,6 +27,7 @@ #include #include +#include #include "JournallingObject.h" #include "Model.h" @@ -144,7 +145,7 @@ public: template static bool castValue( const float v ) { - return ( qRound( v ) != 0 ); + return (std::round(v) != 0); } diff --git a/include/BasicFilters.h b/include/BasicFilters.h index c10c47489..c54053c95 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -209,7 +209,7 @@ public: inline float update( float s, ch_cnt_t ch ) { - if( qAbs( s ) < 1.0e-10f && qAbs( m_z1[ch] ) < 1.0e-10f ) return 0.0f; + if (std::abs(s) < 1.0e-10f && std::abs(m_z1[ch]) < 1.0e-10f) return 0.0f; return m_z1[ch] = s * m_a0 + m_z1[ch] * m_b1; } @@ -340,22 +340,18 @@ public: // four cascaded onepole filters // (bilinear transform) - m_y1[_chnl] = qBound( -10.0f, - ( x + m_oldx[_chnl] ) * m_p - - m_k * m_y1[_chnl], - 10.0f ); - m_y2[_chnl] = qBound( -10.0f, - ( m_y1[_chnl] + m_oldy1[_chnl] ) * m_p - - m_k * m_y2[_chnl], - 10.0f ); - m_y3[_chnl] = qBound( -10.0f, - ( m_y2[_chnl] + m_oldy2[_chnl] ) * m_p - - m_k * m_y3[_chnl], - 10.0f ); - m_y4[_chnl] = qBound( -10.0f, - ( m_y3[_chnl] + m_oldy3[_chnl] ) * m_p - - m_k * m_y4[_chnl], + m_y1[_chnl] = std::clamp((x + m_oldx[_chnl]) * m_p + - m_k * m_y1[_chnl], -10.0f, + 10.0f); + m_y2[_chnl] = std::clamp((m_y1[_chnl] + m_oldy1[_chnl]) * m_p + - m_k * m_y2[_chnl], -10.0f, + 10.0f); + m_y3[_chnl] = std::clamp((m_y2[_chnl] + m_oldy2[_chnl]) * m_p + - m_k * m_y3[_chnl], -10.0f, 10.0f ); + m_y4[_chnl] = std::clamp((m_y3[_chnl] + m_oldy3[_chnl]) * m_p + - m_k * m_y4[_chnl], -10.0f, + 10.0f); m_oldx[_chnl] = x; m_oldy1[_chnl] = m_y1[_chnl]; @@ -377,18 +373,15 @@ public: ip += 0.25f; sample_t x = linearInterpolate( m_last[_chnl], _in0, ip ) - m_r * m_y3[_chnl]; - m_y1[_chnl] = qBound( -10.0f, - ( x + m_oldx[_chnl] ) * m_p - - m_k * m_y1[_chnl], - 10.0f ); - m_y2[_chnl] = qBound( -10.0f, - ( m_y1[_chnl] + m_oldy1[_chnl] ) * m_p - - m_k * m_y2[_chnl], - 10.0f ); - m_y3[_chnl] = qBound( -10.0f, - ( m_y2[_chnl] + m_oldy2[_chnl] ) * m_p - - m_k * m_y3[_chnl], - 10.0f ); + m_y1[_chnl] = std::clamp((x + m_oldx[_chnl]) * m_p + - m_k * m_y1[_chnl], -10.0f, + 10.0f); + m_y2[_chnl] = std::clamp((m_y1[_chnl] + m_oldy1[_chnl]) * m_p + - m_k * m_y2[_chnl], -10.0f, + 10.0f); + m_y3[_chnl] = std::clamp((m_y2[_chnl] + m_oldy2[_chnl]) * m_p + - m_k * m_y3[_chnl], -10.0f, + 10.0f); m_oldx[_chnl] = x; m_oldy1[_chnl] = m_y1[_chnl]; m_oldy2[_chnl] = m_y2[_chnl]; @@ -471,16 +464,16 @@ public: for( int n = 4; n != 0; --n ) { in = _in0 + m_rcbp0[_chnl] * m_rcq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); lp = in * m_rcb + m_rclp0[_chnl] * m_rca; - lp = qBound( -1.0f, lp, 1.0f ); + lp = std::clamp(lp, -1.0f, 1.0f); hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_rclast0[_chnl] = in; m_rclp0[_chnl] = lp; @@ -496,13 +489,13 @@ public: for( int n = 4; n != 0; --n ) { in = _in0 + m_rcbp0[_chnl] * m_rcq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_rclast0[_chnl] = in; m_rchp0[_chnl] = hp; @@ -518,16 +511,16 @@ public: { // first stage is as for the 12dB case... in = _in0 + m_rcbp0[_chnl] * m_rcq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); lp = in * m_rcb + m_rclp0[_chnl] * m_rca; - lp = qBound( -1.0f, lp, 1.0f ); + lp = std::clamp(lp, -1.0f, 1.0f); hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_rclast0[_chnl] = in; m_rclp0[_chnl] = lp; @@ -536,16 +529,16 @@ public: // second stage gets the output of the first stage as input... in = lp + m_rcbp1[_chnl] * m_rcq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f ); lp = in * m_rcb + m_rclp1[_chnl] * m_rca; - lp = qBound( -1.0f, lp, 1.0f ); + lp = std::clamp(lp, -1.0f, 1.0f); hp = m_rcc * ( m_rchp1[_chnl] + in - m_rclast1[_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_rcb + m_rcbp1[_chnl] * m_rca; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_rclast1[_chnl] = in; m_rclp1[_chnl] = lp; @@ -562,13 +555,13 @@ public: { // first stage is as for the 12dB case... in = _in0 + m_rcbp0[_chnl] * m_rcq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_rclast0[_chnl] = in; m_rchp0[_chnl] = hp; @@ -579,13 +572,13 @@ public: ? hp + m_rcbp1[_chnl] * m_rcq : bp + m_rcbp1[_chnl] * m_rcq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); hp = m_rcc * ( m_rchp1[_chnl] + in - m_rclast1[_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_rcb + m_rcbp1[_chnl] * m_rca; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_rclast1[_chnl] = in; m_rchp1[_chnl] = hp; @@ -597,7 +590,7 @@ public: case Formantfilter: case FastFormant: { - if( qAbs( _in0 ) < 1.0e-10f && qAbs( m_vflast[0][_chnl] ) < 1.0e-10f ) { return 0.0f; } // performance hack - skip processing when the numbers get too small + if (std::abs(_in0) < 1.0e-10f && std::abs(m_vflast[0][_chnl]) < 1.0e-10f) { return 0.0f; } // performance hack - skip processing when the numbers get too small sample_t hp, bp, in; out = 0; @@ -606,39 +599,39 @@ public: { // first formant in = _in0 + m_vfbp[0][_chnl] * m_vfq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); hp = m_vfc[0] * ( m_vfhp[0][_chnl] + in - m_vflast[0][_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_vfb[0] + m_vfbp[0][_chnl] * m_vfa[0]; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_vflast[0][_chnl] = in; m_vfhp[0][_chnl] = hp; m_vfbp[0][_chnl] = bp; in = bp + m_vfbp[2][_chnl] * m_vfq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); hp = m_vfc[0] * ( m_vfhp[2][_chnl] + in - m_vflast[2][_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_vfb[0] + m_vfbp[2][_chnl] * m_vfa[0]; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_vflast[2][_chnl] = in; m_vfhp[2][_chnl] = hp; m_vfbp[2][_chnl] = bp; in = bp + m_vfbp[4][_chnl] * m_vfq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); hp = m_vfc[0] * ( m_vfhp[4][_chnl] + in - m_vflast[4][_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_vfb[0] + m_vfbp[4][_chnl] * m_vfa[0]; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_vflast[4][_chnl] = in; m_vfhp[4][_chnl] = hp; @@ -648,39 +641,39 @@ public: // second formant in = _in0 + m_vfbp[0][_chnl] * m_vfq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); hp = m_vfc[1] * ( m_vfhp[1][_chnl] + in - m_vflast[1][_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_vfb[1] + m_vfbp[1][_chnl] * m_vfa[1]; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_vflast[1][_chnl] = in; m_vfhp[1][_chnl] = hp; m_vfbp[1][_chnl] = bp; in = bp + m_vfbp[3][_chnl] * m_vfq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); hp = m_vfc[1] * ( m_vfhp[3][_chnl] + in - m_vflast[3][_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_vfb[1] + m_vfbp[3][_chnl] * m_vfa[1]; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_vflast[3][_chnl] = in; m_vfhp[3][_chnl] = hp; m_vfbp[3][_chnl] = bp; in = bp + m_vfbp[5][_chnl] * m_vfq; - in = qBound( -1.0f, in, 1.0f ); + in = std::clamp(in, -1.0f, 1.0f); hp = m_vfc[1] * ( m_vfhp[5][_chnl] + in - m_vflast[5][_chnl] ); - hp = qBound( -1.0f, hp, 1.0f ); + hp = std::clamp(hp, -1.0f, 1.0f); bp = hp * m_vfb[1] + m_vfbp[5][_chnl] * m_vfa[1]; - bp = qBound( -1.0f, bp, 1.0f ); + bp = std::clamp(bp, -1.0f, 1.0f); m_vflast[5][_chnl] = in; m_vfhp[5][_chnl] = hp; @@ -709,7 +702,7 @@ public: inline void calcFilterCoeffs( float _freq, float _q ) { // temp coef vars - _q = qMax( _q, minQ() ); + _q = std::max(_q, minQ()); if( m_type == Lowpass_RC12 || m_type == Bandpass_RC12 || @@ -718,7 +711,7 @@ public: m_type == Bandpass_RC24 || m_type == Highpass_RC24 ) { - _freq = qBound( 50.0f, _freq, 20000.0f ); + _freq = std::clamp(_freq, 50.0f, 20000.0f); const float sr = m_sampleRatio * 0.25f; const float f = 1.0f / ( _freq * F_2PI ); @@ -734,7 +727,7 @@ public: if( m_type == Formantfilter || m_type == FastFormant ) { - _freq = qBound( minFreq(), _freq, 20000.0f ); // limit freq and q for not getting bad noise out of the filter... + _freq = std::clamp(_freq, minFreq(), 20000.0f); // limit freq and q for not getting bad noise out of the filter... // formats for a, e, i, o, u, a static const float _f[6][2] = { { 1000, 1400 }, { 500, 2300 }, @@ -772,7 +765,7 @@ public: m_type == DoubleMoog ) { // [ 0 - 0.5 ] - const float f = qBound( minFreq(), _freq, 20000.0f ) * m_sampleRatio; + const float f = std::clamp(_freq, minFreq(), 20000.0f) * m_sampleRatio; // (Empirical tunning) m_p = ( 3.6f - 3.2f * f ) * f; m_k = 2.0f * m_p - 1; @@ -789,7 +782,7 @@ public: if( m_type == Tripole ) { - const float f = qBound( 20.0f, _freq, 20000.0f ) * m_sampleRatio * 0.25f; + const float f = std::clamp(_freq, 20.0f, 20000.0f) * m_sampleRatio * 0.25f; m_p = ( 3.6f - 3.2f * f ) * f; m_k = 2.0f * m_p - 1.0f; @@ -803,15 +796,15 @@ public: m_type == Highpass_SV || m_type == Notch_SV ) { - const float f = sinf( qMax( minFreq(), _freq ) * m_sampleRatio * F_PI ); - m_svf1 = qMin( f, 0.825f ); - m_svf2 = qMin( f * 2.0f, 0.825f ); - m_svq = qMax( 0.0001f, 2.0f - ( _q * 0.1995f ) ); + const float f = sinf(std::max(minFreq(), _freq) * m_sampleRatio * F_PI); + m_svf1 = std::min(f, 0.825f); + m_svf2 = std::min(f * 2.0f, 0.825f); + m_svq = std::max(0.0001f, 2.0f - (_q * 0.1995f)); return; } // other filters - _freq = qBound( minFreq(), _freq, 20000.0f ); + _freq = std::clamp(_freq, minFreq(), 20000.0f); const float omega = F_2PI * _freq * m_sampleRatio; const float tsin = sinf( omega ) * 0.5f; const float tcos = cosf( omega ); diff --git a/include/ComboBoxModel.h b/include/ComboBoxModel.h index 8fd1f49f4..e90d804e2 100644 --- a/include/ComboBoxModel.h +++ b/include/ComboBoxModel.h @@ -72,12 +72,12 @@ public: const QString & itemText( int i ) const { - return m_items[qBound( minValue(), i, maxValue() )].first; + return m_items[std::clamp(i, minValue(), maxValue())].first; } const PixmapLoader* itemPixmap( int i ) const { - return m_items[qBound( minValue(), i, maxValue() )].second.get(); + return m_items[std::clamp(i, minValue(), maxValue())].second.get(); } int size() const diff --git a/include/Controller.h b/include/Controller.h index b176b1f74..3387975b8 100644 --- a/include/Controller.h +++ b/include/Controller.h @@ -118,7 +118,7 @@ public: inline static float fittedValue( float _val ) { - return qBound( 0.0f, _val, 1.0f ); + return std::clamp(_val, 0.0f, 1.0f); } static long runningPeriods() diff --git a/include/DspEffectLibrary.h b/include/DspEffectLibrary.h index d8268fd8d..14dbd7ede 100644 --- a/include/DspEffectLibrary.h +++ b/include/DspEffectLibrary.h @@ -187,7 +187,7 @@ namespace lmms::DspEffectLibrary template inline sample_t saturate( sample_t x ) { - return qMin( qMax( -1.0f, x ), 1.0f ); + return std::min(std::max(-1.0f, x), 1.0f); } @@ -198,7 +198,7 @@ namespace lmms::DspEffectLibrary const sample_t _gain, const sample_t _ratio, const FastBassBoost & _orig = FastBassBoost() ) : - m_frequency( qMax( _frequency, 10.0 ) ), + m_frequency(std::max(_frequency, 10.0)), m_gain1( 1.0 / ( m_frequency + 1.0 ) ), m_gain2( _gain ), m_ratio( _ratio ), diff --git a/include/Note.h b/include/Note.h index dd261b3af..a5c60ef8b 100644 --- a/include/Note.h +++ b/include/Note.h @@ -198,7 +198,7 @@ public: int midiVelocity( int midiBaseVelocity ) const { - return qMin( MidiMaxVelocity, getVolume() * midiBaseVelocity / DefaultVolume ); + return std::min(MidiMaxVelocity, getVolume() * midiBaseVelocity / DefaultVolume); } inline panning_t getPanning() const diff --git a/include/lmms_math.h b/include/lmms_math.h index 1f896a683..b62da81c2 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -214,7 +214,7 @@ static inline float logToLinearScale( float min, float max, float value ) { if( min < 0 ) { - const float mmax = qMax( qAbs( min ), qAbs( max ) ); + const float mmax = std::max(std::abs(min), std::abs(max)); const float val = value * ( max - min ) + min; float result = signedPowf( val / mmax, F_E ) * mmax; return std::isnan( result ) ? 0 : result; @@ -228,11 +228,11 @@ static inline float logToLinearScale( float min, float max, float value ) static inline float linearToLogScale( float min, float max, float value ) { static const float EXP = 1.0f / F_E; - const float valueLimited = qBound( min, value, max); + const float valueLimited = std::clamp(value, min, max); const float val = ( valueLimited - min ) / ( max - min ); if( min < 0 ) { - const float mmax = qMax( qAbs( min ), qAbs( max ) ); + const float mmax = std::max(std::abs(min), std::abs(max)); float result = signedPowf( valueLimited / mmax, EXP ) * mmax; return std::isnan( result ) ? 0 : result; } @@ -315,14 +315,14 @@ static inline float fastSqrt( float n ) template static inline T absMax( T a, T b ) { - return qAbs(a) > qAbs(b) ? a : b; + return std::abs(a) > std::abs(b) ? a : b; } //! returns value nearest to zero template static inline T absMin( T a, T b ) { - return qAbs(a) < qAbs(b) ? a : b; + return std::abs(a) < std::abs(b) ? a : b; } diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 91a16ed12..59c7f2cd2 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -314,7 +314,7 @@ void AudioEngine::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames ) if( frames + _frames > size ) { - size = qMax( size * 2, frames + _frames ); + size = std::max(size * 2, frames + _frames); auto ab = new sampleFrame[size]; memcpy( ab, buf, frames * sizeof( sampleFrame ) ); delete [] buf; @@ -551,8 +551,8 @@ AudioEngine::StereoSample AudioEngine::getPeakValues(sampleFrame * ab, const f_c for (f_cnt_t f = 0; f < frames; ++f) { - float const absLeft = qAbs(ab[f][0]); - float const absRight = qAbs(ab[f][1]); + float const absLeft = std::abs(ab[f][0]); + float const absRight = std::abs(ab[f][1]); if (absLeft > peakLeft) { peakLeft = absLeft; diff --git a/src/core/AudioEngineProfiler.cpp b/src/core/AudioEngineProfiler.cpp index 5fd5a813c..9e05ff80a 100644 --- a/src/core/AudioEngineProfiler.cpp +++ b/src/core/AudioEngineProfiler.cpp @@ -41,7 +41,7 @@ void AudioEngineProfiler::finishPeriod( sample_rate_t sampleRate, fpp_t framesPe int periodElapsed = m_periodTimer.elapsed(); const float newCpuLoad = periodElapsed / 10000.0f * sampleRate / framesPerPeriod; - m_cpuLoad = qBound( 0, ( newCpuLoad * 0.1f + m_cpuLoad * 0.9f ), 100 ); + m_cpuLoad = std::clamp((newCpuLoad * 0.1f + m_cpuLoad * 0.9f), 0, 100); if( m_outputFile.isOpen() ) { diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index fa4697c69..c25e813cb 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -354,8 +354,8 @@ float AutomatableModel::inverseScaledValue( float value ) const template void roundAt( T& value, const T& where, const T& step_size ) { - if( qAbs( value - where ) - < typeInfo::minEps() * qAbs( step_size ) ) + if (std::abs(value - where) + < typeInfo::minEps() * std::abs(step_size)) { value = where; } @@ -444,7 +444,7 @@ void AutomatableModel::setStep( const float step ) float AutomatableModel::fittedValue( float value ) const { - value = qBound( m_minValue, value, m_maxValue ); + value = std::clamp(value, m_minValue, m_maxValue); if( m_step != 0 && m_hasStrictStepSize ) { @@ -585,7 +585,7 @@ float AutomatableModel::controllerValue( int frameOffset ) const } if( typeInfo::isEqual( m_step, 1 ) && m_hasStrictStepSize ) { - return qRound( v ); + return std::round(v); } return v; } @@ -794,7 +794,7 @@ void AutomatableModel::setUseControllerValue(bool b) float FloatModel::getRoundedValue() const { - return qRound( value() / step() ) * step(); + return std::round(value() / step()) * step(); } diff --git a/src/core/AutomationClip.cpp b/src/core/AutomationClip.cpp index 7a161c355..e55fc5c1e 100644 --- a/src/core/AutomationClip.cpp +++ b/src/core/AutomationClip.cpp @@ -225,7 +225,7 @@ TimePos AutomationClip::timeMapLength() const void AutomationClip::updateLength() { // Do not resize down in case user manually extended up - changeLength(qMax(length(), timeMapLength())); + changeLength(std::max(length(), timeMapLength())); } @@ -374,8 +374,8 @@ void AutomationClip::removeNodes(const int tick0, const int tick1) return; } - auto start = TimePos(qMin(tick0, tick1)); - auto end = TimePos(qMax(tick0, tick1)); + auto start = TimePos(std::min(tick0, tick1)); + auto end = TimePos(std::max(tick0, tick1)); // Make a list of TimePos with nodes to be removed // because we can't simply remove the nodes from @@ -410,8 +410,8 @@ void AutomationClip::resetNodes(const int tick0, const int tick1) return; } - auto start = TimePos(qMin(tick0, tick1)); - auto end = TimePos(qMax(tick0, tick1)); + auto start = TimePos(std::min(tick0, tick1)); + auto end = TimePos(std::max(tick0, tick1)); for (auto it = m_timeMap.lowerBound(start), endIt = m_timeMap.upperBound(end); it != endIt; ++it) { diff --git a/src/core/BandLimitedWave.cpp b/src/core/BandLimitedWave.cpp index a7dd650c0..a92b22e21 100644 --- a/src/core/BandLimitedWave.cpp +++ b/src/core/BandLimitedWave.cpp @@ -109,7 +109,7 @@ void BandLimitedWave::generateWaves() harm++; } while( hlen > 2.0 ); s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s ); - max = qMax( max, qAbs( s ) ); + max = std::max(max, std::abs(s)); } // normalize for( int ph = 0; ph < len; ph++ ) @@ -151,7 +151,7 @@ void BandLimitedWave::generateWaves() harm += 2; } while( hlen > 2.0 ); s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s ); - max = qMax( max, qAbs( s ) ); + max = std::max(max, std::abs(s)); } // normalize for( int ph = 0; ph < len; ph++ ) @@ -193,7 +193,7 @@ void BandLimitedWave::generateWaves() harm += 2; } while( hlen > 2.0 ); s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s ); - max = qMax( max, qAbs( s ) ); + max = std::max(max, std::abs(s)); } // normalize for( int ph = 0; ph < len; ph++ ) diff --git a/src/core/Clip.cpp b/src/core/Clip.cpp index 74a168fbd..db1200aae 100644 --- a/src/core/Clip.cpp +++ b/src/core/Clip.cpp @@ -92,7 +92,7 @@ Clip::~Clip() */ void Clip::movePosition( const TimePos & pos ) { - TimePos newPos = qMax(0, pos.getTicks()); + TimePos newPos = std::max(0, pos.getTicks()); if (m_startPosition != newPos) { Engine::audioEngine()->requestChangeInModel(); diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index c647a27e6..05574ae35 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -77,9 +77,9 @@ ConfigManager::ConfigManager() : m_sf2Dir = m_workingDir + SF2_PATH; m_gigDir = m_workingDir + GIG_PATH; m_themeDir = defaultThemeDir(); - if (!qgetenv("LMMS_DATA_DIR").isEmpty()) + if (std::getenv("LMMS_DATA_DIR")) { - QDir::addSearchPath("data", QString::fromLocal8Bit(qgetenv("LMMS_DATA_DIR"))); + QDir::addSearchPath("data", QString::fromLocal8Bit(std::getenv("LMMS_DATA_DIR"))); } initDevelopmentWorkingDir(); @@ -557,8 +557,8 @@ void ConfigManager::loadConfigFile(const QString & configFile) upgrade(); QStringList searchPaths; - if(! qgetenv("LMMS_THEME_PATH").isNull()) - searchPaths << qgetenv("LMMS_THEME_PATH"); + if (std::getenv("LMMS_THEME_PATH")) + searchPaths << std::getenv("LMMS_THEME_PATH"); searchPaths << themeDir() << defaultThemeDir(); QDir::setSearchPaths("resources", searchPaths); diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index 2aa77e6aa..a3f41b5aa 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -274,7 +274,7 @@ inline void EnvelopeAndLfoParameters::fillLfoLevel( float * _buf, } fpp_t offset = 0; - const float lafI = 1.0f / qMax( minimumFrames, m_lfoAttackFrames ); + const float lafI = 1.0f / std::max(minimumFrames, m_lfoAttackFrames); for( ; offset < _frames && _frame < m_lfoAttackFrames; ++offset, ++_frame ) { @@ -404,16 +404,16 @@ void EnvelopeAndLfoParameters::updateSampleVars() // TODO: Remove the expKnobVals, time should be linear const auto predelay_frames = static_cast(frames_per_env_seg * expKnobVal(m_predelayModel.value())); - const f_cnt_t attack_frames = qMax( minimumFrames, - static_cast( frames_per_env_seg * - expKnobVal( m_attackModel.value() ) ) ); + const f_cnt_t attack_frames = std::max(minimumFrames, + static_cast(frames_per_env_seg * + expKnobVal(m_attackModel.value()))); const auto hold_frames = static_cast(frames_per_env_seg * expKnobVal(m_holdModel.value())); - const f_cnt_t decay_frames = qMax( minimumFrames, - static_cast( frames_per_env_seg * - expKnobVal( m_decayModel.value() * - ( 1 - m_sustainModel.value() ) ) ) ); + const f_cnt_t decay_frames = std::max(minimumFrames, + static_cast(frames_per_env_seg * + expKnobVal(m_decayModel.value() * + (1 - m_sustainModel.value())))); m_sustainLevel = m_sustainModel.value(); m_amount = m_amountModel.value(); @@ -430,7 +430,7 @@ void EnvelopeAndLfoParameters::updateSampleVars() decay_frames; m_rFrames = static_cast( frames_per_env_seg * expKnobVal( m_releaseModel.value() ) ); - m_rFrames = qMax( minimumFrames, m_rFrames ); + m_rFrames = std::max(minimumFrames, m_rFrames); if( static_cast( floorf( m_amount * 1000.0f ) ) == 0 ) { diff --git a/src/core/Instrument.cpp b/src/core/Instrument.cpp index dde260fdc..b715bcac0 100644 --- a/src/core/Instrument.cpp +++ b/src/core/Instrument.cpp @@ -185,8 +185,8 @@ void Instrument::applyRelease( sampleFrame * buf, const NotePlayHandle * _n ) if( fl <= desiredReleaseFrames()+fpp ) { for( fpp_t f = (fpp_t)( ( fl > desiredReleaseFrames() ) ? - ( qMax( fpp - desiredReleaseFrames(), 0 ) + - fl % fpp ) : 0 ); f < frames; ++f ) + (std::max(fpp - desiredReleaseFrames(), 0) + + fl % fpp) : 0); f < frames; ++f) { const float fac = (float)( fl-f-1 ) / desiredReleaseFrames(); diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index edf3b403c..4aeea453a 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -317,7 +317,7 @@ f_cnt_t InstrumentSoundShaping::releaseFrames() const { if( m_envLfoParameters[i]->isUsed() ) { - ret_val = qMax( ret_val, m_envLfoParameters[i]->releaseFrames() ); + ret_val = std::max(ret_val, m_envLfoParameters[i]->releaseFrames()); } } return ret_val; diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 495ae54d4..c0fe65855 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -109,7 +109,7 @@ void LfoController::updateValueBuffer() ? m_sampleFunction( phase ) : m_userDefSampleBuffer->userWaveSample( phase ); - f = qBound( 0.0f, m_baseModel.value() + ( *amountPtr * currentSample / 2.0f ), 1.0f ); + f = std::clamp(m_baseModel.value() + (*amountPtr * currentSample / 2.0f), 0.0f, 1.0f); phase += 1.0 / m_duration; amountPtr += amountInc; diff --git a/src/core/MixHelpers.cpp b/src/core/MixHelpers.cpp index 02f3d8775..bc55419e9 100644 --- a/src/core/MixHelpers.cpp +++ b/src/core/MixHelpers.cpp @@ -121,7 +121,7 @@ bool sanitize( sampleFrame * src, int frames ) } else { - src[f][c] = qBound( -1000.0f, src[f][c], 1000.0f ); + src[f][c] = std::clamp(src[f][c], -1000.0f, 1000.0f); } } } diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index dba5c334e..5edd88a0a 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -173,8 +173,8 @@ void MixerChannel::doProcessing() m_stillRunning = m_fxChain.processAudioBuffer( m_buffer, fpp, m_hasInput ); AudioEngine::StereoSample peakSamples = Engine::audioEngine()->getPeakValues(m_buffer, fpp); - m_peakLeft = qMax( m_peakLeft, peakSamples.left * v ); - m_peakRight = qMax( m_peakRight, peakSamples.right * v ); + m_peakLeft = std::max(m_peakLeft, peakSamples.left * v); + m_peakRight = std::max(m_peakRight, peakSamples.right * v); } else { diff --git a/src/core/Note.cpp b/src/core/Note.cpp index e4e5f0e5d..1198abdec 100644 --- a/src/core/Note.cpp +++ b/src/core/Note.cpp @@ -38,13 +38,13 @@ Note::Note( const TimePos & length, const TimePos & pos, int key, volume_t volume, panning_t panning, DetuningHelper * detuning ) : m_selected( false ), - m_oldKey( qBound( 0, key, NumKeys ) ), + m_oldKey(std::clamp(key, 0, NumKeys)), m_oldPos( pos ), m_oldLength( length ), m_isPlaying( false ), - m_key( qBound( 0, key, NumKeys ) ), - m_volume( qBound( MinVolume, volume, MaxVolume ) ), - m_panning( qBound( PanningLeft, panning, PanningRight ) ), + m_key(std::clamp(key, 0, NumKeys)), + m_volume(std::clamp(volume, MinVolume, MaxVolume)), + m_panning(std::clamp(panning, PanningLeft, PanningRight)), m_length( length ), m_pos( pos ), m_detuning( nullptr ) @@ -114,7 +114,7 @@ void Note::setPos( const TimePos & pos ) void Note::setKey( const int key ) { - const int k = qBound( 0, key, NumKeys - 1 ); + const int k = std::clamp(key, 0, NumKeys - 1); m_key = k; } @@ -123,7 +123,7 @@ void Note::setKey( const int key ) void Note::setVolume( volume_t volume ) { - const volume_t v = qBound( MinVolume, volume, MaxVolume ); + const volume_t v = std::clamp(volume, MinVolume, MaxVolume); m_volume = v; } @@ -132,7 +132,7 @@ void Note::setVolume( volume_t volume ) void Note::setPanning( panning_t panning ) { - const panning_t p = qBound( PanningLeft, panning, PanningRight ); + const panning_t p = std::clamp(panning, PanningLeft, PanningRight); m_panning = p; } @@ -192,7 +192,7 @@ void Note::saveSettings( QDomDocument & doc, QDomElement & parent ) void Note::loadSettings( const QDomElement & _this ) { const int oldKey = _this.attribute( "tone" ).toInt() + _this.attribute( "oct" ).toInt() * KeysPerOctave; - m_key = qMax( oldKey, _this.attribute( "key" ).toInt() ); + m_key = std::max(oldKey, _this.attribute("key").toInt()); m_volume = _this.attribute( "vol" ).toInt(); m_panning = _this.attribute( "pan" ).toInt(); m_length = _this.attribute( "len" ).toInt(); diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index e87637476..31f7e81cb 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -350,9 +350,9 @@ fpp_t NotePlayHandle::framesLeftForCurrentPeriod() const { if( m_totalFramesPlayed == 0 ) { - return (fpp_t) qMin( framesLeft(), Engine::audioEngine()->framesPerPeriod() - offset() ); + return static_cast(std::min(framesLeft(), Engine::audioEngine()->framesPerPeriod() - offset())); } - return (fpp_t) qMin( framesLeft(), Engine::audioEngine()->framesPerPeriod() ); + return static_cast(std::min(framesLeft(), Engine::audioEngine()->framesPerPeriod())); } @@ -384,7 +384,7 @@ void NotePlayHandle::noteOff( const f_cnt_t _s ) // then set some variables indicating release-state m_framesBeforeRelease = _s; - m_releaseFramesToDo = qMax( 0, actualReleaseFramesToDo() ); + m_releaseFramesToDo = std::max(0, actualReleaseFramesToDo()); if( m_hasMidiNote ) { diff --git a/src/core/PatternStore.cpp b/src/core/PatternStore.cpp index f91c42b72..3d0b60acf 100644 --- a/src/core/PatternStore.cpp +++ b/src/core/PatternStore.cpp @@ -97,7 +97,7 @@ bar_t PatternStore::lengthOfPattern(int pattern) const // Don't create Clips here if they don't exist if (pattern < t->numOfClips()) { - maxLength = qMax(maxLength, t->getClip(pattern)->length()); + maxLength = std::max(maxLength, t->getClip(pattern)->length()); } } @@ -125,7 +125,7 @@ void PatternStore::removePattern(int pattern) } if (pattern <= currentPattern()) { - setCurrentPattern(qMax(currentPattern() - 1, 0)); + setCurrentPattern(std::max(currentPattern() - 1, 0)); } } diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index ece589274..3790a7ec9 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -85,8 +85,7 @@ void PluginFactory::setupSearchPaths() addRelativeIfExists(PLUGIN_DIR); #endif // Or via an environment variable: - QString env_path; - if (!(env_path = qgetenv("LMMS_PLUGIN_DIR")).isEmpty()) + if (const char* env_path = std::getenv("LMMS_PLUGIN_DIR")) QDir::addSearchPath("plugins", env_path); QDir::addSearchPath("plugins", ConfigManager::inst()->workingDir() + "plugins"); diff --git a/src/core/ProjectVersion.cpp b/src/core/ProjectVersion.cpp index b6ee2843f..ffdb8cd43 100644 --- a/src/core/ProjectVersion.cpp +++ b/src/core/ProjectVersion.cpp @@ -95,12 +95,12 @@ int ProjectVersion::compare(const ProjectVersion & a, const ProjectVersion & b, if(aPat != bPat){ return aPat - bPat; } // Decide how many optional identifiers we care about - const int maxLabels = qMax(0, limit - 3); + const int maxLabels = std::max(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()); + const int commonLabels = std::min(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(); } diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index 14c6f3f32..088bc3cd8 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -359,7 +359,7 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf memset( m_audioBuffer.get(), 0, m_audioBufferSize ); - ch_cnt_t inputs = qMin( m_inputCount, DEFAULT_CHANNELS ); + ch_cnt_t inputs = std::min(m_inputCount, DEFAULT_CHANNELS); if( _in_buf != nullptr && inputs > 0 ) { @@ -403,8 +403,8 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf waitForMessage( IdProcessingDone ); unlock(); - const ch_cnt_t outputs = qMin( m_outputCount, - DEFAULT_CHANNELS ); + const ch_cnt_t outputs = std::min(m_outputCount, + DEFAULT_CHANNELS); if( m_splitChannels ) { for( ch_cnt_t ch = 0; ch < outputs; ++ch ) @@ -427,8 +427,8 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf // clear buffer, if plugin didn't fill up both channels BufferManager::clear( _out_buf, frames ); - for( ch_cnt_t ch = 0; ch < - qMin( DEFAULT_CHANNELS, outputs ); ++ch ) + for (ch_cnt_t ch = 0; ch < + std::min(DEFAULT_CHANNELS, outputs); ++ch) { for( fpp_t frame = 0; frame < frames; ++frame ) { diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 677366fdb..2f35b23a8 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -456,10 +456,10 @@ void SampleBuffer::normalizeSampleRate(const sample_rate_t srcSR, bool keepSetti { auto oldRateToNewRateRatio = static_cast(audioEngineSampleRate()) / oldRate; - m_startFrame = qBound(0, f_cnt_t(m_startFrame * oldRateToNewRateRatio), m_frames); - m_endFrame = qBound(m_startFrame, f_cnt_t(m_endFrame * oldRateToNewRateRatio), m_frames); - m_loopStartFrame = qBound(0, f_cnt_t(m_loopStartFrame * oldRateToNewRateRatio), m_frames); - m_loopEndFrame = qBound(m_loopStartFrame, f_cnt_t(m_loopEndFrame * oldRateToNewRateRatio), m_frames); + m_startFrame = std::clamp(f_cnt_t(m_startFrame * oldRateToNewRateRatio), 0, m_frames); + m_endFrame = std::clamp(f_cnt_t(m_endFrame * oldRateToNewRateRatio), m_startFrame, m_frames); + m_loopStartFrame = std::clamp(f_cnt_t(m_loopStartFrame * oldRateToNewRateRatio), 0, m_frames); + m_loopEndFrame = std::clamp(f_cnt_t(m_loopEndFrame * oldRateToNewRateRatio), m_loopStartFrame, m_frames); m_sampleRate = audioEngineSampleRate(); } } @@ -736,7 +736,7 @@ bool SampleBuffer::play( // this holds the index of the first frame to play - f_cnt_t playFrame = qMax(state->m_frameIndex, startFrame); + f_cnt_t playFrame = std::max(state->m_frameIndex, startFrame); if (loopMode == LoopOff) { @@ -915,12 +915,12 @@ sampleFrame * SampleBuffer::getSampleFragment( } else if (loopMode == LoopOn) { - f_cnt_t copied = qMin(frames, loopEnd - index); + f_cnt_t copied = std::min(frames, loopEnd - index); memcpy(*tmp, m_data + index, copied * BYTES_PER_FRAME); f_cnt_t loopFrames = loopEnd - loopStart; while (copied < frames) { - f_cnt_t todo = qMin(frames - copied, loopFrames); + f_cnt_t todo = std::min(frames - copied, loopFrames); memcpy(*tmp + copied, m_data + loopStart, todo * BYTES_PER_FRAME); copied += todo; } @@ -936,7 +936,7 @@ sampleFrame * SampleBuffer::getSampleFragment( if (currentBackwards) { - copied = qMin(frames, pos - loopStart); + copied = std::min(frames, pos - loopStart); for (int i = 0; i < copied; i++) { (*tmp)[i][0] = m_data[pos - i][0]; @@ -947,7 +947,7 @@ sampleFrame * SampleBuffer::getSampleFragment( } else { - copied = qMin(frames, loopEnd - pos); + copied = std::min(frames, loopEnd - pos); memcpy(*tmp, m_data + pos, copied * BYTES_PER_FRAME); pos += copied; if (pos == loopEnd) { currentBackwards = true; } @@ -957,7 +957,7 @@ sampleFrame * SampleBuffer::getSampleFragment( { if (currentBackwards) { - f_cnt_t todo = qMin(frames - copied, pos - loopStart); + f_cnt_t todo = std::min(frames - copied, pos - loopStart); for (int i = 0; i < todo; i++) { (*tmp)[copied + i][0] = m_data[pos - i][0]; @@ -969,7 +969,7 @@ sampleFrame * SampleBuffer::getSampleFragment( } else { - f_cnt_t todo = qMin(frames - copied, loopEnd - pos); + f_cnt_t todo = std::min(frames - copied, loopEnd - pos); memcpy(*tmp + copied, m_data + pos, todo * BYTES_PER_FRAME); pos += todo; copied += todo; @@ -1085,8 +1085,8 @@ void SampleBuffer::visualize( const float trueRmsData = (rmsData[0] + rmsData[1]) / 2 / fpp; const float sqrtRmsData = sqrt(trueRmsData); - const float maxRmsData = qBound(minData, sqrtRmsData, maxData); - const float minRmsData = qBound(minData, -sqrtRmsData, maxData); + const float maxRmsData = std::clamp(sqrtRmsData, minData, maxData); + const float minRmsData = std::clamp(-sqrtRmsData, minData, maxData); // If nbFrames >= w, we can use curPixel to calculate X // but if nbFrames < w, we need to calculate it proportionally @@ -1291,7 +1291,7 @@ QString & SampleBuffer::toBase64(QString & dst) const while (frameCnt < m_frames) { - f_cnt_t remaining = qMin(FRAMES_PER_BUF, m_frames - frameCnt); + f_cnt_t remaining = std::min(FRAMES_PER_BUF, m_frames - frameCnt); FLAC__int32 buf[FRAMES_PER_BUF * DEFAULT_CHANNELS]; for (f_cnt_t f = 0; f < remaining; ++f) { diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 6396d49f3..87d6351bc 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -117,7 +117,7 @@ SampleClip::~SampleClip() void SampleClip::changeLength( const TimePos & _length ) { - Clip::changeLength( qMax( static_cast( _length ), 1 ) ); + Clip::changeLength(std::max(static_cast(_length), 1)); } diff --git a/src/core/TimePos.cpp b/src/core/TimePos.cpp index f3b09474d..86a65f103 100644 --- a/src/core/TimePos.cpp +++ b/src/core/TimePos.cpp @@ -194,7 +194,7 @@ tick_t TimePos::ticksPerBar( const TimeSig &sig ) int TimePos::stepsPerBar() { int steps = ticksPerBar() / DefaultBeatsPerBar; - return qMax( 1, steps ); + return std::max(1, steps); } diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index 33c695f48..af96ff1c4 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -299,7 +299,7 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra } TimePos relTime = time - p->startPosition(); if (! p->getAutoResize()) { - relTime = qMin(relTime, p->length()); + relTime = std::min(relTime, p->length()); } float value = p->valueAt(relTime); diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index 8b3eb32be..6e17ad0fe 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -36,10 +36,10 @@ namespace lmms { AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) : - AudioDevice( qBound( + AudioDevice(std::clamp( + ConfigManager::inst()->value("audioalsa", "channels").toInt(), DEFAULT_CHANNELS, - ConfigManager::inst()->value( "audioalsa", "channels" ).toInt(), - SURROUND_CHANNELS ), _audioEngine ), + SURROUND_CHANNELS), _audioEngine), m_handle( nullptr ), m_hwParams( nullptr ), m_swParams( nullptr ), @@ -87,7 +87,7 @@ AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) : int count = snd_pcm_poll_descriptors_count( m_handle ); ufds = new pollfd[count]; snd_pcm_poll_descriptors( m_handle, ufds, count ); - for( int i = 0; i < qMax( 3, count ); ++i ) + for (int i = 0; i < std::max(3, count); ++i) { const int fd = ( i >= count ) ? ufds[0].fd+i : ufds[i].fd; int oldflags = fcntl( fd, F_GETFD, 0 ); @@ -328,7 +328,7 @@ void AudioAlsa::run() outbuf, m_convertEndian ); } - int min_len = qMin( len, outbuf_size - outbuf_pos ); + int min_len = std::min(len, outbuf_size - outbuf_pos); memcpy( ptr, outbuf + outbuf_pos, min_len * sizeof( int_sample_t ) ); ptr += min_len; diff --git a/src/core/audio/AudioFileFlac.cpp b/src/core/audio/AudioFileFlac.cpp index 6af063683..9b49017c8 100644 --- a/src/core/audio/AudioFileFlac.cpp +++ b/src/core/audio/AudioFileFlac.cpp @@ -104,7 +104,7 @@ void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const fram // Clip the negative side to just above -1.0 in order to prevent it from changing sign // Upstream issue: https://github.com/erikd/libsndfile/issues/309 // When this commit is reverted libsndfile-1.0.29 must be made a requirement for FLAC - buf[frame*channels() + channel] = qMax( clipvalue, _ab[frame][channel] * master_gain ); + buf[frame*channels() + channel] = std::max(clipvalue, _ab[frame][channel] * master_gain); } } sf_writef_float(m_sf, static_cast(buf.data()), frames); diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 222ebf10d..7371c7bfb 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -44,10 +44,10 @@ namespace lmms { AudioJack::AudioJack( bool & _success_ful, AudioEngine* _audioEngine ) : - AudioDevice( qBound( + AudioDevice(std::clamp( + ConfigManager::inst()->value("audiojack", "channels").toInt(), DEFAULT_CHANNELS, - ConfigManager::inst()->value( "audiojack", "channels" ).toInt(), - SURROUND_CHANNELS ), _audioEngine ), + SURROUND_CHANNELS), _audioEngine), m_client( nullptr ), m_active( false ), m_midiClient( nullptr ), @@ -364,7 +364,7 @@ int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata ) } #ifdef AUDIO_PORT_SUPPORT - const int frames = qMin( _nframes, audioEngine()->framesPerPeriod() ); + const int frames = std::min(_nframes, audioEngine()->framesPerPeriod()); for( JackPortMap::iterator it = m_portMap.begin(); it != m_portMap.end(); ++it ) { @@ -389,10 +389,10 @@ int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata ) jack_nframes_t done = 0; while( done < _nframes && m_stopped == false ) { - jack_nframes_t todo = qMin( + jack_nframes_t todo = std::min( _nframes, m_framesToDoInCurBuf - - m_framesDoneInCurBuf ); + m_framesDoneInCurBuf); const float gain = audioEngine()->masterGain(); for( int c = 0; c < channels(); ++c ) { diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index 5166bad79..73969533f 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -70,10 +70,10 @@ static const QString PATH_DEV_DSP = AudioOss::AudioOss( bool & _success_ful, AudioEngine* _audioEngine ) : - AudioDevice( qBound( + AudioDevice(std::clamp( + ConfigManager::inst()->value("audiooss", "channels").toInt(), DEFAULT_CHANNELS, - ConfigManager::inst()->value( "audiooss", "channels" ).toInt(), - SURROUND_CHANNELS ), _audioEngine ), + SURROUND_CHANNELS), _audioEngine), m_convertEndian( false ) { _success_ful = false; diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index 0f5a4122f..c06eee3d4 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -62,10 +62,10 @@ namespace lmms AudioPortAudio::AudioPortAudio( bool & _success_ful, AudioEngine * _audioEngine ) : - AudioDevice( qBound( + AudioDevice(std::clamp( + ConfigManager::inst()->value("audioportaudio", "channels").toInt(), DEFAULT_CHANNELS, - ConfigManager::inst()->value( "audioportaudio", "channels" ).toInt(), - SURROUND_CHANNELS ), _audioEngine ), + SURROUND_CHANNELS), _audioEngine), m_paStream( nullptr ), m_wasPAInitError( false ), m_outBuf( new surroundSampleFrame[audioEngine()->framesPerPeriod()] ), @@ -295,8 +295,8 @@ int AudioPortAudio::process_callback( } m_outBufSize = frames; } - const int min_len = qMin( (int)_framesPerBuffer, - m_outBufSize - m_outBufPos ); + const int min_len = std::min(static_cast(_framesPerBuffer), + m_outBufSize - m_outBufPos); float master_gain = audioEngine()->masterGain(); @@ -496,12 +496,12 @@ void AudioPortAudio::setupWidget::show() const QString& device = ConfigManager::inst()->value( "audioportaudio", "device" ); - int i = qMax( 0, m_setupUtil.m_backendModel.findText( backend ) ); + int i = std::max(0, m_setupUtil.m_backendModel.findText(backend)); m_setupUtil.m_backendModel.setValue( i ); m_setupUtil.updateDevices(); - i = qMax( 0, m_setupUtil.m_deviceModel.findText( device ) ); + i = std::max(0, m_setupUtil.m_deviceModel.findText(device)); m_setupUtil.m_deviceModel.setValue( i ); } diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index bac997075..26a5a02e2 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -47,10 +47,10 @@ static void stream_write_callback(pa_stream *s, size_t length, void *userdata) AudioPulseAudio::AudioPulseAudio( bool & _success_ful, AudioEngine* _audioEngine ) : - AudioDevice( qBound( + AudioDevice(std::clamp( + ConfigManager::inst()->value("audiopa", "channels").toInt(), DEFAULT_CHANNELS, - ConfigManager::inst()->value( "audiopa", "channels" ).toInt(), - SURROUND_CHANNELS ), _audioEngine ), + SURROUND_CHANNELS), _audioEngine), m_s( nullptr ), m_quit( false ), m_convertEndian( false ) diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index e73ccadc1..c5ffa64a9 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -70,7 +70,7 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : // to convert the buffers #endif m_audioHandle.channels = channels(); - m_audioHandle.samples = qMax( 1024, audioEngine()->framesPerPeriod()*2 ); + m_audioHandle.samples = std::max(1024, audioEngine()->framesPerPeriod() * 2); m_audioHandle.callback = sdlAudioCallback; m_audioHandle.userdata = this; @@ -257,9 +257,9 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) m_currentBufferFramesCount = frames; } - const uint min_frames_count = qMin( _len/sizeof(sampleFrame), + const uint min_frames_count = std::min(_len/sizeof(sampleFrame), m_currentBufferFramesCount - - m_currentBufferFramePos ); + - m_currentBufferFramePos); const float gain = audioEngine()->masterGain(); for (uint f = 0; f < min_frames_count; f++) @@ -296,8 +296,8 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) (int_sample_t *)m_convertedBuf, m_outConvertEndian ); } - const int min_len = qMin( _len, m_convertedBufSize - - m_convertedBufPos ); + const int min_len = std::min(_len, m_convertedBufSize + - m_convertedBufPos); memcpy( _buf, m_convertedBuf + m_convertedBufPos, min_len ); _buf += min_len; _len -= min_len; diff --git a/src/core/audio/AudioSndio.cpp b/src/core/audio/AudioSndio.cpp index a8ea34ce1..0e46d08f6 100644 --- a/src/core/audio/AudioSndio.cpp +++ b/src/core/audio/AudioSndio.cpp @@ -44,10 +44,10 @@ namespace lmms { AudioSndio::AudioSndio(bool & _success_ful, AudioEngine * _audioEngine) : - AudioDevice( qBound( + AudioDevice(std::clamp( + ConfigManager::inst()->value("audiosndio", "channels").toInt(), DEFAULT_CHANNELS, - ConfigManager::inst()->value( "audiosndio", "channels" ).toInt(), - SURROUND_CHANNELS ), _audioEngine ), + SURROUND_CHANNELS), _audioEngine), m_convertEndian ( false ) { _success_ful = false; diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp index 633808204..556909a84 100644 --- a/src/core/audio/AudioSoundIo.cpp +++ b/src/core/audio/AudioSoundIo.cpp @@ -40,10 +40,10 @@ namespace lmms { AudioSoundIo::AudioSoundIo( bool & outSuccessful, AudioEngine * _audioEngine ) : - AudioDevice( qBound( + AudioDevice(std::clamp( + ConfigManager::inst()->value("audiosoundio", "channels").toInt(), DEFAULT_CHANNELS, - ConfigManager::inst()->value( "audiosoundio", "channels" ).toInt(), - SURROUND_CHANNELS ), _audioEngine ) + SURROUND_CHANNELS), _audioEngine) { outSuccessful = false; m_soundio = nullptr; diff --git a/src/core/midi/MidiApple.cpp b/src/core/midi/MidiApple.cpp index 4105f18f1..444f093e5 100644 --- a/src/core/midi/MidiApple.cpp +++ b/src/core/midi/MidiApple.cpp @@ -259,7 +259,7 @@ void MidiApple::HandleReadCallback( const MIDIPacketList *pktlist, void *srcConn nBytes = packet->length; // Check if this is the end of a continued SysEx message if (continueSysEx) { - unsigned int lengthToCopy = qMin(nBytes, SYSEX_LENGTH - sysExLength); + unsigned int lengthToCopy = std::min(nBytes, SYSEX_LENGTH - sysExLength); // Copy the message into our SysEx message buffer, // making sure not to overrun the buffer memcpy(sysExMessage + sysExLength, packet->data, lengthToCopy); @@ -298,7 +298,7 @@ void MidiApple::HandleReadCallback( const MIDIPacketList *pktlist, void *srcConn { // MIDI SysEx then we copy the rest of the message into the SysEx message buffer unsigned int lengthLeftInMessage = nBytes - iByte; - unsigned int lengthToCopy = qMin(lengthLeftInMessage, SYSEX_LENGTH); + unsigned int lengthToCopy = std::min(lengthLeftInMessage, SYSEX_LENGTH); memcpy(sysExMessage + sysExLength, packet->data, lengthToCopy); sysExLength += lengthToCopy; diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 1d2546288..a688aed9a 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -265,7 +265,7 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, const f_cnt_t offset = n->noteOffset(); m_soundShaping.processAudioBuffer( buf + offset, frames - offset, n ); const float vol = ( (float) n->getVolume() * DefaultVolumeRatio ); - const panning_t pan = qBound( PanningLeft, n->getPanning(), PanningRight ); + const panning_t pan = std::clamp(n->getPanning(), PanningLeft, PanningRight); StereoVolumeVector vv = panningToVolumeVector( pan, vol ); for( f_cnt_t f = offset; f < frames; ++f ) { @@ -666,7 +666,7 @@ int InstrumentTrack::masterKey( int _midi_key ) const { int key = baseNote(); - return qBound( 0, _midi_key - ( key - DefaultKey ), NumKeys ); + return std::clamp(_midi_key - (key - DefaultKey), 0, NumKeys); } diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index 52a532028..4fa2af5d8 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -157,7 +157,7 @@ void MidiClip::updateLength() { if (note->length() > 0) { - max_length = qMax(max_length, note->endPos()); + max_length = std::max(max_length, note->endPos()); } } changeLength( TimePos( max_length ).nextFullBar() * @@ -176,7 +176,7 @@ TimePos MidiClip::beatClipLength() const { if (note->length() < 0) { - max_length = qMax(max_length, note->pos() + 1); + max_length = std::max(max_length, note->pos() + 1); } } @@ -591,8 +591,8 @@ void MidiClip::changeTimeSignature() } } last_pos = last_pos.nextFullBar() * TimePos::ticksPerBar(); - m_steps = qMax( TimePos::stepsPerBar(), - last_pos.getBar() * TimePos::stepsPerBar() ); + m_steps = std::max(TimePos::stepsPerBar(), + last_pos.getBar() * TimePos::stepsPerBar()); updateLength(); } From f10277715f6da1d3055a1ed75dc3b40a5cfac81d Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Thu, 24 Aug 2023 19:16:02 +0100 Subject: [PATCH 29/45] Classier enums (#6760) --- include/AudioEngine.h | 64 ++-- include/AudioEngineWorkerThread.h | 8 +- include/AutomatableModel.h | 6 +- include/AutomationClip.h | 14 +- include/AutomationEditor.h | 30 +- include/BandLimitedWave.h | 21 +- include/BasicFilters.h | 103 +++--- include/Clip.h | 7 - include/ClipView.h | 8 +- include/Controller.h | 27 +- include/CustomTextKnob.h | 2 +- include/DataFile.h | 8 +- include/EnvelopeAndLfoParameters.h | 5 +- include/ExportProjectDialog.h | 2 +- include/FileBrowser.h | 27 +- include/Flags.h | 83 +++++ include/Graph.h | 19 +- include/Instrument.h | 10 +- include/InstrumentFunctions.h | 21 +- include/InstrumentSoundShaping.h | 7 +- include/Knob.h | 16 +- include/LadspaBase.h | 20 +- include/LadspaManager.h | 14 +- include/LedCheckBox.h | 11 +- include/LmmsStyle.h | 30 -- include/Lv2ControlBase.h | 2 +- include/Lv2Manager.h | 6 +- include/Lv2Ports.h | 5 +- include/Lv2Proc.h | 2 +- include/Lv2SubPluginFeatures.h | 2 +- include/MainWindow.h | 2 +- include/MidiClip.h | 8 +- include/MidiPort.h | 9 +- include/MidiPortMenu.h | 4 +- include/Note.h | 44 ++- include/NotePlayHandle.h | 16 +- include/Oscillator.h | 47 +-- include/OutputSettings.h | 19 +- include/Piano.h | 6 +- include/PianoRoll.h | 96 ++--- include/PlayHandle.h | 21 +- include/Plugin.h | 10 +- include/PluginFactory.h | 4 +- include/PluginIssue.h | 40 +-- include/ProjectJournal.h | 2 +- include/ProjectRenderer.h | 21 +- include/ProjectVersion.h | 6 +- include/RenderManager.h | 4 +- include/SampleBuffer.h | 10 +- include/SetupDialog.h | 4 +- include/Song.h | 65 ++-- include/SongEditor.h | 8 +- include/TempoSyncKnob.h | 2 +- include/TempoSyncKnobModel.h | 34 +- include/TimeDisplayWidget.h | 6 +- include/TimeLineWidget.h | 32 +- include/Track.h | 26 +- include/TrackContainer.h | 14 +- include/TrackContainerView.h | 6 - include/TrackContentWidget.h | 2 +- include/TrackView.h | 12 +- include/fft_helpers.h | 12 +- include/lmms_constants.h | 22 +- plugins/Amplifier/Amplifier.cpp | 2 +- plugins/Amplifier/AmplifierControlDialog.cpp | 8 +- .../AudioFileProcessor/AudioFileProcessor.cpp | 46 +-- .../AudioFileProcessor/AudioFileProcessor.h | 28 +- plugins/BassBooster/BassBooster.cpp | 2 +- .../BassBooster/BassBoosterControlDialog.cpp | 6 +- plugins/BitInvader/BitInvader.cpp | 12 +- plugins/Bitcrush/Bitcrush.cpp | 2 +- plugins/Bitcrush/BitcrushControlDialog.cpp | 18 +- plugins/CarlaBase/Carla.cpp | 6 +- plugins/CarlaPatchbay/CarlaPatchbay.cpp | 2 +- plugins/CarlaRack/CarlaRack.cpp | 2 +- plugins/Compressor/Compressor.cpp | 14 +- plugins/Compressor/Compressor.h | 2 +- .../Compressor/CompressorControlDialog.cpp | 36 +- plugins/CrossoverEQ/CrossoverEQ.cpp | 2 +- .../CrossoverEQ/CrossoverEQControlDialog.cpp | 14 +- plugins/Delay/DelayControlsDialog.cpp | 8 +- plugins/Delay/DelayEffect.cpp | 2 +- plugins/Dispersion/Dispersion.cpp | 2 +- .../Dispersion/DispersionControlDialog.cpp | 6 +- plugins/DualFilter/DualFilter.cpp | 6 +- .../DualFilter/DualFilterControlDialog.cpp | 6 +- .../DynamicsProcessor/DynamicsProcessor.cpp | 10 +- .../DynamicsProcessorControlDialog.cpp | 10 +- .../DynamicsProcessorControls.h | 9 +- plugins/Eq/EqControlsDialog.cpp | 4 +- plugins/Eq/EqCurve.cpp | 30 +- plugins/Eq/EqCurve.h | 19 +- plugins/Eq/EqEffect.cpp | 2 +- plugins/Eq/EqParameterWidget.cpp | 16 +- plugins/Flanger/FlangerControlsDialog.cpp | 12 +- plugins/Flanger/FlangerEffect.cpp | 2 +- plugins/FreeBoy/FreeBoy.cpp | 6 +- plugins/GigPlayer/GigPlayer.cpp | 26 +- plugins/GigPlayer/GigPlayer.h | 6 +- plugins/HydrogenImport/HydrogenImport.cpp | 34 +- plugins/Kicker/Kicker.cpp | 12 +- plugins/Kicker/Kicker.h | 2 +- plugins/LadspaBrowser/LadspaBrowser.cpp | 12 +- plugins/LadspaBrowser/LadspaDescription.cpp | 14 +- plugins/LadspaEffect/LadspaControlDialog.cpp | 10 +- plugins/LadspaEffect/LadspaEffect.cpp | 64 ++-- .../LadspaEffect/LadspaSubPluginFeatures.cpp | 10 +- .../LadspaEffect/LadspaSubPluginFeatures.h | 2 +- plugins/Lb302/Lb302.cpp | 94 ++--- plugins/Lb302/Lb302.h | 20 +- plugins/Lv2Effect/Lv2Effect.cpp | 4 +- plugins/Lv2Instrument/Lv2Instrument.cpp | 6 +- plugins/Lv2Instrument/Lv2Instrument.h | 4 +- plugins/MidiExport/MidiExport.cpp | 16 +- plugins/MidiImport/MidiImport.cpp | 12 +- plugins/Monstro/Monstro.cpp | 2 +- plugins/Monstro/Monstro.h | 14 +- plugins/MultitapEcho/MultitapEcho.cpp | 2 +- .../MultitapEchoControlDialog.cpp | 12 +- plugins/Nes/Nes.cpp | 2 +- plugins/Nes/Nes.h | 2 +- plugins/OpulenZ/OpulenZ.cpp | 8 +- plugins/OpulenZ/OpulenZ.h | 2 +- plugins/Organic/Organic.cpp | 26 +- plugins/Patman/Patman.cpp | 32 +- plugins/Patman/Patman.h | 16 +- .../PeakControllerEffect.cpp | 2 +- .../PeakControllerEffectControlDialog.cpp | 12 +- plugins/ReverbSC/ReverbSC.cpp | 2 +- plugins/ReverbSC/ReverbSCControlDialog.cpp | 8 +- plugins/Sf2Player/Sf2Player.cpp | 10 +- plugins/Sf2Player/Sf2Player.h | 2 +- plugins/Sfxr/Sfxr.cpp | 6 +- plugins/Sfxr/Sfxr.h | 5 +- plugins/Sid/SidInstrument.cpp | 30 +- plugins/Sid/SidInstrument.h | 29 +- plugins/SpectrumAnalyzer/Analyzer.cpp | 2 +- plugins/SpectrumAnalyzer/SaControls.cpp | 4 +- plugins/SpectrumAnalyzer/SaControlsDialog.cpp | 16 +- plugins/SpectrumAnalyzer/SaProcessor.cpp | 50 +-- plugins/StereoEnhancer/StereoEnhancer.cpp | 2 +- .../StereoEnhancerControlDialog.cpp | 2 +- plugins/StereoMatrix/StereoMatrix.cpp | 2 +- .../StereoMatrixControlDialog.cpp | 8 +- plugins/Stk/Mallets/Mallets.cpp | 32 +- plugins/TripleOscillator/TripleOscillator.cpp | 10 +- plugins/Vectorscope/VecControlsDialog.cpp | 2 +- plugins/Vectorscope/Vectorscope.cpp | 2 +- plugins/Vestige/Vestige.cpp | 8 +- plugins/Vestige/Vestige.h | 2 +- plugins/Vibed/Vibed.cpp | 18 +- plugins/Vibed/Vibed.h | 2 +- plugins/VstBase/RemoteVstPlugin.cpp | 24 +- plugins/VstBase/VstPlugin.cpp | 16 +- plugins/VstBase/communication.h | 16 +- plugins/VstBase/vst_base.cpp | 2 +- plugins/VstEffect/VstEffect.cpp | 4 +- plugins/VstEffect/VstEffectControlDialog.cpp | 10 + plugins/VstEffect/VstEffectControls.cpp | 12 +- plugins/VstEffect/VstSubPluginFeatures.cpp | 2 +- plugins/VstEffect/VstSubPluginFeatures.h | 2 +- plugins/Watsyn/Watsyn.cpp | 10 +- plugins/Watsyn/Watsyn.h | 4 +- plugins/WaveShaper/WaveShaper.cpp | 2 +- .../WaveShaper/WaveShaperControlDialog.cpp | 8 +- plugins/Xpressive/Xpressive.cpp | 14 +- plugins/ZynAddSubFx/ZynAddSubFx.cpp | 22 +- plugins/ZynAddSubFx/ZynAddSubFx.h | 2 +- src/core/AudioEngine.cpp | 24 +- src/core/AudioEngineWorkerThread.cpp | 2 +- src/core/AutomatableModel.cpp | 30 +- src/core/AutomationClip.cpp | 34 +- src/core/BandLimitedWave.cpp | 42 +-- src/core/ConfigManager.cpp | 4 +- src/core/Controller.cpp | 24 +- src/core/ControllerConnection.cpp | 18 +- src/core/DataFile.cpp | 65 ++-- src/core/EnvelopeAndLfoParameters.cpp | 16 +- src/core/ImportFilter.cpp | 2 +- src/core/InstrumentFunctions.cpp | 38 +- src/core/InstrumentPlayHandle.cpp | 2 +- src/core/InstrumentSoundShaping.cpp | 40 +-- src/core/Ladspa2LMMS.cpp | 10 +- src/core/LadspaControl.cpp | 90 ++--- src/core/LadspaManager.cpp | 8 +- src/core/LfoController.cpp | 23 +- src/core/Mixer.cpp | 14 +- src/core/Note.cpp | 2 +- src/core/NotePlayHandle.cpp | 12 +- src/core/Oscillator.cpp | 277 +++++++-------- src/core/PatternStore.cpp | 8 +- src/core/PeakController.cpp | 2 +- src/core/Piano.cpp | 14 +- src/core/Plugin.cpp | 2 +- src/core/PluginFactory.cpp | 2 +- src/core/PluginIssue.cpp | 38 +- src/core/PresetPreviewPlayHandle.cpp | 6 +- src/core/ProjectJournal.cpp | 6 +- src/core/ProjectRenderer.cpp | 24 +- src/core/RenderManager.cpp | 10 +- src/core/SampleBuffer.cpp | 24 +- src/core/SampleClip.cpp | 8 +- src/core/SamplePlayHandle.cpp | 2 +- src/core/SampleRecordHandle.cpp | 2 +- src/core/Song.cpp | 160 ++++----- src/core/TempoSyncKnobModel.cpp | 36 +- src/core/ToolPlugin.cpp | 2 +- src/core/Track.cpp | 28 +- src/core/TrackContainer.cpp | 12 +- src/core/UpgradeExtendedNoteRange.cpp | 6 +- src/core/audio/AudioFileFlac.cpp | 6 +- src/core/audio/AudioFileMP3.cpp | 6 +- src/core/audio/AudioFileWave.cpp | 8 +- src/core/audio/AudioPort.cpp | 2 +- src/core/fft_helpers.cpp | 10 +- src/core/lv2/Lv2ControlBase.cpp | 2 +- src/core/lv2/Lv2Manager.cpp | 4 +- src/core/lv2/Lv2Ports.cpp | 30 +- src/core/lv2/Lv2Proc.cpp | 24 +- src/core/lv2/Lv2SubPluginFeatures.cpp | 2 +- src/core/main.cpp | 38 +- src/core/midi/MidiAlsaSeq.cpp | 6 +- src/core/midi/MidiController.cpp | 4 +- src/core/midi/MidiPort.cpp | 10 +- src/gui/ControllerView.cpp | 2 +- src/gui/Controls.cpp | 2 +- src/gui/EffectView.cpp | 8 +- src/gui/FileBrowser.cpp | 110 +++--- src/gui/LadspaControlView.cpp | 18 +- src/gui/LfoControllerDialog.cpp | 8 +- src/gui/MainWindow.cpp | 42 +-- src/gui/MicrotunerConfig.cpp | 10 + src/gui/MidiCCRackView.cpp | 2 +- src/gui/MixerLine.cpp | 2 +- src/gui/MixerView.cpp | 14 +- src/gui/PluginBrowser.cpp | 4 +- src/gui/SampleTrackWindow.cpp | 14 +- src/gui/clips/AutomationClipView.cpp | 2 +- src/gui/clips/ClipView.cpp | 102 +++--- src/gui/clips/MidiClipView.cpp | 12 +- src/gui/editors/AutomationEditor.cpp | 108 +++--- src/gui/editors/PatternEditor.cpp | 10 +- src/gui/editors/PianoRoll.cpp | 330 +++++++++--------- src/gui/editors/PositionLine.cpp | 4 +- src/gui/editors/SongEditor.cpp | 40 +-- src/gui/editors/TimeLineWidget.cpp | 56 +-- src/gui/editors/TrackContainerView.cpp | 10 +- src/gui/instrument/EnvelopeAndLfoView.cpp | 49 +-- .../instrument/InstrumentFunctionViews.cpp | 16 +- .../instrument/InstrumentSoundShapingView.cpp | 4 +- src/gui/instrument/InstrumentTrackWindow.cpp | 14 +- src/gui/instrument/PianoView.cpp | 40 +-- src/gui/menus/MidiPortMenu.cpp | 12 +- src/gui/modals/ControllerConnectionDialog.cpp | 6 +- src/gui/modals/EffectSelectDialog.cpp | 2 +- src/gui/modals/ExportProjectDialog.cpp | 32 +- src/gui/modals/SetupDialog.cpp | 4 +- src/gui/tracks/InstrumentTrackView.cpp | 8 +- src/gui/tracks/SampleTrackView.cpp | 4 +- src/gui/tracks/TrackContentWidget.cpp | 12 +- src/gui/tracks/TrackLabelButton.cpp | 2 +- src/gui/tracks/TrackOperationsWidget.cpp | 6 +- src/gui/tracks/TrackView.cpp | 16 +- src/gui/widgets/CustomTextKnob.cpp | 2 +- src/gui/widgets/Graph.cpp | 10 +- src/gui/widgets/Knob.cpp | 40 +-- src/gui/widgets/LedCheckBox.cpp | 12 +- src/gui/widgets/TempoSyncKnob.cpp | 60 ++-- src/gui/widgets/TimeDisplayWidget.cpp | 16 +- src/tracks/AutomationTrack.cpp | 4 +- src/tracks/InstrumentTrack.cpp | 20 +- src/tracks/MidiClip.cpp | 24 +- src/tracks/PatternTrack.cpp | 8 +- src/tracks/SampleTrack.cpp | 6 +- tests/src/core/ProjectVersionTest.cpp | 16 +- tests/src/tracks/AutomationTrackTest.cpp | 22 +- 276 files changed, 2607 insertions(+), 2521 deletions(-) create mode 100644 include/Flags.h diff --git a/include/AudioEngine.h b/include/AudioEngine.h index dec8f2592..030c5bce3 100644 --- a/include/AudioEngine.h +++ b/include/AudioEngine.h @@ -109,27 +109,27 @@ public: struct qualitySettings { - enum Mode + enum class Mode { - Mode_Draft, - Mode_HighQuality, - Mode_FinalMix + Draft, + HighQuality, + FinalMix } ; - enum Interpolation + enum class Interpolation { - Interpolation_Linear, - Interpolation_SincFastest, - Interpolation_SincMedium, - Interpolation_SincBest + Linear, + SincFastest, + SincMedium, + SincBest } ; - enum Oversampling + enum class Oversampling { - Oversampling_None, - Oversampling_2x, - Oversampling_4x, - Oversampling_8x + None, + X2, + X4, + X8 } ; Interpolation interpolation; @@ -139,18 +139,18 @@ public: { switch (m) { - case Mode_Draft: - interpolation = Interpolation_Linear; - oversampling = Oversampling_None; + case Mode::Draft: + interpolation = Interpolation::Linear; + oversampling = Oversampling::None; break; - case Mode_HighQuality: + case Mode::HighQuality: interpolation = - Interpolation_SincFastest; - oversampling = Oversampling_2x; + Interpolation::SincFastest; + oversampling = Oversampling::X2; break; - case Mode_FinalMix: - interpolation = Interpolation_SincBest; - oversampling = Oversampling_8x; + case Mode::FinalMix: + interpolation = Interpolation::SincBest; + oversampling = Oversampling::X8; break; } } @@ -165,10 +165,10 @@ public: { switch( oversampling ) { - case Oversampling_None: return 1; - case Oversampling_2x: return 2; - case Oversampling_4x: return 4; - case Oversampling_8x: return 8; + case Oversampling::None: return 1; + case Oversampling::X2: return 2; + case Oversampling::X4: return 4; + case Oversampling::X8: return 8; } return 1; } @@ -177,13 +177,13 @@ public: { switch( interpolation ) { - case Interpolation_Linear: + case Interpolation::Linear: return SRC_ZERO_ORDER_HOLD; - case Interpolation_SincFastest: + case Interpolation::SincFastest: return SRC_SINC_FASTEST; - case Interpolation_SincMedium: + case Interpolation::SincMedium: return SRC_SINC_MEDIUM_QUALITY; - case Interpolation_SincBest: + case Interpolation::SincBest: return SRC_SINC_BEST_QUALITY; } return SRC_LINEAR; @@ -255,7 +255,7 @@ public: return m_playHandles; } - void removePlayHandlesOfTypes(Track * track, const quint8 types); + void removePlayHandlesOfTypes(Track * track, PlayHandle::Types types); // methods providing information for other classes diff --git a/include/AudioEngineWorkerThread.h b/include/AudioEngineWorkerThread.h index 16de6ff6f..b76235aa1 100644 --- a/include/AudioEngineWorkerThread.h +++ b/include/AudioEngineWorkerThread.h @@ -45,7 +45,7 @@ public: class JobQueue { public: - enum OperationMode + enum class OperationMode { Static, // no jobs added while processing queue Dynamic // jobs can be added while processing queue @@ -57,7 +57,7 @@ public: m_items(), m_writeIndex( 0 ), m_itemsDone( 0 ), - m_opMode( Static ) + m_opMode( OperationMode::Static ) { std::fill(m_items, m_items + JOB_QUEUE_SIZE, nullptr); } @@ -83,7 +83,7 @@ public: virtual void quit(); static void resetJobQueue( JobQueue::OperationMode _opMode = - JobQueue::Static ) + JobQueue::OperationMode::Static ) { globalJobQueue.reset( _opMode ); } @@ -97,7 +97,7 @@ public: // to ThreadableJob objects template static void fillJobQueue( const T & _vec, - JobQueue::OperationMode _opMode = JobQueue::Static ) + JobQueue::OperationMode _opMode = JobQueue::OperationMode::Static ) { resetJobQueue( _opMode ); for (const auto& job : _vec) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 292da3bec..2264a592e 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -81,7 +81,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject public: using AutoModelVector = std::vector; - enum ScaleType + enum class ScaleType { Linear, Logarithmic, @@ -232,11 +232,11 @@ public: } void setScaleLogarithmic( bool setToTrue = true ) { - setScaleType( setToTrue ? Logarithmic : Linear ); + setScaleType( setToTrue ? ScaleType::Logarithmic : ScaleType::Linear ); } bool isScaleLogarithmic() const { - return m_scaleType == Logarithmic; + return m_scaleType == ScaleType::Logarithmic; } void setStep( const float step ); diff --git a/include/AutomationClip.h b/include/AutomationClip.h index fc6a26d0e..ceb5611c9 100644 --- a/include/AutomationClip.h +++ b/include/AutomationClip.h @@ -54,11 +54,11 @@ class LMMS_EXPORT AutomationClip : public Clip { Q_OBJECT public: - enum ProgressionTypes + enum class ProgressionType { - DiscreteProgression, - LinearProgression, - CubicHermiteProgression + Discrete, + Linear, + CubicHermite } ; using timeMap = QMap; @@ -76,11 +76,11 @@ public: const objectVector& objects() const; // progression-type stuff - inline ProgressionTypes progressionType() const + inline ProgressionType progressionType() const { return m_progressionType; } - void setProgressionType( ProgressionTypes _new_progression_type ); + void setProgressionType( ProgressionType _new_progression_type ); inline float getTension() const { @@ -214,7 +214,7 @@ private: timeMap m_oldTimeMap; // old values for storing the values before setDragValue() is called. float m_tension; bool m_hasAutomation; - ProgressionTypes m_progressionType; + ProgressionType m_progressionType; bool m_dragging; bool m_dragKeepOutValue; // Should we keep the current dragged node's outValue? diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index a1c4d694f..ecefa8b26 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -87,11 +87,11 @@ public: return "automationeditor"; } - enum EditModes + enum class EditMode { - DRAW, - ERASE, - DRAW_OUTVALUES + Draw, + Erase, + DrawOutValues }; public slots: @@ -129,10 +129,10 @@ protected slots: void horScrolled( int new_pos ); void verScrolled( int new_pos ); - void setEditMode(AutomationEditor::EditModes mode); + void setEditMode(AutomationEditor::EditMode mode); void setEditMode(int mode); - void setProgressionType(AutomationClip::ProgressionTypes type); + void setProgressionType(AutomationClip::ProgressionType type); void setProgressionType(int type); void setTension(); @@ -146,14 +146,14 @@ protected slots: private: - enum Actions + enum class Action { - NONE, - MOVE_VALUE, - ERASE_VALUES, - MOVE_OUTVALUE, - RESET_OUTVALUES, - DRAW_LINE + None, + MoveValue, + EraseValues, + MoveOutValue, + ResetOutValues, + DrawLine } ; // some constants... @@ -201,7 +201,7 @@ private: TimePos m_currentPosition; - Actions m_action; + Action m_action; int m_moveXOffset; @@ -215,7 +215,7 @@ private: // Time position (key) of automation node whose outValue is being dragged int m_draggedOutValueKey; - EditModes m_editMode; + EditMode m_editMode; bool m_mouseDownLeft; bool m_mouseDownRight; //true if right click is being held down diff --git a/include/BandLimitedWave.h b/include/BandLimitedWave.h index 2a12c62e3..1f402aa6e 100644 --- a/include/BandLimitedWave.h +++ b/include/BandLimitedWave.h @@ -89,14 +89,15 @@ QDataStream& operator>> ( QDataStream &in, WaveMipMap &waveMipMap ); class LMMS_EXPORT BandLimitedWave { public: - enum Waveforms + enum class Waveform { BLSaw, BLSquare, BLTriangle, BLMoog, - NumBLWaveforms + Count }; + constexpr static auto NumWaveforms = static_cast(Waveform::Count); BandLimitedWave() = default; virtual ~BandLimitedWave() = default; @@ -127,7 +128,7 @@ public: * \param _wavelen The wavelength (length of one cycle, ie. the inverse of frequency) of the wanted oscillation, measured in sample frames * \param _wave The wanted waveform. Options currently are saw, triangle, square and moog saw. */ - static inline sample_t oscillate( float _ph, float _wavelen, Waveforms _wave ) + static inline sample_t oscillate( float _ph, float _wavelen, Waveform _wave ) { // get the next higher tlen int t = 0; @@ -139,12 +140,12 @@ public: int lookup = static_cast( lookupf ); const float ip = fraction( lookupf ); - const sample_t s1 = s_waveforms[ _wave ].sampleAt( t, lookup ); - const sample_t s2 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 1 ) % tlen ); + const sample_t s1 = s_waveforms[ static_cast(_wave) ].sampleAt( t, lookup ); + const sample_t s2 = s_waveforms[ static_cast(_wave) ].sampleAt( t, ( lookup + 1 ) % tlen ); const int lm = lookup == 0 ? tlen - 1 : lookup - 1; - const sample_t s0 = s_waveforms[ _wave ].sampleAt( t, lm ); - const sample_t s3 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 2 ) % tlen ); + const sample_t s0 = s_waveforms[ static_cast(_wave) ].sampleAt( t, lm ); + const sample_t s3 = s_waveforms[ static_cast(_wave) ].sampleAt( t, ( lookup + 2 ) % tlen ); const sample_t sr = optimal4pInterpolate( s0, s1, s2, s3, ip ); return sr; @@ -153,8 +154,8 @@ public: lookup = lookup << 1; tlen = tlen << 1; t += 1; - const sample_t s3 = s_waveforms[ _wave ].sampleAt( t, lookup ); - const sample_t s4 = s_waveforms[ _wave ].sampleAt( t, ( lookup + 1 ) % tlen ); + const sample_t s3 = s_waveforms[ static_cast(_wave) ].sampleAt( t, lookup ); + const sample_t s4 = s_waveforms[ static_cast(_wave) ].sampleAt( t, ( lookup + 1 ) % tlen ); const sample_t s34 = linearInterpolate( s3, s4, ip ); const float ip2 = ( ( tlen - _wavelen ) / tlen - 0.5 ) * 2.0; @@ -168,7 +169,7 @@ public: static bool s_wavesGenerated; - static std::array s_waveforms; + static std::array s_waveforms; static QString s_wavetableDir; }; diff --git a/include/BasicFilters.h b/include/BasicFilters.h index c54053c95..9351cbafb 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -224,7 +224,7 @@ class BasicFilters { MM_OPERATORS public: - enum FilterTypes + enum class FilterType { LowPass, HiPass, @@ -247,8 +247,7 @@ public: Highpass_SV, Notch_SV, FastFormant, - Tripole, - NumFilters + Tripole }; static inline float minFreq() @@ -261,20 +260,20 @@ public: return( 0.01f ); } - inline void setFilterType( const int _idx ) + inline void setFilterType( const FilterType _idx ) { - m_doubleFilter = _idx == DoubleLowPass || _idx == DoubleMoog; + m_doubleFilter = _idx == FilterType::DoubleLowPass || _idx == FilterType::DoubleMoog; if( !m_doubleFilter ) { - m_type = static_cast( _idx ); + m_type = _idx; return; } // Double lowpass mode, backwards-compat for the goofy // Add-NumFilters to signify doubleFilter stuff - m_type = _idx == DoubleLowPass - ? LowPass - : Moog; + m_type = _idx == FilterType::DoubleLowPass + ? FilterType::LowPass + : FilterType::Moog; if( m_subFilter == nullptr ) { m_subFilter = new BasicFilters( @@ -334,7 +333,7 @@ public: sample_t out; switch( m_type ) { - case Moog: + case FilterType::Moog: { sample_t x = _in0 - m_r*m_y4[_chnl]; @@ -364,7 +363,7 @@ public: // 3x onepole filters with 4x oversampling and interpolation of oversampled signal: // input signal is linear-interpolated after oversampling, output signal is averaged from oversampled outputs - case Tripole: + case FilterType::Tripole: { out = 0.0f; float ip = 0.0f; @@ -397,8 +396,8 @@ public: // and extended to other SV filter types // /* Hal Chamberlin's state variable filter */ - case Lowpass_SV: - case Bandpass_SV: + case FilterType::Lowpass_SV: + case FilterType::Bandpass_SV: { float highpass; @@ -414,12 +413,12 @@ public: } /* mix filter output into output buffer */ - return m_type == Lowpass_SV + return m_type == FilterType::Lowpass_SV ? m_delay4[_chnl] : m_delay3[_chnl]; } - case Highpass_SV: + case FilterType::Highpass_SV: { float hp; @@ -433,7 +432,7 @@ public: return hp; } - case Notch_SV: + case FilterType::Notch_SV: { float hp1, hp2; @@ -458,7 +457,7 @@ public: // can be driven up to self-oscillation (BTW: do not remove the limits!!!). // (C) 1998 ... 2009 S.Fendt. Released under the GPL v2.0 or any later version. - case Lowpass_RC12: + case FilterType::Lowpass_RC12: { sample_t lp, bp, hp, in; for( int n = 4; n != 0; --n ) @@ -482,8 +481,8 @@ public: } return lp; } - case Highpass_RC12: - case Bandpass_RC12: + case FilterType::Highpass_RC12: + case FilterType::Bandpass_RC12: { sample_t hp, bp, in; for( int n = 4; n != 0; --n ) @@ -501,10 +500,10 @@ public: m_rchp0[_chnl] = hp; m_rcbp0[_chnl] = bp; } - return m_type == Highpass_RC12 ? hp : bp; + return m_type == FilterType::Highpass_RC12 ? hp : bp; } - case Lowpass_RC24: + case FilterType::Lowpass_RC24: { sample_t lp, bp, hp, in; for( int n = 4; n != 0; --n ) @@ -547,8 +546,8 @@ public: } return lp; } - case Highpass_RC24: - case Bandpass_RC24: + case FilterType::Highpass_RC24: + case FilterType::Bandpass_RC24: { sample_t hp, bp, in; for( int n = 4; n != 0; --n ) @@ -568,7 +567,7 @@ public: m_rcbp0[_chnl] = bp; // second stage gets the output of the first stage as input... - in = m_type == Highpass_RC24 + in = m_type == FilterType::Highpass_RC24 ? hp + m_rcbp1[_chnl] * m_rcq : bp + m_rcbp1[_chnl] * m_rcq; @@ -584,17 +583,17 @@ public: m_rchp1[_chnl] = hp; m_rcbp1[_chnl] = bp; } - return m_type == Highpass_RC24 ? hp : bp; + return m_type == FilterType::Highpass_RC24 ? hp : bp; } - case Formantfilter: - case FastFormant: + case FilterType::Formantfilter: + case FilterType::FastFormant: { if (std::abs(_in0) < 1.0e-10f && std::abs(m_vflast[0][_chnl]) < 1.0e-10f) { return 0.0f; } // performance hack - skip processing when the numbers get too small sample_t hp, bp, in; out = 0; - const int os = m_type == FastFormant ? 1 : 4; // no oversampling for fast formant + const int os = m_type == FilterType::FastFormant ? 1 : 4; // no oversampling for fast formant for( int o = 0; o < os; ++o ) { // first formant @@ -681,7 +680,7 @@ public: out += bp; } - return m_type == FastFormant ? out * 2.0f : out * 0.5f; + return m_type == FilterType::FastFormant ? out * 2.0f : out * 0.5f; } default: @@ -704,12 +703,12 @@ public: // temp coef vars _q = std::max(_q, minQ()); - if( m_type == Lowpass_RC12 || - m_type == Bandpass_RC12 || - m_type == Highpass_RC12 || - m_type == Lowpass_RC24 || - m_type == Bandpass_RC24 || - m_type == Highpass_RC24 ) + if( m_type == FilterType::Lowpass_RC12 || + m_type == FilterType::Bandpass_RC12 || + m_type == FilterType::Highpass_RC12 || + m_type == FilterType::Lowpass_RC24 || + m_type == FilterType::Bandpass_RC24 || + m_type == FilterType::Highpass_RC24 ) { _freq = std::clamp(_freq, 50.0f, 20000.0f); const float sr = m_sampleRatio * 0.25f; @@ -724,8 +723,8 @@ public: return; } - if( m_type == Formantfilter || - m_type == FastFormant ) + if( m_type == FilterType::Formantfilter || + m_type == FilterType::FastFormant ) { _freq = std::clamp(_freq, minFreq(), 20000.0f); // limit freq and q for not getting bad noise out of the filter... @@ -750,7 +749,7 @@ public: const float f1 = 1.0f / ( linearInterpolate( _f[vowel+0][1], _f[vowel+1][1], fract ) * F_2PI ); // samplerate coeff: depends on oversampling - const float sr = m_type == FastFormant ? m_sampleRatio : m_sampleRatio * 0.25f; + const float sr = m_type == FilterType::FastFormant ? m_sampleRatio : m_sampleRatio * 0.25f; m_vfa[0] = 1.0f - sr / ( f0 + sr ); m_vfb[0] = 1.0f - m_vfa[0]; @@ -761,8 +760,8 @@ public: return; } - if( m_type == Moog || - m_type == DoubleMoog ) + if( m_type == FilterType::Moog || + m_type == FilterType::DoubleMoog ) { // [ 0 - 0.5 ] const float f = std::clamp(_freq, minFreq(), 20000.0f) * m_sampleRatio; @@ -780,7 +779,7 @@ public: return; } - if( m_type == Tripole ) + if( m_type == FilterType::Tripole ) { const float f = std::clamp(_freq, 20.0f, 20000.0f) * m_sampleRatio * 0.25f; @@ -791,10 +790,10 @@ public: return; } - if( m_type == Lowpass_SV || - m_type == Bandpass_SV || - m_type == Highpass_SV || - m_type == Notch_SV ) + if( m_type == FilterType::Lowpass_SV || + m_type == FilterType::Bandpass_SV || + m_type == FilterType::Highpass_SV || + m_type == FilterType::Notch_SV ) { const float f = sinf(std::max(minFreq(), _freq) * m_sampleRatio * F_PI); m_svf1 = std::min(f, 0.825f); @@ -818,38 +817,38 @@ public: switch( m_type ) { - case LowPass: + case FilterType::LowPass: { const float b1 = ( 1.0f - tcos ) * a0; const float b0 = b1 * 0.5f; m_biQuad.setCoeffs( a1, a2, b0, b1, b0 ); break; } - case HiPass: + case FilterType::HiPass: { const float b1 = ( -1.0f - tcos ) * a0; const float b0 = b1 * -0.5f; m_biQuad.setCoeffs( a1, a2, b0, b1, b0 ); break; } - case BandPass_CSG: + case FilterType::BandPass_CSG: { const float b0 = tsin * a0; m_biQuad.setCoeffs( a1, a2, b0, 0.0f, -b0 ); break; } - case BandPass_CZPG: + case FilterType::BandPass_CZPG: { const float b0 = alpha * a0; m_biQuad.setCoeffs( a1, a2, b0, 0.0f, -b0 ); break; } - case Notch: + case FilterType::Notch: { m_biQuad.setCoeffs( a1, a2, a0, a1, a0 ); break; } - case AllPass: + case FilterType::AllPass: { m_biQuad.setCoeffs( a1, a2, a2, a1, 1.0f ); break; @@ -898,7 +897,7 @@ private: // in/out history for Lowpass_SV (state-variant lowpass) frame m_delay1, m_delay2, m_delay3, m_delay4; - FilterTypes m_type; + FilterType m_type; bool m_doubleFilter; float m_sampleRate; diff --git a/include/Clip.h b/include/Clip.h index 8f4162d96..96394602f 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -164,13 +164,6 @@ signals: private: - enum Actions - { - NoAction, - Move, - Resize - } ; - Track * m_track; QString m_name; diff --git a/include/ClipView.h b/include/ClipView.h index 32c5130b9..942258367 100644 --- a/include/ClipView.h +++ b/include/ClipView.h @@ -140,7 +140,7 @@ public slots: void resetColor(); protected: - enum ContextMenuAction + enum class ContextMenuAction { Remove, Cut, @@ -191,9 +191,9 @@ protected slots: private: - enum Actions + enum class Action { - NoAction, + None, Move, MoveSelection, Resize, @@ -206,7 +206,7 @@ private: static TextFloat * s_textFloat; Clip * m_clip; - Actions m_action; + Action m_action; QPoint m_initialMousePos; QPoint m_initialMouseGlobalPos; QVector m_initialOffsets; diff --git a/include/Controller.h b/include/Controller.h index 3387975b8..fe78c55cc 100644 --- a/include/Controller.h +++ b/include/Controller.h @@ -51,20 +51,19 @@ class LMMS_EXPORT Controller : public Model, public JournallingObject { Q_OBJECT public: - enum ControllerTypes + enum class ControllerType { - DummyController, - LfoController, - MidiController, - PeakController, + Dummy, + Lfo, + Midi, + Peak, /* - XYController, - EquationController + XY, + Equation */ - NumControllerTypes } ; - Controller( ControllerTypes _type, Model * _parent, + Controller( ControllerType _type, Model * _parent, const QString & _display_name ); ~Controller() override; @@ -83,7 +82,7 @@ public: m_sampleExact = _exact; } - inline ControllerTypes type() const + inline ControllerType type() const { return( m_type ); } @@ -94,8 +93,8 @@ public: { switch( m_type ) { - case LfoController: return( true ); - case PeakController: return( true ); + case ControllerType::Lfo: return( true ); + case ControllerType::Peak: return( true ); default: break; } @@ -112,7 +111,7 @@ public: void loadSettings( const QDomElement & _this ) override; QString nodeName() const override; - static Controller * create( ControllerTypes _tt, Model * _parent ); + static Controller * create( ControllerType _tt, Model * _parent ); static Controller * create( const QDomElement & _this, Model * _parent ); @@ -165,7 +164,7 @@ protected: int m_connectionCount; QString m_name; - ControllerTypes m_type; + ControllerType m_type; static ControllerVector s_controllers; diff --git a/include/CustomTextKnob.h b/include/CustomTextKnob.h index 19768ec87..31a58415e 100644 --- a/include/CustomTextKnob.h +++ b/include/CustomTextKnob.h @@ -36,7 +36,7 @@ class LMMS_EXPORT CustomTextKnob : public Knob protected: inline void setHintText( const QString & _txt_before, const QString & _txt_after ) {} // inaccessible public: - CustomTextKnob( knobTypes _knob_num, QWidget * _parent = nullptr, const QString & _name = QString(), const QString & _value_text = QString() ); + CustomTextKnob( KnobType _knob_num, QWidget * _parent = nullptr, const QString & _name = QString(), const QString & _value_text = QString() ); CustomTextKnob( QWidget * _parent = nullptr, const QString & _name = QString(), const QString & _value_text = QString() ); //!< default ctor diff --git a/include/DataFile.h b/include/DataFile.h index a91b37f9b..137f0156f 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -47,9 +47,9 @@ class LMMS_EXPORT DataFile : public QDomDocument using UpgradeMethod = void(DataFile::*)(); public: - enum Types + enum class Type { - UnknownType, + Unknown, SongProject, SongProjectTemplate, InstrumentTrackSettings, @@ -57,10 +57,8 @@ public: ClipboardData, JournalData, EffectSettings, - MidiClip, - TypeCount + MidiClip } ; - using Type = Types; DataFile( const QString& fileName ); DataFile( const QByteArray& data ); diff --git a/include/EnvelopeAndLfoParameters.h b/include/EnvelopeAndLfoParameters.h index 02abd07e3..7abc3910e 100644 --- a/include/EnvelopeAndLfoParameters.h +++ b/include/EnvelopeAndLfoParameters.h @@ -169,7 +169,7 @@ private: bool m_bad_lfoShapeData; SampleBuffer m_userWave; - enum LfoShapes + enum class LfoShape { SineWave, TriangleWave, @@ -177,8 +177,9 @@ private: SquareWave, UserDefinedWave, RandomWave, - NumLfoShapes + Count } ; + constexpr static auto NumLfoShapes = static_cast(LfoShape::Count); sample_t lfoShapeSample( fpp_t _frame_offset ); void updateLfoShapeData(); diff --git a/include/ExportProjectDialog.h b/include/ExportProjectDialog.h index 56a9fc1f5..37f6e0399 100644 --- a/include/ExportProjectDialog.h +++ b/include/ExportProjectDialog.h @@ -62,7 +62,7 @@ private: QString m_fileExtension; bool m_multiExport; - ProjectRenderer::ExportFileFormats m_ft; + ProjectRenderer::ExportFileFormat m_ft; std::unique_ptr m_renderManager; } ; diff --git a/include/FileBrowser.h b/include/FileBrowser.h index 51103b19f..3334a73f6 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -223,20 +223,19 @@ private: class FileItem : public QTreeWidgetItem { public: - enum FileTypes + enum class FileType { - ProjectFile, - PresetFile, - SampleFile, - SoundFontFile, - PatchFile, - MidiFile, - VstPluginFile, - UnknownFile, - NumFileTypes + Project, + Preset, + Sample, + SoundFont, + Patch, + Midi, + VstPlugin, + Unknown } ; - enum FileHandling + enum class FileHandling { NotSupported, LoadAsProject, @@ -255,7 +254,7 @@ public: return QFileInfo(m_path, text(0)).absoluteFilePath(); } - inline FileTypes type() const + inline FileType type() const { return( m_type ); } @@ -267,7 +266,7 @@ public: inline bool isTrack() const { - return m_handling == LoadAsPreset || m_handling == LoadByPlugin; + return m_handling == FileHandling::LoadAsPreset || m_handling == FileHandling::LoadByPlugin; } QString extension(); @@ -287,7 +286,7 @@ private: static QPixmap * s_unknownFilePixmap; QString m_path; - FileTypes m_type; + FileType m_type; FileHandling m_handling; } ; diff --git a/include/Flags.h b/include/Flags.h new file mode 100644 index 000000000..76106dde6 --- /dev/null +++ b/include/Flags.h @@ -0,0 +1,83 @@ +/* + * Flags.h - class to make flags from enums + * + * Copyright (c) 2023 Dominic Clark + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_FLAGS_H +#define LMMS_FLAGS_H + +#include + +namespace lmms { + +template +class Flags +{ + static_assert(std::is_enum_v, "lmms::Flags can only be used with enum types"); + +public: + using EnumType = T; + using UnderlyingType = std::underlying_type_t; + + constexpr Flags() = default; + + constexpr Flags(T value) : // Intentionally not explicit + m_value{static_cast(value)} + {} + + constexpr explicit Flags(UnderlyingType value) : + m_value{value} + {} + + constexpr auto testAll(Flags flags) const -> bool { return *this & flags == flags; } + constexpr auto testAny(Flags flags) const -> bool { return *this & flags != Flags{}; } + constexpr auto testFlag(EnumType flag) const -> bool { return static_cast(*this & flag); } + + constexpr auto operator~() const -> Flags { return Flags{~m_value}; } + friend constexpr auto operator&(Flags l, Flags r) -> Flags { return Flags{l.m_value & r.m_value}; } + friend constexpr auto operator|(Flags l, Flags r) -> Flags { return Flags{l.m_value | r.m_value}; } + friend constexpr auto operator^(Flags l, Flags r) -> Flags { return Flags{l.m_value ^ r.m_value}; } + friend constexpr auto operator+(Flags l, Flags r) -> Flags { return Flags{l.m_value | r.m_value}; } + friend constexpr auto operator-(Flags l, Flags r) -> Flags { return Flags{l.m_value & ~r.m_value}; } + + constexpr auto operator&=(Flags f) -> Flags& { m_value &= f.m_value; return *this; } + constexpr auto operator|=(Flags f) -> Flags& { m_value |= f.m_value; return *this; } + constexpr auto operator^=(Flags f) -> Flags& { m_value ^= f.m_value; return *this; } + constexpr auto operator+=(Flags f) -> Flags& { m_value |= f.m_value; return *this; } + constexpr auto operator-=(Flags f) -> Flags& { m_value &= ~f.m_value; return *this; } + + constexpr explicit operator UnderlyingType() const { return m_value; } // TODO C++23: explicit(std::is_scoped_enum) + constexpr explicit operator bool() const { return m_value != 0; } + + friend constexpr auto operator==(Flags l, Flags r) -> bool { return l.m_value == r.m_value; } // TODO C++20: = default + friend constexpr auto operator!=(Flags l, Flags r) -> bool { return l.m_value != r.m_value; } // TODO C++20: Remove + +private: + UnderlyingType m_value = 0; +}; + +#define LMMS_DECLARE_OPERATORS_FOR_FLAGS(type) \ +constexpr inline auto operator|(type l, type r) -> ::lmms::Flags { return ::lmms::Flags{l} | ::lmms::Flags{r}; } + +} // namespace lmms + +#endif // LMMS_FLAGS_H diff --git a/include/Graph.h b/include/Graph.h index f62215ac2..0f5f24524 100644 --- a/include/Graph.h +++ b/include/Graph.h @@ -48,13 +48,12 @@ class LMMS_EXPORT Graph : public QWidget, public ModelView { Q_OBJECT public: - enum graphStyle + enum class Style { - NearestStyle, //!< draw as stairs - LinearStyle, //!< connect each 2 samples with a line, with wrapping - LinearNonCyclicStyle, //!< LinearStyle without wrapping - BarStyle, //!< draw thick bars - NumGraphStyles + Nearest, //!< draw as stairs + Linear, //!< connect each 2 samples with a line, with wrapping + LinearNonCyclic, //!< Linear without wrapping + Bar, //!< draw thick bars }; /** @@ -62,7 +61,7 @@ public: * @param _width Pixel width of widget * @param _height Pixel height of widget */ - Graph( QWidget * _parent, graphStyle _style = Graph::LinearStyle, + Graph( QWidget * _parent, Style _style = Style::Linear, int _width = 132, int _height = 104 ); @@ -78,13 +77,13 @@ public: return castModel(); } - inline graphStyle getGraphStyle() + inline Style getGraphStyle() { return m_graphStyle; } - inline void setGraphStyle( graphStyle _s ) + inline void setGraphStyle( Style _s ) { m_graphStyle = _s; update(); @@ -114,7 +113,7 @@ private: QPixmap m_foreground; QColor m_graphColor; - graphStyle m_graphStyle; + Style m_graphStyle; bool m_mouseDown; int m_lastCursorX; diff --git a/include/Instrument.h b/include/Instrument.h index 1c42c970e..f23e0b401 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -27,6 +27,8 @@ #define LMMS_INSTRUMENT_H #include + +#include "Flags.h" #include "lmms_export.h" #include "lmms_basics.h" #include "MemoryManager.h" @@ -47,7 +49,7 @@ class LMMS_EXPORT Instrument : public Plugin { MM_OPERATORS public: - enum Flag + enum class Flag { NoFlags = 0x00, IsSingleStreamed = 0x01, /*! Instrument provides a single audio stream for all notes */ @@ -55,7 +57,7 @@ public: IsNotBendable = 0x04, /*! Instrument can't react to pitch bend changes */ }; - Q_DECLARE_FLAGS(Flags, Flag); + using Flags = lmms::Flags; Instrument(InstrumentTrack * _instrument_track, const Descriptor * _descriptor, @@ -102,7 +104,7 @@ public: virtual Flags flags() const { - return NoFlags; + return Flag::NoFlags; } // sub-classes can re-implement this for receiving all incoming @@ -149,7 +151,7 @@ private: } ; -Q_DECLARE_OPERATORS_FOR_FLAGS(Instrument::Flags) +LMMS_DECLARE_OPERATORS_FOR_FLAGS(Instrument::Flag) } // namespace lmms diff --git a/include/InstrumentFunctions.h b/include/InstrumentFunctions.h index 61d625d83..59c651a68 100644 --- a/include/InstrumentFunctions.h +++ b/include/InstrumentFunctions.h @@ -176,14 +176,13 @@ class InstrumentFunctionArpeggio : public Model, public JournallingObject { Q_OBJECT public: - enum ArpDirections + enum class ArpDirection { - ArpDirUp, - ArpDirDown, - ArpDirUpAndDown, - ArpDirDownAndUp, - ArpDirRandom, - NumArpDirections + Up, + Down, + UpAndDown, + DownAndUp, + Random } ; InstrumentFunctionArpeggio( Model * _parent ); @@ -202,11 +201,11 @@ public: private: - enum ArpModes + enum class ArpMode { - FreeMode, - SortMode, - SyncMode + Free, + Sort, + Sync } ; BoolModel m_arpEnabledModel; diff --git a/include/InstrumentSoundShaping.h b/include/InstrumentSoundShaping.h index 6db3078ec..fb5f1e8bd 100644 --- a/include/InstrumentSoundShaping.h +++ b/include/InstrumentSoundShaping.h @@ -51,13 +51,14 @@ public: void processAudioBuffer( sampleFrame * _ab, const fpp_t _frames, NotePlayHandle * _n ); - enum Targets + enum class Target { Volume, Cut, Resonance, - NumTargets + Count } ; + constexpr static auto NumTargets = static_cast(Target::Count); f_cnt_t envFrames( const bool _only_vol = false ) const; f_cnt_t releaseFrames() const; @@ -82,7 +83,7 @@ private: FloatModel m_filterCutModel; FloatModel m_filterResModel; - static const char *const targetNames[InstrumentSoundShaping::NumTargets][3]; + static const char *const targetNames[NumTargets][3]; friend class gui::InstrumentSoundShapingView; diff --git a/include/Knob.h b/include/Knob.h index 289af8cd5..85a51e363 100644 --- a/include/Knob.h +++ b/include/Knob.h @@ -42,9 +42,9 @@ namespace lmms::gui class SimpleTextFloat; -enum knobTypes +enum class KnobType { - knobDark_28, knobBright_26, knobSmall_17, knobVintage_32, knobStyled + Dark28, Bright26, Small17, Vintage32, Styled } ; @@ -53,7 +53,7 @@ void convertPixmapToGrayScale(QPixmap &pixMap); class LMMS_EXPORT Knob : public QWidget, public FloatModelView { Q_OBJECT - Q_ENUMS( knobTypes ) + Q_ENUMS( KnobType ) Q_PROPERTY(float innerRadius READ innerRadius WRITE setInnerRadius) Q_PROPERTY(float outerRadius READ outerRadius WRITE setOuterRadius) @@ -75,7 +75,7 @@ class LMMS_EXPORT Knob : public QWidget, public FloatModelView mapPropertyFromModel(bool,isVolumeKnob,setVolumeKnob,m_volumeKnob); mapPropertyFromModel(float,volumeRatio,setVolumeRatio,m_volumeRatio); - Q_PROPERTY(knobTypes knobNum READ knobNum WRITE setknobNum) + Q_PROPERTY(KnobType knobNum READ knobNum WRITE setknobNum) Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor) @@ -83,7 +83,7 @@ class LMMS_EXPORT Knob : public QWidget, public FloatModelView void onKnobNumUpdated(); //!< to be called when you updated @a m_knobNum public: - Knob( knobTypes _knob_num, QWidget * _parent = nullptr, const QString & _name = QString() ); + Knob( KnobType _knob_num, QWidget * _parent = nullptr, const QString & _name = QString() ); Knob( QWidget * _parent = nullptr, const QString & _name = QString() ); //!< default ctor Knob( const Knob& other ) = delete; @@ -106,8 +106,8 @@ public: float outerRadius() const; void setOuterRadius( float r ); - knobTypes knobNum() const; - void setknobNum( knobTypes k ); + KnobType knobNum() const; + void setknobNum( KnobType k ); QPointF centerPoint() const; float centerPointX() const; @@ -206,7 +206,7 @@ private: QColor m_textColor; - knobTypes m_knobNum; + KnobType m_knobNum; } ; diff --git a/include/LadspaBase.h b/include/LadspaBase.h index 51c0ca202..6569c5a30 100644 --- a/include/LadspaBase.h +++ b/include/LadspaBase.h @@ -35,16 +35,16 @@ namespace lmms class LadspaControl; -enum buffer_rate_t { - CHANNEL_IN, - CHANNEL_OUT, - AUDIO_RATE_INPUT, - AUDIO_RATE_OUTPUT, - CONTROL_RATE_INPUT, - CONTROL_RATE_OUTPUT +enum class BufferRate { + ChannelIn, + ChannelOut, + AudioRateInput, + AudioRateOutput, + ControlRateInput, + ControlRateOutput }; -enum buffer_data_t { TOGGLED, ENUM, INTEGER, FLOATING, TIME, NONE }; +enum class BufferDataType { Toggled, Enum, Integer, Floating, Time, None }; //! This struct is used to hold port descriptions internally //! which where received from the ladspa plugin @@ -54,8 +54,8 @@ struct port_desc_t ch_cnt_t proc; uint16_t port_id; uint16_t control_id; - buffer_rate_t rate; - buffer_data_t data_type; + BufferRate rate; + BufferDataType data_type; float scale; LADSPA_Data max; LADSPA_Data min; diff --git a/include/LadspaManager.h b/include/LadspaManager.h index 8c00d2604..1a3360231 100644 --- a/include/LadspaManager.h +++ b/include/LadspaManager.h @@ -62,14 +62,14 @@ calls using: as the plug-in key. */ -enum LadspaPluginType +enum class LadspaPluginType { - SOURCE, - TRANSFER, - VALID, - INVALID, - SINK, - OTHER + Source, + Transfer, + Valid, + Invalid, + Sink, + Other }; struct LadspaManagerDescription diff --git a/include/LedCheckBox.h b/include/LedCheckBox.h index 95016b87f..e3629e143 100644 --- a/include/LedCheckBox.h +++ b/include/LedCheckBox.h @@ -38,20 +38,19 @@ class LMMS_EXPORT LedCheckBox : public AutomatableButton { Q_OBJECT public: - enum LedColors + enum class LedColor { Yellow, Green, - Red, - NumColors + Red } ; LedCheckBox( const QString & _txt, QWidget * _parent, const QString & _name = QString(), - LedColors _color = Yellow ); + LedColor _color = LedColor::Yellow ); LedCheckBox( QWidget * _parent, const QString & _name = QString(), - LedColors _color = Yellow ); + LedColor _color = LedColor::Yellow ); ~LedCheckBox() override; @@ -75,7 +74,7 @@ private: QString m_text; - void initUi( LedColors _color ); //!< to be called by ctors + void initUi( LedColor _color ); //!< to be called by ctors void onTextUpdated(); //!< to be called when you updated @a m_text } ; diff --git a/include/LmmsStyle.h b/include/LmmsStyle.h index b3be03952..d17bbed98 100644 --- a/include/LmmsStyle.h +++ b/include/LmmsStyle.h @@ -36,34 +36,6 @@ namespace lmms::gui class LmmsStyle : public QProxyStyle { public: - enum ColorRole - { - AutomationBarFill, - AutomationBarValue, - AutomationSelectedBarFill, - AutomationCrosshair, - PianoRollStepNote, - PianoRollSelectedNote, - PianoRollDefaultNote, - PianoRollFrozenNote, - PianoRollMutedNote, - PianoRollEditHandle, - PianoRollVolumeLevel, - PianoRollPanningLevel, - PianoRollSelectedLevel, - TimelineForecolor, - StandardGraphLine, - StandardGraphHandle, - StandardGraphHandleBorder, - StandardGraphCrosshair, - TextFloatForecolor, - TextFloatFill, - VisualizationLevelLow, - VisualizationLevelMid, - VisualizationLevelPeak, - NumColorRoles - }; - LmmsStyle(); ~LmmsStyle() override = default; @@ -88,8 +60,6 @@ public: private: QImage colorizeXpm( const char * const * xpm, const QBrush& fill ) const; void hoverColors( bool sunken, bool hover, bool active, QColor& color, QColor& blend ) const; - QColor m_colors[ LmmsStyle::NumColorRoles ]; - }; diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index f5cb0cdb4..2d44f0ecf 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -74,7 +74,7 @@ class PluginIssue; class LMMS_EXPORT Lv2ControlBase : public LinkedModelGroups { public: - static Plugin::PluginTypes check(const LilvPlugin* m_plugin, + static Plugin::Type check(const LilvPlugin* m_plugin, std::vector &issues); void shutdown(); diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 89f6a0efd..909dba560 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -95,18 +95,18 @@ public: //! use only for std::map internals Lv2Info() : m_plugin(nullptr) {} //! ctor used inside Lv2Manager - Lv2Info(const LilvPlugin* plug, Plugin::PluginTypes type, bool valid) : + Lv2Info(const LilvPlugin* plug, Plugin::Type type, bool valid) : m_plugin(plug), m_type(type), m_valid(valid) {} Lv2Info(Lv2Info&& other) = default; Lv2Info& operator=(Lv2Info&& other) = default; const LilvPlugin* plugin() const { return m_plugin; } - Plugin::PluginTypes type() const { return m_type; } + Plugin::Type type() const { return m_type; } bool isValid() const { return m_valid; } private: const LilvPlugin* m_plugin; - Plugin::PluginTypes m_type; + Plugin::Type m_type; bool m_valid = false; }; diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index 0f0b02913..e4c896ff3 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -33,6 +33,7 @@ #include #include +#include "Flags.h" #include "lmms_basics.h" #include "PluginIssue.h" @@ -210,12 +211,12 @@ private: struct AtomSeq : public VisitablePort { - enum FlagType + enum class FlagType { None = 0, Midi = 1 }; - unsigned flags = FlagType::None; + Flags flags = FlagType::None; struct Lv2EvbufDeleter { diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 1be284ced..62070def7 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -64,7 +64,7 @@ namespace Lv2Ports class Lv2Proc : public LinkedModelGroup { public: - static Plugin::PluginTypes check(const LilvPlugin* plugin, + static Plugin::Type check(const LilvPlugin* plugin, std::vector &issues); /* diff --git a/include/Lv2SubPluginFeatures.h b/include/Lv2SubPluginFeatures.h index 57eab8715..eb0bd9900 100644 --- a/include/Lv2SubPluginFeatures.h +++ b/include/Lv2SubPluginFeatures.h @@ -47,7 +47,7 @@ private: static QString pluginName(const LilvPlugin *plug); public: - Lv2SubPluginFeatures(Plugin::PluginTypes type); + Lv2SubPluginFeatures(Plugin::Type type); void fillDescriptionWidget( QWidget *parent, const Key *k) const override; diff --git a/include/MainWindow.h b/include/MainWindow.h index c4bbb6767..30d52ec3a 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -113,7 +113,7 @@ public: return m_autoSaveTimer.interval(); } - enum SessionState + enum class SessionState { Normal, Recover diff --git a/include/MidiClip.h b/include/MidiClip.h index bbb7d325d..43b322f80 100644 --- a/include/MidiClip.h +++ b/include/MidiClip.h @@ -46,7 +46,7 @@ class LMMS_EXPORT MidiClip : public Clip { Q_OBJECT public: - enum MidiClipTypes + enum class Type { BeatClip, MelodyClip @@ -82,7 +82,7 @@ public: void splitNotes(NoteVector notes, TimePos pos); // clip-type stuff - inline MidiClipTypes type() const + inline Type type() const { return m_clipType; } @@ -129,14 +129,14 @@ protected slots: private: TimePos beatClipLength() const; - void setType( MidiClipTypes _new_clip_type ); + void setType( Type _new_clip_type ); void checkType(); void resizeToFirstTrack(); InstrumentTrack * m_instrumentTrack; - MidiClipTypes m_clipType; + Type m_clipType; // data-stuff NoteVector m_notes; diff --git a/include/MidiPort.h b/include/MidiPort.h index 6f759708e..6078f7a9b 100644 --- a/include/MidiPort.h +++ b/include/MidiPort.h @@ -69,20 +69,19 @@ class MidiPort : public Model, public SerializingObject public: using Map = QMap; - enum Modes + enum class Mode { Disabled, // don't route any MIDI-events (default) Input, // from MIDI-client to MIDI-event-processor Output, // from MIDI-event-processor to MIDI-client Duplex // both directions } ; - using Mode = Modes; MidiPort( const QString& name, MidiClient* client, MidiEventProcessor* eventProcessor, Model* parent = nullptr, - Mode mode = Disabled ); + Mode mode = Mode::Disabled ); ~MidiPort() override; void setName( const QString& name ); @@ -96,12 +95,12 @@ public: bool isInputEnabled() const { - return mode() == Input || mode() == Duplex; + return mode() == Mode::Input || mode() == Mode::Duplex; } bool isOutputEnabled() const { - return mode() == Output || mode() == Duplex; + return mode() == Mode::Output || mode() == Mode::Duplex; } int realOutputChannel() const diff --git a/include/MidiPortMenu.h b/include/MidiPortMenu.h index 0b3fc1b2f..59604969b 100644 --- a/include/MidiPortMenu.h +++ b/include/MidiPortMenu.h @@ -40,7 +40,7 @@ class MidiPortMenu : public QMenu, public ModelView { Q_OBJECT public: - MidiPortMenu( MidiPort::Modes _mode ); + MidiPortMenu( MidiPort::Mode _mode ); ~MidiPortMenu() override = default; @@ -55,7 +55,7 @@ protected slots: private: void modelChanged() override; - MidiPort::Modes m_mode; + MidiPort::Mode m_mode; } ; diff --git a/include/Note.h b/include/Note.h index a5c60ef8b..5e3a1b8a2 100644 --- a/include/Note.h +++ b/include/Note.h @@ -42,47 +42,53 @@ namespace lmms class DetuningHelper; -enum Keys +enum class Key : int { - Key_C = 0, - Key_CIS = 1, Key_DES = 1, - Key_D = 2, - Key_DIS = 3, Key_ES = 3, - Key_E = 4, Key_FES = 4, - Key_F = 5, - Key_FIS = 6, Key_GES = 6, - Key_G = 7, - Key_GIS = 8, Key_AS = 8, - Key_A = 9, - Key_AIS = 10, Key_B = 10, - Key_H = 11 + C = 0, + Cis = 1, Des = 1, + D = 2, + Dis = 3, Es = 3, + E = 4, Fes = 4, + F = 5, + Fis = 6, Ges = 6, + G = 7, + Gis = 8, As = 8, + A = 9, + Ais = 10, B = 10, + H = 11 } ; -enum Octaves +enum class Octave : int { Octave_m1, // MIDI standard starts at C-1 Octave_0, Octave_1, Octave_2, Octave_3, - Octave_4, DefaultOctave = Octave_4, + Octave_4, Octave_5, Octave_6, Octave_7, Octave_8, Octave_9, // incomplete octave, MIDI only goes up to G9 - NumOctaves }; const int FirstOctave = -1; const int KeysPerOctave = 12; -const int DefaultKey = DefaultOctave * KeysPerOctave + Key_A; + +constexpr inline auto operator+(Octave octave, Key key) -> int +{ + return static_cast(octave) * KeysPerOctave + static_cast(key); +} + +constexpr auto DefaultOctave = Octave::Octave_4; +const int DefaultKey = DefaultOctave + Key::A; //! Number of physical keys, limited to MIDI range (valid for both MIDI 1.0 and 2.0) const int NumKeys = 128; -const int DefaultMiddleKey = Octave_4 * KeysPerOctave + Key_C; -const int DefaultBaseKey = Octave_4 * KeysPerOctave + Key_A; +const int DefaultMiddleKey = Octave::Octave_4 + Key::C; +const int DefaultBaseKey = Octave::Octave_4 + Key::A; const float DefaultBaseFreq = 440.f; const float MaxDetuning = 4 * 12.0f; diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index 29477705b..46b14c4cd 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -56,15 +56,13 @@ public: fpp_t m_fadeInLength; // specifies origin of NotePlayHandle - enum Origins + enum class Origin { - OriginMidiClip, /*! playback of a note from a MIDI clip */ - OriginMidiInput, /*! playback of a MIDI note input event */ - OriginNoteStacking, /*! created by note stacking instrument function */ - OriginArpeggio, /*! created by arpeggio instrument function */ - OriginCount + MidiClip, /*! playback of a note from a MIDI clip */ + MidiInput, /*! playback of a MIDI note input event */ + NoteStacking, /*! created by note stacking instrument function */ + Arpeggio, /*! created by arpeggio instrument function */ }; - using Origin = Origins; NotePlayHandle( InstrumentTrack* instrumentTrack, const f_cnt_t offset, @@ -72,7 +70,7 @@ public: const Note& noteToPlay, NotePlayHandle* parent = nullptr, int midiEventChannel = -1, - Origin origin = OriginMidiClip ); + Origin origin = Origin::MidiClip ); ~NotePlayHandle() override; void * operator new ( size_t size, void * p ) @@ -349,7 +347,7 @@ public: const Note& noteToPlay, NotePlayHandle* parent = nullptr, int midiEventChannel = -1, - NotePlayHandle::Origin origin = NotePlayHandle::OriginMidiClip ); + NotePlayHandle::Origin origin = NotePlayHandle::Origin::MidiClip ); static void release( NotePlayHandle * nph ); static void extend( int i ); static void free(); diff --git a/include/Oscillator.h b/include/Oscillator.h index 46d858032..dab0b948d 100644 --- a/include/Oscillator.h +++ b/include/Oscillator.h @@ -48,31 +48,34 @@ class LMMS_EXPORT Oscillator { MM_OPERATORS public: - enum WaveShapes + enum class WaveShape { - SineWave, - TriangleWave, - SawWave, - SquareWave, - MoogSawWave, - ExponentialWave, + Sine, + Triangle, + Saw, + Square, + MoogSaw, + Exponential, WhiteNoise, - UserDefinedWave, - NumWaveShapes, //!< Number of all available wave shapes - FirstWaveShapeTable = TriangleWave, //!< First wave shape that has a pre-generated table - NumWaveShapeTables = WhiteNoise - FirstWaveShapeTable, //!< Number of band-limited wave shapes to be generated + UserDefined, + Count //!< Number of all available wave shapes }; + constexpr static auto NumWaveShapes = static_cast(WaveShape::Count); + //! First wave shape that has a pre-generated table + constexpr static auto FirstWaveShapeTable = static_cast(WaveShape::Triangle); + //! Number of band-limited wave shapes to be generated + constexpr static auto NumWaveShapeTables = static_cast(WaveShape::WhiteNoise) - FirstWaveShapeTable; - enum ModulationAlgos + enum class ModulationAlgo { PhaseModulation, AmplitudeModulation, SignalMix, SynchronizedBySubOsc, FrequencyModulation, - NumModulationAlgos + Count } ; - + constexpr static auto NumModulationAlgos = static_cast(ModulationAlgo::Count); Oscillator( const IntModel *wave_shape_model, const IntModel *mod_algo_model, @@ -251,7 +254,7 @@ private: bool m_isModulator; /* Multiband WaveTable */ - static sample_t s_waveTables[WaveShapes::NumWaveShapeTables][OscillatorConstants::WAVE_TABLES_PER_WAVEFORM_COUNT][OscillatorConstants::WAVETABLE_LENGTH]; + static sample_t s_waveTables[NumWaveShapeTables][OscillatorConstants::WAVE_TABLES_PER_WAVEFORM_COUNT][OscillatorConstants::WAVETABLE_LENGTH]; static fftwf_plan s_fftPlan; static fftwf_plan s_ifftPlan; static fftwf_complex * s_specBuf; @@ -284,26 +287,26 @@ private: const ch_cnt_t _chnl ); inline bool syncOk( float _osc_coeff ); - template + template void updateNoSub( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - template + template void updatePM( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - template + template void updateAM( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - template + template void updateMix( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - template + template void updateSync( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - template + template void updateFM( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - template + template inline sample_t getSample( const float _sample ); inline void recalcPhase(); diff --git a/include/OutputSettings.h b/include/OutputSettings.h index 12242e1bb..94de0612c 100644 --- a/include/OutputSettings.h +++ b/include/OutputSettings.h @@ -35,19 +35,18 @@ namespace lmms class OutputSettings { public: - enum BitDepth + enum class BitDepth { - Depth_16Bit, - Depth_24Bit, - Depth_32Bit, - NumDepths + Depth16Bit, + Depth24Bit, + Depth32Bit }; - enum StereoMode + enum class StereoMode { - StereoMode_Stereo, - StereoMode_JointStereo, - StereoMode_Mono + Stereo, + JointStereo, + Mono }; class BitRateSettings @@ -85,7 +84,7 @@ public: OutputSettings( sample_rate_t sampleRate, BitRateSettings const & bitRateSettings, BitDepth bitDepth ) : - OutputSettings(sampleRate, bitRateSettings, bitDepth, StereoMode_Stereo ) + OutputSettings(sampleRate, bitRateSettings, bitDepth, StereoMode::Stereo ) { } diff --git a/include/Piano.h b/include/Piano.h index 96f374840..698d9c8fe 100644 --- a/include/Piano.h +++ b/include/Piano.h @@ -38,10 +38,10 @@ class MidiEventProcessor; class Piano final : public Model { public: - enum KeyTypes + enum class KeyType { - WhiteKey, - BlackKey + White, + Black } ; Piano(InstrumentTrack* track); diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 03b93d816..9f3bbcd7d 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -104,13 +104,13 @@ class PianoRoll : public QWidget Q_PROPERTY(QBrush blackKeyActiveBackground MEMBER m_blackKeyActiveBackground) Q_PROPERTY(QBrush blackKeyDisabledBackground MEMBER m_blackKeyDisabledBackground) public: - enum EditModes + enum class EditMode { - ModeDraw, - ModeErase, - ModeSelect, - ModeEditDetuning, - ModeEditKnife + Draw, + Erase, + Select, + Detuning, + Knife }; /*! \brief Resets settings to default when e.g. creating a new project */ @@ -153,16 +153,26 @@ public: int trackOctaveSize() const; - Song::PlayModes desiredPlayModeForAccompany() const; + Song::PlayMode desiredPlayModeForAccompany() const; int quantization() const; protected: - enum QuantizeActions + enum class QuantizeAction { - QuantizeBoth, - QuantizePos, - QuantizeLength + Both, + Pos, + Length + }; + + enum class SemiToneMarkerAction + { + UnmarkAll, + MarkCurrentSemiTone, + MarkAllOctaveSemiTones, + MarkCurrentScale, + MarkCurrentChord, + CopyAllNotesOnKey }; void keyPressEvent( QKeyEvent * ke ) override; @@ -221,12 +231,12 @@ protected slots: void quantizeChanged(); void noteLengthChanged(); void keyChanged(); - void quantizeNotes(lmms::gui::PianoRoll::QuantizeActions mode = QuantizeBoth); + void quantizeNotes(QuantizeAction mode = QuantizeAction::Both); void updateSemiToneMarkerMenu(); void changeNoteEditMode( int i ); - void markSemiTone(int i, bool fromMenu = true); + void markSemiTone(SemiToneMarkerAction i, bool fromMenu = true); void hideMidiClip( lmms::MidiClip* clip ); @@ -248,46 +258,36 @@ signals: private: - enum Actions + enum class Action { - ActionNone, - ActionMoveNote, - ActionResizeNote, - ActionSelectNotes, - ActionChangeNoteProperty, - ActionResizeNoteEditArea, - ActionKnife + None, + MoveNote, + ResizeNote, + SelectNotes, + ChangeNoteProperty, + ResizeNoteEditArea, + Knife }; - enum NoteEditMode + enum class NoteEditMode { - NoteEditVolume, - NoteEditPanning, - NoteEditCount // make sure this one is always last + Volume, + Panning, + Count // make sure this one is always last }; - enum SemiToneMarkerAction + enum class KeyType { - stmaUnmarkAll, - stmaMarkCurrentSemiTone, - stmaMarkAllOctaveSemiTones, - stmaMarkCurrentScale, - stmaMarkCurrentChord, - stmaCopyAllNotesOnKey + WhiteSmall, + WhiteBig, + Black }; - enum PianoRollKeyTypes + enum class GridMode { - PR_WHITE_KEY_SMALL, - PR_WHITE_KEY_BIG, - PR_BLACK_KEY - }; - - enum GridMode - { - gridNudge, - gridSnap - // gridFree + Nudge, + Snap + // Free }; PositionLine * m_positionLine; @@ -346,7 +346,7 @@ private: static QPixmap * s_toolOpen; static QPixmap* s_toolKnife; - static std::array prKeyOrder; + static std::array prKeyOrder; static SimpleTextFloat * s_textFloat; @@ -378,7 +378,7 @@ private: QList m_recordingNotes; Note * m_currentNote; - Actions m_action; + Action m_action; NoteEditMode m_noteEditMode; GridMode m_gridMode; @@ -429,9 +429,9 @@ private: int m_startKey; // first key when drawing int m_lastKey; - EditModes m_editMode; - EditModes m_ctrlMode; // mode they were in before they hit ctrl - EditModes m_knifeMode; // mode they where in before entering knife mode + EditMode m_editMode; + EditMode m_ctrlMode; // mode they were in before they hit ctrl + EditMode m_knifeMode; // mode they where in before entering knife mode bool m_mouseDownRight; //true if right click is being held down diff --git a/include/PlayHandle.h b/include/PlayHandle.h index c64931ac0..8f5d771ed 100644 --- a/include/PlayHandle.h +++ b/include/PlayHandle.h @@ -30,7 +30,7 @@ #include "lmms_export.h" - +#include "Flags.h" #include "ThreadableJob.h" #include "lmms_basics.h" @@ -45,19 +45,16 @@ class AudioPort; class LMMS_EXPORT PlayHandle : public ThreadableJob { public: - enum Types + enum class Type { - TypeNotePlayHandle = 0x01, - TypeInstrumentPlayHandle = 0x02, - TypeSamplePlayHandle = 0x04, - TypePresetPreviewHandle = 0x08 + NotePlayHandle = 0x01, + InstrumentPlayHandle = 0x02, + SamplePlayHandle = 0x04, + PresetPreviewHandle = 0x08 } ; - using Type = Types; + using Types = Flags; - enum - { - MaxNumber = 1024 - } ; + constexpr static std::size_t MaxNumber = 1024; PlayHandle( const Type type, f_cnt_t offset = 0 ); @@ -164,6 +161,8 @@ private: using PlayHandleList = QList; using ConstPlayHandleList = QList; +LMMS_DECLARE_OPERATORS_FOR_FLAGS(PlayHandle::Type) + } // namespace lmms #endif // LMMS_PLAY_HANDLE_H diff --git a/include/Plugin.h b/include/Plugin.h index b1982f98c..439dd95ad 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -74,7 +74,7 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject MM_OPERATORS Q_OBJECT public: - enum PluginTypes + enum class Type { Instrument, // instrument being used in channel-track Effect, // effect-plugin for effect-board @@ -97,7 +97,7 @@ public: const char * description; const char * author; int version; - PluginTypes type; + Type type; const PixmapLoader * logo; const char * supportedFileTypes; //!< csv list of extensions @@ -181,7 +181,7 @@ public: using KeyList = QList; - SubPluginFeatures( Plugin::PluginTypes type ) : + SubPluginFeatures( Plugin::Type type ) : m_type( type ) { } @@ -227,7 +227,7 @@ public: } protected: - const Plugin::PluginTypes m_type; + const Plugin::Type m_type; } ; SubPluginFeatures * subPluginFeatures; @@ -250,7 +250,7 @@ public: const PixmapLoader *logo() const; //! Return plugin type - inline PluginTypes type() const + inline Type type() const { return m_descriptor->type; } diff --git a/include/PluginFactory.h b/include/PluginFactory.h index 10c76e4ee..7221f2b09 100644 --- a/include/PluginFactory.h +++ b/include/PluginFactory.h @@ -55,7 +55,7 @@ public: bool isNull() const {return ! library;} }; using PluginInfoList = QList; - using DescriptorMap = QMultiMap; + using DescriptorMap = QMultiMap; PluginFactory(); ~PluginFactory() = default; @@ -68,7 +68,7 @@ public: /// Returns a list of all found plugins' descriptors. Plugin::DescriptorList descriptors() const; - Plugin::DescriptorList descriptors(Plugin::PluginTypes type) const; + Plugin::DescriptorList descriptors(Plugin::Type type) const; struct PluginInfoAndKey { diff --git a/include/PluginIssue.h b/include/PluginIssue.h index 87e895113..01a4268ec 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -33,32 +33,32 @@ namespace lmms //! Types of issues that can cause LMMS to not load a plugin //! LMMS Plugins should use this to indicate errors -enum PluginIssueType +enum class PluginIssueType { // port flow & type - unknownPortFlow, - unknownPortType, + UnknownPortFlow, + UnknownPortType, // channel count - tooManyInputChannels, - tooManyOutputChannels, - tooManyMidiInputChannels, - tooManyMidiOutputChannels, - noOutputChannel, + TooManyInputChannels, + TooManyOutputChannels, + TooManyMidiInputChannels, + TooManyMidiOutputChannels, + NoOutputChannel, // port metadata - portHasNoDef, - portHasNoMin, - portHasNoMax, - minGreaterMax, - defaultValueNotInRange, - logScaleMinMissing, - logScaleMaxMissing, - logScaleMinMaxDifferentSigns, + PortHasNoDef, + PortHasNoMin, + PortHasNoMax, + MinGreaterMax, + DefaultValueNotInRange, + LogScaleMinMissing, + LogScaleMaxMissing, + LogScaleMinMaxDifferentSigns, // features - featureNotSupported, //!< plugin requires functionality LMMS can't offer + FeatureNotSupported, //!< plugin requires functionality LMMS can't offer // misc - badPortType, //!< port type not supported - blacklisted, - noIssue + BadPortType, //!< port type not supported + Blacklisted, + NoIssue }; //! Issue type bundled with informational string diff --git a/include/ProjectJournal.h b/include/ProjectJournal.h index a4a263078..841bbf094 100644 --- a/include/ProjectJournal.h +++ b/include/ProjectJournal.h @@ -104,7 +104,7 @@ private: struct CheckPoint { - CheckPoint( jo_id_t initID = 0, const DataFile& initData = DataFile( DataFile::JournalData ) ) : + CheckPoint( jo_id_t initID = 0, const DataFile& initData = DataFile( DataFile::Type::JournalData ) ) : joID( initID ), data( initData ) { diff --git a/include/ProjectRenderer.h b/include/ProjectRenderer.h index 95a1f53ed..14c584a2e 100644 --- a/include/ProjectRenderer.h +++ b/include/ProjectRenderer.h @@ -40,20 +40,21 @@ class LMMS_EXPORT ProjectRenderer : public QThread { Q_OBJECT public: - enum ExportFileFormats: int + enum class ExportFileFormat : int { - WaveFile, - FlacFile, - OggFile, - MP3File, - NumFileFormats + Wave, + Flac, + Ogg, + MP3, + Count } ; + constexpr static auto NumFileFormats = static_cast(ExportFileFormat::Count); struct FileEncodeDevice { bool isAvailable() const { return m_getDevInst != nullptr; } - ExportFileFormats m_fileFormat; + ExportFileFormat m_fileFormat; const char * m_description; const char * m_extension; AudioFileDeviceInstantiaton m_getDevInst; @@ -62,7 +63,7 @@ public: ProjectRenderer( const AudioEngine::qualitySettings & _qs, const OutputSettings & _os, - ExportFileFormats _file_format, + ExportFileFormat _file_format, const QString & _out_file ); ~ProjectRenderer() override = default; @@ -71,10 +72,10 @@ public: return m_fileDev != nullptr; } - static ExportFileFormats getFileFormatFromExtension( + static ExportFileFormat getFileFormatFromExtension( const QString & _ext ); - static QString getFileExtensionFromFormat( ExportFileFormats fmt ); + static QString getFileExtensionFromFormat( ExportFileFormat fmt ); static const std::array fileEncodeDevices; diff --git a/include/ProjectVersion.h b/include/ProjectVersion.h index d9a459a43..20b32d1c9 100644 --- a/include/ProjectVersion.h +++ b/include/ProjectVersion.h @@ -42,11 +42,11 @@ namespace lmms class ProjectVersion { public: - enum CompareType : int { None = 0, Major=1, Minor=2, Release=3, Stage=4, Build=5, All = std::numeric_limits::max() }; + enum class CompareType : int { None = 0, Major=1, Minor=2, Release=3, Stage=4, Build=5, All = std::numeric_limits::max() }; - ProjectVersion(QString version, CompareType c = All); - ProjectVersion(const char * version, CompareType c = All); + ProjectVersion(QString version, CompareType c = CompareType::All); + ProjectVersion(const char * version, CompareType c = CompareType::All); const QString& getVersion() const { return m_version; } int getMajor() const { return m_major; } diff --git a/include/RenderManager.h b/include/RenderManager.h index 43f696f14..686522778 100644 --- a/include/RenderManager.h +++ b/include/RenderManager.h @@ -43,7 +43,7 @@ public: RenderManager( const AudioEngine::qualitySettings & qualitySettings, const OutputSettings & outputSettings, - ProjectRenderer::ExportFileFormats fmt, + ProjectRenderer::ExportFileFormat fmt, QString outputPath); ~RenderManager() override; @@ -73,7 +73,7 @@ private: const AudioEngine::qualitySettings m_qualitySettings; const AudioEngine::qualitySettings m_oldQualitySettings; const OutputSettings m_outputSettings; - ProjectRenderer::ExportFileFormats m_format; + ProjectRenderer::ExportFileFormat m_format; QString m_outputPath; std::unique_ptr m_activeRenderer; diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index e2ed24b81..3d1013baa 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -57,10 +57,10 @@ class LMMS_EXPORT SampleBuffer : public QObject, public sharedObject Q_OBJECT MM_OPERATORS public: - enum LoopMode { - LoopOff = 0, - LoopOn, - LoopPingPong + enum class LoopMode { + Off = 0, + On, + PingPong }; class LMMS_EXPORT handleState { @@ -125,7 +125,7 @@ public: handleState * state, const fpp_t frames, const float freq, - const LoopMode loopMode = LoopOff + const LoopMode loopMode = LoopMode::Off ); void visualize( diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 27a4ce4f9..de4cdd9dd 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -53,7 +53,7 @@ class SetupDialog : public QDialog Q_OBJECT public: - enum ConfigTabs + enum class ConfigTab { GeneralSettings, PerformanceSettings, @@ -62,7 +62,7 @@ public: PathsSettings }; - SetupDialog(ConfigTabs tab_to_open = GeneralSettings); + SetupDialog(ConfigTab tab_to_open = ConfigTab::GeneralSettings); ~SetupDialog() override; diff --git a/include/Song.h b/include/Song.h index c13fe0e96..02714d8ac 100644 --- a/include/Song.h +++ b/include/Song.h @@ -68,15 +68,16 @@ class LMMS_EXPORT Song : public TrackContainer mapPropertyFromModel( int,masterPitch,setMasterPitch,m_masterPitchModel ); mapPropertyFromModel( int,masterVolume,setMasterVolume, m_masterVolumeModel ); public: - enum PlayModes + enum class PlayMode { - Mode_None, - Mode_PlaySong, - Mode_PlayPattern, - Mode_PlayMidiClip, - Mode_PlayAutomationClip, - Mode_Count + None, + Song, + Pattern, + MidiClip, + AutomationClip, + Count } ; + constexpr static auto PlayModeCount = static_cast(PlayMode::Count); struct SaveOptions { /** @@ -141,36 +142,34 @@ public: inline int getMilliseconds() const { - return m_elapsedMilliSeconds[m_playMode]; + return getMilliseconds(m_playMode); } - inline int getMilliseconds(PlayModes playMode) const + inline int getMilliseconds(PlayMode playMode) const { - return m_elapsedMilliSeconds[playMode]; + return m_elapsedMilliSeconds[static_cast(playMode)]; } inline void setToTime(TimePos const & pos) { - m_elapsedMilliSeconds[m_playMode] = pos.getTimeInMilliseconds(getTempo()); - m_playPos[m_playMode].setTicks(pos.getTicks()); + setToTime(pos, m_playMode); } - inline void setToTime(TimePos const & pos, PlayModes playMode) + inline void setToTime(TimePos const & pos, PlayMode playMode) { - m_elapsedMilliSeconds[playMode] = pos.getTimeInMilliseconds(getTempo()); - m_playPos[playMode].setTicks(pos.getTicks()); + m_elapsedMilliSeconds[static_cast(playMode)] = pos.getTimeInMilliseconds(getTempo()); + getPlayPos(playMode).setTicks(pos.getTicks()); } inline void setToTimeByTicks(tick_t ticks) { - m_elapsedMilliSeconds[m_playMode] = TimePos::ticksToMilliseconds(ticks, getTempo()); - m_playPos[m_playMode].setTicks(ticks); + setToTimeByTicks(ticks, m_playMode); } - inline void setToTimeByTicks(tick_t ticks, PlayModes playMode) + inline void setToTimeByTicks(tick_t ticks, PlayMode playMode) { - m_elapsedMilliSeconds[playMode] = TimePos::ticksToMilliseconds(ticks, getTempo()); - m_playPos[playMode].setTicks(ticks); + m_elapsedMilliSeconds[static_cast(playMode)] = TimePos::ticksToMilliseconds(ticks, getTempo()); + getPlayPos(playMode).setTicks(ticks); } inline int getBars() const @@ -253,18 +252,18 @@ public: m_renderBetweenMarkers = renderBetweenMarkers; } - inline PlayModes playMode() const + inline PlayMode playMode() const { return m_playMode; } - inline PlayPos & getPlayPos( PlayModes pm ) + inline PlayPos & getPlayPos( PlayMode pm ) { - return m_playPos[pm]; + return m_playPos[static_cast(pm)]; } - inline const PlayPos & getPlayPos( PlayModes pm ) const + inline const PlayPos & getPlayPos( PlayMode pm ) const { - return m_playPos[pm]; + return m_playPos[static_cast(pm)]; } inline PlayPos & getPlayPos() { @@ -417,21 +416,21 @@ private: inline bar_t currentBar() const { - return m_playPos[m_playMode].getBar(); + return getPlayPos(m_playMode).getBar(); } inline tick_t currentTick() const { - return m_playPos[m_playMode].getTicks(); + return getPlayPos(m_playMode).getTicks(); } inline f_cnt_t currentFrame() const { - return m_playPos[m_playMode].getTicks() * Engine::framesPerTick() + - m_playPos[m_playMode].currentFrame(); + return getPlayPos(m_playMode).getTicks() * Engine::framesPerTick() + + getPlayPos(m_playMode).currentFrame(); } - void setPlayPos( tick_t ticks, PlayModes playMode ); + void setPlayPos( tick_t ticks, PlayMode playMode ); void saveControllerStates( QDomDocument & doc, QDomElement & element ); void restoreControllerStates( const QDomElement & element ); @@ -482,14 +481,14 @@ private: QHash m_errors; - PlayModes m_playMode; - PlayPos m_playPos[Mode_Count]; + PlayMode m_playMode; + PlayPos m_playPos[PlayModeCount]; bar_t m_length; const MidiClip* m_midiClipToPlay; bool m_loopMidiClip; - double m_elapsedMilliSeconds[Mode_Count]; + double m_elapsedMilliSeconds[PlayModeCount]; tick_t m_elapsedTicks; bar_t m_elapsedBars; diff --git a/include/SongEditor.h b/include/SongEditor.h index 867ff0f14..ee9e83f44 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -57,11 +57,11 @@ class SongEditor : public TrackContainerView { Q_OBJECT public: - enum EditMode + enum class EditMode { - DrawMode, - KnifeMode, - SelectMode + Draw, + Knife, + Select }; SongEditor( Song * song ); diff --git a/include/TempoSyncKnob.h b/include/TempoSyncKnob.h index a8e2eeb7e..b86320d13 100644 --- a/include/TempoSyncKnob.h +++ b/include/TempoSyncKnob.h @@ -41,7 +41,7 @@ class LMMS_EXPORT TempoSyncKnob : public Knob { Q_OBJECT public: - TempoSyncKnob( knobTypes knobNum, QWidget* parent = nullptr, const QString& name = QString() ); + TempoSyncKnob( KnobType knobNum, QWidget* parent = nullptr, const QString& name = QString() ); ~TempoSyncKnob() override; const QString & syncDescription(); diff --git a/include/TempoSyncKnobModel.h b/include/TempoSyncKnobModel.h index 59d7b5dc8..5cd2db067 100644 --- a/include/TempoSyncKnobModel.h +++ b/include/TempoSyncKnobModel.h @@ -46,17 +46,17 @@ class LMMS_EXPORT TempoSyncKnobModel : public FloatModel Q_OBJECT MODEL_IS_VISITABLE public: - enum TempoSyncMode + enum class SyncMode { - SyncNone, - SyncDoubleWholeNote, - SyncWholeNote, - SyncHalfNote, - SyncQuarterNote, - SyncEighthNote, - SyncSixteenthNote, - SyncThirtysecondNote, - SyncCustom + None, + DoubleWholeNote, + WholeNote, + HalfNote, + QuarterNote, + EighthNote, + SixteenthNote, + ThirtysecondNote, + Custom } ; TempoSyncKnobModel( const float _val, const float _min, @@ -68,12 +68,12 @@ public: void saveSettings( QDomDocument & _doc, QDomElement & _this, const QString& name ) override; void loadSettings( const QDomElement & _this, const QString& name ) override; - TempoSyncMode syncMode() const + SyncMode syncMode() const { return m_tempoSyncMode; } - void setSyncMode( TempoSyncMode _new_mode ); + void setSyncMode( SyncMode _new_mode ); float scale() const { @@ -83,16 +83,16 @@ public: void setScale( float _new_scale ); signals: - void syncModeChanged( lmms::TempoSyncKnobModel::TempoSyncMode _new_mode ); + void syncModeChanged( lmms::TempoSyncKnobModel::SyncMode _new_mode ); void scaleChanged( float _new_scale ); public slots: inline void disableSync() { - setTempoSync( SyncNone ); + setTempoSync( SyncMode::None ); } - void setTempoSync( int _note_type ); + void setTempoSync( SyncMode _note_type ); void setTempoSync( QAction * _item ); @@ -102,8 +102,8 @@ protected slots: private: - TempoSyncMode m_tempoSyncMode; - TempoSyncMode m_tempoLastSyncMode; + SyncMode m_tempoSyncMode; + SyncMode m_tempoLastSyncMode; float m_scale; MeterModel m_custom; diff --git a/include/TimeDisplayWidget.h b/include/TimeDisplayWidget.h index 9e74b57aa..287b4ee7e 100644 --- a/include/TimeDisplayWidget.h +++ b/include/TimeDisplayWidget.h @@ -51,13 +51,11 @@ private slots: private: - enum DisplayModes + enum class DisplayMode { MinutesSeconds, - BarsTicks, - DisplayModeCount + BarsTicks }; - using DisplayMode = DisplayModes; void setDisplayMode( DisplayMode displayMode ); diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 061a31081..2e4ba6a97 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -55,19 +55,19 @@ public: Q_PROPERTY( QColor activeLoopInnerColor READ getActiveLoopInnerColor WRITE setActiveLoopInnerColor ) Q_PROPERTY( int loopRectangleVerticalPadding READ getLoopRectangleVerticalPadding WRITE setLoopRectangleVerticalPadding ) - enum AutoScrollStates + enum class AutoScrollState { - AutoScrollEnabled, - AutoScrollDisabled + Enabled, + Disabled } ; - enum LoopPointStates + enum class LoopPointState { - LoopPointsDisabled, - LoopPointsEnabled + Disabled, + Enabled } ; - enum BehaviourAtStopStates + enum class BehaviourAtStopState { BackToZero, BackToStart, @@ -76,7 +76,7 @@ public: TimeLineWidget(int xoff, int yoff, float ppb, Song::PlayPos & pos, - const TimePos & begin, Song::PlayModes mode, QWidget * parent); + const TimePos & begin, Song::PlayMode mode, QWidget * parent); ~TimeLineWidget() override; inline QColor const & getBarLineColor() const { return m_barLineColor; } @@ -111,12 +111,12 @@ public: return( m_pos ); } - AutoScrollStates autoScroll() const + AutoScrollState autoScroll() const { return m_autoScroll; } - BehaviourAtStopStates behaviourAtStop() const + BehaviourAtStopState behaviourAtStop() const { return m_behaviourAtStop; } @@ -128,7 +128,7 @@ public: bool loopPointsEnabled() const { - return m_loopPoints == LoopPointsEnabled; + return m_loopPoints == LoopPointState::Enabled; } inline const TimePos & loopBegin() const @@ -220,9 +220,9 @@ private: QColor m_barLineColor; QColor m_barNumberColor; - AutoScrollStates m_autoScroll; - LoopPointStates m_loopPoints; - BehaviourAtStopStates m_behaviourAtStop; + AutoScrollState m_autoScroll; + LoopPointState m_loopPoints; + BehaviourAtStopState m_behaviourAtStop; bool m_changedPosition; @@ -232,7 +232,7 @@ private: float m_snapSize; Song::PlayPos & m_pos; const TimePos & m_begin; - const Song::PlayModes m_mode; + const Song::PlayMode m_mode; TimePos m_loopPos[2]; TimePos m_savedPos; @@ -242,7 +242,7 @@ private: int m_initalXSelect; - enum actions + enum class Action { NoAction, MovePositionMarker, diff --git a/include/Track.h b/include/Track.h index 000d564f7..33d1ad233 100644 --- a/include/Track.h +++ b/include/Track.h @@ -72,29 +72,29 @@ class LMMS_EXPORT Track : public Model, public JournallingObject public: using clipVector = std::vector; - enum TrackTypes + enum class Type { - InstrumentTrack, - PatternTrack, - SampleTrack, - EventTrack, - VideoTrack, - AutomationTrack, - HiddenAutomationTrack, - NumTrackTypes + Instrument, + Pattern, + Sample, + Event, + Video, + Automation, + HiddenAutomation, + Count } ; - Track( TrackTypes type, TrackContainer * tc ); + Track( Type type, TrackContainer * tc ); ~Track() override; - static Track * create( TrackTypes tt, TrackContainer * tc ); + static Track * create( Type tt, TrackContainer * tc ); static Track * create( const QDomElement & element, TrackContainer * tc ); Track * clone(); // pure virtual functions - TrackTypes type() const + Type type() const { return m_type; } @@ -224,7 +224,7 @@ public slots: private: TrackContainer* m_trackContainer; - TrackTypes m_type; + Type m_type; QString m_name; int m_height; diff --git a/include/TrackContainer.h b/include/TrackContainer.h index 8739a9e9f..01e94df54 100644 --- a/include/TrackContainer.h +++ b/include/TrackContainer.h @@ -50,10 +50,10 @@ class LMMS_EXPORT TrackContainer : public Model, public JournallingObject Q_OBJECT public: using TrackList = std::vector; - enum TrackContainerTypes + enum class Type { - PatternContainer, - SongContainer + Pattern, + Song } ; TrackContainer(); @@ -63,7 +63,7 @@ public: void loadSettings( const QDomElement & _this ) override; - int countTracks( Track::TrackTypes _tt = Track::NumTrackTypes ) const; + int countTracks( Track::Type _tt = Track::Type::Count ) const; void addTrack( Track * _track ); @@ -85,12 +85,12 @@ public: return "trackcontainer"; } - inline void setType( TrackContainerTypes newType ) + inline void setType( Type newType ) { m_TrackContainerType = newType; } - inline TrackContainerTypes type() const + inline Type type() const { return m_TrackContainerType; } @@ -108,7 +108,7 @@ protected: private: TrackList m_tracks; - TrackContainerTypes m_TrackContainerType; + Type m_TrackContainerType; friend class gui::TrackContainerView; diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index d53291c33..82d6f993b 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -174,12 +174,6 @@ protected: private: - enum Actions - { - AddTrack, - RemoveTrack - } ; - class scrollArea : public QScrollArea { public: diff --git a/include/TrackContentWidget.h b/include/TrackContentWidget.h index 1a3e14a0e..7cf236323 100644 --- a/include/TrackContentWidget.h +++ b/include/TrackContentWidget.h @@ -95,7 +95,7 @@ public slots: void changePosition( const lmms::TimePos & newPos = TimePos( -1 ) ); protected: - enum ContextMenuAction + enum class ContextMenuAction { Paste }; diff --git a/include/TrackView.h b/include/TrackView.h index efa4f4a0a..763705599 100644 --- a/include/TrackView.h +++ b/include/TrackView.h @@ -95,7 +95,7 @@ public: bool isMovingTrack() const { - return m_action == MoveTrack; + return m_action == Action::Move; } virtual void update(); @@ -139,11 +139,11 @@ protected: private: - enum Actions + enum class Action { - NoAction, - MoveTrack, - ResizeTrack + None, + Move, + Resize } ; Track * m_track; @@ -153,7 +153,7 @@ private: QWidget m_trackSettingsWidget; TrackContentWidget m_trackContentWidget; - Actions m_action; + Action m_action; virtual FadeButton * getActivityIndicator() { diff --git a/include/fft_helpers.h b/include/fft_helpers.h index 2d2a8d19e..cd4e5f88d 100644 --- a/include/fft_helpers.h +++ b/include/fft_helpers.h @@ -44,12 +44,12 @@ const unsigned int FFT_BUFFER_SIZE = 2048; const std::vector FFT_BLOCK_SIZES = {256, 512, 1024, 2048, 4096, 8192, 16384}; // List of FFT window functions supported by precomputeWindow() -enum FFT_WINDOWS +enum class FFTWindow { - RECTANGULAR = 0, - BLACKMAN_HARRIS, - HAMMING, - HANNING + Rectangular = 0, + BlackmanHarris, + Hamming, + Hanning }; @@ -83,7 +83,7 @@ int LMMS_EXPORT notEmpty(const std::vector &spectrum); * * @return -1 on error */ -int LMMS_EXPORT precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool normalized = true); +int LMMS_EXPORT precomputeWindow(float *window, unsigned int length, FFTWindow type, bool normalized = true); /** Compute absolute values of complex_buffer, save to absspec_buffer. diff --git a/include/lmms_constants.h b/include/lmms_constants.h index e6fce9f4d..c6452d6c6 100644 --- a/include/lmms_constants.h +++ b/include/lmms_constants.h @@ -62,13 +62,13 @@ constexpr unsigned int MaxKeymapCount = 10; //!< number of keyboard mappings per constexpr int LOWEST_LOG_FREQ = 5; // Full range is defined by LOWEST_LOG_FREQ and current sample rate. -enum FREQUENCY_RANGES +enum class FrequencyRange { - FRANGE_FULL = 0, - FRANGE_AUDIBLE, - FRANGE_BASS, - FRANGE_MIDS, - FRANGE_HIGH + Full = 0, + Audible, + Bass, + Mids, + High }; constexpr int FRANGE_AUDIBLE_START = 20; @@ -83,12 +83,12 @@ constexpr int FRANGE_HIGH_END = 20000; // Amplitude ranges (in dBFS). // Reference: full scale sine wave (-1.0 to 1.0) is 0 dB. // Doubling or halving the amplitude produces 3 dB difference. -enum AMPLITUDE_RANGES +enum class AmplitudeRange { - ARANGE_EXTENDED = 0, - ARANGE_AUDIBLE, - ARANGE_LOUD, - ARANGE_SILENT + Extended = 0, + Audible, + Loud, + Silent }; constexpr int ARANGE_EXTENDED_START = -80; diff --git a/plugins/Amplifier/Amplifier.cpp b/plugins/Amplifier/Amplifier.cpp index 9344807c4..7de8fb180 100644 --- a/plugins/Amplifier/Amplifier.cpp +++ b/plugins/Amplifier/Amplifier.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT amplifier_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "A native amplifier plugin" ), "Vesa Kivimäki ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/Amplifier/AmplifierControlDialog.cpp b/plugins/Amplifier/AmplifierControlDialog.cpp index 226130fcd..ed9e98f29 100644 --- a/plugins/Amplifier/AmplifierControlDialog.cpp +++ b/plugins/Amplifier/AmplifierControlDialog.cpp @@ -43,27 +43,27 @@ AmplifierControlDialog::AmplifierControlDialog( AmplifierControls* controls ) : setPalette( pal ); setFixedSize( 100, 110 ); - auto volumeKnob = new Knob(knobBright_26, this); + auto volumeKnob = new Knob(KnobType::Bright26, this); volumeKnob -> move( 16, 10 ); volumeKnob -> setVolumeKnob( true ); volumeKnob->setModel( &controls->m_volumeModel ); volumeKnob->setLabel( tr( "VOL" ) ); volumeKnob->setHintText( tr( "Volume:" ) , "%" ); - auto panKnob = new Knob(knobBright_26, this); + auto panKnob = new Knob(KnobType::Bright26, this); panKnob -> move( 57, 10 ); panKnob->setModel( &controls->m_panModel ); panKnob->setLabel( tr( "PAN" ) ); panKnob->setHintText( tr( "Panning:" ) , "" ); - auto leftKnob = new Knob(knobBright_26, this); + auto leftKnob = new Knob(KnobType::Bright26, this); leftKnob -> move( 16, 65 ); leftKnob -> setVolumeKnob( true ); leftKnob->setModel( &controls->m_leftModel ); leftKnob->setLabel( tr( "LEFT" ) ); leftKnob->setHintText( tr( "Left gain:" ) , "%" ); - auto rightKnob = new Knob(knobBright_26, this); + auto rightKnob = new Knob(KnobType::Bright26, this); rightKnob -> move( 57, 65 ); rightKnob -> setVolumeKnob( true ); rightKnob->setModel( &controls->m_rightModel ); diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index 2243683ce..a941e773f 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -64,7 +64,7 @@ Plugin::Descriptor PLUGIN_EXPORT audiofileprocessor_plugin_descriptor = "instrument-track" ), "Tobias Doerffel ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), "wav,ogg,ds,spx,au,voc,aif,aiff,flac,raw", nullptr, @@ -516,7 +516,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, m_stutterButton->setToolTip( tr( "Continue sample playback across notes" ) ); - m_ampKnob = new Knob( knobBright_26, this ); + m_ampKnob = new Knob( KnobType::Bright26, this ); m_ampKnob->setVolumeKnob( true ); m_ampKnob->move( 5, 108 ); m_ampKnob->setHintText( tr( "Amplify:" ), "%" ); @@ -567,7 +567,7 @@ void AudioFileProcessorView::dragEnterEvent( QDragEnterEvent * _dee ) QString txt = _dee->mimeData()->data( mimeType( MimeType::StringPair ) ); if( txt.section( ':', 0, 0 ) == QString( "clip_%1" ).arg( - Track::SampleTrack ) ) + static_cast(Track::Type::Sample) ) ) { _dee->acceptProposedAction(); } @@ -619,7 +619,7 @@ void AudioFileProcessorView::dropEvent( QDropEvent * _de ) newWaveView(); return; } - else if( type == QString( "clip_%1" ).arg( Track::SampleTrack ) ) + else if( type == QString( "clip_%1" ).arg( static_cast(Track::Type::Sample) ) ) { DataFile dataFile( value.toUtf8() ); castModel()->setAudioFile( dataFile.content().firstChild().toElement().attribute( "src" ) ); @@ -787,9 +787,9 @@ void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me ) const int end_dist = qAbs( m_endFrameX - x ); const int loop_dist = qAbs( m_loopFrameX - x ); - draggingType dt = sample_loop; int md = loop_dist; - if( start_dist < loop_dist ) { dt = sample_start; md = start_dist; } - else if( end_dist < loop_dist ) { dt = sample_end; md = end_dist; } + DraggingType dt = DraggingType::SampleLoop; int md = loop_dist; + if( start_dist < loop_dist ) { dt = DraggingType::SampleStart; md = start_dist; } + else if( end_dist < loop_dist ) { dt = DraggingType::SampleEnd; md = end_dist; } if( md < 4 ) { @@ -797,7 +797,7 @@ void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me ) } else { - m_draggingType = wave; + m_draggingType = DraggingType::Wave; updateCursor(_me); } } @@ -808,7 +808,7 @@ void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me ) void AudioFileProcessorWaveView::mouseReleaseEvent( QMouseEvent * _me ) { m_isDragging = false; - if( m_draggingType == wave ) + if( m_draggingType == DraggingType::Wave ) { updateCursor(_me); } @@ -828,16 +828,16 @@ void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me ) const int step = _me->x() - m_draggingLastPoint.x(); switch( m_draggingType ) { - case sample_start: - slideSamplePointByPx( start, step ); + case DraggingType::SampleStart: + slideSamplePointByPx( Point::Start, step ); break; - case sample_end: - slideSamplePointByPx( end, step ); + case DraggingType::SampleEnd: + slideSamplePointByPx( Point::End, step ); break; - case sample_loop: - slideSamplePointByPx( loop, step ); + case DraggingType::SampleLoop: + slideSamplePointByPx( Point::Loop, step ); break; - case wave: + case DraggingType::Wave: default: if( qAbs( _me->y() - m_draggingLastPoint.y() ) < 2 * qAbs( _me->x() - m_draggingLastPoint.x() ) ) @@ -983,7 +983,7 @@ void AudioFileProcessorWaveView::updateGraph() if( m_to == 1 ) { m_to = m_sampleBuffer.frames() * 0.7; - slideSamplePointToFrames( end, m_to * 0.7 ); + slideSamplePointToFrames( Point::End, m_to * 0.7 ); } if( m_from > m_sampleBuffer.startFrame() ) @@ -1110,7 +1110,7 @@ void AudioFileProcessorWaveView::setKnobs( knob * _start, knob * _end, knob * _l -void AudioFileProcessorWaveView::slideSamplePointByPx( knobType _point, int _px ) +void AudioFileProcessorWaveView::slideSamplePointByPx( Point _point, int _px ) { slideSamplePointByFrames( _point, @@ -1121,18 +1121,18 @@ void AudioFileProcessorWaveView::slideSamplePointByPx( knobType _point, int _px -void AudioFileProcessorWaveView::slideSamplePointByFrames( knobType _point, f_cnt_t _frames, bool _slide_to ) +void AudioFileProcessorWaveView::slideSamplePointByFrames( Point _point, f_cnt_t _frames, bool _slide_to ) { knob * a_knob = m_startKnob; switch( _point ) { - case end: + case Point::End: a_knob = m_endKnob; break; - case loop: + case Point::Loop: a_knob = m_loopKnob; break; - case start: + case Point::Start: break; } if( a_knob == nullptr ) @@ -1196,7 +1196,7 @@ void AudioFileProcessorWaveView::reverse() void AudioFileProcessorWaveView::updateCursor( QMouseEvent * _me ) { - bool const waveIsDragged = m_isDragging && (m_draggingType == wave); + bool const waveIsDragged = m_isDragging && (m_draggingType == DraggingType::Wave); bool const pointerCloseToStartEndOrLoop = (_me != nullptr ) && ( isCloseTo( _me->x(), m_startFrameX ) || isCloseTo( _me->x(), m_endFrameX ) || diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.h b/plugins/AudioFileProcessor/AudioFileProcessor.h index 6c696784a..5fed10862 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.h +++ b/plugins/AudioFileProcessor/AudioFileProcessor.h @@ -177,11 +177,11 @@ protected: public: - enum knobType + enum class Point { - start, - end, - loop + Start, + End, + Loop } ; class knob : public Knob @@ -192,7 +192,7 @@ public: public: knob( QWidget * _parent ) : - Knob( knobBright_26, _parent ), + Knob( KnobType::Bright26, _parent ), m_waveView( 0 ), m_relatedKnob( 0 ) { @@ -239,12 +239,12 @@ public slots: private: static const int s_padding = 2; - enum draggingType + enum class DraggingType { - wave, - sample_start, - sample_end, - sample_loop + Wave, + SampleStart, + SampleEnd, + SampleLoop } ; SampleBuffer& m_sampleBuffer; @@ -262,7 +262,7 @@ private: f_cnt_t m_loopFrameX; bool m_isDragging; QPoint m_draggingLastPoint; - draggingType m_draggingType; + DraggingType m_draggingType; bool m_reversed; f_cnt_t m_framesPlayed; bool m_animation; @@ -276,11 +276,11 @@ public: private: void zoom( const bool _out = false ); void slide( int _px ); - void slideSamplePointByPx( knobType _point, int _px ); - void slideSamplePointByFrames( knobType _point, f_cnt_t _frames, bool _slide_to = false ); + void slideSamplePointByPx( Point _point, int _px ); + void slideSamplePointByFrames( Point _point, f_cnt_t _frames, bool _slide_to = false ); void slideSampleByFrames( f_cnt_t _frames ); - void slideSamplePointToFrames( knobType _point, f_cnt_t _frames ) + void slideSamplePointToFrames( Point _point, f_cnt_t _frames ) { slideSamplePointByFrames( _point, _frames, true ); } diff --git a/plugins/BassBooster/BassBooster.cpp b/plugins/BassBooster/BassBooster.cpp index 48e265911..e6b25b0d1 100644 --- a/plugins/BassBooster/BassBooster.cpp +++ b/plugins/BassBooster/BassBooster.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT bassbooster_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "Boost your bass the fast and simple way" ), "Tobias Doerffel ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/BassBooster/BassBoosterControlDialog.cpp b/plugins/BassBooster/BassBoosterControlDialog.cpp index 21aacb0f2..9efa07c0d 100644 --- a/plugins/BassBooster/BassBoosterControlDialog.cpp +++ b/plugins/BassBooster/BassBoosterControlDialog.cpp @@ -50,17 +50,17 @@ BassBoosterControlDialog::BassBoosterControlDialog( BassBoosterControls* control auto l = new QHBoxLayout; - auto freqKnob = new Knob(knobBright_26, this); + auto freqKnob = new Knob(KnobType::Bright26, this); freqKnob->setModel( &controls->m_freqModel ); freqKnob->setLabel( tr( "FREQ" ) ); freqKnob->setHintText( tr( "Frequency:" ) , "Hz" ); - auto gainKnob = new Knob(knobBright_26, this); + auto gainKnob = new Knob(KnobType::Bright26, this); gainKnob->setModel( &controls->m_gainModel ); gainKnob->setLabel( tr( "GAIN" ) ); gainKnob->setHintText( tr( "Gain:" ) , "" ); - auto ratioKnob = new Knob(knobBright_26, this); + auto ratioKnob = new Knob(KnobType::Bright26, this); ratioKnob->setModel( &controls->m_ratioModel ); ratioKnob->setLabel( tr( "RATIO" ) ); ratioKnob->setHintText( tr( "Ratio:" ) , "" ); diff --git a/plugins/BitInvader/BitInvader.cpp b/plugins/BitInvader/BitInvader.cpp index d89383560..98ef1e97c 100644 --- a/plugins/BitInvader/BitInvader.cpp +++ b/plugins/BitInvader/BitInvader.cpp @@ -60,7 +60,7 @@ Plugin::Descriptor PLUGIN_EXPORT bitinvader_plugin_descriptor = "Customizable wavetable synthesizer" ), "Andreas Brandmaier ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -345,11 +345,11 @@ BitInvaderView::BitInvaderView( Instrument * _instrument, "artwork" ) ); setPalette( pal ); - m_sampleLengthKnob = new Knob( knobDark_28, this ); + m_sampleLengthKnob = new Knob( KnobType::Dark28, this ); m_sampleLengthKnob->move( 6, 201 ); m_sampleLengthKnob->setHintText( tr( "Sample length" ), "" ); - m_graph = new Graph( this, Graph::NearestStyle, 204, 134 ); + m_graph = new Graph( this, Graph::Style::Nearest, 204, 134 ); m_graph->move(23,59); // 55,120 - 2px border m_graph->setAutoFillBackground( true ); m_graph->setGraphColor( QColor( 255, 255, 255 ) ); @@ -431,12 +431,12 @@ BitInvaderView::BitInvaderView( Instrument * _instrument, m_interpolationToggle = new LedCheckBox( "Interpolation", this, - tr( "Interpolation" ), LedCheckBox::Yellow ); + tr( "Interpolation" ), LedCheckBox::LedColor::Yellow ); m_interpolationToggle->move( 131, 221 ); m_normalizeToggle = new LedCheckBox( "Normalize", this, - tr( "Normalize" ), LedCheckBox::Green ); + tr( "Normalize" ), LedCheckBox::LedColor::Green ); m_normalizeToggle->move( 131, 236 ); @@ -556,7 +556,7 @@ void BitInvaderView::smoothClicked() void BitInvaderView::interpolationToggled( bool value ) { - m_graph->setGraphStyle( value ? Graph::LinearStyle : Graph::NearestStyle); + m_graph->setGraphStyle( value ? Graph::Style::Linear : Graph::Style::Nearest); Engine::getSong()->setModified(); } diff --git a/plugins/Bitcrush/Bitcrush.cpp b/plugins/Bitcrush/Bitcrush.cpp index 963e970db..8d29186b5 100644 --- a/plugins/Bitcrush/Bitcrush.cpp +++ b/plugins/Bitcrush/Bitcrush.cpp @@ -48,7 +48,7 @@ Plugin::Descriptor PLUGIN_EXPORT bitcrush_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "An oversampling bitcrusher" ), "Vesa Kivimäki ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader( "logo" ), nullptr, nullptr, diff --git a/plugins/Bitcrush/BitcrushControlDialog.cpp b/plugins/Bitcrush/BitcrushControlDialog.cpp index f3dc85470..64c9b6361 100644 --- a/plugins/Bitcrush/BitcrushControlDialog.cpp +++ b/plugins/Bitcrush/BitcrushControlDialog.cpp @@ -53,13 +53,13 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) : outLabel->move( 139, 15 ); // input knobs - auto inGain = new Knob(knobBright_26, this); + auto inGain = new Knob(KnobType::Bright26, this); inGain->move( 16, 32 ); inGain->setModel( & controls->m_inGain ); inGain->setLabel( tr( "GAIN" ) ); inGain->setHintText( tr( "Input gain:" ) , " dBFS" ); - auto inNoise = new Knob(knobBright_26, this); + auto inNoise = new Knob(KnobType::Bright26, this); inNoise->move( 14, 76 ); inNoise->setModel( & controls->m_inNoise ); inNoise->setLabel( tr( "NOISE" ) ); @@ -67,13 +67,13 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) : // output knobs - auto outGain = new Knob(knobBright_26, this); + auto outGain = new Knob(KnobType::Bright26, this); outGain->move( 138, 32 ); outGain->setModel( & controls->m_outGain ); outGain->setLabel( tr( "GAIN" ) ); outGain->setHintText( tr( "Output gain:" ) , " dBFS" ); - auto outClip = new Knob(knobBright_26, this); + auto outClip = new Knob(KnobType::Bright26, this); outClip->move( 138, 76 ); outClip->setModel( & controls->m_outClip ); outClip->setLabel( tr( "CLIP" ) ); @@ -82,25 +82,25 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) : // leds - auto rateEnabled = new LedCheckBox("", this, tr("Rate enabled"), LedCheckBox::Green); + auto rateEnabled = new LedCheckBox("", this, tr("Rate enabled"), LedCheckBox::LedColor::Green); rateEnabled->move( 64, 14 ); rateEnabled->setModel( & controls->m_rateEnabled ); rateEnabled->setToolTip(tr("Enable sample-rate crushing")); - auto depthEnabled = new LedCheckBox("", this, tr("Depth enabled"), LedCheckBox::Green); + auto depthEnabled = new LedCheckBox("", this, tr("Depth enabled"), LedCheckBox::LedColor::Green); depthEnabled->move( 101, 14 ); depthEnabled->setModel( & controls->m_depthEnabled ); depthEnabled->setToolTip(tr("Enable bit-depth crushing")); // rate crushing knobs - auto rate = new Knob(knobBright_26, this); + auto rate = new Knob(KnobType::Bright26, this); rate->move( 59, 32 ); rate->setModel( & controls->m_rate ); rate->setLabel( tr( "FREQ" ) ); rate->setHintText( tr( "Sample rate:" ) , " Hz" ); - auto stereoDiff = new Knob(knobBright_26, this); + auto stereoDiff = new Knob(KnobType::Bright26, this); stereoDiff->move( 72, 76 ); stereoDiff->setModel( & controls->m_stereoDiff ); stereoDiff->setLabel( tr( "STEREO" ) ); @@ -108,7 +108,7 @@ BitcrushControlDialog::BitcrushControlDialog( BitcrushControls * controls ) : // depth crushing knob - auto levels = new Knob(knobBright_26, this); + auto levels = new Knob(KnobType::Bright26, this); levels->move( 92, 32 ); levels->setModel( & controls->m_levels ); levels->setLabel( tr( "QUANT" ) ); diff --git a/plugins/CarlaBase/Carla.cpp b/plugins/CarlaBase/Carla.cpp index ef14b7aa4..faff94b57 100644 --- a/plugins/CarlaBase/Carla.cpp +++ b/plugins/CarlaBase/Carla.cpp @@ -220,7 +220,7 @@ CarlaInstrument::CarlaInstrument(InstrumentTrack* const instrumentTrack, const D CarlaInstrument::~CarlaInstrument() { - Engine::audioEngine()->removePlayHandlesOfTypes(instrumentTrack(), PlayHandle::TypeNotePlayHandle | PlayHandle::TypeInstrumentPlayHandle); + Engine::audioEngine()->removePlayHandlesOfTypes(instrumentTrack(), PlayHandle::Type::NotePlayHandle | PlayHandle::Type::InstrumentPlayHandle); if (fHost.resourceDir != nullptr) { @@ -345,7 +345,7 @@ intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opco Instrument::Flags CarlaInstrument::flags() const { - return IsSingleStreamed|IsMidiBased|IsNotBendable; + return Flag::IsSingleStreamed | Flag::IsMidiBased | Flag::IsNotBendable; } QString CarlaInstrument::nodeName() const @@ -1015,7 +1015,7 @@ void CarlaParamsView::refreshKnobs() for (uint32_t i=0; i < m_carlaInstrument->m_paramModels.count(); ++i) { bool enabled = m_carlaInstrument->m_paramModels[i]->enabled(); - m_knobs.push_back(new Knob(knobDark_28, m_inputScrollAreaWidgetContent)); + m_knobs.push_back(new Knob(KnobType::Dark28, m_inputScrollAreaWidgetContent)); QString name = (*m_carlaInstrument->m_paramModels[i]).displayName(); m_knobs[i]->setHintText(name, ""); m_knobs[i]->setLabel(name); diff --git a/plugins/CarlaPatchbay/CarlaPatchbay.cpp b/plugins/CarlaPatchbay/CarlaPatchbay.cpp index e440d4e3f..ae22e30d1 100644 --- a/plugins/CarlaPatchbay/CarlaPatchbay.cpp +++ b/plugins/CarlaPatchbay/CarlaPatchbay.cpp @@ -43,7 +43,7 @@ Plugin::Descriptor PLUGIN_EXPORT carlapatchbay_plugin_descriptor = "Carla Patchbay Instrument" ), "falkTX ", CARLA_VERSION_HEX, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, diff --git a/plugins/CarlaRack/CarlaRack.cpp b/plugins/CarlaRack/CarlaRack.cpp index 5360baf96..8be5e40b3 100644 --- a/plugins/CarlaRack/CarlaRack.cpp +++ b/plugins/CarlaRack/CarlaRack.cpp @@ -43,7 +43,7 @@ Plugin::Descriptor PLUGIN_EXPORT carlarack_plugin_descriptor = "Carla Rack Instrument" ), "falkTX ", CARLA_VERSION_HEX, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, diff --git a/plugins/Compressor/Compressor.cpp b/plugins/Compressor/Compressor.cpp index 92123ffd9..3c5ad6157 100755 --- a/plugins/Compressor/Compressor.cpp +++ b/plugins/Compressor/Compressor.cpp @@ -43,7 +43,7 @@ Plugin::Descriptor PLUGIN_EXPORT compressor_plugin_descriptor = QT_TRANSLATE_NOOP("PluginBrowser", "A dynamic range compressor."), "Lost Robot ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, @@ -442,28 +442,28 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames) m_gainResult[i] = qMax(m_rangeVal, m_gainResult[i]); } - switch (stereoLink) + switch (static_cast(stereoLink)) { - case Unlinked: + case StereoLinkMode::Unlinked: { break; } - case Maximum: + case StereoLinkMode::Maximum: { m_gainResult[0] = m_gainResult[1] = qMin(m_gainResult[0], m_gainResult[1]); break; } - case Average: + case StereoLinkMode::Average: { m_gainResult[0] = m_gainResult[1] = (m_gainResult[0] + m_gainResult[1]) * 0.5f; break; } - case Minimum: + case StereoLinkMode::Minimum: { m_gainResult[0] = m_gainResult[1] = qMax(m_gainResult[0], m_gainResult[1]); break; } - case Blend: + case StereoLinkMode::Blend: { if (blend > 0)// 0 is unlinked { diff --git a/plugins/Compressor/Compressor.h b/plugins/Compressor/Compressor.h index 121e0b88f..da6ab52bc 100755 --- a/plugins/Compressor/Compressor.h +++ b/plugins/Compressor/Compressor.h @@ -79,7 +79,7 @@ private: inline int realmod(int k, int n); inline float realfmod(float k, float n); - enum StereoLinkModes { Unlinked, Maximum, Average, Minimum, Blend }; + enum class StereoLinkMode { Unlinked, Maximum, Average, Minimum, Blend }; std::vector m_preLookaheadBuf[2]; int m_preLookaheadBufLoc[2] = {0}; diff --git a/plugins/Compressor/CompressorControlDialog.cpp b/plugins/Compressor/CompressorControlDialog.cpp index 8c6f61bec..114980a7d 100755 --- a/plugins/Compressor/CompressorControlDialog.cpp +++ b/plugins/Compressor/CompressorControlDialog.cpp @@ -95,92 +95,92 @@ CompressorControlDialog::CompressorControlDialog(CompressorControls* controls) : m_ratioEnabledLabel->setPixmap(PLUGIN_NAME::getIconPixmap("knob_enabled_large")); m_ratioEnabledLabel->setAttribute(Qt::WA_TransparentForMouseEvents); - m_thresholdKnob = new Knob(knobStyled, this); + m_thresholdKnob = new Knob(KnobType::Styled, this); makeLargeKnob(m_thresholdKnob, tr("Threshold:") , " dBFS"); m_thresholdKnob->setModel(&controls->m_thresholdModel); m_thresholdKnob->setToolTip(tr("Volume at which the compression begins to take place")); - m_ratioKnob = new Knob(knobStyled, this); + m_ratioKnob = new Knob(KnobType::Styled, this); makeLargeKnob(m_ratioKnob, tr("Ratio:") , ":1"); m_ratioKnob->setModel(&controls->m_ratioModel); m_ratioKnob->setToolTip(tr("How far the compressor must turn the volume down after crossing the threshold")); - m_attackKnob = new Knob(knobStyled, this); + m_attackKnob = new Knob(KnobType::Styled, this); makeLargeKnob(m_attackKnob, tr("Attack:") , " ms"); m_attackKnob->setModel(&controls->m_attackModel); m_attackKnob->setToolTip(tr("Speed at which the compressor starts to compress the audio")); - m_releaseKnob = new Knob(knobStyled, this); + m_releaseKnob = new Knob(KnobType::Styled, this); makeLargeKnob(m_releaseKnob, tr("Release:") , " ms"); m_releaseKnob->setModel(&controls->m_releaseModel); m_releaseKnob->setToolTip(tr("Speed at which the compressor ceases to compress the audio")); - m_kneeKnob = new Knob(knobStyled, this); + m_kneeKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_kneeKnob, tr("Knee:") , " dB"); m_kneeKnob->setModel(&controls->m_kneeModel); m_kneeKnob->setToolTip(tr("Smooth out the gain reduction curve around the threshold")); - m_rangeKnob = new Knob(knobStyled, this); + m_rangeKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_rangeKnob, tr("Range:") , " dBFS"); m_rangeKnob->setModel(&controls->m_rangeModel); m_rangeKnob->setToolTip(tr("Maximum gain reduction")); - m_lookaheadLengthKnob = new Knob(knobStyled, this); + m_lookaheadLengthKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_lookaheadLengthKnob, tr("Lookahead Length:") , " ms"); m_lookaheadLengthKnob->setModel(&controls->m_lookaheadLengthModel); m_lookaheadLengthKnob->setToolTip(tr("How long the compressor has to react to the sidechain signal ahead of time")); - m_holdKnob = new Knob(knobStyled, this); + m_holdKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_holdKnob, tr("Hold:") , " ms"); m_holdKnob->setModel(&controls->m_holdModel); m_holdKnob->setToolTip(tr("Delay between attack and release stages")); - m_rmsKnob = new Knob(knobStyled, this); + m_rmsKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_rmsKnob, tr("RMS Size:") , ""); m_rmsKnob->setModel(&controls->m_rmsModel); m_rmsKnob->setToolTip(tr("Size of the RMS buffer")); - m_inBalanceKnob = new Knob(knobStyled, this); + m_inBalanceKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_inBalanceKnob, tr("Input Balance:") , ""); m_inBalanceKnob->setModel(&controls->m_inBalanceModel); m_inBalanceKnob->setToolTip(tr("Bias the input audio to the left/right or mid/side")); - m_outBalanceKnob = new Knob(knobStyled, this); + m_outBalanceKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_outBalanceKnob, tr("Output Balance:") , ""); m_outBalanceKnob->setModel(&controls->m_outBalanceModel); m_outBalanceKnob->setToolTip(tr("Bias the output audio to the left/right or mid/side")); - m_stereoBalanceKnob = new Knob(knobStyled, this); + m_stereoBalanceKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_stereoBalanceKnob, tr("Stereo Balance:") , ""); m_stereoBalanceKnob->setModel(&controls->m_stereoBalanceModel); m_stereoBalanceKnob->setToolTip(tr("Bias the sidechain signal to the left/right or mid/side")); - m_blendKnob = new Knob(knobStyled, this); + m_blendKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_blendKnob, tr("Stereo Link Blend:") , ""); m_blendKnob->setModel(&controls->m_blendModel); m_blendKnob->setToolTip(tr("Blend between unlinked/maximum/average/minimum stereo linking modes")); - m_tiltKnob = new Knob(knobStyled, this); + m_tiltKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_tiltKnob, tr("Tilt Gain:") , " dB"); m_tiltKnob->setModel(&controls->m_tiltModel); m_tiltKnob->setToolTip(tr("Bias the sidechain signal to the low or high frequencies. -6 db is lowpass, 6 db is highpass.")); - m_tiltFreqKnob = new Knob(knobStyled, this); + m_tiltFreqKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_tiltFreqKnob, tr("Tilt Frequency:") , " Hz"); m_tiltFreqKnob->setModel(&controls->m_tiltFreqModel); m_tiltFreqKnob->setToolTip(tr("Center frequency of sidechain tilt filter")); - m_mixKnob = new Knob(knobStyled, this); + m_mixKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_mixKnob, tr("Mix:") , "%"); m_mixKnob->setModel(&controls->m_mixModel); m_mixKnob->setToolTip(tr("Balance between wet and dry signals")); - m_autoAttackKnob = new Knob(knobStyled, this); + m_autoAttackKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_autoAttackKnob, tr("Auto Attack:") , "%"); m_autoAttackKnob->setModel(&controls->m_autoAttackModel); m_autoAttackKnob->setToolTip(tr("Automatically control attack value depending on crest factor")); - m_autoReleaseKnob = new Knob(knobStyled, this); + m_autoReleaseKnob = new Knob(KnobType::Styled, this); makeSmallKnob(m_autoReleaseKnob, tr("Auto Release:") , "%"); m_autoReleaseKnob->setModel(&controls->m_autoReleaseModel); m_autoReleaseKnob->setToolTip(tr("Automatically control release value depending on crest factor")); diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp index 641e06b4e..c4334677c 100644 --- a/plugins/CrossoverEQ/CrossoverEQ.cpp +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -43,7 +43,7 @@ Plugin::Descriptor PLUGIN_EXPORT crossovereq_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "A 4-band Crossover Equalizer" ), "Vesa Kivimäki ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader( "logo" ), nullptr, nullptr, diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp index ba32e9dfc..12b560b23 100644 --- a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp @@ -46,19 +46,19 @@ CrossoverEQControlDialog::CrossoverEQControlDialog( CrossoverEQControls * contro setFixedSize( 167, 178 ); // knobs - auto xover12 = new Knob(knobBright_26, this); + auto xover12 = new Knob(KnobType::Bright26, this); xover12->move( 29, 11 ); xover12->setModel( & controls->m_xover12 ); xover12->setLabel( "1/2" ); xover12->setHintText( tr( "Band 1/2 crossover:" ), " Hz" ); - auto xover23 = new Knob(knobBright_26, this); + auto xover23 = new Knob(KnobType::Bright26, this); xover23->move( 69, 11 ); xover23->setModel( & controls->m_xover23 ); xover23->setLabel( "2/3" ); xover23->setHintText( tr( "Band 2/3 crossover:" ), " Hz" ); - auto xover34 = new Knob(knobBright_26, this); + auto xover34 = new Knob(KnobType::Bright26, this); xover34->move( 109, 11 ); xover34->setModel( & controls->m_xover34 ); xover34->setLabel( "3/4" ); @@ -90,22 +90,22 @@ CrossoverEQControlDialog::CrossoverEQControlDialog( CrossoverEQControls * contro gain4->setHintText( tr( "Band 4 gain:" ), " dBFS" ); // leds - auto mute1 = new LedCheckBox("", this, tr("Band 1 mute"), LedCheckBox::Green); + auto mute1 = new LedCheckBox("", this, tr("Band 1 mute"), LedCheckBox::LedColor::Green); mute1->move( 15, 154 ); mute1->setModel( & controls->m_mute1 ); mute1->setToolTip(tr("Mute band 1")); - auto mute2 = new LedCheckBox("", this, tr("Band 2 mute"), LedCheckBox::Green); + auto mute2 = new LedCheckBox("", this, tr("Band 2 mute"), LedCheckBox::LedColor::Green); mute2->move( 55, 154 ); mute2->setModel( & controls->m_mute2 ); mute2->setToolTip(tr("Mute band 2")); - auto mute3 = new LedCheckBox("", this, tr("Band 3 mute"), LedCheckBox::Green); + auto mute3 = new LedCheckBox("", this, tr("Band 3 mute"), LedCheckBox::LedColor::Green); mute3->move( 95, 154 ); mute3->setModel( & controls->m_mute3 ); mute3->setToolTip(tr("Mute band 3")); - auto mute4 = new LedCheckBox("", this, tr("Band 4 mute"), LedCheckBox::Green); + auto mute4 = new LedCheckBox("", this, tr("Band 4 mute"), LedCheckBox::LedColor::Green); mute4->move( 135, 154 ); mute4->setModel( & controls->m_mute4 ); mute4->setToolTip(tr("Mute band 4")); diff --git a/plugins/Delay/DelayControlsDialog.cpp b/plugins/Delay/DelayControlsDialog.cpp index 9a9dea0d4..065b3d1e4 100644 --- a/plugins/Delay/DelayControlsDialog.cpp +++ b/plugins/Delay/DelayControlsDialog.cpp @@ -44,28 +44,28 @@ DelayControlsDialog::DelayControlsDialog( DelayControls *controls ) : setPalette( pal ); setFixedSize( 300, 208 ); - auto sampleDelayKnob = new TempoSyncKnob(knobBright_26, this); + auto sampleDelayKnob = new TempoSyncKnob(KnobType::Bright26, this); sampleDelayKnob->move( 10,14 ); sampleDelayKnob->setVolumeKnob( false ); sampleDelayKnob->setModel( &controls->m_delayTimeModel ); sampleDelayKnob->setLabel( tr( "DELAY" ) ); sampleDelayKnob->setHintText( tr( "Delay time" ) + " ", " s" ); - auto feedbackKnob = new Knob(knobBright_26, this); + auto feedbackKnob = new Knob(KnobType::Bright26, this); feedbackKnob->move( 11, 58 ); feedbackKnob->setVolumeKnob( true) ; feedbackKnob->setModel( &controls->m_feedbackModel); feedbackKnob->setLabel( tr( "FDBK" ) ); feedbackKnob->setHintText( tr ( "Feedback amount" ) + " " , "" ); - auto lfoFreqKnob = new TempoSyncKnob(knobBright_26, this); + auto lfoFreqKnob = new TempoSyncKnob(KnobType::Bright26, this); lfoFreqKnob->move( 11, 119 ); lfoFreqKnob->setVolumeKnob( false ); lfoFreqKnob->setModel( &controls->m_lfoTimeModel ); lfoFreqKnob->setLabel( tr( "RATE" ) ); lfoFreqKnob->setHintText( tr ( "LFO frequency") + " ", " s" ); - auto lfoAmtKnob = new TempoSyncKnob(knobBright_26, this); + auto lfoAmtKnob = new TempoSyncKnob(KnobType::Bright26, this); lfoAmtKnob->move( 11, 159 ); lfoAmtKnob->setVolumeKnob( false ); lfoAmtKnob->setModel( &controls->m_lfoAmountModel ); diff --git a/plugins/Delay/DelayEffect.cpp b/plugins/Delay/DelayEffect.cpp index 6db2f38e3..05204f355 100644 --- a/plugins/Delay/DelayEffect.cpp +++ b/plugins/Delay/DelayEffect.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT delay_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "A native delay plugin" ), "Dave French ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/Dispersion/Dispersion.cpp b/plugins/Dispersion/Dispersion.cpp index 9b98877e5..fb28e1f47 100644 --- a/plugins/Dispersion/Dispersion.cpp +++ b/plugins/Dispersion/Dispersion.cpp @@ -40,7 +40,7 @@ Plugin::Descriptor PLUGIN_EXPORT dispersion_plugin_descriptor = QT_TRANSLATE_NOOP("PluginBrowser", "An all-pass filter allowing for extremely high orders."), "Lost Robot ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr diff --git a/plugins/Dispersion/DispersionControlDialog.cpp b/plugins/Dispersion/DispersionControlDialog.cpp index b9f04baa2..2879e7613 100644 --- a/plugins/Dispersion/DispersionControlDialog.cpp +++ b/plugins/Dispersion/DispersionControlDialog.cpp @@ -51,19 +51,19 @@ DispersionControlDialog::DispersionControlDialog(DispersionControls* controls) : m_amountBox->setLabel(tr("AMOUNT")); m_amountBox->setToolTip(tr("Number of all-pass filters")); - Knob * freqKnob = new Knob(knobBright_26, this); + Knob * freqKnob = new Knob(KnobType::Bright26, this); freqKnob->move(59, 8); freqKnob->setModel(&controls->m_freqModel); freqKnob->setLabel(tr("FREQ")); freqKnob->setHintText(tr("Frequency:") , " Hz"); - Knob * resoKnob = new Knob(knobBright_26, this); + Knob * resoKnob = new Knob(KnobType::Bright26, this); resoKnob->move(99, 8); resoKnob->setModel(&controls->m_resoModel); resoKnob->setLabel(tr("RESO")); resoKnob->setHintText(tr("Resonance:") , " octaves"); - Knob * feedbackKnob = new Knob(knobBright_26, this); + Knob * feedbackKnob = new Knob(KnobType::Bright26, this); feedbackKnob->move(139, 8); feedbackKnob->setModel(&controls->m_feedbackModel); feedbackKnob->setLabel(tr("FEED")); diff --git a/plugins/DualFilter/DualFilter.cpp b/plugins/DualFilter/DualFilter.cpp index e510109e9..4e66db988 100644 --- a/plugins/DualFilter/DualFilter.cpp +++ b/plugins/DualFilter/DualFilter.cpp @@ -43,7 +43,7 @@ Plugin::Descriptor PLUGIN_EXPORT dualfilter_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "A Dual filter plugin" ), "Vesa Kivimäki ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -90,12 +90,12 @@ bool DualFilterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames if( m_dfControls.m_filter1Model.isValueChanged() || m_filter1changed ) { - m_filter1->setFilterType( m_dfControls.m_filter1Model.value() ); + m_filter1->setFilterType( static_cast::FilterType>(m_dfControls.m_filter1Model.value()) ); m_filter1changed = true; } if( m_dfControls.m_filter2Model.isValueChanged() || m_filter2changed ) { - m_filter2->setFilterType( m_dfControls.m_filter2Model.value() ); + m_filter2->setFilterType( static_cast::FilterType>(m_dfControls.m_filter2Model.value()) ); m_filter2changed = true; } diff --git a/plugins/DualFilter/DualFilterControlDialog.cpp b/plugins/DualFilter/DualFilterControlDialog.cpp index 81a835901..d316e3372 100644 --- a/plugins/DualFilter/DualFilterControlDialog.cpp +++ b/plugins/DualFilter/DualFilterControlDialog.cpp @@ -36,7 +36,7 @@ namespace lmms::gui #define makeknob( name, x, y, model, label, hint, unit ) \ - Knob * name = new Knob( knobBright_26, this); \ + Knob * name = new Knob( KnobType::Bright26, this); \ (name) -> move( x, y ); \ (name) ->setModel( &controls-> model ); \ (name) ->setLabel( label ); \ @@ -64,8 +64,8 @@ DualFilterControlDialog::DualFilterControlDialog( DualFilterControls* controls ) gain1Knob-> setVolumeKnob( true ); gain2Knob-> setVolumeKnob( true ); - auto enabled1Toggle = new LedCheckBox("", this, tr("Filter 1 enabled"), LedCheckBox::Green); - auto enabled2Toggle = new LedCheckBox("", this, tr("Filter 2 enabled"), LedCheckBox::Green); + auto enabled1Toggle = new LedCheckBox("", this, tr("Filter 1 enabled"), LedCheckBox::LedColor::Green); + auto enabled2Toggle = new LedCheckBox("", this, tr("Filter 2 enabled"), LedCheckBox::LedColor::Green); enabled1Toggle -> move( 12, 11 ); enabled1Toggle -> setModel( &controls -> m_enabled1Model ); diff --git a/plugins/DynamicsProcessor/DynamicsProcessor.cpp b/plugins/DynamicsProcessor/DynamicsProcessor.cpp index 54f1f0c50..6bdf41eee 100644 --- a/plugins/DynamicsProcessor/DynamicsProcessor.cpp +++ b/plugins/DynamicsProcessor/DynamicsProcessor.cpp @@ -47,7 +47,7 @@ Plugin::Descriptor PLUGIN_EXPORT dynamicsprocessor_plugin_descriptor = "plugin for processing dynamics in a flexible way" ), "Vesa Kivimäki ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, @@ -167,19 +167,19 @@ bool DynProcEffect::processAudioBuffer( sampleFrame * _buf, } // account for stereo mode - switch( stereoMode ) + switch( static_cast(stereoMode) ) { - case DynProcControls::SM_Maximum: + case DynProcControls::StereoMode::Maximum: { sm_peak[0] = sm_peak[1] = qMax( m_currentPeak[0], m_currentPeak[1] ); break; } - case DynProcControls::SM_Average: + case DynProcControls::StereoMode::Average: { sm_peak[0] = sm_peak[1] = ( m_currentPeak[0] + m_currentPeak[1] ) * 0.5; break; } - case DynProcControls::SM_Unlinked: + case DynProcControls::StereoMode::Unlinked: { sm_peak[0] = m_currentPeak[0]; sm_peak[1] = m_currentPeak[1]; diff --git a/plugins/DynamicsProcessor/DynamicsProcessorControlDialog.cpp b/plugins/DynamicsProcessor/DynamicsProcessorControlDialog.cpp index 4d8758e03..bd076b946 100644 --- a/plugins/DynamicsProcessor/DynamicsProcessorControlDialog.cpp +++ b/plugins/DynamicsProcessor/DynamicsProcessorControlDialog.cpp @@ -47,7 +47,7 @@ DynProcControlDialog::DynProcControlDialog( setPalette( pal ); setFixedSize( 224, 319 ); - auto waveGraph = new Graph(this, Graph::LinearNonCyclicStyle, 204, 205); + auto waveGraph = new Graph(this, Graph::Style::LinearNonCyclic, 204, 205); waveGraph -> move( 10, 6 ); waveGraph -> setModel( &_controls -> m_wavegraphModel ); waveGraph -> setAutoFillBackground( true ); @@ -58,7 +58,7 @@ DynProcControlDialog::DynProcControlDialog( waveGraph->setGraphColor( QColor( 85, 204, 145 ) ); waveGraph -> setMaximumSize( 204, 205 ); - auto inputKnob = new Knob(knobBright_26, this); + auto inputKnob = new Knob(KnobType::Bright26, this); inputKnob -> setVolumeKnob( true ); inputKnob -> setVolumeRatio( 1.0 ); inputKnob -> move( 26, 223 ); @@ -66,7 +66,7 @@ DynProcControlDialog::DynProcControlDialog( inputKnob->setLabel( tr( "INPUT" ) ); inputKnob->setHintText( tr( "Input gain:" ) , "" ); - auto outputKnob = new Knob(knobBright_26, this); + auto outputKnob = new Knob(KnobType::Bright26, this); outputKnob -> setVolumeKnob( true ); outputKnob -> setVolumeRatio( 1.0 ); outputKnob -> move( 76, 223 ); @@ -74,13 +74,13 @@ DynProcControlDialog::DynProcControlDialog( outputKnob->setLabel( tr( "OUTPUT" ) ); outputKnob->setHintText( tr( "Output gain:" ) , "" ); - auto attackKnob = new Knob(knobBright_26, this); + auto attackKnob = new Knob(KnobType::Bright26, this); attackKnob -> move( 24, 268 ); attackKnob->setModel( &_controls->m_attackModel ); attackKnob->setLabel( tr( "ATTACK" ) ); attackKnob->setHintText( tr( "Peak attack time:" ) , "ms" ); - auto releaseKnob = new Knob(knobBright_26, this); + auto releaseKnob = new Knob(KnobType::Bright26, this); releaseKnob -> move( 74, 268 ); releaseKnob->setModel( &_controls->m_releaseModel ); releaseKnob->setLabel( tr( "RELEASE" ) ); diff --git a/plugins/DynamicsProcessor/DynamicsProcessorControls.h b/plugins/DynamicsProcessor/DynamicsProcessorControls.h index cbe109eaf..8fb66fee3 100644 --- a/plugins/DynamicsProcessor/DynamicsProcessorControls.h +++ b/plugins/DynamicsProcessor/DynamicsProcessorControls.h @@ -41,12 +41,11 @@ class DynProcControls : public EffectControls { Q_OBJECT public: - enum StereoModes + enum class StereoMode { - SM_Maximum, - SM_Average, - SM_Unlinked, - NumStereoModes + Maximum, + Average, + Unlinked }; DynProcControls( DynProcEffect * _eff ); ~DynProcControls() override = default; diff --git a/plugins/Eq/EqControlsDialog.cpp b/plugins/Eq/EqControlsDialog.cpp index 6535b61f3..a26fa0db9 100644 --- a/plugins/Eq/EqControlsDialog.cpp +++ b/plugins/Eq/EqControlsDialog.cpp @@ -106,14 +106,14 @@ EqControlsDialog::EqControlsDialog( EqControls *controls ) : distance = 81; for( int i = 0; i < m_parameterWidget->bandCount() ; i++ ) { - auto resKnob = new Knob(knobBright_26, this); + auto resKnob = new Knob(KnobType::Bright26, this); resKnob->move( distance, 440 ); resKnob->setVolumeKnob(false); resKnob->setModel( m_parameterWidget->getBandModels( i )->res ); if(i > 1 && i < 6) { resKnob->setHintText( tr( "Bandwidth: " ) , tr( " Octave" ) ); } else { resKnob->setHintText( tr( "Resonance : " ) , "" ); } - auto freqKnob = new Knob(knobBright_26, this); + auto freqKnob = new Knob(KnobType::Bright26, this); freqKnob->move( distance, 396 ); freqKnob->setVolumeKnob( false ); freqKnob->setModel( m_parameterWidget->getBandModels( i )->freq ); diff --git a/plugins/Eq/EqCurve.cpp b/plugins/Eq/EqCurve.cpp index b9fa3f519..10213bfa9 100644 --- a/plugins/Eq/EqCurve.cpp +++ b/plugins/Eq/EqCurve.cpp @@ -137,7 +137,7 @@ void EqHandle::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QRectF textRect2 = QRectF ( rectX+1, rectY+1, 80, 30 ); QString freq = QString::number( xPixelToFreq( EqHandle::x(), m_width ) ); QString res; - if ( getType() != para ) + if ( getType() != EqHandleType::Para ) { res = tr( "Reso: ") + QString::number( getResonance() ); } @@ -171,11 +171,11 @@ QPainterPath EqHandle::getCurvePath() float y = m_heigth * 0.5; for ( float x = 0 ; x < m_width; x++ ) { - if ( m_type == highpass ) y = getLowCutCurve( x ); - if ( m_type == lowshelf ) y = getLowShelfCurve( x ); - if ( m_type == para ) y = getPeakCurve( x ); - if ( m_type == highshelf ) y = getHighShelfCurve( x ); - if ( m_type == lowpass ) y = getHighCutCurve( x ); + if ( m_type == EqHandleType::HighPass ) y = getLowCutCurve( x ); + if ( m_type == EqHandleType::LowShelf ) y = getLowShelfCurve( x ); + if ( m_type == EqHandleType::Para ) y = getPeakCurve( x ); + if ( m_type == EqHandleType::HighShelf ) y = getHighShelfCurve( x ); + if ( m_type == EqHandleType::LowPass ) y = getHighCutCurve( x ); if ( x == 0 ) path.moveTo( x, y ); // sets the begin of Path path.lineTo( x, y ); } @@ -410,7 +410,7 @@ int EqHandle::getNum() -void EqHandle::setType( int t ) +void EqHandle::setType( EqHandleType t ) { EqHandle::m_type = t; } @@ -442,7 +442,7 @@ void EqHandle::setMouseHover( bool d ) -int EqHandle::getType() +EqHandleType EqHandle::getType() { return m_type; } @@ -569,7 +569,7 @@ void EqHandle::mouseReleaseEvent( QGraphicsSceneMouseEvent *event ) void EqHandle::wheelEvent( QGraphicsSceneWheelEvent *wevent ) { float highestBandwich; - if( m_type != para ) + if( m_type != EqHandleType::Para ) { highestBandwich = 10; } @@ -631,7 +631,7 @@ QVariant EqHandle::itemChange( QGraphicsItem::GraphicsItemChange change, const Q if( change == ItemPositionChange ) { // pass filter don't move in y direction - if ( m_type == highpass || m_type == lowpass ) + if ( m_type == EqHandleType::HighPass || m_type == EqHandleType::LowPass ) { float newX = value.toPointF().x(); if( newX < 0 ) @@ -714,23 +714,23 @@ void EqCurve::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, { for ( int x = 0; x < m_width ; x=x+1 ) { - if ( m_handle->at( thatHandle )->getType() == highpass ) + if ( m_handle->at( thatHandle )->getType() == EqHandleType::HighPass ) { mainCurve[x]= ( mainCurve[x] + ( m_handle->at( thatHandle )->getLowCutCurve( x ) * ( activeHandles ) ) - ( ( activeHandles * ( m_heigth/2 ) ) - m_heigth ) ); } - if ( m_handle->at(thatHandle)->getType() == lowshelf ) + if ( m_handle->at(thatHandle)->getType() == EqHandleType::LowShelf ) { mainCurve[x]= ( mainCurve[x] + ( m_handle->at( thatHandle )->getLowShelfCurve( x ) * ( activeHandles ) ) - ( ( activeHandles * ( m_heigth/2 ) ) - m_heigth ) ); } - if ( m_handle->at( thatHandle )->getType() == para ) + if ( m_handle->at( thatHandle )->getType() == EqHandleType::Para ) { mainCurve[x]= ( mainCurve[x] + ( m_handle->at( thatHandle )->getPeakCurve( x ) * ( activeHandles ) ) - ( ( activeHandles * ( m_heigth/2 ) ) - m_heigth ) ); } - if ( m_handle->at( thatHandle )->getType() == highshelf ) + if ( m_handle->at( thatHandle )->getType() == EqHandleType::HighShelf ) { mainCurve[x]= ( mainCurve[x] + ( m_handle->at( thatHandle )->getHighShelfCurve( x ) * ( activeHandles ) ) - ( ( activeHandles * ( m_heigth/2 ) ) - m_heigth ) ); } - if ( m_handle->at(thatHandle)->getType() == lowpass ) + if ( m_handle->at(thatHandle)->getType() == EqHandleType::LowPass ) { mainCurve[x]= ( mainCurve[x] + ( m_handle->at( thatHandle )->getHighCutCurve( x ) * ( activeHandles ) ) - ( ( activeHandles * ( m_heigth/2 ) ) - m_heigth ) ); } diff --git a/plugins/Eq/EqCurve.h b/plugins/Eq/EqCurve.h index e01e30841..27236aabb 100644 --- a/plugins/Eq/EqCurve.h +++ b/plugins/Eq/EqCurve.h @@ -32,12 +32,12 @@ namespace lmms::gui { -enum{ - highpass=1, - lowshelf, - para, - highshelf, - lowpass +enum class EqHandleType { + HighPass=1, + LowShelf, + Para, + HighShelf, + LowPass }; @@ -64,8 +64,8 @@ public: float getHighCutCurve( float x ); float getResonance(); int getNum(); - int getType(); - void setType( int t ); + EqHandleType getType(); + void setType( EqHandleType t ); void setResonance( float r ); bool isMouseHover(); void setMouseHover( bool d ); @@ -104,7 +104,8 @@ private: bool m_lp24; bool m_lp48; bool m_mouseHover; - int m_type, m_numb; + EqHandleType m_type; + int m_numb; float m_width, m_heigth; float m_resonance; bool m_mousePressed; diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index eb168a9f9..8a7954144 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -44,7 +44,7 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "A native eq plugin" ), "Dave French ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp index fb60322a1..b48f0f317 100644 --- a/plugins/Eq/EqParameterWidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -164,35 +164,35 @@ void EqParameterWidget::changeHandle( int i ) switch ( i ) { case 0 : - m_handleList->at( i )->setType( highpass ); + m_handleList->at( i )->setType( EqHandleType::HighPass ); m_handleList->at( i )->setPos( x, m_displayHeigth / 2 ); break; case 1: - m_handleList->at( i )->setType( lowshelf ); + m_handleList->at( i )->setType( EqHandleType::LowShelf ); m_handleList->at( i )->setPos( x, y ); break; case 2: - m_handleList->at( i )->setType( para ); + m_handleList->at( i )->setType( EqHandleType::Para ); m_handleList->at( i )->setPos( x, y ); break; case 3: - m_handleList->at( i )->setType( para ); + m_handleList->at( i )->setType( EqHandleType::Para ); m_handleList->at( i )->setPos( x, y ); break; case 4: - m_handleList->at( i )->setType( para ); + m_handleList->at( i )->setType( EqHandleType::Para ); m_handleList->at( i )->setPos( x, y ); break; case 5: - m_handleList->at( i )->setType( para ); + m_handleList->at( i )->setType( EqHandleType::Para ); m_handleList->at( i )->setPos( x, y ); break; case 6: - m_handleList->at( i )->setType( highshelf ); + m_handleList->at( i )->setType( EqHandleType::HighShelf ); m_handleList->at( i )->setPos( x, y ); break; case 7: - m_handleList->at( i )->setType( lowpass ); + m_handleList->at( i )->setType( EqHandleType::LowPass ); m_handleList->at( i )->setPos( QPointF( x, m_displayHeigth / 2 ) ); break; } diff --git a/plugins/Flanger/FlangerControlsDialog.cpp b/plugins/Flanger/FlangerControlsDialog.cpp index f35aabfd4..3ac5dc9c6 100644 --- a/plugins/Flanger/FlangerControlsDialog.cpp +++ b/plugins/Flanger/FlangerControlsDialog.cpp @@ -42,42 +42,42 @@ FlangerControlsDialog::FlangerControlsDialog( FlangerControls *controls ) : setPalette( pal ); setFixedSize( 233, 75 ); - auto delayKnob = new Knob(knobBright_26, this); + auto delayKnob = new Knob(KnobType::Bright26, this); delayKnob->move( 10,10 ); delayKnob->setVolumeKnob( false ); delayKnob->setModel( &controls->m_delayTimeModel ); delayKnob->setLabel( tr( "DELAY" ) ); delayKnob->setHintText( tr( "Delay time:" ) + " ", "s" ); - auto lfoFreqKnob = new TempoSyncKnob(knobBright_26, this); + auto lfoFreqKnob = new TempoSyncKnob(KnobType::Bright26, this); lfoFreqKnob->move( 48,10 ); lfoFreqKnob->setVolumeKnob( false ); lfoFreqKnob->setModel( &controls->m_lfoFrequencyModel ); lfoFreqKnob->setLabel( tr( "RATE" ) ); lfoFreqKnob->setHintText( tr( "Period:" ) , " Sec" ); - auto lfoAmtKnob = new Knob(knobBright_26, this); + auto lfoAmtKnob = new Knob(KnobType::Bright26, this); lfoAmtKnob->move( 85,10 ); lfoAmtKnob->setVolumeKnob( false ); lfoAmtKnob->setModel( &controls->m_lfoAmountModel ); lfoAmtKnob->setLabel( tr( "AMNT" ) ); lfoAmtKnob->setHintText( tr( "Amount:" ) , "" ); - auto lfoPhaseKnob = new Knob(knobBright_26, this); + auto lfoPhaseKnob = new Knob(KnobType::Bright26, this); lfoPhaseKnob->move( 123,10 ); lfoPhaseKnob->setVolumeKnob( false ); lfoPhaseKnob->setModel( &controls->m_lfoPhaseModel ); lfoPhaseKnob->setLabel( tr( "PHASE" ) ); lfoPhaseKnob->setHintText( tr( "Phase:" ) , " degrees" ); - auto feedbackKnob = new Knob(knobBright_26, this); + auto feedbackKnob = new Knob(KnobType::Bright26, this); feedbackKnob->move( 160,10 ); feedbackKnob->setVolumeKnob( true) ; feedbackKnob->setModel( &controls->m_feedbackModel ); feedbackKnob->setLabel( tr( "FDBK" ) ); feedbackKnob->setHintText( tr( "Feedback amount:" ) , "" ); - auto whiteNoiseKnob = new Knob(knobBright_26, this); + auto whiteNoiseKnob = new Knob(KnobType::Bright26, this); whiteNoiseKnob->move( 196,10 ); whiteNoiseKnob->setVolumeKnob( true) ; whiteNoiseKnob->setModel( &controls->m_whiteNoiseAmountModel ); diff --git a/plugins/Flanger/FlangerEffect.cpp b/plugins/Flanger/FlangerEffect.cpp index 9f7200f0e..60b5df67b 100644 --- a/plugins/Flanger/FlangerEffect.cpp +++ b/plugins/Flanger/FlangerEffect.cpp @@ -45,7 +45,7 @@ Plugin::Descriptor PLUGIN_EXPORT flanger_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "A native flanger plugin" ), "Dave French ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index c26cd79c4..6450253ee 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -63,7 +63,7 @@ Plugin::Descriptor PLUGIN_EXPORT freeboy_plugin_descriptor = "Attila Herman " "Csaba Hruska ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, } ; @@ -446,7 +446,7 @@ class FreeBoyKnob : public Knob { public: FreeBoyKnob( QWidget * _parent ) : - Knob( knobStyled, _parent ) + Knob( KnobType::Styled, _parent ) { setFixedSize( 30, 30 ); setCenterPointX( 15.0 ); @@ -677,7 +677,7 @@ FreeBoyInstrumentView::FreeBoyInstrumentView( Instrument * _instrument, m_graph = new Graph( this ); - m_graph->setGraphStyle( Graph::NearestStyle ); + m_graph->setGraphStyle( Graph::Style::Nearest ); m_graph->setGraphColor( QColor(0x4E, 0x83, 0x2B) ); m_graph->move( 37, 199 ); m_graph->resize(208, 47); diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 42d64cf07..c2e155a20 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -69,7 +69,7 @@ Plugin::Descriptor PLUGIN_EXPORT gigplayer_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "Player for GIG files" ), "Garrett Wilson ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), "gig", nullptr, @@ -108,8 +108,8 @@ GigInstrument::GigInstrument( InstrumentTrack * _instrument_track ) : GigInstrument::~GigInstrument() { Engine::audioEngine()->removePlayHandlesOfTypes( instrumentTrack(), - PlayHandle::TypeNotePlayHandle - | PlayHandle::TypeInstrumentPlayHandle ); + PlayHandle::Type::NotePlayHandle + | PlayHandle::Type::InstrumentPlayHandle ); freeInstance(); } @@ -341,16 +341,16 @@ void GigInstrument::play( sampleFrame * _working_buffer ) for( QList::iterator it = m_notes.begin(); it != m_notes.end(); ++it ) { // Process notes in the KeyUp state, adding release samples if desired - if( it->state == KeyUp ) + if( it->state == GigState::KeyUp ) { // If there are no samples, we're done if( it->samples.empty() ) { - it->state = Completed; + it->state = GigState::Completed; } else { - it->state = PlayingKeyUp; + it->state = GigState::PlayingKeyUp; // Notify each sample that the key has been released for (auto& sample : it->samples) @@ -366,9 +366,9 @@ void GigInstrument::play( sampleFrame * _working_buffer ) } } // Process notes in the KeyDown state, adding samples for the notes - else if( it->state == KeyDown ) + else if( it->state == GigState::KeyDown ) { - it->state = PlayingKeyDown; + it->state = GigState::PlayingKeyDown; addSamples( *it, false ); } @@ -393,7 +393,7 @@ void GigInstrument::play( sampleFrame * _working_buffer ) } // Delete ended notes (either in the completed state or all the samples ended) - if( it->state == Completed || it->samples.empty() ) + if( it->state == GigState::Completed || it->samples.empty() ) { it = m_notes.erase( it ); @@ -408,7 +408,7 @@ void GigInstrument::play( sampleFrame * _working_buffer ) for (auto& note : m_notes) { // Only process the notes if we're in a playing state - if (!(note.state == PlayingKeyDown || note.state == PlayingKeyUp )) + if (!(note.state == GigState::PlayingKeyDown || note.state == GigState::PlayingKeyUp )) { continue; } @@ -680,9 +680,9 @@ void GigInstrument::deleteNotePluginData( NotePlayHandle * _n ) for (auto& note : m_notes) { // Find the note by matching pointers to the plugin data - if (note.handle == pluginData && (note.state == KeyDown || note.state == PlayingKeyDown)) + if (note.handle == pluginData && (note.state == GigState::KeyDown || note.state == GigState::PlayingKeyDown)) { - note.state = KeyUp; + note.state = GigState::KeyUp; } } @@ -906,7 +906,7 @@ class gigKnob : public Knob { public: gigKnob( QWidget * _parent ) : - Knob( knobBright_26, _parent ) + Knob( KnobType::Bright26, _parent ) { setFixedSize( 31, 38 ); } diff --git a/plugins/GigPlayer/GigPlayer.h b/plugins/GigPlayer/GigPlayer.h index 20058424b..e5039f109 100644 --- a/plugins/GigPlayer/GigPlayer.h +++ b/plugins/GigPlayer/GigPlayer.h @@ -187,7 +187,7 @@ public: // What portion of a note are we in? -enum GigState +enum class GigState { // We just pressed the key KeyDown, @@ -224,7 +224,7 @@ public: GigNote( int midiNote, int velocity, float frequency, GIGPluginData * handle ) : midiNote( midiNote ), velocity( velocity ), - release( false ), isRelease( false ), state( KeyDown ), + release( false ), isRelease( false ), state( GigState::KeyDown ), frequency( frequency ), handle( handle ) { } @@ -268,7 +268,7 @@ public: Flags flags() const override { - return IsSingleStreamed|IsNotBendable; + return Flag::IsSingleStreamed | Flag::IsNotBendable; } gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/HydrogenImport/HydrogenImport.cpp b/plugins/HydrogenImport/HydrogenImport.cpp index d8f76071a..144a2f5e7 100644 --- a/plugins/HydrogenImport/HydrogenImport.cpp +++ b/plugins/HydrogenImport/HydrogenImport.cpp @@ -30,7 +30,7 @@ Plugin::Descriptor PLUGIN_EXPORT hydrogenimport_plugin_descriptor = "Filter for importing Hydrogen files into LMMS" ), "frank mather", 0x0100, - Plugin::ImportFilter, + Plugin::Type::ImportFilter, nullptr, nullptr, nullptr, @@ -42,7 +42,7 @@ QString filename; class NoteKey { public: - enum Key { + enum class Key { C = 0, Cs, D, @@ -59,7 +59,7 @@ public: static int stringToNoteKey( const QString& str ) { - int m_key = NoteKey::C; + auto m_key = Key::C; QString sKey = str.left( str.length() - 1 ); @@ -74,54 +74,54 @@ public: if ( sKey == "C" ) { - m_key = NoteKey::C; + m_key = Key::C; } else if ( sKey == "Cs" ) { - m_key = NoteKey::Cs; + m_key = Key::Cs; } else if ( sKey == "D" ) { - m_key = NoteKey::D; + m_key = Key::D; } else if ( sKey == "Ef" ) { - m_key = NoteKey::Ef; + m_key = Key::Ef; } else if ( sKey == "E" ) { - m_key = NoteKey::E; + m_key = Key::E; } else if ( sKey == "F" ) { - m_key = NoteKey::F; + m_key = Key::F; } else if ( sKey == "Fs" ) { - m_key = NoteKey::Fs; + m_key = Key::Fs; } else if ( sKey == "G" ) { - m_key = NoteKey::G; + m_key = Key::G; } else if ( sKey == "Af" ) { - m_key = NoteKey::Af; + m_key = Key::Af; } else if ( sKey == "A" ) { - m_key = NoteKey::A; + m_key = Key::A; } else if ( sKey == "Bf" ) { - m_key = NoteKey::Bf; + m_key = Key::Bf; } else if ( sKey == "B" ) { - m_key = NoteKey::B; + m_key = Key::B; } // Hydrogen records MIDI notes from C-1 to B5, and exports them as a number ranging from -3 to 3 - return m_key + ((nOctave + 3) * 12); + return static_cast(m_key) + ((nOctave + 3) * 12); } }; @@ -218,7 +218,7 @@ bool HydrogenImport::readSong() if ( nLayer == 0 ) { drum_track[sId] = static_cast( - Track::create(Track::InstrumentTrack, Engine::patternStore()) + Track::create(Track::Type::Instrument, Engine::patternStore()) ); drum_track[sId]->volumeModel()->setValue( fVolume * 100 ); drum_track[sId]->panningModel()->setValue( ( fPan_R - fPan_L ) * 100 ); diff --git a/plugins/Kicker/Kicker.cpp b/plugins/Kicker/Kicker.cpp index 29f7dc8f1..ef1d623c1 100644 --- a/plugins/Kicker/Kicker.cpp +++ b/plugins/Kicker/Kicker.cpp @@ -54,7 +54,7 @@ Plugin::Descriptor PLUGIN_EXPORT kicker_plugin_descriptor = "Versatile drum synthesizer" ), "Tobias Doerffel ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -226,7 +226,7 @@ class KickerKnob : public Knob { public: KickerKnob( QWidget * _parent ) : - Knob( knobStyled, _parent ) + Knob( KnobType::Styled, _parent ) { setFixedSize( 29, 29 ); setObjectName( "smallKnob" ); @@ -238,7 +238,7 @@ class KickerEnvKnob : public TempoSyncKnob { public: KickerEnvKnob( QWidget * _parent ) : - TempoSyncKnob( knobStyled, _parent ) + TempoSyncKnob( KnobType::Styled, _parent ) { setFixedSize( 29, 29 ); setObjectName( "smallKnob" ); @@ -250,7 +250,7 @@ class KickerLargeKnob : public Knob { public: KickerLargeKnob( QWidget * _parent ) : - Knob( knobStyled, _parent ) + Knob( KnobType::Styled, _parent ) { setFixedSize( 34, 34 ); setObjectName( "largeKnob" ); @@ -315,10 +315,10 @@ KickerInstrumentView::KickerInstrumentView( Instrument * _instrument, m_distEndKnob->setHintText( tr( "End distortion:" ), "" ); m_distEndKnob->move( COL5, ROW2 ); - m_startNoteToggle = new LedCheckBox( "", this, "", LedCheckBox::Green ); + m_startNoteToggle = new LedCheckBox( "", this, "", LedCheckBox::LedColor::Green ); m_startNoteToggle->move( COL1 + 8, LED_ROW ); - m_endNoteToggle = new LedCheckBox( "", this, "", LedCheckBox::Green ); + m_endNoteToggle = new LedCheckBox( "", this, "", LedCheckBox::LedColor::Green ); m_endNoteToggle->move( END_COL + 8, LED_ROW ); setAutoFillBackground( true ); diff --git a/plugins/Kicker/Kicker.h b/plugins/Kicker/Kicker.h index cef732adb..b5d065598 100644 --- a/plugins/Kicker/Kicker.h +++ b/plugins/Kicker/Kicker.h @@ -66,7 +66,7 @@ public: Flags flags() const override { - return IsNotBendable; + return Flag::IsNotBendable; } f_cnt_t desiredReleaseFrames() const override diff --git a/plugins/LadspaBrowser/LadspaBrowser.cpp b/plugins/LadspaBrowser/LadspaBrowser.cpp index 09f83ab4e..31be64056 100644 --- a/plugins/LadspaBrowser/LadspaBrowser.cpp +++ b/plugins/LadspaBrowser/LadspaBrowser.cpp @@ -56,7 +56,7 @@ Plugin::Descriptor PLUGIN_EXPORT ladspabrowser_plugin_descriptor = "List installed LADSPA plugins" ), "Danny McRae ", 0x0100, - Plugin::Tool, + Plugin::Type::Tool, new PluginPixmapLoader("logo"), nullptr, nullptr, @@ -107,12 +107,12 @@ LadspaBrowserView::LadspaBrowserView( ToolPlugin * _tool ) : auto ws = new QWidget(this); ws->setFixedSize( 500, 480 ); - QWidget * available = createTab( ws, tr( "Available Effects" ), VALID ); + QWidget * available = createTab( ws, tr( "Available Effects" ), LadspaPluginType::Valid ); QWidget * unavailable = createTab( ws, tr( "Unavailable Effects" ), - INVALID ); - QWidget * instruments = createTab( ws, tr( "Instruments" ), SOURCE ); - QWidget * analysis = createTab( ws, tr( "Analysis Tools" ), SINK ); - QWidget * other = createTab( ws, tr( "Don't know" ), OTHER ); + LadspaPluginType::Invalid ); + QWidget * instruments = createTab( ws, tr( "Instruments" ), LadspaPluginType::Source ); + QWidget * analysis = createTab( ws, tr( "Analysis Tools" ), LadspaPluginType::Sink ); + QWidget * other = createTab( ws, tr( "Don't know" ), LadspaPluginType::Other ); m_tabBar->addTab( available, tr( "Available Effects" ), diff --git a/plugins/LadspaBrowser/LadspaDescription.cpp b/plugins/LadspaBrowser/LadspaDescription.cpp index 004505d66..fbcd6c25d 100644 --- a/plugins/LadspaBrowser/LadspaDescription.cpp +++ b/plugins/LadspaBrowser/LadspaDescription.cpp @@ -49,22 +49,22 @@ LadspaDescription::LadspaDescription( QWidget * _parent, l_sortable_plugin_t plugins; switch( _type ) { - case SOURCE: + case LadspaPluginType::Source: plugins = manager->getInstruments(); break; - case TRANSFER: + case LadspaPluginType::Transfer: plugins = manager->getValidEffects(); break; - case VALID: + case LadspaPluginType::Valid: plugins = manager->getValidEffects(); break; - case INVALID: + case LadspaPluginType::Invalid: plugins = manager->getInvalidEffects(); break; - case SINK: + case LadspaPluginType::Sink: plugins = manager->getAnalysisTools(); break; - case OTHER: + case LadspaPluginType::Other: plugins = manager->getOthers(); break; default: @@ -75,7 +75,7 @@ LadspaDescription::LadspaDescription( QWidget * _parent, for (const auto& plugin : plugins) { ch_cnt_t audioDeviceChannels = Engine::audioEngine()->audioDev()->channels(); - if (_type != VALID || manager->getDescription(plugin.second)->inputChannels <= audioDeviceChannels) + if (_type != LadspaPluginType::Valid || manager->getDescription(plugin.second)->inputChannels <= audioDeviceChannels) { pluginNames.push_back(plugin.first); m_pluginKeys.push_back(plugin.second); diff --git a/plugins/LadspaEffect/LadspaControlDialog.cpp b/plugins/LadspaEffect/LadspaControlDialog.cpp index 571dbd454..2a5437fb1 100644 --- a/plugins/LadspaEffect/LadspaControlDialog.cpp +++ b/plugins/LadspaEffect/LadspaControlDialog.cpp @@ -86,7 +86,7 @@ void LadspaControlDialog::updateEffectView( LadspaControls * _ctl ) control_list_t & controls = _ctl->m_controls[proc]; int row = 0; int col = 0; - buffer_data_t last_port = NONE; + BufferDataType last_port = BufferDataType::None; QGroupBox * grouper; if( _ctl->m_processors > 1 ) @@ -108,10 +108,10 @@ void LadspaControlDialog::updateEffectView( LadspaControls * _ctl ) { if (control->port()->proc == proc) { - buffer_data_t this_port = control->port()->data_type; - if( last_port != NONE && - ( this_port == TOGGLED || this_port == ENUM ) && - ( last_port != TOGGLED && last_port != ENUM ) ) + BufferDataType this_port = control->port()->data_type; + if( last_port != BufferDataType::None && + ( this_port == BufferDataType::Toggled || this_port == BufferDataType::Enum ) && + ( last_port != BufferDataType::Toggled && last_port != BufferDataType::Enum ) ) { ++row; col = 0; diff --git a/plugins/LadspaEffect/LadspaEffect.cpp b/plugins/LadspaEffect/LadspaEffect.cpp index e5d1c5d69..cc754a829 100644 --- a/plugins/LadspaEffect/LadspaEffect.cpp +++ b/plugins/LadspaEffect/LadspaEffect.cpp @@ -60,10 +60,10 @@ Plugin::Descriptor PLUGIN_EXPORT ladspaeffect_plugin_descriptor = "inside LMMS." ), "Danny McRae ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, - new LadspaSubPluginFeatures( Plugin::Effect ) + new LadspaSubPluginFeatures( Plugin::Type::Effect ) } ; } @@ -106,7 +106,7 @@ LadspaEffect::~LadspaEffect() void LadspaEffect::changeSampleRate() { - DataFile dataFile( DataFile::EffectSettings ); + DataFile dataFile( DataFile::Type::EffectSettings ); m_controls->saveState( dataFile, dataFile.content() ); LadspaControls * controls = m_controls; @@ -163,7 +163,7 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf, port_desc_t * pp = m_ports.at( proc ).at( port ); switch( pp->rate ) { - case CHANNEL_IN: + case BufferRate::ChannelIn: for( fpp_t frame = 0; frame < frames; ++frame ) { @@ -172,7 +172,7 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf, } ++channel; break; - case AUDIO_RATE_INPUT: + case BufferRate::AudioRateInput: { ValueBuffer * vb = pp->control->valueBuffer(); if( vb ) @@ -195,7 +195,7 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf, } break; } - case CONTROL_RATE_INPUT: + case BufferRate::ControlRateInput: if( pp->control == nullptr ) { break; @@ -205,9 +205,9 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf, pp->buffer[0] = pp->value; break; - case CHANNEL_OUT: - case AUDIO_RATE_OUTPUT: - case CONTROL_RATE_OUTPUT: + case BufferRate::ChannelOut: + case BufferRate::AudioRateOutput: + case BufferRate::ControlRateOutput: break; default: break; @@ -234,11 +234,11 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf, port_desc_t * pp = m_ports.at( proc ).at( port ); switch( pp->rate ) { - case CHANNEL_IN: - case AUDIO_RATE_INPUT: - case CONTROL_RATE_INPUT: + case BufferRate::ChannelIn: + case BufferRate::AudioRateInput: + case BufferRate::ControlRateInput: break; - case CHANNEL_OUT: + case BufferRate::ChannelOut: for( fpp_t frame = 0; frame < frames; ++frame ) { @@ -247,8 +247,8 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf, } ++channel; break; - case AUDIO_RATE_OUTPUT: - case CONTROL_RATE_OUTPUT: + case BufferRate::AudioRateOutput: + case BufferRate::ControlRateOutput: break; default: break; @@ -325,7 +325,7 @@ void LadspaEffect::pluginInstantiation() if( p->name.toUpper().contains( "IN" ) && manager->isPortInput( m_key, port ) ) { - p->rate = CHANNEL_IN; + p->rate = BufferRate::ChannelIn; p->buffer = MM_ALLOC( Engine::audioEngine()->framesPerPeriod() ); inbuf[ inputch ] = p->buffer; inputch++; @@ -333,7 +333,7 @@ void LadspaEffect::pluginInstantiation() else if( p->name.toUpper().contains( "OUT" ) && manager->isPortOutput( m_key, port ) ) { - p->rate = CHANNEL_OUT; + p->rate = BufferRate::ChannelOut; if( ! m_inPlaceBroken && inbuf[ outputch ] ) { p->buffer = inbuf[ outputch ]; @@ -347,12 +347,12 @@ void LadspaEffect::pluginInstantiation() } else if( manager->isPortInput( m_key, port ) ) { - p->rate = AUDIO_RATE_INPUT; + p->rate = BufferRate::AudioRateInput; p->buffer = MM_ALLOC( Engine::audioEngine()->framesPerPeriod() ); } else { - p->rate = AUDIO_RATE_OUTPUT; + p->rate = BufferRate::AudioRateOutput; p->buffer = MM_ALLOC( Engine::audioEngine()->framesPerPeriod() ); } } @@ -362,30 +362,30 @@ void LadspaEffect::pluginInstantiation() if( manager->isPortInput( m_key, port ) ) { - p->rate = CONTROL_RATE_INPUT; + p->rate = BufferRate::ControlRateInput; } else { - p->rate = CONTROL_RATE_OUTPUT; + p->rate = BufferRate::ControlRateOutput; } } p->scale = 1.0f; if( manager->isEnum( m_key, port ) ) { - p->data_type = ENUM; + p->data_type = BufferDataType::Enum; } else if( manager->isPortToggled( m_key, port ) ) { - p->data_type = TOGGLED; + p->data_type = BufferDataType::Toggled; } else if( manager->isInteger( m_key, port ) ) { - p->data_type = INTEGER; + p->data_type = BufferDataType::Integer; } else if( p->name.toUpper().contains( "(SECONDS)" ) ) { - p->data_type = TIME; + p->data_type = BufferDataType::Time; p->scale = 1000.0f; int loc = p->name.toUpper().indexOf( "(SECONDS)" ); @@ -393,20 +393,20 @@ void LadspaEffect::pluginInstantiation() } else if( p->name.toUpper().contains( "(S)" ) ) { - p->data_type = TIME; + p->data_type = BufferDataType::Time; p->scale = 1000.0f; int loc = p->name.toUpper().indexOf( "(S)" ); p->name.replace( loc, 3, "(ms)" ); } else if( p->name.toUpper().contains( "(MS)" ) ) { - p->data_type = TIME; + p->data_type = BufferDataType::Time; int loc = p->name.toUpper().indexOf( "(MS)" ); p->name.replace( loc, 4, "(ms)" ); } else { - p->data_type = FLOATING; + p->data_type = BufferDataType::Floating; } // Get the range and default values. @@ -438,7 +438,7 @@ void LadspaEffect::pluginInstantiation() p->def = manager->getDefaultSetting( m_key, port ); if( p->def == NOHINT ) { - if( p->data_type != TOGGLED ) + if( p->data_type != BufferDataType::Toggled ) { p->def = ( p->min + p->max ) / 2.0f; } @@ -465,8 +465,8 @@ void LadspaEffect::pluginInstantiation() // For convenience, keep a separate list of the ports that are used // to control the processors. - if( p->rate == AUDIO_RATE_INPUT || - p->rate == CONTROL_RATE_INPUT ) + if( p->rate == BufferRate::AudioRateInput || + p->rate == BufferRate::ControlRateInput ) { p->control_id = m_portControls.count(); m_portControls.append( p ); @@ -555,7 +555,7 @@ void LadspaEffect::pluginDestruction() for( int port = 0; port < m_portCount; port++ ) { port_desc_t * pp = m_ports.at( proc ).at( port ); - if( m_inPlaceBroken || pp->rate != CHANNEL_OUT ) + if( m_inPlaceBroken || pp->rate != BufferRate::ChannelOut ) { if( pp->buffer) MM_FREE( pp->buffer ); } diff --git a/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp b/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp index 1522d7187..46a211f9f 100644 --- a/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp +++ b/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp @@ -39,7 +39,7 @@ namespace lmms { -LadspaSubPluginFeatures::LadspaSubPluginFeatures( Plugin::PluginTypes _type ) : +LadspaSubPluginFeatures::LadspaSubPluginFeatures( Plugin::Type _type ) : SubPluginFeatures( _type ) { } @@ -137,17 +137,17 @@ void LadspaSubPluginFeatures::listSubPluginKeys( l_sortable_plugin_t plugins; switch( m_type ) { - case Plugin::Instrument: + case Plugin::Type::Instrument: plugins = lm->getInstruments(); break; - case Plugin::Effect: + case Plugin::Type::Effect: plugins = lm->getValidEffects(); //plugins += lm->getInvalidEffects(); break; - case Plugin::Tool: + case Plugin::Type::Tool: plugins = lm->getAnalysisTools(); break; - case Plugin::Other: + case Plugin::Type::Other: plugins = lm->getOthers(); break; default: diff --git a/plugins/LadspaEffect/LadspaSubPluginFeatures.h b/plugins/LadspaEffect/LadspaSubPluginFeatures.h index c65dce9fe..7747127b8 100644 --- a/plugins/LadspaEffect/LadspaSubPluginFeatures.h +++ b/plugins/LadspaEffect/LadspaSubPluginFeatures.h @@ -38,7 +38,7 @@ namespace lmms class LadspaSubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures { public: - LadspaSubPluginFeatures( Plugin::PluginTypes _type ); + LadspaSubPluginFeatures( Plugin::Type _type ); QString displayName(const Key& k) const override; void fillDescriptionWidget( QWidget * _parent, diff --git a/plugins/Lb302/Lb302.cpp b/plugins/Lb302/Lb302.cpp index c0e477dcc..b8fff2c0b 100644 --- a/plugins/Lb302/Lb302.cpp +++ b/plugins/Lb302/Lb302.cpp @@ -86,7 +86,7 @@ Plugin::Descriptor PLUGIN_EXPORT lb302_plugin_descriptor = "Incomplete monophonic imitation TB-303" ), "Paul Giblock ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -290,7 +290,7 @@ Lb302Synth::Lb302Synth( InstrumentTrack * _instrumentTrack ) : vca_decay(0.99897516), vca_a0(0.5), vca_a(0.), - vca_mode(never_played) + vca_mode(VcaMode::NeverPlayed) { connect( Engine::audioEngine(), SIGNAL( sampleRateChanged() ), @@ -332,7 +332,7 @@ Lb302Synth::Lb302Synth( InstrumentTrack * _instrumentTrack ) : vcf_envpos = ENVINC; - vco_shape = BL_SAWTOOTH; + vco_shape = VcoShape::BLSawtooth; vcfs[0] = new Lb302FilterIIR2(&fs); vcfs[1] = new Lb302Filter3Pole(&fs); @@ -469,7 +469,7 @@ int Lb302Synth::process(sampleFrame *outbuf, const int size) if( release_frame == 0 || ! m_playingNote ) { - vca_mode = decay; + vca_mode = VcaMode::Decay; } if( new_freq ) @@ -493,7 +493,7 @@ int Lb302Synth::process(sampleFrame *outbuf, const int size) // start decay if we're past release if( i >= release_frame ) { - vca_mode = decay; + vca_mode = VcaMode::Decay; } // update vcf @@ -523,43 +523,43 @@ int Lb302Synth::process(sampleFrame *outbuf, const int size) vco_c -= 1.0; switch(int(rint(wave_shape.value()))) { - case 0: vco_shape = SAWTOOTH; break; - case 1: vco_shape = TRIANGLE; break; - case 2: vco_shape = SQUARE; break; - case 3: vco_shape = ROUND_SQUARE; break; - case 4: vco_shape = MOOG; break; - case 5: vco_shape = SINE; break; - case 6: vco_shape = EXPONENTIAL; break; - case 7: vco_shape = WHITE_NOISE; break; - case 8: vco_shape = BL_SAWTOOTH; break; - case 9: vco_shape = BL_SQUARE; break; - case 10: vco_shape = BL_TRIANGLE; break; - case 11: vco_shape = BL_MOOG; break; - default: vco_shape = SAWTOOTH; break; + case 0: vco_shape = VcoShape::Sawtooth; break; + case 1: vco_shape = VcoShape::Triangle; break; + case 2: vco_shape = VcoShape::Square; break; + case 3: vco_shape = VcoShape::RoundSquare; break; + case 4: vco_shape = VcoShape::Moog; break; + case 5: vco_shape = VcoShape::Sine; break; + case 6: vco_shape = VcoShape::Exponential; break; + case 7: vco_shape = VcoShape::WhiteNoise; break; + case 8: vco_shape = VcoShape::BLSawtooth; break; + case 9: vco_shape = VcoShape::BLSquare; break; + case 10: vco_shape = VcoShape::BLTriangle; break; + case 11: vco_shape = VcoShape::BLMoog; break; + default: vco_shape = VcoShape::Sawtooth; break; } // add vco_shape_param the changes the shape of each curve. // merge sawtooths with triangle and square with round square? switch (vco_shape) { - case SAWTOOTH: // p0: curviness of line + case VcoShape::Sawtooth: // p0: curviness of line vco_k = vco_c; // Is this sawtooth backwards? break; - case TRIANGLE: // p0: duty rev.saw<->triangle<->saw p1: curviness + case VcoShape::Triangle: // p0: duty rev.saw<->triangle<->saw p1: curviness vco_k = (vco_c*2.0)+0.5; if (vco_k>0.5) vco_k = 1.0- vco_k; break; - case SQUARE: // p0: slope of top + case VcoShape::Square: // p0: slope of top vco_k = (vco_c<0)?0.5:-0.5; break; - case ROUND_SQUARE: // p0: width of round + case VcoShape::RoundSquare: // p0: width of round vco_k = (vco_c<0)?(sqrtf(1-(vco_c*vco_c*4))-0.5):-0.5; break; - case MOOG: // Maybe the fall should be exponential/sinsoidal instead of quadric. + case VcoShape::Moog: // Maybe the fall should be exponential/sinsoidal instead of quadric. // [-0.5, 0]: Rise, [0,0.25]: Slope down, [0.25,0.5]: Low vco_k = (vco_c*2.0)+0.5; if (vco_k>1.0) { @@ -572,33 +572,33 @@ int Lb302Synth::process(sampleFrame *outbuf, const int size) vco_k *= 2.0; // MOOG wave gets filtered away break; - case SINE: + case VcoShape::Sine: // [-0.5, 0.5] : [-pi, pi] vco_k = 0.5f * Oscillator::sinSample( vco_c ); break; - case EXPONENTIAL: + case VcoShape::Exponential: vco_k = 0.5 * Oscillator::expSample( vco_c ); break; - case WHITE_NOISE: + case VcoShape::WhiteNoise: vco_k = 0.5 * Oscillator::noiseSample( vco_c ); break; - case BL_SAWTOOTH: - vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::BLSaw ) * 0.5f; + case VcoShape::BLSawtooth: + vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::Waveform::BLSaw ) * 0.5f; break; - case BL_SQUARE: - vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::BLSquare ) * 0.5f; + case VcoShape::BLSquare: + vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::Waveform::BLSquare ) * 0.5f; break; - case BL_TRIANGLE: - vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::BLTriangle ) * 0.5f; + case VcoShape::BLTriangle: + vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::Waveform::BLTriangle ) * 0.5f; break; - case BL_MOOG: - vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::BLMoog ); + case VcoShape::BLMoog: + vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::Waveform::BLMoog ); break; } @@ -633,18 +633,18 @@ int Lb302Synth::process(sampleFrame *outbuf, const int size) } // Handle Envelope - if(vca_mode==attack) { + if(vca_mode==VcaMode::Attack) { vca_a+=(vca_a0-vca_a)*vca_attack; if(sample_cnt>=0.5*Engine::audioEngine()->processingSampleRate()) - vca_mode = idle; + vca_mode = VcaMode::Idle; } - else if(vca_mode == decay) { + else if(vca_mode == VcaMode::Decay) { vca_a *= vca_decay; // the following line actually speeds up processing if(vca_a < (1/65536.0)) { vca_a = 0; - vca_mode = never_played; + vca_mode = VcaMode::NeverPlayed; } } @@ -666,15 +666,15 @@ void Lb302Synth::initNote( Lb302Note *n) // Always reset vca on non-dead notes, and // Only reset vca on decaying(decayed) and never-played - if(n->dead == 0 || (vca_mode == decay || vca_mode == never_played)) { + if(n->dead == 0 || (vca_mode == VcaMode::Decay || vca_mode == VcaMode::NeverPlayed)) { //printf(" good\n"); sample_cnt = 0; - vca_mode = attack; + vca_mode = VcaMode::Attack; // LB303: //vca_a = 0; } else { - vca_mode = idle; + vca_mode = VcaMode::Idle; } initSlide(); @@ -819,22 +819,22 @@ Lb302SynthView::Lb302SynthView( Instrument * _instrument, QWidget * _parent ) : InstrumentViewFixedSize( _instrument, _parent ) { // GUI - m_vcfCutKnob = new Knob( knobBright_26, this ); + m_vcfCutKnob = new Knob( KnobType::Bright26, this ); m_vcfCutKnob->move( 75, 130 ); m_vcfCutKnob->setHintText( tr( "Cutoff Freq:" ), "" ); m_vcfCutKnob->setLabel( "" ); - m_vcfResKnob = new Knob( knobBright_26, this ); + m_vcfResKnob = new Knob( KnobType::Bright26, this ); m_vcfResKnob->move( 120, 130 ); m_vcfResKnob->setHintText( tr( "Resonance:" ), "" ); m_vcfResKnob->setLabel( "" ); - m_vcfModKnob = new Knob( knobBright_26, this ); + m_vcfModKnob = new Knob( KnobType::Bright26, this ); m_vcfModKnob->move( 165, 130 ); m_vcfModKnob->setHintText( tr( "Env Mod:" ), "" ); m_vcfModKnob->setLabel( "" ); - m_vcfDecKnob = new Knob( knobBright_26, this ); + m_vcfDecKnob = new Knob( KnobType::Bright26, this ); m_vcfDecKnob->move( 210, 130 ); m_vcfDecKnob->setHintText( tr( "Decay:" ), "" ); m_vcfDecKnob->setLabel( "" ); @@ -855,12 +855,12 @@ Lb302SynthView::Lb302SynthView( Instrument * _instrument, QWidget * _parent ) : tr( "303-es-que, 24dB/octave, 3 pole filter" ) ); - m_slideDecKnob = new Knob( knobBright_26, this ); + m_slideDecKnob = new Knob( KnobType::Bright26, this ); m_slideDecKnob->move( 210, 75 ); m_slideDecKnob->setHintText( tr( "Slide Decay:" ), "" ); m_slideDecKnob->setLabel( ""); - m_distKnob = new Knob( knobBright_26, this ); + m_distKnob = new Knob( KnobType::Bright26, this ); m_distKnob->move( 210, 190 ); m_distKnob->setHintText( tr( "DIST:" ), "" ); m_distKnob->setLabel( tr( "")); diff --git a/plugins/Lb302/Lb302.h b/plugins/Lb302/Lb302.h index 3abece98f..237a3f3f8 100644 --- a/plugins/Lb302/Lb302.h +++ b/plugins/Lb302/Lb302.h @@ -165,7 +165,7 @@ public: Flags flags() const override { - return IsSingleStreamed; + return Flag::IsSingleStreamed; } f_cnt_t desiredReleaseFrames() const override @@ -213,9 +213,9 @@ private: vco_slideinc, //* Slide base to use in next node. Nonzero=slide next note vco_slidebase; //* The base vco_inc while sliding. - enum vco_shape_t { SAWTOOTH, SQUARE, TRIANGLE, MOOG, ROUND_SQUARE, SINE, EXPONENTIAL, WHITE_NOISE, - BL_SAWTOOTH, BL_SQUARE, BL_TRIANGLE, BL_MOOG }; - vco_shape_t vco_shape; + enum class VcoShape { Sawtooth, Square, Triangle, Moog, RoundSquare, Sine, Exponential, WhiteNoise, + BLSawtooth, BLSquare, BLTriangle, BLMoog }; + VcoShape vco_shape; // Filters (just keep both loaded and switch) Lb302Filter* vcfs[NUM_FILTERS]; @@ -235,14 +235,14 @@ private: vca_a; // Amplifier coefficient. // Envelope State - enum VCA_Mode + enum class VcaMode { - attack = 0, - decay = 1, - idle = 2, - never_played = 3 + Attack = 0, + Decay = 1, + Idle = 2, + NeverPlayed = 3 }; - VCA_Mode vca_mode; + VcaMode vca_mode; // My hacks int sample_cnt; diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index ec2dc1f48..9c21b3f2a 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -46,10 +46,10 @@ Plugin::Descriptor PLUGIN_EXPORT lv2effect_plugin_descriptor = "plugin for using arbitrary LV2-effects inside LMMS."), "Johannes Lorenz ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, - new Lv2SubPluginFeatures(Plugin::Effect) + new Lv2SubPluginFeatures(Plugin::Type::Effect) }; } diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 052445844..1e45f4e91 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -55,10 +55,10 @@ Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = "plugin for using arbitrary LV2 instruments inside LMMS."), "Johannes Lorenz ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader("logo"), nullptr, - new Lv2SubPluginFeatures(Plugin::Instrument) + new Lv2SubPluginFeatures(Plugin::Type::Instrument) }; } @@ -97,7 +97,7 @@ Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg, Lv2Instrument::~Lv2Instrument() { Engine::audioEngine()->removePlayHandlesOfTypes(instrumentTrack(), - PlayHandle::TypeNotePlayHandle | PlayHandle::TypeInstrumentPlayHandle); + PlayHandle::Type::NotePlayHandle | PlayHandle::Type::InstrumentPlayHandle); } diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 7b521e1b0..2cd73632d 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -89,9 +89,9 @@ public: Flags flags() const override { #ifdef LV2_INSTRUMENT_USE_MIDI - return IsSingleStreamed | IsMidiBased; + return Flag::IsSingleStreamed | Flag::IsMidiBased; #else - return IsSingleStreamed; + return Flag::IsSingleStreamed; #endif } gui::PluginView* instantiateView(QWidget *parent) override; diff --git a/plugins/MidiExport/MidiExport.cpp b/plugins/MidiExport/MidiExport.cpp index 0d18d8ae1..df968e36a 100644 --- a/plugins/MidiExport/MidiExport.cpp +++ b/plugins/MidiExport/MidiExport.cpp @@ -51,7 +51,7 @@ Plugin::Descriptor PLUGIN_EXPORT midiexport_plugin_descriptor = "Mohamed Abdel Maksoud and " "Hyunjin Song ", 0x0100, - Plugin::ExportFilter, + Plugin::Type::ExportFilter, nullptr, nullptr, nullptr, @@ -84,8 +84,8 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, auto buffer = std::array{}; uint32_t size; - for (const Track* track : tracks) if (track->type() == Track::InstrumentTrack) nTracks++; - for (const Track* track : patternStoreTracks) if (track->type() == Track::InstrumentTrack) nTracks++; + for (const Track* track : tracks) if (track->type() == Track::Type::Instrument) nTracks++; + for (const Track* track : patternStoreTracks) if (track->type() == Track::Type::Instrument) nTracks++; // midi header MidiFile::MIDIHeader header(nTracks); @@ -97,10 +97,10 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, // midi tracks for (Track* track : tracks) { - DataFile dataFile(DataFile::SongProject); + DataFile dataFile(DataFile::Type::SongProject); MTrack mtrack; - if (track->type() == Track::InstrumentTrack) + if (track->type() == Track::Type::Instrument) { mtrack.addName(track->name().toStdString(), 0); @@ -143,7 +143,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, midiout.writeRawData((char *)buffer.data(), size); } - if (track->type() == Track::PatternTrack) + if (track->type() == Track::Type::Pattern) { patternTrack = dynamic_cast(track); element = patternTrack->saveState(dataFile, dataFile.content()); @@ -169,7 +169,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, // for each instrument in the pattern editor for (Track* track : patternStoreTracks) { - DataFile dataFile(DataFile::SongProject); + DataFile dataFile(DataFile::Type::SongProject); MTrack mtrack; // begin at the first pattern track (first pattern) @@ -177,7 +177,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, std::vector> st; - if (track->type() != Track::InstrumentTrack) continue; + if (track->type() != Track::Type::Instrument) continue; mtrack.addName(track->name().toStdString(), 0); //mtrack.addProgramChange(0, 0); diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index 6836060f3..785cd079a 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -71,7 +71,7 @@ Plugin::Descriptor PLUGIN_EXPORT midiimport_plugin_descriptor = "Filter for importing MIDI-files into LMMS" ), "Tobias Doerffel ", 0x0100, - Plugin::ImportFilter, + Plugin::Type::ImportFilter, nullptr, nullptr, nullptr, @@ -165,7 +165,7 @@ public: // in the main thread. This should probably be // removed if that ever changes. qApp->processEvents(); - at = dynamic_cast( Track::create( Track::AutomationTrack, tc ) ); + at = dynamic_cast( Track::create( Track::Type::Automation, tc ) ); } if( tn != "") { at->setName( tn ); @@ -227,7 +227,7 @@ public: if( !it ) { // Keep LMMS responsive qApp->processEvents(); - it = dynamic_cast( Track::create( Track::InstrumentTrack, tc ) ); + it = dynamic_cast( Track::create( Track::Type::Instrument, tc ) ); #ifdef LMMS_HAVE_FLUIDSYNTH it_inst = it->loadInstrument( "sf2player" ); @@ -328,9 +328,9 @@ bool MidiImport::readSMF( TrackContainer* tc ) // NOTE: unordered_map::operator[] creates a new element if none exists MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel(); - auto nt = dynamic_cast(Track::create(Track::AutomationTrack, Engine::getSong())); + auto nt = dynamic_cast(Track::create(Track::Type::Automation, Engine::getSong())); nt->setName(tr("MIDI Time Signature Numerator")); - auto dt = dynamic_cast(Track::create(Track::AutomationTrack, Engine::getSong())); + auto dt = dynamic_cast(Track::create(Track::Type::Automation, Engine::getSong())); dt->setName(tr("MIDI Time Signature Denominator")); auto timeSigNumeratorPat = new AutomationClip(nt); timeSigNumeratorPat->setDisplayName(tr("Numerator")); @@ -358,7 +358,7 @@ bool MidiImport::readSMF( TrackContainer* tc ) pd.setValue( 2 ); // Tempo stuff - auto tt = dynamic_cast(Track::create(Track::AutomationTrack, Engine::getSong())); + auto tt = dynamic_cast(Track::create(Track::Type::Automation, Engine::getSong())); tt->setName(tr("Tempo")); auto tap = new AutomationClip(tt); tap->setDisplayName(tr("Tempo")); diff --git a/plugins/Monstro/Monstro.cpp b/plugins/Monstro/Monstro.cpp index 10dbf5b1f..f588d6b78 100644 --- a/plugins/Monstro/Monstro.cpp +++ b/plugins/Monstro/Monstro.cpp @@ -53,7 +53,7 @@ Plugin::Descriptor PLUGIN_EXPORT monstro_plugin_descriptor = "Monstrous 3-oscillator synth with modulation matrix" ), "Vesa Kivimäki ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, diff --git a/plugins/Monstro/Monstro.h b/plugins/Monstro/Monstro.h index 319b7e000..21efedaf3 100644 --- a/plugins/Monstro/Monstro.h +++ b/plugins/Monstro/Monstro.h @@ -43,14 +43,14 @@ // #define makeknob( name, x, y, hint, unit, oname ) \ - name = new Knob( knobStyled, view ); \ + name = new Knob( KnobType::Styled, view ); \ name ->move( x, y ); \ name ->setHintText( hint, unit ); \ name ->setObjectName( oname ); \ name ->setFixedSize( 20, 20 ); #define maketsknob( name, x, y, hint, unit, oname ) \ - name = new TempoSyncKnob( knobStyled, view ); \ + name = new TempoSyncKnob( KnobType::Styled, view ); \ name ->move( x, y ); \ name ->setHintText( hint, unit ); \ name ->setObjectName( oname ); \ @@ -211,19 +211,19 @@ private: break; case WAVE_TRI: //return Oscillator::triangleSample( _ph ); - return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLTriangle ); + return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::Waveform::BLTriangle ); break; case WAVE_SAW: //return Oscillator::sawSample( _ph ); - return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLSaw ); + return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::Waveform::BLSaw ); break; case WAVE_RAMP: //return Oscillator::sawSample( _ph ) * -1.0; - return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLSaw ) * -1.0; + return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::Waveform::BLSaw ) * -1.0; break; case WAVE_SQR: //return Oscillator::squareSample( _ph ); - return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLSquare ); + return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::Waveform::BLSquare ); break; case WAVE_SQRSOFT: { @@ -236,7 +236,7 @@ private: } case WAVE_MOOG: //return Oscillator::moogSawSample( _ph ); - return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::BLMoog ); + return BandLimitedWave::oscillate( _ph, _wavelen, BandLimitedWave::Waveform::BLMoog ); break; case WAVE_SINABS: return qAbs( Oscillator::sinSample( _ph ) ); diff --git a/plugins/MultitapEcho/MultitapEcho.cpp b/plugins/MultitapEcho/MultitapEcho.cpp index 05c3f30fe..4f5e9fdf8 100644 --- a/plugins/MultitapEcho/MultitapEcho.cpp +++ b/plugins/MultitapEcho/MultitapEcho.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor PLUGIN_EXPORT multitapecho_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "A multitap echo delay plugin" ), "Vesa Kivimäki ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader( "logo" ), nullptr, nullptr, diff --git a/plugins/MultitapEcho/MultitapEchoControlDialog.cpp b/plugins/MultitapEcho/MultitapEchoControlDialog.cpp index dbf428e5f..e89137bf0 100644 --- a/plugins/MultitapEcho/MultitapEchoControlDialog.cpp +++ b/plugins/MultitapEcho/MultitapEchoControlDialog.cpp @@ -48,8 +48,8 @@ MultitapEchoControlDialog::MultitapEchoControlDialog( MultitapEchoControls * con // graph widgets - auto ampGraph = new Graph(this, Graph::BarStyle, 204, 105); - auto lpGraph = new Graph(this, Graph::BarStyle, 204, 105); + auto ampGraph = new Graph(this, Graph::Style::Bar, 204, 105); + auto lpGraph = new Graph(this, Graph::Style::Bar, 204, 105); ampGraph->move( 30, 10 ); lpGraph->move( 30, 125 ); @@ -78,26 +78,26 @@ MultitapEchoControlDialog::MultitapEchoControlDialog( MultitapEchoControls * con // knobs - auto stepLength = new TempoSyncKnob(knobBright_26, this); + auto stepLength = new TempoSyncKnob(KnobType::Bright26, this); stepLength->move( 100, 245 ); stepLength->setModel( & controls->m_stepLength ); stepLength->setLabel( tr( "Length" ) ); stepLength->setHintText( tr( "Step length:" ) , " ms" ); - auto dryGain = new Knob(knobBright_26, this); + auto dryGain = new Knob(KnobType::Bright26, this); dryGain->move( 150, 245 ); dryGain->setModel( & controls->m_dryGain ); dryGain->setLabel( tr( "Dry" ) ); dryGain->setHintText( tr( "Dry gain:" ) , " dBFS" ); - auto stages = new Knob(knobBright_26, this); + auto stages = new Knob(KnobType::Bright26, this); stages->move( 200, 245 ); stages->setModel( & controls->m_stages ); stages->setLabel( tr( "Stages" ) ); stages->setHintText( tr( "Low-pass stages:" ) , "x" ); // switch led - auto swapInputs = new LedCheckBox("Swap inputs", this, tr("Swap inputs"), LedCheckBox::Green); + auto swapInputs = new LedCheckBox("Swap inputs", this, tr("Swap inputs"), LedCheckBox::LedColor::Green); swapInputs->move( 20, 275 ); swapInputs->setModel( & controls->m_swapInputs ); swapInputs->setToolTip(tr("Swap left and right input channels for reflections")); diff --git a/plugins/Nes/Nes.cpp b/plugins/Nes/Nes.cpp index 4260bca1a..a530ac19b 100644 --- a/plugins/Nes/Nes.cpp +++ b/plugins/Nes/Nes.cpp @@ -51,7 +51,7 @@ Plugin::Descriptor PLUGIN_EXPORT nes_plugin_descriptor = "A NES-like synthesizer" ), "Vesa Kivimäki ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, diff --git a/plugins/Nes/Nes.h b/plugins/Nes/Nes.h index 91e5e556c..3ddf0fc9a 100644 --- a/plugins/Nes/Nes.h +++ b/plugins/Nes/Nes.h @@ -35,7 +35,7 @@ #define makeknob( name, x, y, hint, unit, oname ) \ - name = new Knob( knobStyled, this ); \ + name = new Knob( KnobType::Styled, this ); \ name ->move( x, y ); \ name ->setHintText( hint, unit ); \ name ->setObjectName( oname ); \ diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index 3a18c1e3f..64f609995 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -73,7 +73,7 @@ Plugin::Descriptor PLUGIN_EXPORT opulenz_plugin_descriptor = "2-operator FM Synth" ), "Raine M. Ekman ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), "sbi", nullptr, @@ -222,8 +222,8 @@ OpulenzInstrument::OpulenzInstrument( InstrumentTrack * _instrument_track ) : OpulenzInstrument::~OpulenzInstrument() { delete theEmulator; Engine::audioEngine()->removePlayHandlesOfTypes( instrumentTrack(), - PlayHandle::TypeNotePlayHandle - | PlayHandle::TypeInstrumentPlayHandle ); + PlayHandle::Type::NotePlayHandle + | PlayHandle::Type::InstrumentPlayHandle ); delete [] renderbuffer; } @@ -686,7 +686,7 @@ OpulenzInstrumentView::OpulenzInstrumentView( Instrument * _instrument, { #define KNOB_GEN(knobname, hinttext, hintunit,xpos,ypos) \ - knobname = new Knob( knobStyled, this );\ + knobname = new Knob( KnobType::Styled, this );\ knobname->setHintText( tr(hinttext), hintunit );\ knobname->setFixedSize(22,22);\ knobname->setCenterPointX(11.0);\ diff --git a/plugins/OpulenZ/OpulenZ.h b/plugins/OpulenZ/OpulenZ.h index 1f999252f..a3e11a6c0 100644 --- a/plugins/OpulenZ/OpulenZ.h +++ b/plugins/OpulenZ/OpulenZ.h @@ -66,7 +66,7 @@ public: Flags flags() const override { - return IsSingleStreamed | IsMidiBased; + return Flag::IsSingleStreamed | Flag::IsMidiBased; } bool handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset = 0 ) override; diff --git a/plugins/Organic/Organic.cpp b/plugins/Organic/Organic.cpp index 476b24961..f8a2b0d13 100644 --- a/plugins/Organic/Organic.cpp +++ b/plugins/Organic/Organic.cpp @@ -52,7 +52,7 @@ Plugin::Descriptor PLUGIN_EXPORT organic_plugin_descriptor = "Additive Synthesizer for organ-like sounds" ), "Andreas Brandmaier ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -74,7 +74,9 @@ float * OrganicInstrument::s_harmonics = nullptr; OrganicInstrument::OrganicInstrument( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &organic_plugin_descriptor ), - m_modulationAlgo( Oscillator::SignalMix, Oscillator::SignalMix, Oscillator::SignalMix), + m_modulationAlgo(static_cast(Oscillator::ModulationAlgo::SignalMix), + static_cast(Oscillator::ModulationAlgo::SignalMix), + static_cast(Oscillator::ModulationAlgo::SignalMix)), m_fx1Model( 0.0f, 0.0f, 0.99f, 0.01f , this, tr( "Distortion" ) ), m_volModel( 100.0f, 0.0f, 200.0f, 1.0f, this, tr( "Volume" ) ) { @@ -403,7 +405,7 @@ class OrganicKnob : public Knob { public: OrganicKnob( QWidget * _parent ) : - Knob( knobStyled, _parent ) + Knob( KnobType::Styled, _parent ) { setFixedSize( 21, 21 ); } @@ -506,7 +508,7 @@ void OrganicInstrumentView::modelChanged() oscKnob->setHintText( tr( "Osc %1 waveform:" ).arg( i + 1 ), QString() ); // setup volume-knob - auto volKnob = new Knob(knobStyled, this); + auto volKnob = new Knob(KnobType::Styled, this); volKnob->setVolumeKnob( true ); volKnob->move( x + i * colWidth, y + rowHeight*1 ); volKnob->setFixedSize( 21, 21 ); @@ -560,7 +562,7 @@ void OrganicInstrumentView::updateKnobHint() OscillatorObject::OscillatorObject( Model * _parent, int _index ) : Model( _parent ), - m_waveShape( Oscillator::SineWave, 0, Oscillator::NumWaveShapes-1, this ), + m_waveShape( static_cast(Oscillator::WaveShape::Sine), 0, Oscillator::NumWaveShapes-1, this ), m_oscModel( 0.0f, 0.0f, 5.0f, 1.0f, this, tr( "Osc %1 waveform" ).arg( _index + 1 ) ), m_harmModel( static_cast( _index ), 0.0f, 17.0f, 1.0f, @@ -582,15 +584,15 @@ void OscillatorObject::oscButtonChanged() static auto shapes = std::array { - Oscillator::SineWave, - Oscillator::SawWave, - Oscillator::SquareWave, - Oscillator::TriangleWave, - Oscillator::MoogSawWave, - Oscillator::ExponentialWave + Oscillator::WaveShape::Sine, + Oscillator::WaveShape::Saw, + Oscillator::WaveShape::Square, + Oscillator::WaveShape::Triangle, + Oscillator::WaveShape::MoogSaw, + Oscillator::WaveShape::Exponential } ; - m_waveShape.setValue( shapes[(int)roundf( m_oscModel.value() )] ); + m_waveShape.setValue( static_cast(shapes[(int)roundf( m_oscModel.value() )]) ); } diff --git a/plugins/Patman/Patman.cpp b/plugins/Patman/Patman.cpp index 7bcd46dcb..a2b829940 100644 --- a/plugins/Patman/Patman.cpp +++ b/plugins/Patman/Patman.cpp @@ -61,7 +61,7 @@ Plugin::Descriptor PLUGIN_EXPORT patman_plugin_descriptor = "GUS-compatible patch instrument" ), "Javier Serrano Polo ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), "pat", nullptr, @@ -154,7 +154,7 @@ void PatmanInstrument::playNote( NotePlayHandle * _n, hdata->sample->frequency(); if( hdata->sample->play( _working_buffer + offset, hdata->state, frames, - play_freq, m_loopedModel.value() ? SampleBuffer::LoopOn : SampleBuffer::LoopOff ) ) + play_freq, m_loopedModel.value() ? SampleBuffer::LoopMode::On : SampleBuffer::LoopMode::Off ) ) { applyRelease( _working_buffer, _n ); instrumentTrack()->processAudioBuffer( _working_buffer, @@ -201,8 +201,8 @@ void PatmanInstrument::setFile( const QString & _patch_file, bool _rename ) // named it self m_patchFile = PathUtil::toShortestRelative( _patch_file ); - LoadErrors error = loadPatch( PathUtil::toAbsolute( _patch_file ) ); - if( error ) + LoadError error = loadPatch( PathUtil::toAbsolute( _patch_file ) ); + if( error != LoadError::OK ) { printf("Load error\n"); } @@ -213,7 +213,7 @@ void PatmanInstrument::setFile( const QString & _patch_file, bool _rename ) -PatmanInstrument::LoadErrors PatmanInstrument::loadPatch( +PatmanInstrument::LoadError PatmanInstrument::loadPatch( const QString & _filename ) { unloadCurrentPatch(); @@ -222,7 +222,7 @@ PatmanInstrument::LoadErrors PatmanInstrument::loadPatch( if( !fd ) { perror( "fopen" ); - return( LoadOpen ); + return( LoadError::Open ); } auto header = std::array{}; @@ -232,19 +232,19 @@ PatmanInstrument::LoadErrors PatmanInstrument::loadPatch( && memcmp(header.data(), "GF1PATCH100\0ID#000002", 22))) { fclose( fd ); - return( LoadNotGUS ); + return( LoadError::NotGUS ); } if( header[82] != 1 && header[82] != 0 ) { fclose( fd ); - return( LoadInstruments ); + return( LoadError::Instruments ); } if( header[151] != 1 && header[151] != 0 ) { fclose( fd ); - return( LoadLayers ); + return( LoadError::Layers ); } int sample_count = header[198]; @@ -256,14 +256,14 @@ PatmanInstrument::LoadErrors PatmanInstrument::loadPatch( if ( fseek( fd, x, SEEK_CUR ) == -1 ) \ { \ fclose( fd ); \ - return( LoadIO ); \ + return( LoadError::IO ); \ } #define READ_SHORT( x ) \ if ( fread( &tmpshort, 2, 1, fd ) != 1 ) \ { \ fclose( fd ); \ - return( LoadIO ); \ + return( LoadError::IO ); \ } \ x = (unsigned short)swap16IfBE( tmpshort ); @@ -271,7 +271,7 @@ PatmanInstrument::LoadErrors PatmanInstrument::loadPatch( if ( fread( &x, 4, 1, fd ) != 1 ) \ { \ fclose( fd ); \ - return( LoadIO ); \ + return( LoadError::IO ); \ } \ x = (unsigned)swap32IfBE( x ); @@ -295,7 +295,7 @@ PatmanInstrument::LoadErrors PatmanInstrument::loadPatch( if ( fread( &modes, 1, 1, fd ) != 1 ) { fclose( fd ); - return( LoadIO ); + return( LoadError::IO ); } // skip scale frequency, scale factor, reserved space SKIP_BYTES( 2 + 2 + 36 ); @@ -313,7 +313,7 @@ PatmanInstrument::LoadErrors PatmanInstrument::loadPatch( { delete[] wave_samples; fclose( fd ); - return( LoadIO ); + return( LoadError::IO ); } sample = swap16IfBE( sample ); if( modes & MODES_UNSIGNED ) @@ -337,7 +337,7 @@ PatmanInstrument::LoadErrors PatmanInstrument::loadPatch( { delete[] wave_samples; fclose( fd ); - return( LoadIO ); + return( LoadError::IO ); } if( modes & MODES_UNSIGNED ) { @@ -374,7 +374,7 @@ PatmanInstrument::LoadErrors PatmanInstrument::loadPatch( delete[] data; } fclose( fd ); - return( LoadOK ); + return( LoadError::OK ); } diff --git a/plugins/Patman/Patman.h b/plugins/Patman/Patman.h index acdf4f7ef..3a15db5f3 100644 --- a/plugins/Patman/Patman.h +++ b/plugins/Patman/Patman.h @@ -98,17 +98,17 @@ private: BoolModel m_tunedModel; - enum LoadErrors + enum class LoadError { - LoadOK, - LoadOpen, - LoadNotGUS, - LoadInstruments, - LoadLayers, - LoadIO + OK, + Open, + NotGUS, + Instruments, + Layers, + IO } ; - LoadErrors loadPatch( const QString & _filename ); + LoadError loadPatch( const QString & _filename ); void unloadCurrentPatch(); void selectSample( NotePlayHandle * _n ); diff --git a/plugins/PeakControllerEffect/PeakControllerEffect.cpp b/plugins/PeakControllerEffect/PeakControllerEffect.cpp index bd99631b7..7aff6f803 100644 --- a/plugins/PeakControllerEffect/PeakControllerEffect.cpp +++ b/plugins/PeakControllerEffect/PeakControllerEffect.cpp @@ -48,7 +48,7 @@ Plugin::Descriptor PLUGIN_EXPORT peakcontrollereffect_plugin_descriptor = "Plugin for controlling knobs with sound peaks" ), "Paul Giblock ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp b/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp index 69566a86c..e44d5bcc2 100644 --- a/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp +++ b/plugins/PeakControllerEffect/PeakControllerEffectControlDialog.cpp @@ -49,32 +49,32 @@ PeakControllerEffectControlDialog::PeakControllerEffectControlDialog( setPalette( pal ); setFixedSize( 240, 80 ); - m_baseKnob = new Knob( knobBright_26, this ); + m_baseKnob = new Knob( KnobType::Bright26, this ); m_baseKnob->setLabel( tr( "BASE" ) ); m_baseKnob->setModel( &_controls->m_baseModel ); m_baseKnob->setHintText( tr( "Base:" ) , "" ); - m_amountKnob = new Knob( knobBright_26, this ); + m_amountKnob = new Knob( KnobType::Bright26, this ); m_amountKnob->setLabel( tr( "AMNT" ) ); m_amountKnob->setModel( &_controls->m_amountModel ); m_amountKnob->setHintText( tr( "Modulation amount:" ) , "" ); - m_amountMultKnob = new Knob( knobBright_26, this ); + m_amountMultKnob = new Knob( KnobType::Bright26, this ); m_amountMultKnob->setLabel( tr( "MULT" ) ); m_amountMultKnob->setModel( &_controls->m_amountMultModel ); m_amountMultKnob->setHintText( tr( "Amount multiplicator:" ) , "" ); - m_attackKnob = new Knob( knobBright_26, this ); + m_attackKnob = new Knob( KnobType::Bright26, this ); m_attackKnob->setLabel( tr( "ATCK" ) ); m_attackKnob->setModel( &_controls->m_attackModel ); m_attackKnob->setHintText( tr( "Attack:" ) , "" ); - m_decayKnob = new Knob( knobBright_26, this ); + m_decayKnob = new Knob( KnobType::Bright26, this ); m_decayKnob->setLabel( tr( "DCAY" ) ); m_decayKnob->setModel( &_controls->m_decayModel ); m_decayKnob->setHintText( tr( "Release:" ) , "" ); - m_tresholdKnob = new Knob( knobBright_26, this ); + m_tresholdKnob = new Knob( KnobType::Bright26, this ); m_tresholdKnob->setLabel( tr( "TRSH" ) ); m_tresholdKnob->setModel( &_controls->m_tresholdModel ); m_tresholdKnob->setHintText( tr( "Treshold:" ) , "" ); diff --git a/plugins/ReverbSC/ReverbSC.cpp b/plugins/ReverbSC/ReverbSC.cpp index e8dec8fd7..9006f8c9f 100644 --- a/plugins/ReverbSC/ReverbSC.cpp +++ b/plugins/ReverbSC/ReverbSC.cpp @@ -42,7 +42,7 @@ Plugin::Descriptor PLUGIN_EXPORT reverbsc_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "Reverb algorithm by Sean Costello" ), "Paul Batchelor", 0x0123, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader( "logo" ), nullptr, nullptr, diff --git a/plugins/ReverbSC/ReverbSCControlDialog.cpp b/plugins/ReverbSC/ReverbSCControlDialog.cpp index c9ddef384..615d3823e 100644 --- a/plugins/ReverbSC/ReverbSCControlDialog.cpp +++ b/plugins/ReverbSC/ReverbSCControlDialog.cpp @@ -42,25 +42,25 @@ ReverbSCControlDialog::ReverbSCControlDialog( ReverbSCControls* controls ) : setPalette( pal ); setFixedSize( 185, 55 ); - auto inputGainKnob = new Knob(knobBright_26, this); + auto inputGainKnob = new Knob(KnobType::Bright26, this); inputGainKnob -> move( 16, 10 ); inputGainKnob->setModel( &controls->m_inputGainModel ); inputGainKnob->setLabel( tr( "Input" ) ); inputGainKnob->setHintText( tr( "Input gain:" ) , "dB" ); - auto sizeKnob = new Knob(knobBright_26, this); + auto sizeKnob = new Knob(KnobType::Bright26, this); sizeKnob -> move( 57, 10 ); sizeKnob->setModel( &controls->m_sizeModel ); sizeKnob->setLabel( tr( "Size" ) ); sizeKnob->setHintText( tr( "Size:" ) , "" ); - auto colorKnob = new Knob(knobBright_26, this); + auto colorKnob = new Knob(KnobType::Bright26, this); colorKnob -> move( 98, 10 ); colorKnob->setModel( &controls->m_colorModel ); colorKnob->setLabel( tr( "Color" ) ); colorKnob->setHintText( tr( "Color:" ) , "" ); - auto outputGainKnob = new Knob(knobBright_26, this); + auto outputGainKnob = new Knob(KnobType::Bright26, this); outputGainKnob -> move( 139, 10 ); outputGainKnob->setModel( &controls->m_outputGainModel ); outputGainKnob->setLabel( tr( "Output" ) ); diff --git a/plugins/Sf2Player/Sf2Player.cpp b/plugins/Sf2Player/Sf2Player.cpp index ef4f884ca..1f0cb7c59 100644 --- a/plugins/Sf2Player/Sf2Player.cpp +++ b/plugins/Sf2Player/Sf2Player.cpp @@ -63,7 +63,7 @@ Plugin::Descriptor PLUGIN_EXPORT sf2player_plugin_descriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "Player for SoundFont files" ), "Paul Giblock ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), "sf2,sf3", nullptr, @@ -200,8 +200,8 @@ Sf2Instrument::Sf2Instrument( InstrumentTrack * _instrument_track ) : Sf2Instrument::~Sf2Instrument() { Engine::audioEngine()->removePlayHandlesOfTypes( instrumentTrack(), - PlayHandle::TypeNotePlayHandle - | PlayHandle::TypeInstrumentPlayHandle ); + PlayHandle::Type::NotePlayHandle + | PlayHandle::Type::InstrumentPlayHandle ); freeFont(); delete_fluid_synth( m_synth ); delete_fluid_settings( m_settings ); @@ -616,7 +616,7 @@ void Sf2Instrument::reloadSynth() m_synthMutex.lock(); if( Engine::audioEngine()->currentQualitySettings().interpolation >= - AudioEngine::qualitySettings::Interpolation_SincFastest ) + AudioEngine::qualitySettings::Interpolation::SincFastest ) { fluid_synth_set_interp_method( m_synth, -1, FLUID_INTERP_7THORDER ); } @@ -936,7 +936,7 @@ class Sf2Knob : public Knob { public: Sf2Knob( QWidget * _parent ) : - Knob( knobStyled, _parent ) + Knob( KnobType::Styled, _parent ) { setFixedSize( 31, 38 ); } diff --git a/plugins/Sf2Player/Sf2Player.h b/plugins/Sf2Player/Sf2Player.h index 5a88d0f95..bd7fa1b81 100644 --- a/plugins/Sf2Player/Sf2Player.h +++ b/plugins/Sf2Player/Sf2Player.h @@ -88,7 +88,7 @@ public: Flags flags() const override { - return IsSingleStreamed; + return Flag::IsSingleStreamed; } gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/Sfxr/Sfxr.cpp b/plugins/Sfxr/Sfxr.cpp index 33f588521..fc39ea0fa 100644 --- a/plugins/Sfxr/Sfxr.cpp +++ b/plugins/Sfxr/Sfxr.cpp @@ -67,7 +67,7 @@ Plugin::Descriptor PLUGIN_EXPORT sfxr_plugin_descriptor = "LMMS port of sfxr" ), "Wong Cho Ching", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -350,7 +350,7 @@ SfxrInstrument::SfxrInstrument( InstrumentTrack * _instrument_track ) : m_lpFilResoModel(0.0f, this, "LP Filter Resonance"), m_hpFilCutModel(0.0f, this, "HP Filter Cutoff"), m_hpFilCutSweepModel(0.0f, this, "HP Filter Cutoff Sweep"), - m_waveFormModel( SQR_WAVE, 0, WAVES_NUM-1, this, tr( "Wave" ) ) + m_waveFormModel( static_cast(SfxrWave::Square), 0, NumSfxrWaves-1, this, tr( "Wave" ) ) { } @@ -549,7 +549,7 @@ class SfxrKnob : public Knob { public: SfxrKnob( QWidget * _parent ) : - Knob( knobStyled, _parent ) + Knob( KnobType::Styled, _parent ) { setFixedSize( 20, 20 ); setCenterPointX( 10.0 ); diff --git a/plugins/Sfxr/Sfxr.h b/plugins/Sfxr/Sfxr.h index 632ccfebd..edec0ba6f 100644 --- a/plugins/Sfxr/Sfxr.h +++ b/plugins/Sfxr/Sfxr.h @@ -37,10 +37,11 @@ namespace lmms { -enum SfxrWaves +enum class SfxrWave { - SQR_WAVE, SAW_WAVE, SINE_WAVE, NOISE_WAVE, WAVES_NUM + Square, Saw, Sine, Noise, Count }; +constexpr auto NumSfxrWaves = static_cast(SfxrWave::Count); const int WAVEFORM_BASE_X = 20; const int WAVEFORM_BASE_Y = 15; diff --git a/plugins/Sid/SidInstrument.cpp b/plugins/Sid/SidInstrument.cpp index 3b41d8972..f663c3b69 100644 --- a/plugins/Sid/SidInstrument.cpp +++ b/plugins/Sid/SidInstrument.cpp @@ -83,7 +83,7 @@ Plugin::Descriptor PLUGIN_EXPORT sid_plugin_descriptor = "Csaba Hruska " "Attila Herman ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -105,7 +105,7 @@ VoiceObject::VoiceObject( Model * _parent, int _idx ) : tr( "Voice %1 release" ).arg( _idx+1 ) ), m_coarseModel( 0.0f, -24.0, 24.0, 1.0f, this, tr( "Voice %1 coarse detuning" ).arg( _idx+1 ) ), - m_waveFormModel( TriangleWave, 0, NumWaveShapes-1, this, + m_waveFormModel( static_cast(WaveForm::Triangle), 0, NumWaveShapes-1, this, tr( "Voice %1 wave shape" ).arg( _idx+1 ) ), m_syncModel( false, this, tr( "Voice %1 sync" ).arg( _idx+1 ) ), @@ -121,12 +121,12 @@ SidInstrument::SidInstrument( InstrumentTrack * _instrument_track ) : // filter m_filterFCModel( 1024.0f, 0.0f, 2047.0f, 1.0f, this, tr( "Cutoff frequency" ) ), m_filterResonanceModel( 8.0f, 0.0f, 15.0f, 1.0f, this, tr( "Resonance" ) ), - m_filterModeModel( LowPass, 0, NumFilterTypes-1, this, tr( "Filter type" )), + m_filterModeModel( static_cast(FilterType::LowPass), 0, NumFilterTypes-1, this, tr( "Filter type" )), // misc m_voice3OffModel( false, this, tr( "Voice 3 off" ) ), m_volumeModel( 15.0f, 0.0f, 15.0f, 1.0f, this, tr( "Volume" ) ), - m_chipModel( sidMOS8580, 0, NumChipModels-1, this, tr( "Chip model" ) ) + m_chipModel( static_cast(ChipModel::MOS8580), 0, NumChipModels-1, this, tr( "Chip model" ) ) { for( int i = 0; i < 3; ++i ) { @@ -323,7 +323,7 @@ void SidInstrument::playNote( NotePlayHandle * _n, reg = 0x00; } - if( (ChipModel)m_chipModel.value() == sidMOS6581 ) + if( (ChipModel)m_chipModel.value() == ChipModel::MOS6581 ) { sid->set_chip_model( MOS6581 ); } @@ -360,13 +360,13 @@ void SidInstrument::playNote( NotePlayHandle * _n, data8 += m_voice[i]->m_syncModel.value()?2:0; data8 += m_voice[i]->m_ringModModel.value()?4:0; data8 += m_voice[i]->m_testModel.value()?8:0; - switch( m_voice[i]->m_waveFormModel.value() ) + switch( static_cast(m_voice[i]->m_waveFormModel.value()) ) { default: break; - case VoiceObject::NoiseWave: data8 += 128; break; - case VoiceObject::SquareWave: data8 += 64; break; - case VoiceObject::SawWave: data8 += 32; break; - case VoiceObject::TriangleWave: data8 += 16; break; + case VoiceObject::WaveForm::Noise: data8 += 128; break; + case VoiceObject::WaveForm::Square: data8 += 64; break; + case VoiceObject::WaveForm::Saw: data8 += 32; break; + case VoiceObject::WaveForm::Triangle: data8 += 16; break; } sidreg[base+4] = data8&0x00FF; // ad @@ -406,12 +406,12 @@ void SidInstrument::playNote( NotePlayHandle * _n, data8 = data16&0x000F; data8 += m_voice3OffModel.value()?128:0; - switch( m_filterModeModel.value() ) + switch( static_cast(m_filterModeModel.value()) ) { default: break; - case LowPass: data8 += 16; break; - case BandPass: data8 += 32; break; - case HighPass: data8 += 64; break; + case FilterType::LowPass: data8 += 16; break; + case FilterType::BandPass: data8 += 32; break; + case FilterType::HighPass: data8 += 64; break; } sidreg[24] = data8&0x00FF; @@ -459,7 +459,7 @@ class sidKnob : public Knob { public: sidKnob( QWidget * _parent ) : - Knob( knobStyled, _parent ) + Knob( KnobType::Styled, _parent ) { setFixedSize( 16, 16 ); setCenterPointX( 7.5 ); diff --git a/plugins/Sid/SidInstrument.h b/plugins/Sid/SidInstrument.h index 203e52e7b..53efa8942 100644 --- a/plugins/Sid/SidInstrument.h +++ b/plugins/Sid/SidInstrument.h @@ -50,13 +50,15 @@ class VoiceObject : public Model Q_OBJECT MM_OPERATORS public: - enum WaveForm { - SquareWave = 0, - TriangleWave, - SawWave, - NoiseWave, - NumWaveShapes + enum class WaveForm { + Square = 0, + Triangle, + Saw, + Noise, + Count }; + constexpr static auto NumWaveShapes = static_cast(WaveForm::Count); + VoiceObject( Model * _parent, int _idx ); ~VoiceObject() override = default; @@ -82,19 +84,20 @@ class SidInstrument : public Instrument { Q_OBJECT public: - enum FilerType { + enum class FilterType { HighPass = 0, BandPass, LowPass, - NumFilterTypes + Count }; + constexpr static auto NumFilterTypes = static_cast(FilterType::Count); - enum ChipModel { - sidMOS6581 = 0, - sidMOS8580, - NumChipModels + enum class ChipModel { + MOS6581 = 0, + MOS8580, + Count }; - + constexpr static auto NumChipModels = static_cast(ChipModel::Count); SidInstrument( InstrumentTrack * _instrument_track ); ~SidInstrument() override = default; diff --git a/plugins/SpectrumAnalyzer/Analyzer.cpp b/plugins/SpectrumAnalyzer/Analyzer.cpp index de4ac8c68..0bbada7db 100644 --- a/plugins/SpectrumAnalyzer/Analyzer.cpp +++ b/plugins/SpectrumAnalyzer/Analyzer.cpp @@ -48,7 +48,7 @@ extern "C" { QT_TRANSLATE_NOOP("PluginBrowser", "A graphical spectrum analyzer."), "Martin Pavelek ", 0x0112, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/SpectrumAnalyzer/SaControls.cpp b/plugins/SpectrumAnalyzer/SaControls.cpp index 1ebfcdca3..d14176ed3 100644 --- a/plugins/SpectrumAnalyzer/SaControls.cpp +++ b/plugins/SpectrumAnalyzer/SaControls.cpp @@ -66,7 +66,7 @@ SaControls::SaControls(Analyzer *effect) : m_zeroPaddingModel(2.0f, 0.0f, 4.0f, 1.0f, this, tr("FFT zero padding")) { // Frequency and amplitude ranges; order must match - // FREQUENCY_RANGES and AMPLITUDE_RANGES defined in SaControls.h + // FrequencyRange and AmplitudeRange defined in SaControls.h m_freqRangeModel.addItem(tr("Full (auto)")); m_freqRangeModel.addItem(tr("Audible")); m_freqRangeModel.addItem(tr("Bass")); @@ -99,7 +99,7 @@ SaControls::SaControls(Analyzer *effect) : } m_blockSizeModel.setValue(m_blockSizeModel.findText("2048")); - // Window type order must match FFT_WINDOWS defined in fft_helpers.h + // Window type order must match FFTWindow defined in fft_helpers.h m_windowModel.addItem(tr("Rectangular (Off)")); m_windowModel.addItem(tr("Blackman-Harris (Default)")); m_windowModel.addItem(tr("Hamming")); diff --git a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp index c4fc431b1..eb09c793a 100644 --- a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp +++ b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp @@ -236,7 +236,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) controls_layout->setStretchFactor(advanced_widget, 10); // Peak envelope resolution - auto envelopeResolutionKnob = new Knob(knobSmall_17, this); + auto envelopeResolutionKnob = new Knob(KnobType::Small17, this); envelopeResolutionKnob->setModel(&controls->m_envelopeResolutionModel); envelopeResolutionKnob->setLabel(tr("Envelope res.")); envelopeResolutionKnob->setToolTip(tr("Increase envelope resolution for better details, decrease for better GUI performance.")); @@ -244,7 +244,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) advanced_layout->addWidget(envelopeResolutionKnob, 0, 0, 1, 1, Qt::AlignCenter); // Spectrum graph resolution - auto spectrumResolutionKnob = new Knob(knobSmall_17, this); + auto spectrumResolutionKnob = new Knob(KnobType::Small17, this); spectrumResolutionKnob->setModel(&controls->m_spectrumResolutionModel); spectrumResolutionKnob->setLabel(tr("Spectrum res.")); spectrumResolutionKnob->setToolTip(tr("Increase spectrum resolution for better details, decrease for better GUI performance.")); @@ -252,7 +252,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) advanced_layout->addWidget(spectrumResolutionKnob, 1, 0, 1, 1, Qt::AlignCenter); // Peak falloff speed - auto peakDecayFactorKnob = new Knob(knobSmall_17, this); + auto peakDecayFactorKnob = new Knob(KnobType::Small17, this); peakDecayFactorKnob->setModel(&controls->m_peakDecayFactorModel); peakDecayFactorKnob->setLabel(tr("Falloff factor")); peakDecayFactorKnob->setToolTip(tr("Decrease to make peaks fall faster.")); @@ -260,7 +260,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) advanced_layout->addWidget(peakDecayFactorKnob, 0, 1, 1, 1, Qt::AlignCenter); // Averaging weight - auto averagingWeightKnob = new Knob(knobSmall_17, this); + auto averagingWeightKnob = new Knob(KnobType::Small17, this); averagingWeightKnob->setModel(&controls->m_averagingWeightModel); averagingWeightKnob->setLabel(tr("Averaging weight")); averagingWeightKnob->setToolTip(tr("Decrease to make averaging slower and smoother.")); @@ -268,7 +268,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) advanced_layout->addWidget(averagingWeightKnob, 1, 1, 1, 1, Qt::AlignCenter); // Waterfall history size - auto waterfallHeightKnob = new Knob(knobSmall_17, this); + auto waterfallHeightKnob = new Knob(KnobType::Small17, this); waterfallHeightKnob->setModel(&controls->m_waterfallHeightModel); waterfallHeightKnob->setLabel(tr("Waterfall height")); waterfallHeightKnob->setToolTip(tr("Increase to get slower scrolling, decrease to see fast transitions better. Warning: medium CPU usage.")); @@ -278,7 +278,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) connect(&controls->m_waterfallHeightModel, &FloatModel::dataChanged, [=] {processor->reallocateBuffers();}); // Waterfall gamma correction - auto waterfallGammaKnob = new Knob(knobSmall_17, this); + auto waterfallGammaKnob = new Knob(KnobType::Small17, this); waterfallGammaKnob->setModel(&controls->m_waterfallGammaModel); waterfallGammaKnob->setLabel(tr("Waterfall gamma")); waterfallGammaKnob->setToolTip(tr("Decrease to see very weak signals, increase to get better contrast.")); @@ -286,7 +286,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) advanced_layout->addWidget(waterfallGammaKnob, 1, 2, 1, 1, Qt::AlignCenter); // FFT window overlap - auto windowOverlapKnob = new Knob(knobSmall_17, this); + auto windowOverlapKnob = new Knob(KnobType::Small17, this); windowOverlapKnob->setModel(&controls->m_windowOverlapModel); windowOverlapKnob->setLabel(tr("Window overlap")); windowOverlapKnob->setToolTip(tr("Increase to prevent missing fast transitions arriving near FFT window edges. Warning: high CPU usage.")); @@ -294,7 +294,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) advanced_layout->addWidget(windowOverlapKnob, 0, 3, 1, 1, Qt::AlignCenter); // FFT zero padding - auto zeroPaddingKnob = new Knob(knobSmall_17, this); + auto zeroPaddingKnob = new Knob(KnobType::Small17, this); zeroPaddingKnob->setModel(&controls->m_zeroPaddingModel); zeroPaddingKnob->setLabel(tr("Zero padding")); zeroPaddingKnob->setToolTip(tr("Increase to get smoother-looking spectrum. Warning: high CPU usage.")); diff --git a/plugins/SpectrumAnalyzer/SaProcessor.cpp b/plugins/SpectrumAnalyzer/SaProcessor.cpp index d7a1dd5fb..a79d52bdc 100644 --- a/plugins/SpectrumAnalyzer/SaProcessor.cpp +++ b/plugins/SpectrumAnalyzer/SaProcessor.cpp @@ -58,7 +58,7 @@ SaProcessor::SaProcessor(const SaControls *controls) : m_reallocating(false) { m_fftWindow.resize(m_inBlockSize, 1.0); - precomputeWindow(m_fftWindow.data(), m_inBlockSize, BLACKMAN_HARRIS); + precomputeWindow(m_fftWindow.data(), m_inBlockSize, FFTWindow::BlackmanHarris); m_bufferL.resize(m_inBlockSize, 0); m_bufferR.resize(m_inBlockSize, 0); @@ -402,7 +402,7 @@ void SaProcessor::reallocateBuffers() // allocate new space, create new plan and resize containers m_fftWindow.resize(new_in_size, 1.0); - precomputeWindow(m_fftWindow.data(), new_in_size, (FFT_WINDOWS) m_controls->m_windowModel.value()); + precomputeWindow(m_fftWindow.data(), new_in_size, (FFTWindow) m_controls->m_windowModel.value()); m_bufferL.resize(new_in_size, 0); m_bufferR.resize(new_in_size, 0); m_filteredBufferL.resize(new_fft_size, 0); @@ -448,7 +448,7 @@ void SaProcessor::rebuildWindow() { // computation is done in fft_helpers QMutexLocker lock(&m_dataAccess); - precomputeWindow(m_fftWindow.data(), m_inBlockSize, (FFT_WINDOWS) m_controls->m_windowModel.value()); + precomputeWindow(m_fftWindow.data(), m_inBlockSize, (FFTWindow) m_controls->m_windowModel.value()); } @@ -545,28 +545,28 @@ float SaProcessor::binBandwidth() const float SaProcessor::getFreqRangeMin(bool linear) const { - switch (m_controls->m_freqRangeModel.value()) + switch (static_cast(m_controls->m_freqRangeModel.value())) { - case FRANGE_AUDIBLE: return FRANGE_AUDIBLE_START; - case FRANGE_BASS: return FRANGE_BASS_START; - case FRANGE_MIDS: return FRANGE_MIDS_START; - case FRANGE_HIGH: return FRANGE_HIGH_START; + case FrequencyRange::Audible: return FRANGE_AUDIBLE_START; + case FrequencyRange::Bass: return FRANGE_BASS_START; + case FrequencyRange::Mids: return FRANGE_MIDS_START; + case FrequencyRange::High: return FRANGE_HIGH_START; default: - case FRANGE_FULL: return linear ? 0 : LOWEST_LOG_FREQ; + case FrequencyRange::Full: return linear ? 0 : LOWEST_LOG_FREQ; } } float SaProcessor::getFreqRangeMax() const { - switch (m_controls->m_freqRangeModel.value()) + switch (static_cast(m_controls->m_freqRangeModel.value())) { - case FRANGE_AUDIBLE: return FRANGE_AUDIBLE_END; - case FRANGE_BASS: return FRANGE_BASS_END; - case FRANGE_MIDS: return FRANGE_MIDS_END; - case FRANGE_HIGH: return FRANGE_HIGH_END; + case FrequencyRange::Audible: return FRANGE_AUDIBLE_END; + case FrequencyRange::Bass: return FRANGE_BASS_END; + case FrequencyRange::Mids: return FRANGE_MIDS_END; + case FrequencyRange::High: return FRANGE_HIGH_END; default: - case FRANGE_FULL: return getNyquistFreq(); + case FrequencyRange::Full: return getNyquistFreq(); } } @@ -619,26 +619,26 @@ float SaProcessor::getAmpRangeMin(bool linear) const { // return very low limit to make sure zero gets included at linear grid if (linear) {return -900;} - switch (m_controls->m_ampRangeModel.value()) + switch (static_cast(m_controls->m_ampRangeModel.value())) { - case ARANGE_EXTENDED: return ARANGE_EXTENDED_START; - case ARANGE_SILENT: return ARANGE_SILENT_START; - case ARANGE_LOUD: return ARANGE_LOUD_START; + case AmplitudeRange::Extended: return ARANGE_EXTENDED_START; + case AmplitudeRange::Silent: return ARANGE_SILENT_START; + case AmplitudeRange::Loud: return ARANGE_LOUD_START; default: - case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_START; + case AmplitudeRange::Audible: return ARANGE_AUDIBLE_START; } } float SaProcessor::getAmpRangeMax() const { - switch (m_controls->m_ampRangeModel.value()) + switch (static_cast(m_controls->m_ampRangeModel.value())) { - case ARANGE_EXTENDED: return ARANGE_EXTENDED_END; - case ARANGE_SILENT: return ARANGE_SILENT_END; - case ARANGE_LOUD: return ARANGE_LOUD_END; + case AmplitudeRange::Extended: return ARANGE_EXTENDED_END; + case AmplitudeRange::Silent: return ARANGE_SILENT_END; + case AmplitudeRange::Loud: return ARANGE_LOUD_END; default: - case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_END; + case AmplitudeRange::Audible: return ARANGE_AUDIBLE_END; } } diff --git a/plugins/StereoEnhancer/StereoEnhancer.cpp b/plugins/StereoEnhancer/StereoEnhancer.cpp index 01e55f2de..a7937a2ec 100644 --- a/plugins/StereoEnhancer/StereoEnhancer.cpp +++ b/plugins/StereoEnhancer/StereoEnhancer.cpp @@ -43,7 +43,7 @@ Plugin::Descriptor PLUGIN_EXPORT stereoenhancer_plugin_descriptor = "Plugin for enhancing stereo separation of a stereo input file" ), "Lou Herard ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp b/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp index 3f7841706..05c78616e 100644 --- a/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp +++ b/plugins/StereoEnhancer/StereoEnhancerControlDialog.cpp @@ -40,7 +40,7 @@ StereoEnhancerControlDialog::StereoEnhancerControlDialog( { auto l = new QHBoxLayout(this); - auto widthKnob = new Knob(knobBright_26, this); + auto widthKnob = new Knob(KnobType::Bright26, this); widthKnob->setModel( &_controls->m_widthModel ); widthKnob->setLabel( tr( "WIDTH" ) ); widthKnob->setHintText( tr( "Width:" ) , " samples" ); diff --git a/plugins/StereoMatrix/StereoMatrix.cpp b/plugins/StereoMatrix/StereoMatrix.cpp index 2dff69b77..b96d2e107 100644 --- a/plugins/StereoMatrix/StereoMatrix.cpp +++ b/plugins/StereoMatrix/StereoMatrix.cpp @@ -43,7 +43,7 @@ Plugin::Descriptor PLUGIN_EXPORT stereomatrix_plugin_descriptor = "Plugin for freely manipulating stereo output" ), "Paul Giblock ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/StereoMatrix/StereoMatrixControlDialog.cpp b/plugins/StereoMatrix/StereoMatrixControlDialog.cpp index 06ff2a03b..da9a3aa9e 100644 --- a/plugins/StereoMatrix/StereoMatrixControlDialog.cpp +++ b/plugins/StereoMatrix/StereoMatrixControlDialog.cpp @@ -48,22 +48,22 @@ StereoMatrixControlDialog::StereoMatrixControlDialog( PLUGIN_NAME::getIconPixmap( "artwork" ) ); setPalette( pal ); - auto llKnob = new Knob(knobBright_26, this); + auto llKnob = new Knob(KnobType::Bright26, this); llKnob->setModel( &_controls->m_llModel ); llKnob->setHintText( tr( "Left to Left Vol:" ) , "" ); llKnob->move( 10, 79 ); - auto lrKnob = new Knob(knobBright_26, this); + auto lrKnob = new Knob(KnobType::Bright26, this); lrKnob->setModel( &_controls->m_lrModel ); lrKnob->setHintText( tr( "Left to Right Vol:" ) , "" ); lrKnob->move( 48, 79 ); - auto rlKnob = new Knob(knobBright_26, this); + auto rlKnob = new Knob(KnobType::Bright26, this); rlKnob->setModel( &_controls->m_rlModel ); rlKnob->setHintText( tr( "Right to Left Vol:" ) , "" ); rlKnob->move( 85, 79 ); - auto rrKnob = new Knob(knobBright_26, this); + auto rrKnob = new Knob(KnobType::Bright26, this); rrKnob->setModel( &_controls->m_rrModel ); rrKnob->setHintText( tr( "Right to Right Vol:" ) , "" ); rrKnob->move( 123, 79 ); diff --git a/plugins/Stk/Mallets/Mallets.cpp b/plugins/Stk/Mallets/Mallets.cpp index be144e764..4fb077de5 100644 --- a/plugins/Stk/Mallets/Mallets.cpp +++ b/plugins/Stk/Mallets/Mallets.cpp @@ -59,7 +59,7 @@ Plugin::Descriptor PLUGIN_EXPORT malletsstk_plugin_descriptor = "Tuneful things to bang on" ), "Danny McRae ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -409,7 +409,7 @@ MalletsInstrumentView::MalletsInstrumentView( MalletsInstrument * _instrument, connect( &_instrument->m_presetsModel, SIGNAL( dataChanged() ), this, SLOT( changePreset() ) ); - m_spreadKnob = new Knob( knobVintage_32, this ); + m_spreadKnob = new Knob( KnobType::Vintage32, this ); m_spreadKnob->setLabel( tr( "Spread" ) ); m_spreadKnob->move( 190, 140 ); m_spreadKnob->setHintText( tr( "Spread:" ), "" ); @@ -445,27 +445,27 @@ QWidget * MalletsInstrumentView::setupModalBarControls( QWidget * _parent ) auto widget = new QWidget(_parent); widget->setFixedSize( 250, 250 ); - m_hardnessKnob = new Knob( knobVintage_32, widget ); + m_hardnessKnob = new Knob( KnobType::Vintage32, widget ); m_hardnessKnob->setLabel( tr( "Hardness" ) ); m_hardnessKnob->move( 30, 90 ); m_hardnessKnob->setHintText( tr( "Hardness:" ), "" ); - m_positionKnob = new Knob( knobVintage_32, widget ); + m_positionKnob = new Knob( KnobType::Vintage32, widget ); m_positionKnob->setLabel( tr( "Position" ) ); m_positionKnob->move( 110, 90 ); m_positionKnob->setHintText( tr( "Position:" ), "" ); - m_vibratoGainKnob = new Knob( knobVintage_32, widget ); + m_vibratoGainKnob = new Knob( KnobType::Vintage32, widget ); m_vibratoGainKnob->setLabel( tr( "Vibrato gain" ) ); m_vibratoGainKnob->move( 30, 140 ); m_vibratoGainKnob->setHintText( tr( "Vibrato gain:" ), "" ); - m_vibratoFreqKnob = new Knob( knobVintage_32, widget ); + m_vibratoFreqKnob = new Knob( KnobType::Vintage32, widget ); m_vibratoFreqKnob->setLabel( tr( "Vibrato frequency" ) ); m_vibratoFreqKnob->move( 110, 140 ); m_vibratoFreqKnob->setHintText( tr( "Vibrato frequency:" ), "" ); - m_stickKnob = new Knob( knobVintage_32, widget ); + m_stickKnob = new Knob( KnobType::Vintage32, widget ); m_stickKnob->setLabel( tr( "Stick mix" ) ); m_stickKnob->move( 190, 90 ); m_stickKnob->setHintText( tr( "Stick mix:" ), "" ); @@ -481,27 +481,27 @@ QWidget * MalletsInstrumentView::setupTubeBellControls( QWidget * _parent ) auto widget = new QWidget(_parent); widget->setFixedSize( 250, 250 ); - m_modulatorKnob = new Knob( knobVintage_32, widget ); + m_modulatorKnob = new Knob( KnobType::Vintage32, widget ); m_modulatorKnob->setLabel( tr( "Modulator" ) ); m_modulatorKnob->move( 30, 90 ); m_modulatorKnob->setHintText( tr( "Modulator:" ), "" ); - m_crossfadeKnob = new Knob( knobVintage_32, widget ); + m_crossfadeKnob = new Knob( KnobType::Vintage32, widget ); m_crossfadeKnob->setLabel( tr( "Crossfade" ) ); m_crossfadeKnob->move( 110, 90 ); m_crossfadeKnob->setHintText( tr( "Crossfade:" ), "" ); - m_lfoSpeedKnob = new Knob( knobVintage_32, widget ); + m_lfoSpeedKnob = new Knob( KnobType::Vintage32, widget ); m_lfoSpeedKnob->setLabel( tr( "LFO speed" ) ); m_lfoSpeedKnob->move( 30, 140 ); m_lfoSpeedKnob->setHintText( tr( "LFO speed:" ), "" ); - m_lfoDepthKnob = new Knob( knobVintage_32, widget ); + m_lfoDepthKnob = new Knob( KnobType::Vintage32, widget ); m_lfoDepthKnob->setLabel( tr( "LFO depth" ) ); m_lfoDepthKnob->move( 110, 140 ); m_lfoDepthKnob->setHintText( tr( "LFO depth:" ), "" ); - m_adsrKnob = new Knob( knobVintage_32, widget ); + m_adsrKnob = new Knob( KnobType::Vintage32, widget ); m_adsrKnob->setLabel( tr( "ADSR" ) ); m_adsrKnob->move( 190, 90 ); m_adsrKnob->setHintText( tr( "ADSR:" ), "" ); @@ -521,22 +521,22 @@ QWidget * MalletsInstrumentView::setupBandedWGControls( QWidget * _parent ) /* m_strikeLED = new LedCheckBox( tr( "Bowed" ), widget ); m_strikeLED->move( 138, 25 );*/ - m_pressureKnob = new Knob( knobVintage_32, widget ); + m_pressureKnob = new Knob( KnobType::Vintage32, widget ); m_pressureKnob->setLabel( tr( "Pressure" ) ); m_pressureKnob->move( 30, 90 ); m_pressureKnob->setHintText( tr( "Pressure:" ), "" ); -/* m_motionKnob = new Knob( knobVintage_32, widget ); +/* m_motionKnob = new Knob( KnobType::Vintage32, widget ); m_motionKnob->setLabel( tr( "Motion" ) ); m_motionKnob->move( 110, 90 ); m_motionKnob->setHintText( tr( "Motion:" ), "" );*/ - m_velocityKnob = new Knob( knobVintage_32, widget ); + m_velocityKnob = new Knob( KnobType::Vintage32, widget ); m_velocityKnob->setLabel( tr( "Speed" ) ); m_velocityKnob->move( 30, 140 ); m_velocityKnob->setHintText( tr( "Speed:" ), "" ); -/* m_vibratoKnob = new Knob( knobVintage_32, widget, tr( "Vibrato" ) ); +/* m_vibratoKnob = new Knob( KnobType::Vintage32, widget, tr( "Vibrato" ) ); m_vibratoKnob->setLabel( tr( "Vibrato" ) ); m_vibratoKnob->move( 110, 140 ); m_vibratoKnob->setHintText( tr( "Vibrato:" ), "" );*/ diff --git a/plugins/TripleOscillator/TripleOscillator.cpp b/plugins/TripleOscillator/TripleOscillator.cpp index c66247541..f2340d3d6 100644 --- a/plugins/TripleOscillator/TripleOscillator.cpp +++ b/plugins/TripleOscillator/TripleOscillator.cpp @@ -57,7 +57,7 @@ Plugin::Descriptor PLUGIN_EXPORT tripleoscillator_plugin_descriptor = "in several ways" ), "Tobias Doerffel ", 0x0110, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -84,10 +84,10 @@ OscillatorObject::OscillatorObject( Model * _parent, int _idx ) : tr( "Osc %1 phase-offset" ).arg( _idx+1 ) ), m_stereoPhaseDetuningModel( 0.0f, 0.0f, 360.0f, 1.0f, this, tr( "Osc %1 stereo phase-detuning" ).arg( _idx+1 ) ), - m_waveShapeModel( Oscillator::SineWave, 0, + m_waveShapeModel( static_cast(Oscillator::WaveShape::Sine), 0, Oscillator::NumWaveShapes-1, this, tr( "Osc %1 wave shape" ).arg( _idx+1 ) ), - m_modulationAlgoModel( Oscillator::SignalMix, 0, + m_modulationAlgoModel( static_cast(Oscillator::ModulationAlgo::SignalMix), 0, Oscillator::NumModulationAlgos-1, this, tr( "Modulation type %1" ).arg( _idx+1 ) ), m_useWaveTableModel(true), @@ -426,7 +426,7 @@ class TripleOscKnob : public Knob { public: TripleOscKnob( QWidget * _parent ) : - Knob( knobStyled, _parent ) + Knob( KnobType::Styled, _parent ) { setFixedSize( 28, 35 ); } @@ -554,7 +554,7 @@ TripleOscillatorView::TripleOscillatorView( Instrument * _instrument, int knob_y = osc_y + i * osc_h; // setup volume-knob - auto vk = new Knob(knobStyled, this); + auto vk = new Knob(KnobType::Styled, this); vk->setVolumeKnob( true ); vk->setFixedSize( 28, 35 ); vk->move( 6, knob_y ); diff --git a/plugins/Vectorscope/VecControlsDialog.cpp b/plugins/Vectorscope/VecControlsDialog.cpp index 97898fe70..9aa2cfd8d 100644 --- a/plugins/Vectorscope/VecControlsDialog.cpp +++ b/plugins/Vectorscope/VecControlsDialog.cpp @@ -79,7 +79,7 @@ VecControlsDialog::VecControlsDialog(VecControls *controls) : config_layout->addStretch(); // Persistence knob - auto persistenceKnob = new Knob(knobSmall_17, this); + auto persistenceKnob = new Knob(KnobType::Small17, this); persistenceKnob->setModel(&controls->m_persistenceModel); persistenceKnob->setLabel(tr("Persist.")); persistenceKnob->setToolTip(tr("Trace persistence: higher amount means the trace will stay bright for longer time.")); diff --git a/plugins/Vectorscope/Vectorscope.cpp b/plugins/Vectorscope/Vectorscope.cpp index b9880691c..f843fc86d 100644 --- a/plugins/Vectorscope/Vectorscope.cpp +++ b/plugins/Vectorscope/Vectorscope.cpp @@ -39,7 +39,7 @@ extern "C" { QT_TRANSLATE_NOOP("PluginBrowser", "A stereo field visualizer."), "Martin Pavelek ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp index eab3693b8..dd8e9cbef 100644 --- a/plugins/Vestige/Vestige.cpp +++ b/plugins/Vestige/Vestige.cpp @@ -77,7 +77,7 @@ Plugin::Descriptor Q_DECL_EXPORT vestige_plugin_descriptor = "VST-host for using VST(i)-plugins within LMMS" ), "Tobias Doerffel ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), #ifdef LMMS_BUILD_LINUX "dll,so", @@ -185,8 +185,8 @@ VestigeInstrument::~VestigeInstrument() } Engine::audioEngine()->removePlayHandlesOfTypes( instrumentTrack(), - PlayHandle::TypeNotePlayHandle - | PlayHandle::TypeInstrumentPlayHandle ); + PlayHandle::Type::NotePlayHandle + | PlayHandle::Type::InstrumentPlayHandle ); closePlugin(); } @@ -1004,7 +1004,7 @@ ManageVestigeInstrumentView::ManageVestigeInstrumentView( Instrument * _instrume sprintf(paramStr.data(), "param%d", i); s_dumpValues = dump[paramStr.data()].split(":"); - vstKnobs[ i ] = new CustomTextKnob( knobBright_26, this, s_dumpValues.at( 1 ) ); + vstKnobs[ i ] = new CustomTextKnob( KnobType::Bright26, this, s_dumpValues.at( 1 ) ); vstKnobs[ i ]->setDescription( s_dumpValues.at( 1 ) + ":" ); vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); diff --git a/plugins/Vestige/Vestige.h b/plugins/Vestige/Vestige.h index 0a36c4924..f740913ea 100644 --- a/plugins/Vestige/Vestige.h +++ b/plugins/Vestige/Vestige.h @@ -72,7 +72,7 @@ public: virtual Flags flags() const { - return IsSingleStreamed | IsMidiBased; + return Flag::IsSingleStreamed | Flag::IsMidiBased; } virtual bool handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset = 0 ); diff --git a/plugins/Vibed/Vibed.cpp b/plugins/Vibed/Vibed.cpp index 014ab1429..3ed51fe79 100644 --- a/plugins/Vibed/Vibed.cpp +++ b/plugins/Vibed/Vibed.cpp @@ -56,7 +56,7 @@ Plugin::Descriptor PLUGIN_EXPORT vibedstrings_plugin_descriptor = QT_TRANSLATE_NOOP("PluginBrowser", "Vibrating string modeler"), "Danny McRae ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader("logo"), nullptr, nullptr @@ -272,14 +272,14 @@ namespace gui VibedView::VibedView(Instrument* instrument, QWidget* parent) : InstrumentViewFixedSize(instrument, parent), - m_volumeKnob(knobBright_26, this), - m_stiffnessKnob(knobBright_26, this), - m_pickKnob(knobBright_26, this), - m_pickupKnob(knobBright_26, this), - m_panKnob(knobBright_26, this), - m_detuneKnob(knobBright_26, this), - m_randomKnob(knobBright_26, this), - m_lengthKnob(knobBright_26, this), + m_volumeKnob(KnobType::Bright26, this), + m_stiffnessKnob(KnobType::Bright26, this), + m_pickKnob(KnobType::Bright26, this), + m_pickupKnob(KnobType::Bright26, this), + m_panKnob(KnobType::Bright26, this), + m_detuneKnob(KnobType::Bright26, this), + m_randomKnob(KnobType::Bright26, this), + m_lengthKnob(KnobType::Bright26, this), m_graph(this), m_impulse("", this), m_power("", this, tr("Enable waveform")), diff --git a/plugins/Vibed/Vibed.h b/plugins/Vibed/Vibed.h index 308ae64d5..18d334c4d 100644 --- a/plugins/Vibed/Vibed.h +++ b/plugins/Vibed/Vibed.h @@ -65,7 +65,7 @@ public: QString nodeName() const override; - Flags flags() const override { return IsNotBendable; } + Flags flags() const override { return Flag::IsNotBendable; } gui::PluginView* instantiateView(QWidget* parent) override; diff --git a/plugins/VstBase/RemoteVstPlugin.cpp b/plugins/VstBase/RemoteVstPlugin.cpp index aaa88f6c8..0ec60bea4 100644 --- a/plugins/VstBase/RemoteVstPlugin.cpp +++ b/plugins/VstBase/RemoteVstPlugin.cpp @@ -127,7 +127,7 @@ struct ERect using namespace std; -static lmms::VstHostLanguages hlang = lmms::LanguageEnglish; +static lmms::VstHostLanguage hlang = lmms::VstHostLanguage::English; static bool EMBED = false; static bool EMBED_X11 = false; @@ -390,7 +390,7 @@ public: #endif private: - enum GuiThreadMessages + enum class GuiThreadMessage { None, ProcessPluginMessage, @@ -628,7 +628,7 @@ bool RemoteVstPlugin::processMessage( const message & _m ) break; case IdVstSetLanguage: - hlang = static_cast( _m.getInt() ); + hlang = static_cast( _m.getInt() ); break; case IdVstGetParameterDump: @@ -1763,7 +1763,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, // call application idle routine (this will // call effEditIdle for all open editors too) #ifndef NATIVE_LINUX_VST - PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 ); + PostMessage( __MessageHwnd, WM_USER, static_cast(GuiThreadMessage::GiveIdle), 0 ); #else __plugin->sendX11Idle(); #endif @@ -2066,7 +2066,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, case audioMasterGetLanguage: SHOW_CALLBACK( "amc: audioMasterGetLanguage\n" ); - return hlang; + return static_cast(hlang); case audioMasterGetDirectory: SHOW_CALLBACK( "amc: audioMasterGetDirectory\n" ); @@ -2077,7 +2077,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, SHOW_CALLBACK( "amc: audioMasterUpdateDisplay\n" ); // something has changed, update 'multi-fx' display #ifndef NATIVE_LINUX_VST - PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 ); + PostMessage( __MessageHwnd, WM_USER, static_cast(GuiThreadMessage::GiveIdle), 0 ); #else __plugin->sendX11Idle(); #endif @@ -2234,7 +2234,7 @@ void * RemoteVstPlugin::processingThread(void * _param) #ifndef NATIVE_LINUX_VST PostMessage( __MessageHwnd, WM_USER, - ProcessPluginMessage, + static_cast(GuiThreadMessage::ProcessPluginMessage), (LPARAM) new message( m ) ); #else _this->queueMessage( m ); @@ -2244,7 +2244,7 @@ void * RemoteVstPlugin::processingThread(void * _param) // notify GUI thread about shutdown #ifndef NATIVE_LINUX_VST - PostMessage( __MessageHwnd, WM_USER, ClosePlugin, 0 ); + PostMessage( __MessageHwnd, WM_USER, static_cast(GuiThreadMessage::ClosePlugin), 0 ); return 0; #else @@ -2349,9 +2349,9 @@ LRESULT CALLBACK RemoteVstPlugin::wndProc( HWND hwnd, UINT uMsg, } else if( uMsg == WM_USER ) { - switch( wParam ) + switch( static_cast(wParam) ) { - case ProcessPluginMessage: + case GuiThreadMessage::ProcessPluginMessage: { message * m = (message *) lParam; __plugin->queueMessage( *m ); @@ -2363,11 +2363,11 @@ LRESULT CALLBACK RemoteVstPlugin::wndProc( HWND hwnd, UINT uMsg, return 0; } - case GiveIdle: + case GuiThreadMessage::GiveIdle: __plugin->idle(); return 0; - case ClosePlugin: + case GuiThreadMessage::ClosePlugin: PostQuitMessage(0); return 0; diff --git a/plugins/VstBase/VstPlugin.cpp b/plugins/VstBase/VstPlugin.cpp index c3fe17d72..b23ae39bf 100644 --- a/plugins/VstBase/VstPlugin.cpp +++ b/plugins/VstBase/VstPlugin.cpp @@ -216,18 +216,18 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) lock(); - VstHostLanguages hlang = LanguageEnglish; + VstHostLanguage hlang = VstHostLanguage::English; switch( QLocale::system().language() ) { - case QLocale::French: hlang = LanguageFrench; break; - case QLocale::German: hlang = LanguageGerman; break; - case QLocale::Italian: hlang = LanguageItalian; break; - case QLocale::Japanese: hlang = LanguageJapanese; break; - case QLocale::Korean: hlang = LanguageKorean; break; - case QLocale::Spanish: hlang = LanguageSpanish; break; + case QLocale::French: hlang = VstHostLanguage::French; break; + case QLocale::German: hlang = VstHostLanguage::German; break; + case QLocale::Italian: hlang = VstHostLanguage::Italian; break; + case QLocale::Japanese: hlang = VstHostLanguage::Japanese; break; + case QLocale::Korean: hlang = VstHostLanguage::Korean; break; + case QLocale::Spanish: hlang = VstHostLanguage::Spanish; break; default: break; } - sendMessage( message( IdVstSetLanguage ).addInt( hlang ) ); + sendMessage( message( IdVstSetLanguage ).addInt( static_cast(hlang) ) ); sendMessage( message( IdVstLoadPlugin ).addString( QSTR_TO_STDSTR( m_plugin ) ) ); waitForInitDone(); diff --git a/plugins/VstBase/communication.h b/plugins/VstBase/communication.h index 1f32dd135..50351d38e 100644 --- a/plugins/VstBase/communication.h +++ b/plugins/VstBase/communication.h @@ -40,15 +40,15 @@ struct VstParameterDumpItem -enum VstHostLanguages +enum class VstHostLanguage { - LanguageEnglish = 1, - LanguageGerman, - LanguageFrench, - LanguageItalian, - LanguageSpanish, - LanguageJapanese, - LanguageKorean + English = 1, + German, + French, + Italian, + Spanish, + Japanese, + Korean } ; diff --git a/plugins/VstBase/vst_base.cpp b/plugins/VstBase/vst_base.cpp index 80cb9d736..154dca975 100644 --- a/plugins/VstBase/vst_base.cpp +++ b/plugins/VstBase/vst_base.cpp @@ -41,7 +41,7 @@ Plugin::Descriptor VSTBASE_EXPORT vstbase_plugin_descriptor = "library for all LMMS plugins dealing with VST-plugins", "Tobias Doerffel ", 0x0100, - Plugin::Library, + Plugin::Type::Library, nullptr, nullptr, } ; diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index c3e40cfa7..bdbdea806 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -49,10 +49,10 @@ Plugin::Descriptor PLUGIN_EXPORT vsteffect_plugin_descriptor = "plugin for using arbitrary VST effects inside LMMS." ), "Tobias Doerffel ", 0x0200, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, - new VstSubPluginFeatures( Plugin::Effect ) + new VstSubPluginFeatures( Plugin::Type::Effect ) } ; } diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 52160af57..5bee94155 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -48,6 +48,16 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : m_plugin( nullptr ), tbLabel( nullptr ) { +#if QT_VERSION < 0x50C00 + // Workaround for a bug in Qt versions below 5.12, + // where argument-dependent-lookup fails for QFlags operators + // declared inside a namepsace. + // This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h + // See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348 + + using ::operator|; +#endif + auto l = new QGridLayout(this); l->setContentsMargins( 10, 10, 10, 10 ); l->setVerticalSpacing( 2 ); diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index e73530a09..cf0c831a6 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -316,6 +316,16 @@ namespace gui ManageVSTEffectView::ManageVSTEffectView( VstEffect * _eff, VstEffectControls * m_vi ) : m_effect( _eff ) { +#if QT_VERSION < 0x50C00 + // Workaround for a bug in Qt versions below 5.12, + // where argument-dependent-lookup fails for QFlags operators + // declared inside a namepsace. + // This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h + // See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348 + + using ::operator|; +#endif + m_vi2 = m_vi; widget = new QWidget(); m_vi->m_scrollArea = new QScrollArea( widget ); @@ -379,7 +389,7 @@ ManageVSTEffectView::ManageVSTEffectView( VstEffect * _eff, VstEffectControls * sprintf(paramStr.data(), "param%d", i); s_dumpValues = dump[paramStr.data()].split(":"); - vstKnobs[ i ] = new CustomTextKnob( knobBright_26, widget, s_dumpValues.at( 1 ) ); + vstKnobs[ i ] = new CustomTextKnob( KnobType::Bright26, widget, s_dumpValues.at( 1 ) ); vstKnobs[ i ]->setDescription( s_dumpValues.at( 1 ) + ":" ); vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) ); diff --git a/plugins/VstEffect/VstSubPluginFeatures.cpp b/plugins/VstEffect/VstSubPluginFeatures.cpp index e60fee0c1..f929b5526 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.cpp +++ b/plugins/VstEffect/VstSubPluginFeatures.cpp @@ -34,7 +34,7 @@ namespace lmms { -VstSubPluginFeatures::VstSubPluginFeatures( Plugin::PluginTypes _type ) : +VstSubPluginFeatures::VstSubPluginFeatures( Plugin::Type _type ) : SubPluginFeatures( _type ) { } diff --git a/plugins/VstEffect/VstSubPluginFeatures.h b/plugins/VstEffect/VstSubPluginFeatures.h index c5dc87d14..a5673dfb7 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.h +++ b/plugins/VstEffect/VstSubPluginFeatures.h @@ -38,7 +38,7 @@ namespace lmms class VstSubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures { public: - VstSubPluginFeatures( Plugin::PluginTypes _type ); + VstSubPluginFeatures( Plugin::Type _type ); void fillDescriptionWidget( QWidget * _parent, const Key * _key ) const override; diff --git a/plugins/Watsyn/Watsyn.cpp b/plugins/Watsyn/Watsyn.cpp index 7916eb45f..7603a9c1b 100644 --- a/plugins/Watsyn/Watsyn.cpp +++ b/plugins/Watsyn/Watsyn.cpp @@ -52,7 +52,7 @@ Plugin::Descriptor PLUGIN_EXPORT watsyn_plugin_descriptor = "4-oscillator modulatable wavetable synth" ), "Vesa Kivimäki ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), nullptr, nullptr, @@ -816,7 +816,7 @@ WatsynView::WatsynView( Instrument * _instrument, pal = QPalette(); pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap("wavegraph") ); // a1 graph - a1_graph = new Graph( this, Graph::LinearStyle, 224, 105 ); + a1_graph = new Graph( this, Graph::Style::Linear, 224, 105 ); a1_graph->move( 4, 141 ); a1_graph->setAutoFillBackground( true ); a1_graph->setGraphColor( QColor( 0x43, 0xb2, 0xff ) ); @@ -824,7 +824,7 @@ WatsynView::WatsynView( Instrument * _instrument, a1_graph->setPalette( pal ); // a2 graph - a2_graph = new Graph( this, Graph::LinearStyle, 224, 105 ); + a2_graph = new Graph( this, Graph::Style::Linear, 224, 105 ); a2_graph->move( 4, 141 ); a2_graph->setAutoFillBackground( true ); a2_graph->setGraphColor( QColor( 0x43, 0xb2, 0xff ) ); @@ -832,7 +832,7 @@ WatsynView::WatsynView( Instrument * _instrument, a2_graph->setPalette( pal ); // b1 graph - b1_graph = new Graph( this, Graph::LinearStyle, 224, 105 ); + b1_graph = new Graph( this, Graph::Style::Linear, 224, 105 ); b1_graph->move( 4, 141 ); b1_graph->setAutoFillBackground( true ); b1_graph->setGraphColor( QColor( 0xfc, 0x54, 0x31 ) ); @@ -840,7 +840,7 @@ WatsynView::WatsynView( Instrument * _instrument, b1_graph->setPalette( pal ); // b2 graph - b2_graph = new Graph( this, Graph::LinearStyle, 224, 105 ); + b2_graph = new Graph( this, Graph::Style::Linear, 224, 105 ); b2_graph->move( 4, 141 ); b2_graph->setAutoFillBackground( true ); b2_graph->setGraphColor( QColor( 0xfc, 0x54, 0x31 ) ); diff --git a/plugins/Watsyn/Watsyn.h b/plugins/Watsyn/Watsyn.h index 3c69be06f..3a736e162 100644 --- a/plugins/Watsyn/Watsyn.h +++ b/plugins/Watsyn/Watsyn.h @@ -39,14 +39,14 @@ namespace lmms #define makeknob( name, x, y, hint, unit, oname ) \ - name = new Knob( knobStyled, this ); \ + name = new Knob( KnobType::Styled, this ); \ name ->move( x, y ); \ name ->setHintText( hint, unit ); \ name ->setObjectName( oname ); \ name ->setFixedSize( 19, 19 ); #define maketsknob( name, x, y, hint, unit, oname ) \ - name = new TempoSyncKnob( knobStyled, this ); \ + name = new TempoSyncKnob( KnobType::Styled, this ); \ name ->move( x, y ); \ name ->setHintText( hint, unit ); \ name ->setObjectName( oname ); \ diff --git a/plugins/WaveShaper/WaveShaper.cpp b/plugins/WaveShaper/WaveShaper.cpp index 94845e672..acd5a933b 100644 --- a/plugins/WaveShaper/WaveShaper.cpp +++ b/plugins/WaveShaper/WaveShaper.cpp @@ -46,7 +46,7 @@ Plugin::Descriptor PLUGIN_EXPORT waveshaper_plugin_descriptor = "plugin for waveshaping" ), "Vesa Kivimäki ", 0x0100, - Plugin::Effect, + Plugin::Type::Effect, new PluginPixmapLoader("logo"), nullptr, nullptr, diff --git a/plugins/WaveShaper/WaveShaperControlDialog.cpp b/plugins/WaveShaper/WaveShaperControlDialog.cpp index 5ef061fdb..045f84763 100644 --- a/plugins/WaveShaper/WaveShaperControlDialog.cpp +++ b/plugins/WaveShaper/WaveShaperControlDialog.cpp @@ -48,7 +48,7 @@ WaveShaperControlDialog::WaveShaperControlDialog( setPalette( pal ); setFixedSize( 224, 274 ); - auto waveGraph = new Graph(this, Graph::LinearNonCyclicStyle, 204, 205); + auto waveGraph = new Graph(this, Graph::Style::LinearNonCyclic, 204, 205); waveGraph -> move( 10, 6 ); waveGraph -> setModel( &_controls -> m_wavegraphModel ); waveGraph -> setAutoFillBackground( true ); @@ -59,7 +59,7 @@ WaveShaperControlDialog::WaveShaperControlDialog( waveGraph->setGraphColor( QColor( 85, 204, 145 ) ); waveGraph -> setMaximumSize( 204, 205 ); - auto inputKnob = new Knob(knobBright_26, this); + auto inputKnob = new Knob(KnobType::Bright26, this); inputKnob -> setVolumeKnob( true ); inputKnob -> setVolumeRatio( 1.0 ); inputKnob -> move( 26, 225 ); @@ -67,7 +67,7 @@ WaveShaperControlDialog::WaveShaperControlDialog( inputKnob->setLabel( tr( "INPUT" ) ); inputKnob->setHintText( tr( "Input gain:" ) , "" ); - auto outputKnob = new Knob(knobBright_26, this); + auto outputKnob = new Knob(KnobType::Bright26, this); outputKnob -> setVolumeKnob( true ); outputKnob -> setVolumeRatio( 1.0 ); outputKnob -> move( 76, 225 ); @@ -103,7 +103,7 @@ WaveShaperControlDialog::WaveShaperControlDialog( subOneButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "sub1_inactive" ) ); subOneButton->setToolTip(tr("Decrease wavegraph amplitude by 1 dB")); - auto clipInputToggle = new LedCheckBox("Clip input", this, tr("Clip input"), LedCheckBox::Green); + auto clipInputToggle = new LedCheckBox("Clip input", this, tr("Clip input"), LedCheckBox::LedColor::Green); clipInputToggle -> move( 131, 252 ); clipInputToggle -> setModel( &_controls -> m_clipModel ); clipInputToggle->setToolTip(tr("Clip input signal to 0 dB")); diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index 3cc5564e6..b1a17a1ce 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -57,7 +57,7 @@ extern "C" { Plugin::Descriptor PLUGIN_EXPORT xpressive_plugin_descriptor = { LMMS_STRINGIFY( PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("PluginBrowser", "Mathematical expression parser"), "Orr Dvori", 0x0100, - Plugin::Instrument, new PluginPixmapLoader("logo"), nullptr, nullptr }; + Plugin::Type::Instrument, new PluginPixmapLoader("logo"), nullptr, nullptr }; } @@ -291,11 +291,11 @@ public: setLineWidth(3); } XpressiveKnob(QWidget * _parent, const QString & _name) : - Knob(knobStyled, _parent,_name) { + Knob(KnobType::Styled, _parent,_name) { setStyle(); } XpressiveKnob(QWidget * _parent) : - Knob(knobStyled, _parent) { + Knob(KnobType::Styled, _parent) { setStyle(); } @@ -325,7 +325,7 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); setPalette(pal); - m_graph = new Graph(this, Graph::LinearStyle, 180, 81); + m_graph = new Graph(this, Graph::Style::Linear, 180, 81); m_graph->move(3, BASE_START + 1); m_graph->setAutoFillBackground(true); m_graph->setGraphColor(QColor(255, 255, 255)); @@ -447,11 +447,11 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : m_waveInterpolate = new LedCheckBox("Interpolate", this, tr("WaveInterpolate"), - LedCheckBox::Green); + LedCheckBox::LedColor::Green); m_waveInterpolate->move(2, 230); m_expressionValidToggle = new LedCheckBox("", this, tr("ExpressionValid"), - LedCheckBox::Red); + LedCheckBox::LedColor::Red); m_expressionValidToggle->move(168, EXPR_TEXT_Y+EXPR_TEXT_H-2); m_expressionValidToggle->setEnabled( false ); @@ -485,7 +485,7 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : - m_smoothKnob=new Knob(knobStyled, this, "Smoothness"); + m_smoothKnob=new Knob(KnobType::Styled, this, "Smoothness"); m_smoothKnob->setFixedSize(25, 25); m_smoothKnob->setCenterPointX(12.5); m_smoothKnob->setCenterPointY(12.5); diff --git a/plugins/ZynAddSubFx/ZynAddSubFx.cpp b/plugins/ZynAddSubFx/ZynAddSubFx.cpp index d3d23c65f..2ec864592 100644 --- a/plugins/ZynAddSubFx/ZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/ZynAddSubFx.cpp @@ -66,7 +66,7 @@ Plugin::Descriptor PLUGIN_EXPORT zynaddsubfx_plugin_descriptor = "Embedded ZynAddSubFX" ), "Tobias Doerffel ", 0x0100, - Plugin::Instrument, + Plugin::Type::Instrument, new PluginPixmapLoader( "logo" ), "xiz", nullptr, @@ -151,8 +151,8 @@ ZynAddSubFxInstrument::ZynAddSubFxInstrument( ZynAddSubFxInstrument::~ZynAddSubFxInstrument() { Engine::audioEngine()->removePlayHandlesOfTypes( instrumentTrack(), - PlayHandle::TypeNotePlayHandle - | PlayHandle::TypeInstrumentPlayHandle ); + PlayHandle::Type::NotePlayHandle + | PlayHandle::Type::InstrumentPlayHandle ); m_pluginMutex.lock(); delete m_plugin; @@ -380,7 +380,7 @@ bool ZynAddSubFxInstrument::handleMidiEvent( const MidiEvent& event, const TimeP void ZynAddSubFxInstrument::reloadPlugin() { // save state of current plugin instance - DataFile m( DataFile::InstrumentTrackSettings ); + DataFile m( DataFile::Type::InstrumentTrackSettings ); saveSettings( m, m.content() ); // init plugin (will delete current one and create a new instance) @@ -508,31 +508,31 @@ ZynAddSubFxView::ZynAddSubFxView( Instrument * _instrument, QWidget * _parent ) l->setVerticalSpacing( 16 ); l->setHorizontalSpacing( 10 ); - m_portamento = new Knob( knobBright_26, this ); + m_portamento = new Knob( KnobType::Bright26, this ); m_portamento->setHintText( tr( "Portamento:" ), "" ); m_portamento->setLabel( tr( "PORT" ) ); - m_filterFreq = new Knob( knobBright_26, this ); + m_filterFreq = new Knob( KnobType::Bright26, this ); m_filterFreq->setHintText( tr( "Filter frequency:" ), "" ); m_filterFreq->setLabel( tr( "FREQ" ) ); - m_filterQ = new Knob( knobBright_26, this ); + m_filterQ = new Knob( KnobType::Bright26, this ); m_filterQ->setHintText( tr( "Filter resonance:" ), "" ); m_filterQ->setLabel( tr( "RES" ) ); - m_bandwidth = new Knob( knobBright_26, this ); + m_bandwidth = new Knob( KnobType::Bright26, this ); m_bandwidth->setHintText( tr( "Bandwidth:" ), "" ); m_bandwidth->setLabel( tr( "BW" ) ); - m_fmGain = new Knob( knobBright_26, this ); + m_fmGain = new Knob( KnobType::Bright26, this ); m_fmGain->setHintText( tr( "FM gain:" ), "" ); m_fmGain->setLabel( tr( "FM GAIN" ) ); - m_resCenterFreq = new Knob( knobBright_26, this ); + m_resCenterFreq = new Knob( KnobType::Bright26, this ); m_resCenterFreq->setHintText( tr( "Resonance center frequency:" ), "" ); m_resCenterFreq->setLabel( tr( "RES CF" ) ); - m_resBandwidth = new Knob( knobBright_26, this ); + m_resBandwidth = new Knob( KnobType::Bright26, this ); m_resBandwidth->setHintText( tr( "Resonance bandwidth:" ), "" ); m_resBandwidth->setLabel( tr( "RES BW" ) ); diff --git a/plugins/ZynAddSubFx/ZynAddSubFx.h b/plugins/ZynAddSubFx/ZynAddSubFx.h index 996c187ee..a391203f3 100644 --- a/plugins/ZynAddSubFx/ZynAddSubFx.h +++ b/plugins/ZynAddSubFx/ZynAddSubFx.h @@ -88,7 +88,7 @@ public: Flags flags() const override { - return IsSingleStreamed | IsMidiBased; + return Flag::IsSingleStreamed | Flag::IsMidiBased; } gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 59c7f2cd2..21a9a3598 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -81,7 +81,7 @@ AudioEngine::AudioEngine( bool renderOnly ) : m_workers(), m_numWorkers( QThread::idealThreadCount()-1 ), m_newPlayHandles( PlayHandle::MaxNumber ), - m_qualitySettings( qualitySettings::Mode_Draft ), + m_qualitySettings( qualitySettings::Mode::Draft ), m_masterGain( 1.0f ), m_isProcessing( false ), m_audioDev( nullptr ), @@ -357,7 +357,7 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer() if( it != m_playHandles.end() ) { ( *it )->audioPort()->removePlayHandle( ( *it ) ); - if( ( *it )->type() == PlayHandle::TypeNotePlayHandle ) + if( ( *it )->type() == PlayHandle::Type::NotePlayHandle ) { NotePlayHandleManager::release( (NotePlayHandle*) *it ); } @@ -405,7 +405,7 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer() if( ( *it )->isFinished() ) { ( *it )->audioPort()->removePlayHandle( ( *it ) ); - if( ( *it )->type() == PlayHandle::TypeNotePlayHandle ) + if( ( *it )->type() == PlayHandle::Type::NotePlayHandle ) { NotePlayHandleManager::release( (NotePlayHandle*) *it ); } @@ -464,12 +464,12 @@ void AudioEngine::handleMetronome() static tick_t lastMetroTicks = -1; Song * song = Engine::getSong(); - Song::PlayModes currentPlayMode = song->playMode(); + Song::PlayMode currentPlayMode = song->playMode(); bool metronomeSupported = - currentPlayMode == Song::Mode_PlayMidiClip - || currentPlayMode == Song::Mode_PlaySong - || currentPlayMode == Song::Mode_PlayPattern; + currentPlayMode == Song::PlayMode::MidiClip + || currentPlayMode == Song::PlayMode::Song + || currentPlayMode == Song::PlayMode::Pattern; if (!metronomeSupported || !m_metronomeActive || song->isExporting()) { @@ -534,7 +534,7 @@ void AudioEngine::clearInternal() // TODO: m_midiClient->noteOffAll(); for (auto ph : m_playHandles) { - if (ph->type() != PlayHandle::TypeInstrumentPlayHandle) + if (ph->type() != PlayHandle::Type::InstrumentPlayHandle) { m_playHandlesToRemove.push_back(ph); } @@ -681,7 +681,7 @@ bool AudioEngine::addPlayHandle( PlayHandle* handle ) return true; } - if( handle->type() == PlayHandle::TypeNotePlayHandle ) + if( handle->type() == PlayHandle::Type::NotePlayHandle ) { NotePlayHandleManager::release( (NotePlayHandle*)handle ); } @@ -732,7 +732,7 @@ void AudioEngine::removePlayHandle(PlayHandle * ph) // (See tobydox's 2008 commit 4583e48) if ( removedFromList ) { - if (ph->type() == PlayHandle::TypeNotePlayHandle) + if (ph->type() == PlayHandle::Type::NotePlayHandle) { NotePlayHandleManager::release(dynamic_cast(ph)); } @@ -749,7 +749,7 @@ void AudioEngine::removePlayHandle(PlayHandle * ph) -void AudioEngine::removePlayHandlesOfTypes(Track * track, const quint8 types) +void AudioEngine::removePlayHandlesOfTypes(Track * track, PlayHandle::Types types) { requestChangeInModel(); PlayHandleList::Iterator it = m_playHandles.begin(); @@ -758,7 +758,7 @@ void AudioEngine::removePlayHandlesOfTypes(Track * track, const quint8 types) if ((*it)->isFromTrack(track) && ((*it)->type() & types)) { ( *it )->audioPort()->removePlayHandle( ( *it ) ); - if( ( *it )->type() == PlayHandle::TypeNotePlayHandle ) + if( ( *it )->type() == PlayHandle::Type::NotePlayHandle ) { NotePlayHandleManager::release( (NotePlayHandle*) *it ); } diff --git a/src/core/AudioEngineWorkerThread.cpp b/src/core/AudioEngineWorkerThread.cpp index 129b3accf..528841c71 100644 --- a/src/core/AudioEngineWorkerThread.cpp +++ b/src/core/AudioEngineWorkerThread.cpp @@ -91,7 +91,7 @@ void AudioEngineWorkerThread::JobQueue::run() } } // always exit loop if we're not in dynamic mode - processedJob = processedJob && ( m_opMode == Dynamic ); + processedJob = processedJob && ( m_opMode == OperationMode::Dynamic ); } } diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index c25e813cb..e46a864f8 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -44,7 +44,7 @@ AutomatableModel::AutomatableModel( const float val, const float min, const float max, const float step, Model* parent, const QString & displayName, bool defaultConstructed ) : Model( parent, displayName, defaultConstructed ), - m_scaleType( Linear ), + m_scaleType( ScaleType::Linear ), m_minValue( min ), m_maxValue( max ), m_step( step ), @@ -105,7 +105,7 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co { bool mustQuote = mustQuoteName(name); - if( isAutomated() || m_scaleType != Linear ) + if( isAutomated() || m_scaleType != ScaleType::Linear ) { // automation needs tuple of data (name, id, value) // scale type also needs an extra value @@ -114,7 +114,7 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co QDomElement me = doc.createElement( mustQuote ? QString("automatablemodel") : name ); me.setAttribute( "id", ProjectJournal::idToSave( id() ) ); me.setAttribute( "value", m_value ); - me.setAttribute( "scale_type", m_scaleType == Logarithmic ? "log" : "linear" ); + me.setAttribute( "scale_type", m_scaleType == ScaleType::Logarithmic ? "log" : "linear" ); if(mustQuote) { me.setAttribute( "nodename", name ); } @@ -140,11 +140,11 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co // the discardMIDIConnections option is true. auto controllerType = m_controllerConnection ? m_controllerConnection->getController()->type() - : Controller::DummyController; + : Controller::ControllerType::Dummy; bool skipMidiController = Engine::getSong()->isSavingProject() && Engine::getSong()->getSaveOptions().discardMIDIConnections.value(); - if (m_controllerConnection && controllerType != Controller::DummyController - && !(skipMidiController && controllerType == Controller::MidiController)) + if (m_controllerConnection && controllerType != Controller::ControllerType::Dummy + && !(skipMidiController && controllerType == Controller::ControllerType::Midi)) { QDomElement controllerElement; @@ -264,18 +264,18 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString& { if( nodeElement.attribute( "scale_type" ) == "linear" ) { - setScaleType( Linear ); + setScaleType( ScaleType::Linear ); } else if( nodeElement.attribute( "scale_type" ) == "log" ) { - setScaleType( Logarithmic ); + setScaleType( ScaleType::Logarithmic ); } } } else { - setScaleType( Linear ); + setScaleType( ScaleType::Linear ); if( element.hasAttribute( name ) ) // attribute => read the element's value from the attribute list @@ -335,7 +335,7 @@ template T AutomatableModel::logToLinearScale( T value ) const float AutomatableModel::scaledValue( float value ) const { - return m_scaleType == Linear + return m_scaleType == ScaleType::Linear ? value : logToLinearScale( ( value - minValue() ) / m_range ); } @@ -343,7 +343,7 @@ float AutomatableModel::scaledValue( float value ) const float AutomatableModel::inverseScaledValue( float value ) const { - return m_scaleType == Linear + return m_scaleType == ScaleType::Linear ? value : lmms::linearToLogScale( minValue(), maxValue(), value ); } @@ -571,10 +571,10 @@ float AutomatableModel::controllerValue( int frameOffset ) const float v = 0; switch(m_scaleType) { - case Linear: + case ScaleType::Linear: v = minValue() + ( range() * controllerConnection()->currentValue( frameOffset ) ); break; - case Logarithmic: + case ScaleType::Logarithmic: v = logToLinearScale( controllerConnection()->currentValue( frameOffset )); break; @@ -623,13 +623,13 @@ ValueBuffer * AutomatableModel::valueBuffer() float * nvalues = m_valueBuffer.values(); switch( m_scaleType ) { - case Linear: + case ScaleType::Linear: for( int i = 0; i < m_valueBuffer.length(); i++ ) { nvalues[i] = minValue() + ( range() * values[i] ); } break; - case Logarithmic: + case ScaleType::Logarithmic: for( int i = 0; i < m_valueBuffer.length(); i++ ) { nvalues[i] = logToLinearScale( values[i] ); diff --git a/src/core/AutomationClip.cpp b/src/core/AutomationClip.cpp index e55fc5c1e..906cb148c 100644 --- a/src/core/AutomationClip.cpp +++ b/src/core/AutomationClip.cpp @@ -53,7 +53,7 @@ AutomationClip::AutomationClip( AutomationTrack * _auto_track ) : m_autoTrack( _auto_track ), m_objects(), m_tension( 1.0 ), - m_progressionType( DiscreteProgression ), + m_progressionType( ProgressionType::Discrete ), m_dragging( false ), m_isRecording( false ), m_lastRecordedValue( 0 ) @@ -63,11 +63,11 @@ AutomationClip::AutomationClip( AutomationTrack * _auto_track ) : { switch( getTrack()->trackContainer()->type() ) { - case TrackContainer::PatternContainer: + case TrackContainer::Type::Pattern: setAutoResize( true ); break; - case TrackContainer::SongContainer: + case TrackContainer::Type::Song: // move down default: setAutoResize( false ); @@ -104,11 +104,11 @@ AutomationClip::AutomationClip( const AutomationClip & _clip_to_copy ) : if (!getTrack()){ return; } switch( getTrack()->trackContainer()->type() ) { - case TrackContainer::PatternContainer: + case TrackContainer::Type::Pattern: setAutoResize( true ); break; - case TrackContainer::SongContainer: + case TrackContainer::Type::Song: // move down default: setAutoResize( false ); @@ -147,13 +147,13 @@ bool AutomationClip::addObject( AutomatableModel * _obj, bool _search_dup ) void AutomationClip::setProgressionType( - ProgressionTypes _new_progression_type ) + ProgressionType _new_progression_type ) { QMutexLocker m(&m_clipMutex); - if ( _new_progression_type == DiscreteProgression || - _new_progression_type == LinearProgression || - _new_progression_type == CubicHermiteProgression ) + if ( _new_progression_type == ProgressionType::Discrete || + _new_progression_type == ProgressionType::Linear || + _new_progression_type == ProgressionType::CubicHermite ) { m_progressionType = _new_progression_type; emit dataChanged(); @@ -560,11 +560,11 @@ float AutomationClip::valueAt( timeMap::const_iterator v, int offset ) const // value if we do if (offset == 0) { return INVAL(v); } - if (m_progressionType == DiscreteProgression) + if (m_progressionType == ProgressionType::Discrete) { return OUTVAL(v); } - else if( m_progressionType == LinearProgression ) + else if( m_progressionType == ProgressionType::Linear ) { float slope = (INVAL(v + 1) - OUTVAL(v)) @@ -572,7 +572,7 @@ float AutomationClip::valueAt( timeMap::const_iterator v, int offset ) const return OUTVAL(v) + offset * slope; } - else /* CubicHermiteProgression */ + else /* ProgressionType::CubicHermite */ { // Implements a Cubic Hermite spline as explained at: // http://en.wikipedia.org/wiki/Cubic_Hermite_spline#Unit_interval_.280.2C_1.29 @@ -767,7 +767,7 @@ void AutomationClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) _this.setAttribute( "pos", startPosition() ); _this.setAttribute( "len", length() ); _this.setAttribute( "name", name() ); - _this.setAttribute( "prog", QString::number( progressionType() ) ); + _this.setAttribute( "prog", QString::number( static_cast(progressionType()) ) ); _this.setAttribute( "tens", QString::number( getTension() ) ); _this.setAttribute( "mute", QString::number( isMuted() ) ); @@ -808,7 +808,7 @@ void AutomationClip::loadSettings( const QDomElement & _this ) movePosition( _this.attribute( "pos" ).toInt() ); setName( _this.attribute( "name" ) ); - setProgressionType( static_cast( _this.attribute( + setProgressionType( static_cast( _this.attribute( "prog" ).toInt() ) ); setTension( _this.attribute( "tens" ) ); setMuted(_this.attribute( "mute", QString::number( false ) ).toInt() ); @@ -891,7 +891,7 @@ bool AutomationClip::isAutomated( const AutomatableModel * _m ) auto l = combineAllTracks(); for (const auto track : l) { - if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack) + if (track->type() == Track::Type::Automation || track->type() == Track::Type::HiddenAutomation) { for (const auto& clip : track->getClips()) { @@ -926,7 +926,7 @@ std::vector AutomationClip::clipsForModel(const AutomatableMod for (const auto track : l) { // we want only automation tracks... - if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack ) + if (track->type() == Track::Type::Automation || track->type() == Track::Type::HiddenAutomation ) { // go through all the clips... for (const auto& trackClip : track->getClips()) @@ -985,7 +985,7 @@ void AutomationClip::resolveAllIDs() auto l = combineAllTracks(); for (const auto& track : l) { - if (track->type() == Track::AutomationTrack || track->type() == Track::HiddenAutomationTrack) + if (track->type() == Track::Type::Automation || track->type() == Track::Type::HiddenAutomation) { for (const auto& clip : track->getClips()) { diff --git a/src/core/BandLimitedWave.cpp b/src/core/BandLimitedWave.cpp index a92b22e21..cb09dc5b8 100644 --- a/src/core/BandLimitedWave.cpp +++ b/src/core/BandLimitedWave.cpp @@ -30,7 +30,7 @@ namespace lmms { -std::array BandLimitedWave::s_waveforms = { }; +std::array BandLimitedWave::s_waveforms = { }; bool BandLimitedWave::s_wavesGenerated = false; QString BandLimitedWave::s_wavetableDir = ""; @@ -84,7 +84,7 @@ void BandLimitedWave::generateWaves() { saw_file.open( QIODevice::ReadOnly ); QDataStream in( &saw_file ); - in >> s_waveforms[ BandLimitedWave::BLSaw ]; + in >> s_waveforms[static_cast(BandLimitedWave::Waveform::BLSaw)]; saw_file.close(); } else @@ -108,14 +108,14 @@ void BandLimitedWave::generateWaves() s += amp * /*a2 **/sin( static_cast( ph * harm ) / static_cast( len ) * F_2PI ); harm++; } while( hlen > 2.0 ); - s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s ); + s_waveforms[static_cast(BandLimitedWave::Waveform::BLSaw)].setSampleAt( i, ph, s ); max = std::max(max, std::abs(s)); } // normalize for( int ph = 0; ph < len; ph++ ) { - sample_t s = s_waveforms[ BandLimitedWave::BLSaw ].sampleAt( i, ph ) / max; - s_waveforms[ BandLimitedWave::BLSaw ].setSampleAt( i, ph, s ); + sample_t s = s_waveforms[static_cast(BandLimitedWave::Waveform::BLSaw)].sampleAt( i, ph ) / max; + s_waveforms[static_cast(BandLimitedWave::Waveform::BLSaw)].setSampleAt( i, ph, s ); } } } @@ -126,7 +126,7 @@ void BandLimitedWave::generateWaves() { sqr_file.open( QIODevice::ReadOnly ); QDataStream in( &sqr_file ); - in >> s_waveforms[ BandLimitedWave::BLSquare ]; + in >> s_waveforms[static_cast(BandLimitedWave::Waveform::BLSquare)]; sqr_file.close(); } else @@ -150,14 +150,14 @@ void BandLimitedWave::generateWaves() s += amp * /*a2 **/ sin( static_cast( ph * harm ) / static_cast( len ) * F_2PI ); harm += 2; } while( hlen > 2.0 ); - s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s ); + s_waveforms[static_cast(BandLimitedWave::Waveform::BLSquare)].setSampleAt( i, ph, s ); max = std::max(max, std::abs(s)); } // normalize for( int ph = 0; ph < len; ph++ ) { - sample_t s = s_waveforms[ BandLimitedWave::BLSquare ].sampleAt( i, ph ) / max; - s_waveforms[ BandLimitedWave::BLSquare ].setSampleAt( i, ph, s ); + sample_t s = s_waveforms[static_cast(BandLimitedWave::Waveform::BLSquare)].sampleAt( i, ph ) / max; + s_waveforms[static_cast(BandLimitedWave::Waveform::BLSquare)].setSampleAt( i, ph, s ); } } } @@ -167,7 +167,7 @@ void BandLimitedWave::generateWaves() { tri_file.open( QIODevice::ReadOnly ); QDataStream in( &tri_file ); - in >> s_waveforms[ BandLimitedWave::BLTriangle ]; + in >> s_waveforms[static_cast(BandLimitedWave::Waveform::BLTriangle)]; tri_file.close(); } else @@ -192,14 +192,14 @@ void BandLimitedWave::generateWaves() ( ( harm + 1 ) % 4 == 0 ? 0.5 : 0.0 ) ) * F_2PI ); harm += 2; } while( hlen > 2.0 ); - s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s ); + s_waveforms[static_cast(BandLimitedWave::Waveform::BLTriangle)].setSampleAt( i, ph, s ); max = std::max(max, std::abs(s)); } // normalize for( int ph = 0; ph < len; ph++ ) { - sample_t s = s_waveforms[ BandLimitedWave::BLTriangle ].sampleAt( i, ph ) / max; - s_waveforms[ BandLimitedWave::BLTriangle ].setSampleAt( i, ph, s ); + sample_t s = s_waveforms[static_cast(BandLimitedWave::Waveform::BLTriangle)].sampleAt( i, ph ) / max; + s_waveforms[static_cast(BandLimitedWave::Waveform::BLTriangle)].setSampleAt( i, ph, s ); } } } @@ -210,7 +210,7 @@ void BandLimitedWave::generateWaves() { moog_file.open( QIODevice::ReadOnly ); QDataStream in( &moog_file ); - in >> s_waveforms[ BandLimitedWave::BLMoog ]; + in >> s_waveforms[static_cast(BandLimitedWave::Waveform::BLMoog)]; moog_file.close(); } else @@ -222,9 +222,9 @@ void BandLimitedWave::generateWaves() for( int ph = 0; ph < len; ph++ ) { const int sawph = ( ph + static_cast( len * 0.75 ) ) % len; - const sample_t saw = s_waveforms[ BandLimitedWave::BLSaw ].sampleAt( i, sawph ); - const sample_t tri = s_waveforms[ BandLimitedWave::BLTriangle ].sampleAt( i, ph ); - s_waveforms[ BandLimitedWave::BLMoog ].setSampleAt( i, ph, ( saw + tri ) * 0.5f ); + const sample_t saw = s_waveforms[static_cast(BandLimitedWave::Waveform::BLSaw)].sampleAt( i, sawph ); + const sample_t tri = s_waveforms[static_cast(BandLimitedWave::Waveform::BLTriangle)].sampleAt( i, ph ); + s_waveforms[static_cast(BandLimitedWave::Waveform::BLMoog)].setSampleAt( i, ph, ( saw + tri ) * 0.5f ); } } } @@ -252,22 +252,22 @@ QFile moogfile( "path-to-wavetables/moog.bin" ); sawfile.open( QIODevice::WriteOnly ); QDataStream sawout( &sawfile ); -sawout << s_waveforms[ BandLimitedWave::BLSaw ]; +sawout << s_waveforms[static_cast(BandLimitedWave::Waveform::BLSaw)]; sawfile.close(); sqrfile.open( QIODevice::WriteOnly ); QDataStream sqrout( &sqrfile ); -sqrout << s_waveforms[ BandLimitedWave::BLSquare ]; +sqrout << s_waveforms[static_cast(BandLimitedWave::Waveform::BLSquare)]; sqrfile.close(); trifile.open( QIODevice::WriteOnly ); QDataStream triout( &trifile ); -triout << s_waveforms[ BandLimitedWave::BLTriangle ]; +triout << s_waveforms[static_cast(BandLimitedWave::Waveform::BLTriangle)]; trifile.close(); moogfile.open( QIODevice::WriteOnly ); QDataStream moogout( &moogfile ); -moogout << s_waveforms[ BandLimitedWave::BLMoog ]; +moogout << s_waveforms[static_cast(BandLimitedWave::Waveform::BLMoog)]; moogfile.close(); */ diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 05574ae35..61d84770a 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -173,7 +173,7 @@ void ConfigManager::upgrade() 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) + if (createdWith.setCompareType(ProjectVersion::CompareType::Minor) != LMMS_VERSION) { m_themeDir = defaultThemeDir(); } @@ -707,7 +707,7 @@ unsigned int ConfigManager::legacyConfigVersion() { ProjectVersion createdWith = m_version; - createdWith.setCompareType(ProjectVersion::Build); + createdWith.setCompareType(ProjectVersion::CompareType::Build); if( createdWith < "1.1.90" ) { diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index e7031f20c..3e1b596b8 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -44,7 +44,7 @@ std::vector Controller::s_controllers; -Controller::Controller( ControllerTypes _type, Model * _parent, +Controller::Controller( ControllerType _type, Model * _parent, const QString & _display_name ) : Model( _parent, _display_name ), JournallingObject(), @@ -53,7 +53,7 @@ Controller::Controller( ControllerTypes _type, Model * _parent, m_connectionCount( 0 ), m_type( _type ) { - if( _type != DummyController && _type != MidiController ) + if( _type != ControllerType::Dummy && _type != ControllerType::Midi ) { s_controllers.push_back(this); // Determine which name to use @@ -182,30 +182,30 @@ void Controller::resetFrameCounter() -Controller * Controller::create( ControllerTypes _ct, Model * _parent ) +Controller * Controller::create( ControllerType _ct, Model * _parent ) { static Controller * dummy = nullptr; Controller * c = nullptr; switch( _ct ) { - case Controller::DummyController: + case ControllerType::Dummy: if (!dummy) - dummy = new Controller( DummyController, nullptr, + dummy = new Controller( ControllerType::Dummy, nullptr, QString() ); c = dummy; break; - case Controller::LfoController: + case ControllerType::Lfo: c = new class LfoController( _parent ); break; - case Controller::PeakController: + case ControllerType::Peak: //Already instantiated in EffectChain::loadSettings() Q_ASSERT( false ); break; - case Controller::MidiController: + case ControllerType::Midi: c = new class MidiController( _parent ); break; @@ -221,14 +221,14 @@ Controller * Controller::create( ControllerTypes _ct, Model * _parent ) Controller * Controller::create( const QDomElement & _this, Model * _parent ) { Controller * c; - if( _this.attribute( "type" ).toInt() == Controller::PeakController ) + if( static_cast(_this.attribute( "type" ).toInt()) == ControllerType::Peak ) { c = PeakController::getControllerBySetting( _this ); } else { c = create( - static_cast( _this.attribute( "type" ).toInt() ), + static_cast( _this.attribute( "type" ).toInt() ), _parent ); } @@ -269,7 +269,7 @@ bool Controller::hasModel( const Model * m ) const void Controller::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - _this.setAttribute( "type", type() ); + _this.setAttribute( "type", static_cast(type()) ); _this.setAttribute( "name", name() ); } @@ -277,7 +277,7 @@ void Controller::saveSettings( QDomDocument & _doc, QDomElement & _this ) void Controller::loadSettings( const QDomElement & _this ) { - if( _this.attribute( "type" ).toInt() != type() ) + if( static_cast(_this.attribute( "type" ).toInt()) != type() ) { qWarning( "controller-type does not match controller-type of " "settings-node!\n" ); diff --git a/src/core/ControllerConnection.cpp b/src/core/ControllerConnection.cpp index a4477c563..fea907942 100644 --- a/src/core/ControllerConnection.cpp +++ b/src/core/ControllerConnection.cpp @@ -50,7 +50,7 @@ ControllerConnection::ControllerConnection(Controller * _controller) : } else { - m_controller = Controller::create( Controller::DummyController, + m_controller = Controller::create( Controller::ControllerType::Dummy, nullptr ); } s_connections.push_back(this); @@ -60,7 +60,7 @@ ControllerConnection::ControllerConnection(Controller * _controller) : ControllerConnection::ControllerConnection( int _controllerId ) : - m_controller( Controller::create( Controller::DummyController, nullptr ) ), + m_controller( Controller::create( Controller::ControllerType::Dummy, nullptr ) ), m_controllerId( _controllerId ), m_ownsController( false ) { @@ -72,7 +72,7 @@ ControllerConnection::ControllerConnection( int _controllerId ) : ControllerConnection::~ControllerConnection() { - if( m_controller && m_controller->type() != Controller::DummyController ) + if( m_controller && m_controller->type() != Controller::ControllerType::Dummy ) { m_controller->removeConnection( this ); } @@ -104,14 +104,14 @@ void ControllerConnection::setController( Controller * _controller ) m_controller = nullptr; } - if( m_controller && m_controller->type() != Controller::DummyController ) + if( m_controller && m_controller->type() != Controller::ControllerType::Dummy ) { m_controller->removeConnection( this ); } if( !_controller ) { - m_controller = Controller::create( Controller::DummyController, nullptr ); + m_controller = Controller::create( Controller::ControllerType::Dummy, nullptr ); } else { @@ -119,7 +119,7 @@ void ControllerConnection::setController( Controller * _controller ) } m_controllerId = -1; - if( _controller->type() != Controller::DummyController ) + if( _controller->type() != Controller::ControllerType::Dummy ) { _controller->addConnection( this ); QObject::connect( _controller, SIGNAL(valueChanged()), @@ -127,7 +127,7 @@ void ControllerConnection::setController( Controller * _controller ) } m_ownsController = - (_controller->type() == Controller::MidiController); + (_controller->type() == Controller::ControllerType::Midi); // If we don't own the controller, allow deletion of controller // to delete the connection @@ -168,7 +168,7 @@ void ControllerConnection::finalizeConnections() c->setController( Engine::getSong()-> controllers().at( c->m_controllerId ) ); } - else if (c->getController()->type() == Controller::DummyController) + else if (c->getController()->type() == Controller::ControllerType::Dummy) { delete c; --i; @@ -228,7 +228,7 @@ void ControllerConnection::loadSettings( const QDomElement & _this ) } else { - m_controller = Controller::create( Controller::DummyController, nullptr ); + m_controller = Controller::create( Controller::ControllerType::Dummy, nullptr ); } } } diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 2361b1926..6ad2f8526 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -26,6 +26,7 @@ #include "DataFile.h" +#include #include #include @@ -98,17 +99,16 @@ namespace QString m_name; }; - const auto s_types = std::array - { - TypeDescStruct{ DataFile::UnknownType, "unknown" }, - TypeDescStruct{ DataFile::SongProject, "song" }, - TypeDescStruct{ DataFile::SongProjectTemplate, "songtemplate" }, - TypeDescStruct{ DataFile::InstrumentTrackSettings, "instrumenttracksettings" }, - TypeDescStruct{ DataFile::DragNDropData, "dnddata" }, - TypeDescStruct{ DataFile::ClipboardData, "clipboard-data" }, - TypeDescStruct{ DataFile::JournalData, "journaldata" }, - TypeDescStruct{ DataFile::EffectSettings, "effectsettings" }, - TypeDescStruct{ DataFile::MidiClip, "midiclip" } + const auto s_types = std::array{ + TypeDescStruct{ DataFile::Type::Unknown, "unknown" }, + TypeDescStruct{ DataFile::Type::SongProject, "song" }, + TypeDescStruct{ DataFile::Type::SongProjectTemplate, "songtemplate" }, + TypeDescStruct{ DataFile::Type::InstrumentTrackSettings, "instrumenttracksettings" }, + TypeDescStruct{ DataFile::Type::DragNDropData, "dnddata" }, + TypeDescStruct{ DataFile::Type::ClipboardData, "clipboard-data" }, + TypeDescStruct{ DataFile::Type::JournalData, "journaldata" }, + TypeDescStruct{ DataFile::Type::EffectSettings, "effectsettings" }, + TypeDescStruct{ DataFile::Type::MidiClip, "midiclip" } }; } @@ -214,7 +214,7 @@ bool DataFile::validate( QString extension ) return true; } break; - case Type::UnknownType: + case Type::Unknown: if (! ( extension == "mmp" || extension == "mpt" || extension == "mmpz" || extension == "xpf" || extension == "xml" || ( extension == "xiz" && ! getPluginFactory()->pluginSupportingExtension(extension).isNull()) || @@ -251,7 +251,7 @@ QString DataFile::nameWithExtension( const QString & _fn ) const switch( type() ) { - case SongProject: + case Type::SongProject: if( extension != "mmp" && extension != "mpt" && extension != "mmpz" ) @@ -264,13 +264,13 @@ QString DataFile::nameWithExtension( const QString & _fn ) const return _fn + ".mmp"; } break; - case SongProjectTemplate: + case Type::SongProjectTemplate: if( extension != "mpt" ) { return _fn + ".mpt"; } break; - case InstrumentTrackSettings: + case Type::InstrumentTrackSettings: if( extension != "xpf" ) { return _fn + ".xpf"; @@ -286,8 +286,8 @@ QString DataFile::nameWithExtension( const QString & _fn ) const void DataFile::write( QTextStream & _strm ) { - if( type() == SongProject || type() == SongProjectTemplate - || type() == InstrumentTrackSettings ) + if( type() == Type::SongProject || type() == Type::SongProjectTemplate + || type() == Type::InstrumentTrackSettings ) { cleanMetaNodes( documentElement() ); } @@ -585,21 +585,17 @@ bool DataFile::hasLocalPlugins(QDomElement parent /* = QDomElement()*/, bool fir DataFile::Type DataFile::type( const QString& typeName ) { - for( int i = 0; i < TypeCount; ++i ) - { - if( s_types[i].m_name == typeName ) - { - return static_cast( i ); - } - } + const auto it = std::find_if(s_types.begin(), s_types.end(), + [&typeName](const TypeDescStruct& type) { return type.m_name == typeName; }); + if (it != s_types.end()) { return it->m_type; } // compat code if( typeName == "channelsettings" ) { - return DataFile::InstrumentTrackSettings; + return Type::InstrumentTrackSettings; } - return UnknownType; + return Type::Unknown; } @@ -607,12 +603,7 @@ DataFile::Type DataFile::type( const QString& typeName ) QString DataFile::typeName( Type type ) { - if( type >= UnknownType && type < TypeCount ) - { - return s_types[type].m_name; - } - - return s_types[UnknownType].m_name; + return s_types[static_cast(type)].m_name; } @@ -1760,8 +1751,8 @@ void DataFile::upgrade_bbTcoRename() for (int i = 0; !elements.item(i).isNull(); ++i) { auto e = elements.item(i).toElement(); - static_assert(Track::PatternTrack == 1, "Must be type=1 for backwards compatibility"); - if (e.attribute("type").toInt() == Track::PatternTrack) + static_assert(Track::Type::Pattern == static_cast(1), "Must be type=1 for backwards compatibility"); + if (static_cast(e.attribute("type").toInt()) == Track::Type::Pattern) { e.setAttribute("name", e.attribute("name").replace("Beat/Bassline", "Pattern")); } @@ -1789,7 +1780,7 @@ void DataFile::upgrade() documentElement().setAttribute( "creator", "LMMS" ); documentElement().setAttribute( "creatorversion", LMMS_VERSION ); - if( type() == SongProject || type() == SongProjectTemplate ) + if( type() == Type::SongProject || type() == Type::SongProjectTemplate ) { // Time-signature if ( !m_head.hasAttribute( "timesig_numerator" ) ) @@ -1867,8 +1858,8 @@ void DataFile::loadData( const QByteArray & _data, const QString & _sourceFile ) ProjectVersion createdWith = root.attribute("creatorversion"); ProjectVersion openedWith = LMMS_VERSION; - if (createdWith.setCompareType(ProjectVersion::Minor) - != openedWith.setCompareType(ProjectVersion::Minor) + if (createdWith.setCompareType(ProjectVersion::CompareType::Minor) + != openedWith.setCompareType(ProjectVersion::CompareType::Minor) && gui::getGUI() != nullptr && root.attribute("type") == "song" ){ auto projectType = _sourceFile.endsWith(".mpt") ? diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index a3f41b5aa..0a9673c8e 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -113,7 +113,7 @@ EnvelopeAndLfoParameters::EnvelopeAndLfoParameters( SECS_PER_LFO_OSCILLATION * 1000.0, this, tr( "LFO frequency" ) ), m_lfoAmountModel( 0.0, -1.0, 1.0, 0.005, this, tr( "LFO mod amount" ) ), - m_lfoWaveModel( SineWave, 0, NumLfoShapes, this, tr( "LFO wave shape" ) ), + m_lfoWaveModel( static_cast(LfoShape::SineWave), 0, NumLfoShapes, this, tr( "LFO wave shape" ) ), m_x100Model( false, this, tr( "LFO frequency x 100" ) ), m_controlEnvAmountModel( false, this, tr( "Modulate env amount" ) ), m_lfoFrame( 0 ), @@ -209,28 +209,28 @@ inline sample_t EnvelopeAndLfoParameters::lfoShapeSample( fpp_t _frame_offset ) const float phase = frame / static_cast( m_lfoOscillationFrames ); sample_t shape_sample; - switch( m_lfoWaveModel.value() ) + switch( static_cast(m_lfoWaveModel.value()) ) { - case TriangleWave: + case LfoShape::TriangleWave: shape_sample = Oscillator::triangleSample( phase ); break; - case SquareWave: + case LfoShape::SquareWave: shape_sample = Oscillator::squareSample( phase ); break; - case SawWave: + case LfoShape::SawWave: shape_sample = Oscillator::sawSample( phase ); break; - case UserDefinedWave: + case LfoShape::UserDefinedWave: shape_sample = m_userWave.userWaveSample( phase ); break; - case RandomWave: + case LfoShape::RandomWave: if( frame == 0 ) { m_random = Oscillator::noiseSample( 0.0f ); } shape_sample = m_random; break; - case SineWave: + case LfoShape::SineWave: default: shape_sample = Oscillator::sinSample( phase ); break; diff --git a/src/core/ImportFilter.cpp b/src/core/ImportFilter.cpp index 35c34078e..bc43b0c6c 100644 --- a/src/core/ImportFilter.cpp +++ b/src/core/ImportFilter.cpp @@ -61,7 +61,7 @@ void ImportFilter::import( const QString & _file_to_import, const bool j = Engine::projectJournal()->isJournalling(); Engine::projectJournal()->setJournalling( false ); - for (const Plugin::Descriptor* desc : getPluginFactory()->descriptors(Plugin::ImportFilter)) + for (const Plugin::Descriptor* desc : getPluginFactory()->descriptors(Plugin::Type::ImportFilter)) { unique_ptr p(Plugin::instantiate( desc->name, nullptr, s.data() )); if( dynamic_cast( p.get() ) != nullptr && diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index d2fea9dbd..431afd2fe 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -234,7 +234,7 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n ) // at the same time we only add sub-notes if nothing of the note was // played yet, because otherwise we would add chord-subnotes every // time an audio-buffer is rendered... - if( ( _n->origin() == NotePlayHandle::OriginArpeggio || ( _n->hasParent() == false && _n->instrumentTrack()->isArpeggioEnabled() == false ) ) && + if( ( _n->origin() == NotePlayHandle::Origin::Arpeggio || ( _n->hasParent() == false && _n->instrumentTrack()->isArpeggioEnabled() == false ) ) && _n->totalFramesPlayed() == 0 && m_chordsEnabledModel.value() == true && ! _n->isReleased() ) { @@ -263,7 +263,7 @@ void InstrumentFunctionNoteStacking::processNote( NotePlayHandle * _n ) // different Engine::audioEngine()->addPlayHandle( NotePlayHandleManager::acquire( _n->instrumentTrack(), _n->offset(), _n->frames(), note_copy, - _n, -1, NotePlayHandle::OriginNoteStacking ) + _n, -1, NotePlayHandle::Origin::NoteStacking ) ); } } @@ -321,7 +321,7 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) : 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_arpDirectionModel.setInitValue( static_cast(ArpDirection::Up) ); m_arpModeModel.addItem( tr( "Free" ), std::make_unique( "arp_free" ) ); m_arpModeModel.addItem( tr( "Sort" ), std::make_unique( "arp_sort" ) ); @@ -336,8 +336,8 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) : void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) { const int base_note_key = _n->key(); - if( _n->origin() == NotePlayHandle::OriginArpeggio || - _n->origin() == NotePlayHandle::OriginNoteStacking || + if( _n->origin() == NotePlayHandle::Origin::Arpeggio || + _n->origin() == NotePlayHandle::Origin::NoteStacking || !m_arpEnabledModel.value() || _n->isReleased() ) { @@ -351,7 +351,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() ); - if( m_arpModeModel.value() != FreeMode && cnphv.size() == 0 ) + if( static_cast(m_arpModeModel.value()) != ArpMode::Free && cnphv.size() == 0 ) { // maybe we're playing only a preset-preview-note? cnphv = PresetPreviewPlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() ); @@ -375,11 +375,11 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) // used for calculating remaining frames for arp-note, we have to add // arp_frames-1, otherwise the first arp-note will not be setup // correctly... -> arp_frames frames silence at the start of every note! - int cur_frame = ( ( m_arpModeModel.value() != FreeMode ) ? + int cur_frame = ( ( static_cast(m_arpModeModel.value()) != ArpMode::Free ) ? cnphv.first()->totalFramesPlayed() : _n->totalFramesPlayed() ) + arp_frames - 1; // used for loop - f_cnt_t frames_processed = ( m_arpModeModel.value() != FreeMode ) ? cnphv.first()->noteOffset() : _n->noteOffset(); + f_cnt_t frames_processed = ( static_cast(m_arpModeModel.value()) != ArpMode::Free ) ? cnphv.first()->noteOffset() : _n->noteOffset(); while( frames_processed < Engine::audioEngine()->framesPerPeriod() ) { @@ -397,7 +397,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) // in sorted mode: is it our turn or do we have to be quiet for // now? - if( m_arpModeModel.value() == SortMode && + if( static_cast(m_arpModeModel.value()) == ArpMode::Sort && ( ( cur_frame / arp_frames ) % total_range ) / range != (f_cnt_t) _n->index() ) { // update counters @@ -418,7 +418,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) } } - int dir = m_arpDirectionModel.value(); + auto dir = static_cast(m_arpDirectionModel.value()); // Miss notes randomly. We intercept int dir and abuse it // after need. :) @@ -427,22 +427,22 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) { if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpMissModel.value() ) { - dir = ArpDirRandom; + dir = ArpDirection::Random; } } int cur_arp_idx = 0; // process according to arpeggio-direction... - if( dir == ArpDirUp ) + if( dir == ArpDirection::Up ) { cur_arp_idx = ( cur_frame / arp_frames ) % range; } - else if( dir == ArpDirDown ) + else if( dir == ArpDirection::Down ) { cur_arp_idx = range - ( cur_frame / arp_frames ) % range - 1; } - else if( dir == ArpDirUpAndDown && range > 1 ) + else if( dir == ArpDirection::UpAndDown && range > 1 ) { // imagine, we had to play the arp once up and then // once down -> makes 2 * range possible notes... @@ -456,9 +456,9 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) cur_arp_idx = range - cur_arp_idx % ( range - 1 ) - 1; } } - else if( dir == ArpDirDownAndUp && range > 1 ) + else if( dir == ArpDirection::DownAndUp && range > 1 ) { - // copied from ArpDirUpAndDown above + // copied from ArpDirection::UpAndDown above cur_arp_idx = ( cur_frame / arp_frames ) % ( range * 2 - 2 ); // if greater than range, we have to play down... // looks like the code for arp_dir==DOWN... :) @@ -469,7 +469,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) // inverts direction cur_arp_idx = range - cur_arp_idx - 1; } - else if( dir == ArpDirRandom ) + else if( dir == ArpDirection::Random ) { // just pick a random chord-index cur_arp_idx = (int)( range * ( (float) rand() / (float) RAND_MAX ) ); @@ -479,7 +479,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) cur_arp_idx = static_cast(cur_arp_idx / m_arpRepeatsModel.value()); // Cycle notes - if( m_arpCycleModel.value() && dir != ArpDirRandom ) + if( m_arpCycleModel.value() && dir != ArpDirection::Random ) { cur_arp_idx *= m_arpCycleModel.value() + 1; cur_arp_idx %= static_cast( range / m_arpRepeatsModel.value() ); @@ -507,7 +507,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) gated_frames, Note( TimePos( 0 ), TimePos( 0 ), sub_note_key, _n->getVolume(), _n->getPanning(), _n->detuning() ), - _n, -1, NotePlayHandle::OriginArpeggio ) + _n, -1, NotePlayHandle::Origin::Arpeggio ) ); // update counters diff --git a/src/core/InstrumentPlayHandle.cpp b/src/core/InstrumentPlayHandle.cpp index 8c272cd47..e1a9d9d65 100644 --- a/src/core/InstrumentPlayHandle.cpp +++ b/src/core/InstrumentPlayHandle.cpp @@ -31,7 +31,7 @@ namespace lmms InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ) : - PlayHandle( TypeInstrumentPlayHandle ), + PlayHandle( Type::InstrumentPlayHandle ), m_instrument( instrument ) { setAudioPort( instrumentTrack->audioPort() ); diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 4aeea453a..07c3bbf7c 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -69,7 +69,7 @@ InstrumentSoundShaping::InstrumentSoundShaping( for( int i = 0; i < NumTargets; ++i ) { float value_for_zero_amount = 0.0; - if( i == Volume ) + if( static_cast(i) == Target::Volume ) { value_for_zero_amount = 1.0; } @@ -119,7 +119,7 @@ float InstrumentSoundShaping::volumeLevel( NotePlayHandle* n, const f_cnt_t fram } float level; - m_envLfoParameters[Volume]->fillLevel( &level, frame, envReleaseBegin, 1 ); + m_envLfoParameters[static_cast(Target::Volume)]->fillLevel( &level, frame, envReleaseBegin, 1 ); return level; } @@ -160,22 +160,22 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, { n->m_filter = std::make_unique>( Engine::audioEngine()->processingSampleRate() ); } - n->m_filter->setFilterType( m_filterModel.value() ); + n->m_filter->setFilterType( static_cast::FilterType>(m_filterModel.value()) ); - if( m_envLfoParameters[Cut]->isUsed() ) + if( m_envLfoParameters[static_cast(Target::Cut)]->isUsed() ) { - m_envLfoParameters[Cut]->fillLevel( cutBuffer.data(), envTotalFrames, envReleaseBegin, frames ); + m_envLfoParameters[static_cast(Target::Cut)]->fillLevel( cutBuffer.data(), envTotalFrames, envReleaseBegin, frames ); } - if( m_envLfoParameters[Resonance]->isUsed() ) + if( m_envLfoParameters[static_cast(Target::Resonance)]->isUsed() ) { - m_envLfoParameters[Resonance]->fillLevel( resBuffer.data(), envTotalFrames, envReleaseBegin, frames ); + m_envLfoParameters[static_cast(Target::Resonance)]->fillLevel( resBuffer.data(), envTotalFrames, envReleaseBegin, frames ); } const float fcv = m_filterCutModel.value(); const float frv = m_filterResModel.value(); - if( m_envLfoParameters[Cut]->isUsed() && - m_envLfoParameters[Resonance]->isUsed() ) + if( m_envLfoParameters[static_cast(Target::Cut)]->isUsed() && + m_envLfoParameters[static_cast(Target::Resonance)]->isUsed() ) { for( fpp_t frame = 0; frame < frames; ++frame ) { @@ -196,7 +196,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, buffer[frame][1] = n->m_filter->update( buffer[frame][1], 1 ); } } - else if( m_envLfoParameters[Cut]->isUsed() ) + else if( m_envLfoParameters[static_cast(Target::Cut)]->isUsed() ) { for( fpp_t frame = 0; frame < frames; ++frame ) { @@ -213,7 +213,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, buffer[frame][1] = n->m_filter->update( buffer[frame][1], 1 ); } } - else if( m_envLfoParameters[Resonance]->isUsed() ) + else if( m_envLfoParameters[static_cast(Target::Resonance)]->isUsed() ) { for( fpp_t frame = 0; frame < frames; ++frame ) { @@ -241,10 +241,10 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, } } - if( m_envLfoParameters[Volume]->isUsed() ) + if( m_envLfoParameters[static_cast(Target::Volume)]->isUsed() ) { QVarLengthArray volBuffer(frames); - m_envLfoParameters[Volume]->fillLevel( volBuffer.data(), envTotalFrames, envReleaseBegin, frames ); + m_envLfoParameters[static_cast(Target::Volume)]->fillLevel( volBuffer.data(), envTotalFrames, envReleaseBegin, frames ); for( fpp_t frame = 0; frame < frames; ++frame ) { @@ -255,7 +255,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, } } -/* else if( m_envLfoParameters[Volume]->isUsed() == false && m_envLfoParameters[PANNING]->isUsed() ) +/* else if( m_envLfoParameters[static_cast(Target::Volume)]->isUsed() == false && m_envLfoParameters[PANNING]->isUsed() ) { // only use panning-envelope... for( fpp_t frame = 0; frame < frames; ++frame ) @@ -275,11 +275,11 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, f_cnt_t InstrumentSoundShaping::envFrames( const bool _only_vol ) const { - f_cnt_t ret_val = m_envLfoParameters[Volume]->PAHD_Frames(); + f_cnt_t ret_val = m_envLfoParameters[static_cast(Target::Volume)]->PAHD_Frames(); if( _only_vol == false ) { - for( int i = Volume+1; i < NumTargets; ++i ) + for( int i = static_cast(Target::Volume)+1; i < NumTargets; ++i ) { if( m_envLfoParameters[i]->isUsed() && m_envLfoParameters[i]->PAHD_Frames() > ret_val ) @@ -303,17 +303,17 @@ f_cnt_t InstrumentSoundShaping::releaseFrames() const f_cnt_t ret_val = m_instrumentTrack->instrument()->desiredReleaseFrames(); - if( m_instrumentTrack->instrument()->flags().testFlag( Instrument::IsSingleStreamed ) ) + if( m_instrumentTrack->instrument()->flags().testFlag( Instrument::Flag::IsSingleStreamed ) ) { return ret_val; } - if( m_envLfoParameters[Volume]->isUsed() ) + if( m_envLfoParameters[static_cast(Target::Volume)]->isUsed() ) { - return m_envLfoParameters[Volume]->releaseFrames(); + return m_envLfoParameters[static_cast(Target::Volume)]->releaseFrames(); } - for( int i = Volume+1; i < NumTargets; ++i ) + for( int i = static_cast(Target::Volume)+1; i < NumTargets; ++i ) { if( m_envLfoParameters[i]->isUsed() ) { diff --git a/src/core/Ladspa2LMMS.cpp b/src/core/Ladspa2LMMS.cpp index 24dfc76f1..440b052f8 100644 --- a/src/core/Ladspa2LMMS.cpp +++ b/src/core/Ladspa2LMMS.cpp @@ -40,12 +40,12 @@ Ladspa2LMMS::Ladspa2LMMS() ladspa_key_t key = plugin.second; LadspaManagerDescription * desc = getDescription( key ); - if( desc->type == SOURCE ) + if( desc->type == LadspaPluginType::Source ) { m_instruments.append( qMakePair( getName( key ), key ) ); } - else if( desc->type == TRANSFER && + else if( desc->type == LadspaPluginType::Transfer && ( desc->inputChannels == desc->outputChannels && ( desc->inputChannels == 1 || desc->inputChannels == 2 || @@ -55,7 +55,7 @@ Ladspa2LMMS::Ladspa2LMMS() m_validEffects.append( qMakePair( getName( key ), key ) ); } - else if( desc->type == TRANSFER && + else if( desc->type == LadspaPluginType::Transfer && ( desc->inputChannels != desc->outputChannels || ( desc->inputChannels != 1 && desc->inputChannels != 2 && @@ -65,12 +65,12 @@ Ladspa2LMMS::Ladspa2LMMS() m_invalidEffects.append( qMakePair( getName( key ), key ) ); } - else if( desc->type == SINK ) + else if( desc->type == LadspaPluginType::Sink ) { m_analysisTools.append( qMakePair( getName( key ), key ) ); } - else if( desc->type == OTHER ) + else if( desc->type == LadspaPluginType::Other ) { m_otherPlugins.append( qMakePair( getName( key ), key ) ); diff --git a/src/core/LadspaControl.cpp b/src/core/LadspaControl.cpp index 7e8e92623..3282a0c7a 100644 --- a/src/core/LadspaControl.cpp +++ b/src/core/LadspaControl.cpp @@ -53,7 +53,7 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port, switch( m_port->data_type ) { - case TOGGLED: + case BufferDataType::Toggled: m_toggledModel.setInitValue( static_cast( m_port->def ) ); connect( &m_toggledModel, SIGNAL(dataChanged()), @@ -66,8 +66,8 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port, m_toggledModel.setScaleLogarithmic( m_port->suggests_logscale ); break; - case INTEGER: - case ENUM: + case BufferDataType::Integer: + case BufferDataType::Enum: m_knobModel.setRange( static_cast( m_port->max ), static_cast( m_port->min ), 1 + static_cast( m_port->max - @@ -80,7 +80,7 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port, m_knobModel.setScaleLogarithmic( m_port->suggests_logscale ); break; - case FLOATING: + case BufferDataType::Floating: m_knobModel.setRange( m_port->min, m_port->max, ( m_port->max - m_port->min ) / ( m_port->name.toUpper() == "GAIN" @@ -93,7 +93,7 @@ LadspaControl::LadspaControl( Model * _parent, port_desc_t * _port, m_knobModel.setScaleLogarithmic( m_port->suggests_logscale ); break; - case TIME: + case BufferDataType::Time: m_tempoSyncKnobModel.setRange( m_port->min, m_port->max, ( m_port->max - m_port->min ) / 800.0f ); @@ -116,13 +116,13 @@ LADSPA_Data LadspaControl::value() { switch( m_port->data_type ) { - case TOGGLED: + case BufferDataType::Toggled: return static_cast( m_toggledModel.value() ); - case INTEGER: - case ENUM: - case FLOATING: + case BufferDataType::Integer: + case BufferDataType::Enum: + case BufferDataType::Floating: return static_cast( m_knobModel.value() ); - case TIME: + case BufferDataType::Time: return static_cast( m_tempoSyncKnobModel.value() ); default: qWarning( "LadspaControl::value(): BAD BAD BAD\n" ); @@ -137,13 +137,13 @@ ValueBuffer * LadspaControl::valueBuffer() { switch( m_port->data_type ) { - case TOGGLED: - case INTEGER: - case ENUM: + case BufferDataType::Toggled: + case BufferDataType::Integer: + case BufferDataType::Enum: return nullptr; - case FLOATING: + case BufferDataType::Floating: return m_knobModel.valueBuffer(); - case TIME: + case BufferDataType::Time: return m_tempoSyncKnobModel.valueBuffer(); default: qWarning( "LadspaControl::valueBuffer(): BAD BAD BAD\n" ); @@ -159,17 +159,17 @@ void LadspaControl::setValue( LADSPA_Data _value ) { switch( m_port->data_type ) { - case TOGGLED: + case BufferDataType::Toggled: m_toggledModel.setValue( static_cast( _value ) ); break; - case INTEGER: - case ENUM: + case BufferDataType::Integer: + case BufferDataType::Enum: m_knobModel.setValue( static_cast( _value ) ); break; - case FLOATING: + case BufferDataType::Floating: m_knobModel.setValue( static_cast( _value ) ); break; - case TIME: + case BufferDataType::Time: m_tempoSyncKnobModel.setValue( static_cast( _value ) ); break; @@ -194,15 +194,15 @@ void LadspaControl::saveSettings( QDomDocument& doc, } switch( m_port->data_type ) { - case TOGGLED: + case BufferDataType::Toggled: m_toggledModel.saveSettings( doc, e, "data" ); break; - case INTEGER: - case ENUM: - case FLOATING: + case BufferDataType::Integer: + case BufferDataType::Enum: + case BufferDataType::Floating: m_knobModel.saveSettings( doc, e, "data" ); break; - case TIME: + case BufferDataType::Time: m_tempoSyncKnobModel.saveSettings( doc, e, "data" ); break; default: @@ -230,15 +230,15 @@ void LadspaControl::loadSettings( const QDomElement& parent, const QString& name m_linkEnabledModel.setValue(m_linkEnabledModel.initValue()); switch( m_port->data_type ) { - case TOGGLED: + case BufferDataType::Toggled: m_toggledModel.setValue(m_toggledModel.initValue()); break; - case INTEGER: - case ENUM: - case FLOATING: + case BufferDataType::Integer: + case BufferDataType::Enum: + case BufferDataType::Floating: m_knobModel.setValue(m_knobModel.initValue()); break; - case TIME: + case BufferDataType::Time: m_tempoSyncKnobModel.setValue(m_tempoSyncKnobModel.initValue()); break; default: @@ -265,15 +265,15 @@ void LadspaControl::loadSettings( const QDomElement& parent, const QString& name switch( m_port->data_type ) { - case TOGGLED: + case BufferDataType::Toggled: m_toggledModel.loadSettings( e, dataModelName ); break; - case INTEGER: - case ENUM: - case FLOATING: + case BufferDataType::Integer: + case BufferDataType::Enum: + case BufferDataType::Floating: m_knobModel.loadSettings( e, dataModelName ); break; - case TIME: + case BufferDataType::Time: m_tempoSyncKnobModel.loadSettings( e, dataModelName ); break; default: @@ -290,15 +290,15 @@ void LadspaControl::linkControls( LadspaControl * _control ) { switch( m_port->data_type ) { - case TOGGLED: + case BufferDataType::Toggled: BoolModel::linkModels( &m_toggledModel, _control->toggledModel() ); break; - case INTEGER: - case ENUM: - case FLOATING: + case BufferDataType::Integer: + case BufferDataType::Enum: + case BufferDataType::Floating: FloatModel::linkModels( &m_knobModel, _control->knobModel() ); break; - case TIME: + case BufferDataType::Time: TempoSyncKnobModel::linkModels( &m_tempoSyncKnobModel, _control->tempoSyncKnobModel() ); break; @@ -341,15 +341,15 @@ void LadspaControl::unlinkControls( LadspaControl * _control ) { switch( m_port->data_type ) { - case TOGGLED: + case BufferDataType::Toggled: BoolModel::unlinkModels( &m_toggledModel, _control->toggledModel() ); break; - case INTEGER: - case ENUM: - case FLOATING: + case BufferDataType::Integer: + case BufferDataType::Enum: + case BufferDataType::Floating: FloatModel::unlinkModels( &m_knobModel, _control->knobModel() ); break; - case TIME: + case BufferDataType::Time: TempoSyncKnobModel::unlinkModels( &m_tempoSyncKnobModel, _control->tempoSyncKnobModel() ); break; diff --git a/src/core/LadspaManager.cpp b/src/core/LadspaManager.cpp index 8c3ab8f97..064c928ef 100644 --- a/src/core/LadspaManager.cpp +++ b/src/core/LadspaManager.cpp @@ -159,21 +159,21 @@ void LadspaManager::addPlugins( if( plugIn->inputChannels == 0 && plugIn->outputChannels > 0 ) { - plugIn->type = SOURCE; + plugIn->type = LadspaPluginType::Source; } else if( plugIn->inputChannels > 0 && plugIn->outputChannels > 0 ) { - plugIn->type = TRANSFER; + plugIn->type = LadspaPluginType::Transfer; } else if( plugIn->inputChannels > 0 && plugIn->outputChannels == 0 ) { - plugIn->type = SINK; + plugIn->type = LadspaPluginType::Sink; } else { - plugIn->type = OTHER; + plugIn->type = LadspaPluginType::Other; } m_ladspaManagerMap[key] = plugIn; diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index c0fe65855..23621b847 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -36,12 +36,12 @@ namespace lmms LfoController::LfoController( Model * _parent ) : - Controller( Controller::LfoController, _parent, tr( "LFO Controller" ) ), + Controller( ControllerType::Lfo, _parent, tr( "LFO Controller" ) ), m_baseModel( 0.5, 0.0, 1.0, 0.001, this, tr( "Base value" ) ), m_speedModel( 2.0, 0.01, 20.0, 0.0001, 20000.0, this, tr( "Oscillator speed" ) ), m_amountModel( 1.0, -1.0, 1.0, 0.005, this, tr( "Oscillator amount" ) ), m_phaseModel( 0.0, 0.0, 360.0, 4.0, this, tr( "Oscillator phase" ) ), - m_waveModel( Oscillator::SineWave, 0, Oscillator::NumWaveShapes, + m_waveModel( static_cast(Oscillator::WaveShape::Sine), 0, Oscillator::NumWaveShapes, this, tr( "Oscillator waveform" ) ), m_multiplierModel( 0, 0, 2, this, tr( "Frequency Multiplier" ) ), m_duration( 1000 ), @@ -149,30 +149,31 @@ void LfoController::updateDuration() void LfoController::updateSampleFunction() { - switch( m_waveModel.value() ) + switch( static_cast(m_waveModel.value()) ) { - case Oscillator::SineWave: + case Oscillator::WaveShape::Sine: + default: m_sampleFunction = &Oscillator::sinSample; break; - case Oscillator::TriangleWave: + case Oscillator::WaveShape::Triangle: m_sampleFunction = &Oscillator::triangleSample; break; - case Oscillator::SawWave: + case Oscillator::WaveShape::Saw: m_sampleFunction = &Oscillator::sawSample; break; - case Oscillator::SquareWave: + case Oscillator::WaveShape::Square: m_sampleFunction = &Oscillator::squareSample; break; - case Oscillator::MoogSawWave: + case Oscillator::WaveShape::MoogSaw: m_sampleFunction = &Oscillator::moogSawSample; break; - case Oscillator::ExponentialWave: + case Oscillator::WaveShape::Exponential: m_sampleFunction = &Oscillator::expSample; break; - case Oscillator::WhiteNoise: + case Oscillator::WaveShape::WhiteNoise: m_sampleFunction = &Oscillator::noiseSample; break; - case Oscillator::UserDefinedWave: + case Oscillator::WaveShape::UserDefined: m_sampleFunction = nullptr; /*TODO: If C++11 is allowed, should change the type of m_sampleFunction be std::function diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 5edd88a0a..e14660e1f 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -301,7 +301,7 @@ void Mixer::deleteChannel( int index ) for( Track* t : tracks ) { - if( t->type() == Track::InstrumentTrack ) + if( t->type() == Track::Type::Instrument ) { auto inst = dynamic_cast(t); int val = inst->mixerChannelModel()->value(0); @@ -317,7 +317,7 @@ void Mixer::deleteChannel( int index ) inst->mixerChannelModel()->setValue(val-1); } } - else if( t->type() == Track::SampleTrack ) + else if( t->type() == Track::Type::Sample ) { auto strk = dynamic_cast(t); int val = strk->mixerChannelModel()->value(0); @@ -401,7 +401,7 @@ void Mixer::moveChannelLeft( int index ) { for (const auto& track : trackList) { - if (track->type() == Track::InstrumentTrack) + if (track->type() == Track::Type::Instrument) { auto inst = (InstrumentTrack*)track; int val = inst->mixerChannelModel()->value(0); @@ -414,7 +414,7 @@ void Mixer::moveChannelLeft( int index ) inst->mixerChannelModel()->setValue(a); } } - else if (track->type() == Track::SampleTrack) + else if (track->type() == Track::Type::Sample) { auto strk = (SampleTrack*)track; int val = strk->mixerChannelModel()->value(0); @@ -630,7 +630,7 @@ void Mixer::masterMix( sampleFrame * _buf ) // also instantly add all muted channels as they don't need to care // about their senders, and can just increment the deps of their // recipients right away. - AudioEngineWorkerThread::resetJobQueue( AudioEngineWorkerThread::JobQueue::Dynamic ); + AudioEngineWorkerThread::resetJobQueue( AudioEngineWorkerThread::JobQueue::OperationMode::Dynamic ); for( MixerChannel * ch : m_mixerChannels ) { ch->m_muted = ch->m_muteModel.value(); @@ -863,7 +863,7 @@ bool Mixer::isChannelInUse(int index) for (const auto t : tracks) { - if (t->type() == Track::InstrumentTrack) + if (t->type() == Track::Type::Instrument) { auto inst = dynamic_cast(t); if (inst->mixerChannelModel()->value() == index) @@ -871,7 +871,7 @@ bool Mixer::isChannelInUse(int index) return true; } } - else if (t->type() == Track::SampleTrack) + else if (t->type() == Track::Type::Sample) { auto strack = dynamic_cast(t); if (strack->mixerChannelModel()->value() == index) diff --git a/src/core/Note.cpp b/src/core/Note.cpp index 1198abdec..a4ad61412 100644 --- a/src/core/Note.cpp +++ b/src/core/Note.cpp @@ -216,7 +216,7 @@ void Note::createDetuning() m_detuning = new DetuningHelper; (void) m_detuning->automationClip(); m_detuning->setRange( -MaxDetuning, MaxDetuning, 0.5f ); - m_detuning->automationClip()->setProgressionType( AutomationClip::LinearProgression ); + m_detuning->automationClip()->setProgressionType( AutomationClip::ProgressionType::Linear ); } } diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 31f7e81cb..70007ebf1 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -53,7 +53,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, NotePlayHandle *parent, int midiEventChannel, Origin origin ) : - PlayHandle( TypeNotePlayHandle, _offset ), + PlayHandle( Type::NotePlayHandle, _offset ), Note( n.length(), n.pos(), n.key(), n.getVolume(), n.getPanning(), n.detuning() ), m_pluginData( nullptr ), m_instrumentTrack( instrumentTrack ), @@ -104,12 +104,12 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, setFrames( _frames ); // inform attached components about new MIDI note (used for recording in Piano Roll) - if( m_origin == OriginMidiInput ) + if( m_origin == Origin::MidiInput ) { m_instrumentTrack->midiNoteOn( *this ); } - if(m_instrumentTrack->instrument() && m_instrumentTrack->instrument()->flags() & Instrument::IsSingleStreamed ) + if(m_instrumentTrack->instrument() && m_instrumentTrack->instrument()->flags() & Instrument::Flag::IsSingleStreamed ) { setUsesBuffer( false ); } @@ -400,7 +400,7 @@ void NotePlayHandle::noteOff( const f_cnt_t _s ) // inform attached components about MIDI finished (used for recording in Piano Roll) if (!instrumentTrack()->isSustainPedalPressed()) { - if( m_origin == OriginMidiInput ) + if( m_origin == Origin::MidiInput ) { setLength( TimePos( static_cast( totalFramesPlayed() / Engine::framesPerTick() ) ) ); m_instrumentTrack->midiNoteOff( *this ); @@ -575,8 +575,8 @@ void NotePlayHandle::processTimePos( const TimePos& time ) void NotePlayHandle::resize( const bpm_t _new_tempo ) { - if (origin() == OriginMidiInput || - (origin() == OriginNoteStacking && m_parent->origin() == OriginMidiInput)) + if (origin() == Origin::MidiInput || + (origin() == Origin::NoteStacking && m_parent->origin() == Origin::MidiInput)) { // Don't resize notes from MIDI input - they should continue to play // until the key is released, and their large duration can cause diff --git a/src/core/Oscillator.cpp b/src/core/Oscillator.cpp index 189dede6e..06033b63e 100644 --- a/src/core/Oscillator.cpp +++ b/src/core/Oscillator.cpp @@ -90,21 +90,22 @@ void Oscillator::update(sampleFrame* ab, const fpp_t frames, const ch_cnt_t chnl m_isModulator = modulator; if (m_subOsc != nullptr) { - switch (m_modulationAlgoModel->value()) + switch (static_cast(m_modulationAlgoModel->value())) { - case PhaseModulation: + case ModulationAlgo::PhaseModulation: updatePM(ab, frames, chnl); break; - case AmplitudeModulation: + case ModulationAlgo::AmplitudeModulation: updateAM(ab, frames, chnl); break; - case SignalMix: + case ModulationAlgo::SignalMix: + default: updateMix(ab, frames, chnl); break; - case SynchronizedBySubOsc: + case ModulationAlgo::SynchronizedBySubOsc: updateSync(ab, frames, chnl); break; - case FrequencyModulation: + case ModulationAlgo::FrequencyModulation: updateFM(ab, frames, chnl); } } @@ -199,7 +200,7 @@ void Oscillator::generateAntiAliasUserWaveTable(SampleBuffer *sampleBuffer) sample_t Oscillator::s_waveTables - [Oscillator::WaveShapes::NumWaveShapeTables] + [Oscillator::NumWaveShapeTables] [OscillatorConstants::WAVE_TABLES_PER_WAVEFORM_COUNT] [OscillatorConstants::WAVETABLE_LENGTH]; fftwf_plan Oscillator::s_fftPlan; @@ -235,9 +236,9 @@ void Oscillator::generateWaveTables() // Start from the table that contains the least number of bands, and re-use each table in the following // iteration, adding more bands in each step and avoiding repeated computation of earlier bands. using generator_t = void (*)(int, sample_t*, int); - auto simpleGen = [](WaveShapes shape, generator_t generator) + auto simpleGen = [](WaveShape shape, generator_t generator) { - const int shapeID = shape - FirstWaveShapeTable; + const int shapeID = static_cast(shape) - FirstWaveShapeTable; int lastBands = 0; // Clear the first wave table @@ -273,7 +274,7 @@ void Oscillator::generateWaveTables() Oscillator::s_sampleBuffer[i] = moogSawSample((float)i / (float)OscillatorConstants::WAVETABLE_LENGTH); } fftwf_execute(s_fftPlan); - generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), s_waveTables[WaveShapes::MoogSawWave - FirstWaveShapeTable][i]); + generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), s_waveTables[static_cast(WaveShape::MoogSaw) - FirstWaveShapeTable][i]); } // Generate exponential tables @@ -284,7 +285,7 @@ void Oscillator::generateWaveTables() s_sampleBuffer[i] = expSample((float)i / (float)OscillatorConstants::WAVETABLE_LENGTH); } fftwf_execute(s_fftPlan); - generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), s_waveTables[WaveShapes::ExponentialWave - FirstWaveShapeTable][i]); + generateFromFFT(OscillatorConstants::MAX_FREQ / freqFromWaveTableBand(i), s_waveTables[static_cast(WaveShape::Exponential) - FirstWaveShapeTable][i]); } }; @@ -292,18 +293,18 @@ void Oscillator::generateWaveTables() // but since threading is not essential in this case, it is easier and more reliable to simply generate // the wavetables serially. Remove the the check and #else branch once std::thread is well supported. #if !defined(__MINGW32__) && !defined(__MINGW64__) - std::thread sawThread(simpleGen, WaveShapes::SawWave, generateSawWaveTable); - std::thread squareThread(simpleGen, WaveShapes::SquareWave, generateSquareWaveTable); - std::thread triangleThread(simpleGen, WaveShapes::TriangleWave, generateTriangleWaveTable); + std::thread sawThread(simpleGen, WaveShape::Saw, generateSawWaveTable); + std::thread squareThread(simpleGen, WaveShape::Square, generateSquareWaveTable); + std::thread triangleThread(simpleGen, WaveShape::Triangle, generateTriangleWaveTable); std::thread fftThread(fftGen); sawThread.join(); squareThread.join(); triangleThread.join(); fftThread.join(); #else - simpleGen(WaveShapes::SawWave, generateSawWaveTable); - simpleGen(WaveShapes::SquareWave, generateSquareWaveTable); - simpleGen(WaveShapes::TriangleWave, generateTriangleWaveTable); + simpleGen(WaveShape::Saw, generateSawWaveTable); + simpleGen(WaveShape::Square, generateSquareWaveTable); + simpleGen(WaveShape::Triangle, generateTriangleWaveTable); fftGen(); #endif } @@ -314,32 +315,32 @@ void Oscillator::generateWaveTables() void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { - switch( m_waveShapeModel->value() ) + switch( static_cast(m_waveShapeModel->value()) ) { - case SineWave: + case WaveShape::Sine: default: - updateNoSub( _ab, _frames, _chnl ); + updateNoSub( _ab, _frames, _chnl ); break; - case TriangleWave: - updateNoSub( _ab, _frames, _chnl ); + case WaveShape::Triangle: + updateNoSub( _ab, _frames, _chnl ); break; - case SawWave: - updateNoSub( _ab, _frames, _chnl ); + case WaveShape::Saw: + updateNoSub( _ab, _frames, _chnl ); break; - case SquareWave: - updateNoSub( _ab, _frames, _chnl ); + case WaveShape::Square: + updateNoSub( _ab, _frames, _chnl ); break; - case MoogSawWave: - updateNoSub( _ab, _frames, _chnl ); + case WaveShape::MoogSaw: + updateNoSub( _ab, _frames, _chnl ); break; - case ExponentialWave: - updateNoSub( _ab, _frames, _chnl ); + case WaveShape::Exponential: + updateNoSub( _ab, _frames, _chnl ); break; - case WhiteNoise: - updateNoSub( _ab, _frames, _chnl ); + case WaveShape::WhiteNoise: + updateNoSub( _ab, _frames, _chnl ); break; - case UserDefinedWave: - updateNoSub( _ab, _frames, _chnl ); + case WaveShape::UserDefined: + updateNoSub( _ab, _frames, _chnl ); break; } } @@ -350,32 +351,32 @@ void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames, void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { - switch( m_waveShapeModel->value() ) + switch( static_cast(m_waveShapeModel->value()) ) { - case SineWave: + case WaveShape::Sine: default: - updatePM( _ab, _frames, _chnl ); + updatePM( _ab, _frames, _chnl ); break; - case TriangleWave: - updatePM( _ab, _frames, _chnl ); + case WaveShape::Triangle: + updatePM( _ab, _frames, _chnl ); break; - case SawWave: - updatePM( _ab, _frames, _chnl ); + case WaveShape::Saw: + updatePM( _ab, _frames, _chnl ); break; - case SquareWave: - updatePM( _ab, _frames, _chnl ); + case WaveShape::Square: + updatePM( _ab, _frames, _chnl ); break; - case MoogSawWave: - updatePM( _ab, _frames, _chnl ); + case WaveShape::MoogSaw: + updatePM( _ab, _frames, _chnl ); break; - case ExponentialWave: - updatePM( _ab, _frames, _chnl ); + case WaveShape::Exponential: + updatePM( _ab, _frames, _chnl ); break; - case WhiteNoise: - updatePM( _ab, _frames, _chnl ); + case WaveShape::WhiteNoise: + updatePM( _ab, _frames, _chnl ); break; - case UserDefinedWave: - updatePM( _ab, _frames, _chnl ); + case WaveShape::UserDefined: + updatePM( _ab, _frames, _chnl ); break; } } @@ -386,32 +387,32 @@ void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames, void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { - switch( m_waveShapeModel->value() ) + switch( static_cast(m_waveShapeModel->value()) ) { - case SineWave: + case WaveShape::Sine: default: - updateAM( _ab, _frames, _chnl ); + updateAM( _ab, _frames, _chnl ); break; - case TriangleWave: - updateAM( _ab, _frames, _chnl ); + case WaveShape::Triangle: + updateAM( _ab, _frames, _chnl ); break; - case SawWave: - updateAM( _ab, _frames, _chnl ); + case WaveShape::Saw: + updateAM( _ab, _frames, _chnl ); break; - case SquareWave: - updateAM( _ab, _frames, _chnl ); + case WaveShape::Square: + updateAM( _ab, _frames, _chnl ); break; - case MoogSawWave: - updateAM( _ab, _frames, _chnl ); + case WaveShape::MoogSaw: + updateAM( _ab, _frames, _chnl ); break; - case ExponentialWave: - updateAM( _ab, _frames, _chnl ); + case WaveShape::Exponential: + updateAM( _ab, _frames, _chnl ); break; - case WhiteNoise: - updateAM( _ab, _frames, _chnl ); + case WaveShape::WhiteNoise: + updateAM( _ab, _frames, _chnl ); break; - case UserDefinedWave: - updateAM( _ab, _frames, _chnl ); + case WaveShape::UserDefined: + updateAM( _ab, _frames, _chnl ); break; } } @@ -422,32 +423,32 @@ void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames, void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { - switch( m_waveShapeModel->value() ) + switch( static_cast(m_waveShapeModel->value()) ) { - case SineWave: + case WaveShape::Sine: default: - updateMix( _ab, _frames, _chnl ); + updateMix( _ab, _frames, _chnl ); break; - case TriangleWave: - updateMix( _ab, _frames, _chnl ); + case WaveShape::Triangle: + updateMix( _ab, _frames, _chnl ); break; - case SawWave: - updateMix( _ab, _frames, _chnl ); + case WaveShape::Saw: + updateMix( _ab, _frames, _chnl ); break; - case SquareWave: - updateMix( _ab, _frames, _chnl ); + case WaveShape::Square: + updateMix( _ab, _frames, _chnl ); break; - case MoogSawWave: - updateMix( _ab, _frames, _chnl ); + case WaveShape::MoogSaw: + updateMix( _ab, _frames, _chnl ); break; - case ExponentialWave: - updateMix( _ab, _frames, _chnl ); + case WaveShape::Exponential: + updateMix( _ab, _frames, _chnl ); break; - case WhiteNoise: - updateMix( _ab, _frames, _chnl ); + case WaveShape::WhiteNoise: + updateMix( _ab, _frames, _chnl ); break; - case UserDefinedWave: - updateMix( _ab, _frames, _chnl ); + case WaveShape::UserDefined: + updateMix( _ab, _frames, _chnl ); break; } } @@ -458,32 +459,32 @@ void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames, void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { - switch( m_waveShapeModel->value() ) + switch( static_cast(m_waveShapeModel->value()) ) { - case SineWave: + case WaveShape::Sine: default: - updateSync( _ab, _frames, _chnl ); + updateSync( _ab, _frames, _chnl ); break; - case TriangleWave: - updateSync( _ab, _frames, _chnl ); + case WaveShape::Triangle: + updateSync( _ab, _frames, _chnl ); break; - case SawWave: - updateSync( _ab, _frames, _chnl ); + case WaveShape::Saw: + updateSync( _ab, _frames, _chnl ); break; - case SquareWave: - updateSync( _ab, _frames, _chnl ); + case WaveShape::Square: + updateSync( _ab, _frames, _chnl ); break; - case MoogSawWave: - updateSync( _ab, _frames, _chnl ); + case WaveShape::MoogSaw: + updateSync( _ab, _frames, _chnl ); break; - case ExponentialWave: - updateSync( _ab, _frames, _chnl ); + case WaveShape::Exponential: + updateSync( _ab, _frames, _chnl ); break; - case WhiteNoise: - updateSync( _ab, _frames, _chnl ); + case WaveShape::WhiteNoise: + updateSync( _ab, _frames, _chnl ); break; - case UserDefinedWave: - updateSync( _ab, _frames, _chnl ); + case WaveShape::UserDefined: + updateSync( _ab, _frames, _chnl ); break; } } @@ -494,32 +495,32 @@ void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames, void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { - switch( m_waveShapeModel->value() ) + switch( static_cast(m_waveShapeModel->value()) ) { - case SineWave: + case WaveShape::Sine: default: - updateFM( _ab, _frames, _chnl ); + updateFM( _ab, _frames, _chnl ); break; - case TriangleWave: - updateFM( _ab, _frames, _chnl ); + case WaveShape::Triangle: + updateFM( _ab, _frames, _chnl ); break; - case SawWave: - updateFM( _ab, _frames, _chnl ); + case WaveShape::Saw: + updateFM( _ab, _frames, _chnl ); break; - case SquareWave: - updateFM( _ab, _frames, _chnl ); + case WaveShape::Square: + updateFM( _ab, _frames, _chnl ); break; - case MoogSawWave: - updateFM( _ab, _frames, _chnl ); + case WaveShape::MoogSaw: + updateFM( _ab, _frames, _chnl ); break; - case ExponentialWave: - updateFM( _ab, _frames, _chnl ); + case WaveShape::Exponential: + updateFM( _ab, _frames, _chnl ); break; - case WhiteNoise: - updateFM( _ab, _frames, _chnl ); + case WaveShape::WhiteNoise: + updateFM( _ab, _frames, _chnl ); break; - case UserDefinedWave: - updateFM( _ab, _frames, _chnl ); + case WaveShape::UserDefined: + updateFM( _ab, _frames, _chnl ); break; } } @@ -568,7 +569,7 @@ float Oscillator::syncInit( sampleFrame * _ab, const fpp_t _frames, // if we have no sub-osc, we can't do any modulation... just get our samples -template +template void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { @@ -586,7 +587,7 @@ void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames, // do pm by using sub-osc as modulator -template +template void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { @@ -607,7 +608,7 @@ void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames, // do am by using sub-osc as modulator -template +template void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { @@ -626,7 +627,7 @@ void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames, // do mix by using sub-osc as mix-sample -template +template void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { @@ -646,7 +647,7 @@ void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames, // sync with sub-osc (every time sub-osc starts new period, we also start new // period) -template +template void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { @@ -669,7 +670,7 @@ void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames, // do fm by using sub-osc as modulator -template +template void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { @@ -690,7 +691,7 @@ void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames, template<> -inline sample_t Oscillator::getSample(const float sample) +inline sample_t Oscillator::getSample(const float sample) { const float current_freq = m_freq * m_detuning_div_samplerate * Engine::audioEngine()->processingSampleRate(); @@ -708,12 +709,12 @@ inline sample_t Oscillator::getSample(const float sample) template<> -inline sample_t Oscillator::getSample( +inline sample_t Oscillator::getSample( const float _sample ) { if (m_useWaveTable && !m_isModulator) { - return wtSample(s_waveTables[WaveShapes::TriangleWave - FirstWaveShapeTable],_sample); + return wtSample(s_waveTables[static_cast(WaveShape::Triangle) - FirstWaveShapeTable],_sample); } else { @@ -725,12 +726,12 @@ inline sample_t Oscillator::getSample( template<> -inline sample_t Oscillator::getSample( +inline sample_t Oscillator::getSample( const float _sample ) { if (m_useWaveTable && !m_isModulator) { - return wtSample(s_waveTables[WaveShapes::SawWave - FirstWaveShapeTable], _sample); + return wtSample(s_waveTables[static_cast(WaveShape::Saw) - FirstWaveShapeTable], _sample); } else { @@ -742,12 +743,12 @@ inline sample_t Oscillator::getSample( template<> -inline sample_t Oscillator::getSample( +inline sample_t Oscillator::getSample( const float _sample ) { if (m_useWaveTable && !m_isModulator) { - return wtSample(s_waveTables[WaveShapes::SquareWave - FirstWaveShapeTable], _sample); + return wtSample(s_waveTables[static_cast(WaveShape::Square) - FirstWaveShapeTable], _sample); } else { @@ -759,12 +760,12 @@ inline sample_t Oscillator::getSample( template<> -inline sample_t Oscillator::getSample( +inline sample_t Oscillator::getSample( const float _sample ) { if (m_useWaveTable && !m_isModulator) { - return wtSample(s_waveTables[WaveShapes::MoogSawWave - FirstWaveShapeTable], _sample); + return wtSample(s_waveTables[static_cast(WaveShape::MoogSaw) - FirstWaveShapeTable], _sample); } else { @@ -776,12 +777,12 @@ inline sample_t Oscillator::getSample( template<> -inline sample_t Oscillator::getSample( +inline sample_t Oscillator::getSample( const float _sample ) { if (m_useWaveTable && !m_isModulator) { - return wtSample(s_waveTables[WaveShapes::ExponentialWave - FirstWaveShapeTable], _sample); + return wtSample(s_waveTables[static_cast(WaveShape::Exponential) - FirstWaveShapeTable], _sample); } else { @@ -793,7 +794,7 @@ inline sample_t Oscillator::getSample( template<> -inline sample_t Oscillator::getSample( +inline sample_t Oscillator::getSample( const float _sample ) { return( noiseSample( _sample ) ); @@ -803,7 +804,7 @@ inline sample_t Oscillator::getSample( template<> -inline sample_t Oscillator::getSample( +inline sample_t Oscillator::getSample( const float _sample ) { if (m_useWaveTable && !m_isModulator) diff --git a/src/core/PatternStore.cpp b/src/core/PatternStore.cpp index 3d0b60acf..c5a352139 100644 --- a/src/core/PatternStore.cpp +++ b/src/core/PatternStore.cpp @@ -44,7 +44,7 @@ PatternStore::PatternStore() : // not change upon setCurrentPattern()-call connect(&m_patternComboBoxModel, SIGNAL(dataUnchanged()), this, SLOT(currentPatternChanged())); - setType(PatternContainer); + setType(Type::Pattern); } @@ -109,7 +109,7 @@ bar_t PatternStore::lengthOfPattern(int pattern) const int PatternStore::numOfPatterns() const { - return Engine::getSong()->countTracks(Track::PatternTrack); + return Engine::getSong()->countTracks(Track::Type::Pattern); } @@ -174,7 +174,7 @@ void PatternStore::fixIncorrectPositions() void PatternStore::play() { - if (Engine::getSong()->playMode() != Song::Mode_PlayPattern) + if (Engine::getSong()->playMode() != Song::PlayMode::Pattern) { Engine::getSong()->playPattern(); } @@ -218,7 +218,7 @@ void PatternStore::currentPatternChanged() TrackList tl = Engine::getSong()->tracks(); for (Track * t : tl) { - if (t->type() == Track::PatternTrack) + if (t->type() == Track::Type::Pattern) { t->dataChanged(); } diff --git a/src/core/PeakController.cpp b/src/core/PeakController.cpp index af0e7e69d..cfcd3765c 100644 --- a/src/core/PeakController.cpp +++ b/src/core/PeakController.cpp @@ -46,7 +46,7 @@ bool PeakController::m_buggedFile; PeakController::PeakController( Model * _parent, PeakControllerEffect * _peak_effect ) : - Controller( Controller::PeakController, _parent, tr( "Peak Controller" ) ), + Controller( ControllerType::Peak, _parent, tr( "Peak Controller" ) ), m_peakEffect( _peak_effect ), m_currentSample( 0.0f ) { diff --git a/src/core/Piano.cpp b/src/core/Piano.cpp index 82323526e..0ddbf5c60 100644 --- a/src/core/Piano.cpp +++ b/src/core/Piano.cpp @@ -49,12 +49,12 @@ namespace lmms */ static const auto KEY_ORDER = std::array { -// C CIS D DIS - Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey, -// E F FIS G - Piano::WhiteKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey, -// GIS A AIS B - Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey +// C CIS D DIS + Piano::KeyType::White, Piano::KeyType::Black, Piano::KeyType::White, Piano::KeyType::Black, +// E F FIS G + Piano::KeyType::White, Piano::KeyType::White, Piano::KeyType::Black, Piano::KeyType::White, +// GIS A AIS B + Piano::KeyType::Black, Piano::KeyType::White, Piano::KeyType::Black, Piano::KeyType::White } ; @@ -127,7 +127,7 @@ bool Piano::isBlackKey(int key) { int keyCode = key % KeysPerOctave; - return KEY_ORDER[keyCode] == Piano::BlackKey; + return KEY_ORDER[keyCode] == Piano::KeyType::Black; } diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index edc183c23..973914501 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -50,7 +50,7 @@ static Plugin::Descriptor dummyPluginDescriptor = QT_TRANSLATE_NOOP( "PluginBrowser", "no description" ), "Tobias Doerffel ", 0x0100, - Plugin::Undefined, + Plugin::Type::Undefined, &dummyLoader, nullptr } ; diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index 3790a7ec9..ec0f4ec4e 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -109,7 +109,7 @@ Plugin::DescriptorList PluginFactory::descriptors() const return m_descriptors.values(); } -Plugin::DescriptorList PluginFactory::descriptors(Plugin::PluginTypes type) const +Plugin::DescriptorList PluginFactory::descriptors(Plugin::Type type) const { return m_descriptors.values(type); } diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index f8ae06bc7..c9cf3400f 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -34,43 +34,43 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) { switch (it) { - case unknownPortFlow: + case PluginIssueType::UnknownPortFlow: return "unknown port flow for mandatory port"; - case unknownPortType: + case PluginIssueType::UnknownPortType: return "unknown port type for mandatory port"; - case tooManyInputChannels: + case PluginIssueType::TooManyInputChannels: return "too many audio input channels"; - case tooManyOutputChannels: + case PluginIssueType::TooManyOutputChannels: return "too many audio output channels"; - case tooManyMidiInputChannels: + case PluginIssueType::TooManyMidiInputChannels: return "too many MIDI input channels"; - case tooManyMidiOutputChannels: + case PluginIssueType::TooManyMidiOutputChannels: return "too many MIDI output channels"; - case noOutputChannel: + case PluginIssueType::NoOutputChannel: return "no audio output channel"; - case portHasNoDef: + case PluginIssueType::PortHasNoDef: return "port is missing default value"; - case portHasNoMin: + case PluginIssueType::PortHasNoMin: return "port is missing min value"; - case portHasNoMax: + case PluginIssueType::PortHasNoMax: return "port is missing max value"; - case minGreaterMax: + case PluginIssueType::MinGreaterMax: return "port minimum is greater than maximum"; - case defaultValueNotInRange: + case PluginIssueType::DefaultValueNotInRange: return "default value is not in range [min, max]"; - case logScaleMinMissing: + case PluginIssueType::LogScaleMinMissing: return "logscale requires minimum value"; - case logScaleMaxMissing: + case PluginIssueType::LogScaleMaxMissing: return "logscale requires maximum value"; - case logScaleMinMaxDifferentSigns: + case PluginIssueType::LogScaleMinMaxDifferentSigns: return "logscale with min < 0 < max"; - case featureNotSupported: + case PluginIssueType::FeatureNotSupported: return "required feature not supported"; - case badPortType: + case PluginIssueType::BadPortType: return "unsupported port type"; - case blacklisted: + case PluginIssueType::Blacklisted: return "blacklisted plugin"; - case noIssue: + case PluginIssueType::NoIssue: return nullptr; } return nullptr; diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index 6a5b86424..0930de0de 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -48,7 +48,7 @@ public: m_dataMutex() { setJournalling( false ); - m_previewInstrumentTrack = dynamic_cast( Track::create( Track::InstrumentTrack, this ) ); + m_previewInstrumentTrack = dynamic_cast( Track::create( Track::Type::Instrument, this ) ); m_previewInstrumentTrack->setJournalling( false ); m_previewInstrumentTrack->setPreviewMode( true ); } @@ -116,7 +116,7 @@ PreviewTrackContainer * PresetPreviewPlayHandle::s_previewTC; PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, bool _load_by_plugin, DataFile *dataFile ) : - PlayHandle( TypePresetPreviewHandle ), + PlayHandle( Type::PresetPreviewHandle ), m_previewNote(nullptr) { setUsesBuffer( false ); @@ -169,7 +169,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, // make sure, our preset-preview-track does not appear in any MIDI- // devices list, so just disable receiving/sending MIDI-events at all s_previewTC->previewInstrumentTrack()-> - midiPort()->setMode( MidiPort::Disabled ); + midiPort()->setMode( MidiPort::Mode::Disabled ); Engine::audioEngine()->requestChangeInModel(); // create note-play-handle for it diff --git a/src/core/ProjectJournal.cpp b/src/core/ProjectJournal.cpp index bc1fee5c0..fc77c98e6 100644 --- a/src/core/ProjectJournal.cpp +++ b/src/core/ProjectJournal.cpp @@ -58,7 +58,7 @@ void ProjectJournal::undo() if( jo ) { - DataFile curState( DataFile::JournalData ); + DataFile curState( DataFile::Type::JournalData ); jo->saveState( curState, curState.content() ); m_redoCheckPoints.push( CheckPoint( c.joID, curState ) ); @@ -83,7 +83,7 @@ void ProjectJournal::redo() if( jo ) { - DataFile curState( DataFile::JournalData ); + DataFile curState( DataFile::Type::JournalData ); jo->saveState( curState, curState.content() ); m_undoCheckPoints.push( CheckPoint( c.joID, curState ) ); @@ -115,7 +115,7 @@ void ProjectJournal::addJournalCheckPoint( JournallingObject *jo ) { m_redoCheckPoints.clear(); - DataFile dataFile( DataFile::JournalData ); + DataFile dataFile( DataFile::Type::JournalData ); jo->saveState( dataFile, dataFile.content() ); m_undoCheckPoints.push( CheckPoint( jo->id(), dataFile ) ); diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index da6c729c8..3f101330a 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -42,15 +42,15 @@ namespace lmms const std::array ProjectRenderer::fileEncodeDevices { - FileEncodeDevice{ ProjectRenderer::WaveFile, + FileEncodeDevice{ ProjectRenderer::ExportFileFormat::Wave, QT_TRANSLATE_NOOP( "ProjectRenderer", "WAV (*.wav)" ), ".wav", &AudioFileWave::getInst }, - FileEncodeDevice{ ProjectRenderer::FlacFile, + FileEncodeDevice{ ProjectRenderer::ExportFileFormat::Flac, QT_TRANSLATE_NOOP("ProjectRenderer", "FLAC (*.flac)"), ".flac", &AudioFileFlac::getInst }, - FileEncodeDevice{ ProjectRenderer::OggFile, + FileEncodeDevice{ ProjectRenderer::ExportFileFormat::Ogg, QT_TRANSLATE_NOOP( "ProjectRenderer", "OGG (*.ogg)" ), ".ogg", #ifdef LMMS_HAVE_OGGVORBIS @@ -59,7 +59,7 @@ const std::array ProjectRenderer::fileEnco nullptr #endif }, - FileEncodeDevice{ ProjectRenderer::MP3File, + FileEncodeDevice{ ProjectRenderer::ExportFileFormat::MP3, QT_TRANSLATE_NOOP( "ProjectRenderer", "MP3 (*.mp3)" ), ".mp3", #ifdef LMMS_HAVE_MP3LAME @@ -71,7 +71,7 @@ const std::array ProjectRenderer::fileEnco // Insert your own file-encoder infos here. // Maybe one day the user can add own encoders inside the program. - FileEncodeDevice{ ProjectRenderer::NumFileFormats, nullptr, nullptr, nullptr } + FileEncodeDevice{ ProjectRenderer::ExportFileFormat::Count, nullptr, nullptr, nullptr } } ; @@ -80,7 +80,7 @@ const std::array ProjectRenderer::fileEnco ProjectRenderer::ProjectRenderer( const AudioEngine::qualitySettings & qualitySettings, const OutputSettings & outputSettings, - ExportFileFormats exportFileFormat, + ExportFileFormat exportFileFormat, const QString & outputFilename ) : QThread( Engine::audioEngine() ), m_fileDev( nullptr ), @@ -88,7 +88,7 @@ ProjectRenderer::ProjectRenderer( const AudioEngine::qualitySettings & qualitySe m_progress( 0 ), m_abort( false ) { - AudioFileDeviceInstantiaton audioEncoderFactory = fileEncodeDevices[exportFileFormat].m_getDevInst; + AudioFileDeviceInstantiaton audioEncoderFactory = fileEncodeDevices[static_cast(exportFileFormat)].m_getDevInst; if (audioEncoderFactory) { @@ -110,11 +110,11 @@ ProjectRenderer::ProjectRenderer( const AudioEngine::qualitySettings & qualitySe // Little help function for getting file format from a file extension // (only for registered file-encoders). -ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension( +ProjectRenderer::ExportFileFormat ProjectRenderer::getFileFormatFromExtension( const QString & _ext ) { int idx = 0; - while( fileEncodeDevices[idx].m_fileFormat != NumFileFormats ) + while( fileEncodeDevices[idx].m_fileFormat != ExportFileFormat::Count ) { if( QString( fileEncodeDevices[idx].m_extension ) == _ext ) { @@ -123,16 +123,16 @@ ProjectRenderer::ExportFileFormats ProjectRenderer::getFileFormatFromExtension( ++idx; } - return( WaveFile ); // Default. + return( ExportFileFormat::Wave ); // Default. } QString ProjectRenderer::getFileExtensionFromFormat( - ExportFileFormats fmt ) + ExportFileFormat fmt ) { - return fileEncodeDevices[fmt].m_extension; + return fileEncodeDevices[static_cast(fmt)].m_extension; } diff --git a/src/core/RenderManager.cpp b/src/core/RenderManager.cpp index 8c031b970..969cad15b 100644 --- a/src/core/RenderManager.cpp +++ b/src/core/RenderManager.cpp @@ -37,7 +37,7 @@ namespace lmms RenderManager::RenderManager( const AudioEngine::qualitySettings & qualitySettings, const OutputSettings & outputSettings, - ProjectRenderer::ExportFileFormats fmt, + ProjectRenderer::ExportFileFormat fmt, QString outputPath) : m_qualitySettings(qualitySettings), m_oldQualitySettings( Engine::audioEngine()->currentQualitySettings() ), @@ -102,11 +102,11 @@ void RenderManager::renderTracks() // find all currently unnmuted tracks -- we want to render these. for (const auto& tk : tl) { - Track::TrackTypes type = tk->type(); + Track::Type type = tk->type(); // Don't render automation tracks if ( tk->isMuted() == false && - ( type == Track::InstrumentTrack || type == Track::SampleTrack ) ) + ( type == Track::Type::Instrument || type == Track::Type::Sample ) ) { m_unmuted.push_back(tk); } @@ -115,11 +115,11 @@ void RenderManager::renderTracks() const TrackContainer::TrackList t2 = Engine::patternStore()->tracks(); for (const auto& tk : t2) { - Track::TrackTypes type = tk->type(); + Track::Type type = tk->type(); // Don't render automation tracks if ( tk->isMuted() == false && - ( type == Track::InstrumentTrack || type == Track::SampleTrack ) ) + ( type == Track::Type::Instrument || type == Track::Type::Sample ) ) { m_unmuted.push_back(tk); } diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 2f35b23a8..775db125b 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -738,7 +738,7 @@ bool SampleBuffer::play( // this holds the index of the first frame to play f_cnt_t playFrame = std::max(state->m_frameIndex, startFrame); - if (loopMode == LoopOff) + if (loopMode == LoopMode::Off) { if (playFrame >= endFrame || (endFrame - playFrame) / freqFactor == 0) { @@ -746,7 +746,7 @@ bool SampleBuffer::play( return false; } } - else if (loopMode == LoopOn) + else if (loopMode == LoopMode::On) { playFrame = getLoopedIndex(playFrame, loopStartFrame, loopEndFrame); } @@ -786,14 +786,14 @@ bool SampleBuffer::play( // Advance switch (loopMode) { - case LoopOff: + case LoopMode::Off: playFrame += srcData.input_frames_used; break; - case LoopOn: + case LoopMode::On: playFrame += srcData.input_frames_used; playFrame = getLoopedIndex(playFrame, loopStartFrame, loopEndFrame); break; - case LoopPingPong: + case LoopMode::PingPong: { f_cnt_t left = srcData.input_frames_used; if (state->isBackwards()) @@ -825,14 +825,14 @@ bool SampleBuffer::play( // Advance switch (loopMode) { - case LoopOff: + case LoopMode::Off: playFrame += frames; break; - case LoopOn: + case LoopMode::On: playFrame += frames; playFrame = getLoopedIndex(playFrame, loopStartFrame, loopEndFrame); break; - case LoopPingPong: + case LoopMode::PingPong: { f_cnt_t left = frames; if (state->isBackwards()) @@ -883,14 +883,14 @@ sampleFrame * SampleBuffer::getSampleFragment( f_cnt_t end ) const { - if (loopMode == LoopOff) + if (loopMode == LoopMode::Off) { if (index + frames <= end) { return m_data + index; } } - else if (loopMode == LoopOn) + else if (loopMode == LoopMode::On) { if (index + frames <= loopEnd) { @@ -907,13 +907,13 @@ sampleFrame * SampleBuffer::getSampleFragment( *tmp = MM_ALLOC( frames); - if (loopMode == LoopOff) + if (loopMode == LoopMode::Off) { f_cnt_t available = end - index; memcpy(*tmp, m_data + index, available * BYTES_PER_FRAME); memset(*tmp + available, 0, (frames - available) * BYTES_PER_FRAME); } - else if (loopMode == LoopOn) + else if (loopMode == LoopMode::On) { f_cnt_t copied = std::min(frames, loopEnd - index); memcpy(*tmp, m_data + index, copied * BYTES_PER_FRAME); diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 87d6351bc..592a63827 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -53,7 +53,7 @@ SampleClip::SampleClip( Track * _track ) : this, SLOT(updateLength())); //care about positionmarker - gui::TimeLineWidget* timeLine = Engine::getSong()->getPlayPos( Engine::getSong()->Mode_PlaySong ).m_timeLine; + gui::TimeLineWidget* timeLine = Engine::getSong()->getPlayPos( Song::PlayMode::Song ).m_timeLine; if( timeLine ) { connect( timeLine, SIGNAL(positionMarkerMoved()), this, SLOT(playbackPositionChanged())); @@ -74,11 +74,11 @@ SampleClip::SampleClip( Track * _track ) : switch( getTrack()->trackContainer()->type() ) { - case TrackContainer::PatternContainer: + case TrackContainer::Type::Pattern: setAutoResize( true ); break; - case TrackContainer::SongContainer: + case TrackContainer::Type::Song: // move down default: setAutoResize( false ); @@ -179,7 +179,7 @@ void SampleClip::toggleRecord() void SampleClip::playbackPositionChanged() { - Engine::audioEngine()->removePlayHandlesOfTypes( getTrack(), PlayHandle::TypeSamplePlayHandle ); + Engine::audioEngine()->removePlayHandlesOfTypes( getTrack(), PlayHandle::Type::SamplePlayHandle ); auto st = dynamic_cast(getTrack()); st->setPlayingClips( false ); } diff --git a/src/core/SamplePlayHandle.cpp b/src/core/SamplePlayHandle.cpp index 77658e1d8..ea27146cb 100644 --- a/src/core/SamplePlayHandle.cpp +++ b/src/core/SamplePlayHandle.cpp @@ -36,7 +36,7 @@ namespace lmms SamplePlayHandle::SamplePlayHandle( SampleBuffer* sampleBuffer , bool ownAudioPort ) : - PlayHandle( TypeSamplePlayHandle ), + PlayHandle( Type::SamplePlayHandle ), m_sampleBuffer( sharedObject::ref( sampleBuffer ) ), m_doneMayReturnTrue( true ), m_frame( 0 ), diff --git a/src/core/SampleRecordHandle.cpp b/src/core/SampleRecordHandle.cpp index edebd27fd..10e970b8f 100644 --- a/src/core/SampleRecordHandle.cpp +++ b/src/core/SampleRecordHandle.cpp @@ -37,7 +37,7 @@ namespace lmms SampleRecordHandle::SampleRecordHandle( SampleClip* clip ) : - PlayHandle( TypeSamplePlayHandle ), + PlayHandle( Type::SamplePlayHandle ), m_framesRecorded( 0 ), m_minLength( clip->length() ), m_track( clip->getTrack() ), diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 6f4c5212e..e8073f225 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -68,7 +68,7 @@ tick_t TimePos::s_ticksPerBar = DefaultTicksPerBar; Song::Song() : TrackContainer(), m_globalAutomationTrack( dynamic_cast( - Track::create( Track::HiddenAutomationTrack, + Track::create( Track::Type::HiddenAutomation, this ) ) ), m_tempoModel( DefaultTempo, MinTempo, MaxTempo, this, tr( "Tempo" ) ), m_timeSigModel( this ), @@ -89,7 +89,7 @@ Song::Song() : m_savingProject( false ), m_loadingProject( false ), m_isCancelled( false ), - m_playMode( Mode_None ), + m_playMode( PlayMode::None ), m_length( 0 ), m_midiClipToPlay( nullptr ), m_loopMidiClip( false ), @@ -117,7 +117,7 @@ Song::Song() : this, SLOT(masterPitchChanged()));*/ qRegisterMetaType( "lmms::Note" ); - setType( SongContainer ); + setType( Type::Song ); for (auto& scale : m_scales) {scale = std::make_shared();} for (auto& keymap : m_keymaps) {keymap = std::make_shared();} @@ -186,11 +186,11 @@ void Song::setTimeSignature() void Song::savePos() { - gui::TimeLineWidget* tl = m_playPos[m_playMode].m_timeLine; + gui::TimeLineWidget* tl = getPlayPos().m_timeLine; if( tl != nullptr ) { - tl->savePos( m_playPos[m_playMode] ); + tl->savePos( getPlayPos() ); } } @@ -205,7 +205,7 @@ void Song::processNextBuffer() if (!m_playing) { return; } // At the beginning of the song, we have to reset the LFOs - if (m_playMode == Mode_PlaySong && getPlayPos() == 0) + if (m_playMode == PlayMode::Song && getPlayPos() == 0) { EnvelopeAndLfoParameters::instances()->reset(); } @@ -216,11 +216,11 @@ void Song::processNextBuffer() // Determine the list of tracks to play and the clip number switch (m_playMode) { - case Mode_PlaySong: + case PlayMode::Song: trackList = tracks(); break; - case Mode_PlayPattern: + case PlayMode::Pattern: if (Engine::patternStore()->numOfPatterns() > 0) { clipNum = Engine::patternStore()->currentPattern(); @@ -228,7 +228,7 @@ void Song::processNextBuffer() } break; - case Mode_PlayMidiClip: + case PlayMode::MidiClip: if (m_midiClipToPlay) { clipNum = m_midiClipToPlay->getTrack()->getClipNum(m_midiClipToPlay); @@ -291,11 +291,11 @@ void Song::processNextBuffer() // If we are playing a pattern track, or a MIDI clip with no loop enabled, // loop back to the beginning when we reach the end - if (m_playMode == Mode_PlayPattern) + if (m_playMode == PlayMode::Pattern) { enforceLoop(TimePos{0}, TimePos{Engine::patternStore()->lengthOfCurrentPattern(), 0}); } - else if (m_playMode == Mode_PlayMidiClip && m_loopMidiClip && !loopEnabled) + else if (m_playMode == PlayMode::MidiClip && m_loopMidiClip && !loopEnabled) { enforceLoop(TimePos{0}, m_midiClipToPlay->length()); } @@ -349,9 +349,9 @@ void Song::processNextBuffer() frameOffsetInPeriod += framesToPlay; frameOffsetInTick += framesToPlay; getPlayPos().setCurrentFrame(frameOffsetInTick); - m_elapsedMilliSeconds[m_playMode] += TimePos::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo()); - m_elapsedBars = m_playPos[Mode_PlaySong].getBar(); - m_elapsedTicks = (m_playPos[Mode_PlaySong].getTicks() % ticksPerBar()) / 48; + m_elapsedMilliSeconds[static_cast(m_playMode)] += TimePos::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo()); + m_elapsedBars = getPlayPos(PlayMode::Song).getBar(); + m_elapsedTicks = (getPlayPos(PlayMode::Song).getTicks() % ticksPerBar()) / 48; } } @@ -367,12 +367,12 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp switch (m_playMode) { - case Mode_PlaySong: + case PlayMode::Song: break; - case Mode_PlayPattern: + case PlayMode::Pattern: { Q_ASSERT(tracklist.size() == 1); - Q_ASSERT(tracklist.at(0)->type() == Track::PatternTrack); + Q_ASSERT(tracklist.at(0)->type() == Track::Type::Pattern); auto patternTrack = dynamic_cast(tracklist.at(0)); container = Engine::patternStore(); clipNum = patternTrack->patternIndex(); @@ -388,7 +388,7 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp Track::clipVector clips; for (Track* track : tracks) { - if (track->type() == Track::AutomationTrack) { + if (track->type() == Track::Type::Automation) { track->getClipsInRange(clips, 0, timeStart); } } @@ -444,12 +444,12 @@ void Song::setModified(bool value) bool Song::isExportDone() const { - return !isExporting() || m_playPos[m_playMode] >= m_exportSongEnd; + return !isExporting() || getPlayPos() >= m_exportSongEnd; } int Song::getExportProgress() const { - TimePos pos = m_playPos[m_playMode]; + TimePos pos = getPlayPos(); if (pos >= m_exportSongEnd) { @@ -486,7 +486,7 @@ void Song::playSong() stop(); } - m_playMode = Mode_PlaySong; + m_playMode = PlayMode::Song; m_playing = true; m_paused = false; @@ -525,7 +525,7 @@ void Song::playPattern() stop(); } - m_playMode = Mode_PlayPattern; + m_playMode = PlayMode::Pattern; m_playing = true; m_paused = false; @@ -551,7 +551,7 @@ void Song::playMidiClip( const MidiClip* midiClipToPlay, bool loop ) if( m_midiClipToPlay != nullptr ) { - m_playMode = Mode_PlayMidiClip; + m_playMode = PlayMode::MidiClip; m_playing = true; m_paused = false; } @@ -589,14 +589,14 @@ void Song::updateLength() -void Song::setPlayPos( tick_t ticks, PlayModes playMode ) +void Song::setPlayPos( tick_t ticks, PlayMode playMode ) { - tick_t ticksFromPlayMode = m_playPos[playMode].getTicks(); + tick_t ticksFromPlayMode = getPlayPos(playMode).getTicks(); m_elapsedTicks += ticksFromPlayMode - ticks; - m_elapsedMilliSeconds[playMode] += TimePos::ticksToMilliseconds( ticks - ticksFromPlayMode, getTempo() ); - m_playPos[playMode].setTicks( ticks ); - m_playPos[playMode].setCurrentFrame( 0.0f ); - m_playPos[playMode].setJumped( true ); + m_elapsedMilliSeconds[static_cast(playMode)] += TimePos::ticksToMilliseconds( ticks - ticksFromPlayMode, getTempo() ); + getPlayPos(playMode).setTicks( ticks ); + getPlayPos(playMode).setCurrentFrame( 0.0f ); + getPlayPos(playMode).setJumped( true ); // send a signal if playposition changes during playback if( isPlaying() ) @@ -634,7 +634,7 @@ void Song::togglePause() void Song::stop() { // do not stop/reset things again if we're stopped already - if( m_playMode == Mode_None ) + if( m_playMode == PlayMode::None ) { return; } @@ -644,7 +644,7 @@ void Song::stop() // To avoid race conditions with the processing threads Engine::audioEngine()->requestChangeInModel(); - TimeLineWidget * tl = m_playPos[m_playMode].m_timeLine; + TimeLineWidget * tl = getPlayPos().m_timeLine; m_paused = false; m_recording = true; @@ -652,41 +652,41 @@ void Song::stop() { switch( tl->behaviourAtStop() ) { - case TimeLineWidget::BackToZero: - m_playPos[m_playMode].setTicks(0); - m_elapsedMilliSeconds[m_playMode] = 0; + case TimeLineWidget::BehaviourAtStopState::BackToZero: + getPlayPos().setTicks(0); + m_elapsedMilliSeconds[static_cast(m_playMode)] = 0; break; - case TimeLineWidget::BackToStart: + case TimeLineWidget::BehaviourAtStopState::BackToStart: if( tl->savedPos() >= 0 ) { - m_playPos[m_playMode].setTicks(tl->savedPos().getTicks()); + getPlayPos().setTicks(tl->savedPos().getTicks()); setToTime(tl->savedPos()); tl->savePos( -1 ); } break; - case TimeLineWidget::KeepStopPosition: + case TimeLineWidget::BehaviourAtStopState::KeepStopPosition: break; } } else { - m_playPos[m_playMode].setTicks( 0 ); - m_elapsedMilliSeconds[m_playMode] = 0; + getPlayPos().setTicks( 0 ); + m_elapsedMilliSeconds[static_cast(m_playMode)] = 0; } m_playing = false; - m_elapsedMilliSeconds[Mode_None] = m_elapsedMilliSeconds[m_playMode]; - m_playPos[Mode_None].setTicks(m_playPos[m_playMode].getTicks()); + m_elapsedMilliSeconds[static_cast(PlayMode::None)] = m_elapsedMilliSeconds[static_cast(m_playMode)]; + getPlayPos(PlayMode::None).setTicks(getPlayPos().getTicks()); - m_playPos[m_playMode].setCurrentFrame( 0 ); + getPlayPos().setCurrentFrame( 0 ); m_vstSyncController.setPlaybackState( m_exporting ); m_vstSyncController.setAbsolutePosition( - m_playPos[m_playMode].getTicks() - + m_playPos[m_playMode].currentFrame() + getPlayPos().getTicks() + + getPlayPos().currentFrame() / (double) Engine::framesPerTick() ); // remove all note-play-handles that are active @@ -701,7 +701,7 @@ void Song::stop() } m_oldAutomatedValues.clear(); - m_playMode = Mode_None; + m_playMode = PlayMode::None; Engine::audioEngine()->doneChangeInModel(); @@ -721,19 +721,19 @@ void Song::startExport() if (m_renderBetweenMarkers) { - m_exportSongBegin = m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin(); - m_exportSongEnd = m_exportLoopEnd = m_playPos[Mode_PlaySong].m_timeLine->loopEnd(); + m_exportSongBegin = m_exportLoopBegin = getPlayPos(PlayMode::Song).m_timeLine->loopBegin(); + m_exportSongEnd = m_exportLoopEnd = getPlayPos(PlayMode::Song).m_timeLine->loopEnd(); - m_playPos[Mode_PlaySong].setTicks( m_playPos[Mode_PlaySong].m_timeLine->loopBegin().getTicks() ); + getPlayPos(PlayMode::Song).setTicks( getPlayPos(PlayMode::Song).m_timeLine->loopBegin().getTicks() ); } else { m_exportSongEnd = TimePos(m_length, 0); // Handle potentially ridiculous loop points gracefully. - if (m_loopRenderCount > 1 && m_playPos[Mode_PlaySong].m_timeLine->loopEnd() > m_exportSongEnd) + if (m_loopRenderCount > 1 && getPlayPos(PlayMode::Song).m_timeLine->loopEnd() > m_exportSongEnd) { - m_exportSongEnd = m_playPos[Mode_PlaySong].m_timeLine->loopEnd(); + m_exportSongEnd = getPlayPos(PlayMode::Song).m_timeLine->loopEnd(); } if (!m_exportLoop) @@ -741,17 +741,17 @@ void Song::startExport() m_exportSongBegin = TimePos(0,0); // FIXME: remove this check once we load timeline in headless mode - if (m_playPos[Mode_PlaySong].m_timeLine) + if (getPlayPos(PlayMode::Song).m_timeLine) { - m_exportLoopBegin = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && - m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? - m_playPos[Mode_PlaySong].m_timeLine->loopBegin() : TimePos(0,0); - m_exportLoopEnd = m_playPos[Mode_PlaySong].m_timeLine->loopBegin() < m_exportSongEnd && - m_playPos[Mode_PlaySong].m_timeLine->loopEnd() <= m_exportSongEnd ? - m_playPos[Mode_PlaySong].m_timeLine->loopEnd() : TimePos(0,0); + m_exportLoopBegin = getPlayPos(PlayMode::Song).m_timeLine->loopBegin() < m_exportSongEnd && + getPlayPos(PlayMode::Song).m_timeLine->loopEnd() <= m_exportSongEnd ? + getPlayPos(PlayMode::Song).m_timeLine->loopBegin() : TimePos(0,0); + m_exportLoopEnd = getPlayPos(PlayMode::Song).m_timeLine->loopBegin() < m_exportSongEnd && + getPlayPos(PlayMode::Song).m_timeLine->loopEnd() <= m_exportSongEnd ? + getPlayPos(PlayMode::Song).m_timeLine->loopEnd() : TimePos(0,0); } - m_playPos[Mode_PlaySong].setTicks( 0 ); + getPlayPos(PlayMode::Song).setTicks( 0 ); } m_exportEffectiveLength = (m_exportLoopBegin - m_exportSongBegin) + (m_exportLoopEnd - m_exportLoopBegin) @@ -784,7 +784,7 @@ void Song::insertBar() { // FIXME journal batch of tracks instead of each track individually if (track->numOfClips() > 0) { track->addJournalCheckPoint(); } - track->insertBar(m_playPos[Mode_PlaySong]); + track->insertBar(getPlayPos(PlayMode::Song)); } m_tracksMutex.unlock(); } @@ -799,7 +799,7 @@ void Song::removeBar() { // FIXME journal batch of tracks instead of each track individually if (track->numOfClips() > 0) { track->addJournalCheckPoint(); } - track->removeBar(m_playPos[Mode_PlaySong]); + track->removeBar(getPlayPos(PlayMode::Song)); } m_tracksMutex.unlock(); } @@ -809,7 +809,7 @@ void Song::removeBar() void Song::addPatternTrack() { - Track * t = Track::create(Track::PatternTrack, this); + Track * t = Track::create(Track::Type::Pattern, this); Engine::patternStore()->setCurrentPattern(dynamic_cast(t)->patternIndex()); } @@ -818,7 +818,7 @@ void Song::addPatternTrack() void Song::addSampleTrack() { - ( void )Track::create( Track::SampleTrack, this ); + ( void )Track::create( Track::Type::Sample, this ); } @@ -826,7 +826,7 @@ void Song::addSampleTrack() void Song::addAutomationTrack() { - ( void )Track::create( Track::AutomationTrack, this ); + ( void )Track::create( Track::Type::Automation, this ); } @@ -859,9 +859,9 @@ void Song::clearProject() stop(); } - for( int i = 0; i < Mode_Count; i++ ) + for( int i = 0; i < PlayModeCount; i++ ) { - setPlayPos( 0, ( PlayModes )i ); + setPlayPos( 0, ( PlayMode )i ); } @@ -960,15 +960,15 @@ void Song::createNewProject() setProjectFileName(""); Track * t; - t = Track::create( Track::InstrumentTrack, this ); + t = Track::create( Track::Type::Instrument, this ); dynamic_cast( t )->loadInstrument( "tripleoscillator" ); - t = Track::create(Track::InstrumentTrack, Engine::patternStore()); + t = Track::create(Track::Type::Instrument, Engine::patternStore()); dynamic_cast( t )->loadInstrument( "kicker" ); - Track::create( Track::SampleTrack, this ); - Track::create( Track::PatternTrack, this ); - Track::create( Track::AutomationTrack, this ); + Track::create( Track::Type::Sample, this ); + Track::create( Track::Type::Pattern, this ); + Track::create( Track::Type::Automation, this ); m_tempoModel.setInitValue( DefaultTempo ); m_timeSigModel.reset(); @@ -1080,10 +1080,10 @@ void Song::loadProject( const QString & fileName ) m_masterVolumeModel.loadSettings( dataFile.head(), "mastervol" ); m_masterPitchModel.loadSettings( dataFile.head(), "masterpitch" ); - if( m_playPos[Mode_PlaySong].m_timeLine ) + if( getPlayPos(PlayMode::Song).m_timeLine ) { // reset loop-point-state - m_playPos[Mode_PlaySong].m_timeLine->toggleLoopPoints( 0 ); + getPlayPos(PlayMode::Song).m_timeLine->toggleLoopPoints( 0 ); } if( !dataFile.content().firstChildElement( "track" ).isNull() ) @@ -1119,7 +1119,7 @@ void Song::loadProject( const QString & fileName ) if( nd.isElement() && nd.nodeName() == "track" ) { ++m_nLoadingTrack; - if (nd.toElement().attribute("type").toInt() == Track::PatternTrack) + if (static_cast(nd.toElement().attribute("type").toInt()) == Track::Type::Pattern) { n += nd.toElement().elementsByTagName("patterntrack").at(0) .toElement().firstChildElement().childNodes().count(); @@ -1167,9 +1167,9 @@ void Song::loadProject( const QString & fileName ) { getGUI()->getProjectNotes()->SerializingObject::restoreState( node.toElement() ); } - else if( node.nodeName() == m_playPos[Mode_PlaySong].m_timeLine->nodeName() ) + else if( node.nodeName() == getPlayPos(PlayMode::Song).m_timeLine->nodeName() ) { - m_playPos[Mode_PlaySong].m_timeLine->restoreState( node.toElement() ); + getPlayPos(PlayMode::Song).m_timeLine->restoreState( node.toElement() ); } } } @@ -1185,7 +1185,7 @@ void Song::loadProject( const QString & fileName ) // Remove dummy controllers that was added for correct connections m_controllers.erase(std::remove_if(m_controllers.begin(), m_controllers.end(), - [](Controller* c){return c->type() == Controller::DummyController;}), + [](Controller* c){return c->type() == Controller::ControllerType::Dummy;}), m_controllers.end()); // resolve all IDs so that autoModels are automated @@ -1235,7 +1235,7 @@ bool Song::saveProjectFile(const QString & filename, bool withResources) { using gui::getGUI; - DataFile dataFile( DataFile::SongProject ); + DataFile dataFile( DataFile::Type::SongProject ); m_savingProject = true; m_tempoModel.saveSettings( dataFile, dataFile.head(), "bpm" ); @@ -1253,7 +1253,7 @@ bool Song::saveProjectFile(const QString & filename, bool withResources) getGUI()->pianoRoll()->saveState( dataFile, dataFile.content() ); getGUI()->automationEditor()->m_editor->saveState( dataFile, dataFile.content() ); getGUI()->getProjectNotes()->SerializingObject::saveState( dataFile, dataFile.content() ); - m_playPos[Mode_PlaySong].m_timeLine->saveState( dataFile, dataFile.content() ); + getPlayPos(PlayMode::Song).m_timeLine->saveState( dataFile, dataFile.content() ); } saveControllerStates( dataFile, dataFile.content() ); @@ -1280,7 +1280,7 @@ bool Song::guiSaveProject() // Save the current song with the given filename bool Song::guiSaveProjectAs(const QString & filename) { - DataFile dataFile(DataFile::SongProject); + DataFile dataFile(DataFile::Type::SongProject); QString fileNameWithExtension = dataFile.nameWithExtension(filename); bool withResources = m_saveOptions.saveAsProjectBundle.value(); @@ -1328,7 +1328,7 @@ void Song::restoreControllerStates( const QDomElement & element ) else { // Fix indices to ensure correct connections - m_controllers.push_back(Controller::create(Controller::DummyController, this)); + m_controllers.push_back(Controller::create(Controller::ControllerType::Dummy, this)); } node = node.nextSibling(); diff --git a/src/core/TempoSyncKnobModel.cpp b/src/core/TempoSyncKnobModel.cpp index 6fb457f2c..e89716ab2 100644 --- a/src/core/TempoSyncKnobModel.cpp +++ b/src/core/TempoSyncKnobModel.cpp @@ -40,8 +40,8 @@ TempoSyncKnobModel::TempoSyncKnobModel( const float _val, const float _min, const float _scale, Model * _parent, const QString & _display_name ) : FloatModel( _val, _min, _max, _step, _parent, _display_name ), - m_tempoSyncMode( SyncNone ), - m_tempoLastSyncMode( SyncNone ), + m_tempoSyncMode( SyncMode::None ), + m_tempoLastSyncMode( SyncMode::None ), m_scale( _scale ), m_custom( _parent ) { @@ -55,15 +55,15 @@ TempoSyncKnobModel::TempoSyncKnobModel( const float _val, const float _min, void TempoSyncKnobModel::setTempoSync( QAction * _item ) { - setTempoSync( _item->data().toInt() ); + setTempoSync( static_cast(_item->data().toInt()) ); } -void TempoSyncKnobModel::setTempoSync( int _note_type ) +void TempoSyncKnobModel::setTempoSync( SyncMode _note_type ) { - setSyncMode( ( TempoSyncMode ) _note_type ); + setSyncMode( _note_type ); Engine::getSong()->setModified(); } @@ -74,34 +74,34 @@ void TempoSyncKnobModel::calculateTempoSyncTime( bpm_t _bpm ) { float conversionFactor = 1.0; - if( m_tempoSyncMode ) + if( m_tempoSyncMode != SyncMode::None ) { switch( m_tempoSyncMode ) { - case SyncCustom: + case SyncMode::Custom: conversionFactor = static_cast( m_custom.getDenominator() ) / static_cast( m_custom.getNumerator() ); break; - case SyncDoubleWholeNote: + case SyncMode::DoubleWholeNote: conversionFactor = 0.125; break; - case SyncWholeNote: + case SyncMode::WholeNote: conversionFactor = 0.25; break; - case SyncHalfNote: + case SyncMode::HalfNote: conversionFactor = 0.5; break; - case SyncQuarterNote: + case SyncMode::QuarterNote: conversionFactor = 1.0; break; - case SyncEighthNote: + case SyncMode::EighthNote: conversionFactor = 2.0; break; - case SyncSixteenthNote: + case SyncMode::SixteenthNote: conversionFactor = 4.0; break; - case SyncThirtysecondNote: + case SyncMode::ThirtysecondNote: conversionFactor = 8.0; break; default: ; @@ -138,18 +138,18 @@ void TempoSyncKnobModel::loadSettings( const QDomElement & _this, { FloatModel::loadSettings( _this, _name ); m_custom.loadSettings( _this, _name ); - setSyncMode( ( TempoSyncMode ) _this.attribute( _name + "_syncmode" ).toInt() ); + setSyncMode( ( SyncMode ) _this.attribute( _name + "_syncmode" ).toInt() ); } -void TempoSyncKnobModel::setSyncMode( TempoSyncMode _new_mode ) +void TempoSyncKnobModel::setSyncMode( SyncMode _new_mode ) { if( m_tempoSyncMode != _new_mode ) { m_tempoSyncMode = _new_mode; - if( _new_mode == SyncCustom ) + if( _new_mode == SyncMode::Custom ) { connect( &m_custom, SIGNAL(dataChanged()), this, SLOT(updateCustom()), @@ -174,7 +174,7 @@ void TempoSyncKnobModel::setScale( float _new_scale ) void TempoSyncKnobModel::updateCustom() { - setSyncMode( SyncCustom ); + setSyncMode( SyncMode::Custom ); } diff --git a/src/core/ToolPlugin.cpp b/src/core/ToolPlugin.cpp index 137029137..dfcacc1bc 100644 --- a/src/core/ToolPlugin.cpp +++ b/src/core/ToolPlugin.cpp @@ -42,7 +42,7 @@ ToolPlugin * ToolPlugin::instantiate( const QString & _plugin_name, Model * _par { Plugin * p = Plugin::instantiate( _plugin_name, _parent, nullptr ); // check whether instantiated plugin is a tool - if( p->type() == Plugin::Tool ) + if( p->type() == Plugin::Type::Tool ) { // everything ok, so return pointer return dynamic_cast( p ); diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 33b9c8ef3..b034b95fb 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -56,7 +56,7 @@ namespace lmms * * \todo check the definitions of all the properties - are they OK? */ -Track::Track( TrackTypes type, TrackContainer * tc ) : +Track::Track( Type type, TrackContainer * tc ) : Model( tc ), /*!< The track Model */ m_trackContainer( tc ), /*!< The track container object */ m_type( type ), /*!< The track type */ @@ -101,7 +101,7 @@ Track::~Track() * \param tt The type of track to create * \param tc The track container to attach to */ -Track * Track::create( TrackTypes tt, TrackContainer * tc ) +Track * Track::create( Type tt, TrackContainer * tc ) { Engine::audioEngine()->requestChangeInModel(); @@ -109,13 +109,13 @@ Track * Track::create( TrackTypes tt, TrackContainer * tc ) switch( tt ) { - case InstrumentTrack: t = new class InstrumentTrack( tc ); break; - case PatternTrack: t = new class PatternTrack( tc ); break; - case SampleTrack: t = new class SampleTrack( tc ); break; -// case EVENT_TRACK: -// case VIDEO_TRACK: - case AutomationTrack: t = new class AutomationTrack( tc ); break; - case HiddenAutomationTrack: + case Type::Instrument: t = new class InstrumentTrack( tc ); break; + case Type::Pattern: t = new class PatternTrack( tc ); break; + case Type::Sample: t = new class SampleTrack( tc ); break; +// case Type::Event: +// case Type::Video: + case Type::Automation: t = new class AutomationTrack( tc ); break; + case Type::HiddenAutomation: t = new class AutomationTrack( tc, true ); break; default: break; } @@ -145,7 +145,7 @@ Track * Track::create( const QDomElement & element, TrackContainer * tc ) Engine::audioEngine()->requestChangeInModel(); Track * t = create( - static_cast( element.attribute( "type" ).toInt() ), + static_cast( element.attribute( "type" ).toInt() ), tc ); if( t != nullptr ) { @@ -197,7 +197,7 @@ void Track::saveSettings( QDomDocument & doc, QDomElement & element ) { element.setTagName( "track" ); } - element.setAttribute( "type", type() ); + element.setAttribute( "type", static_cast(type()) ); element.setAttribute( "name", name() ); m_mutedModel.saveSettings( doc, element, "muted" ); m_soloModel.saveSettings( doc, element, "solo" ); @@ -249,7 +249,7 @@ void Track::saveSettings( QDomDocument & doc, QDomElement & element ) */ void Track::loadSettings( const QDomElement & element ) { - if( element.attribute( "type" ).toInt() != type() ) + if( static_cast(element.attribute( "type" ).toInt()) != type() ) { qWarning( "Current track-type does not match track-type of " "settings-node!\n" ); @@ -613,7 +613,7 @@ void Track::toggleSolo() { track->setMuted(false); } - else if (soloLegacyBehavior || track->type() != AutomationTrack) + else if (soloLegacyBehavior || track->type() != Type::Automation) { track->setMuted(true); } @@ -626,7 +626,7 @@ void Track::toggleSolo() { // Unless we are on the sololegacybehavior mode, only restores the // mute state if the track isn't an Automation Track - if (soloLegacyBehavior || track->type() != AutomationTrack) + if (soloLegacyBehavior || track->type() != Type::Automation) { track->setMuted(track->m_mutedBeforeSolo); } diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index af96ff1c4..d4120e761 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -156,13 +156,13 @@ void TrackContainer::loadSettings( const QDomElement & _this ) -int TrackContainer::countTracks( Track::TrackTypes _tt ) const +int TrackContainer::countTracks( Track::Type _tt ) const { int cnt = 0; m_tracksMutex.lockForRead(); for (const auto& track : m_tracks) { - if (track->type() == _tt || _tt == Track::NumTrackTypes) + if (track->type() == _tt || _tt == Track::Type::Count) { ++cnt; } @@ -176,7 +176,7 @@ int TrackContainer::countTracks( Track::TrackTypes _tt ) const void TrackContainer::addTrack( Track * _track ) { - if( _track->type() != Track::HiddenAutomationTrack ) + if( _track->type() != Track::Type::HiddenAutomation ) { _track->lock(); m_tracksMutex.lockForWrite(); @@ -268,9 +268,9 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra switch(track->type()) { - case Track::AutomationTrack: - case Track::HiddenAutomationTrack: - case Track::PatternTrack: + case Track::Type::Automation: + case Track::Type::HiddenAutomation: + case Track::Type::Pattern: if (clipNum < 0) { track->getClipsInRange(clips, 0, time); } else { diff --git a/src/core/UpgradeExtendedNoteRange.cpp b/src/core/UpgradeExtendedNoteRange.cpp index 41cbc5c26..e61da3723 100644 --- a/src/core/UpgradeExtendedNoteRange.cpp +++ b/src/core/UpgradeExtendedNoteRange.cpp @@ -183,10 +183,10 @@ static void fixTrack(QDomElement & track, std::set & automatedBase return; } - Track::TrackTypes const trackType = static_cast(track.attribute("type").toInt()); + Track::Type const trackType = static_cast(track.attribute("type").toInt()); // BB tracks need special handling because they contain a track container of their own - if (trackType == Track::PatternTrack) + if (trackType == Track::Type::Pattern) { // Assuming that a BB track cannot contain another BB track here... QDomNodeList subTracks = track.elementsByTagName("track"); @@ -233,7 +233,7 @@ static void fixAutomationTracks(QDomElement & song, std::set const for (int i = 0; i < tracks.size(); ++i) { QDomElement currentTrack = tracks.item(i).toElement(); - if (currentTrack.attribute("type").toInt() != Track::AutomationTrack) + if (static_cast(currentTrack.attribute("type").toInt()) != Track::Type::Automation) { continue; } diff --git a/src/core/audio/AudioFileFlac.cpp b/src/core/audio/AudioFileFlac.cpp index 9b49017c8..af71003d1 100644 --- a/src/core/audio/AudioFileFlac.cpp +++ b/src/core/audio/AudioFileFlac.cpp @@ -58,8 +58,8 @@ bool AudioFileFlac::startEncoding() switch (getOutputSettings().getBitDepth()) { - case OutputSettings::Depth_24Bit: - case OutputSettings::Depth_32Bit: + case OutputSettings::BitDepth::Depth24Bit: + case OutputSettings::BitDepth::Depth32Bit: // FLAC does not support 32bit sampling, so take it as 24. m_sfinfo.format |= SF_FORMAT_PCM_24; break; @@ -94,7 +94,7 @@ void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const fram OutputSettings::BitDepth depth = getOutputSettings().getBitDepth(); float clipvalue = std::nextafterf( -1.0f, 0.0f ); - if (depth == OutputSettings::Depth_24Bit || depth == OutputSettings::Depth_32Bit) // Float encoding + if (depth == OutputSettings::BitDepth::Depth24Bit || depth == OutputSettings::BitDepth::Depth32Bit) // Float encoding { auto buf = std::vector(frames * channels()); for(fpp_t frame = 0; frame < frames; ++frame) diff --git a/src/core/audio/AudioFileMP3.cpp b/src/core/audio/AudioFileMP3.cpp index 4930e9ad6..ef0677152 100644 --- a/src/core/audio/AudioFileMP3.cpp +++ b/src/core/audio/AudioFileMP3.cpp @@ -94,11 +94,11 @@ MPEG_mode mapToMPEG_mode(OutputSettings::StereoMode stereoMode) { switch (stereoMode) { - case OutputSettings::StereoMode_Stereo: + case OutputSettings::StereoMode::Stereo: return STEREO; - case OutputSettings::StereoMode_JointStereo: + case OutputSettings::StereoMode::JointStereo: return JOINT_STEREO; - case OutputSettings::StereoMode_Mono: + case OutputSettings::StereoMode::Mono: return MONO; default: return NOT_SET; diff --git a/src/core/audio/AudioFileWave.cpp b/src/core/audio/AudioFileWave.cpp index b78b04403..9c51437ff 100644 --- a/src/core/audio/AudioFileWave.cpp +++ b/src/core/audio/AudioFileWave.cpp @@ -64,13 +64,13 @@ bool AudioFileWave::startEncoding() switch( getOutputSettings().getBitDepth() ) { - case OutputSettings::Depth_32Bit: + case OutputSettings::BitDepth::Depth32Bit: m_si.format |= SF_FORMAT_FLOAT; break; - case OutputSettings::Depth_24Bit: + case OutputSettings::BitDepth::Depth24Bit: m_si.format |= SF_FORMAT_PCM_24; break; - case OutputSettings::Depth_16Bit: + case OutputSettings::BitDepth::Depth16Bit: default: m_si.format |= SF_FORMAT_PCM_16; break; @@ -102,7 +102,7 @@ void AudioFileWave::writeBuffer( const surroundSampleFrame * _ab, { OutputSettings::BitDepth bitDepth = getOutputSettings().getBitDepth(); - if( bitDepth == OutputSettings::Depth_32Bit || bitDepth == OutputSettings::Depth_24Bit ) + if( bitDepth == OutputSettings::BitDepth::Depth32Bit || bitDepth == OutputSettings::BitDepth::Depth24Bit ) { auto buf = new float[_frames * channels()]; for( fpp_t frame = 0; frame < _frames; ++frame ) diff --git a/src/core/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index 4bbf509d8..7bae3db1c 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -121,7 +121,7 @@ void AudioPort::doProcessing() if( ph->buffer() ) { if( ph->usesBuffer() - && ( ph->type() == PlayHandle::TypeNotePlayHandle + && ( ph->type() == PlayHandle::Type::NotePlayHandle || !MixHelpers::isSilent( ph->buffer(), fpp ) ) ) { m_bufferUsage = true; diff --git a/src/core/fft_helpers.cpp b/src/core/fft_helpers.cpp index 63088f655..35906e8d3 100644 --- a/src/core/fft_helpers.cpp +++ b/src/core/fft_helpers.cpp @@ -102,7 +102,7 @@ int notEmpty(const std::vector &spectrum) * * return -1 on error */ -int precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool normalized) +int precomputeWindow(float *window, unsigned int length, FFTWindow type, bool normalized) { if (window == nullptr) {return -1;} @@ -117,23 +117,23 @@ int precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool switch (type) { default: - case RECTANGULAR: + case FFTWindow::Rectangular: for (unsigned int i = 0; i < length; i++) {window[i] = 1.0;} gain = 1; return 0; - case BLACKMAN_HARRIS: + case FFTWindow::BlackmanHarris: a0 = 0.35875; a1 = 0.48829; a2 = 0.14128; a3 = 0.01168; break; - case HAMMING: + case FFTWindow::Hamming: a0 = 0.54; a1 = 1.0 - a0; a2 = 0; a3 = 0; break; - case HANNING: + case FFTWindow::Hanning: a0 = 0.5; a1 = 1.0 - a0; a2 = 0; diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 3d21474b0..64cdc51fd 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -39,7 +39,7 @@ namespace lmms { -Plugin::PluginTypes Lv2ControlBase::check(const LilvPlugin *plugin, +Plugin::Type Lv2ControlBase::check(const LilvPlugin *plugin, std::vector &issues) { // for some reason, all checks can be done by one processor... diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index cc90f5e05..9c62703e0 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -217,7 +217,7 @@ void Lv2Manager::initPlugins() const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr); std::vector issues; - Plugin::PluginTypes type = Lv2ControlBase::check(curPlug, issues); + Plugin::Type type = Lv2ControlBase::check(curPlug, issues); std::sort(issues.begin(), issues.end()); auto last = std::unique(issues.begin(), issues.end()); issues.erase(last, issues.end()); @@ -240,7 +240,7 @@ void Lv2Manager::initPlugins() { if(std::any_of(issues.begin(), issues.end(), [](const PluginIssue& iss) { - return iss.type() == PluginIssueType::blacklisted; })) + return iss.type() == PluginIssueType::Blacklisted; })) { ++blacklisted; } diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index 2faee067d..a4625936e 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -126,7 +126,7 @@ std::vector Meta::get(const LilvPlugin *plugin, else if (isA(LV2_CORE__OutputPort)) { m_flow = Flow::Output; } else { m_flow = Flow::Unknown; - issue(unknownPortFlow, portName); + issue(PluginIssueType::UnknownPortFlow, portName); } m_def = .0f; @@ -145,7 +145,7 @@ std::vector Meta::get(const LilvPlugin *plugin, if (isA(LV2_CORE__CVPort)) { // currently not supported, but we can still check the metadata - issue(badPortType, "cvPort"); + issue(PluginIssueType::BadPortType, "cvPort"); } m_type = isA(LV2_CORE__CVPort) ? Type::Cv : Type::Control; @@ -172,21 +172,21 @@ std::vector Meta::get(const LilvPlugin *plugin, } }; - takeRangeValue(def.get(), m_def, portHasNoDef); + takeRangeValue(def.get(), m_def, PluginIssueType::PortHasNoDef); if (isToggle) { m_min = .0f; m_max = 1.f; if(def.get() && m_def != m_min && m_def != m_max) { - issue(defaultValueNotInRange, portName); + issue(PluginIssueType::DefaultValueNotInRange, portName); } } else { // take min/max - takeRangeValue(min.get(), m_min, portHasNoMin); - takeRangeValue(max.get(), m_max, portHasNoMax); + takeRangeValue(min.get(), m_min, PluginIssueType::PortHasNoMin); + takeRangeValue(max.get(), m_max, PluginIssueType::PortHasNoMax); if(m_type == Type::Cv) { // no range is allowed and bashed to [-1,+1], @@ -196,10 +196,10 @@ std::vector Meta::get(const LilvPlugin *plugin, m_min = -1.f; m_max = +1.f; } - else if(!m_min_set()) { issue(portHasNoMin, portName); } - else if(!m_max_set()) { issue(portHasNoMax, portName); } + else if(!m_min_set()) { issue(PluginIssueType::PortHasNoMin, portName); } + else if(!m_max_set()) { issue(PluginIssueType::PortHasNoMax, portName); } } - if(m_min > m_max) { issue(minGreaterMax, portName); } + if(m_min > m_max) { issue(PluginIssueType::MinGreaterMax, portName); } // sampleRate if (hasProperty(LV2_CORE__sampleRate)) { m_sampleRate = true; } @@ -207,7 +207,7 @@ std::vector Meta::get(const LilvPlugin *plugin, // default value if (def.get()) { - if (m_def < m_min) { issue(defaultValueNotInRange, portName); } + if (m_def < m_min) { issue(PluginIssueType::DefaultValueNotInRange, portName); } else if (m_def > m_max) { if(m_sampleRate) @@ -215,7 +215,7 @@ std::vector Meta::get(const LilvPlugin *plugin, // multiplying with sample rate will hopefully lead us // to a good default value } - else { issue(defaultValueNotInRange, portName); } + else { issue(PluginIssueType::DefaultValueNotInRange, portName); } } } @@ -254,7 +254,7 @@ std::vector Meta::get(const LilvPlugin *plugin, { if (m_optional) { m_used = false; } else { - issue(PluginIssueType::unknownPortType, portName); + issue(PluginIssueType::UnknownPortType, portName); } } @@ -265,16 +265,16 @@ std::vector Meta::get(const LilvPlugin *plugin, // be non-Lv2-conforming if(m_min == std::numeric_limits::lowest()) { - issue(PluginIssueType::logScaleMinMissing, portName); + issue(PluginIssueType::LogScaleMinMissing, portName); } if(m_max == std::numeric_limits::max()) { - issue(PluginIssueType::logScaleMaxMissing, portName); + issue(PluginIssueType::LogScaleMaxMissing, portName); } // forbid min < 0 < max if(m_min < 0.f && m_max > 0.f) { - issue(PluginIssueType::logScaleMinMaxDifferentSigns, portName); + issue(PluginIssueType::LogScaleMinMaxDifferentSigns, portName); } m_logarithmic = true; } diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 31af47a63..e0541b948 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -61,7 +61,7 @@ struct MidiInputEvent -Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, +Plugin::Type Lv2Proc::check(const LilvPlugin *plugin, std::vector& issues) { unsigned maxPorts = lilv_plugin_get_num_ports(plugin); @@ -79,7 +79,7 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, if (!Engine::ignorePluginBlacklist() && pluginBlacklist.find(pluginUri) != pluginBlacklist.end()) { - issues.emplace_back(blacklisted); + issues.emplace_back(PluginIssueType::Blacklisted); } for (unsigned portNum = 0; portNum < maxPorts; ++portNum) @@ -106,19 +106,19 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, } if (audioChannels[inCount] > 2) - issues.emplace_back(tooManyInputChannels, + issues.emplace_back(PluginIssueType::TooManyInputChannels, std::to_string(audioChannels[inCount])); if (audioChannels[outCount] == 0) - issues.emplace_back(noOutputChannel); + issues.emplace_back(PluginIssueType::NoOutputChannel); else if (audioChannels[outCount] > 2) - issues.emplace_back(tooManyOutputChannels, + issues.emplace_back(PluginIssueType::TooManyOutputChannels, std::to_string(audioChannels[outCount])); if (midiChannels[inCount] > 1) - issues.emplace_back(tooManyMidiInputChannels, + issues.emplace_back(PluginIssueType::TooManyMidiInputChannels, std::to_string(midiChannels[inCount])); if (midiChannels[outCount] > 1) - issues.emplace_back(tooManyMidiOutputChannels, + issues.emplace_back(PluginIssueType::TooManyMidiOutputChannels, std::to_string(midiChannels[outCount])); AutoLilvNodes reqFeats(lilv_plugin_get_required_features(plugin)); @@ -128,7 +128,7 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, lilv_nodes_get(reqFeats.get(), itr)); if(!Lv2Features::isFeatureSupported(reqFeatName)) { - issues.emplace_back(featureNotSupported, reqFeatName); + issues.emplace_back(PluginIssueType::FeatureNotSupported, reqFeatName); } } @@ -144,16 +144,16 @@ Plugin::PluginTypes Lv2Proc::check(const LilvPlugin *plugin, { // yes, this is not a Lv2 feature, // but it's a feature in abstract sense - issues.emplace_back(featureNotSupported, ro); + issues.emplace_back(PluginIssueType::FeatureNotSupported, ro); } } } return (audioChannels[inCount] > 2 || audioChannels[outCount] > 2) - ? Plugin::Undefined + ? Plugin::Type::Undefined : (audioChannels[inCount] > 0) - ? Plugin::Effect - : Plugin::Instrument; + ? Plugin::Type::Effect + : Plugin::Type::Instrument; } diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index 4e02bc698..135da3e2a 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -59,7 +59,7 @@ QString Lv2SubPluginFeatures::pluginName(const LilvPlugin *plug) -Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::PluginTypes type) : +Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::Type type) : SubPluginFeatures(type) { } diff --git a/src/core/main.cpp b/src/core/main.cpp index da13181fe..25a6ab9c5 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -363,9 +363,9 @@ int main( int argc, char * * argv ) new QCoreApplication( argc, argv ) : new gui::MainApplication(argc, argv); - AudioEngine::qualitySettings qs( AudioEngine::qualitySettings::Mode_HighQuality ); - OutputSettings os( 44100, OutputSettings::BitRateSettings(160, false), OutputSettings::Depth_16Bit, OutputSettings::StereoMode_JointStereo ); - ProjectRenderer::ExportFileFormats eff = ProjectRenderer::WaveFile; + AudioEngine::qualitySettings qs( AudioEngine::qualitySettings::Mode::HighQuality ); + OutputSettings os( 44100, OutputSettings::BitRateSettings(160, false), OutputSettings::BitDepth::Depth16Bit, OutputSettings::StereoMode::JointStereo ); + ProjectRenderer::ExportFileFormat eff = ProjectRenderer::ExportFileFormat::Wave; // second of two command-line parsing stages for( int i = 1; i < argc; ++i ) @@ -517,23 +517,23 @@ int main( int argc, char * * argv ) if( ext == "wav" ) { - eff = ProjectRenderer::WaveFile; + eff = ProjectRenderer::ExportFileFormat::Wave; } #ifdef LMMS_HAVE_OGGVORBIS else if( ext == "ogg" ) { - eff = ProjectRenderer::OggFile; + eff = ProjectRenderer::ExportFileFormat::Ogg; } #endif #ifdef LMMS_HAVE_MP3LAME else if( ext == "mp3" ) { - eff = ProjectRenderer::MP3File; + eff = ProjectRenderer::ExportFileFormat::MP3; } #endif else if (ext == "flac") { - eff = ProjectRenderer::FlacFile; + eff = ProjectRenderer::ExportFileFormat::Flac; } else { @@ -596,15 +596,15 @@ int main( int argc, char * * argv ) if( mode == "s" ) { - os.setStereoMode(OutputSettings::StereoMode_Stereo); + os.setStereoMode(OutputSettings::StereoMode::Stereo); } else if( mode == "j" ) { - os.setStereoMode(OutputSettings::StereoMode_JointStereo); + os.setStereoMode(OutputSettings::StereoMode::JointStereo); } else if( mode == "m" ) { - os.setStereoMode(OutputSettings::StereoMode_Mono); + os.setStereoMode(OutputSettings::StereoMode::Mono); } else { @@ -613,7 +613,7 @@ int main( int argc, char * * argv ) } else if( arg =="--float" || arg == "-a" ) { - os.setBitDepth(OutputSettings::Depth_32Bit); + os.setBitDepth(OutputSettings::BitDepth::Depth32Bit); } else if( arg == "--interpolation" || arg == "-i" ) { @@ -629,19 +629,19 @@ int main( int argc, char * * argv ) if( ip == "linear" ) { - qs.interpolation = AudioEngine::qualitySettings::Interpolation_Linear; + qs.interpolation = AudioEngine::qualitySettings::Interpolation::Linear; } else if( ip == "sincfastest" ) { - qs.interpolation = AudioEngine::qualitySettings::Interpolation_SincFastest; + qs.interpolation = AudioEngine::qualitySettings::Interpolation::SincFastest; } else if( ip == "sincmedium" ) { - qs.interpolation = AudioEngine::qualitySettings::Interpolation_SincMedium; + qs.interpolation = AudioEngine::qualitySettings::Interpolation::SincMedium; } else if( ip == "sincbest" ) { - qs.interpolation = AudioEngine::qualitySettings::Interpolation_SincBest; + qs.interpolation = AudioEngine::qualitySettings::Interpolation::SincBest; } else { @@ -663,16 +663,16 @@ int main( int argc, char * * argv ) switch( o ) { case 1: - qs.oversampling = AudioEngine::qualitySettings::Oversampling_None; + qs.oversampling = AudioEngine::qualitySettings::Oversampling::None; break; case 2: - qs.oversampling = AudioEngine::qualitySettings::Oversampling_2x; + qs.oversampling = AudioEngine::qualitySettings::Oversampling::X2; break; case 4: - qs.oversampling = AudioEngine::qualitySettings::Oversampling_4x; + qs.oversampling = AudioEngine::qualitySettings::Oversampling::X4; break; case 8: - qs.oversampling = AudioEngine::qualitySettings::Oversampling_8x; + qs.oversampling = AudioEngine::qualitySettings::Oversampling::X8; break; default: return usageError( QString( "Invalid oversampling %1" ).arg( argv[i] ) ); diff --git a/src/core/midi/MidiAlsaSeq.cpp b/src/core/midi/MidiAlsaSeq.cpp index 760840c77..0b3bab819 100644 --- a/src/core/midi/MidiAlsaSeq.cpp +++ b/src/core/midi/MidiAlsaSeq.cpp @@ -245,16 +245,16 @@ void MidiAlsaSeq::applyPortMode( MidiPort * _port ) switch( _port->mode() ) { - case MidiPort::Duplex: + case MidiPort::Mode::Duplex: caps[1] |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; - case MidiPort::Input: + case MidiPort::Mode::Input: caps[0] |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; break; - case MidiPort::Output: + case MidiPort::Mode::Output: caps[1] |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; break; diff --git a/src/core/midi/MidiController.cpp b/src/core/midi/MidiController.cpp index fbd48e945..d7c89e940 100644 --- a/src/core/midi/MidiController.cpp +++ b/src/core/midi/MidiController.cpp @@ -33,9 +33,9 @@ namespace lmms MidiController::MidiController( Model * _parent ) : - Controller( Controller::MidiController, _parent, tr( "MIDI Controller" ) ), + Controller( ControllerType::Midi, _parent, tr( "MIDI Controller" ) ), MidiEventProcessor(), - m_midiPort( tr( "unnamed_midi_controller" ), Engine::audioEngine()->midiClient(), this, this, MidiPort::Input ), + m_midiPort( tr( "unnamed_midi_controller" ), Engine::audioEngine()->midiClient(), this, this, MidiPort::Mode::Input ), m_lastValue( 0.0f ), m_previousValue( 0.0f ) { diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index b656d9541..c7c947e8e 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -66,8 +66,8 @@ MidiPort::MidiPort( const QString& name, { m_midiClient->addPort( this ); - m_readableModel.setValue( m_mode == Input || m_mode == Duplex ); - m_writableModel.setValue( m_mode == Output || m_mode == Duplex ); + m_readableModel.setValue( m_mode == Mode::Input || m_mode == Mode::Duplex ); + m_writableModel.setValue( m_mode == Mode::Output || m_mode == Mode::Duplex ); connect( &m_readableModel, SIGNAL(dataChanged()), this, SLOT(updateMidiPortMode()), Qt::DirectConnection ); @@ -325,10 +325,10 @@ void MidiPort::subscribeWritablePort( const QString& port, bool subscribe ) void MidiPort::updateMidiPortMode() { // this small lookup-table makes everything easier - static const Modes modeTable[2][2] = + static const Mode modeTable[2][2] = { - { Disabled, Output }, - { Input, Duplex } + { Mode::Disabled, Mode::Output }, + { Mode::Input, Mode::Duplex } } ; setMode( modeTable[m_readableModel.value()][m_writableModel.value()] ); diff --git a/src/gui/ControllerView.cpp b/src/gui/ControllerView.cpp index 3e3f4b9d1..d32e8d49c 100644 --- a/src/gui/ControllerView.cpp +++ b/src/gui/ControllerView.cpp @@ -149,7 +149,7 @@ void ControllerView::renameController() if( ok && !new_name.isEmpty() ) { c->setName( new_name ); - if( getController()->type() == Controller::LfoController ) + if( getController()->type() == Controller::ControllerType::Lfo ) { m_controllerDlg->setWindowTitle( tr( "LFO" ) + " (" + new_name + ")" ); } diff --git a/src/gui/Controls.cpp b/src/gui/Controls.cpp index 8c007ee99..209b0fce1 100644 --- a/src/gui/Controls.cpp +++ b/src/gui/Controls.cpp @@ -95,7 +95,7 @@ AutomatableModelView* CheckControl::modelView() { return m_checkBox; } CheckControl::CheckControl(QWidget *parent) : m_widget(new QWidget(parent)), - m_checkBox(new LedCheckBox(nullptr, QString(), LedCheckBox::Green)), + m_checkBox(new LedCheckBox(nullptr, QString(), LedCheckBox::LedColor::Green)), m_label(new QLabel(m_widget)) { auto vbox = new QVBoxLayout(m_widget); diff --git a/src/gui/EffectView.cpp b/src/gui/EffectView.cpp index f2caaadd5..7f7f9ee9d 100644 --- a/src/gui/EffectView.cpp +++ b/src/gui/EffectView.cpp @@ -56,28 +56,28 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : // Disable effects that are of type "DummyEffect" bool isEnabled = !dynamic_cast( effect() ); - m_bypass = new LedCheckBox( this, "", isEnabled ? LedCheckBox::Green : LedCheckBox::Red ); + m_bypass = new LedCheckBox( this, "", isEnabled ? LedCheckBox::LedColor::Green : LedCheckBox::LedColor::Red ); m_bypass->move( 3, 3 ); m_bypass->setEnabled( isEnabled ); m_bypass->setToolTip(tr("On/Off")); - m_wetDry = new Knob( knobBright_26, this ); + m_wetDry = new Knob( KnobType::Bright26, this ); m_wetDry->setLabel( tr( "W/D" ) ); m_wetDry->move( 40 - m_wetDry->width() / 2, 5 ); m_wetDry->setEnabled( isEnabled ); m_wetDry->setHintText( tr( "Wet Level:" ), "" ); - m_autoQuit = new TempoSyncKnob( knobBright_26, this ); + m_autoQuit = new TempoSyncKnob( KnobType::Bright26, this ); m_autoQuit->setLabel( tr( "DECAY" ) ); m_autoQuit->move( 78 - m_autoQuit->width() / 2, 5 ); m_autoQuit->setEnabled( isEnabled && !effect()->m_autoQuitDisabled ); m_autoQuit->setHintText( tr( "Time:" ), "ms" ); - m_gate = new Knob( knobBright_26, this ); + m_gate = new Knob( KnobType::Bright26, this ); m_gate->setLabel( tr( "GATE" ) ); m_gate->move( 116 - m_gate->width() / 2, 5 ); m_gate->setEnabled( isEnabled && !effect()->m_autoQuitDisabled ); diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 0672cd347..c0763d542 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -440,7 +440,7 @@ void FileBrowserTreeWidget::keyPressEvent(QKeyEvent * ke ) if (file == nullptr) { return; } // When moving to a new sound, preview it. Skip presets, they can play forever - if (vertical && file->type() == FileItem::SampleFile) + if (vertical && file->type() == FileItem::FileType::Sample) { previewFileItem(file); } @@ -535,7 +535,7 @@ void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e ) QList FileBrowserTreeWidget::getContextActions(FileItem* file, bool songEditor) { QList result = QList(); - const bool fileIsSample = file->type() == FileItem::SampleFile; + const bool fileIsSample = file->type() == FileItem::FileType::Sample; QString instrumentAction = fileIsSample ? tr("Send to new AudioFileProcessor instance") : @@ -603,7 +603,7 @@ void FileBrowserTreeWidget::previewFileItem(FileItem* file) // In special case of sample-files we do not care about // handling() rather than directly creating a SamplePlayHandle - if (file->type() == FileItem::SampleFile) + if (file->type() == FileItem::FileType::Sample) { TextFloat * tf = TextFloat::displayMessage( tr("Loading sample"), @@ -621,15 +621,15 @@ void FileBrowserTreeWidget::previewFileItem(FileItem* file) ext == "gig" || ext == "pat") && !getPluginFactory()->pluginSupportingExtension(ext).isNull()) { - const bool isPlugin = file->handling() == FileItem::LoadByPlugin; + const bool isPlugin = file->handling() == FileItem::FileHandling::LoadByPlugin; newPPH = new PresetPreviewPlayHandle(fileName, isPlugin); } - else if (file->type() != FileItem::VstPluginFile && file->isTrack()) + else if (file->type() != FileItem::FileType::VstPlugin && file->isTrack()) { DataFile dataFile(fileName); if (dataFile.validate(ext)) { - const bool isPlugin = file->handling() == FileItem::LoadByPlugin; + const bool isPlugin = file->handling() == FileItem::FileHandling::LoadByPlugin; newPPH = new PresetPreviewPlayHandle(fileName, isPlugin, &dataFile); } else @@ -681,34 +681,34 @@ void FileBrowserTreeWidget::mouseMoveEvent( QMouseEvent * me ) { switch( f->type() ) { - case FileItem::PresetFile: - new StringPairDrag( f->handling() == FileItem::LoadAsPreset ? + case FileItem::FileType::Preset: + new StringPairDrag( f->handling() == FileItem::FileHandling::LoadAsPreset ? "presetfile" : "pluginpresetfile", f->fullName(), embed::getIconPixmap( "preset_file" ), this ); break; - case FileItem::SampleFile: + case FileItem::FileType::Sample: new StringPairDrag( "samplefile", f->fullName(), embed::getIconPixmap( "sample_file" ), this ); break; - case FileItem::SoundFontFile: + case FileItem::FileType::SoundFont: new StringPairDrag( "soundfontfile", f->fullName(), embed::getIconPixmap( "soundfont_file" ), this ); break; - case FileItem::PatchFile: + case FileItem::FileType::Patch: new StringPairDrag( "patchfile", f->fullName(), embed::getIconPixmap( "sample_file" ), this ); break; - case FileItem::VstPluginFile: + case FileItem::FileType::VstPlugin: new StringPairDrag( "vstpluginfile", f->fullName(), embed::getIconPixmap( "vst_plugin_file" ), this ); break; - case FileItem::MidiFile: + case FileItem::FileType::Midi: new StringPairDrag( "importedproject", f->fullName(), embed::getIconPixmap( "midi_file" ), this ); break; - case FileItem::ProjectFile: + case FileItem::FileType::Project: new StringPairDrag( "projectfile", f->fullName(), embed::getIconPixmap( "project_file" ), this ); break; @@ -732,7 +732,7 @@ void FileBrowserTreeWidget::mouseReleaseEvent(QMouseEvent * me ) if (m_previewPlayHandle == nullptr) { return; } // Only sample previews may continue after mouse up. Is this a sample preview? - bool isSample = m_previewPlayHandle->type() == PlayHandle::TypeSamplePlayHandle; + bool isSample = m_previewPlayHandle->type() == PlayHandle::Type::SamplePlayHandle; // Even sample previews should only continue if the user wants them to. Do they? bool shouldContinue = ConfigManager::inst()->value("ui", "letpreviewsfinish").toInt(); // If both are true the preview may continue, otherwise we stop it @@ -747,14 +747,14 @@ void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it) Engine::audioEngine()->requestChangeInModel(); switch( f->handling() ) { - case FileItem::LoadAsProject: + case FileItem::FileHandling::LoadAsProject: if( getGUI()->mainWindow()->mayChangeProject(true) ) { Engine::getSong()->loadProject( f->fullName() ); } break; - case FileItem::LoadByPlugin: + case FileItem::FileHandling::LoadByPlugin: { const QString e = f->extension(); Instrument * i = it->instrument(); @@ -769,17 +769,17 @@ void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it) break; } - case FileItem::LoadAsPreset: { + case FileItem::FileHandling::LoadAsPreset: { DataFile dataFile(f->fullName()); it->replaceInstrument(dataFile); break; } - case FileItem::ImportAsProject: + case FileItem::FileHandling::ImportAsProject: ImportFilter::import( f->fullName(), Engine::getSong() ); break; - case FileItem::NotSupported: + case FileItem::FileHandling::NotSupported: default: break; @@ -799,14 +799,14 @@ void FileBrowserTreeWidget::activateListItem(QTreeWidgetItem * item, return; } - if( f->handling() == FileItem::LoadAsProject || - f->handling() == FileItem::ImportAsProject ) + if( f->handling() == FileItem::FileHandling::LoadAsProject || + f->handling() == FileItem::FileHandling::ImportAsProject ) { handleFile( f, nullptr ); } - else if( f->handling() != FileItem::NotSupported ) + else if( f->handling() != FileItem::FileHandling::NotSupported ) { - auto it = dynamic_cast(Track::create(Track::InstrumentTrack, Engine::patternStore())); + auto it = dynamic_cast(Track::create(Track::Type::Instrument, Engine::patternStore())); handleFile( f, it ); } } @@ -818,7 +818,7 @@ void FileBrowserTreeWidget::openInNewInstrumentTrack(TrackContainer* tc, FileIte { if(item->isTrack()) { - auto it = dynamic_cast(Track::create(Track::InstrumentTrack, tc)); + auto it = dynamic_cast(Track::create(Track::Type::Instrument, tc)); handleFile(item, it); } } @@ -840,10 +840,10 @@ void FileBrowserTreeWidget::openInNewInstrumentTrack(FileItem* item, bool songEd bool FileBrowserTreeWidget::openInNewSampleTrack(FileItem* item) { // Can't add non-samples to a sample track - if (item->type() != FileItem::SampleFile) { return false; } + if (item->type() != FileItem::FileType::Sample) { return false; } // Create a new sample track for this sample - auto sampleTrack = static_cast(Track::create(Track::SampleTrack, Engine::getSong())); + auto sampleTrack = static_cast(Track::create(Track::Type::Sample, Engine::getSong())); // Add the sample clip to the track Engine::audioEngine()->requestChangeInModel(); @@ -1113,26 +1113,26 @@ void FileItem::initPixmaps() switch( m_type ) { - case ProjectFile: + case FileType::Project: setIcon( 0, *s_projectFilePixmap ); break; - case PresetFile: + case FileType::Preset: setIcon( 0, *s_presetFilePixmap ); break; - case SoundFontFile: + case FileType::SoundFont: setIcon( 0, *s_soundfontFilePixmap ); break; - case VstPluginFile: + case FileType::VstPlugin: setIcon( 0, *s_vstPluginFilePixmap ); break; - case SampleFile: - case PatchFile: // TODO + case FileType::Sample: + case FileType::Patch: // TODO setIcon( 0, *s_sampleFilePixmap ); break; - case MidiFile: + case FileType::Midi: setIcon( 0, *s_midiFilePixmap ); break; - case UnknownFile: + case FileType::Unknown: default: setIcon( 0, *s_unknownFilePixmap ); break; @@ -1144,36 +1144,36 @@ void FileItem::initPixmaps() void FileItem::determineFileType() { - m_handling = NotSupported; + m_handling = FileHandling::NotSupported; const QString ext = extension(); if( ext == "mmp" || ext == "mpt" || ext == "mmpz" ) { - m_type = ProjectFile; - m_handling = LoadAsProject; + m_type = FileType::Project; + m_handling = FileHandling::LoadAsProject; } else if( ext == "xpf" || ext == "xml" ) { - m_type = PresetFile; - m_handling = LoadAsPreset; + m_type = FileType::Preset; + m_handling = FileHandling::LoadAsPreset; } else if( ext == "xiz" && ! getPluginFactory()->pluginSupportingExtension(ext).isNull() ) { - m_type = PresetFile; - m_handling = LoadByPlugin; + m_type = FileType::Preset; + m_handling = FileHandling::LoadByPlugin; } else if( ext == "sf2" || ext == "sf3" ) { - m_type = SoundFontFile; + m_type = FileType::SoundFont; } else if( ext == "pat" ) { - m_type = PatchFile; + m_type = FileType::Patch; } else if( ext == "mid" || ext == "midi" || ext == "rmi" ) { - m_type = MidiFile; - m_handling = ImportAsProject; + m_type = FileType::Midi; + m_handling = FileHandling::ImportAsProject; } else if( ext == "dll" #ifdef LMMS_BUILD_LINUX @@ -1181,28 +1181,28 @@ void FileItem::determineFileType() #endif ) { - m_type = VstPluginFile; - m_handling = LoadByPlugin; + m_type = FileType::VstPlugin; + m_handling = FileHandling::LoadByPlugin; } else if ( ext == "lv2" ) { - m_type = PresetFile; - m_handling = LoadByPlugin; + m_type = FileType::Preset; + m_handling = FileHandling::LoadByPlugin; } else { - m_type = UnknownFile; + m_type = FileType::Unknown; } - if( m_handling == NotSupported && + if( m_handling == FileHandling::NotSupported && !ext.isEmpty() && ! getPluginFactory()->pluginSupportingExtension(ext).isNull() ) { - m_handling = LoadByPlugin; + m_handling = FileHandling::LoadByPlugin; // classify as sample if not classified by anything yet but can // be handled by a certain plugin - if( m_type == UnknownFile ) + if( m_type == FileType::Unknown ) { - m_type = SampleFile; + m_type = FileType::Sample; } } } diff --git a/src/gui/LadspaControlView.cpp b/src/gui/LadspaControlView.cpp index 46e208d2e..dbc3b8059 100644 --- a/src/gui/LadspaControlView.cpp +++ b/src/gui/LadspaControlView.cpp @@ -60,9 +60,9 @@ LadspaControlView::LadspaControlView( QWidget * _parent, switch( m_ctl->port()->data_type ) { - case TOGGLED: + case BufferDataType::Toggled: { - auto toggle = new LedCheckBox(m_ctl->port()->name, this, QString(), LedCheckBox::Green); + auto toggle = new LedCheckBox(m_ctl->port()->name, this, QString(), LedCheckBox::LedColor::Green); toggle->setModel( m_ctl->toggledModel() ); layout->addWidget( toggle ); if( link != nullptr ) @@ -78,14 +78,14 @@ LadspaControlView::LadspaControlView( QWidget * _parent, break; } - case INTEGER: - case ENUM: - case FLOATING: - knb = new Knob( knobBright_26, this, m_ctl->port()->name ); + case BufferDataType::Integer: + case BufferDataType::Enum: + case BufferDataType::Floating: + knb = new Knob( KnobType::Bright26, this, m_ctl->port()->name ); break; - case TIME: - knb = new TempoSyncKnob( knobBright_26, this, m_ctl->port()->name ); + case BufferDataType::Time: + knb = new TempoSyncKnob( KnobType::Bright26, this, m_ctl->port()->name ); break; default: @@ -94,7 +94,7 @@ LadspaControlView::LadspaControlView( QWidget * _parent, if( knb != nullptr ) { - if( m_ctl->port()->data_type != TIME ) + if( m_ctl->port()->data_type != BufferDataType::Time ) { knb->setModel( m_ctl->knobModel() ); } diff --git a/src/gui/LfoControllerDialog.cpp b/src/gui/LfoControllerDialog.cpp index 64602cd74..77362b169 100644 --- a/src/gui/LfoControllerDialog.cpp +++ b/src/gui/LfoControllerDialog.cpp @@ -61,22 +61,22 @@ LfoControllerDialog::LfoControllerDialog( Controller * _model, QWidget * _parent setWindowIcon( embed::getIconPixmap( "controller" ) ); setFixedSize( 240, 58 ); - m_baseKnob = new Knob( knobBright_26, this ); + m_baseKnob = new Knob( KnobType::Bright26, this ); m_baseKnob->setLabel( tr( "BASE" ) ); m_baseKnob->move( CD_LFO_BASE_CD_KNOB_X, CD_LFO_CD_KNOB_Y ); m_baseKnob->setHintText( tr( "Base:" ), "" ); - m_speedKnob = new TempoSyncKnob( knobBright_26, this ); + m_speedKnob = new TempoSyncKnob( KnobType::Bright26, this ); m_speedKnob->setLabel( tr( "FREQ" ) ); m_speedKnob->move( CD_LFO_SPEED_CD_KNOB_X, CD_LFO_CD_KNOB_Y ); m_speedKnob->setHintText( tr( "LFO frequency:" ), "" ); - m_amountKnob = new Knob( knobBright_26, this ); + m_amountKnob = new Knob( KnobType::Bright26, this ); m_amountKnob->setLabel( tr( "AMNT" ) ); m_amountKnob->move( CD_LFO_AMOUNT_CD_KNOB_X, CD_LFO_CD_KNOB_Y ); m_amountKnob->setHintText( tr( "Modulation amount:" ), "" ); - m_phaseKnob = new Knob( knobBright_26, this ); + m_phaseKnob = new Knob( KnobType::Bright26, this ); m_phaseKnob->setLabel( tr( "PHS" ) ); m_phaseKnob->move( CD_LFO_PHASE_CD_KNOB_X, CD_LFO_CD_KNOB_Y ); m_phaseKnob->setHintText( tr( "Phase offset:" ) , "" + tr( " degrees" ) ); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 40c8334ba..559756169 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -84,7 +84,7 @@ MainWindow::MainWindow() : m_autoSaveTimer( this ), m_viewMenu( nullptr ), m_metronomeToggle( 0 ), - m_session( Normal ) + m_session( SessionState::Normal ) { setAttribute( Qt::WA_DeleteOnClose ); @@ -377,7 +377,7 @@ void MainWindow::finalize() m_toolsMenu = new QMenu( this ); - for( const Plugin::Descriptor* desc : getPluginFactory()->descriptors(Plugin::Tool) ) + for( const Plugin::Descriptor* desc : getPluginFactory()->descriptors(Plugin::Type::Tool) ) { m_toolsMenu->addAction( desc->logo->pixmap(), desc->displayName ); m_tools.push_back( ToolPlugin::instantiate( desc->name, /*this*/nullptr ) @@ -514,7 +514,7 @@ void MainWindow::finalize() ConfigManager::inst()->value( "audioengine", "audiodev" ) ) ) { // if so, offer the audio settings section of the setup dialog - SetupDialog sd( SetupDialog::AudioSettings ); + SetupDialog sd( SetupDialog::ConfigTab::AudioSettings ); sd.exec(); } @@ -600,7 +600,7 @@ void MainWindow::resetWindowTitle() title += '*'; } - if( getSession() == Recover ) + if( getSession() == SessionState::Recover ) { title += " - " + tr( "Recover session. Please save your work!" ); } @@ -618,7 +618,7 @@ bool MainWindow::mayChangeProject(bool stopPlayback) Engine::getSong()->stop(); } - if( !Engine::getSong()->isModified() && getSession() != Recover ) + if( !Engine::getSong()->isModified() && getSession() != SessionState::Recover ) { return( true ); } @@ -635,9 +635,9 @@ bool MainWindow::mayChangeProject(bool stopPlayback) "last saving. Do you want to save it " "now?" ); - QMessageBox mb( ( getSession() == Recover ? + QMessageBox mb( ( getSession() == SessionState::Recover ? messageTitleRecovered : messageTitleUnsaved ), - ( getSession() == Recover ? + ( getSession() == SessionState::Recover ? messageRecovered : messageUnsaved ), QMessageBox::Question, QMessageBox::Save, @@ -652,7 +652,7 @@ bool MainWindow::mayChangeProject(bool stopPlayback) } else if( answer == QMessageBox::Discard ) { - if( getSession() == Recover ) + if( getSession() == SessionState::Recover ) { sessionCleanup(); } @@ -795,7 +795,7 @@ bool MainWindow::saveProject() } else if( this->guiSaveProject() ) { - if( getSession() == Recover ) + if( getSession() == SessionState::Recover ) { sessionCleanup(); } @@ -850,7 +850,7 @@ bool MainWindow::saveProjectAs() } if( this->guiSaveProjectAs( fname ) ) { - if( getSession() == Recover ) + if( getSession() == SessionState::Recover ) { sessionCleanup(); } @@ -1214,19 +1214,19 @@ void MainWindow::updatePlayPauseIcons() { switch( Engine::getSong()->playMode() ) { - case Song::Mode_PlaySong: + case Song::PlayMode::Song: getGUI()->songEditor()->setPauseIcon( true ); break; - case Song::Mode_PlayAutomationClip: + case Song::PlayMode::AutomationClip: getGUI()->automationEditor()->setPauseIcon( true ); break; - case Song::Mode_PlayPattern: + case Song::PlayMode::Pattern: getGUI()->patternEditor()->setPauseIcon( true ); break; - case Song::Mode_PlayMidiClip: + case Song::PlayMode::MidiClip: getGUI()->pianoRoll()->setPauseIcon( true ); break; @@ -1288,7 +1288,7 @@ void MainWindow::sessionCleanup() { // delete recover session files QFile::remove( ConfigManager::inst()->recoveryFile() ); - setSession( Normal ); + setSession( SessionState::Normal ); } @@ -1480,7 +1480,7 @@ void MainWindow::exportProject(bool multiExport) efd.setFileMode( FileDialog::AnyFile ); int idx = 0; QStringList types; - while( ProjectRenderer::fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::NumFileFormats) + while( ProjectRenderer::fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::ExportFileFormat::Count) { if(ProjectRenderer::fileEncodeDevices[idx].isAvailable()) { types << tr(ProjectRenderer::fileEncodeDevices[idx].m_description); @@ -1625,17 +1625,17 @@ void MainWindow::onSongStopped() SongEditorWindow* songEditor = getGUI()->songEditor(); switch( tl->behaviourAtStop() ) { - case TimeLineWidget::BackToZero: - if( songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) ) + case TimeLineWidget::BehaviourAtStopState::BackToZero: + if( songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ) ) { songEditor->m_editor->updatePosition(0); } break; - case TimeLineWidget::BackToStart: + case TimeLineWidget::BehaviourAtStopState::BackToStart: if( tl->savedPos() >= 0 ) { - if(songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) ) + if(songEditor && ( tl->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ) ) { songEditor->m_editor->updatePosition( TimePos(tl->savedPos().getTicks() ) ); } @@ -1643,7 +1643,7 @@ void MainWindow::onSongStopped() } break; - case TimeLineWidget::KeepStopPosition: + case TimeLineWidget::BehaviourAtStopState::KeepStopPosition: break; } } diff --git a/src/gui/MicrotunerConfig.cpp b/src/gui/MicrotunerConfig.cpp index 316bf54d6..7ab4cc0b1 100644 --- a/src/gui/MicrotunerConfig.cpp +++ b/src/gui/MicrotunerConfig.cpp @@ -64,6 +64,16 @@ MicrotunerConfig::MicrotunerConfig() : m_baseKeyModel(DefaultBaseKey, 0, NumKeys - 1, nullptr, tr("Base key")), m_baseFreqModel(DefaultBaseFreq, 0.1f, 9999.999f, 0.001f, nullptr, tr("Base note frequency")) { +#if QT_VERSION < 0x50C00 + // Workaround for a bug in Qt versions below 5.12, + // where argument-dependent-lookup fails for QFlags operators + // declared inside a namepsace. + // This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h + // See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348 + + using ::operator|; +#endif + setWindowIcon(embed::getIconPixmap("microtuner")); setWindowTitle(tr("Microtuner")); diff --git a/src/gui/MidiCCRackView.cpp b/src/gui/MidiCCRackView.cpp index 7e0d71f78..a0b1496fb 100644 --- a/src/gui/MidiCCRackView.cpp +++ b/src/gui/MidiCCRackView.cpp @@ -89,7 +89,7 @@ MidiCCRackView::MidiCCRackView(InstrumentTrack * track) : // Adds the controller knobs for (int i = 0; i < MidiControllerCount; ++i) { - m_controllerKnob[i] = new Knob(knobBright_26); + m_controllerKnob[i] = new Knob(KnobType::Bright26); m_controllerKnob[i]->setLabel(tr("CC %1").arg(i)); knobsAreaLayout->addWidget(m_controllerKnob[i], i/4, i%4); } diff --git a/src/gui/MixerLine.cpp b/src/gui/MixerLine.cpp index 030ea892e..a90f13f83 100644 --- a/src/gui/MixerLine.cpp +++ b/src/gui/MixerLine.cpp @@ -96,7 +96,7 @@ MixerLine::MixerLine( QWidget * _parent, MixerView * _mv, int _channelIndex ) : setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) ); // mixer sends knob - m_sendKnob = new Knob( knobBright_26, this, tr( "Channel send amount" ) ); + m_sendKnob = new Knob( KnobType::Bright26, this, tr( "Channel send amount" ) ); m_sendKnob->move( 3, 22 ); m_sendKnob->setVisible( false ); diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp index a9582b4e2..0edebcb8a 100644 --- a/src/gui/MixerView.cpp +++ b/src/gui/MixerView.cpp @@ -57,6 +57,16 @@ MixerView::MixerView() : ModelView( nullptr, this ), SerializingObjectHook() { +#if QT_VERSION < 0x50C00 + // Workaround for a bug in Qt versions below 5.12, + // where argument-dependent-lookup fails for QFlags operators + // declared inside a namepsace. + // This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h + // See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348 + + using ::operator|; +#endif + Mixer * m = Engine::mixer(); m->setHook( this ); @@ -245,13 +255,13 @@ void MixerView::updateMaxChannelSelector() { for (const auto& track : trackList) { - if (track->type() == Track::InstrumentTrack) + if (track->type() == Track::Type::Instrument) { auto inst = (InstrumentTrack*)track; inst->mixerChannelModel()->setRange(0, m_mixerChannelViews.size()-1,1); } - else if (track->type() == Track::SampleTrack) + else if (track->type() == Track::Type::Sample) { auto strk = (SampleTrack*)track; strk->mixerChannelModel()->setRange(0, diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index b59064f05..7ba8bcc53 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -161,7 +161,7 @@ void PluginBrowser::addPlugins() m_descTree->clear(); // Fetch and sort all instrument plugin descriptors - auto descs = getPluginFactory()->descriptors(Plugin::Instrument); + auto descs = getPluginFactory()->descriptors(Plugin::Type::Instrument); std::sort(descs.begin(), descs.end(), [](auto d1, auto d2) { @@ -305,7 +305,7 @@ void PluginDescWidget::contextMenuEvent(QContextMenuEvent* e) void PluginDescWidget::openInNewInstrumentTrack(QString value) { TrackContainer* tc = Engine::getSong(); - auto it = dynamic_cast(Track::create(Track::InstrumentTrack, tc)); + auto it = dynamic_cast(Track::create(Track::Type::Instrument, tc)); auto ilt = new InstrumentLoaderThread(this, it, value); ilt->start(); } diff --git a/src/gui/SampleTrackWindow.cpp b/src/gui/SampleTrackWindow.cpp index 6fe70bf41..68b5eb8a2 100644 --- a/src/gui/SampleTrackWindow.cpp +++ b/src/gui/SampleTrackWindow.cpp @@ -54,6 +54,16 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : m_track(tv->model()), m_stv(tv) { +#if QT_VERSION < 0x50C00 + // Workaround for a bug in Qt versions below 5.12, + // where argument-dependent-lookup fails for QFlags operators + // declared inside a namepsace. + // This affects the Q_DECLARE_OPERATORS_FOR_FLAGS macro in Instrument.h + // See also: https://codereview.qt-project.org/c/qt/qtbase/+/225348 + + using ::operator|; +#endif + // init own layout + widgets setFocusPolicy(Qt::StrongFocus); auto vlayout = new QVBoxLayout(this); @@ -94,7 +104,7 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : Qt::Alignment widgetAlignment = Qt::AlignHCenter | Qt::AlignCenter; // set up volume knob - m_volumeKnob = new Knob(knobBright_26, nullptr, tr("Sample volume")); + m_volumeKnob = new Knob(KnobType::Bright26, nullptr, tr("Sample volume")); m_volumeKnob->setVolumeKnob(true); m_volumeKnob->setHintText(tr("Volume:"), "%"); @@ -108,7 +118,7 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : // set up panning knob - m_panningKnob = new Knob(knobBright_26, nullptr, tr("Panning")); + m_panningKnob = new Knob(KnobType::Bright26, nullptr, tr("Panning")); m_panningKnob->setHintText(tr("Panning:"), ""); basicControlsLayout->addWidget(m_panningKnob, 0, 1); diff --git a/src/gui/clips/AutomationClipView.cpp b/src/gui/clips/AutomationClipView.cpp index a5d415a2e..3e0e12b75 100644 --- a/src/gui/clips/AutomationClipView.cpp +++ b/src/gui/clips/AutomationClipView.cpp @@ -321,7 +321,7 @@ void AutomationClipView::paintEvent( QPaintEvent * ) // the value of the end of the shape between the two nodes will be the inValue of // the next node. float nextValue; - if( m_clip->progressionType() == AutomationClip::DiscreteProgression ) + if( m_clip->progressionType() == AutomationClip::ProgressionType::Discrete ) { nextValue = OUTVAL(it); } diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index b78605906..7a7a19c11 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -87,7 +87,7 @@ ClipView::ClipView( Clip * clip, m_initialClipPos( TimePos(0) ), m_initialClipEnd( TimePos(0) ), m_clip( clip ), - m_action( NoAction ), + m_action( Action::None ), m_initialMousePos( QPoint( 0, 0 ) ), m_initialMouseGlobalPos( QPoint( 0, 0 ) ), m_initialOffsets( QVector() ), @@ -435,7 +435,7 @@ void ClipView::dragEnterEvent( QDragEnterEvent * dee ) else { StringPairDrag::processDragEnterEvent( dee, "clip_" + - QString::number( m_clip->getTrack()->type() ) ); + QString::number( static_cast(m_clip->getTrack()->type()) ) ); } } @@ -457,7 +457,7 @@ void ClipView::dropEvent( QDropEvent * de ) QString value = StringPairDrag::decodeValue( de ); // Track must be the same type to paste into - if( type != ( "clip_" + QString::number( m_clip->getTrack()->type() ) ) ) + if( type != ( "clip_" + QString::number( static_cast(m_clip->getTrack()->type()) ) ) ) { return; } @@ -537,7 +537,7 @@ DataFile ClipView::createClipDataFiles( { Track * t = m_trackView->getTrack(); TrackContainer * tc = t->trackContainer(); - DataFile dataFile( DataFile::DragNDropData ); + DataFile dataFile( DataFile::Type::DragNDropData ); QDomElement clipParent = dataFile.createElement("clips"); for (const auto& clipView : clipViews) @@ -547,7 +547,7 @@ DataFile ClipView::createClipDataFiles( int trackIndex = std::distance(tc->tracks().begin(), std::find(tc->tracks().begin(), tc->tracks().end(), clipTrack)); QDomElement clipElement = dataFile.createElement("clip"); clipElement.setAttribute( "trackIndex", trackIndex ); - clipElement.setAttribute( "trackType", clipTrack->type() ); + clipElement.setAttribute( "trackType", static_cast(clipTrack->type()) ); clipElement.setAttribute( "trackName", clipTrack->name() ); clipView->m_clip->saveState(dataFile, clipElement); clipParent.appendChild( clipElement ); @@ -645,27 +645,27 @@ void ClipView::mousePressEvent( QMouseEvent * me ) { if( isSelected() ) { - m_action = CopySelection; + m_action = Action::CopySelection; } else { - m_action = ToggleSelected; + m_action = Action::ToggleSelected; } } else { if( isSelected() ) { - m_action = MoveSelection; + m_action = Action::MoveSelection; } else { getGUI()->songEditor()->m_editor->selectAllClips( false ); m_clip->addJournalCheckPoint(); - // Move, Resize and ResizeLeft - // Split action doesn't disable Clip journalling - if (m_action == Move || m_action == Resize || m_action == ResizeLeft) + // Action::Move, Action::Resize and Action::ResizeLeft + // Action::Split action doesn't disable Clip journalling + if (m_action == Action::Move || m_action == Action::Resize || m_action == Action::ResizeLeft) { m_clip->setJournalling(false); } @@ -675,22 +675,22 @@ void ClipView::mousePressEvent( QMouseEvent * me ) if( m_clip->getAutoResize() ) { // Always move clips that can't be manually resized - m_action = Move; + m_action = Action::Move; setCursor( Qt::SizeAllCursor ); } else if( me->x() >= width() - RESIZE_GRIP_WIDTH ) { - m_action = Resize; + m_action = Action::Resize; setCursor( Qt::SizeHorCursor ); } else if( me->x() < RESIZE_GRIP_WIDTH && (sClip || pClip) ) { - m_action = ResizeLeft; + m_action = Action::ResizeLeft; setCursor( Qt::SizeHorCursor ); } else if( sClip && knifeMode ) { - m_action = Split; + m_action = Action::Split; setCursor( m_cursorKnife ); setMarkerPos( knifeMarkerPos( me ) ); setMarkerEnabled( true ); @@ -698,11 +698,11 @@ void ClipView::mousePressEvent( QMouseEvent * me ) } else { - m_action = Move; + m_action = Action::Move; setCursor( Qt::SizeAllCursor ); } - if( m_action == Move ) + if( m_action == Action::Move ) { s_textFloat->setTitle( tr( "Current position" ) ); s_textFloat->setText( QString( "%1:%2" ). @@ -710,7 +710,7 @@ void ClipView::mousePressEvent( QMouseEvent * me ) arg( m_clip->startPosition().getTicks() % TimePos::ticksPerBar() ) ); } - else if( m_action == Resize || m_action == ResizeLeft ) + else if( m_action == Action::Resize || m_action == Action::ResizeLeft ) { s_textFloat->setTitle( tr( "Current length" ) ); s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). @@ -727,11 +727,11 @@ void ClipView::mousePressEvent( QMouseEvent * me ) // s_textFloat->reparent( this ); // setup text-float as if Clip was already moved/resized s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); - if ( m_action != Split) { s_textFloat->show(); } + if ( m_action != Action::Split) { s_textFloat->show(); } } delete m_hint; - QString hint = m_action == Move || m_action == MoveSelection + QString hint = m_action == Action::Move || m_action == Action::MoveSelection ? tr( "Press <%1> and drag to make a copy." ) : tr( "Press <%1> for free resizing." ); m_hint = TextFloat::displayMessage( tr( "Hint" ), hint.arg(UI_CTRL_KEY), @@ -748,9 +748,9 @@ void ClipView::mousePressEvent( QMouseEvent * me ) { remove( active ); } - if (m_action == Split) + if (m_action == Action::Split) { - m_action = NoAction; + m_action = Action::None; auto sClip = dynamic_cast(m_clip); if (sClip) { @@ -790,12 +790,12 @@ void ClipView::mousePressEvent( QMouseEvent * me ) */ void ClipView::mouseMoveEvent( QMouseEvent * me ) { - if( m_action == CopySelection || m_action == ToggleSelected ) + if( m_action == Action::CopySelection || m_action == Action::ToggleSelected ) { if( mouseMovedDistance( me, 2 ) == true ) { QVector clipViews; - if( m_action == CopySelection ) + if( m_action == Action::CopySelection ) { // Collect all selected Clips QVector so = @@ -816,7 +816,7 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) } // Clear the action here because mouseReleaseEvent will not get // triggered once we go into drag. - m_action = NoAction; + m_action = Action::None; // Write the Clips to the DataFile for copying DataFile dataFile = createClipDataFiles( clipViews ); @@ -827,7 +827,7 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) Qt::KeepAspectRatio, Qt::SmoothTransformation ); new StringPairDrag( QString( "clip_%1" ).arg( - m_clip->getTrack()->type() ), + static_cast(m_clip->getTrack()->type()) ), dataFile.toString(), thumbnail, this ); } } @@ -839,7 +839,7 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) } const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); - if( m_action == Move ) + if( m_action == Action::Move ) { TimePos newPos = draggedClipPos( me ); m_clip->movePosition(newPos); @@ -851,7 +851,7 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) TimePos::ticksPerBar() ) ); s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2 ) ); } - else if( m_action == MoveSelection ) + else if( m_action == Action::MoveSelection ) { // 1: Find the position we want to move the grabbed Clip to TimePos newPos = draggedClipPos( me ); @@ -881,13 +881,13 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) ( *it )->movePosition( newPos + m_initialOffsets[index] ); } } - else if( m_action == Resize || m_action == ResizeLeft ) + else if( m_action == Action::Resize || m_action == Action::ResizeLeft ) { const float snapSize = getGUI()->songEditor()->m_editor->getSnapSize(); // Length in ticks of one snap increment const TimePos snapLength = TimePos( (int)(snapSize * TimePos::ticksPerBar()) ); - if( m_action == Resize ) + if( m_action == Action::Resize ) { // The clip's new length TimePos l = static_cast( me->x() * TimePos::ticksPerBar() / ppb ); @@ -990,7 +990,7 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) TimePos::ticksPerBar() ) ); s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); } - else if( m_action == Split ) + else if( m_action == Action::Split ) { auto sClip = dynamic_cast(m_clip); if (sClip) { @@ -1015,21 +1015,21 @@ void ClipView::mouseMoveEvent( QMouseEvent * me ) */ void ClipView::mouseReleaseEvent( QMouseEvent * me ) { - // If the CopySelection was chosen as the action due to mouse movement, + // If the Action::CopySelection was chosen as the action due to mouse movement, // it will have been cleared. At this point Toggle is the desired action. // An active StringPairDrag will prevent this method from being called, - // so a real CopySelection would not have occurred. - if( m_action == CopySelection || - ( m_action == ToggleSelected && mouseMovedDistance( me, 2 ) == false ) ) + // so a real Action::CopySelection would not have occurred. + if( m_action == Action::CopySelection || + ( m_action == Action::ToggleSelected && mouseMovedDistance( me, 2 ) == false ) ) { setSelected( !isSelected() ); } - else if( m_action == Move || m_action == Resize || m_action == ResizeLeft ) + else if( m_action == Action::Move || m_action == Action::Resize || m_action == Action::ResizeLeft ) { // TODO: Fix m_clip->setJournalling() consistency m_clip->setJournalling( true ); } - else if( m_action == Split ) + else if( m_action == Action::Split ) { const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); const TimePos relPos = me->pos().x() * TimePos::ticksPerBar() / ppb; @@ -1039,7 +1039,7 @@ void ClipView::mouseReleaseEvent( QMouseEvent * me ) ); } - m_action = NoAction; + m_action = Action::None; delete m_hint; m_hint = nullptr; s_textFloat->hide(); @@ -1079,7 +1079,7 @@ void ClipView::contextMenuEvent( QContextMenuEvent * cme ) individualClip ? tr("Delete (middle mousebutton)") : tr("Delete selection (middle mousebutton)"), - [this](){ contextMenuAction( Remove ); } ); + [this](){ contextMenuAction( ContextMenuAction::Remove ); } ); contextMenu.addSeparator(); @@ -1088,14 +1088,14 @@ void ClipView::contextMenuEvent( QContextMenuEvent * cme ) individualClip ? tr("Cut") : tr("Cut selection"), - [this](){ contextMenuAction( Cut ); } ); + [this](){ contextMenuAction( ContextMenuAction::Cut ); } ); if (canMergeSelection(selectedClips)) { contextMenu.addAction( embed::getIconPixmap("edit_merge"), tr("Merge Selection"), - [this]() { contextMenuAction(Merge); } + [this]() { contextMenuAction(ContextMenuAction::Merge); } ); } } @@ -1105,12 +1105,12 @@ void ClipView::contextMenuEvent( QContextMenuEvent * cme ) individualClip ? tr("Copy") : tr("Copy selection"), - [this](){ contextMenuAction( Copy ); } ); + [this](){ contextMenuAction( ContextMenuAction::Copy ); } ); contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), tr( "Paste" ), - [this](){ contextMenuAction( Paste ); } ); + [this](){ contextMenuAction( ContextMenuAction::Paste ); } ); contextMenu.addSeparator(); @@ -1119,7 +1119,7 @@ void ClipView::contextMenuEvent( QContextMenuEvent * cme ) (individualClip ? tr("Mute/unmute (<%1> + middle click)") : tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY), - [this](){ contextMenuAction( Mute ); } ); + [this](){ contextMenuAction( ContextMenuAction::Mute ); } ); contextMenu.addSeparator(); @@ -1143,22 +1143,22 @@ void ClipView::contextMenuAction( ContextMenuAction action ) switch( action ) { - case Remove: + case ContextMenuAction::Remove: remove( active ); break; - case Cut: + case ContextMenuAction::Cut: cut( active ); break; - case Copy: + case ContextMenuAction::Copy: copy( active ); break; - case Paste: + case ContextMenuAction::Paste: paste(); break; - case Mute: + case ContextMenuAction::Mute: toggleMute( active ); break; - case Merge: + case ContextMenuAction::Merge: mergeClips(active); break; } @@ -1205,7 +1205,7 @@ void ClipView::copy( QVector clipvs ) DataFile dataFile = createClipDataFiles( clipvs ); // Copy the Clip type as a key and the Clip data file to the clipboard - copyStringPair( QString( "clip_%1" ).arg( m_clip->getTrack()->type() ), + copyStringPair( QString( "clip_%1" ).arg( static_cast(m_clip->getTrack()->type()) ), dataFile.toString() ); } diff --git a/src/gui/clips/MidiClipView.cpp b/src/gui/clips/MidiClipView.cpp index e3ef9fd20..79c4cd73d 100644 --- a/src/gui/clips/MidiClipView.cpp +++ b/src/gui/clips/MidiClipView.cpp @@ -204,7 +204,7 @@ void MidiClipView::transposeSelection() void MidiClipView::constructContextMenu( QMenu * _cm ) { - bool isBeat = m_clip->type() == MidiClip::BeatClip; + bool isBeat = m_clip->type() == MidiClip::Type::BeatClip; auto a = new QAction(embed::getIconPixmap("piano"), tr("Open in piano-roll"), _cm); _cm->insertAction( _cm->actions()[0], a ); @@ -253,7 +253,7 @@ void MidiClipView::mousePressEvent( QMouseEvent * _me ) { bool displayPattern = fixedClips() || (pixelsPerBar() >= 96 && m_legacySEPattern); if( _me->button() == Qt::LeftButton && - m_clip->m_clipType == MidiClip::BeatClip && + m_clip->m_clipType == MidiClip::Type::BeatClip && displayPattern && _me->y() > height() - s_stepBtnOff->height() ) // when mouse button is pressed in pattern mode @@ -311,7 +311,7 @@ void MidiClipView::mouseDoubleClickEvent(QMouseEvent *_me) _me->ignore(); return; } - if( m_clip->m_clipType == MidiClip::MelodyClip || !fixedClips() ) + if( m_clip->m_clipType == MidiClip::Type::MelodyClip || !fixedClips() ) { openInPianoRoll(); } @@ -322,7 +322,7 @@ void MidiClipView::mouseDoubleClickEvent(QMouseEvent *_me) void MidiClipView::wheelEvent(QWheelEvent * we) { - if(m_clip->m_clipType == MidiClip::BeatClip && + if(m_clip->m_clipType == MidiClip::Type::BeatClip && (fixedClips() || pixelsPerBar() >= 96) && position(we).y() > height() - s_stepBtnOff->height()) { @@ -400,7 +400,7 @@ void MidiClipView::paintEvent( QPaintEvent * ) QColor c; bool const muted = m_clip->getTrack()->isMuted() || m_clip->isMuted(); bool current = getGUI()->pianoRoll()->currentMidiClip() == m_clip; - bool beatClip = m_clip->m_clipType == MidiClip::BeatClip; + bool beatClip = m_clip->m_clipType == MidiClip::Type::BeatClip; if( beatClip ) { @@ -460,7 +460,7 @@ void MidiClipView::paintEvent( QPaintEvent * ) bool displayPattern = fixedClips() || (pixelsPerBar >= 96 && m_legacySEPattern); // melody clip paint event NoteVector const & noteCollection = m_clip->m_notes; - if( m_clip->m_clipType == MidiClip::MelodyClip && !noteCollection.empty() ) + if( m_clip->m_clipType == MidiClip::Type::MelodyClip && !noteCollection.empty() ) { // Compute the minimum and maximum key in the clip // so that we know how much there is to draw. diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 1289c5626..a24165332 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -90,13 +90,13 @@ AutomationEditor::AutomationEditor() : m_bottomLevel( 0 ), m_topLevel( 0 ), m_currentPosition(), - m_action( NONE ), + m_action( Action::None ), m_drawLastLevel( 0.0f ), m_drawLastTick( 0 ), m_ppb( DEFAULT_PPB ), m_y_delta( DEFAULT_Y_DELTA ), m_y_auto( true ), - m_editMode( DRAW ), + m_editMode( EditMode::Draw ), m_mouseDownLeft(false), m_mouseDownRight( false ), m_scrollBack( false ), @@ -147,9 +147,9 @@ AutomationEditor::AutomationEditor() : // add time-line m_timeLine = new TimeLineWidget( VALUES_WIDTH, 0, m_ppb, Engine::getSong()->getPlayPos( - Song::Mode_PlayAutomationClip ), + Song::PlayMode::AutomationClip ), m_currentPosition, - Song::Mode_PlayAutomationClip, this ); + Song::PlayMode::AutomationClip, this ); connect( this, SIGNAL( positionChanged( const lmms::TimePos& ) ), m_timeLine, SLOT( updatePosition( const lmms::TimePos& ) ) ); connect( m_timeLine, SIGNAL( positionChanged( const lmms::TimePos& ) ), @@ -492,15 +492,15 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) // a node, while others require that we know if we clicked the outValue // of a node. bool editingOutValue = ( - m_editMode == DRAW_OUTVALUES - || (m_editMode == ERASE && m_mouseDownRight) + m_editMode == EditMode::DrawOutValues + || (m_editMode == EditMode::Erase && m_mouseDownRight) ); timeMap::iterator clickedNode = getNodeAt(mouseEvent->x(), mouseEvent->y(), editingOutValue); switch (m_editMode) { - case DRAW: + case EditMode::Draw: { m_clip->addJournalCheckPoint(); @@ -518,7 +518,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) m_drawLastLevel = level; // Changes the action to drawing a line of nodes - m_action = DRAW_LINE; + m_action = Action::DrawLine; } else // No shift, we are just creating/moving nodes { @@ -540,8 +540,8 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) // is being dragged, so if we don't update it we have a bogus iterator clickedNode = tm.find(newTime); - // Set the action to MOVE_VALUE so moveMouseEvent() knows we are moving a node - m_action = MOVE_VALUE; + // Set the action to Action::MoveValue so moveMouseEvent() knows we are moving a node + m_action = Action::MoveValue; // Calculate the offset from the place the mouse click happened in comparison // to the center of the node @@ -559,7 +559,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) Engine::getSong()->setModified(); } - else if (m_mouseDownRight) // Right click on DRAW mode erases values + else if (m_mouseDownRight) // Right click on EditMode::Draw mode erases values { // Update the last clicked position so we remove all nodes from // that point up to the point we release the mouse button @@ -568,11 +568,11 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) // If we right-clicked a node, remove it eraseNode(clickedNode); - m_action = ERASE_VALUES; + m_action = Action::EraseValues; } break; } - case ERASE: + case EditMode::Erase: { m_clip->addJournalCheckPoint(); @@ -586,7 +586,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) // If we right-clicked a node, remove it eraseNode(clickedNode); - m_action = ERASE_VALUES; + m_action = Action::EraseValues; } else if (m_mouseDownRight) // And right click resets outValues { @@ -597,11 +597,11 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) // that point up to the point we release the mouse button m_drawLastTick = posTicks; - m_action = RESET_OUTVALUES; + m_action = Action::ResetOutValues; } break; } - case DRAW_OUTVALUES: + case EditMode::DrawOutValues: { m_clip->addJournalCheckPoint(); @@ -615,7 +615,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) clickedNode.value().setOutValue(level); - m_action = MOVE_OUTVALUE; + m_action = Action::MoveOutValue; Engine::getSong()->setModified(); } @@ -635,7 +635,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) m_draggedOutValueKey = POS(clickedNode); clickedNode.value().setOutValue(level); - m_action = MOVE_OUTVALUE; + m_action = Action::MoveOutValue; Engine::getSong()->setModified(); } @@ -650,7 +650,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) // that point up to the point we release the mouse button m_drawLastTick = posTicks; - m_action = RESET_OUTVALUES; + m_action = Action::ResetOutValues; } break; } @@ -671,13 +671,13 @@ void AutomationEditor::mouseDoubleClickEvent(QMouseEvent * mouseEvent) if (mouseEvent->y() <= TOP_MARGIN || mouseEvent->x() < VALUES_WIDTH) { return; } // Are we fine tuning the inValue or outValue? - const bool isOutVal = (m_editMode == DRAW_OUTVALUES); + const bool isOutVal = (m_editMode == EditMode::DrawOutValues); timeMap::iterator clickedNode = getNodeAt(mouseEvent->x(), mouseEvent->y(), isOutVal); switch (m_editMode) { - case DRAW: - case DRAW_OUTVALUES: + case EditMode::Draw: + case EditMode::DrawOutValues: if (fineTuneValue(clickedNode, isOutVal)) { update(); } break; default: @@ -703,9 +703,9 @@ void AutomationEditor::mouseReleaseEvent(QMouseEvent * mouseEvent ) mustRepaint = true; } - if (m_editMode == DRAW) + if (m_editMode == EditMode::Draw) { - if (m_action == MOVE_VALUE) + if (m_action == Action::MoveValue) { // Actually apply the value of the node being dragged m_clip->applyDragValue(); @@ -714,7 +714,7 @@ void AutomationEditor::mouseReleaseEvent(QMouseEvent * mouseEvent ) QApplication::restoreOverrideCursor(); } - m_action = NONE; + m_action = Action::None; if (mustRepaint) { repaint(); } } @@ -742,12 +742,12 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) switch (m_editMode) { - case DRAW: + case EditMode::Draw: { // We are dragging a node if (m_mouseDownLeft) { - if (m_action == MOVE_VALUE) + if (m_action == Action::MoveValue) { // When we clicked the node, we might have clicked slightly off // so we account for that offset for a smooth drag @@ -770,7 +770,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) Engine::getSong()->setModified(); } - /* else if (m_action == DRAW_LINE) + /* else if (m_action == Action::DrawLine) { // We are drawing a line. For now do nothing (as before), but later logic // could be added here so the line is updated according to the new mouse position @@ -779,7 +779,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } else if (m_mouseDownRight) // We are removing nodes { - if (m_action == ERASE_VALUES) + if (m_action == Action::EraseValues) { // If we moved the mouse past the beginning correct the position in ticks posTicks = qMax(posTicks, 0); @@ -794,7 +794,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } break; } - case ERASE: + case EditMode::Erase: { // If we moved the mouse past the beginning correct the position in ticks posTicks = qMax(posTicks, 0); @@ -802,7 +802,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) // Left button removes nodes if (m_mouseDownLeft) { - if (m_action == ERASE_VALUES) + if (m_action == Action::EraseValues) { // Removing automation nodes @@ -814,7 +814,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } else if (m_mouseDownRight) // Right button resets outValues { - if (m_action == RESET_OUTVALUES) + if (m_action == Action::ResetOutValues) { // Reseting outValues @@ -826,7 +826,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } break; } - case DRAW_OUTVALUES: + case EditMode::DrawOutValues: { // If we moved the mouse past the beginning correct the position in ticks posTicks = qMax(posTicks, 0); @@ -834,7 +834,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) // Left button moves outValues if (m_mouseDownLeft) { - if (m_action == MOVE_OUTVALUE) + if (m_action == Action::MoveOutValue) { // We are moving the outValue of the node timeMap & tm = m_clip->getTimeMap(); @@ -850,7 +850,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } else if (m_mouseDownRight) // Right button resets them { - if (m_action == RESET_OUTVALUES) + if (m_action == Action::ResetOutValues) { // Reseting outValues @@ -1172,7 +1172,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) // the value of the end of the shape between the two nodes will be the inValue of // the next node. float nextValue; - if( m_clip->progressionType() == AutomationClip::DiscreteProgression ) + if( m_clip->progressionType() == AutomationClip::ProgressionType::Discrete ) { nextValue = OUTVAL(it); } @@ -1248,22 +1248,22 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) // draw current edit-mode-icon below the cursor switch( m_editMode ) { - case DRAW: + case EditMode::Draw: { - if (m_action == ERASE_VALUES) { cursor = s_toolErase; } - else if (m_action == MOVE_VALUE) { cursor = s_toolMove; } + if (m_action == Action::EraseValues) { cursor = s_toolErase; } + else if (m_action == Action::MoveValue) { cursor = s_toolMove; } else { cursor = s_toolDraw; } break; } - case ERASE: + case EditMode::Erase: { cursor = s_toolErase; break; } - case DRAW_OUTVALUES: + case EditMode::DrawOutValues: { - if (m_action == RESET_OUTVALUES) { cursor = s_toolErase; } - else if (m_action == MOVE_OUTVALUE) { cursor = s_toolMove; } + if (m_action == Action::ResetOutValues) { cursor = s_toolErase; } + else if (m_action == Action::MoveOutValue) { cursor = s_toolMove; } else { cursor = s_toolDrawOut; } break; } @@ -1400,7 +1400,7 @@ void AutomationEditor::resizeEvent(QResizeEvent * re) if( Engine::getSong() ) { - Engine::getSong()->getPlayPos( Song::Mode_PlayAutomationClip + Engine::getSong()->getPlayPos( Song::PlayMode::AutomationClip ).m_timeLine->setFixedWidth( width() ); } @@ -1525,7 +1525,7 @@ void AutomationEditor::play() if( !m_clip->getTrack() ) { - if( Engine::getSong()->playMode() != Song::Mode_PlayMidiClip ) + if( Engine::getSong()->playMode() != Song::PlayMode::MidiClip ) { Engine::getSong()->stop(); Engine::getSong()->playMidiClip( getGUI()->pianoRoll()->currentMidiClip() ); @@ -1599,7 +1599,7 @@ void AutomationEditor::verScrolled(int new_pos ) -void AutomationEditor::setEditMode(AutomationEditor::EditModes mode) +void AutomationEditor::setEditMode(AutomationEditor::EditMode mode) { if (m_editMode == mode) return; @@ -1614,13 +1614,13 @@ void AutomationEditor::setEditMode(AutomationEditor::EditModes mode) void AutomationEditor::setEditMode(int mode) { - setEditMode((AutomationEditor::EditModes) mode); + setEditMode((AutomationEditor::EditMode) mode); } -void AutomationEditor::setProgressionType(AutomationClip::ProgressionTypes type) +void AutomationEditor::setProgressionType(AutomationClip::ProgressionType type) { if (validClip()) { @@ -1633,7 +1633,7 @@ void AutomationEditor::setProgressionType(AutomationClip::ProgressionTypes type) void AutomationEditor::setProgressionType(int type) { - setProgressionType((AutomationClip::ProgressionTypes) type); + setProgressionType((AutomationClip::ProgressionType) type); } @@ -1655,7 +1655,7 @@ void AutomationEditor::updatePosition(const TimePos & t ) { if( ( Engine::getSong()->isPlaying() && Engine::getSong()->playMode() == - Song::Mode_PlayAutomationClip ) || + Song::PlayMode::AutomationClip ) || m_scrollBack == true ) { const int w = width() - VALUES_WIDTH; @@ -1877,7 +1877,7 @@ AutomationEditorWindow::AutomationEditorWindow() : connect(progression_type_group, SIGNAL(triggered(int)), m_editor, SLOT(setProgressionType(int))); // setup tension-stuff - m_tensionKnob = new Knob( knobSmall_17, this, "Tension" ); + m_tensionKnob = new Knob( KnobType::Small17, this, "Tension" ); m_tensionKnob->setModel(m_editor->m_tensionModel); m_tensionKnob->setToolTip(tr("Tension value for spline")); @@ -1989,15 +1989,15 @@ void AutomationEditorWindow::setCurrentClip(AutomationClip* clip) switch(m_editor->m_clip->progressionType()) { - case AutomationClip::DiscreteProgression: + case AutomationClip::ProgressionType::Discrete: m_discreteAction->setChecked(true); m_tensionKnob->setEnabled(false); break; - case AutomationClip::LinearProgression: + case AutomationClip::ProgressionType::Linear: m_linearAction->setChecked(true); m_tensionKnob->setEnabled(false); break; - case AutomationClip::CubicHermiteProgression: + case AutomationClip::ProgressionType::CubicHermite: m_cubicHermiteAction->setChecked(true); m_tensionKnob->setEnabled(true); break; diff --git a/src/gui/editors/PatternEditor.cpp b/src/gui/editors/PatternEditor.cpp index b2d2f5c3f..229c90bc2 100644 --- a/src/gui/editors/PatternEditor.cpp +++ b/src/gui/editors/PatternEditor.cpp @@ -73,7 +73,7 @@ void PatternEditor::removeSteps() for (const auto& track : tl) { - if (track->type() == Track::InstrumentTrack) + if (track->type() == Track::Type::Instrument) { auto p = static_cast(track->getClip(m_ps->currentPattern())); p->removeSteps(); @@ -86,7 +86,7 @@ void PatternEditor::removeSteps() void PatternEditor::addSampleTrack() { - (void) Track::create( Track::SampleTrack, model() ); + (void) Track::create( Track::Type::Sample, model() ); } @@ -94,7 +94,7 @@ void PatternEditor::addSampleTrack() void PatternEditor::addAutomationTrack() { - (void) Track::create( Track::AutomationTrack, model() ); + (void) Track::create( Track::Type::Automation, model() ); } @@ -180,7 +180,7 @@ void PatternEditor::makeSteps( bool clone ) for (const auto& track : tl) { - if (track->type() == Track::InstrumentTrack) + if (track->type() == Track::Type::Instrument) { auto p = static_cast(track->getClip(m_ps->currentPattern())); if( clone ) @@ -306,7 +306,7 @@ QSize PatternEditorWindow::sizeHint() const void PatternEditorWindow::play() { - if (Engine::getSong()->playMode() != Song::Mode_PlayPattern) + if (Engine::getSong()->playMode() != Song::PlayMode::Pattern) { Engine::getSong()->playPattern(); } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 753dfe77f..0cc825725 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -113,7 +113,7 @@ const int RESIZE_AREA_WIDTH = 9; const int NOTE_EDIT_LINE_WIDTH = 3; // key where to start -const int INITIAL_START_KEY = Key_C + Octave_4 * KeysPerOctave; +const int INITIAL_START_KEY = Octave::Octave_4 + Key::C; // number of each note to provide in quantization and note lengths const int NUM_EVEN_LENGTHS = 6; @@ -138,11 +138,11 @@ static QString getNoteString(int key) } // used for drawing of piano -std::array PianoRoll::prKeyOrder +std::array PianoRoll::prKeyOrder { - PR_WHITE_KEY_SMALL, PR_BLACK_KEY, PR_WHITE_KEY_BIG, PR_BLACK_KEY, - PR_WHITE_KEY_SMALL, PR_WHITE_KEY_SMALL, PR_BLACK_KEY, PR_WHITE_KEY_BIG, - PR_BLACK_KEY, PR_WHITE_KEY_BIG, PR_BLACK_KEY, PR_WHITE_KEY_SMALL + KeyType::WhiteSmall, KeyType::Black, KeyType::WhiteBig, KeyType::Black, + KeyType::WhiteSmall, KeyType::WhiteSmall, KeyType::Black, KeyType::WhiteBig, + KeyType::Black, KeyType::WhiteBig, KeyType::Black, KeyType::WhiteSmall } ; @@ -168,8 +168,8 @@ PianoRoll::PianoRoll() : m_currentPosition(), m_recording( false ), m_currentNote( nullptr ), - m_action( ActionNone ), - m_noteEditMode( NoteEditVolume ), + m_action( Action::None ), + m_noteEditMode( NoteEditMode::Volume ), m_moveBoundaryLeft( 0 ), m_moveBoundaryTop( 0 ), m_moveBoundaryRight( 0 ), @@ -191,8 +191,8 @@ PianoRoll::PianoRoll() : m_minResizeLen( 0 ), m_startKey( INITIAL_START_KEY ), m_lastKey( 0 ), - m_editMode( ModeDraw ), - m_ctrlMode( ModeDraw ), + m_editMode( EditMode::Draw ), + m_ctrlMode( EditMode::Draw ), m_mouseDownRight( false ), m_scrollBack( false ), m_stepRecorderWidget(this, DEFAULT_PR_PPB, PR_TOP_MARGIN, PR_BOTTOM_MARGIN + m_notesEditHeight, WHITE_KEY_WIDTH, 0), @@ -241,12 +241,12 @@ PianoRoll::PianoRoll() : auto unmarkAllAction = new QAction(tr("Unmark all"), this); auto copyAllNotesAction = new QAction(tr("Select all notes on this key"), this); - connect( markSemitoneAction, &QAction::triggered, [this](){ markSemiTone(stmaMarkCurrentSemiTone); }); - connect( markAllOctaveSemitonesAction, &QAction::triggered, [this](){ markSemiTone(stmaMarkAllOctaveSemiTones); }); - connect( markScaleAction, &QAction::triggered, [this](){ markSemiTone(stmaMarkCurrentScale); }); - connect( markChordAction, &QAction::triggered, [this](){ markSemiTone(stmaMarkCurrentChord); }); - connect( unmarkAllAction, &QAction::triggered, [this](){ markSemiTone(stmaUnmarkAll); }); - connect( copyAllNotesAction, &QAction::triggered, [this](){ markSemiTone(stmaCopyAllNotesOnKey); }); + connect( markSemitoneAction, &QAction::triggered, [this](){ markSemiTone(SemiToneMarkerAction::MarkCurrentSemiTone); }); + connect( markAllOctaveSemitonesAction, &QAction::triggered, [this](){ markSemiTone(SemiToneMarkerAction::MarkAllOctaveSemiTones); }); + connect( markScaleAction, &QAction::triggered, [this](){ markSemiTone(SemiToneMarkerAction::MarkCurrentScale); }); + connect( markChordAction, &QAction::triggered, [this](){ markSemiTone(SemiToneMarkerAction::MarkCurrentChord); }); + connect( unmarkAllAction, &QAction::triggered, [this](){ markSemiTone(SemiToneMarkerAction::UnmarkAll); }); + connect( copyAllNotesAction, &QAction::triggered, [this](){ markSemiTone(SemiToneMarkerAction::CopyAllNotesOnKey); }); markScaleAction->setEnabled( false ); markChordAction->setEnabled( false ); @@ -298,9 +298,9 @@ PianoRoll::PianoRoll() : // add time-line m_timeLine = new TimeLineWidget(m_whiteKeyWidth, 0, m_ppb, Engine::getSong()->getPlayPos( - Song::Mode_PlayMidiClip ), + Song::PlayMode::MidiClip ), m_currentPosition, - Song::Mode_PlayMidiClip, this ); + Song::PlayMode::MidiClip, this ); connect( this, SIGNAL( positionChanged( const lmms::TimePos& ) ), m_timeLine, SLOT( updatePosition( const lmms::TimePos& ) ) ); connect( m_timeLine, SIGNAL( positionChanged( const lmms::TimePos& ) ), @@ -314,12 +314,12 @@ PianoRoll::PianoRoll() : this, SLOT( updatePositionStepRecording( const lmms::TimePos& ) ) ); // update timeline when in record-accompany mode - connect( Engine::getSong()->getPlayPos( Song::Mode_PlaySong ).m_timeLine, + connect( Engine::getSong()->getPlayPos( Song::PlayMode::Song ).m_timeLine, SIGNAL( positionChanged( const lmms::TimePos& ) ), this, SLOT( updatePositionAccompany( const lmms::TimePos& ) ) ); // TODO -/* connect( engine::getSong()->getPlayPos( Song::Mode_PlayPattern ).m_timeLine, +/* connect( engine::getSong()->getPlayPos( Song::PlayMode::Pattern ).m_timeLine, SIGNAL( positionChanged( const lmms::TimePos& ) ), this, SLOT( updatePositionAccompany( const lmms::TimePos& ) ) );*/ @@ -524,7 +524,7 @@ void PianoRoll::changeNoteEditMode( int i ) } -void PianoRoll::markSemiTone(int i, bool fromMenu) +void PianoRoll::markSemiTone(SemiToneMarkerAction i, bool fromMenu) { const int key = fromMenu ? getKey(mapFromGlobal(m_semiToneMarkerMenu->pos()).y()) @@ -533,14 +533,14 @@ void PianoRoll::markSemiTone(int i, bool fromMenu) // 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; } + if (!fromMenu && (key < 0 || m_scaleModel.value() == 0)) { i = SemiToneMarkerAction::UnmarkAll; } - switch( static_cast( i ) ) + switch(i) { - case stmaUnmarkAll: + case SemiToneMarkerAction::UnmarkAll: m_markedSemiTones.clear(); break; - case stmaMarkCurrentSemiTone: + case SemiToneMarkerAction::MarkCurrentSemiTone: { QList::iterator it = std::find( m_markedSemiTones.begin(), m_markedSemiTones.end(), key ); if( it != m_markedSemiTones.end() ) @@ -553,7 +553,7 @@ void PianoRoll::markSemiTone(int i, bool fromMenu) } break; } - case stmaMarkAllOctaveSemiTones: + case SemiToneMarkerAction::MarkAllOctaveSemiTones: { QList aok = getAllOctavesForKey(key); @@ -578,10 +578,10 @@ void PianoRoll::markSemiTone(int i, bool fromMenu) break; } - case stmaMarkCurrentScale: + case SemiToneMarkerAction::MarkCurrentScale: chord = & InstrumentFunctionNoteStacking::ChordTable::getInstance() .getScaleByName( m_scaleModel.currentText() ); - case stmaMarkCurrentChord: + case SemiToneMarkerAction::MarkCurrentChord: { if( ! chord ) { @@ -611,7 +611,7 @@ void PianoRoll::markSemiTone(int i, bool fromMenu) } break; } - case stmaCopyAllNotesOnKey: + case SemiToneMarkerAction::CopyAllNotesOnKey: { selectNotesOnKey(); break; @@ -858,7 +858,7 @@ void PianoRoll::setCurrentMidiClip( MidiClip* newMidiClip ) } // force the song-editor to stop playing if it played a MIDI clip before - if (Engine::getSong()->playMode() == Song::Mode_PlayMidiClip) + if (Engine::getSong()->playMode() == Song::PlayMode::MidiClip) { Engine::getSong()->playMidiClip( nullptr ); } @@ -1146,12 +1146,12 @@ void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x, // node to the other switch (_n->detuning()->automationClip()->progressionType()) { - case AutomationClip::DiscreteProgression: + case AutomationClip::ProgressionType::Discrete: _p.drawLine(old_x, pre_y, cur_x, pre_y); _p.drawLine(cur_x, pre_y, cur_x, cur_y); break; - case AutomationClip::CubicHermiteProgression: /* TODO */ - case AutomationClip::LinearProgression: + case AutomationClip::ProgressionType::CubicHermite: /* TODO */ + case AutomationClip::ProgressionType::Linear: _p.drawLine(old_x, pre_y, cur_x, cur_y); break; } @@ -1299,7 +1299,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) if( hasValidMidiClip() && ke->modifiers() == Qt::NoModifier ) { - const int key_num = PianoView::getKeyFromKeyEvent( ke ) + ( DefaultOctave - 1 ) * KeysPerOctave; + const int key_num = PianoView::getKeyFromKeyEvent( ke ); if (!ke->isAutoRepeat() && key_num > -1) { @@ -1316,7 +1316,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) case Qt::Key_Down: { int direction = (ke->key() == Qt::Key_Up ? +1 : -1); - if( ( ke->modifiers() & Qt::ControlModifier ) && m_action == ActionNone ) + if( ( ke->modifiers() & Qt::ControlModifier ) && m_action == Action::None ) { // shift selection by one octave // if nothing selected, shift _everything_ @@ -1334,7 +1334,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) } } } - else if((ke->modifiers() & Qt::ShiftModifier) && m_action == ActionNone) + else if((ke->modifiers() & Qt::ShiftModifier) && m_action == Action::None) { // Move selected notes by one semitone if (hasValidMidiClip()) @@ -1350,8 +1350,8 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) // if they are moving notes around or resizing, // recalculate the note/resize position - if( m_action == ActionMoveNote || - m_action == ActionResizeNote ) + if( m_action == Action::MoveNote || + m_action == Action::ResizeNote ) { dragNotes( m_lastMouseX, @@ -1370,7 +1370,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) case Qt::Key_Left: { int direction = (ke->key() == Qt::Key_Right ? +1 : -1); - if( ke->modifiers() & Qt::ControlModifier && m_action == ActionNone ) + if( ke->modifiers() & Qt::ControlModifier && m_action == Action::None ) { // Move selected notes by one bar to the left if (hasValidMidiClip()) @@ -1378,7 +1378,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) shiftPos( direction * TimePos::ticksPerBar() ); } } - else if( ke->modifiers() & Qt::ShiftModifier && m_action == ActionNone) + else if( ke->modifiers() & Qt::ShiftModifier && m_action == Action::None) { // move notes if (hasValidMidiClip()) @@ -1409,8 +1409,8 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) // if they are moving notes around or resizing, // recalculate the note/resize position - if( m_action == ActionMoveNote || - m_action == ActionResizeNote ) + if( m_action == Action::MoveNote || + m_action == Action::ResizeNote ) { dragNotes( m_lastMouseX, @@ -1446,7 +1446,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) case Qt::Key_Escape: // On the Knife mode, ESC cancels it - if (m_editMode == ModeEditKnife) + if (m_editMode == EditMode::Knife) { cancelKnifeAction(); } @@ -1501,7 +1501,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) case Qt::Key_Control: // Ctrl will not enter selection mode if we are // in Knife mode, but unquantize it - if (m_editMode == ModeEditKnife) + if (m_editMode == EditMode::Knife) { break; } @@ -1512,7 +1512,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) if ( !( ke->modifiers() & Qt::ShiftModifier ) && isActiveWindow() ) { m_ctrlMode = m_editMode; - m_editMode = ModeSelect; + m_editMode = EditMode::Select; setCursor( Qt::ArrowCursor ); ke->accept(); } @@ -1531,7 +1531,7 @@ void PianoRoll::keyReleaseEvent(QKeyEvent* ke ) { if( hasValidMidiClip() && ke->modifiers() == Qt::NoModifier ) { - const int key_num = PianoView::getKeyFromKeyEvent( ke ) + ( DefaultOctave - 1 ) * KeysPerOctave; + const int key_num = PianoView::getKeyFromKeyEvent( ke ); if (!ke->isAutoRepeat() && key_num > -1) { m_midiClip->instrumentTrack()->pianoModel()->handleKeyRelease(key_num); @@ -1544,7 +1544,7 @@ void PianoRoll::keyReleaseEvent(QKeyEvent* ke ) switch( ke->key() ) { case Qt::Key_Control: - if (m_editMode == ModeEditKnife) + if (m_editMode == EditMode::Knife) { break; } @@ -1637,7 +1637,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) } // -- Knife - if (m_editMode == ModeEditKnife && me->button() == Qt::LeftButton) + if (m_editMode == EditMode::Knife && me->button() == Qt::LeftButton) { NoteVector n; Note* note = noteUnderMouse(); @@ -1656,7 +1656,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) return; } - if( m_editMode == ModeEditDetuning && noteUnderMouse() ) + if( m_editMode == EditMode::Detuning && noteUnderMouse() ) { static QPointer detuningClip = nullptr; if (detuningClip.data() != nullptr) @@ -1675,10 +1675,10 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) } // if holding control, go to selection mode unless shift is also pressed - if( me->modifiers() & Qt::ControlModifier && m_editMode != ModeSelect ) + if( me->modifiers() & Qt::ControlModifier && m_editMode != EditMode::Select ) { m_ctrlMode = m_editMode; - m_editMode = ModeSelect; + m_editMode = EditMode::Select; setCursor( Qt::ArrowCursor ); update(); } @@ -1694,7 +1694,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) me->y() > keyAreaBottom() && me->y() < noteEditTop()) { // resizing the note edit area - m_action = ActionResizeNoteEditArea; + m_action = Action::ResizeNoteEditArea; return; } @@ -1764,7 +1764,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) } // left button?? else if( me->button() == Qt::LeftButton && - m_editMode == ModeDraw ) + m_editMode == EditMode::Draw ) { // whether this action creates new note(s) or not bool is_new_note = false; @@ -1871,7 +1871,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) { m_midiClip->addJournalCheckPoint(); // then resize the note - m_action = ActionResizeNote; + m_action = Action::ResizeNote; //Calculate the minimum length we should allow when resizing //each note, and let all notes use the smallest one found @@ -1901,7 +1901,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) } // otherwise move it - m_action = ActionMoveNote; + m_action = Action::MoveNote; // set move-cursor setCursor( Qt::SizeAllCursor ); @@ -1931,8 +1931,8 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) Engine::getSong()->setModified(); } else if( ( me->buttons() == Qt::RightButton && - m_editMode == ModeDraw ) || - m_editMode == ModeErase ) + m_editMode == EditMode::Draw ) || + m_editMode == EditMode::Erase ) { // erase single note m_mouseDownRight = true; @@ -1944,7 +1944,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) } } else if( me->button() == Qt::LeftButton && - m_editMode == ModeSelect ) + m_editMode == EditMode::Select ) { // select an area of notes @@ -1952,7 +1952,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) m_selectedTick = 0; m_selectStartKey = key_num; m_selectedKeys = 1; - m_action = ActionSelectNotes; + m_action = Action::SelectNotes; // call mousemove to fix glitch where selection // appears in wrong spot on mousedown @@ -1990,7 +1990,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) { // clicked in the box below the keys to the left of note edit area m_noteEditMode = (NoteEditMode)(((int)m_noteEditMode)+1); - if( m_noteEditMode == NoteEditCount ) + if( m_noteEditMode == NoteEditMode::Count ) { m_noteEditMode = (NoteEditMode) 0; } @@ -2163,11 +2163,11 @@ void PianoRoll::pauseChordNotes(int key) void PianoRoll::setKnifeAction() { - if (m_editMode != ModeEditKnife) + if (m_editMode != EditMode::Knife) { m_knifeMode = m_editMode; - m_editMode = ModeEditKnife; - m_action = ActionKnife; + m_editMode = EditMode::Knife; + m_action = Action::Knife; setCursor(Qt::ArrowCursor); update(); } @@ -2176,7 +2176,7 @@ void PianoRoll::setKnifeAction() void PianoRoll::cancelKnifeAction() { m_editMode = m_knifeMode; - m_action = ActionNone; + m_action = Action::None; update(); } @@ -2281,7 +2281,7 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) s_textFloat->hide(); // Quit knife mode if we pressed and released the right mouse button - if (m_editMode == ModeEditKnife && me->button() == Qt::RightButton) + if (m_editMode == EditMode::Knife && me->button() == Qt::RightButton) { cancelKnifeAction(); } @@ -2290,14 +2290,14 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) { mustRepaint = true; - if( m_action == ActionSelectNotes && m_editMode == ModeSelect ) + if( m_action == Action::SelectNotes && m_editMode == EditMode::Select ) { // select the notes within the selection rectangle and // then destroy the selection rectangle computeSelectedNotes( me->modifiers() & Qt::ShiftModifier ); } - else if( m_action == ActionMoveNote ) + else if( m_action == Action::MoveNote ) { // we moved one or more notes so they have to be // moved properly according to new starting- @@ -2306,7 +2306,7 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) } - if( m_action == ActionMoveNote || m_action == ActionResizeNote ) + if( m_action == Action::MoveNote || m_action == Action::ResizeNote ) { // if we only moved one note, deselect it so we can // edit the notes in the note edit area @@ -2345,12 +2345,12 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) m_currentNote = nullptr; - if (m_action != ActionKnife) + if (m_action != Action::Knife) { - m_action = ActionNone; + m_action = Action::None; } - if( m_editMode == ModeDraw ) + if( m_editMode == EditMode::Draw ) { setCursor( Qt::ArrowCursor ); } @@ -2372,7 +2372,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) return; } - if( m_action == ActionNone && me->buttons() == 0 ) + if( m_action == Action::None && me->buttons() == 0 ) { // When cursor is between note editing area and volume/panning // area show vertical size cursor. @@ -2382,7 +2382,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) return; } } - else if( m_action == ActionResizeNoteEditArea ) + else if( m_action == Action::ResizeNoteEditArea ) { // Don't try to show more keys than the full keyboard, bail if trying to if (m_pianoKeysVisible == NumKeys && me->y() > m_moveStartY) @@ -2406,22 +2406,22 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } // Update Knife position if we are on knife mode - if (m_editMode == ModeEditKnife) + if (m_editMode == EditMode::Knife) { updateKnifePos(me); } - if( me->y() > PR_TOP_MARGIN || m_action != ActionNone ) + if( me->y() > PR_TOP_MARGIN || m_action != Action::None ) { bool edit_note = ( me->y() > noteEditTop() ) - && m_action != ActionSelectNotes; + && m_action != Action::SelectNotes; int key_num = getKey( me->y() ); int x = me->x(); // see if they clicked on the keyboard on the left - if (x < m_whiteKeyWidth && m_action == ActionNone + if (x < m_whiteKeyWidth && m_action == Action::None && ! edit_note && key_num != m_lastKey && me->buttons() & Qt::LeftButton ) { @@ -2434,14 +2434,14 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) x -= m_whiteKeyWidth; if( me->buttons() & Qt::LeftButton - && m_editMode == ModeDraw - && (m_action == ActionMoveNote || m_action == ActionResizeNote ) ) + && m_editMode == EditMode::Draw + && (m_action == Action::MoveNote || m_action == Action::ResizeNote ) ) { // handle moving notes and resizing them bool replay_note = key_num != m_lastKey - && m_action == ActionMoveNote; + && m_action == Action::MoveNote; - if( replay_note || ( m_action == ActionMoveNote && ( me->modifiers() & Qt::ShiftModifier ) && ! m_startedWithShift ) ) + if( replay_note || ( m_action == Action::MoveNote && ( me->modifiers() & Qt::ShiftModifier ) && ! m_startedWithShift ) ) { pauseTestNotes(); } @@ -2454,13 +2454,13 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) me->modifiers() & Qt::ControlModifier ); - if( replay_note && m_action == ActionMoveNote && ! ( ( me->modifiers() & Qt::ShiftModifier ) && ! m_startedWithShift ) ) + if( replay_note && m_action == Action::MoveNote && ! ( ( me->modifiers() & Qt::ShiftModifier ) && ! m_startedWithShift ) ) { pauseTestNotes( false ); } } - else if( m_editMode != ModeErase && - ( edit_note || m_action == ActionChangeNoteProperty ) && + else if( m_editMode != EditMode::Erase && + ( edit_note || m_action == Action::ChangeNoteProperty ) && ( me->buttons() & Qt::LeftButton || me->buttons() & Qt::MiddleButton || ( me->buttons() & Qt::RightButton && me->modifiers() & Qt::ShiftModifier ) ) ) { @@ -2501,12 +2501,12 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) PanningRight); } - if( m_noteEditMode == NoteEditVolume ) + if( m_noteEditMode == NoteEditMode::Volume ) { m_lastNoteVolume = vol; showVolTextFloat( vol, me->pos() ); } - else if( m_noteEditMode == NoteEditPanning ) + else if( m_noteEditMode == NoteEditMode::Panning ) { m_lastNotePanning = pan; showPanTextFloat( pan, me->pos() ); @@ -2533,7 +2533,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) ( isUnderPosition && n->selected() && altPressed ) ) { - if( m_noteEditMode == NoteEditVolume ) + if( m_noteEditMode == NoteEditMode::Volume ) { n->setVolume( vol ); @@ -2541,7 +2541,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) m_midiClip->instrumentTrack()->processInEvent( MidiEvent( MidiKeyPressure, -1, n->key(), n->midiVelocity( baseVelocity ) ) ); } - else if( m_noteEditMode == NoteEditPanning ) + else if( m_noteEditMode == NoteEditMode::Panning ) { n->setPanning( pan ); MidiEvent evt( MidiMetaEvent, -1, n->key(), panningToMidi( pan ) ); @@ -2566,7 +2566,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) m_midiClip->dataChanged(); } - else if( me->buttons() == Qt::NoButton && m_editMode == ModeDraw ) + else if( me->buttons() == Qt::NoButton && m_editMode == EditMode::Draw ) { // set move- or resize-cursor @@ -2619,8 +2619,8 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } } else if( me->buttons() & Qt::LeftButton && - m_editMode == ModeSelect && - m_action == ActionSelectNotes ) + m_editMode == EditMode::Select && + m_action == Action::SelectNotes ) { // change size of selection @@ -2640,8 +2640,8 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) --m_selectedKeys; } } - else if( ( m_editMode == ModeDraw && me->buttons() & Qt::RightButton ) - || ( m_editMode == ModeErase && me->buttons() ) ) + else if( ( m_editMode == EditMode::Draw && me->buttons() & Qt::RightButton ) + || ( m_editMode == EditMode::Erase && me->buttons() ) ) { // holding down right-click to delete notes or holding down // any key if in erase mode @@ -2693,7 +2693,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } } } - else if (me->buttons() == Qt::NoButton && m_editMode != ModeDraw && m_editMode != ModeEditKnife) + else if (me->buttons() == Qt::NoButton && m_editMode != EditMode::Draw && m_editMode != EditMode::Knife) { // Is needed to restore cursor when it previously was set to // Qt::SizeVerCursor (between keyAreaBottom and noteEditTop) @@ -2703,8 +2703,8 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) else { if( me->buttons() & Qt::LeftButton && - m_editMode == ModeSelect && - m_action == ActionSelectNotes ) + m_editMode == EditMode::Select && + m_action == Action::SelectNotes ) { int x = me->x() - m_whiteKeyWidth; @@ -2823,11 +2823,11 @@ void PianoRoll::dragNotes(int x, int y, bool alt, bool shift, bool ctrl) // get note-vector of current MIDI clip const NoteVector & notes = m_midiClip->notes(); - if (m_action == ActionMoveNote) + if (m_action == Action::MoveNote) { // Calculate the offset for either Nudge or Snap modes int noteOffset = off_ticks; - if (m_gridMode == gridSnap && quantization () > 1) + if (m_gridMode == GridMode::Snap && quantization () > 1) { // Get the mouse timeline absolute position TimePos mousePos(m_currentNote->oldPos().getTicks() + off_ticks); @@ -2849,7 +2849,7 @@ void PianoRoll::dragNotes(int x, int y, bool alt, bool shift, bool ctrl) ? mousePosEndQ.getTicks() - m_currentNote->oldPos().getTicks() - m_currentNote->oldLength().getTicks() : mousePosQ.getTicks() - m_currentNote->oldPos().getTicks(); } - else if (m_gridMode == gridNudge) + else if (m_gridMode == GridMode::Nudge) { // if they're not holding alt, quantize the offset if (!alt) @@ -2877,7 +2877,7 @@ void PianoRoll::dragNotes(int x, int y, bool alt, bool shift, bool ctrl) { // Quick resize is only enabled on Nudge mode, since resizing the note // while in Snap mode breaks the calculation of the note offset - if (shift && ! m_startedWithShift && m_gridMode == gridNudge) + if (shift && ! m_startedWithShift && m_gridMode == GridMode::Nudge) { // quick resize, toggled by holding shift after starting a note move, but not before int ticks_new = note->oldLength().getTicks() + noteOffset; @@ -2901,7 +2901,7 @@ void PianoRoll::dragNotes(int x, int y, bool alt, bool shift, bool ctrl) } } } - else if (m_action == ActionResizeNote) + else if (m_action == Action::ResizeNote) { // When resizing notes: // If shift is not pressed, resize the selected notes but do not rearrange them @@ -3002,7 +3002,7 @@ void PianoRoll::dragNotes(int x, int y, bool alt, bool shift, bool ctrl) // shift is not pressed; stretch length of selected notes but not their position int minLength = alt ? 1 : m_minResizeLen.getTicks(); - if (m_gridMode == gridSnap) + if (m_gridMode == GridMode::Snap) { // Calculate the end point of the note being dragged TimePos oldEndPoint = m_currentNote->oldPos() + m_currentNote->oldLength(); @@ -3099,7 +3099,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) topKey = qBound(0, m_startKey + m_pianoKeysVisible - 1, NumKeys - 1); topNote = topKey % KeysPerOctave; // if not resizing the note edit area, we can change m_notesEditHeight - if (m_action != ActionResizeNoteEditArea && partialKeyVisible != 0) + if (m_action != Action::ResizeNoteEditArea && partialKeyVisible != 0) { // calculate the height change adding and subtracting the partial key int noteAreaPlus = (m_notesEditHeight + partialKeyVisible) - m_userSetNotesEditHeight; @@ -3153,11 +3153,11 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) { switch (prKeyOrder[key % KeysPerOctave]) { - case PR_WHITE_KEY_BIG: + case KeyType::WhiteBig: return m_whiteKeyBigHeight; - case PR_WHITE_KEY_SMALL: + case KeyType::WhiteSmall: return m_whiteKeySmallHeight; - case PR_BLACK_KEY: + case KeyType::Black: return m_blackKeyHeight; } return 0; // should never happen @@ -3170,15 +3170,15 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int keyCode = key % KeysPerOctave; switch (prKeyOrder[keyCode]) { - case PR_WHITE_KEY_BIG: + case KeyType::WhiteBig: return m_whiteKeySmallHeight; - case PR_WHITE_KEY_SMALL: + case KeyType::WhiteSmall: // These two keys need to adjust up small height instead of only key line height - if (keyCode == Key_C || keyCode == Key_F) + if (static_cast(keyCode) == Key::C || static_cast(keyCode) == Key::F) { return m_whiteKeySmallHeight; } - case PR_BLACK_KEY: + case KeyType::Black: return m_blackKeyHeight; } return 0; // should never happen @@ -3189,10 +3189,10 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) { switch (prKeyOrder[key % KeysPerOctave]) { - case PR_WHITE_KEY_SMALL: - case PR_WHITE_KEY_BIG: + case KeyType::WhiteSmall: + case KeyType::WhiteBig: return m_whiteKeyWidth; - case PR_BLACK_KEY: + case KeyType::Black: return m_blackKeyWidth; } return 0; // should never happen @@ -3212,8 +3212,8 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) p.setPen(QColor(0, 0, 0)); switch (prKeyOrder[keyCode]) { - case PR_WHITE_KEY_SMALL: - case PR_WHITE_KEY_BIG: + case KeyType::WhiteSmall: + case KeyType::WhiteBig: if (mapped) { if (pressed) { p.setBrush(m_whiteKeyActiveBackground); } @@ -3224,7 +3224,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) p.setBrush(m_whiteKeyDisabledBackground); } break; - case PR_BLACK_KEY: + case KeyType::Black: if (mapped) { if (pressed) { p.setBrush(m_blackKeyActiveBackground); } @@ -3238,7 +3238,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // draw key p.drawRect(PIANO_X, yt, kw, kh); // draw note name - if (keyCode == Key_C || (drawNoteNames && Piano::isWhiteKey(key))) + if (static_cast(keyCode) == Key::C || (drawNoteNames && Piano::isWhiteKey(key))) { // small font sizes have 1 pixel offset instead of 2 auto zoomOffset = m_zoomYLevels[m_zoomingYModel.value()] > 1.0f ? 2 : 1; @@ -3252,7 +3252,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) p.setPen(pressed ? m_whiteKeyActiveTextShadow : m_whiteKeyInactiveTextShadow); p.drawText(textRect.adjusted(0, 1, 1, 0), Qt::AlignRight | Qt::AlignHCenter, noteString); p.setPen(pressed ? m_whiteKeyActiveTextColor : m_whiteKeyInactiveTextColor); - // if (keyCode == Key_C) { p.setPen(textColor()); } + // if (static_cast(keyCode) == Key::C) { p.setPen(textColor()); } // else { p.setPen(textColorLight()); } p.drawText(textRect, Qt::AlignRight | Qt::AlignHCenter, noteString); } @@ -3263,17 +3263,17 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) const int y ) { - if (key % KeysPerOctave == Key_C) { p.setPen(m_beatLineColor); } + if (static_cast(key % KeysPerOctave) == Key::C) { p.setPen(m_beatLineColor); } else { p.setPen(m_lineColor); } p.drawLine(m_whiteKeyWidth, y, width(), y); }; // correct y offset of the top key switch (prKeyOrder[topNote]) { - case PR_WHITE_KEY_SMALL: - case PR_WHITE_KEY_BIG: + case KeyType::WhiteSmall: + case KeyType::WhiteBig: break; - case PR_BLACK_KEY: + case KeyType::Black: // draw extra white key drawKey(topKey + 1, grid_line_y - m_keyLineHeight); } @@ -3381,7 +3381,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) p.drawText( QRect( 0, keyAreaBottom(), m_whiteKeyWidth, noteEditBottom() - keyAreaBottom()), Qt::AlignCenter | Qt::TextWordWrap, - m_nemStr.at( m_noteEditMode ) + ":" ); + m_nemStr.at(static_cast(m_noteEditMode)) + ":" ); // set clipping area, because we are not allowed to paint over // keyboard... @@ -3507,7 +3507,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // draw note editing stuff int editHandleTop = 0; - if( m_noteEditMode == NoteEditVolume ) + if( m_noteEditMode == NoteEditMode::Volume ) { QColor color = m_barColor.lighter(30 + (note->getVolume() * 90 / MaxVolume)); if( note->selected() ) @@ -3525,7 +3525,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) noteEditLeft() + x + 0.5, noteEditBottom() + 0.5 ) ); } - else if( m_noteEditMode == NoteEditPanning ) + else if( m_noteEditMode == NoteEditMode::Panning ) { QColor color = m_noteColor; if( note->selected() ) @@ -3559,7 +3559,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } // -- Knife tool (draw cut line) - if (m_action == ActionKnife) + if (m_action == Action::Knife) { auto xCoordOfTick = [this](int tick) { return m_whiteKeyWidth + ( @@ -3696,12 +3696,12 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // draw current edit-mode-icon below the cursor switch( m_editMode ) { - case ModeDraw: + case EditMode::Draw: if( m_mouseDownRight ) { cursor = s_toolErase; } - else if( m_action == ActionMoveNote ) + else if( m_action == Action::MoveNote ) { cursor = s_toolMove; } @@ -3710,10 +3710,10 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) cursor = s_toolDraw; } break; - case ModeErase: cursor = s_toolErase; break; - case ModeSelect: cursor = s_toolSelect; break; - case ModeEditDetuning: cursor = s_toolOpen; break; - case ModeEditKnife: cursor = s_toolKnife; break; + case EditMode::Erase: cursor = s_toolErase; break; + case EditMode::Select: cursor = s_toolSelect; break; + case EditMode::Detuning: cursor = s_toolOpen; break; + case EditMode::Knife: cursor = s_toolKnife; break; } QPoint mousePosition = mapFromGlobal( QCursor::pos() ); if( cursor != nullptr && mousePosition.y() > keyAreaTop() && mousePosition.x() > noteEditLeft()) @@ -3756,7 +3756,7 @@ void PianoRoll::resizeEvent(QResizeEvent* re) { updatePositionLineHeight(); updateScrollbars(); - Engine::getSong()->getPlayPos(Song::Mode_PlayMidiClip) + Engine::getSong()->getPlayPos(Song::PlayMode::MidiClip) .m_timeLine->setFixedWidth(width()); update(); } @@ -3794,7 +3794,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) if( nv.size() > 0 ) { const int step = we->angleDelta().y() > 0 ? 1 : -1; - if( m_noteEditMode == NoteEditVolume ) + if( m_noteEditMode == NoteEditMode::Volume ) { for ( Note * n : nv ) { @@ -3813,7 +3813,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) showVolTextFloat(nv[0]->getVolume(), position(we), 1000); } } - else if( m_noteEditMode == NoteEditPanning ) + else if( m_noteEditMode == NoteEditMode::Panning ) { for ( Note * n : nv ) { @@ -3921,9 +3921,9 @@ void PianoRoll::focusOutEvent( QFocusEvent * ) m_midiClip->instrumentTrack()->pianoModel()->setKeyState( i, false ); } } - if (m_editMode == ModeEditKnife) { + if (m_editMode == EditMode::Knife) { m_editMode = m_knifeMode; - m_action = ActionNone; + m_action = Action::None; } else { m_editMode = m_ctrlMode; } @@ -3968,13 +3968,13 @@ QList PianoRoll::getAllOctavesForKey( int keyToMirror ) const return keys; } -Song::PlayModes PianoRoll::desiredPlayModeForAccompany() const +Song::PlayMode PianoRoll::desiredPlayModeForAccompany() const { if (m_midiClip->getTrack()->trackContainer() == Engine::patternStore()) { - return Song::Mode_PlayPattern; + return Song::PlayMode::Pattern; } - return Song::Mode_PlaySong; + return Song::PlayMode::Song; } @@ -3987,7 +3987,7 @@ void PianoRoll::play() return; } - if( Engine::getSong()->playMode() != Song::Mode_PlayMidiClip ) + if( Engine::getSong()->playMode() != Song::PlayMode::MidiClip ) { Engine::getSong()->playMidiClip( m_midiClip ); } @@ -4065,7 +4065,7 @@ bool PianoRoll::toggleStepRecording() { m_stepRecorder.start( Engine::getSong()->getPlayPos( - Song::Mode_PlayMidiClip), newNoteLen()); + Song::PlayMode::MidiClip), newNoteLen()); } } } @@ -4080,7 +4080,7 @@ void PianoRoll::stop() { Engine::getSong()->stop(); m_recording = false; - m_scrollBack = ( m_timeLine->autoScroll() == TimeLineWidget::AutoScrollEnabled ); + m_scrollBack = ( m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ); } @@ -4093,10 +4093,10 @@ void PianoRoll::startRecordNote(const Note & n ) if( m_recording && Engine::getSong()->isPlaying() && (Engine::getSong()->playMode() == desiredPlayModeForAccompany() || - Engine::getSong()->playMode() == Song::Mode_PlayMidiClip )) + Engine::getSong()->playMode() == Song::PlayMode::MidiClip )) { TimePos sub; - if( Engine::getSong()->playMode() == Song::Mode_PlaySong ) + if( Engine::getSong()->playMode() == Song::PlayMode::Song ) { sub = m_midiClip->startPosition(); } @@ -4127,7 +4127,7 @@ void PianoRoll::finishRecordNote(const Note & n ) ( Engine::getSong()->playMode() == desiredPlayModeForAccompany() || Engine::getSong()->playMode() == - Song::Mode_PlayMidiClip ) ) + Song::PlayMode::MidiClip ) ) { for( QList::Iterator it = m_recordingNotes.begin(); it != m_recordingNotes.end(); ++it ) @@ -4179,7 +4179,7 @@ void PianoRoll::verScrolled( int new_pos ) void PianoRoll::setEditMode(int mode) { - m_ctrlMode = m_editMode = (EditModes) mode; + m_ctrlMode = m_editMode = (EditMode) mode; } @@ -4271,7 +4271,7 @@ void PianoRoll::selectNotesOnKey() void PianoRoll::enterValue( NoteVector* nv ) { - if( m_noteEditMode == NoteEditVolume ) + if( m_noteEditMode == NoteEditMode::Volume ) { bool ok; int new_val; @@ -4290,7 +4290,7 @@ void PianoRoll::enterValue( NoteVector* nv ) m_lastNoteVolume = new_val; } } - else if( m_noteEditMode == NoteEditPanning ) + else if( m_noteEditMode == NoteEditMode::Panning ) { bool ok; int new_val; @@ -4338,7 +4338,7 @@ void PianoRoll::copyToClipboard( const NoteVector & notes ) const // For copyString() and MimeType enum class using namespace Clipboard; - DataFile dataFile( DataFile::ClipboardData ); + DataFile dataFile( DataFile::Type::ClipboardData ); QDomElement note_list = dataFile.createElement( "note-list" ); dataFile.content().appendChild( note_list ); @@ -4495,8 +4495,8 @@ void PianoRoll::autoScroll( const TimePos & t ) void PianoRoll::updatePosition( const TimePos & t ) { if( ( Engine::getSong()->isPlaying() - && Engine::getSong()->playMode() == Song::Mode_PlayMidiClip - && m_timeLine->autoScroll() == TimeLineWidget::AutoScrollEnabled + && Engine::getSong()->playMode() == Song::PlayMode::MidiClip + && m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ) || m_scrollBack ) { autoScroll( t ); @@ -4532,16 +4532,16 @@ void PianoRoll::updatePositionAccompany( const TimePos & t ) Song * s = Engine::getSong(); if( m_recording && hasValidMidiClip() && - s->playMode() != Song::Mode_PlayMidiClip ) + s->playMode() != Song::PlayMode::MidiClip ) { TimePos pos = t; - if (s->playMode() != Song::Mode_PlayPattern) + if (s->playMode() != Song::PlayMode::Pattern) { pos -= m_midiClip->startPosition(); } if( (int) pos > 0 ) { - s->getPlayPos( Song::Mode_PlayMidiClip ).setTicks( pos ); + s->getPlayPos( Song::PlayMode::MidiClip ).setTicks( pos ); autoScroll( pos ); } } @@ -4596,7 +4596,7 @@ void PianoRoll::noteLengthChanged() void PianoRoll::keyChanged() { - markSemiTone(stmaMarkCurrentScale, false); + markSemiTone(SemiToneMarkerAction::MarkCurrentScale, false); } int PianoRoll::quantization() const @@ -4617,7 +4617,7 @@ int PianoRoll::quantization() const } -void PianoRoll::quantizeNotes(QuantizeActions mode) +void PianoRoll::quantizeNotes(QuantizeAction mode) { if( ! hasValidMidiClip() ) { @@ -4645,11 +4645,11 @@ void PianoRoll::quantizeNotes(QuantizeActions mode) Note copy(*n); m_midiClip->removeNote( n ); - if (mode == QuantizeBoth || mode == QuantizePos) + if (mode == QuantizeAction::Both || mode == QuantizeAction::Pos) { copy.quantizePos(quantization()); } - if (mode == QuantizeBoth || mode == QuantizeLength) + if (mode == QuantizeAction::Both || mode == QuantizeAction::Length) { copy.quantizeLength(quantization()); } @@ -4737,9 +4737,9 @@ Note * PianoRoll::noteUnderMouse() void PianoRoll::changeSnapMode() { - // gridNudge, - // gridSnap, - // gridFree - to be implemented + // GridMode::Nudge, + // GridMode::Snap, + // GridMode::Free - to be implemented m_gridMode = static_cast(m_snapModel.value()); } @@ -4783,8 +4783,8 @@ PianoRollWindow::PianoRollWindow() : auto quantizeLengthAction = new QAction(tr("Quantize lengths"), this); connect(quantizeAction, &QAction::triggered, [this](){ m_editor->quantizeNotes(); }); - connect(quantizePosAction, &QAction::triggered, [this](){ m_editor->quantizeNotes(PianoRoll::QuantizePos); }); - connect(quantizeLengthAction, &QAction::triggered, [this](){ m_editor->quantizeNotes(PianoRoll::QuantizeLength); }); + connect(quantizePosAction, &QAction::triggered, [this](){ m_editor->quantizeNotes(PianoRoll::QuantizeAction::Pos); }); + connect(quantizeLengthAction, &QAction::triggered, [this](){ m_editor->quantizeNotes(PianoRoll::QuantizeAction::Length); }); quantizeButton->setPopupMode(QToolButton::MenuButtonPopup); quantizeButton->setDefaultAction(quantizeAction); @@ -5189,7 +5189,7 @@ void PianoRollWindow::saveSettings( QDomDocument & doc, QDomElement & de ) de.appendChild(markedSemiTonesRoot); } - de.setAttribute("stopbehaviour", m_editor->m_timeLine->behaviourAtStop()); + de.setAttribute("stopbehaviour", static_cast(m_editor->m_timeLine->behaviourAtStop())); MainWindow::saveWidgetState( this, de ); } @@ -5281,7 +5281,7 @@ void PianoRollWindow::exportMidiClip() exportDialog.setDefaultSuffix(suffix); const QString fullPath = exportDialog.selectedFiles()[0]; - DataFile dataFile(DataFile::MidiClip); + DataFile dataFile(DataFile::Type::MidiClip); m_editor->m_midiClip->saveSettings(dataFile, dataFile.content()); if (dataFile.writeFile(fullPath)) diff --git a/src/gui/editors/PositionLine.cpp b/src/gui/editors/PositionLine.cpp index 7dd8b3b13..8b938443d 100644 --- a/src/gui/editors/PositionLine.cpp +++ b/src/gui/editors/PositionLine.cpp @@ -64,8 +64,8 @@ void PositionLine::paintEvent(QPaintEvent* pe) // If gradient is enabled, we're in focus and we're playing, enable gradient if (m_hasTailGradient && Engine::getSong()->isPlaying() && - (Engine::getSong()->playMode() == Song::Mode_PlaySong || - Engine::getSong()->playMode() == Song::Mode_PlayMidiClip)) + (Engine::getSong()->playMode() == Song::PlayMode::Song || + Engine::getSong()->playMode() == Song::PlayMode::MidiClip)) { c.setAlpha(60); gradient.setColorAt(w, c); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 939c1fdf4..518068759 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -82,7 +82,7 @@ SongEditor::SongEditor( Song * song ) : m_proportionalSnap( false ), m_scrollBack( false ), m_smoothScroll( ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt() ), - m_mode(DrawMode), + m_mode(EditMode::Draw), m_origin(), m_scrollPos(), m_mousePos(), @@ -99,11 +99,11 @@ SongEditor::SongEditor( Song * song ) : m_timeLine = new TimeLineWidget( m_trackHeadWidth, 32, pixelsPerBar(), - m_song->m_playPos[Song::Mode_PlaySong], + m_song->getPlayPos(Song::PlayMode::Song), m_currentPosition, - Song::Mode_PlaySong, this ); + Song::PlayMode::Song, this ); connect( this, SIGNAL( positionChanged( const lmms::TimePos& ) ), - m_song->m_playPos[Song::Mode_PlaySong].m_timeLine, + m_song->getPlayPos(Song::PlayMode::Song).m_timeLine, SLOT( updatePosition( const lmms::TimePos& ) ) ); connect( m_timeLine, SIGNAL( positionChanged( const lmms::TimePos& ) ), this, SLOT( updatePosition( const lmms::TimePos& ) ) ); @@ -341,8 +341,8 @@ QString SongEditor::getSnapSizeString() const void SongEditor::setHighQuality( bool hq ) { Engine::audioEngine()->changeQuality( AudioEngine::qualitySettings( - hq ? AudioEngine::qualitySettings::Mode_HighQuality : - AudioEngine::qualitySettings::Mode_Draft ) ); + hq ? AudioEngine::qualitySettings::Mode::HighQuality : + AudioEngine::qualitySettings::Mode::Draft ) ); } @@ -456,17 +456,17 @@ void SongEditor::setEditMode( EditMode mode ) void SongEditor::setEditModeDraw() { - setEditMode(DrawMode); + setEditMode(EditMode::Draw); } void SongEditor::setEditModeKnife() { - setEditMode(KnifeMode); + setEditMode(EditMode::Knife); } void SongEditor::setEditModeSelect() { - setEditMode(SelectMode); + setEditMode(EditMode::Select); } void SongEditor::toggleProportionalSnap() @@ -496,7 +496,7 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) tick_t t = m_song->currentTick() - TimePos::ticksPerBar(); if( t >= 0 ) { - m_song->setPlayPos( t, Song::Mode_PlaySong ); + m_song->setPlayPos( t, Song::PlayMode::Song ); } } else if( ke->key() == Qt::Key_Right ) @@ -504,12 +504,12 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) tick_t t = m_song->currentTick() + TimePos::ticksPerBar(); if( t < MaxSongLength ) { - m_song->setPlayPos( t, Song::Mode_PlaySong ); + m_song->setPlayPos( t, Song::PlayMode::Song ); } } else if( ke->key() == Qt::Key_Home ) { - m_song->setPlayPos( 0, Song::Mode_PlaySong ); + m_song->setPlayPos( 0, Song::PlayMode::Song ); } else if( ke->key() == Qt::Key_Delete || ke->key() == Qt::Key_Backspace ) { @@ -560,7 +560,7 @@ void SongEditor::wheelEvent( QWheelEvent * we ) m_leftRightScroll->setValue(m_leftRightScroll->value() + bar - newBar); // update timeline - m_song->m_playPos[Song::Mode_PlaySong].m_timeLine->setPixelsPerBar(pixelsPerBar()); + m_song->getPlayPos(Song::PlayMode::Song).m_timeLine->setPixelsPerBar(pixelsPerBar()); // and make sure, all Clip's are resized and relocated realignTracks(); } @@ -788,8 +788,8 @@ void SongEditor::updatePosition( const TimePos & t ) trackOpWidth = TRACK_OP_WIDTH; } - if( ( m_song->isPlaying() && m_song->m_playMode == Song::Mode_PlaySong - && m_timeLine->autoScroll() == TimeLineWidget::AutoScrollEnabled) || + if( ( m_song->isPlaying() && m_song->m_playMode == Song::PlayMode::Song + && m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled) || m_scrollBack == true ) { m_smoothScroll = ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt(); @@ -808,7 +808,7 @@ void SongEditor::updatePosition( const TimePos & t ) m_scrollBack = false; } - const int x = m_song->m_playPos[Song::Mode_PlaySong].m_timeLine-> + const int x = m_song->getPlayPos(Song::PlayMode::Song).m_timeLine-> markerX( t ) + 8; if( x >= trackOpWidth + widgetWidth -1 ) { @@ -872,7 +872,7 @@ void SongEditor::zoomingChanged() int ppb = calculatePixelsPerBar(); setPixelsPerBar(ppb); - m_song->m_playPos[Song::Mode_PlaySong].m_timeLine->setPixelsPerBar(ppb); + m_song->getPlayPos(Song::PlayMode::Song).m_timeLine->setPixelsPerBar(ppb); realignTracks(); updateRubberband(); m_timeLine->setSnapSize(getSnapSize()); @@ -895,7 +895,7 @@ void SongEditor::selectAllClips( bool select ) bool SongEditor::allowRubberband() const { - return m_mode == SelectMode; + return m_mode == EditMode::Select; } @@ -903,7 +903,7 @@ bool SongEditor::allowRubberband() const bool SongEditor::knifeMode() const { - return m_mode == KnifeMode; + return m_mode == EditMode::Knife; } @@ -1104,7 +1104,7 @@ void SongEditorWindow::changeEvent(QEvent *event) void SongEditorWindow::play() { emit playTriggered(); - if( Engine::getSong()->playMode() != Song::Mode_PlaySong ) + if( Engine::getSong()->playMode() != Song::PlayMode::Song ) { Engine::getSong()->playSong(); } diff --git a/src/gui/editors/TimeLineWidget.cpp b/src/gui/editors/TimeLineWidget.cpp index b051fa79b..423485a25 100644 --- a/src/gui/editors/TimeLineWidget.cpp +++ b/src/gui/editors/TimeLineWidget.cpp @@ -49,7 +49,7 @@ namespace QPixmap * TimeLineWidget::s_posMarkerPixmap = nullptr; TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppb, - Song::PlayPos & pos, const TimePos & begin, Song::PlayModes mode, + Song::PlayPos & pos, const TimePos & begin, Song::PlayMode mode, QWidget * parent ) : QWidget( parent ), m_inactiveLoopColor( 52, 63, 53, 64 ), @@ -61,9 +61,9 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppb, m_loopRectangleVerticalPadding( 1 ), m_barLineColor( 192, 192, 192 ), m_barNumberColor( m_barLineColor.darker( 120 ) ), - m_autoScroll( AutoScrollEnabled ), - m_loopPoints( LoopPointsDisabled ), - m_behaviourAtStop( BackToZero ), + m_autoScroll( AutoScrollState::Enabled ), + m_loopPoints( LoopPointState::Disabled ), + m_behaviourAtStop( BehaviourAtStopState::BackToZero ), m_changedPosition( true ), m_xOffset( xoff ), m_posMarkerX( 0 ), @@ -74,7 +74,7 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppb, m_mode( mode ), m_savedPos( -1 ), m_hint( nullptr ), - m_action( NoAction ), + m_action( Action::NoAction ), m_moveXOff( 0 ) { m_loopPos[0] = 0; @@ -157,7 +157,7 @@ void TimeLineWidget::addToolButtons( QToolBar * _tool_bar ) SLOT(toggleBehaviourAtStop(int))); connect( this, SIGNAL(loadBehaviourAtStop(int)), behaviourAtStop, SLOT(changeState(int))); - behaviourAtStop->changeState( BackToStart ); + behaviourAtStop->changeState( static_cast(BehaviourAtStopState::BackToStart) ); _tool_bar->addWidget( autoScroll ); _tool_bar->addWidget( loopPoints ); @@ -171,8 +171,8 @@ void TimeLineWidget::saveSettings( QDomDocument & _doc, QDomElement & _this ) { _this.setAttribute( "lp0pos", (int) loopBegin() ); _this.setAttribute( "lp1pos", (int) loopEnd() ); - _this.setAttribute( "lpstate", m_loopPoints ); - _this.setAttribute( "stopbehaviour", m_behaviourAtStop ); + _this.setAttribute( "lpstate", static_cast(m_loopPoints) ); + _this.setAttribute( "stopbehaviour", static_cast(m_behaviourAtStop) ); } @@ -182,10 +182,10 @@ void TimeLineWidget::loadSettings( const QDomElement & _this ) { m_loopPos[0] = _this.attribute( "lp0pos" ).toInt(); m_loopPos[1] = _this.attribute( "lp1pos" ).toInt(); - m_loopPoints = static_cast( + m_loopPoints = static_cast( _this.attribute( "lpstate" ).toInt() ); update(); - emit loopPointStateLoaded( m_loopPoints ); + emit loopPointStateLoaded( static_cast(m_loopPoints) ); if( _this.hasAttribute( "stopbehaviour" ) ) { @@ -214,7 +214,7 @@ void TimeLineWidget::updatePosition( const TimePos & ) void TimeLineWidget::toggleAutoScroll( int _n ) { - m_autoScroll = static_cast( _n ); + m_autoScroll = static_cast( _n ); } @@ -222,7 +222,7 @@ void TimeLineWidget::toggleAutoScroll( int _n ) void TimeLineWidget::toggleLoopPoints( int _n ) { - m_loopPoints = static_cast( _n ); + m_loopPoints = static_cast( _n ); update(); } @@ -231,7 +231,7 @@ void TimeLineWidget::toggleLoopPoints( int _n ) void TimeLineWidget::toggleBehaviourAtStop( int _n ) { - m_behaviourAtStop = static_cast( _n ); + m_behaviourAtStop = static_cast( _n ); } @@ -327,7 +327,7 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event ) } if( event->button() == Qt::LeftButton && !(event->modifiers() & Qt::ShiftModifier) ) { - m_action = MovePositionMarker; + m_action = Action::MovePositionMarker; if( event->x() - m_xOffset < s_posMarkerPixmap->width() ) { m_moveXOff = event->x() - m_xOffset; @@ -339,7 +339,7 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event ) } else if( event->button() == Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier) ) { - m_action = SelectSongClip; + m_action = Action::SelectSongClip; m_initalXSelect = event->x(); } else if( event->button() == Qt::RightButton ) @@ -348,12 +348,12 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event ) const TimePos t = m_begin + static_cast( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * TimePos::ticksPerBar() / m_ppb ); const TimePos loopMid = ( m_loopPos[0] + m_loopPos[1] ) / 2; - m_action = t < loopMid ? MoveLoopBegin : MoveLoopEnd; + m_action = t < loopMid ? Action::MoveLoopBegin : Action::MoveLoopEnd; std::sort(std::begin(m_loopPos), std::end(m_loopPos)); - m_loopPos[( m_action == MoveLoopBegin ) ? 0 : 1] = t; + m_loopPos[( m_action == Action::MoveLoopBegin ) ? 0 : 1] = t; } - if( m_action == MoveLoopBegin || m_action == MoveLoopEnd ) + if( m_action == Action::MoveLoopBegin || m_action == Action::MoveLoopEnd ) { delete m_hint; m_hint = TextFloat::displayMessage( tr( "Hint" ), @@ -373,13 +373,13 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event ) switch( m_action ) { - case MovePositionMarker: + case Action::MovePositionMarker: m_pos.setTicks(t.getTicks()); Engine::getSong()->setToTime(t, m_mode); if (!( Engine::getSong()->isPlaying())) { - //Song::Mode_None is used when nothing is being played. - Engine::getSong()->setToTime(t, Song::Mode_None); + //Song::PlayMode::None is used when nothing is being played. + Engine::getSong()->setToTime(t, Song::PlayMode::None); } m_pos.setCurrentFrame( 0 ); m_pos.setJumped( true ); @@ -387,10 +387,10 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event ) positionMarkerMoved(); break; - case MoveLoopBegin: - case MoveLoopEnd: + case Action::MoveLoopBegin: + case Action::MoveLoopEnd: { - const int i = m_action - MoveLoopBegin; // i == 0 || i == 1 + const int i = m_action == Action::MoveLoopBegin ? 0 : 1; const bool control = event->modifiers() & Qt::ControlModifier; if (control) { @@ -409,13 +409,13 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event ) const int offset = control ? 1 : m_snapSize * TimePos::ticksPerBar(); // Note, swap 1 and 0 below and the behavior "skips" the other // marking instead of pushing it. - if (m_action == MoveLoopBegin) { m_loopPos[0] -= offset; } + if (m_action == Action::MoveLoopBegin) { m_loopPos[0] -= offset; } else { m_loopPos[1] += offset; } } update(); break; } - case SelectSongClip: + case Action::SelectSongClip: emit regionSelectedFromPixels( m_initalXSelect , event->x() ); break; @@ -431,8 +431,8 @@ void TimeLineWidget::mouseReleaseEvent( QMouseEvent* event ) { delete m_hint; m_hint = nullptr; - if ( m_action == SelectSongClip ) { emit selectionFinished(); } - m_action = NoAction; + if ( m_action == Action::SelectSongClip ) { emit selectionFinished(); } + m_action = Action::NoAction; } diff --git a/src/gui/editors/TrackContainerView.cpp b/src/gui/editors/TrackContainerView.cpp index 83017fb1a..60a468380 100644 --- a/src/gui/editors/TrackContainerView.cpp +++ b/src/gui/editors/TrackContainerView.cpp @@ -372,8 +372,8 @@ void TrackContainerView::dragEnterEvent( QDragEnterEvent * _dee ) QString( "presetfile,pluginpresetfile,samplefile,instrument," "importedproject,soundfontfile,patchfile,vstpluginfile,projectfile," "track_%1,track_%2" ). - arg( Track::InstrumentTrack ). - arg( Track::SampleTrack ) ); + arg( static_cast(Track::Type::Instrument) ). + arg( static_cast(Track::Type::Sample) ) ); } @@ -394,7 +394,7 @@ void TrackContainerView::dropEvent( QDropEvent * _de ) QString value = StringPairDrag::decodeValue( _de ); if( type == "instrument" ) { - auto it = dynamic_cast(Track::create(Track::InstrumentTrack, m_tc)); + auto it = dynamic_cast(Track::create(Track::Type::Instrument, m_tc)); auto ilt = new InstrumentLoaderThread(this, it, value); ilt->start(); //it->toggledInstrumentTrackButton( true ); @@ -404,7 +404,7 @@ void TrackContainerView::dropEvent( QDropEvent * _de ) || type == "soundfontfile" || type == "vstpluginfile" || type == "patchfile" ) { - auto it = dynamic_cast(Track::create(Track::InstrumentTrack, m_tc)); + auto it = dynamic_cast(Track::create(Track::Type::Instrument, m_tc)); PluginFactory::PluginInfoAndKey piakn = getPluginFactory()->pluginSupportingExtension(FileItem::extension(value)); Instrument * i = it->loadInstrument(piakn.info.name(), &piakn.key); @@ -415,7 +415,7 @@ void TrackContainerView::dropEvent( QDropEvent * _de ) else if( type == "presetfile" ) { DataFile dataFile( value ); - auto it = dynamic_cast(Track::create(Track::InstrumentTrack, m_tc)); + auto it = dynamic_cast(Track::create(Track::Type::Instrument, m_tc)); it->setSimpleSerializing(); it->loadSettings( dataFile.content().toElement() ); //it->toggledInstrumentTrackButton( true ); diff --git a/src/gui/instrument/EnvelopeAndLfoView.cpp b/src/gui/instrument/EnvelopeAndLfoView.cpp index df0e1120d..edb6c99c7 100644 --- a/src/gui/instrument/EnvelopeAndLfoView.cpp +++ b/src/gui/instrument/EnvelopeAndLfoView.cpp @@ -99,43 +99,43 @@ EnvelopeAndLfoView::EnvelopeAndLfoView( QWidget * _parent ) : s_lfoGraph = new QPixmap( embed::getIconPixmap( "lfo_graph" ) ); } - m_predelayKnob = new Knob( knobBright_26, this ); + m_predelayKnob = new Knob( KnobType::Bright26, this ); m_predelayKnob->setLabel( tr( "DEL" ) ); m_predelayKnob->move( PREDELAY_KNOB_X, ENV_KNOBS_Y ); m_predelayKnob->setHintText( tr( "Pre-delay:" ), "" ); - m_attackKnob = new Knob( knobBright_26, this ); + m_attackKnob = new Knob( KnobType::Bright26, this ); m_attackKnob->setLabel( tr( "ATT" ) ); m_attackKnob->move( ATTACK_KNOB_X, ENV_KNOBS_Y ); m_attackKnob->setHintText( tr( "Attack:" ), "" ); - m_holdKnob = new Knob( knobBright_26, this ); + m_holdKnob = new Knob( KnobType::Bright26, this ); m_holdKnob->setLabel( tr( "HOLD" ) ); m_holdKnob->move( HOLD_KNOB_X, ENV_KNOBS_Y ); m_holdKnob->setHintText( tr( "Hold:" ), "" ); - m_decayKnob = new Knob( knobBright_26, this ); + m_decayKnob = new Knob( KnobType::Bright26, this ); m_decayKnob->setLabel( tr( "DEC" ) ); m_decayKnob->move( DECAY_KNOB_X, ENV_KNOBS_Y ); m_decayKnob->setHintText( tr( "Decay:" ), "" ); - m_sustainKnob = new Knob( knobBright_26, this ); + m_sustainKnob = new Knob( KnobType::Bright26, this ); m_sustainKnob->setLabel( tr( "SUST" ) ); m_sustainKnob->move( SUSTAIN_KNOB_X, ENV_KNOBS_Y ); m_sustainKnob->setHintText( tr( "Sustain:" ), "" ); - m_releaseKnob = new Knob( knobBright_26, this ); + m_releaseKnob = new Knob( KnobType::Bright26, this ); m_releaseKnob->setLabel( tr( "REL" ) ); m_releaseKnob->move( RELEASE_KNOB_X, ENV_KNOBS_Y ); m_releaseKnob->setHintText( tr( "Release:" ), "" ); - m_amountKnob = new Knob( knobBright_26, this ); + m_amountKnob = new Knob( KnobType::Bright26, this ); m_amountKnob->setLabel( tr( "AMT" ) ); m_amountKnob->move( AMOUNT_KNOB_X, ENV_GRAPH_Y ); m_amountKnob->setHintText( tr( "Modulation amount:" ), "" ); @@ -143,25 +143,25 @@ EnvelopeAndLfoView::EnvelopeAndLfoView( QWidget * _parent ) : - m_lfoPredelayKnob = new Knob( knobBright_26, this ); + m_lfoPredelayKnob = new Knob( KnobType::Bright26, this ); m_lfoPredelayKnob->setLabel( tr( "DEL" ) ); m_lfoPredelayKnob->move( LFO_PREDELAY_KNOB_X, LFO_KNOB_Y ); m_lfoPredelayKnob->setHintText( tr( "Pre-delay:" ), "" ); - m_lfoAttackKnob = new Knob( knobBright_26, this ); + m_lfoAttackKnob = new Knob( KnobType::Bright26, this ); m_lfoAttackKnob->setLabel( tr( "ATT" ) ); m_lfoAttackKnob->move( LFO_ATTACK_KNOB_X, LFO_KNOB_Y ); m_lfoAttackKnob->setHintText( tr( "Attack:" ), "" ); - m_lfoSpeedKnob = new TempoSyncKnob( knobBright_26, this ); + m_lfoSpeedKnob = new TempoSyncKnob( KnobType::Bright26, this ); m_lfoSpeedKnob->setLabel( tr( "SPD" ) ); m_lfoSpeedKnob->move( LFO_SPEED_KNOB_X, LFO_KNOB_Y ); m_lfoSpeedKnob->setHintText( tr( "Frequency:" ), "" ); - m_lfoAmountKnob = new Knob( knobBright_26, this ); + m_lfoAmountKnob = new Knob( KnobType::Bright26, this ); m_lfoAmountKnob->setLabel( tr( "AMT" ) ); m_lfoAmountKnob->move( LFO_AMOUNT_KNOB_X, LFO_KNOB_Y ); m_lfoAmountKnob->setHintText( tr( "Modulation amount:" ), "" ); @@ -310,7 +310,7 @@ void EnvelopeAndLfoView::dragEnterEvent( QDragEnterEvent * _dee ) { StringPairDrag::processDragEnterEvent( _dee, QString( "samplefile,clip_%1" ).arg( - Track::SampleTrack ) ); + static_cast(Track::Type::Sample) ) ); } @@ -325,18 +325,18 @@ void EnvelopeAndLfoView::dropEvent( QDropEvent * _de ) m_params->m_userWave.setAudioFile( StringPairDrag::decodeValue( _de ) ); m_userLfoBtn->model()->setValue( true ); - m_params->m_lfoWaveModel.setValue(EnvelopeAndLfoParameters::UserDefinedWave); + m_params->m_lfoWaveModel.setValue(static_cast(EnvelopeAndLfoParameters::LfoShape::UserDefinedWave)); _de->accept(); update(); } - else if( type == QString( "clip_%1" ).arg( Track::SampleTrack ) ) + else if( type == QString( "clip_%1" ).arg( static_cast(Track::Type::Sample) ) ) { DataFile dataFile( value.toUtf8() ); m_params->m_userWave.setAudioFile( dataFile.content(). firstChildElement().firstChildElement(). firstChildElement().attribute( "src" ) ); m_userLfoBtn->model()->setValue( true ); - m_params->m_lfoWaveModel.setValue(EnvelopeAndLfoParameters::UserDefinedWave); + m_params->m_lfoWaveModel.setValue(static_cast(EnvelopeAndLfoParameters::LfoShape::UserDefinedWave)); _de->accept(); update(); } @@ -459,29 +459,30 @@ void EnvelopeAndLfoView::paintEvent( QPaintEvent * ) float phase = ( cur_sample -= m_params->m_lfoPredelayFrames ) / osc_frames; - switch( m_params->m_lfoWaveModel.value() ) + switch( static_cast(m_params->m_lfoWaveModel.value()) ) { - case EnvelopeAndLfoParameters::SineWave: + case EnvelopeAndLfoParameters::LfoShape::SineWave: + default: val = Oscillator::sinSample( phase ); break; - case EnvelopeAndLfoParameters::TriangleWave: + case EnvelopeAndLfoParameters::LfoShape::TriangleWave: val = Oscillator::triangleSample( phase ); break; - case EnvelopeAndLfoParameters::SawWave: + case EnvelopeAndLfoParameters::LfoShape::SawWave: val = Oscillator::sawSample( phase ); break; - case EnvelopeAndLfoParameters::SquareWave: + case EnvelopeAndLfoParameters::LfoShape::SquareWave: val = Oscillator::squareSample( phase ); break; - case EnvelopeAndLfoParameters::RandomWave: + case EnvelopeAndLfoParameters::LfoShape::RandomWave: if( x % (int)( 900 * m_lfoSpeedKnob->value() + 1 ) == 0 ) { m_randomGraph = Oscillator::noiseSample( 0.0f ); } val = m_randomGraph; break; - case EnvelopeAndLfoParameters::UserDefinedWave: + case EnvelopeAndLfoParameters::LfoShape::UserDefinedWave: val = m_params->m_userWave. userWaveSample( phase ); break; @@ -516,8 +517,8 @@ void EnvelopeAndLfoView::paintEvent( QPaintEvent * ) void EnvelopeAndLfoView::lfoUserWaveChanged() { - if( m_params->m_lfoWaveModel.value() == - EnvelopeAndLfoParameters::UserDefinedWave ) + if( static_cast(m_params->m_lfoWaveModel.value()) == + EnvelopeAndLfoParameters::LfoShape::UserDefinedWave ) { if( m_params->m_userWave.frames() <= 1 ) { diff --git a/src/gui/instrument/InstrumentFunctionViews.cpp b/src/gui/instrument/InstrumentFunctionViews.cpp index a4f0e670a..c9aa04272 100644 --- a/src/gui/instrument/InstrumentFunctionViews.cpp +++ b/src/gui/instrument/InstrumentFunctionViews.cpp @@ -44,7 +44,7 @@ InstrumentFunctionNoteStackingView::InstrumentFunctionNoteStackingView( Instrume m_cc( cc ), m_chordsGroupBox( new GroupBox( tr( "STACKING" ) ) ), m_chordsComboBox( new ComboBox() ), - m_chordRangeKnob( new Knob( knobBright_26 ) ) + m_chordRangeKnob( new Knob( KnobType::Bright26 ) ) { auto topLayout = new QHBoxLayout(this); topLayout->setContentsMargins(0, 0, 0, 0); @@ -98,13 +98,13 @@ InstrumentFunctionArpeggioView::InstrumentFunctionArpeggioView( InstrumentFuncti m_a( arp ), m_arpGroupBox( new GroupBox( tr( "ARPEGGIO" ) ) ), m_arpComboBox( new ComboBox() ), - m_arpRangeKnob( new Knob( knobBright_26 ) ), - m_arpRepeatsKnob( new Knob( knobBright_26 ) ), - m_arpCycleKnob( new Knob( knobBright_26 ) ), - m_arpSkipKnob( new Knob( knobBright_26 ) ), - m_arpMissKnob( new Knob( knobBright_26 ) ), - m_arpTimeKnob( new TempoSyncKnob( knobBright_26 ) ), - m_arpGateKnob( new Knob( knobBright_26 ) ), + m_arpRangeKnob( new Knob( KnobType::Bright26 ) ), + m_arpRepeatsKnob( new Knob( KnobType::Bright26 ) ), + m_arpCycleKnob( new Knob( KnobType::Bright26 ) ), + m_arpSkipKnob( new Knob( KnobType::Bright26 ) ), + m_arpMissKnob( new Knob( KnobType::Bright26 ) ), + m_arpTimeKnob( new TempoSyncKnob( KnobType::Bright26 ) ), + m_arpGateKnob( new Knob( KnobType::Bright26 ) ), m_arpDirectionComboBox( new ComboBox() ), m_arpModeComboBox( new ComboBox() ) { diff --git a/src/gui/instrument/InstrumentSoundShapingView.cpp b/src/gui/instrument/InstrumentSoundShapingView.cpp index dd5c14a09..1bfc166b3 100644 --- a/src/gui/instrument/InstrumentSoundShapingView.cpp +++ b/src/gui/instrument/InstrumentSoundShapingView.cpp @@ -80,13 +80,13 @@ InstrumentSoundShapingView::InstrumentSoundShapingView( QWidget * _parent ) : m_filterComboBox->setFont( pointSize<8>( m_filterComboBox->font() ) ); - m_filterCutKnob = new Knob( knobBright_26, m_filterGroupBox ); + m_filterCutKnob = new Knob( KnobType::Bright26, m_filterGroupBox ); m_filterCutKnob->setLabel( tr( "FREQ" ) ); m_filterCutKnob->move( 140, 18 ); m_filterCutKnob->setHintText( tr( "Cutoff frequency:" ), " " + tr( "Hz" ) ); - m_filterResKnob = new Knob( knobBright_26, m_filterGroupBox ); + m_filterResKnob = new Knob( KnobType::Bright26, m_filterGroupBox ); m_filterResKnob->setLabel( tr( "Q/RESO" ) ); m_filterResKnob->move( 196, 18 ); m_filterResKnob->setHintText( tr( "Q/Resonance:" ), "" ); diff --git a/src/gui/instrument/InstrumentTrackWindow.cpp b/src/gui/instrument/InstrumentTrackWindow.cpp index 1d26dd9dc..43cca0dac 100644 --- a/src/gui/instrument/InstrumentTrackWindow.cpp +++ b/src/gui/instrument/InstrumentTrackWindow.cpp @@ -148,7 +148,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : Qt::Alignment widgetAlignment = Qt::AlignHCenter | Qt::AlignCenter; // set up volume knob - m_volumeKnob = new Knob( knobBright_26, nullptr, tr( "Volume" ) ); + m_volumeKnob = new Knob( KnobType::Bright26, nullptr, tr( "Volume" ) ); m_volumeKnob->setVolumeKnob( true ); m_volumeKnob->setHintText( tr( "Volume:" ), "%" ); @@ -162,7 +162,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : // set up panning knob - m_panningKnob = new Knob( knobBright_26, nullptr, tr( "Panning" ) ); + m_panningKnob = new Knob( KnobType::Bright26, nullptr, tr( "Panning" ) ); m_panningKnob->setHintText( tr( "Panning:" ), "" ); basicControlsLayout->addWidget( m_panningKnob, 0, 1 ); @@ -178,7 +178,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : // set up pitch knob - m_pitchKnob = new Knob( knobBright_26, nullptr, tr( "Pitch" ) ); + m_pitchKnob = new Knob( KnobType::Bright26, nullptr, tr( "Pitch" ) ); m_pitchKnob->setHintText( tr( "Pitch:" ), " " + tr( "cents" ) ); basicControlsLayout->addWidget( m_pitchKnob, 0, 3 ); @@ -356,7 +356,7 @@ void InstrumentTrackWindow::modelChanged() m_mixerChannelNumber->setModel( &m_track->m_mixerChannelModel ); m_pianoView->setModel( &m_track->m_piano ); - if( m_track->instrument() && m_track->instrument()->flags().testFlag( Instrument::IsNotBendable ) == false ) + if( m_track->instrument() && m_track->instrument()->flags().testFlag( Instrument::Flag::IsNotBendable ) == false ) { m_pitchKnob->setModel( &m_track->m_pitchModel ); m_pitchRangeSpinBox->setModel( &m_track->m_pitchRangeModel ); @@ -374,7 +374,7 @@ void InstrumentTrackWindow::modelChanged() m_pitchRangeLabel->hide(); } - if (m_track->instrument() && m_track->instrument()->flags().testFlag(Instrument::IsMidiBased)) + if (m_track->instrument() && m_track->instrument()->flags().testFlag(Instrument::Flag::IsMidiBased)) { m_miscView->microtunerGroupBox()->hide(); m_track->m_microtuner.enabledModel()->setValue(false); @@ -425,7 +425,7 @@ void InstrumentTrackWindow::saveSettingsBtnClicked() !sfd.selectedFiles().isEmpty() && !sfd.selectedFiles().first().isEmpty() ) { - DataFile dataFile(DataFile::InstrumentTrackSettings); + DataFile dataFile(DataFile::Type::InstrumentTrackSettings); QDomElement& content(dataFile.content()); m_track->setSimpleSerializing(); @@ -466,7 +466,7 @@ void InstrumentTrackWindow::updateInstrumentView() m_tabWidget->addTab( m_instrumentView, tr( "Plugin" ), "plugin_tab", 0 ); m_tabWidget->setActiveTab( 0 ); - m_ssView->setFunctionsHidden( m_track->m_instrument->flags().testFlag( Instrument::IsSingleStreamed ) ); + m_ssView->setFunctionsHidden( m_track->m_instrument->flags().testFlag( Instrument::Flag::IsSingleStreamed ) ); modelChanged(); // Get the instrument window to refresh m_track->dataChanged(); // Get the text on the trackButton to change diff --git a/src/gui/instrument/PianoView.cpp b/src/gui/instrument/PianoView.cpp index a2df50e47..20db2e8e8 100644 --- a/src/gui/instrument/PianoView.cpp +++ b/src/gui/instrument/PianoView.cpp @@ -64,7 +64,7 @@ namespace lmms::gui */ auto WhiteKeys = std::array { - Key_C, Key_D, Key_E, Key_F, Key_G, Key_A, Key_H + Key::C, Key::D, Key::E, Key::F, Key::G, Key::A, Key::H } ; @@ -95,7 +95,7 @@ PianoView::PianoView(QWidget *parent) : QWidget(parent), /*!< Our parent */ ModelView(nullptr, this), /*!< Our view Model */ m_piano(nullptr), /*!< Our piano Model */ - m_startKey(Key_C + Octave_3*KeysPerOctave), /*!< The first key displayed? */ + m_startKey(Octave::Octave_3 + Key::C), /*!< The first key displayed? */ m_lastKey(-1), /*!< The last key displayed? */ m_movedNoteModel(nullptr) /*!< Key marker which is being moved */ { @@ -138,7 +138,7 @@ PianoView::PianoView(QWidget *parent) : m_pianoScroll = new QScrollBar( Qt::Horizontal, this ); m_pianoScroll->setSingleStep( 1 ); m_pianoScroll->setPageStep( 20 ); - m_pianoScroll->setValue(Octave_3 * Piano::WhiteKeysPerOctave); + m_pianoScroll->setValue(static_cast(Octave::Octave_3) * Piano::WhiteKeysPerOctave); // and connect it to this widget connect( m_pianoScroll, SIGNAL(valueChanged(int)), @@ -155,14 +155,10 @@ PianoView::PianoView(QWidget *parent) : connect(Engine::getSong(), SIGNAL(keymapListChanged(int)), this, SLOT(update())); } -/*! \brief Map a keyboard key being pressed to a note in our keyboard view - * - * \param _k The keyboard scan code of the key being pressed. - * \todo check the scan codes for ',' = c, 'L' = c#, '.' = d, ':' = d#, - * '/' = d, '[' = f', '=' = f'#, ']' = g' - Paul's additions - */ -int PianoView::getKeyFromKeyEvent( QKeyEvent * _ke ) +static int getKeyOffsetFromKeyEvent( QKeyEvent * _ke ) { + // TODO: check the scan codes for ',' = c, 'L' = c#, '.' = d, ':' = d#, + // '/' = d, '[' = f', '=' = f'#, ']' = g' - Paul's additions #ifdef LMMS_BUILD_APPLE const int k = _ke->nativeVirtualKey(); #else @@ -297,8 +293,14 @@ int PianoView::getKeyFromKeyEvent( QKeyEvent * _ke ) return -100; } - - +/*! \brief Map a keyboard key being pressed to a note in our keyboard view + * + */ +int PianoView::getKeyFromKeyEvent( QKeyEvent * ke ) +{ + const auto key = static_cast(getKeyOffsetFromKeyEvent(ke)); + return DefaultOctave + key - KeysPerOctave; +} /*! \brief Register a change to this piano display view * @@ -398,8 +400,8 @@ int PianoView::getKeyFromMouse( const QPoint & _p ) const */ void PianoView::pianoScrolled(int new_pos) { - m_startKey = WhiteKeys[new_pos % Piano::WhiteKeysPerOctave] + - (new_pos / Piano::WhiteKeysPerOctave) * KeysPerOctave; + m_startKey = static_cast(new_pos / Piano::WhiteKeysPerOctave) + + WhiteKeys[new_pos % Piano::WhiteKeysPerOctave]; update(); } @@ -625,8 +627,7 @@ void PianoView::mouseMoveEvent( QMouseEvent * _me ) */ void PianoView::keyPressEvent( QKeyEvent * _ke ) { - const int key_num = getKeyFromKeyEvent( _ke ) + - ( DefaultOctave - 1 ) * KeysPerOctave; + const int key_num = getKeyFromKeyEvent( _ke ); if( _ke->isAutoRepeat() == false && key_num > -1 ) { @@ -654,8 +655,7 @@ void PianoView::keyPressEvent( QKeyEvent * _ke ) */ void PianoView::keyReleaseEvent( QKeyEvent * _ke ) { - const int key_num = getKeyFromKeyEvent( _ke ) + - ( DefaultOctave - 1 ) * KeysPerOctave; + const int key_num = getKeyFromKeyEvent( _ke ); if( _ke->isAutoRepeat() == false && key_num > -1 ) { if( m_piano != nullptr ) @@ -913,7 +913,7 @@ void PianoView::paintEvent( QPaintEvent * ) x += PW_WHITE_KEY_WIDTH; - if ((Keys)(cur_key % KeysPerOctave) == Key_C) + if ((Key)(cur_key % KeysPerOctave) == Key::C) { // label key of note C with "C" and number of current octave p.drawText(x - PW_WHITE_KEY_WIDTH, LABEL_TEXT_SIZE + 2, @@ -927,7 +927,7 @@ void PianoView::paintEvent( QPaintEvent * ) int white_cnt = 0; int startKey = m_startKey; - if (startKey > 0 && Piano::isBlackKey(static_cast(--startKey))) + if (startKey > 0 && Piano::isBlackKey(--startKey)) { if (m_piano && m_piano->instrumentTrack()->isKeyMapped(startKey)) { diff --git a/src/gui/menus/MidiPortMenu.cpp b/src/gui/menus/MidiPortMenu.cpp index b1ddf71c9..296be3506 100644 --- a/src/gui/menus/MidiPortMenu.cpp +++ b/src/gui/menus/MidiPortMenu.cpp @@ -30,7 +30,7 @@ namespace lmms::gui { -MidiPortMenu::MidiPortMenu( MidiPort::Modes _mode ) : +MidiPortMenu::MidiPortMenu( MidiPort::Mode _mode ) : ModelView( nullptr, this ), m_mode( _mode ) { @@ -46,12 +46,12 @@ MidiPortMenu::MidiPortMenu( MidiPort::Modes _mode ) : void MidiPortMenu::modelChanged() { auto mp = castModel(); - if( m_mode == MidiPort::Input ) + if( m_mode == MidiPort::Mode::Input ) { connect( mp, SIGNAL(readablePortsChanged()), this, SLOT(updateMenu())); } - else if( m_mode == MidiPort::Output ) + else if( m_mode == MidiPort::Mode::Output ) { connect( mp, SIGNAL(writablePortsChanged()), this, SLOT(updateMenu())); @@ -64,12 +64,12 @@ void MidiPortMenu::modelChanged() void MidiPortMenu::activatedPort( QAction * _item ) { - if( m_mode == MidiPort::Input ) + if( m_mode == MidiPort::Mode::Input ) { castModel()->subscribeReadablePort( _item->text(), _item->isChecked() ); } - else if( m_mode == MidiPort::Output ) + else if( m_mode == MidiPort::Mode::Output ) { castModel()->subscribeWritablePort( _item->text(), _item->isChecked() ); @@ -82,7 +82,7 @@ void MidiPortMenu::activatedPort( QAction * _item ) void MidiPortMenu::updateMenu() { auto mp = castModel(); - const MidiPort::Map & map = ( m_mode == MidiPort::Input ) ? + const MidiPort::Map & map = ( m_mode == MidiPort::Mode::Input ) ? mp->readablePorts() : mp->writablePorts(); clear(); for( MidiPort::Map::ConstIterator it = map.begin(); diff --git a/src/gui/modals/ControllerConnectionDialog.cpp b/src/gui/modals/ControllerConnectionDialog.cpp index 040ed9e0f..79daa25b5 100644 --- a/src/gui/modals/ControllerConnectionDialog.cpp +++ b/src/gui/modals/ControllerConnectionDialog.cpp @@ -169,7 +169,7 @@ ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent, // our port-menus when being clicked if( !Engine::audioEngine()->midiClient()->isRaw() ) { - m_readablePorts = new MidiPortMenu( MidiPort::Input ); + m_readablePorts = new MidiPortMenu( MidiPort::Mode::Input ); connect( m_readablePorts, SIGNAL(triggered(QAction*)), this, SLOT(enableAutoDetect(QAction*))); auto rp_btn = new ToolButton(m_midiGroupBox); @@ -242,9 +242,9 @@ ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent, { cc = m_targetModel->controllerConnection(); - if( cc && cc->getController()->type() != Controller::DummyController && Engine::getSong() ) + if( cc && cc->getController()->type() != Controller::ControllerType::Dummy && Engine::getSong() ) { - if ( cc->getController()->type() == Controller::MidiController ) + if ( cc->getController()->type() == Controller::ControllerType::Midi ) { m_midiGroupBox->model()->setValue( true ); // ensure controller is created diff --git a/src/gui/modals/EffectSelectDialog.cpp b/src/gui/modals/EffectSelectDialog.cpp index eac5a3783..31ffd7728 100644 --- a/src/gui/modals/EffectSelectDialog.cpp +++ b/src/gui/modals/EffectSelectDialog.cpp @@ -53,7 +53,7 @@ EffectSelectDialog::EffectSelectDialog( QWidget * _parent ) : EffectKeyList subPluginEffectKeys; - for (const Plugin::Descriptor* desc: getPluginFactory()->descriptors(Plugin::Effect)) + for (const Plugin::Descriptor* desc: getPluginFactory()->descriptors(Plugin::Type::Effect)) { if( desc->subPluginFeatures ) { diff --git a/src/gui/modals/ExportProjectDialog.cpp b/src/gui/modals/ExportProjectDialog.cpp index 1f937c374..fe39082e4 100644 --- a/src/gui/modals/ExportProjectDialog.cpp +++ b/src/gui/modals/ExportProjectDialog.cpp @@ -66,7 +66,7 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name, // Add to combo box. fileFormatCB->addItem( ProjectRenderer::tr( ProjectRenderer::fileEncodeDevices[i].m_description ), - QVariant( ProjectRenderer::fileEncodeDevices[i].m_fileFormat ) // Format tag; later used for identification. + QVariant( static_cast(ProjectRenderer::fileEncodeDevices[i].m_fileFormat) ) // Format tag; later used for identification. ); // If this is our extension, select it. @@ -142,13 +142,13 @@ OutputSettings::StereoMode mapToStereoMode(int index) switch (index) { case 0: - return OutputSettings::StereoMode_Mono; + return OutputSettings::StereoMode::Mono; case 1: - return OutputSettings::StereoMode_Stereo; + return OutputSettings::StereoMode::Stereo; case 2: - return OutputSettings::StereoMode_JointStereo; + return OutputSettings::StereoMode::JointStereo; default: - return OutputSettings::StereoMode_Stereo; + return OutputSettings::StereoMode::Stereo; } } @@ -216,27 +216,27 @@ void ExportProjectDialog::onFileFormatChanged(int index) // and adjust the UI properly. QVariant format_tag = fileFormatCB->itemData(index); bool successful_conversion = false; - auto exportFormat = static_cast( + auto exportFormat = static_cast( format_tag.toInt(&successful_conversion) ); Q_ASSERT(successful_conversion); - bool stereoModeVisible = (exportFormat == ProjectRenderer::MP3File); + bool stereoModeVisible = (exportFormat == ProjectRenderer::ExportFileFormat::MP3); - bool sampleRateControlsVisible = (exportFormat != ProjectRenderer::MP3File); + bool sampleRateControlsVisible = (exportFormat != ProjectRenderer::ExportFileFormat::MP3); bool bitRateControlsEnabled = - (exportFormat == ProjectRenderer::OggFile || - exportFormat == ProjectRenderer::MP3File); + (exportFormat == ProjectRenderer::ExportFileFormat::Ogg || + exportFormat == ProjectRenderer::ExportFileFormat::MP3); bool bitDepthControlEnabled = - (exportFormat == ProjectRenderer::WaveFile || - exportFormat == ProjectRenderer::FlacFile); + (exportFormat == ProjectRenderer::ExportFileFormat::Wave || + exportFormat == ProjectRenderer::ExportFileFormat::Flac); - bool variableBitrateVisible = !(exportFormat == ProjectRenderer::MP3File || exportFormat == ProjectRenderer::FlacFile); + bool variableBitrateVisible = !(exportFormat == ProjectRenderer::ExportFileFormat::MP3 || exportFormat == ProjectRenderer::ExportFileFormat::Flac); #ifdef LMMS_HAVE_SF_COMPLEVEL - bool compressionLevelVisible = (exportFormat == ProjectRenderer::FlacFile); + bool compressionLevelVisible = (exportFormat == ProjectRenderer::ExportFileFormat::Flac); compressionWidget->setVisible(compressionLevelVisible); #endif @@ -251,12 +251,12 @@ void ExportProjectDialog::onFileFormatChanged(int index) void ExportProjectDialog::startBtnClicked() { - m_ft = ProjectRenderer::NumFileFormats; + m_ft = ProjectRenderer::ExportFileFormat::Count; // Get file format from current menu selection. bool successful_conversion = false; QVariant tag = fileFormatCB->itemData(fileFormatCB->currentIndex()); - m_ft = static_cast( + m_ft = static_cast( tag.toInt(&successful_conversion) ); diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 33505c399..63b84506e 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -91,7 +91,7 @@ inline void labelWidget(QWidget * w, const QString & txt) -SetupDialog::SetupDialog(ConfigTabs tab_to_open) : +SetupDialog::SetupDialog(ConfigTab tab_to_open) : m_displaydBFS(ConfigManager::inst()->value( "app", "displaydbfs").toInt()), m_tooltips(!ConfigManager::inst()->value( @@ -837,7 +837,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : tr("Paths"), 4, true, true)->setIcon( embed::getIconPixmap("setup_directories")); - m_tabBar->setActiveTab(tab_to_open); + m_tabBar->setActiveTab(static_cast(tab_to_open)); // Horizontal layout ordering. hlayout->addSpacing(2); diff --git a/src/gui/tracks/InstrumentTrackView.cpp b/src/gui/tracks/InstrumentTrackView.cpp index 17adc99a6..669fdaccb 100644 --- a/src/gui/tracks/InstrumentTrackView.cpp +++ b/src/gui/tracks/InstrumentTrackView.cpp @@ -87,7 +87,7 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV widgetWidth = DEFAULT_SETTINGS_WIDGET_WIDTH; } - m_volumeKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), + m_volumeKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), tr( "Volume" ) ); m_volumeKnob->setVolumeKnob( true ); m_volumeKnob->setModel( &_it->m_volumeModel ); @@ -96,7 +96,7 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV m_volumeKnob->setLabel( tr( "VOL" ) ); m_volumeKnob->show(); - m_panningKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), + m_panningKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), tr( "Panning" ) ); m_panningKnob->setModel( &_it->m_panningModel ); m_panningKnob->setHintText(tr("Panning:"), "%"); @@ -110,9 +110,9 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV if( !Engine::audioEngine()->midiClient()->isRaw() ) { _it->m_midiPort.m_readablePortsMenu = new MidiPortMenu( - MidiPort::Input ); + MidiPort::Mode::Input ); _it->m_midiPort.m_writablePortsMenu = new MidiPortMenu( - MidiPort::Output ); + MidiPort::Mode::Output ); _it->m_midiPort.m_readablePortsMenu->setModel( &_it->m_midiPort ); _it->m_midiPort.m_writablePortsMenu->setModel( diff --git a/src/gui/tracks/SampleTrackView.cpp b/src/gui/tracks/SampleTrackView.cpp index fbdd41ded..6a6a2c5fd 100644 --- a/src/gui/tracks/SampleTrackView.cpp +++ b/src/gui/tracks/SampleTrackView.cpp @@ -59,7 +59,7 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_tlb->move(3, 1); m_tlb->show(); - m_volumeKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), + m_volumeKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), tr( "Track volume" ) ); m_volumeKnob->setVolumeKnob( true ); m_volumeKnob->setModel( &_t->m_volumeModel ); @@ -73,7 +73,7 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_volumeKnob->setLabel( tr( "VOL" ) ); m_volumeKnob->show(); - m_panningKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), + m_panningKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), tr( "Panning" ) ); m_panningKnob->setModel( &_t->m_panningModel ); m_panningKnob->setHintText( tr( "Panning:" ), "%" ); diff --git a/src/gui/tracks/TrackContentWidget.cpp b/src/gui/tracks/TrackContentWidget.cpp index ca7123bda..442d717bf 100644 --- a/src/gui/tracks/TrackContentWidget.cpp +++ b/src/gui/tracks/TrackContentWidget.cpp @@ -291,7 +291,7 @@ void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) else { StringPairDrag::processDragEnterEvent( dee, "clip_" + - QString::number( getTrack()->type() ) ); + QString::number( static_cast(getTrack()->type()) ) ); } } @@ -325,7 +325,7 @@ bool TrackContentWidget::canPasteSelection( TimePos clipPos, const QMimeData* md QString value = decodeValue( md ); // We can only paste into tracks of the same type - if (type != ("clip_" + QString::number(t->type()))) + if (type != ("clip_" + QString::number(static_cast(t->type())))) { return false; } @@ -382,7 +382,7 @@ bool TrackContentWidget::canPasteSelection( TimePos clipPos, const QMimeData* md } // Track must be of the same type - auto startTrackType = clipElement.attributeNode("trackType").value().toInt(); + auto startTrackType = static_cast(clipElement.attributeNode("trackType").value().toInt()); Track * endTrack = tracks.at( finalTrackIndex ); if( startTrackType != endTrack->type() ) { @@ -538,7 +538,7 @@ void TrackContentWidget::mousePressEvent( QMouseEvent * me ) // Enable box select if control is held when clicking an empty space // (If we had clicked a Clip it would have intercepted the mouse event) if( me->modifiers() & Qt::ControlModifier ){ - getGUI()->songEditor()->m_editor->setEditMode(SongEditor::EditMode::SelectMode); + getGUI()->songEditor()->m_editor->setEditMode(SongEditor::EditMode::Select); } // Forward event to allow box select if the editor supports it and is in that mode if( m_trackView->trackContainerView()->allowRubberband() == true ) @@ -655,7 +655,7 @@ void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme ) QMenu contextMenu( this ); QAction *pasteA = contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), - tr( "Paste" ), [this, cme](){ contextMenuAction( cme, Paste ); } ); + tr( "Paste" ), [this, cme](){ contextMenuAction( cme, ContextMenuAction::Paste ); } ); // If we can't paste in the current TCW for some reason, disable the action so the user knows pasteA->setEnabled( canPasteSelection( getPosition( cme->x() ), getMimeData() ) ? true : false ); @@ -669,7 +669,7 @@ void TrackContentWidget::contextMenuAction( QContextMenuEvent * cme, ContextMenu switch( action ) { - case Paste: + case ContextMenuAction::Paste: // Paste the selection on the TimePos of the context menu event TimePos clipPos = getPosition( cme->x() ); diff --git a/src/gui/tracks/TrackLabelButton.cpp b/src/gui/tracks/TrackLabelButton.cpp index 3f1b45871..2a50a4aa2 100644 --- a/src/gui/tracks/TrackLabelButton.cpp +++ b/src/gui/tracks/TrackLabelButton.cpp @@ -187,7 +187,7 @@ void TrackLabelButton::mouseReleaseEvent( QMouseEvent *_me ) void TrackLabelButton::paintEvent( QPaintEvent * _pe ) { - if( m_trackView->getTrack()->type() == Track::InstrumentTrack ) + if( m_trackView->getTrack()->type() == Track::Type::Instrument ) { auto it = dynamic_cast(m_trackView->getTrack()); const PixmapLoader * pl; diff --git a/src/gui/tracks/TrackOperationsWidget.cpp b/src/gui/tracks/TrackOperationsWidget.cpp index ddbd2eacd..ce6177d76 100644 --- a/src/gui/tracks/TrackOperationsWidget.cpp +++ b/src/gui/tracks/TrackOperationsWidget.cpp @@ -137,12 +137,12 @@ void TrackOperationsWidget::mousePressEvent( QMouseEvent * me ) { if( me->button() == Qt::LeftButton && me->modifiers() & Qt::ControlModifier && - m_trackView->getTrack()->type() != Track::PatternTrack) + m_trackView->getTrack()->type() != Track::Type::Pattern) { - DataFile dataFile( DataFile::DragNDropData ); + DataFile dataFile( DataFile::Type::DragNDropData ); m_trackView->getTrack()->saveState( dataFile, dataFile.content() ); new StringPairDrag( QString( "track_%1" ).arg( - m_trackView->getTrack()->type() ), + static_cast(m_trackView->getTrack()->type()) ), dataFile.toString(), m_trackView->getTrackSettingsWidget()->grab(), this ); } diff --git a/src/gui/tracks/TrackView.cpp b/src/gui/tracks/TrackView.cpp index 3e9257a0d..426be7e36 100644 --- a/src/gui/tracks/TrackView.cpp +++ b/src/gui/tracks/TrackView.cpp @@ -65,7 +65,7 @@ TrackView::TrackView( Track * track, TrackContainerView * tcv ) : m_trackOperationsWidget( this ), /*!< Our trackOperationsWidget */ m_trackSettingsWidget( this ), /*!< Our trackSettingsWidget */ m_trackContentWidget( this ), /*!< Our trackContentWidget */ - m_action( NoAction ) /*!< The action we're currently performing */ + m_action( Action::None ) /*!< The action we're currently performing */ { setAutoFillBackground( true ); QPalette pal; @@ -207,7 +207,7 @@ void TrackView::modelChanged() void TrackView::dragEnterEvent( QDragEnterEvent * dee ) { StringPairDrag::processDragEnterEvent( dee, "track_" + - QString::number( m_track->type() ) ); + QString::number( static_cast(m_track->type()) ) ); } @@ -225,7 +225,7 @@ void TrackView::dropEvent( QDropEvent * de ) { QString type = StringPairDrag::decodeKey( de ); QString value = StringPairDrag::decodeValue( de ); - if( type == ( "track_" + QString::number( m_track->type() ) ) ) + if( type == ( "track_" + QString::number( static_cast(m_track->type()) ) ) ) { // value contains our XML-data so simply create a // DataFile which does the rest for us... @@ -278,7 +278,7 @@ void TrackView::mousePressEvent( QMouseEvent * me ) { if( me->modifiers() & Qt::ShiftModifier ) { - m_action = ResizeTrack; + m_action = Action::Resize; QCursor::setPos( mapToGlobal( QPoint( me->x(), height() ) ) ); QCursor c( Qt::SizeVerCursor); @@ -292,7 +292,7 @@ void TrackView::mousePressEvent( QMouseEvent * me ) return; } - m_action = MoveTrack; + m_action = Action::Move; QCursor c( Qt::SizeVerCursor ); QApplication::setOverrideCursor( c ); @@ -338,7 +338,7 @@ void TrackView::mouseMoveEvent( QMouseEvent * me ) { QWidget::mouseMoveEvent( me ); } - else if( m_action == MoveTrack ) + else if( m_action == Action::Move ) { // look which track-widget the mouse-cursor is over const int yPos = @@ -362,7 +362,7 @@ void TrackView::mouseMoveEvent( QMouseEvent * me ) } } } - else if( m_action == ResizeTrack ) + else if( m_action == Action::Resize ) { setFixedHeight( qMax( me->y(), MINIMAL_TRACK_HEIGHT ) ); m_trackContainerView->realignTracks(); @@ -383,7 +383,7 @@ void TrackView::mouseMoveEvent( QMouseEvent * me ) */ void TrackView::mouseReleaseEvent( QMouseEvent * me ) { - m_action = NoAction; + m_action = Action::None; while( QApplication::overrideCursor() != nullptr ) { QApplication::restoreOverrideCursor(); diff --git a/src/gui/widgets/CustomTextKnob.cpp b/src/gui/widgets/CustomTextKnob.cpp index ce880608c..a4edde47c 100644 --- a/src/gui/widgets/CustomTextKnob.cpp +++ b/src/gui/widgets/CustomTextKnob.cpp @@ -28,7 +28,7 @@ namespace lmms::gui { -CustomTextKnob::CustomTextKnob( knobTypes _knob_num, QWidget * _parent, const QString & _name, const QString & _value_text ) : +CustomTextKnob::CustomTextKnob( KnobType _knob_num, QWidget * _parent, const QString & _name, const QString & _value_text ) : Knob( _knob_num, _parent, _name ), m_value_text( _value_text ) {} diff --git a/src/gui/widgets/Graph.cpp b/src/gui/widgets/Graph.cpp index 53a0a130d..9972209a8 100644 --- a/src/gui/widgets/Graph.cpp +++ b/src/gui/widgets/Graph.cpp @@ -36,7 +36,7 @@ namespace lmms namespace gui { -Graph::Graph( QWidget * _parent, graphStyle _style, int _width, +Graph::Graph( QWidget * _parent, Style _style, int _width, int _height ) : QWidget( _parent ), /* TODO: size, background? */ @@ -305,7 +305,7 @@ void Graph::paintEvent( QPaintEvent * ) switch( m_graphStyle ) { - case Graph::LinearStyle: + case Style::Linear: p.setRenderHints( QPainter::Antialiasing, true ); for( int i=0; i < length; i++ ) @@ -329,7 +329,7 @@ void Graph::paintEvent( QPaintEvent * ) break; - case Graph::NearestStyle: + case Style::Nearest: for( int i=0; i < length; i++ ) { p.drawLine(2+static_cast(i*xscale), @@ -350,7 +350,7 @@ void Graph::paintEvent( QPaintEvent * ) 2+static_cast( ( (*samps)[length] - maxVal ) * yscale ) ); break; - case Graph::LinearNonCyclicStyle: + case Style::LinearNonCyclic: p.setRenderHints( QPainter::Antialiasing, true ); for( int i=0; i < length; i++ ) @@ -369,7 +369,7 @@ void Graph::paintEvent( QPaintEvent * ) p.setRenderHints( QPainter::Antialiasing, false ); break; - case Graph::BarStyle: + case Style::Bar: for( int i=0; i <= length; i++ ) { p.fillRect( 2+static_cast( i*xscale ), diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 8640bb81d..d67c6dbdd 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -57,7 +57,7 @@ SimpleTextFloat * Knob::s_textFloat = nullptr; -Knob::Knob( knobTypes _knob_num, QWidget * _parent, const QString & _name ) : +Knob::Knob( KnobType _knob_num, QWidget * _parent, const QString & _name ) : QWidget( _parent ), FloatModelView( new FloatModel( 0, 0, 0, 1, nullptr, _name, true ), this ), m_label( "" ), @@ -75,7 +75,7 @@ Knob::Knob( knobTypes _knob_num, QWidget * _parent, const QString & _name ) : } Knob::Knob( QWidget * _parent, const QString & _name ) : - Knob( knobBright_26, _parent, _name ) + Knob( KnobType::Bright26, _parent, _name ) { } @@ -106,15 +106,15 @@ void Knob::initUi( const QString & _name ) // overrides that color. switch (knobNum()) { - case knobSmall_17: - case knobBright_26: - case knobDark_28: + case KnobType::Small17: + case KnobType::Bright26: + case KnobType::Dark28: m_lineActiveColor = QApplication::palette().color(QPalette::Active, QPalette::WindowText); m_arcActiveColor = QColor(QApplication::palette().color( QPalette::Active, QPalette::WindowText)); m_arcActiveColor.setAlpha(70); break; - case knobVintage_32: + case KnobType::Vintage32: m_lineActiveColor = QApplication::palette().color(QPalette::Active, QPalette::Shadow); m_arcActiveColor = QColor(QApplication::palette().color( QPalette::Active, QPalette::Shadow)); @@ -132,24 +132,24 @@ void Knob::initUi( const QString & _name ) void Knob::onKnobNumUpdated() { - if( m_knobNum != knobStyled ) + if( m_knobNum != KnobType::Styled ) { QString knobFilename; switch (m_knobNum) { - case knobDark_28: + case KnobType::Dark28: knobFilename = "knob01"; break; - case knobBright_26: + case KnobType::Bright26: knobFilename = "knob02"; break; - case knobSmall_17: + case KnobType::Small17: knobFilename = "knob03"; break; - case knobVintage_32: + case KnobType::Vintage32: knobFilename = "knob05"; break; - case knobStyled: // only here to stop the compiler from complaining + case KnobType::Styled: // only here to stop the compiler from complaining break; } @@ -251,7 +251,7 @@ void Knob::setOuterRadius( float r ) -knobTypes Knob::knobNum() const +KnobType Knob::knobNum() const { return m_knobNum; } @@ -259,7 +259,7 @@ knobTypes Knob::knobNum() const -void Knob::setknobNum( knobTypes k ) +void Knob::setknobNum( KnobType k ) { if( m_knobNum != k ) { @@ -397,7 +397,7 @@ void Knob::drawKnob( QPainter * _p ) QPoint mid; - if( m_knobNum == knobStyled ) + if( m_knobNum == KnobType::Styled ) { p.setRenderHint( QPainter::Antialiasing ); @@ -448,17 +448,17 @@ void Knob::drawKnob( QPainter * _p ) p.setPen(QPen(currentLineColor, 2)); switch( m_knobNum ) { - case knobSmall_17: + case KnobType::Small17: { p.drawLine( calculateLine( mid, radius-2 ) ); break; } - case knobBright_26: + case KnobType::Bright26: { p.drawLine( calculateLine( mid, radius-5 ) ); break; } - case knobDark_28: + case KnobType::Dark28: { const float rb = qMax( ( radius - 10 ) / 3.0, 0.0 ); @@ -468,12 +468,12 @@ void Knob::drawKnob( QPainter * _p ) p.drawLine( ln ); break; } - case knobVintage_32: + case KnobType::Vintage32: { p.drawLine( calculateLine( mid, radius-2, 2 ) ); break; } - case knobStyled: + case KnobType::Styled: break; } diff --git a/src/gui/widgets/LedCheckBox.cpp b/src/gui/widgets/LedCheckBox.cpp index 1be072815..0c16bf391 100644 --- a/src/gui/widgets/LedCheckBox.cpp +++ b/src/gui/widgets/LedCheckBox.cpp @@ -44,7 +44,7 @@ static const auto names = std::array LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent, - const QString & _name, LedColors _color ) : + const QString & _name, LedColor _color ) : AutomatableButton( _parent, _name ), m_text( _text ) { @@ -55,7 +55,7 @@ LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent, LedCheckBox::LedCheckBox( QWidget * _parent, - const QString & _name, LedColors _color ) : + const QString & _name, LedColor _color ) : LedCheckBox( QString(), _parent, _name, _color ) { } @@ -103,16 +103,12 @@ void LedCheckBox::paintEvent( QPaintEvent * ) -void LedCheckBox::initUi( LedColors _color ) +void LedCheckBox::initUi( LedColor _color ) { setCheckable( true ); - if( _color >= NumColors || _color < Yellow ) - { - _color = Yellow; - } m_ledOnPixmap = new QPixmap( embed::getIconPixmap( - names[_color].toUtf8().constData() ) ); + names[static_cast(_color)].toUtf8().constData() ) ); m_ledOffPixmap = new QPixmap( embed::getIconPixmap( "led_off" ) ); setFont( pointSize<7>( font() ) ); diff --git a/src/gui/widgets/TempoSyncKnob.cpp b/src/gui/widgets/TempoSyncKnob.cpp index 86ee6df3c..b58e61cf4 100644 --- a/src/gui/widgets/TempoSyncKnob.cpp +++ b/src/gui/widgets/TempoSyncKnob.cpp @@ -42,7 +42,7 @@ namespace lmms::gui -TempoSyncKnob::TempoSyncKnob( knobTypes _knob_num, QWidget * _parent, +TempoSyncKnob::TempoSyncKnob( KnobType _knob_num, QWidget * _parent, const QString & _name ) : Knob( _knob_num, _parent, _name ), m_tempoSyncIcon( embed::getIconPixmap( "tempo_sync" ) ), @@ -104,51 +104,51 @@ void TempoSyncKnob::contextMenuEvent( QContextMenuEvent * ) connect( syncMenu, SIGNAL(triggered(QAction*)), model(), SLOT(setTempoSync(QAction*))); syncMenu->addAction( embed::getIconPixmap( "note_none" ), - tr( "No Sync" ) )->setData( (int) TempoSyncKnobModel::SyncNone ); + tr( "No Sync" ) )->setData( (int) TempoSyncKnobModel::SyncMode::None ); if( limit / 0.125f <= model()->maxValue() ) { syncMenu->addAction( embed::getIconPixmap( "note_double_whole" ), tr( "Eight beats" ) )->setData( - (int) TempoSyncKnobModel::SyncDoubleWholeNote ); + (int) TempoSyncKnobModel::SyncMode::DoubleWholeNote ); } if( limit / 0.25f <= model()->maxValue() ) { syncMenu->addAction( embed::getIconPixmap( "note_whole" ), tr( "Whole note" ) )->setData( - (int) TempoSyncKnobModel::SyncWholeNote ); + (int) TempoSyncKnobModel::SyncMode::WholeNote ); } if( limit / 0.5f <= model()->maxValue() ) { syncMenu->addAction( embed::getIconPixmap( "note_half" ), tr( "Half note" ) )->setData( - (int) TempoSyncKnobModel::SyncHalfNote ); + (int) TempoSyncKnobModel::SyncMode::HalfNote ); } if( limit <= model()->maxValue() ) { syncMenu->addAction( embed::getIconPixmap( "note_quarter" ), tr( "Quarter note" ) )->setData( - (int) TempoSyncKnobModel::SyncQuarterNote ); + (int) TempoSyncKnobModel::SyncMode::QuarterNote ); } if( limit / 2.0f <= model()->maxValue() ) { syncMenu->addAction( embed::getIconPixmap( "note_eighth" ), tr( "8th note" ) )->setData( - (int) TempoSyncKnobModel::SyncEighthNote ); + (int) TempoSyncKnobModel::SyncMode::EighthNote ); } if( limit / 4.0f <= model()->maxValue() ) { syncMenu->addAction( embed::getIconPixmap( "note_sixteenth" ), tr( "16th note" ) )->setData( - (int) TempoSyncKnobModel::SyncSixteenthNote ); + (int) TempoSyncKnobModel::SyncMode::SixteenthNote ); } syncMenu->addAction( embed::getIconPixmap( "note_thirtysecond" ), tr( "32nd note" ) )->setData( - (int) TempoSyncKnobModel::SyncThirtysecondNote ); + (int) TempoSyncKnobModel::SyncMode::ThirtysecondNote ); syncMenu->addAction( embed::getIconPixmap( "dont_know" ), tr( "Custom..." ), this, SLOT(showCustom()) )->setData( - (int) TempoSyncKnobModel::SyncCustom ); + (int) TempoSyncKnobModel::SyncMode::Custom ); contextMenu.addSeparator(); } @@ -162,11 +162,11 @@ void TempoSyncKnob::contextMenuEvent( QContextMenuEvent * ) void TempoSyncKnob::updateDescAndIcon() { - if( model()->m_tempoSyncMode ) + if( model()->m_tempoSyncMode != TempoSyncKnobModel::SyncMode::None ) { switch( model()->m_tempoSyncMode ) { - case TempoSyncKnobModel::SyncCustom: + case TempoSyncKnobModel::SyncMode::Custom: m_tempoSyncDescription = tr( "Custom " ) + "(" + QString::number( model()->m_custom.numeratorModel().value() ) + @@ -174,31 +174,31 @@ void TempoSyncKnob::updateDescAndIcon() QString::number( model()->m_custom.denominatorModel().value() ) + ")"; break; - case TempoSyncKnobModel::SyncDoubleWholeNote: + case TempoSyncKnobModel::SyncMode::DoubleWholeNote: m_tempoSyncDescription = tr( "Synced to Eight Beats" ); break; - case TempoSyncKnobModel::SyncWholeNote: + case TempoSyncKnobModel::SyncMode::WholeNote: m_tempoSyncDescription = tr( "Synced to Whole Note" ); break; - case TempoSyncKnobModel::SyncHalfNote: + case TempoSyncKnobModel::SyncMode::HalfNote: m_tempoSyncDescription = tr( "Synced to Half Note" ); break; - case TempoSyncKnobModel::SyncQuarterNote: + case TempoSyncKnobModel::SyncMode::QuarterNote: m_tempoSyncDescription = tr( "Synced to Quarter Note" ); break; - case TempoSyncKnobModel::SyncEighthNote: + case TempoSyncKnobModel::SyncMode::EighthNote: m_tempoSyncDescription = tr( "Synced to 8th Note" ); break; - case TempoSyncKnobModel::SyncSixteenthNote: + case TempoSyncKnobModel::SyncMode::SixteenthNote: m_tempoSyncDescription = tr( "Synced to 16th Note" ); break; - case TempoSyncKnobModel::SyncThirtysecondNote: + case TempoSyncKnobModel::SyncMode::ThirtysecondNote: m_tempoSyncDescription = tr( "Synced to 32nd Note" ); break; @@ -210,38 +210,38 @@ void TempoSyncKnob::updateDescAndIcon() m_tempoSyncDescription = tr( "Tempo Sync" ); } if( m_custom != nullptr && - model()->m_tempoSyncMode != TempoSyncKnobModel::SyncCustom ) + model()->m_tempoSyncMode != TempoSyncKnobModel::SyncMode::Custom ) { m_custom->parentWidget()->hide(); } switch( model()->m_tempoSyncMode ) { - case TempoSyncKnobModel::SyncNone: + case TempoSyncKnobModel::SyncMode::None: m_tempoSyncIcon = embed::getIconPixmap( "tempo_sync" ); break; - case TempoSyncKnobModel::SyncCustom: + case TempoSyncKnobModel::SyncMode::Custom: m_tempoSyncIcon = embed::getIconPixmap( "dont_know" ); break; - case TempoSyncKnobModel::SyncDoubleWholeNote: + case TempoSyncKnobModel::SyncMode::DoubleWholeNote: m_tempoSyncIcon = embed::getIconPixmap( "note_double_whole" ); break; - case TempoSyncKnobModel::SyncWholeNote: + case TempoSyncKnobModel::SyncMode::WholeNote: m_tempoSyncIcon = embed::getIconPixmap( "note_whole" ); break; - case TempoSyncKnobModel::SyncHalfNote: + case TempoSyncKnobModel::SyncMode::HalfNote: m_tempoSyncIcon = embed::getIconPixmap( "note_half" ); break; - case TempoSyncKnobModel::SyncQuarterNote: + case TempoSyncKnobModel::SyncMode::QuarterNote: m_tempoSyncIcon = embed::getIconPixmap( "note_quarter" ); break; - case TempoSyncKnobModel::SyncEighthNote: + case TempoSyncKnobModel::SyncMode::EighthNote: m_tempoSyncIcon = embed::getIconPixmap( "note_eighth" ); break; - case TempoSyncKnobModel::SyncSixteenthNote: + case TempoSyncKnobModel::SyncMode::SixteenthNote: m_tempoSyncIcon = embed::getIconPixmap( "note_sixteenth" ); break; - case TempoSyncKnobModel::SyncThirtysecondNote: + case TempoSyncKnobModel::SyncMode::ThirtysecondNote: m_tempoSyncIcon = embed::getIconPixmap( "note_thirtysecond" ); break; default: @@ -305,7 +305,7 @@ void TempoSyncKnob::showCustom() m_custom->setModel( &model()->m_custom ); } m_custom->parentWidget()->show(); - model()->setTempoSync( TempoSyncKnobModel::SyncCustom ); + model()->setTempoSync( TempoSyncKnobModel::SyncMode::Custom ); } diff --git a/src/gui/widgets/TimeDisplayWidget.cpp b/src/gui/widgets/TimeDisplayWidget.cpp index cf139d4a8..3dad6b1b0 100644 --- a/src/gui/widgets/TimeDisplayWidget.cpp +++ b/src/gui/widgets/TimeDisplayWidget.cpp @@ -36,7 +36,7 @@ namespace lmms::gui TimeDisplayWidget::TimeDisplayWidget() : QWidget(), - m_displayMode( MinutesSeconds ), + m_displayMode( DisplayMode::MinutesSeconds ), m_spinBoxesLayout( this ), m_majorLCD( 4, this ), m_minorLCD( 2, this ), @@ -65,13 +65,13 @@ void TimeDisplayWidget::setDisplayMode( DisplayMode displayMode ) switch( m_displayMode ) { - case MinutesSeconds: + case DisplayMode::MinutesSeconds: m_majorLCD.setLabel( tr( "MIN" ) ); m_minorLCD.setLabel( tr( "SEC" ) ); m_milliSecondsLCD.setLabel( tr( "MSEC" ) ); break; - case BarsTicks: + case DisplayMode::BarsTicks: m_majorLCD.setLabel( tr( "BAR" ) ); m_minorLCD.setLabel( tr( "BEAT" ) ); m_milliSecondsLCD.setLabel( tr( "TICK" ) ); @@ -90,7 +90,7 @@ void TimeDisplayWidget::updateTime() switch( m_displayMode ) { - case MinutesSeconds: + case DisplayMode::MinutesSeconds: int msec; msec = s->getMilliseconds(); m_majorLCD.setValue(msec / 60000); @@ -98,7 +98,7 @@ void TimeDisplayWidget::updateTime() m_milliSecondsLCD.setValue(msec % 1000); break; - case BarsTicks: + case DisplayMode::BarsTicks: int tick; tick = s->getPlayPos().getTicks(); m_majorLCD.setValue((int)(tick / s->ticksPerBar()) + 1); @@ -119,13 +119,13 @@ void TimeDisplayWidget::mousePressEvent( QMouseEvent* mouseEvent ) { if( mouseEvent->button() == Qt::LeftButton ) { - if( m_displayMode == MinutesSeconds ) + if( m_displayMode == DisplayMode::MinutesSeconds ) { - setDisplayMode( BarsTicks ); + setDisplayMode( DisplayMode::BarsTicks ); } else { - setDisplayMode( MinutesSeconds ); + setDisplayMode( DisplayMode::MinutesSeconds ); } } } diff --git a/src/tracks/AutomationTrack.cpp b/src/tracks/AutomationTrack.cpp index deb6f763c..e353197f8 100644 --- a/src/tracks/AutomationTrack.cpp +++ b/src/tracks/AutomationTrack.cpp @@ -34,7 +34,7 @@ namespace lmms AutomationTrack::AutomationTrack( TrackContainer* tc, bool _hidden ) : - Track( _hidden ? HiddenAutomationTrack : Track::AutomationTrack, tc ) + Track( _hidden ? Type::HiddenAutomation : Type::Automation, tc ) { setName( tr( "Automation track" ) ); } @@ -77,7 +77,7 @@ void AutomationTrack::saveTrackSpecificSettings( QDomDocument & _doc, void AutomationTrack::loadTrackSpecificSettings( const QDomElement & _this ) { // just in case something somehow wrent wrong... - if( type() == HiddenAutomationTrack ) + if( type() == Type::HiddenAutomation ) { setMuted( false ); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index a688aed9a..84b73614b 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -45,7 +45,7 @@ namespace lmms InstrumentTrack::InstrumentTrack( TrackContainer* tc ) : - Track( Track::InstrumentTrack, tc ), + Track( Track::Type::Instrument, tc ), MidiEventProcessor(), m_midiPort( tr( "unnamed_track" ), Engine::audioEngine()->midiClient(), this, this ), @@ -220,7 +220,7 @@ InstrumentTrack::~InstrumentTrack() void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, NotePlayHandle* n ) { // we must not play the sound if this InstrumentTrack is muted... - if( isMuted() || ( Engine::getSong()->playMode() != Song::Mode_PlayMidiClip && + if( isMuted() || ( Engine::getSong()->playMode() != Song::PlayMode::MidiClip && n && n->isPatternTrackMuted() ) || ! m_instrument ) { return; @@ -230,7 +230,7 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, // We could do that in all other cases as well but the overhead for silence test is bigger than // what we potentially save. While playing a note, a NotePlayHandle-driven instrument will produce sound in // 99 of 100 cases so that test would be a waste of time. - if( m_instrument->flags().testFlag( Instrument::IsSingleStreamed ) && + if( m_instrument->flags().testFlag( Instrument::Flag::IsSingleStreamed ) && MixHelpers::isSilent( buf, frames ) ) { // at least pass one silent buffer to allow @@ -260,7 +260,7 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, // instruments using instrument-play-handles will call this method // without any knowledge about notes, so they pass NULL for n, which // is no problem for us since we just bypass the envelopes+LFOs - if( m_instrument->flags().testFlag( Instrument::IsSingleStreamed ) == false && n != nullptr ) + if( m_instrument->flags().testFlag( Instrument::Flag::IsSingleStreamed ) == false && n != nullptr ) { const f_cnt_t offset = n->noteOffset(); m_soundShaping.processAudioBuffer( buf + offset, frames - offset, n ); @@ -343,7 +343,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim typeInfo::max() / 2, Note( TimePos(), TimePos(), event.key(), event.volume( midiPort()->baseVelocity() ) ), nullptr, event.channel(), - NotePlayHandle::OriginMidiInput ); + NotePlayHandle::Origin::MidiInput ); m_notes[event.key()] = nph; if( ! Engine::audioEngine()->addPlayHandle( nph ) ) { @@ -363,7 +363,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim m_notes[event.key()]->noteOff( offset ); if (isSustainPedalPressed() && m_notes[event.key()]->origin() == - m_notes[event.key()]->OriginMidiInput) + NotePlayHandle::Origin::MidiInput) { m_sustainedNotes << m_notes[event.key()]; } @@ -403,7 +403,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim if (nph && nph->isReleased()) { if( nph->origin() == - nph->OriginMidiInput) + NotePlayHandle::Origin::MidiInput) { nph->setLength( TimePos( static_cast( @@ -536,10 +536,10 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH ) // invalidate all NotePlayHandles and PresetPreviewHandles linked to this track m_processHandles.clear(); - quint8 flags = PlayHandle::TypeNotePlayHandle | PlayHandle::TypePresetPreviewHandle; + auto flags = PlayHandle::Type::NotePlayHandle | PlayHandle::Type::PresetPreviewHandle; if( removeIPH ) { - flags |= PlayHandle::TypeInstrumentPlayHandle; + flags |= PlayHandle::Type::InstrumentPlayHandle; } Engine::audioEngine()->removePlayHandlesOfTypes( this, flags ); Engine::audioEngine()->doneChangeInModel(); @@ -726,7 +726,7 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames, auto c = dynamic_cast(clip); // everything which is not a MIDI clip won't be played // A MIDI clip playing in the Piano Roll window will always play - if (c == nullptr || (Engine::getSong()->playMode() != Song::Mode_PlayMidiClip && clip->isMuted())) + if (c == nullptr || (Engine::getSong()->playMode() != Song::PlayMode::MidiClip && clip->isMuted())) { continue; } diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index 4fa2af5d8..b35979f61 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -48,7 +48,7 @@ QPixmap * gui::MidiClipView::s_stepBtnOffLight = nullptr; MidiClip::MidiClip( InstrumentTrack * _instrument_track ) : Clip( _instrument_track ), m_instrumentTrack( _instrument_track ), - m_clipType( BeatClip ), + m_clipType( Type::BeatClip ), m_steps( TimePos::stepsPerBar() ) { if (_instrument_track->trackContainer() == Engine::patternStore()) @@ -76,11 +76,11 @@ MidiClip::MidiClip( const MidiClip& other ) : init(); switch( getTrack()->trackContainer()->type() ) { - case TrackContainer::PatternContainer: + case TrackContainer::Type::Pattern: setAutoResize( true ); break; - case TrackContainer::SongContainer: + case TrackContainer::Type::Song: // move down default: setAutoResize( false ); @@ -111,7 +111,7 @@ void MidiClip::resizeToFirstTrack() m_instrumentTrack->trackContainer()->tracks(); for (const auto& track : tracks) { - if (track->type() == Track::InstrumentTrack) + if (track->type() == Track::Type::Instrument) { if (track != m_instrumentTrack) { @@ -144,7 +144,7 @@ void MidiClip::init() void MidiClip::updateLength() { - if( m_clipType == BeatClip ) + if( m_clipType == Type::BeatClip ) { changeLength( beatClipLength() ); updatePatternTrack(); @@ -337,10 +337,10 @@ void MidiClip::splitNotes(NoteVector notes, TimePos pos) -void MidiClip::setType( MidiClipTypes _new_clip_type ) +void MidiClip::setType( Type _new_clip_type ) { - if( _new_clip_type == BeatClip || - _new_clip_type == MelodyClip ) + if( _new_clip_type == Type::BeatClip || + _new_clip_type == Type::MelodyClip ) { m_clipType = _new_clip_type; } @@ -355,11 +355,11 @@ void MidiClip::checkType() { if (note->length() > 0) { - setType(MelodyClip); + setType(Type::MelodyClip); return; } } - setType( BeatClip ); + setType( Type::BeatClip ); } @@ -367,7 +367,7 @@ void MidiClip::checkType() void MidiClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) { - _this.setAttribute( "type", m_clipType ); + _this.setAttribute( "type", static_cast(m_clipType) ); _this.setAttribute( "name", name() ); if( usesCustomClipColor() ) @@ -401,7 +401,7 @@ void MidiClip::saveSettings( QDomDocument & _doc, QDomElement & _this ) void MidiClip::loadSettings( const QDomElement & _this ) { - m_clipType = static_cast( _this.attribute( "type" + m_clipType = static_cast( _this.attribute( "type" ).toInt() ); setName( _this.attribute( "name" ) ); diff --git a/src/tracks/PatternTrack.cpp b/src/tracks/PatternTrack.cpp index cdcd108ff..bdde4780c 100644 --- a/src/tracks/PatternTrack.cpp +++ b/src/tracks/PatternTrack.cpp @@ -41,7 +41,7 @@ PatternTrack::infoMap PatternTrack::s_infoMap; PatternTrack::PatternTrack(TrackContainer* tc) : - Track(Track::PatternTrack, tc) + Track(Track::Type::Pattern, tc) { int patternNum = s_infoMap.size(); s_infoMap[this] = patternNum; @@ -61,9 +61,9 @@ PatternTrack::PatternTrack(TrackContainer* tc) : PatternTrack::~PatternTrack() { Engine::audioEngine()->removePlayHandlesOfTypes( this, - PlayHandle::TypeNotePlayHandle - | PlayHandle::TypeInstrumentPlayHandle - | PlayHandle::TypeSamplePlayHandle ); + PlayHandle::Type::NotePlayHandle + | PlayHandle::Type::InstrumentPlayHandle + | PlayHandle::Type::SamplePlayHandle ); const int pattern = s_infoMap[this]; Engine::patternStore()->removePattern(pattern); diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index af5cbef6d..876cb307f 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -45,7 +45,7 @@ namespace lmms SampleTrack::SampleTrack(TrackContainer* tc) : - Track(Track::SampleTrack, tc), + Track(Track::Type::Sample, tc), m_volumeModel(DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr("Volume")), m_panningModel(DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr("Panning")), m_mixerChannelModel(0, 0, 0, this, tr("Mixer channel")), @@ -64,7 +64,7 @@ SampleTrack::SampleTrack(TrackContainer* tc) : SampleTrack::~SampleTrack() { - Engine::audioEngine()->removePlayHandlesOfTypes( this, PlayHandle::TypeSamplePlayHandle ); + Engine::audioEngine()->removePlayHandlesOfTypes( this, PlayHandle::Type::SamplePlayHandle ); } @@ -229,7 +229,7 @@ void SampleTrack::loadTrackSpecificSettings( const QDomElement & _this ) void SampleTrack::updateClips() { - Engine::audioEngine()->removePlayHandlesOfTypes( this, PlayHandle::TypeSamplePlayHandle ); + Engine::audioEngine()->removePlayHandlesOfTypes( this, PlayHandle::Type::SamplePlayHandle ); setPlayingClips( false ); } diff --git a/tests/src/core/ProjectVersionTest.cpp b/tests/src/core/ProjectVersionTest.cpp index 2d066dca5..387d90056 100644 --- a/tests/src/core/ProjectVersionTest.cpp +++ b/tests/src/core/ProjectVersionTest.cpp @@ -34,13 +34,13 @@ private slots: { using namespace lmms; - QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Minor) > "1.0.3"); - QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Major) < "2.1.0"); - QVERIFY(ProjectVersion("1.1.0", ProjectVersion::Release) > "0.2.1"); - QVERIFY(ProjectVersion("1.1.4", ProjectVersion::Release) < "1.1.10"); - 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" ) ); + QVERIFY(ProjectVersion("1.1.0", ProjectVersion::CompareType::Minor) > "1.0.3"); + QVERIFY(ProjectVersion("1.1.0", ProjectVersion::CompareType::Major) < "2.1.0"); + QVERIFY(ProjectVersion("1.1.0", ProjectVersion::CompareType::Release) > "0.2.1"); + QVERIFY(ProjectVersion("1.1.4", ProjectVersion::CompareType::Release) < "1.1.10"); + QVERIFY(ProjectVersion("1.1.0", ProjectVersion::CompareType::Minor) == "1.1.5"); + QVERIFY( ! ( ProjectVersion("3.1.0", ProjectVersion::CompareType::Minor) < "2.2.5" ) ); + QVERIFY( ! ( ProjectVersion("2.5.0", ProjectVersion::CompareType::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 @@ -62,7 +62,7 @@ private slots: 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) + ProjectVersion("1.0.0-a.b.c.d.e.f.g.h.i.j.k.l", ProjectVersion::CompareType::All) < "1.0.0-a.b.c.d.e.f.g.h.i.j.k.m" ); //Prerelease identifiers may contain hyphens diff --git a/tests/src/tracks/AutomationTrackTest.cpp b/tests/src/tracks/AutomationTrackTest.cpp index 2bf875992..05f345f8b 100644 --- a/tests/src/tracks/AutomationTrackTest.cpp +++ b/tests/src/tracks/AutomationTrackTest.cpp @@ -52,7 +52,7 @@ private slots: using namespace lmms; AutomationClip c(nullptr); - c.setProgressionType(AutomationClip::LinearProgression); + c.setProgressionType(AutomationClip::ProgressionType::Linear); c.putValue(0, 0.0, false); c.putValue(100, 1.0, false); @@ -69,7 +69,7 @@ private slots: using namespace lmms; AutomationClip c(nullptr); - c.setProgressionType(AutomationClip::DiscreteProgression); + c.setProgressionType(AutomationClip::ProgressionType::Discrete); c.putValue(0, 0.0, false); c.putValue(100, 1.0, false); @@ -89,14 +89,14 @@ private slots: AutomationTrack track(song); AutomationClip c1(&track); - c1.setProgressionType(AutomationClip::LinearProgression); + c1.setProgressionType(AutomationClip::ProgressionType::Linear); c1.putValue(0, 0.0, false); c1.putValue(10, 1.0, false); c1.movePosition(0); c1.addObject(&model); AutomationClip c2(&track); - c2.setProgressionType(AutomationClip::LinearProgression); + c2.setProgressionType(AutomationClip::ProgressionType::Linear); c2.putValue(0, 0.0, false); c2.putValue(100, 1.0, false); c2.movePosition(100); @@ -125,7 +125,7 @@ private slots: AutomationTrack track(song); AutomationClip c(&track); - c.setProgressionType(AutomationClip::LinearProgression); + c.setProgressionType(AutomationClip::ProgressionType::Linear); c.addObject(&model); c.putValue(0, 0.0, false); @@ -149,7 +149,7 @@ private slots: auto song = Engine::getSong(); InstrumentTrack* instrumentTrack = - dynamic_cast(Track::create(Track::InstrumentTrack, song)); + dynamic_cast(Track::create(Track::Type::Instrument, song)); MidiClip* midiClip = dynamic_cast(instrumentTrack->createClip(0)); midiClip->changeLength(TimePos(4, 0)); @@ -158,7 +158,7 @@ private slots: DetuningHelper* dh = note->detuning(); auto clip = dh->automationClip(); - clip->setProgressionType( AutomationClip::LinearProgression ); + clip->setProgressionType( AutomationClip::ProgressionType::Linear ); clip->putValue(TimePos(0, 0), 0.0); clip->putValue(TimePos(4, 0), 1.0); @@ -175,7 +175,7 @@ private slots: auto song = Engine::getSong(); auto patternStore = Engine::patternStore(); PatternTrack patternTrack(song); - Track* automationTrack = Track::create(Track::AutomationTrack, patternStore); + Track* automationTrack = Track::create(Track::Type::Automation, patternStore); QVERIFY(automationTrack->numOfClips()); AutomationClip* c1 = dynamic_cast(automationTrack->getClip(0)); @@ -183,7 +183,7 @@ private slots: FloatModel model; - c1->setProgressionType(AutomationClip::LinearProgression); + c1->setProgressionType(AutomationClip::ProgressionType::Linear); c1->putValue(0, 0.0, false); c1->putValue(10, 1.0, false); c1->addObject(&model); @@ -222,8 +222,8 @@ private slots: AutomationClip localClip(&localTrack); FloatModel model; - globalClip.setProgressionType(AutomationClip::DiscreteProgression); - localClip.setProgressionType(AutomationClip::DiscreteProgression); + globalClip.setProgressionType(AutomationClip::ProgressionType::Discrete); + localClip.setProgressionType(AutomationClip::ProgressionType::Discrete); globalClip.addObject(&model); localClip.addObject(&model); globalClip.putValue(0, 100.0f, false); From a311eed8e8c4fc4efc0683160edfe01d2b92f327 Mon Sep 17 00:00:00 2001 From: saker Date: Fri, 25 Aug 2023 14:43:09 -0400 Subject: [PATCH 30/45] Add Tap Tempo (#6375) * Add Tap Tempo Feature (#6375) Resolves #5181 * Update formatting, use namespaces, etc. * Use Qt Designer .ui file to handle the UI Thanks to irrenhaus for the idea Also added a few buttons I will add functionality for * Play metronome sound when tapped * Improve stabilization speed by comparing differences in length between intervals To improve the speed at which the BPM counter stabilizes at a certain number, we now compare the differences in length between the last two taps and the most recent two taps and reset the counter if the threshold is passed. I made it so that a difference of 500 ms resets the counter. * Remove duplicate m_ui An artifact from my battle with Git and merge conflicts.. * Format TapTempoUi header file * Add lmms namespace in UI file * Remove taptempo.ui XML file Not needed * Add LMMS_EXPORT to SamplePlayHandle * Use std::chrono::duration for intervals Co-authored-by: irrenhaus3 * Use alias for steady_clock Co-authored-by: irrenhaus3 * Speed up convergence by accounting for recent intervals This uses a combination of keeping track of a more accurate BPM, while also using a BPM difference threshold to move towards the true BPM more quickly. After three taps, a "recent interval" is calculated, which is similar to the latest interval, but less fluctuating since it accounts for three taps instead of one. This allows for comparing between the BPM on the display with the recent BPM more closely. We then compare the difference in magnitude of both BPM's with the threshold. If the threshold is passed, the counter gets reset. * Remove semicolon from "QOBJECT;" in plugins/TapTempo/TapTempo.h Co-authored-by: Hyunjin Song * Add newline between using alias and constructor * Cleanup header files * Add // namespace lmms::gui comment * Rename header guards in plugins/TapTempo/TapTempo.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Rename header guards in plugins/TapTempo/TapTempo.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Rename header guards in plugins/TapTempo/TapTempoUi.h Will merge this file with ``TapTempoView`` soon. Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Rename header guards in plugins/TapTempo/TapTempoUi.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Update plugins/TapTempo/TapTempo.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Replace virtual with override in plugins/TapTempo/TapTempo.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Merge UI file into TapTempoView * Pass TapTempo* directly into constructor in plugins/TapTempo/TapTempoView.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Update style in plugins/TapTempo/TapTempoView.h Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Add parameter name for keyPressEvent function in plugins/TapTempo/TapTempoView.h Also reorder the function declarations to match the order in the source file. Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Remove dynamic_cast to TapTempo* in plugins/TapTempo/TapTempoView.cpp Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Restrict C linkage to only lmms_plugin_main and plugin descriptor Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Simplify algorithm * Update labels when calling closeEvent * Add reset button * Adjust layout * Format document * Only allow the tap button to gain focus * Use icon provided by LMMS * Add sync button and adjust formatting * Make the metronome downbeat the first beat Also simplify code Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Round BPM values when syncing with project tempo Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> * Change Plugin::Tool to Plugin::Type::Tool --------- Co-authored-by: Hyunjin Song Co-authored-by: Dalton Messmer <33463986+messmerd@users.noreply.github.com> --- cmake/modules/PluginList.cmake | 1 + include/SamplePlayHandle.h | 2 +- plugins/TapTempo/CMakeLists.txt | 3 + plugins/TapTempo/TapTempo.cpp | 85 +++++++++++++++ plugins/TapTempo/TapTempo.h | 61 +++++++++++ plugins/TapTempo/TapTempoView.cpp | 168 ++++++++++++++++++++++++++++++ plugins/TapTempo/TapTempoView.h | 64 ++++++++++++ plugins/TapTempo/logo.png | Bin 0 -> 510 bytes 8 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 plugins/TapTempo/CMakeLists.txt create mode 100644 plugins/TapTempo/TapTempo.cpp create mode 100644 plugins/TapTempo/TapTempo.h create mode 100644 plugins/TapTempo/TapTempoView.cpp create mode 100644 plugins/TapTempo/TapTempoView.h create mode 100644 plugins/TapTempo/logo.png diff --git a/cmake/modules/PluginList.cmake b/cmake/modules/PluginList.cmake index 6b2c7519a..0a4686fb2 100644 --- a/cmake/modules/PluginList.cmake +++ b/cmake/modules/PluginList.cmake @@ -63,6 +63,7 @@ SET(LMMS_PLUGIN_LIST StereoEnhancer StereoMatrix Stk + TapTempo VstBase Vestige VstEffect diff --git a/include/SamplePlayHandle.h b/include/SamplePlayHandle.h index d016fcf0b..31b4f0bd5 100644 --- a/include/SamplePlayHandle.h +++ b/include/SamplePlayHandle.h @@ -40,7 +40,7 @@ class Track; class AudioPort; -class SamplePlayHandle : public PlayHandle +class LMMS_EXPORT SamplePlayHandle : public PlayHandle { public: SamplePlayHandle( SampleBuffer* sampleBuffer , bool ownAudioPort = true ); diff --git a/plugins/TapTempo/CMakeLists.txt b/plugins/TapTempo/CMakeLists.txt new file mode 100644 index 000000000..1fb6ba8dc --- /dev/null +++ b/plugins/TapTempo/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(taptempo TapTempo.cpp TapTempoView.cpp MOCFILES TapTempo.h TapTempoView.h EMBEDDED_RESOURCES logo.png) \ No newline at end of file diff --git a/plugins/TapTempo/TapTempo.cpp b/plugins/TapTempo/TapTempo.cpp new file mode 100644 index 000000000..aad8d99b9 --- /dev/null +++ b/plugins/TapTempo/TapTempo.cpp @@ -0,0 +1,85 @@ +/* + * TapTempo.cpp - Plugin to count beats per minute + * + * + * Copyright (c) 2022 saker + * + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "TapTempo.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AudioEngine.h" +#include "Engine.h" +#include "LedCheckBox.h" +#include "SamplePlayHandle.h" +#include "Song.h" +#include "embed.h" +#include "plugin_export.h" + +namespace lmms { + +extern "C" { +Plugin::Descriptor PLUGIN_EXPORT taptempo_plugin_descriptor + = {LMMS_STRINGIFY(PLUGIN_NAME), "Tap Tempo", QT_TRANSLATE_NOOP("PluginBrowser", "Tap to the beat"), + "saker ", 0x0100, Plugin::Type::Tool, new PluginPixmapLoader("logo"), nullptr, nullptr}; + +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model*, void*) +{ + return new TapTempo; +} +} + +TapTempo::TapTempo() + : ToolPlugin(&taptempo_plugin_descriptor, nullptr) +{ +} + +void TapTempo::onBpmClick() +{ + const auto currentTime = clock::now(); + if (m_numTaps == 0) + { + m_startTime = currentTime; + } + else + { + using namespace std::chrono_literals; + const auto secondsElapsed = (currentTime - m_startTime) / 1.0s; + if (m_numTaps >= m_tapsNeededToDisplay) { m_bpm = m_numTaps / secondsElapsed * 60; } + } + + ++m_numTaps; +} + +QString TapTempo::nodeName() const +{ + return taptempo_plugin_descriptor.name; +} +} // namespace lmms \ No newline at end of file diff --git a/plugins/TapTempo/TapTempo.h b/plugins/TapTempo/TapTempo.h new file mode 100644 index 000000000..be540b8da --- /dev/null +++ b/plugins/TapTempo/TapTempo.h @@ -0,0 +1,61 @@ +/* + * TapTempo.h - Plugin to count beats per minute + * + * Copyright (c) 2022 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_TAP_TEMPO_H +#define LMMS_TAP_TEMPO_H + +#include + +#include "TapTempoView.h" +#include "ToolPlugin.h" + +namespace lmms { + +class TapTempo : public ToolPlugin +{ + Q_OBJECT +public: + using clock = std::chrono::steady_clock; + + TapTempo(); + void onBpmClick(); + + QString nodeName() const override; + void saveSettings(QDomDocument&, QDomElement&) override {} + void loadSettings(const QDomElement&) override {} + + gui::PluginView* instantiateView(QWidget*) override { return new gui::TapTempoView(this); } + +private: + std::chrono::time_point m_startTime; + int m_numTaps = 0; + int m_tapsNeededToDisplay = 2; + double m_bpm = 0.0; + bool m_showDecimal = false; + + friend class gui::TapTempoView; +}; +} // namespace lmms + +#endif // LMMS_TAP_TEMPO_H diff --git a/plugins/TapTempo/TapTempoView.cpp b/plugins/TapTempo/TapTempoView.cpp new file mode 100644 index 000000000..d6c24fcf5 --- /dev/null +++ b/plugins/TapTempo/TapTempoView.cpp @@ -0,0 +1,168 @@ +/* + * TapTempoView.cpp - Plugin to count beats per minute + * + * + * Copyright (c) 2022 saker + * + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ +#include "TapTempoView.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Engine.h" +#include "SamplePlayHandle.h" +#include "Song.h" +#include "TapTempo.h" + +namespace lmms::gui { +TapTempoView::TapTempoView(TapTempo* plugin) + : ToolPluginView(plugin) + , m_plugin(plugin) +{ + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + auto font = QFont(); + font.setPointSize(24); + + m_tapButton = new QPushButton(); + m_tapButton->setFixedSize(200, 200); + m_tapButton->setFont(font); + m_tapButton->setText(tr("0")); + + auto precisionCheckBox = new QCheckBox(tr("Precision")); + precisionCheckBox->setFocusPolicy(Qt::NoFocus); + precisionCheckBox->setToolTip(tr("Display in high precision")); + precisionCheckBox->setText(tr("Precision")); + + auto muteCheckBox = new QCheckBox(tr("0.0 ms")); + muteCheckBox->setFocusPolicy(Qt::NoFocus); + muteCheckBox->setToolTip(tr("Mute metronome")); + muteCheckBox->setText(tr("Mute")); + + m_msLabel = new QLabel(); + m_msLabel->setFocusPolicy(Qt::NoFocus); + m_msLabel->setToolTip(tr("BPM in milliseconds")); + m_msLabel->setText(tr("0 ms")); + + m_hzLabel = new QLabel(); + m_hzLabel->setFocusPolicy(Qt::NoFocus); + m_hzLabel->setToolTip(tr("Frequency of BPM")); + m_hzLabel->setText(tr("0.0000 hz")); + + auto resetButton = new QPushButton(tr("Reset")); + resetButton->setFocusPolicy(Qt::NoFocus); + resetButton->setToolTip(tr("Reset counter and sidebar information")); + + auto syncButton = new QPushButton(tr("Sync")); + syncButton->setFocusPolicy(Qt::NoFocus); + syncButton->setToolTip(tr("Sync with project tempo")); + + auto optionLayout = new QVBoxLayout(); + optionLayout->addWidget(precisionCheckBox); + optionLayout->addWidget(muteCheckBox); + + auto bpmInfoLayout = new QVBoxLayout(); + bpmInfoLayout->addWidget(m_msLabel, 0, Qt::AlignHCenter); + bpmInfoLayout->addWidget(m_hzLabel, 0, Qt::AlignHCenter); + + auto sidebarLayout = new QHBoxLayout(); + sidebarLayout->addLayout(optionLayout); + sidebarLayout->addLayout(bpmInfoLayout); + + auto buttonsLayout = new QHBoxLayout(); + buttonsLayout->addWidget(resetButton, 0, Qt::AlignCenter); + buttonsLayout->addWidget(syncButton, 0, Qt::AlignCenter); + + auto mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(m_tapButton, 0, Qt::AlignCenter); + mainLayout->addLayout(buttonsLayout); + mainLayout->addLayout(sidebarLayout); + + connect(m_tapButton, &QPushButton::pressed, this, [this, muteCheckBox]() { + if (!muteCheckBox->isChecked()) + { + const auto timeSigNumerator = Engine::getSong()->getTimeSigModel().getNumerator(); + Engine::audioEngine()->addPlayHandle(new SamplePlayHandle( + m_plugin->m_numTaps % timeSigNumerator == 0 ? "misc/metronome02.ogg" : "misc/metronome01.ogg")); + } + + m_plugin->onBpmClick(); + updateLabels(); + }); + + connect(resetButton, &QPushButton::pressed, this, [this]() { closeEvent(nullptr); }); + + connect(precisionCheckBox, &QCheckBox::toggled, [this](bool checked) { + m_plugin->m_showDecimal = checked; + updateLabels(); + }); + + connect(syncButton, &QPushButton::clicked, this, [this]() { + const auto& tempoModel = Engine::getSong()->tempoModel(); + if (m_plugin->m_bpm < tempoModel.minValue() || m_plugin->m_bpm > tempoModel.maxValue()) { return; } + Engine::getSong()->setTempo(std::round(m_plugin->m_bpm)); + }); + + hide(); + if (parentWidget()) + { + parentWidget()->hide(); + parentWidget()->layout()->setSizeConstraint(QLayout::SetFixedSize); + + Qt::WindowFlags flags = parentWidget()->windowFlags(); + flags |= Qt::MSWindowsFixedSizeDialogHint; + flags &= ~Qt::WindowMaximizeButtonHint; + parentWidget()->setWindowFlags(flags); + } +} + +void TapTempoView::updateLabels() +{ + const double bpm = m_plugin->m_showDecimal ? m_plugin->m_bpm : std::round(m_plugin->m_bpm); + const double hz = bpm / 60; + const double ms = bpm > 0 ? 1 / hz * 1000 : 0; + + m_tapButton->setText(QString::number(bpm, 'f', m_plugin->m_showDecimal ? 1 : 0)); + m_msLabel->setText(tr("%1 ms").arg(ms, 0, 'f', m_plugin->m_showDecimal ? 1 : 0)); + m_hzLabel->setText(tr("%1 hz").arg(hz, 0, 'f', 4)); +} + +void TapTempoView::keyPressEvent(QKeyEvent* event) +{ + QWidget::keyPressEvent(event); + if (!event->isAutoRepeat()) { m_plugin->onBpmClick(); } +} + +void TapTempoView::closeEvent(QCloseEvent*) +{ + m_plugin->m_numTaps = 0; + m_plugin->m_bpm = 0; + updateLabels(); +} + +} // namespace lmms::gui diff --git a/plugins/TapTempo/TapTempoView.h b/plugins/TapTempo/TapTempoView.h new file mode 100644 index 000000000..a2a522d3f --- /dev/null +++ b/plugins/TapTempo/TapTempoView.h @@ -0,0 +1,64 @@ +/* + * TapTempoView.h - Plugin to count beats per minute + * + * + * Copyright (c) 2022 saker + * + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GUI_TAP_TEMPO_VIEW_H +#define LMMS_GUI_TAP_TEMPO_VIEW_H + +#include "ToolPluginView.h" + +class QVBoxLayout; +class QHBoxLayout; +class QPushButton; +class QLabel; +class QCloseEvent; +class QKeyEvent; +class QCheckBox; + +namespace lmms { +class TapTempo; +} + +namespace lmms::gui { + +class TapTempoView : public ToolPluginView +{ + Q_OBJECT +public: + TapTempoView(TapTempo* plugin); + void updateLabels(); + void keyPressEvent(QKeyEvent* event) override; + void closeEvent(QCloseEvent*) override; + +private: + QPushButton* m_tapButton; + QLabel* m_msLabel; + QLabel* m_hzLabel; + TapTempo* m_plugin; + friend class TapTempo; +}; +} // namespace lmms::gui + +#endif // LMMS_GUI_TAP_TEMPO_VIEW_H diff --git a/plugins/TapTempo/logo.png b/plugins/TapTempo/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..67e6a350346a826763c804621bae006580a71ef7 GIT binary patch literal 510 zcmV Date: Fri, 25 Aug 2023 21:16:34 -0400 Subject: [PATCH 31/45] fixes #6759: Tempo Sync Knob - Context menu string don't update on custom tempo (#6827) * fixed #6759: Context menu string doesn't update The TempoSyncKnobModel didn't emit any signal when the a SyncMode::Custom was recaclulated. Also it looks like someone broke the TempoSyncKnowModel bc SyncMode had been renamed to TempoSyncMode and the build was screaming. * fixed #6759: Knob custom tempo The TempoSyncKnobModel didn't emit any signal when the a SyncMode::Custom was recalculated. Also it looks like someone broke the TempoSyncKnowModel because SyncMode had been renamed to TempoSyncMode and the build was screaming. Recommit, fixed silly mistake where the signal would be emitted twice on mode change to Custom. * Update src/core/TempoSyncKnobModel.cpp Co-authored-by: saker * Update src/core/TempoSyncKnobModel.cpp Co-authored-by: saker * Use function pointers for connect TempoSyncKnob.cpp * Silly fp mistake fixed in TempoSyncKnob.cpp * Unfixed second macro call for now: TempoSyncKnob.cpp --------- Co-authored-by: saker --- src/core/TempoSyncKnobModel.cpp | 4 ++++ src/gui/widgets/TempoSyncKnob.cpp | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/TempoSyncKnobModel.cpp b/src/core/TempoSyncKnobModel.cpp index e89716ab2..7f707478f 100644 --- a/src/core/TempoSyncKnobModel.cpp +++ b/src/core/TempoSyncKnobModel.cpp @@ -117,6 +117,10 @@ void TempoSyncKnobModel::calculateTempoSyncTime( bpm_t _bpm ) emit syncModeChanged( m_tempoSyncMode ); m_tempoLastSyncMode = m_tempoSyncMode; } + else if (m_tempoSyncMode == SyncMode::Custom) + { + emit syncModeChanged(m_tempoSyncMode); + } } diff --git a/src/gui/widgets/TempoSyncKnob.cpp b/src/gui/widgets/TempoSyncKnob.cpp index b58e61cf4..473cee28c 100644 --- a/src/gui/widgets/TempoSyncKnob.cpp +++ b/src/gui/widgets/TempoSyncKnob.cpp @@ -75,8 +75,7 @@ void TempoSyncKnob::modelChanged() { m_custom->setModel( &model()->m_custom ); } - connect( model(), SIGNAL(syncModeChanged(lmms::TempoSyncKnobModel::TempoSyncMode)), - this, SLOT(updateDescAndIcon())); + connect(model(), &TempoSyncKnobModel::syncModeChanged, this, &TempoSyncKnob::updateDescAndIcon); connect( this, SIGNAL(sliderMoved(float)), model(), SLOT(disableSync())); updateDescAndIcon(); From 7000afb2eac1deab0ed76db48371613525b94986 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 26 Aug 2023 04:11:03 +0200 Subject: [PATCH 32/45] Modifier keys for mouse wheel adjustments (#6769) (#6770) * Modifier keys for mouse wheel adjustments (#6769) Give the users the option to use modifier keys (Shift, Ctrl, Alt) to switch between different scales of adjustment when changing knob values using the mouse wheel. The commit implements the following behaviour: * Using the mouse wheel without any modifier keys makes coarser adjustments than the current default, i.e. the range of the parameter can be swept with 100 mouse wheel events instead of 2000. * Pressing the "Shift" key while using the mouse wheel allows making coarser adjustments than the default. The range can be swept with 10 mouse wheel events. * Pressing the "Ctrl" key allows making finer adjustments than the default. The range is swept with 1000 events. * Pressing the "Alt" key allows even finer adjustments. The range is swept with 2000 events (the current default). Most of these scales are organized in magnitudes (10, 100, 1000) which should give a very natural feeling to "zone in" on a value. * Fix indentation of comments Fix the indention of comments as Qt Creator seems to be incapable of copy-pasting code in a sensible way. * Fix comments Fix the comments by describing better the ideas instead of referencing values. * Fix format in src/gui/widgets/Knob.cpp --------- Co-authored-by: saker --- src/gui/widgets/Knob.cpp | 48 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index d67c6dbdd..c2f90fb2b 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -689,10 +689,52 @@ void Knob::paintEvent( QPaintEvent * _me ) void Knob::wheelEvent(QWheelEvent * we) { we->accept(); - const float stepMult = model()->range() / 2000 / model()->step(); - const int inc = ((we->angleDelta().y() > 0 ) ? 1 : -1) * ((stepMult < 1 ) ? 1 : stepMult); - model()->incValue( inc ); + const int deltaY = we->angleDelta().y(); + float direction = deltaY > 0 ? 1 : -1; + auto * m = model(); + float const step = m->step(); + float const range = m->range(); + + // This is the default number of steps or mouse wheel events that it takes to sweep + // from the lowest value to the highest value. + // It might be modified if the user presses modifier keys. See below. + float numberOfStepsForFullSweep = 100.; + + auto const modKeys = we->modifiers(); + if (modKeys == Qt::ShiftModifier) + { + // The shift is intended to go through the values in very coarse steps as in: + // "Shift into overdrive" + numberOfStepsForFullSweep = 10; + } + else if (modKeys == Qt::ControlModifier) + { + // The control key gives more control, i.e. it enables more fine-grained adjustments + numberOfStepsForFullSweep = 1000; + } + else if (modKeys == Qt::AltModifier) + { + // The alt key enables even finer adjustments + numberOfStepsForFullSweep = 2000; + + // It seems that on some systems pressing Alt with mess with the directions, + // i.e. scrolling the mouse wheel is interpreted as pressing the mouse wheel + // left and right. Account for this quirk. + if (deltaY == 0) + { + int const deltaX = we->angleDelta().x(); + if (deltaX != 0) + { + direction = deltaX > 0 ? 1 : -1; + } + } + } + + // Compute the number of steps but make sure that we always do at least one step + const float stepMult = std::max(range / numberOfStepsForFullSweep / step, 1.f); + const int inc = direction * stepMult; + model()->incValue(inc); s_textFloat->setText( displayValue() ); s_textFloat->moveGlobal( this, QPoint( width() + 2, 0 ) ); From fc2f6a0b31bdd10c47cc0c6c2c18723d208eaae0 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sat, 26 Aug 2023 11:53:34 +0900 Subject: [PATCH 33/45] Replace the CI status badge with GitHub Actions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c64ca0a97..c8324226e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ![LMMS Logo](https://raw.githubusercontent.com/LMMS/artwork/master/Icon%20%26%20Mimetypes/lmms-64x64.svg) LMMS -[![Build status](https://circleci.com/gh/LMMS/lmms.svg?style=shield)](https://circleci.com/gh/LMMS/lmms) +[![Build status](https://github.com/LMMS/lmms/actions/workflows/build.yml/badge.svg)](https://github.com/LMMS/lmms/actions/workflows/build.yml) [![Latest stable release](https://img.shields.io/github/release/LMMS/lmms.svg?maxAge=3600)](https://lmms.io/download) [![Overall downloads on Github](https://img.shields.io/github/downloads/LMMS/lmms/total.svg?maxAge=3600)](https://github.com/LMMS/lmms/releases) [![Join the chat at Discord](https://img.shields.io/badge/chat-on%20discord-7289DA.svg)](https://discord.gg/3sc5su7) From 4cb09e2b6058155efc9920ea09a973071ce1c58e Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 27 Aug 2023 18:21:38 +0200 Subject: [PATCH 34/45] Fix the base note automation fix (#6828) Towards the end of the development for the fix of #6548 (via #6725) the upgrade code was refactored into its own class. While doing so it was forgotten to actually call the `upgrade` method on the `UpgradeExtendedNoteRange` instance. As a result almost all files should currently open in a wrong state with many instruments transposed. This commit fixes this. Also explicitly check the assertion that BB tracks do not contain other BB tracks. --- src/core/DataFile.cpp | 2 ++ src/core/UpgradeExtendedNoteRange.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 6ad2f8526..5c98ec81c 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1671,6 +1671,8 @@ void DataFile::upgrade_extendedNoteRange() { auto root = documentElement(); UpgradeExtendedNoteRange upgradeExtendedNoteRange(root); + + upgradeExtendedNoteRange.upgrade(); } diff --git a/src/core/UpgradeExtendedNoteRange.cpp b/src/core/UpgradeExtendedNoteRange.cpp index e61da3723..6ed98e63e 100644 --- a/src/core/UpgradeExtendedNoteRange.cpp +++ b/src/core/UpgradeExtendedNoteRange.cpp @@ -193,6 +193,7 @@ static void fixTrack(QDomElement & track, std::set & automatedBase for (int i = 0; i < subTracks.size(); ++i) { QDomElement subTrack = subTracks.item(i).toElement(); + assert (static_cast(subTrack.attribute("type").toInt()) != Track::Type::Pattern); fixTrack(subTrack, automatedBaseNoteIds); } } From e2fd288ae7413e37e078a1f1b57ba9bf45761a3a Mon Sep 17 00:00:00 2001 From: MrTopom Date: Sun, 27 Aug 2023 20:11:41 +0200 Subject: [PATCH 35/45] =?UTF-8?q?Change=20the=20title=20for=20SideBarWidge?= =?UTF-8?q?ts=20to=20be=20vertically=20centered=20related=E2=80=A6=20(#683?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change the title for SideBarWidgets to be vertically centered related to icon and with no underlining * Update src/gui/SideBarWidget.cpp Co-authored-by: saker --------- Co-authored-by: saker --- src/gui/SideBarWidget.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/SideBarWidget.cpp b/src/gui/SideBarWidget.cpp index 60760ba59..c218bedd3 100644 --- a/src/gui/SideBarWidget.cpp +++ b/src/gui/SideBarWidget.cpp @@ -62,16 +62,16 @@ void SideBarWidget::paintEvent( QPaintEvent * ) QFont f = p.font(); f.setBold( true ); - f.setUnderline( true ); + f.setUnderline(false); f.setPointSize( f.pointSize() + 2 ); p.setFont( f ); p.setPen( palette().highlightedText().color() ); - const int tx = m_icon.width()+4; + const int tx = m_icon.width() + 8; QFontMetrics metrics( f ); - const int ty = metrics.ascent(); + const int ty = (metrics.ascent() + m_icon.height()) / 2; p.drawText( tx, ty, m_title ); p.drawPixmap( 2, 2, m_icon.transformed( QTransform().rotate( -90 ) ) ); From 1e6a66f4ac4ecd3890d1dc66268590979cfb2b53 Mon Sep 17 00:00:00 2001 From: saker Date: Mon, 28 Aug 2023 13:14:19 -0400 Subject: [PATCH 36/45] Add mixer LCD channels for Instrument & Sample tracks (#6831) * Add mixer channel LCD to SampleTrackView * Increase sizes to compensate for LCD box The DEFAULT_SETTINGS_WIDGET_WIDTH and DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT were both increased by +32 pixels. TRACK_OP_WIDTH and TRACK_OP_WIDTH_COMPACT were then changed relative to that increase. * Use Qt layout in SampleTrackView * Add mixer channel LCD to InstrumentTrackView * Move LCD box to the right of the track label * Revert changes to TRACK_OP_WIDTH and TRACK_OP_WIDTH_COMPACT --- include/InstrumentTrackView.h | 3 +++ include/SampleTrackView.h | 2 ++ include/TrackView.h | 4 +-- src/gui/tracks/InstrumentTrackView.cpp | 36 ++++++++++++++------------ src/gui/tracks/SampleTrackView.cpp | 23 ++++++++++------ 5 files changed, 42 insertions(+), 26 deletions(-) diff --git a/include/InstrumentTrackView.h b/include/InstrumentTrackView.h index 363f5b3ab..d7d5fb83a 100644 --- a/include/InstrumentTrackView.h +++ b/include/InstrumentTrackView.h @@ -25,6 +25,7 @@ #ifndef LMMS_GUI_INSTRUMENT_TRACK_VIEW_H #define LMMS_GUI_INSTRUMENT_TRACK_VIEW_H +#include "MixerLineLcdSpinBox.h" #include "TrackView.h" #include "InstrumentTrack.h" @@ -72,6 +73,7 @@ public: protected: + void modelChanged() override; void dragEnterEvent( QDragEnterEvent * _dee ) override; void dropEvent( QDropEvent * _de ) override; @@ -97,6 +99,7 @@ private: // widgets in track-settings-widget TrackLabelButton * m_tlb; + MixerLineLcdSpinBox* m_mixerChannelNumber; Knob * m_volumeKnob; Knob * m_panningKnob; FadeButton * m_activityIndicator; diff --git a/include/SampleTrackView.h b/include/SampleTrackView.h index b586df15e..3ccb97aea 100644 --- a/include/SampleTrackView.h +++ b/include/SampleTrackView.h @@ -26,6 +26,7 @@ #define LMMS_GUI_SAMPLE_TRACK_VIEW_H +#include "MixerLineLcdSpinBox.h" #include "TrackView.h" namespace lmms @@ -90,6 +91,7 @@ private slots: private: SampleTrackWindow * m_window; + MixerLineLcdSpinBox* m_mixerChannelNumber; Knob * m_volumeKnob; Knob * m_panningKnob; FadeButton * m_activityIndicator; diff --git a/include/TrackView.h b/include/TrackView.h index 763705599..f697d9ea8 100644 --- a/include/TrackView.h +++ b/include/TrackView.h @@ -48,11 +48,11 @@ class FadeButton; class TrackContainerView; -const int DEFAULT_SETTINGS_WIDGET_WIDTH = 224; +const int DEFAULT_SETTINGS_WIDGET_WIDTH = 256; const int TRACK_OP_WIDTH = 78; // This shaves 150-ish pixels off track buttons, // ruled from config: ui.compacttrackbuttons -const int DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT = 96; +const int DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT = 128; const int TRACK_OP_WIDTH_COMPACT = 62; diff --git a/src/gui/tracks/InstrumentTrackView.cpp b/src/gui/tracks/InstrumentTrackView.cpp index 669fdaccb..87c0f0449 100644 --- a/src/gui/tracks/InstrumentTrackView.cpp +++ b/src/gui/tracks/InstrumentTrackView.cpp @@ -63,7 +63,6 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV m_tlb = new TrackLabelButton( this, getTrackSettingsWidget() ); m_tlb->setCheckable( true ); m_tlb->setIcon( embed::getIconPixmap( "instrument_track" ) ); - m_tlb->move( 3, 1 ); m_tlb->show(); connect( m_tlb, SIGNAL(toggled(bool)), @@ -75,24 +74,14 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV connect(ConfigManager::inst(), SIGNAL(valueChanged(QString,QString,QString)), this, SLOT(handleConfigChange(QString,QString,QString))); - // creation of widgets for track-settings-widget - int widgetWidth; - if( ConfigManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt() ) - { - widgetWidth = DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT; - } - else - { - widgetWidth = DEFAULT_SETTINGS_WIDGET_WIDTH; - } + m_mixerChannelNumber = new MixerLineLcdSpinBox(2, getTrackSettingsWidget(), tr("Mixer channel"), this); + m_mixerChannelNumber->show(); m_volumeKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), tr( "Volume" ) ); m_volumeKnob->setVolumeKnob( true ); m_volumeKnob->setModel( &_it->m_volumeModel ); m_volumeKnob->setHintText( tr( "Volume:" ), "%" ); - m_volumeKnob->move( widgetWidth-2*24, 2 ); m_volumeKnob->setLabel( tr( "VOL" ) ); m_volumeKnob->show(); @@ -100,7 +89,6 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV tr( "Panning" ) ); m_panningKnob->setModel( &_it->m_panningModel ); m_panningKnob->setHintText(tr("Panning:"), "%"); - m_panningKnob->move( widgetWidth-24, 2 ); m_panningKnob->setLabel( tr( "PAN" ) ); m_panningKnob->show(); @@ -151,9 +139,18 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV QApplication::palette().color( QPalette::Active, QPalette::BrightText).darker(), getTrackSettingsWidget() ); - m_activityIndicator->setGeometry( - widgetWidth-2*24-11, 2, 8, 28 ); + m_activityIndicator->setFixedSize(8, 28); m_activityIndicator->show(); + + auto layout = new QHBoxLayout(getTrackSettingsWidget()); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(m_tlb); + layout->addWidget(m_mixerChannelNumber); + layout->addWidget(m_activityIndicator); + layout->addWidget(m_volumeKnob); + layout->addWidget(m_panningKnob); + connect( m_activityIndicator, SIGNAL(pressed()), this, SLOT(activityIndicatorPressed())); connect( m_activityIndicator, SIGNAL(released()), @@ -268,6 +265,13 @@ void InstrumentTrackView::handleConfigChange(QString cls, QString attr, QString } } +void InstrumentTrackView::modelChanged() +{ + TrackView::modelChanged(); + auto st = castModel(); + m_mixerChannelNumber->setModel(&st->m_mixerChannelModel); +} + void InstrumentTrackView::dragEnterEvent( QDragEnterEvent * _dee ) { InstrumentTrackWindow::dragEnterEventGeneric( _dee ); diff --git a/src/gui/tracks/SampleTrackView.cpp b/src/gui/tracks/SampleTrackView.cpp index 6a6a2c5fd..8516eb5c2 100644 --- a/src/gui/tracks/SampleTrackView.cpp +++ b/src/gui/tracks/SampleTrackView.cpp @@ -56,20 +56,17 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : connect(m_tlb, SIGNAL(clicked(bool)), this, SLOT(showEffects())); m_tlb->setIcon(embed::getIconPixmap("sample_track")); - m_tlb->move(3, 1); m_tlb->show(); + m_mixerChannelNumber = new MixerLineLcdSpinBox(2, getTrackSettingsWidget(), tr("Mixer channel"), this); + m_mixerChannelNumber->show(); + m_volumeKnob = new Knob( KnobType::Small17, getTrackSettingsWidget(), tr( "Track volume" ) ); m_volumeKnob->setVolumeKnob( true ); m_volumeKnob->setModel( &_t->m_volumeModel ); m_volumeKnob->setHintText( tr( "Channel volume:" ), "%" ); - int settingsWidgetWidth = ConfigManager::inst()-> - value( "ui", "compacttrackbuttons" ).toInt() - ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT - : DEFAULT_SETTINGS_WIDGET_WIDTH; - m_volumeKnob->move( settingsWidgetWidth - 2 * 24, 2 ); m_volumeKnob->setLabel( tr( "VOL" ) ); m_volumeKnob->show(); @@ -77,7 +74,6 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : tr( "Panning" ) ); m_panningKnob->setModel( &_t->m_panningModel ); m_panningKnob->setHintText( tr( "Panning:" ), "%" ); - m_panningKnob->move( settingsWidgetWidth - 24, 2 ); m_panningKnob->setLabel( tr( "PAN" ) ); m_panningKnob->show(); @@ -87,8 +83,18 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : QApplication::palette().color(QPalette::Active, QPalette::BrightText).darker(), getTrackSettingsWidget() ); - m_activityIndicator->setGeometry(settingsWidgetWidth - 2 * 24 - 11, 2, 8, 28); + m_activityIndicator->setFixedSize(8, 28); m_activityIndicator->show(); + + auto layout = new QHBoxLayout(getTrackSettingsWidget()); + layout->setContentsMargins(0, 0, 0, 0); + layout->setSpacing(0); + layout->addWidget(m_tlb); + layout->addWidget(m_mixerChannelNumber); + layout->addWidget(m_activityIndicator); + layout->addWidget(m_volumeKnob); + layout->addWidget(m_panningKnob); + connect(_t, SIGNAL(playingChanged()), this, SLOT(updateIndicator())); setModel( _t ); @@ -170,6 +176,7 @@ void SampleTrackView::modelChanged() { auto st = castModel(); m_volumeKnob->setModel(&st->m_volumeModel); + m_mixerChannelNumber->setModel(&st->m_mixerChannelModel); TrackView::modelChanged(); } From fcdf4c0568407762db38c180684cea9af101a937 Mon Sep 17 00:00:00 2001 From: MrTopom Date: Tue, 29 Aug 2023 21:32:11 +0200 Subject: [PATCH 37/45] Showing Knob value on mouse over (#6835) * Showing Knob value on mouse over * Correcting minors source code issues * Correcting double QTimer include * Removing blank lines * Removing space and add one * Update src/gui/widgets/SimpleTextFloat.cpp Co-authored-by: saker * Correcting QTimer * Remove a parameter that has the default value --------- Co-authored-by: saker --- include/Knob.h | 4 ++++ include/SimpleTextFloat.h | 8 +++++++- src/gui/widgets/Knob.cpp | 26 ++++++++++++++++++------ src/gui/widgets/SimpleTextFloat.cpp | 31 +++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/include/Knob.h b/include/Knob.h index 85a51e363..d5739bb1c 100644 --- a/include/Knob.h +++ b/include/Knob.h @@ -144,6 +144,9 @@ protected: void wheelEvent( QWheelEvent * _me ) override; void changeEvent(QEvent * ev) override; + void enterEvent(QEvent *event) override; + void leaveEvent(QEvent *event) override; + virtual float getValue( const QPoint & _p ); private slots: @@ -160,6 +163,7 @@ private: float _innerRadius = 1) const; void drawKnob( QPainter * _p ); + void showTextFloat(int msecBeforeDisplay, int msecDisplayTime); void setPosition( const QPoint & _p ); bool updateAngle(); diff --git a/include/SimpleTextFloat.h b/include/SimpleTextFloat.h index f720d0b3e..bde6c84fa 100644 --- a/include/SimpleTextFloat.h +++ b/include/SimpleTextFloat.h @@ -31,6 +31,7 @@ #include "lmms_export.h" class QLabel; +class QTimer; namespace lmms::gui { @@ -44,6 +45,8 @@ public: void setText(const QString & text); + void showWithDelay(int msecBeforeDisplay, int msecDisplayTime); + void setVisibilityTimeOut(int msecs); void moveGlobal(QWidget * w, const QPoint & offset) @@ -51,11 +54,14 @@ public: move(w->mapToGlobal(QPoint(0, 0)) + offset); } + void hide(); + private: QLabel * m_textLabel; + QTimer * m_showTimer; + QTimer * m_hideTimer; }; - } // namespace lmms::gui #endif diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index c2f90fb2b..56cf29345 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -22,6 +22,8 @@ * */ +#include "Knob.h" + #include #include #include @@ -34,7 +36,6 @@ #endif #include "lmms_math.h" -#include "Knob.h" #include "CaptionMenu.h" #include "ConfigManager.h" #include "ControllerConnection.h" @@ -48,7 +49,6 @@ #include "SimpleTextFloat.h" #include "StringPairDrag.h" - namespace lmms::gui { @@ -484,6 +484,13 @@ void Knob::drawKnob( QPainter * _p ) _p->drawImage( 0, 0, m_cache ); } +void Knob::showTextFloat(int msecBeforeDisplay, int msecDisplayTime) +{ + s_textFloat->setText(displayValue()); + s_textFloat->moveGlobal(this, QPoint(width() + 2, 0)); + s_textFloat->showWithDelay(msecBeforeDisplay, msecDisplayTime); +} + float Knob::getValue( const QPoint & _p ) { float value; @@ -580,10 +587,8 @@ void Knob::mousePressEvent( QMouseEvent * _me ) emit sliderPressed(); - s_textFloat->setText( displayValue() ); - s_textFloat->moveGlobal( this, - QPoint( width() + 2, 0 ) ); - s_textFloat->show(); + showTextFloat(0, 0); + m_buttonPressed = true; } else if( _me->button() == Qt::LeftButton && @@ -613,6 +618,7 @@ void Knob::mouseMoveEvent( QMouseEvent * _me ) m_lastMousePos = _me->pos(); } s_textFloat->setText( displayValue() ); + s_textFloat->show(); } @@ -638,7 +644,15 @@ void Knob::mouseReleaseEvent( QMouseEvent* event ) s_textFloat->hide(); } +void Knob::enterEvent(QEvent *event) +{ + showTextFloat(700, 2000); +} +void Knob::leaveEvent(QEvent *event) +{ + s_textFloat->hide(); +} void Knob::focusOutEvent( QFocusEvent * _fe ) diff --git a/src/gui/widgets/SimpleTextFloat.cpp b/src/gui/widgets/SimpleTextFloat.cpp index d1f490b5e..df438423e 100644 --- a/src/gui/widgets/SimpleTextFloat.cpp +++ b/src/gui/widgets/SimpleTextFloat.cpp @@ -45,6 +45,14 @@ SimpleTextFloat::SimpleTextFloat() : m_textLabel = new QLabel(this); layout->addWidget(m_textLabel); + + m_showTimer = new QTimer(); + m_showTimer->setSingleShot(true); + QObject::connect(m_showTimer, &QTimer::timeout, this, &SimpleTextFloat::show); + + m_hideTimer = new QTimer(); + m_hideTimer->setSingleShot(true); + QObject::connect(m_hideTimer, &QTimer::timeout, this, &SimpleTextFloat::hide); } void SimpleTextFloat::setText(const QString & text) @@ -52,6 +60,29 @@ void SimpleTextFloat::setText(const QString & text) m_textLabel->setText(text); } +void SimpleTextFloat::showWithDelay(int msecBeforeDisplay, int msecDisplayTime) +{ + if (msecBeforeDisplay != 0) + { + m_showTimer->start(msecBeforeDisplay); + } + else + { + show(); + } + + if (msecDisplayTime != 0) + { + m_hideTimer->start(msecBeforeDisplay + msecDisplayTime); + } +} + +void SimpleTextFloat::hide() +{ + m_showTimer->stop(); + m_hideTimer->stop(); + QWidget::hide(); +} void SimpleTextFloat::setVisibilityTimeOut(int msecs) { From 3263bfd555982d52735a19763eff26f4be7e0eb7 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Thu, 31 Aug 2023 02:01:15 +0100 Subject: [PATCH 38/45] Fix generator expression in strip command (#6762) * Fix generator expression in strip command * Add TODO comment for CMake 3.19 --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a68b788d6..1ec3f992e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -605,7 +605,9 @@ else() set(NOOP_COMMAND "${CMAKE_COMMAND}" "-E" "echo") endif() if(STRIP) - set(STRIP_COMMAND "$,${NOOP_COMMAND},${STRIP}>") + # TODO CMake 3.19: Now that CONFIG generator expressions support testing for + # multiple configurations, combine the OR into a single CONFIG expression. + set(STRIP_COMMAND "$,$>,${NOOP_COMMAND},${STRIP}>") else() set(STRIP_COMMAND "${NOOP_COMMAND}") endif() From 4804ab678501f9c746b4b2c870f0a323f7d3953b Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Thu, 31 Aug 2023 12:12:00 +0100 Subject: [PATCH 39/45] Support per-note detuning and panning with Sf2 Player (#6602) * Add `ArrayVector` class template and tests * Fix counting of failed test suites * Support detuning and panning with Sf2 Player * Restrict panning to supported FluidSynth versions * Fix data array cast type * Fix tests for Qt<5.10 and correct mistaken test * DIsplay warning for FluidSynth < 2 * Remove unnecessary clamp * Update include guard name --- CMakeLists.txt | 6 +- include/ArrayVector.h | 388 ++++++++++++++ include/NotePlayHandle.h | 3 + plugins/Sf2Player/Sf2Player.cpp | 99 +++- tests/CMakeLists.txt | 1 + tests/main.cpp | 2 +- tests/src/core/ArrayVectorTest.cpp | 831 +++++++++++++++++++++++++++++ 7 files changed, 1306 insertions(+), 24 deletions(-) create mode 100644 include/ArrayVector.h create mode 100644 tests/src/core/ArrayVectorTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ec3f992e..e20f13527 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -494,7 +494,11 @@ IF(WANT_SF2) find_package(FluidSynth 1.1.0) if(FluidSynth_FOUND) SET(LMMS_HAVE_FLUIDSYNTH TRUE) - SET(STATUS_FLUIDSYNTH "OK") + if(FluidSynth_VERSION_STRING VERSION_GREATER_EQUAL 2) + set(STATUS_FLUIDSYNTH "OK") + else() + set(STATUS_FLUIDSYNTH "OK (FluidSynth version < 2: per-note panning unsupported)") + endif() else() SET(STATUS_FLUIDSYNTH "not found, libfluidsynth-dev (or similar)" "is highly recommended") diff --git a/include/ArrayVector.h b/include/ArrayVector.h new file mode 100644 index 000000000..06e09226c --- /dev/null +++ b/include/ArrayVector.h @@ -0,0 +1,388 @@ +/* + * ArrayVector.h + * + * Copyright (c) 2023 Dominic Clark + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_ARRAY_VECTOR_H +#define LMMS_ARRAY_VECTOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace lmms { + +namespace detail { + +template +constexpr bool is_input_iterator_v = false; + +template +constexpr bool is_input_iterator_v::iterator_category>> = + std::is_convertible_v::iterator_category, std::input_iterator_tag>; + +} // namespace detail + +/** + * A container that stores up to a maximum of `N` elements of type `T` directly + * within itself, rather than separately on the heap. Useful when a dynamically + * resizeable container is needed for use in real-time code. Can be thought of + * as a hybrid between `std::array` and `std::vector`. The interface follows + * that of `std::vector` - see standard C++ documentation. + */ +template +class ArrayVector +{ +public: + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using value_type = T; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + ArrayVector() = default; + + ArrayVector(const ArrayVector& other) noexcept(std::is_nothrow_copy_constructible_v) : + m_size{other.m_size} + { + std::uninitialized_copy(other.begin(), other.end(), begin()); + } + + ArrayVector(ArrayVector&& other) noexcept(std::is_nothrow_move_constructible_v) : + m_size{other.m_size} + { + std::uninitialized_move(other.begin(), other.end(), begin()); + other.clear(); + } + + ArrayVector(size_type count, const T& value) noexcept(std::is_nothrow_copy_constructible_v) : + m_size{count} + { + assert(count <= N); + std::uninitialized_fill_n(begin(), count, value); + } + + explicit ArrayVector(size_type count) noexcept(std::is_nothrow_default_constructible_v) : + m_size{count} + { + assert(count <= N); + std::uninitialized_value_construct_n(begin(), count); + } + + template, int> = 0> + ArrayVector(It first, It last) + { + // Can't check the size first as the iterator may not be multipass + const auto end = std::uninitialized_copy(first, last, begin()); + m_size = end - begin(); + assert(m_size <= N); + } + + ArrayVector(std::initializer_list il) noexcept(std::is_nothrow_copy_constructible_v) : + m_size{il.size()} + { + assert(il.size() <= N); + std::uninitialized_copy(il.begin(), il.end(), begin()); + } + + ~ArrayVector() { std::destroy(begin(), end()); } + + ArrayVector& operator=(const ArrayVector& other) + noexcept(std::is_nothrow_copy_assignable_v && std::is_nothrow_copy_constructible_v) + { + if (this != &other) { + const auto toAssign = std::min(other.size(), size()); + const auto assignedFromEnd = other.begin() + toAssign; + const auto assignedToEnd = std::copy(other.begin(), other.begin() + toAssign, begin()); + std::destroy(assignedToEnd, end()); + std::uninitialized_copy(assignedFromEnd, other.end(), end()); + m_size = other.size(); + } + return *this; + } + + ArrayVector& operator=(ArrayVector&& other) + noexcept(std::is_nothrow_move_assignable_v && std::is_nothrow_move_constructible_v) + { + if (this != &other) { + const auto toAssign = std::min(other.size(), size()); + const auto assignedFromEnd = other.begin() + toAssign; + const auto assignedToEnd = std::move(other.begin(), other.begin() + toAssign, begin()); + std::destroy(assignedToEnd, end()); + std::uninitialized_move(assignedFromEnd, other.end(), end()); + m_size = other.size(); + other.clear(); + } + return *this; + } + + ArrayVector& operator=(std::initializer_list il) + noexcept(std::is_nothrow_copy_assignable_v && std::is_nothrow_copy_constructible_v) + { + assert(il.size() <= N); + const auto toAssign = std::min(il.size(), size()); + const auto assignedFromEnd = il.begin() + toAssign; + const auto assignedToEnd = std::copy(il.begin(), assignedFromEnd, begin()); + std::destroy(assignedToEnd, end()); + std::uninitialized_copy(assignedFromEnd, il.end(), end()); + m_size = il.size(); + return *this; + } + + void assign(size_type count, const T& value) + noexcept(std::is_nothrow_copy_assignable_v && std::is_nothrow_copy_constructible_v) + { + assert(count <= N); + const auto temp = value; + const auto toAssign = std::min(count, size()); + const auto toConstruct = count - toAssign; + const auto assignedToEnd = std::fill_n(begin(), toAssign, temp); + std::destroy(assignedToEnd, end()); + std::uninitialized_fill_n(assignedToEnd, toConstruct, temp); + m_size = count; + } + + template, int> = 0> + void assign(It first, It last) + { + // Can't check the size first as the iterator may not be multipass + auto pos = begin(); + for (; first != last && pos != end(); ++pos, ++first) { + *pos = *first; + } + std::destroy(pos, end()); + pos = std::uninitialized_copy(first, last, pos); + m_size = pos - begin(); + assert(m_size <= N); + } + + reference at(size_type index) + { + if (index >= m_size) { throw std::out_of_range{"index out of range"}; } + return data()[index]; + } + + const_reference at(size_type index) const + { + if (index >= m_size) { throw std::out_of_range{"index out of range"}; } + return data()[index]; + } + + reference operator[](size_type index) noexcept + { + assert(index < m_size); + return data()[index]; + } + + const_reference operator[](size_type index) const noexcept + { + assert(index < m_size); + return data()[index]; + } + + reference front() noexcept { return operator[](0); } + const_reference front() const noexcept { return operator[](0); } + + reference back() noexcept { return operator[](m_size - 1); } + const_reference back() const noexcept { return operator[](m_size - 1); } + + pointer data() noexcept { return *std::launder(reinterpret_cast(m_data)); } + const_pointer data() const noexcept { return *std::launder(reinterpret_cast(m_data)); } + + iterator begin() noexcept { return data(); } + const_iterator begin() const noexcept { return data(); } + const_iterator cbegin() const noexcept { return data(); } + + iterator end() noexcept { return data() + m_size; } + const_iterator end() const noexcept { return data() + m_size; } + const_iterator cend() const noexcept { return data() + m_size; } + + reverse_iterator rbegin() noexcept { return std::reverse_iterator{end()}; } + const_reverse_iterator rbegin() const noexcept { return std::reverse_iterator{end()}; } + const_reverse_iterator crbegin() const noexcept { return std::reverse_iterator{cend()}; } + + reverse_iterator rend() noexcept { return std::reverse_iterator{begin()}; } + const_reverse_iterator rend() const noexcept { return std::reverse_iterator{begin()}; } + const_reverse_iterator crend() const noexcept { return std::reverse_iterator{cbegin()}; } + + bool empty() const noexcept { return m_size == 0; } + bool full() const noexcept { return m_size == N; } + size_type size() const noexcept { return m_size; } + size_type max_size() const noexcept { return N; } + size_type capacity() const noexcept { return N; } + + void clear() noexcept + { + std::destroy(begin(), end()); + m_size = 0; + } + + iterator insert(const_iterator pos, const T& value) { return emplace(pos, value); } + iterator insert(const_iterator pos, T&& value) { return emplace(pos, std::move(value)); } + + iterator insert(const_iterator pos, size_type count, const T& value) + { + assert(m_size + count <= N); + assert(cbegin() <= pos && pos <= cend()); + const auto mutPos = begin() + (pos - cbegin()); + const auto newEnd = std::uninitialized_fill_n(end(), count, value); + std::rotate(mutPos, end(), newEnd); + m_size += count; + return mutPos; + } + + template, int> = 0> + iterator insert(const_iterator pos, It first, It last) + { + // Can't check the size first as the iterator may not be multipass + assert(cbegin() <= pos && pos <= cend()); + const auto mutPos = begin() + (pos - cbegin()); + const auto newEnd = std::uninitialized_copy(first, last, end()); + std::rotate(mutPos, end(), newEnd); + m_size = newEnd - begin(); + assert(m_size <= N); + return mutPos; + } + + iterator insert(const_iterator pos, std::initializer_list il) { return insert(pos, il.begin(), il.end()); } + + template + iterator emplace(const_iterator pos, Args&&... args) + { + assert(cbegin() <= pos && pos <= cend()); + const auto mutPos = begin() + (pos - cbegin()); + emplace_back(std::forward(args)...); + std::rotate(mutPos, end() - 1, end()); + return mutPos; + } + + iterator erase(const_iterator pos) { return erase(pos, pos + 1); } + iterator erase(const_iterator first, const_iterator last) + { + assert(cbegin() <= first && first <= last && last <= cend()); + const auto mutFirst = begin() + (first - cbegin()); + const auto mutLast = begin() + (last - cbegin()); + const auto newEnd = std::move(mutLast, end(), mutFirst); + std::destroy(newEnd, end()); + m_size = newEnd - begin(); + return mutFirst; + } + + void push_back(const T& value) { emplace_back(value); } + void push_back(T&& value) { emplace_back(std::move(value)); } + + template + reference emplace_back(Args&&... args) + { + assert(!full()); + // TODO C++20: Use std::construct_at + const auto result = new(static_cast(end())) T(std::forward(args)...); + ++m_size; + return *result; + } + + void pop_back() + { + assert(!empty()); + --m_size; + std::destroy_at(end()); + } + + void resize(size_type size) + { + if (size > N) { throw std::length_error{"size exceeds maximum size"}; } + if (size < m_size) { + std::destroy(begin() + size, end()); + } else { + std::uninitialized_value_construct(end(), begin() + size); + } + m_size = size; + } + + void resize(size_type size, const value_type& value) + { + if (size > N) { throw std::length_error{"size exceeds maximum size"}; } + if (size < m_size) { + std::destroy(begin() + size, end()); + } else { + std::uninitialized_fill(end(), begin() + size, value); + } + m_size = size; + } + + void swap(ArrayVector& other) + noexcept(std::is_nothrow_swappable_v && std::is_nothrow_move_constructible_v) + { + using std::swap; + swap(*this, other); + } + + friend void swap(ArrayVector& a, ArrayVector& b) + noexcept(std::is_nothrow_swappable_v && std::is_nothrow_move_constructible_v) + { + const auto toSwap = std::min(a.size(), b.size()); + const auto aSwapEnd = a.begin() + toSwap; + const auto bSwapEnd = b.begin() + toSwap; + std::swap_ranges(a.begin(), aSwapEnd, b.begin()); + std::uninitialized_move(aSwapEnd, a.end(), bSwapEnd); + std::uninitialized_move(bSwapEnd, b.end(), aSwapEnd); + std::destroy(aSwapEnd, a.end()); + std::destroy(bSwapEnd, b.end()); + std::swap(a.m_size, b.m_size); + } + + // TODO C++20: Replace with operator<=> + friend bool operator<(const ArrayVector& l, const ArrayVector& r) + { + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); + } + friend bool operator<=(const ArrayVector& l, const ArrayVector& r) { return !(r < l); } + friend bool operator>(const ArrayVector& l, const ArrayVector& r) { return r < l; } + friend bool operator>=(const ArrayVector& l, const ArrayVector& r) { return !(l < r); } + + friend bool operator==(const ArrayVector& l, const ArrayVector& r) + { + return std::equal(l.begin(), l.end(), r.begin(), r.end()); + } + // TODO C++20: Remove + friend bool operator!=(const ArrayVector& l, const ArrayVector& r) { return !(l == r); } + +private: + alignas(T) std::byte m_data[std::max(N * sizeof(T), std::size_t{1})]; // Intentionally a raw array + size_type m_size = 0; +}; + +} // namespace lmms + +#endif // LMMS_ARRAY_VECTOR_H diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index 46b14c4cd..61df5a77a 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -108,6 +108,9 @@ public: return m_unpitchedFrequency; } + //! Get the current per-note detuning for this note + float currentDetuning() const { return m_baseDetuning->value(); } + /*! Renders one chunk using the attached instrument into the buffer */ void play( sampleFrame* buffer ) override; diff --git a/plugins/Sf2Player/Sf2Player.cpp b/plugins/Sf2Player/Sf2Player.cpp index 1f0cb7c59..d46af5e4f 100644 --- a/plugins/Sf2Player/Sf2Player.cpp +++ b/plugins/Sf2Player/Sf2Player.cpp @@ -30,6 +30,7 @@ #include #include +#include "ArrayVector.h" #include "AudioEngine.h" #include "ConfigManager.h" #include "FileDialog.h" @@ -71,17 +72,47 @@ Plugin::Descriptor PLUGIN_EXPORT sf2player_plugin_descriptor = } +/** + * A non-owning reference to a single FluidSynth voice, for tracking whether the + * referenced voice is still the same voice that was passed to the constructor. + */ +class FluidVoice +{ +public: + //! Create a reference to the voice currently pointed at by `voice`. + explicit FluidVoice(fluid_voice_t* voice) : + m_voice{voice}, + m_id{fluid_voice_get_id(voice)} + { } + + //! Get a pointer to the referenced voice. + fluid_voice_t* get() const noexcept { return m_voice; } + + //! Test whether this object still refers to the original voice. + bool isValid() const + { + return fluid_voice_get_id(m_voice) == m_id && fluid_voice_is_playing(m_voice); + } + +private: + fluid_voice_t* m_voice; + unsigned int m_id; +}; struct Sf2PluginData { int midiNote; int lastPanning; float lastVelocity; - fluid_voice_t * fluidVoice; + // The soundfonts I checked used at most two voices per note, so space for + // four should be safe. This may need to be increased if a soundfont with + // more voices per note is found. + ArrayVector fluidVoices; bool isNew; f_cnt_t offset; bool noteOffSent; -} ; + panning_t panning; +}; @@ -681,10 +712,10 @@ void Sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * ) pluginData->midiNote = midiNote; pluginData->lastPanning = 0; pluginData->lastVelocity = _n->midiVelocity( baseVelocity ); - pluginData->fluidVoice = nullptr; pluginData->isNew = true; pluginData->offset = _n->offset(); pluginData->noteOffSent = false; + pluginData->panning = _n->getPanning(); _n->m_pluginData = pluginData; @@ -703,6 +734,17 @@ void Sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * ) m_playingNotes.append( _n ); m_playingNotesMutex.unlock(); } + + // Update the pitch of all the voices + if (const auto data = static_cast(_n->m_pluginData)) { + const auto detuning = _n->currentDetuning(); + for (const auto& voice : data->fluidVoices) { + if (voice.isValid()) { + fluid_voice_gen_set(voice.get(), GEN_COARSETUNE, detuning); + fluid_voice_update_param(voice.get(), GEN_COARSETUNE); + } + } + } } @@ -715,35 +757,47 @@ void Sf2Instrument::noteOn( Sf2PluginData * n ) const int poly = fluid_synth_get_polyphony( m_synth ); #ifndef _MSC_VER fluid_voice_t* voices[poly]; - unsigned int id[poly]; #else const auto voices = static_cast(_alloca(poly * sizeof(fluid_voice_t*))); - const auto id = static_cast(_alloca(poly * sizeof(unsigned int))); #endif - fluid_synth_get_voicelist( m_synth, voices, poly, -1 ); - for( int i = 0; i < poly; ++i ) - { - id[i] = 0; - } - for( int i = 0; i < poly && voices[i]; ++i ) - { - id[i] = fluid_voice_get_id( voices[i] ); - } fluid_synth_noteon( m_synth, m_channel, n->midiNote, n->lastVelocity ); - // get new voice and save it - fluid_synth_get_voicelist( m_synth, voices, poly, -1 ); - for( int i = 0; i < poly && voices[i]; ++i ) + // Get any new voices and store them in the plugin data + fluid_synth_get_voicelist(m_synth, voices, poly, -1); + for (int i = 0; i < poly && voices[i] && !n->fluidVoices.full(); ++i) { - const unsigned int newID = fluid_voice_get_id( voices[i] ); - if( id[i] != newID || newID == 0 ) - { - n->fluidVoice = voices[i]; - break; + const auto voice = voices[i]; + // FluidSynth stops voices with the same channel and pitch upon note-on, + // so voices with the current channel and pitch are playing this note. + if (fluid_voice_get_channel(voice) == m_channel + && fluid_voice_get_key(voice) == n->midiNote + && fluid_voice_is_on(voice) + ) { + n->fluidVoices.emplace_back(voices[i]); } } +#if FLUIDSYNTH_VERSION_MAJOR >= 2 + // Smallest balance value that results in full attenuation of one channel. + // Corresponds to internal FluidSynth macro `FLUID_CB_AMP_SIZE`. + constexpr static auto maxBalance = 1441.f; + // Convert panning from linear to exponential for FluidSynth + const auto panning = n->panning; + const auto factor = 1.f - std::abs(panning) / static_cast(PanningRight); + const auto balance = std::copysign( + factor <= 0 ? maxBalance : std::min(-200.f * std::log10(factor), maxBalance), + panning + ); + // Set note panning on all the voices + for (const auto& voice : n->fluidVoices) { + if (voice.isValid()) { + fluid_voice_gen_set(voice.get(), GEN_CUSTOM_BALANCE, balance); + fluid_voice_update_param(voice.get(), GEN_CUSTOM_BALANCE); + } + } +#endif + m_synthMutex.unlock(); m_notesRunningMutex.lock(); @@ -859,6 +913,7 @@ void Sf2Instrument::play( sampleFrame * _working_buffer ) void Sf2Instrument::renderFrames( f_cnt_t frames, sampleFrame * buf ) { m_synthMutex.lock(); + fluid_synth_get_gain(m_synth); // This flushes voice updates as a side effect if( m_internalSampleRate < Engine::audioEngine()->processingSampleRate() && m_srcState != nullptr ) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6ff9c41e9..514bf7815 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,6 +19,7 @@ ADD_EXECUTABLE(tests QTestSuite.cpp $ + src/core/ArrayVectorTest.cpp src/core/AutomatableModelTest.cpp src/core/ProjectVersionTest.cpp src/core/RelativePathsTest.cpp diff --git a/tests/main.cpp b/tests/main.cpp index 6d375e6c6..c1a5b5a10 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -16,7 +16,7 @@ int main(int argc, char* argv[]) int failed = 0; for (QTestSuite*& suite : QTestSuite::suites()) { - failed += QTest::qExec(suite, argc, argv); + if (QTest::qExec(suite, argc, argv) != 0) { ++failed; } } qDebug() << "<<" << failed << "out of"< +#include + +#include "QTestSuite.h" + +using lmms::ArrayVector; + +struct ShouldNotConstruct +{ + ShouldNotConstruct() { QFAIL("should not construct"); } +}; + +struct ShouldNotDestruct +{ + ~ShouldNotDestruct() { QFAIL("should not destruct"); } +}; + +enum class Construction { Default, Copy, Move, CopyAssign, MoveAssign }; + +struct Constructible +{ + Constructible() : construction{Construction::Default} {} + Constructible(const Constructible&) : construction{Construction::Copy} {} + Constructible(Constructible&&) : construction{Construction::Move} {} + Constructible& operator=(const Constructible&) { construction = Construction::CopyAssign; return *this; } + Constructible& operator=(Constructible&&) { construction = Construction::MoveAssign; return *this; } + Construction construction; +}; + +struct DestructorCheck +{ + ~DestructorCheck() { *destructed = true; } + bool* destructed; +}; + +class ArrayVectorTest : QTestSuite +{ + Q_OBJECT + +private slots: + void defaultConstructorTest() + { + // Ensure no elements are constructed + const auto v = ArrayVector(); + // Ensure the container is empty + QVERIFY(v.empty()); + } + + void copyConstructorTest() + { + { + // Ensure all elements are copy constructed + const auto v = ArrayVector{{}}; + const auto copy = v; + for (const auto& element : copy) { + QCOMPARE(element.construction, Construction::Copy); + } + } + { + // Ensure corresponding elements are used + const auto v = ArrayVector{1, 2, 3}; + const auto copy = v; + const auto expected = std::array{1, 2, 3}; + QVERIFY(std::equal(copy.begin(), copy.end(), expected.begin(), expected.end())); + } + } + + void moveConstructorTest() + { + { + // Ensure all elements are move constructed + auto v = ArrayVector{{}}; + const auto moved = std::move(v); + for (const auto& element : moved) { + QCOMPARE(element.construction, Construction::Move); + } + } + { + // Ensure corresponding elements are used + auto v = ArrayVector{1, 2, 3}; + const auto moved = std::move(v); + const auto expected = std::array{1, 2, 3}; + QVERIFY(std::equal(moved.begin(), moved.end(), expected.begin(), expected.end())); + // Move construction should leave the source empty + QVERIFY(v.empty()); + } + } + + void fillValueConstructorTest() + { + // Ensure all elements are copy constructed + const auto v = ArrayVector(1, {}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Copy); + } + // Ensure the container has the correct size + QCOMPARE(v.size(), std::size_t{1}); + } + + void fillDefaultConstructorTest() + { + // Ensure all elements are copy constructed + const auto v = ArrayVector(1); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Default); + } + // Ensure the container has the correct size + QCOMPARE(v.size(), std::size_t{1}); + } + + void rangeConstructorTest() + { + { + // Ensure the elements are copy constructed from normal iterators + const auto data = std::array{Constructible{}}; + const auto v = ArrayVector(data.begin(), data.end()); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Copy); + } + } + { + // Ensure the elements are move constructed from move iterators + auto data = std::array{Constructible{}}; + const auto v = ArrayVector( + std::move_iterator{data.begin()}, std::move_iterator{data.end()}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Move); + } + } + { + // Ensure corresponding elements are used + const auto data = std::array{1, 2, 3}; + const auto v = ArrayVector(data.begin(), data.end()); + QVERIFY(std::equal(v.begin(), v.end(), data.begin(), data.end())); + } + } + + void initializerListConstructorTest() + { + // Ensure the container is constructed with the correct data + const auto v = ArrayVector{1, 2, 3}; + const auto expected = std::array{1, 2, 3}; + QVERIFY(std::equal(v.begin(), v.end(), expected.begin(), expected.end())); + } + + void destructorTest() + { + { + // Should not call destructors for space without elements + const auto v = ArrayVector{}; + } + { + // Should call destructors for all elements + auto destructed = false; + { + const auto v = ArrayVector{{&destructed}}; + } + QVERIFY(destructed); + } + } + + void copyAssignmentTest() + { + { + // Self-assignment should not change the contents + auto v = ArrayVector{1, 2, 3}; + const auto oldValue = v; + v = v; + QCOMPARE(v, oldValue); + } + { + // Assignment to a larger container should copy assign + const auto src = ArrayVector(3); + auto dst = ArrayVector(5); + dst = src; + QCOMPARE(dst.size(), std::size_t{3}); + for (const auto& element : dst) { + QCOMPARE(element.construction, Construction::CopyAssign); + } + } + { + // Assignment to a smaller container should copy construct + const auto src = ArrayVector(3); + auto dst = ArrayVector{}; + dst = src; + QCOMPARE(dst.size(), std::size_t{3}); + for (const auto& element : dst) { + QCOMPARE(element.construction, Construction::Copy); + } + } + { + // Ensure corresponding elements are used + const auto src = ArrayVector{1, 2, 3}; + auto dst = ArrayVector{}; + dst = src; + QCOMPARE(dst, (ArrayVector{1, 2, 3})); + } + } + + void moveAssignmentTest() + { + { + // Self-assignment should not change the contents + auto v = ArrayVector{1, 2, 3}; + const auto oldValue = v; + v = std::move(v); + QCOMPARE(v, oldValue); + } + { + // Assignment to a larger container should move assign + auto src = ArrayVector(3); + auto dst = ArrayVector(5); + dst = std::move(src); + QCOMPARE(dst.size(), std::size_t{3}); + for (const auto& element : dst) { + QCOMPARE(element.construction, Construction::MoveAssign); + } + } + { + // Assignment to a smaller container should move construct + auto src = ArrayVector(3); + auto dst = ArrayVector{}; + dst = std::move(src); + QCOMPARE(dst.size(), std::size_t{3}); + for (const auto& element : dst) { + QCOMPARE(element.construction, Construction::Move); + } + } + { + // Ensure corresponding elements are used + auto src = ArrayVector{1, 2, 3}; + auto dst = ArrayVector{}; + dst = std::move(src); + QCOMPARE(dst, (ArrayVector{1, 2, 3})); + } + } + + void initializerListAssignmentTest() + { + { + // Assignment to a larger container should copy assign + auto v = ArrayVector(2); + v = {Constructible{}}; + QCOMPARE(v.size(), std::size_t{1}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::CopyAssign); + } + } + { + // Assignment to a smaller container should copy construct + auto v = ArrayVector{}; + v = {Constructible{}}; + QCOMPARE(v.size(), std::size_t{1}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Copy); + } + } + { + // Ensure corresponding elements are used + auto v = ArrayVector{}; + v = {1, 2, 3}; + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + } + + void fillValueAssignTest() + { + { + // Assignment to a larger container should copy assign + auto v = ArrayVector(5); + v.assign(3, {}); + QCOMPARE(v.size(), std::size_t{3}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::CopyAssign); + } + } + { + // Assignment to a smaller container should copy construct + auto v = ArrayVector{}; + v.assign(3, {}); + QCOMPARE(v.size(), std::size_t{3}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Copy); + } + } + { + // Ensure correct value is filled + auto v = ArrayVector{}; + v.assign(3, 1); + QCOMPARE(v, (ArrayVector{1, 1, 1})); + } + } + + void rangeAssignTest() + { + { + // Assignment to a larger container should copy assign + const auto data = std::array{Constructible{}}; + auto v = ArrayVector(2); + v.assign(data.begin(), data.end()); + QCOMPARE(v.size(), std::size_t{1}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::CopyAssign); + } + } + { + // Assignment to a smaller container should copy construct + const auto data = std::array{Constructible{}}; + auto v = ArrayVector{}; + v.assign(data.begin(), data.end()); + QCOMPARE(v.size(), std::size_t{1}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Copy); + } + } + { + // Ensure correct value is filled + const auto data = std::array{1, 2, 3}; + auto v = ArrayVector{}; + v.assign(data.begin(), data.end()); + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + } + + void atTest() + { + { + // Non-const version + auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v.at(1), 2); + QVERIFY_EXCEPTION_THROWN(v.at(3), std::out_of_range); + } + { + // Const version + const auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v.at(1), 2); + QVERIFY_EXCEPTION_THROWN(v.at(3), std::out_of_range); + } + } + + void subscriptTest() + { + { + // Non-const version + auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v[1], 2); + } + { + // Const version + const auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v[1], 2); + } + } + + void frontTest() + { + { + // Non-const version + auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v.front(), 1); + } + { + // Const version + const auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v.front(), 1); + } + } + + void backTest() + { + { + // Non-const version + auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v.back(), 3); + } + { + // Const version + const auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v.back(), 3); + } + } + + void dataTest() + { + { + // Non-const version + auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v.data(), &v.front()); + } + { + // Const version + const auto v = ArrayVector{1, 2, 3}; + QCOMPARE(v.data(), &v.front()); + } + } + + void beginEndTest() + { + const auto expected = std::array{1, 2, 3}; + { + // Non-const version + auto v = ArrayVector{1, 2, 3}; + QVERIFY(std::equal(v.begin(), v.end(), expected.begin(), expected.end())); + QVERIFY(std::equal(v.cbegin(), v.cend(), expected.begin(), expected.end())); + } + { + // Const version + const auto v = ArrayVector{1, 2, 3}; + QVERIFY(std::equal(v.begin(), v.end(), expected.begin(), expected.end())); + } + } + + void rbeginRendTest() + { + const auto expected = std::array{3, 2, 1}; + { + // Non-const version + auto v = ArrayVector{1, 2, 3}; + QVERIFY(std::equal(v.rbegin(), v.rend(), expected.begin(), expected.end())); + QVERIFY(std::equal(v.crbegin(), v.crend(), expected.begin(), expected.end())); + } + { + // Const version + const auto v = ArrayVector{1, 2, 3}; + QVERIFY(std::equal(v.rbegin(), v.rend(), expected.begin(), expected.end())); + } + } + + void emptyFullSizeMaxCapacityTest() + { + auto v = ArrayVector{}; + QVERIFY(v.empty()); + QVERIFY(!v.full()); + QCOMPARE(v.size(), std::size_t{0}); + QCOMPARE(v.max_size(), std::size_t{2}); + QCOMPARE(v.capacity(), std::size_t{2}); + + v.push_back(1); + QVERIFY(!v.empty()); + QVERIFY(!v.full()); + QCOMPARE(v.size(), std::size_t{1}); + QCOMPARE(v.max_size(), std::size_t{2}); + QCOMPARE(v.capacity(), std::size_t{2}); + + v.push_back(2); + QVERIFY(!v.empty()); + QVERIFY(v.full()); + QCOMPARE(v.size(), std::size_t{2}); + QCOMPARE(v.max_size(), std::size_t{2}); + QCOMPARE(v.capacity(), std::size_t{2}); + + auto empty = ArrayVector{}; + QVERIFY(empty.empty()); + QVERIFY(empty.full()); + QCOMPARE(empty.size(), std::size_t{0}); + QCOMPARE(empty.max_size(), std::size_t{0}); + QCOMPARE(empty.capacity(), std::size_t{0}); + } + + void insertValueTest() + { + { + // Copy + const auto data = Constructible{}; + auto v = ArrayVector{}; + v.insert(v.cbegin(), data); + QCOMPARE(v.size(), std::size_t{1}); + QCOMPARE(v[0].construction, Construction::Copy); + } + { + // Move + auto v = ArrayVector{}; + v.insert(v.cbegin(), Constructible{}); + QCOMPARE(v.size(), std::size_t{1}); + QCOMPARE(v[0].construction, Construction::Move); + } + { + // Ensure the correct value is used (copy) + const auto data = 1; + auto v = ArrayVector{2, 3}; + v.insert(v.cbegin(), data); + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + { + // Ensure the correct value is used (move) + auto v = ArrayVector{2, 3}; + v.insert(v.cbegin(), 1); + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + } + + void insertFillValueTest() + { + { + // Insertion should copy construct + auto v = ArrayVector{}; + v.insert(v.cbegin(), 3, {}); + QCOMPARE(v.size(), std::size_t{3}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Copy); + } + } + { + // Ensure correct value is filled + auto v = ArrayVector{1, 3}; + v.insert(v.cbegin() + 1, 3, 2); + QCOMPARE(v, (ArrayVector{1, 2, 2, 2, 3})); + } + } + + void insertRangeTest() + { + { + // Insertion should copy construct + const auto data = std::array{Constructible{}}; + auto v = ArrayVector{}; + v.insert(v.cbegin(), data.begin(), data.end()); + QCOMPARE(v.size(), std::size_t{1}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Copy); + } + } + { + // Ensure correct value is filled + const auto data = std::array{2, 3}; + auto v = ArrayVector{1, 4}; + v.insert(v.cbegin() + 1, data.begin(), data.end()); + QCOMPARE(v, (ArrayVector{1, 2, 3, 4})); + } + } + + void insertInitializerListTest() + { + { + // Insertion should copy construct + auto v = ArrayVector{}; + v.insert(v.cbegin(), {Constructible{}}); + QCOMPARE(v.size(), std::size_t{1}); + for (const auto& element : v) { + QCOMPARE(element.construction, Construction::Copy); + } + } + { + // Ensure corresponding elements are used + auto v = ArrayVector{1, 4}; + v.insert(v.cbegin() + 1, {2, 3}); + QCOMPARE(v, (ArrayVector{1, 2, 3, 4})); + } + } + + void emplaceTest() + { + { + // Ensure the value is constructed in-place + auto v = ArrayVector{}; + v.emplace(v.cbegin()); + QCOMPARE(v.size(), std::size_t{1}); + QCOMPARE(v[0].construction, Construction::Default); + } + { + // Ensure the correct value is used (move) + auto v = ArrayVector{2, 3}; + v.emplace(v.cbegin(), 1); + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + } + + void eraseTest() + { + { + // Ensure destructors are run + auto destructed = false; + auto v = ArrayVector{{&destructed}}; + v.erase(v.cbegin()); + QVERIFY(destructed); + } + { + // Ensure the result is correct + auto v = ArrayVector{10, 1, 2, 3}; + v.erase(v.cbegin()); + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + } + + void eraseRangeTest() + { + { + // Ensure destructors are run + auto destructed = false; + auto v = ArrayVector{{&destructed}}; + v.erase(v.cbegin(), v.cend()); + QVERIFY(destructed); + } + { + // Ensure the result is correct + auto v = ArrayVector{1, 20, 21, 2, 3}; + v.erase(v.cbegin() + 1, v.cbegin() + 3); + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + } + + void pushBackTest() + { + { + // Copy + const auto data = Constructible{}; + auto v = ArrayVector{}; + v.push_back(data); + QCOMPARE(v.size(), std::size_t{1}); + QCOMPARE(v[0].construction, Construction::Copy); + } + { + // Move + auto v = ArrayVector{}; + v.push_back({}); + QCOMPARE(v.size(), std::size_t{1}); + QCOMPARE(v[0].construction, Construction::Move); + } + { + // Ensure the correct value is used (copy) + const auto data = 3; + auto v = ArrayVector{1, 2}; + v.push_back(data); + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + { + // Ensure the correct value is used (move) + auto v = ArrayVector{1, 2}; + v.push_back(3); + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + } + + void emplaceBackTest() + { + { + // Ensure the value is constructed in-place + auto v = ArrayVector{}; + v.emplace_back(); + QCOMPARE(v.size(), std::size_t{1}); + QCOMPARE(v[0].construction, Construction::Default); + } + { + // Ensure the correct value is used (move) + auto v = ArrayVector{1, 2}; + v.emplace_back(3); + QCOMPARE(v, (ArrayVector{1, 2, 3})); + } + } + + void popBackTest() + { + { + // Ensure destructors are run + auto destructed = false; + auto v = ArrayVector{{&destructed}}; + v.pop_back(); + QVERIFY(destructed); + } + { + // Ensure the result is correct + auto v = ArrayVector{1, 2, 3}; + v.pop_back(); + QCOMPARE(v, (ArrayVector{1, 2})); + } + } + + void resizeDefaultTest() + { + { + // Smaller + auto destructed = false; + auto v = ArrayVector{{&destructed}}; + QCOMPARE(v.size(), std::size_t{1}); + v.resize(0); + QCOMPARE(v.size(), std::size_t{0}); + QVERIFY(destructed); + } + { + // Bigger + auto v = ArrayVector{}; + QCOMPARE(v.size(), std::size_t{0}); + v.resize(1); + QCOMPARE(v.size(), std::size_t{1}); + QCOMPARE(v[0].construction, Construction::Default); + } + { + // Too big + auto v = ArrayVector{}; + QVERIFY_EXCEPTION_THROWN(v.resize(2), std::length_error); + } + } + + void resizeValueTest() + { + { + // Smaller + auto dummy = false; + auto destructed = false; + auto v = ArrayVector{{&destructed}}; + QCOMPARE(v.size(), std::size_t{1}); + v.resize(0, {&dummy}); + QCOMPARE(v.size(), std::size_t{0}); + QVERIFY(destructed); + } + { + // Bigger + auto v = ArrayVector{}; + QCOMPARE(v.size(), std::size_t{0}); + v.resize(1, {}); + QCOMPARE(v.size(), std::size_t{1}); + QCOMPARE(v[0].construction, Construction::Copy); + } + { + // Too big + auto v = ArrayVector{}; + QVERIFY_EXCEPTION_THROWN(v.resize(2), std::length_error); + } + { + // Ensure the correct value is used + auto v = ArrayVector{}; + v.resize(1, 1); + QCOMPARE(v, (ArrayVector{1})); + } + } + + void clearTest() + { + { + // Ensure destructors are run + auto destructed = false; + auto v = ArrayVector{{&destructed}}; + v.clear(); + QVERIFY(destructed); + } + { + // Ensure the result is correct + auto v = ArrayVector{1, 2, 3}; + v.clear(); + QCOMPARE(v, (ArrayVector{})); + } + } + + void memberSwapTest() + { + auto a = ArrayVector{1, 2, 3, 4}; + auto b = ArrayVector{2, 4, 6}; + + const auto aOriginal = a; + const auto bOriginal = b; + + a.swap(b); + + QCOMPARE(a, bOriginal); + QCOMPARE(b, aOriginal); + } + + void freeSwapTest() + { + auto a = ArrayVector{1, 2, 3, 4}; + auto b = ArrayVector{2, 4, 6}; + + const auto aOriginal = a; + const auto bOriginal = b; + + swap(a, b); + + QCOMPARE(a, bOriginal); + QCOMPARE(b, aOriginal); + } + + void comparisonTest() + { + const auto v = ArrayVector{1, 2, 3}; + const auto l = ArrayVector{1, 2, 2}; + const auto e = ArrayVector{1, 2, 3}; + const auto g = ArrayVector{1, 3, 3}; + + QVERIFY(l < v); + QVERIFY(!(e < v)); + QVERIFY(!(g < v)); + + QVERIFY(l <= v); + QVERIFY(e <= v); + QVERIFY(!(g <= v)); + + QVERIFY(!(l > v)); + QVERIFY(!(e > v)); + QVERIFY(g > v); + + QVERIFY(!(l >= v)); + QVERIFY(e >= v); + QVERIFY(g >= v); + + QVERIFY(!(l == v)); + QVERIFY(e == v); + QVERIFY(!(g == v)); + + QVERIFY(l != v); + QVERIFY(!(e != v)); + QVERIFY(g != v); + } +} ArrayVectorTests; + +#include "ArrayVectorTest.moc" From 005ee47d439f0ccf023de4c73f552f8c5119ec63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Thu, 31 Aug 2023 12:17:00 -0300 Subject: [PATCH 40/45] pitch wheel recording in the detune pattern (#6297) --- include/Note.h | 2 +- include/NotePlayHandle.h | 2 +- src/core/NotePlayHandle.cpp | 16 +++++++++++----- src/gui/editors/PianoRoll.cpp | 4 ++-- src/tracks/InstrumentTrack.cpp | 10 +++++++--- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/include/Note.h b/include/Note.h index 5e3a1b8a2..2df196af2 100644 --- a/include/Note.h +++ b/include/Note.h @@ -91,7 +91,7 @@ const int DefaultMiddleKey = Octave::Octave_4 + Key::C; const int DefaultBaseKey = Octave::Octave_4 + Key::A; const float DefaultBaseFreq = 440.f; -const float MaxDetuning = 4 * 12.0f; +const float MaxDetuning = 5 * 12.0f; diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index 61df5a77a..7105d6672 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -248,7 +248,7 @@ public: } /*! Process note detuning automation */ - void processTimePos( const TimePos& time ); + void processTimePos(const TimePos& time, float pitchValue, bool isRecording); /*! Updates total length (m_frames) depending on a new tempo */ void resize( const bpm_t newTempo ); diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 70007ebf1..eb9c7ddbf 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -557,14 +557,20 @@ void NotePlayHandle::updateFrequency() -void NotePlayHandle::processTimePos( const TimePos& time ) +void NotePlayHandle::processTimePos(const TimePos& time, float pitchValue, bool isRecording) { - if( detuning() && time >= songGlobalParentOffset()+pos() ) + if (!detuning() || time < songGlobalParentOffset() + pos()) { return; } + + if (isRecording && m_origin == Origin::MidiInput) { - const float v = detuning()->automationClip()->valueAt( time - songGlobalParentOffset() - pos() ); - if( !typeInfo::isEqual( v, m_baseDetuning->value() ) ) + detuning()->automationClip()->recordValue(time - songGlobalParentOffset() - pos(), pitchValue / 100); + } + else + { + const float v = detuning()->automationClip()->valueAt(time - songGlobalParentOffset() - pos()); + if (!typeInfo::isEqual(v, m_baseDetuning->value())) { - m_baseDetuning->setValue( v ); + m_baseDetuning->setValue(v); updateFrequency(); } } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 0cc825725..05900bf0b 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -4134,9 +4134,9 @@ void PianoRoll::finishRecordNote(const Note & n ) { if( it->key() == n.key() ) { - Note n1( n.length(), it->pos(), + Note n1(n.length(), it->pos(), it->key(), it->getVolume(), - it->getPanning() ); + it->getPanning(), n.detuning()); n1.quantizeLength( quantization() ); m_midiClip->addNote( n1 ); update(); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 84b73614b..a4de188a5 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -28,6 +28,7 @@ #include "ConfigManager.h" #include "ControllerConnection.h" #include "DataFile.h" +#include "GuiApplication.h" #include "Mixer.h" #include "InstrumentTrackView.h" #include "Instrument.h" @@ -37,6 +38,7 @@ #include "MixHelpers.h" #include "PatternStore.h" #include "PatternTrack.h" +#include "PianoRoll.h" #include "Pitch.h" #include "Song.h" @@ -72,6 +74,7 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) : m_microtuner() { m_pitchModel.setCenterValue( 0 ); + m_pitchModel.setStrictStepSize(true); m_panningModel.setCenterValue( DefaultPanning ); m_baseNoteModel.setInitValue( DefaultKey ); m_firstKeyModel.setInitValue(0); @@ -341,9 +344,10 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim NotePlayHandleManager::acquire( this, offset, typeInfo::max() / 2, - Note( TimePos(), TimePos(), event.key(), event.volume( midiPort()->baseVelocity() ) ), + Note(TimePos(), Engine::getSong()->getPlayPos(Engine::getSong()->playMode()), + event.key(), event.volume(midiPort()->baseVelocity())), nullptr, event.channel(), - NotePlayHandle::Origin::MidiInput ); + NotePlayHandle::Origin::MidiInput); m_notes[event.key()] = nph; if( ! Engine::audioEngine()->addPlayHandle( nph ) ) { @@ -710,7 +714,7 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames, // Handle automation: detuning for (const auto& processHandle : m_processHandles) { - processHandle->processTimePos(_start); + processHandle->processTimePos(_start, m_pitchModel.value(), gui::GuiApplication::instance()->pianoRoll()->isRecording()); } if ( clips.size() == 0 ) From 8a94fb36818b7af8eb6fa5815c4836f19e7c2210 Mon Sep 17 00:00:00 2001 From: Dalton Messmer <33463986+messmerd@users.noreply.github.com> Date: Thu, 31 Aug 2023 12:55:02 -0400 Subject: [PATCH 41/45] Fix std::vector refactor mistake (#6836) * Use std::vector const reference instead of copying * Add assertions * Simplification --- include/MidiClip.h | 2 +- include/PianoRoll.h | 4 ++-- src/core/EffectChain.cpp | 3 +++ src/core/Mixer.cpp | 4 ++-- src/core/PatternStore.cpp | 12 +++++------ src/core/RenderManager.cpp | 4 ++-- src/core/Song.cpp | 2 +- src/gui/MixerView.cpp | 4 ++-- src/gui/clips/ClipView.cpp | 4 +++- src/gui/editors/PatternEditor.cpp | 4 ++-- src/gui/editors/PianoRoll.cpp | 29 ++++++++++++--------------- src/gui/tracks/TrackContentWidget.cpp | 4 ++-- src/tracks/MidiClip.cpp | 4 ++-- 13 files changed, 41 insertions(+), 39 deletions(-) diff --git a/include/MidiClip.h b/include/MidiClip.h index 43b322f80..c2287bd00 100644 --- a/include/MidiClip.h +++ b/include/MidiClip.h @@ -79,7 +79,7 @@ public: void setStep( int step, bool enabled ); // Split the list of notes on the given position - void splitNotes(NoteVector notes, TimePos pos); + void splitNotes(const NoteVector& notes, TimePos pos); // clip-type stuff inline Type type() const diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 9f3bbcd7d..38788180f 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -308,9 +308,9 @@ private: TimePos newNoteLen() const; void shiftPos(int amount); - void shiftPos(NoteVector notes, int amount); + void shiftPos(const NoteVector& notes, int amount); void shiftSemiTone(int amount); - void shiftSemiTone(NoteVector notes, int amount); + void shiftSemiTone(const NoteVector& notes, int amount); bool isSelection() const; int selectionCount() const; void testPlayNote( Note * n ); diff --git a/src/core/EffectChain.cpp b/src/core/EffectChain.cpp index b07a7227b..4da5c5197 100644 --- a/src/core/EffectChain.cpp +++ b/src/core/EffectChain.cpp @@ -25,6 +25,7 @@ #include +#include #include "EffectChain.h" #include "Effect.h" @@ -162,6 +163,7 @@ void EffectChain::moveDown( Effect * _effect ) if (_effect != m_effects.back()) { auto it = std::find(m_effects.begin(), m_effects.end(), _effect); + assert(it != m_effects.end()); std::swap(*std::next(it), *it); } } @@ -174,6 +176,7 @@ void EffectChain::moveUp( Effect * _effect ) if (_effect != m_effects.front()) { auto it = std::find(m_effects.begin(), m_effects.end(), _effect); + assert(it != m_effects.end()); std::swap(*std::prev(it), *it); } } diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index e14660e1f..59c2dd72e 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -394,8 +394,8 @@ void Mixer::moveChannelLeft( int index ) else if (m_lastSoloed == b) { m_lastSoloed = a; } // go through every instrument and adjust for the channel index change - TrackContainer::TrackList songTrackList = Engine::getSong()->tracks(); - TrackContainer::TrackList patternTrackList = Engine::patternStore()->tracks(); + const TrackContainer::TrackList& songTrackList = Engine::getSong()->tracks(); + const TrackContainer::TrackList& patternTrackList = Engine::patternStore()->tracks(); for (const auto& trackList : {songTrackList, patternTrackList}) { diff --git a/src/core/PatternStore.cpp b/src/core/PatternStore.cpp index c5a352139..6af434f65 100644 --- a/src/core/PatternStore.cpp +++ b/src/core/PatternStore.cpp @@ -61,7 +61,7 @@ bool PatternStore::play(TimePos start, fpp_t frames, f_cnt_t offset, int clipNum start = start % (lengthOfPattern(clipNum) * TimePos::ticksPerBar()); - TrackList tl = tracks(); + const TrackList& tl = tracks(); for (Track * t : tl) { if (t->play(start, frames, offset, clipNum)) @@ -117,7 +117,7 @@ int PatternStore::numOfPatterns() const void PatternStore::removePattern(int pattern) { - TrackList tl = tracks(); + const TrackList& tl = tracks(); for (Track * t : tl) { delete t->getClip(pattern); @@ -134,7 +134,7 @@ void PatternStore::removePattern(int pattern) void PatternStore::swapPattern(int pattern1, int pattern2) { - TrackList tl = tracks(); + const TrackList& tl = tracks(); for (Track * t : tl) { t->swapPositionOfClips(pattern1, pattern2); @@ -159,7 +159,7 @@ void PatternStore::updatePatternTrack(Clip* clip) void PatternStore::fixIncorrectPositions() { - TrackList tl = tracks(); + const TrackList& tl = tracks(); for (Track * t : tl) { for (int i = 0; i < numOfPatterns(); ++i) @@ -215,7 +215,7 @@ void PatternStore::updateComboBox() void PatternStore::currentPatternChanged() { // now update all track-labels (the current one has to become white, the others gray) - TrackList tl = Engine::getSong()->tracks(); + const TrackList& tl = Engine::getSong()->tracks(); for (Track * t : tl) { if (t->type() == Track::Type::Pattern) @@ -230,7 +230,7 @@ void PatternStore::currentPatternChanged() void PatternStore::createClipsForPattern(int pattern) { - TrackList tl = tracks(); + const TrackList& tl = tracks(); for (Track * t : tl) { t->createClipsForPattern(pattern); diff --git a/src/core/RenderManager.cpp b/src/core/RenderManager.cpp index 969cad15b..9f6192039 100644 --- a/src/core/RenderManager.cpp +++ b/src/core/RenderManager.cpp @@ -97,7 +97,7 @@ void RenderManager::renderNextTrack() // Render the song into individual tracks void RenderManager::renderTracks() { - const TrackContainer::TrackList & tl = Engine::getSong()->tracks(); + const TrackContainer::TrackList& tl = Engine::getSong()->tracks(); // find all currently unnmuted tracks -- we want to render these. for (const auto& tk : tl) @@ -112,7 +112,7 @@ void RenderManager::renderTracks() } } - const TrackContainer::TrackList t2 = Engine::patternStore()->tracks(); + const TrackContainer::TrackList& t2 = Engine::patternStore()->tracks(); for (const auto& tk : t2) { Track::Type type = tk->type(); diff --git a/src/core/Song.cpp b/src/core/Song.cpp index e8073f225..3a735331c 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -383,7 +383,7 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp } values = container->automatedValuesAt(timeStart, clipNum); - TrackList tracks = container->tracks(); + const TrackList& tracks = container->tracks(); Track::clipVector clips; for (Track* track : tracks) diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp index 0edebcb8a..dff19ca3e 100644 --- a/src/gui/MixerView.cpp +++ b/src/gui/MixerView.cpp @@ -248,8 +248,8 @@ void MixerView::refreshDisplay() // update the and max. channel number for every instrument void MixerView::updateMaxChannelSelector() { - TrackContainer::TrackList songTracks = Engine::getSong()->tracks(); - TrackContainer::TrackList patternStoreTracks = Engine::patternStore()->tracks(); + const TrackContainer::TrackList& songTracks = Engine::getSong()->tracks(); + const TrackContainer::TrackList& patternStoreTracks = Engine::patternStore()->tracks(); for (const auto& trackList : {songTracks, patternStoreTracks}) { diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index 7a7a19c11..de7690d26 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -25,6 +25,7 @@ #include "ClipView.h" #include +#include #include #include @@ -545,6 +546,7 @@ DataFile ClipView::createClipDataFiles( // Insert into the dom under the "clips" element Track* clipTrack = clipView->m_trackView->getTrack(); int trackIndex = std::distance(tc->tracks().begin(), std::find(tc->tracks().begin(), tc->tracks().end(), clipTrack)); + assert(trackIndex != tc->tracks().size()); QDomElement clipElement = dataFile.createElement("clip"); clipElement.setAttribute( "trackIndex", trackIndex ); clipElement.setAttribute( "trackType", static_cast(clipTrack->type()) ); @@ -1308,7 +1310,7 @@ void ClipView::mergeClips(QVector clipvs) continue; } - NoteVector currentClipNotes = mcView->getMidiClip()->notes(); + const NoteVector& currentClipNotes = mcView->getMidiClip()->notes(); TimePos mcViewPos = mcView->getMidiClip()->startPosition(); for (Note* note: currentClipNotes) diff --git a/src/gui/editors/PatternEditor.cpp b/src/gui/editors/PatternEditor.cpp index 229c90bc2..5237690a7 100644 --- a/src/gui/editors/PatternEditor.cpp +++ b/src/gui/editors/PatternEditor.cpp @@ -69,7 +69,7 @@ void PatternEditor::cloneSteps() void PatternEditor::removeSteps() { - TrackContainer::TrackList tl = model()->tracks(); + const TrackContainer::TrackList& tl = model()->tracks(); for (const auto& track : tl) { @@ -176,7 +176,7 @@ void PatternEditor::updatePosition() void PatternEditor::makeSteps( bool clone ) { - TrackContainer::TrackList tl = model()->tracks(); + const TrackContainer::TrackList& tl = model()->tracks(); for (const auto& track : tl) { diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 05900bf0b..b38335995 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -741,10 +741,10 @@ void PianoRoll::fitNoteLengths(bool fill) { if (!hasValidMidiClip()) { return; } m_midiClip->addJournalCheckPoint(); + m_midiClip->rearrangeAllNotes(); // Reference notes - NoteVector refNotes = m_midiClip->notes(); - std::sort(refNotes.begin(), refNotes.end(), Note::lessThan); + const NoteVector& refNotes = m_midiClip->notes(); // Notes to edit NoteVector notes = getSelectedNotes(); @@ -762,7 +762,7 @@ void PianoRoll::fitNoteLengths(bool fill) } int length; - NoteVector::iterator ref = refNotes.begin(); + auto ref = refNotes.begin(); for (Note* note : notes) { // Fast forward to next reference note @@ -797,14 +797,11 @@ void PianoRoll::constrainNoteLengths(bool constrainMax) if (!hasValidMidiClip()) { return; } m_midiClip->addJournalCheckPoint(); - NoteVector notes = getSelectedNotes(); - if (notes.empty()) - { - notes = m_midiClip->notes(); - } + const NoteVector selectedNotes = getSelectedNotes(); + const auto& notes = selectedNotes.empty() ? m_midiClip->notes() : selectedNotes; - TimePos bound = m_lenOfNewNotes; // will be length of last note - for (Note* note : notes) + TimePos bound = m_lenOfNewNotes; // will be length of last note + for (auto note : notes) { if (constrainMax ? note->length() > bound : note->length() < bound) { @@ -1207,11 +1204,11 @@ void PianoRoll::shiftSemiTone(int amount) //Shift notes by amount semitones auto selectedNotes = getSelectedNotes(); //If no notes are selected, shift all of them, otherwise shift selection - if (selectedNotes.empty()) { return shiftSemiTone(m_midiClip->notes(), amount); } - else { return shiftSemiTone(selectedNotes, amount); } + if (selectedNotes.empty()) { shiftSemiTone(m_midiClip->notes(), amount); } + else { shiftSemiTone(selectedNotes, amount); } } -void PianoRoll::shiftSemiTone(NoteVector notes, int amount) +void PianoRoll::shiftSemiTone(const NoteVector& notes, int amount) { m_midiClip->addJournalCheckPoint(); for (Note *note : notes) { note->setKey( note->key() + amount ); } @@ -1232,11 +1229,11 @@ void PianoRoll::shiftPos(int amount) //Shift notes pos by amount auto selectedNotes = getSelectedNotes(); //If no notes are selected, shift all of them, otherwise shift selection - if (selectedNotes.empty()) { return shiftPos(m_midiClip->notes(), amount); } - else { return shiftPos(selectedNotes, amount); } + if (selectedNotes.empty()) { shiftPos(m_midiClip->notes(), amount); } + else { shiftPos(selectedNotes, amount); } } -void PianoRoll::shiftPos(NoteVector notes, int amount) +void PianoRoll::shiftPos(const NoteVector& notes, int amount) { m_midiClip->addJournalCheckPoint(); diff --git a/src/gui/tracks/TrackContentWidget.cpp b/src/gui/tracks/TrackContentWidget.cpp index 442d717bf..619eff831 100644 --- a/src/gui/tracks/TrackContentWidget.cpp +++ b/src/gui/tracks/TrackContentWidget.cpp @@ -344,7 +344,7 @@ bool TrackContentWidget::canPasteSelection( TimePos clipPos, const QMimeData* md const int initialTrackIndex = tiAttr.value().toInt(); // Get the current track's index - const TrackContainer::TrackList tracks = t->trackContainer()->tracks(); + const TrackContainer::TrackList& tracks = t->trackContainer()->tracks(); const auto currentTrackIt = std::find(tracks.begin(), tracks.end(), t); const int currentTrackIndex = currentTrackIt != tracks.end() ? std::distance(tracks.begin(), currentTrackIt) : -1; @@ -443,7 +443,7 @@ bool TrackContentWidget::pasteSelection( TimePos clipPos, const QMimeData * md, TimePos grabbedClipPos = clipPosAttr.value().toInt(); // Snap the mouse position to the beginning of the dropped bar, in ticks - const TrackContainer::TrackList tracks = getTrack()->trackContainer()->tracks(); + const TrackContainer::TrackList& tracks = getTrack()->trackContainer()->tracks(); const auto currentTrackIt = std::find(tracks.begin(), tracks.end(), getTrack()); const int currentTrackIndex = currentTrackIt != tracks.end() ? std::distance(tracks.begin(), currentTrackIt) : -1; diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index b35979f61..490f6e6d0 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -305,7 +305,7 @@ void MidiClip::setStep( int step, bool enabled ) -void MidiClip::splitNotes(NoteVector notes, TimePos pos) +void MidiClip::splitNotes(const NoteVector& notes, TimePos pos) { if (notes.empty()) { return; } @@ -472,7 +472,7 @@ MidiClip * MidiClip::nextMidiClip() const MidiClip * MidiClip::adjacentMidiClipByOffset(int offset) const { - std::vector clips = m_instrumentTrack->getClips(); + auto& clips = m_instrumentTrack->getClips(); int clipNum = m_instrumentTrack->getClipNum(this); if (clipNum < 0 || clipNum > clips.size() - 1) { return nullptr; } return dynamic_cast(clips[clipNum + offset]); From e1d3ecb1843df3a4aff96ea086df3260fdd26672 Mon Sep 17 00:00:00 2001 From: Martin Pavelek Date: Thu, 31 Aug 2023 19:21:26 +0200 Subject: [PATCH 42/45] Microtuner UI update (#6206) * Replace deprecated sprintf() function * Update microtuner-related UI (add more clues explaining how to use it) * Simpler wording in the tooltips for "apply" buttons * Post-merge fix of a forward declaration * Rename Misc tab to Tuning and transposition; move Microtuner config dialog to Edit menu and make it also available from instrument tab * Enable word wrap on "MIDI unsupported" label * Remove forgotten new unnecessary includes * Rename InstrumentMiscView to InstrumentTuningView in locales --- data/locale/ar.ts | 2 +- data/locale/bs.ts | 2 +- data/locale/ca.ts | 2 +- data/locale/cs.ts | 2 +- data/locale/de.ts | 2 +- data/locale/el.ts | 2 +- data/locale/en.ts | 2 +- data/locale/eo.ts | 2 +- data/locale/es.ts | 2 +- data/locale/eu.ts | 2 +- data/locale/fa.ts | 2 +- data/locale/fr.ts | 2 +- data/locale/gl.ts | 2 +- data/locale/he.ts | 2 +- data/locale/hi_IN.ts | 2 +- data/locale/hu_HU.ts | 2 +- data/locale/id.ts | 2 +- data/locale/it.ts | 2 +- data/locale/ja.ts | 2 +- data/locale/ka.ts | 2 +- data/locale/ko.ts | 2 +- data/locale/ms_MY.ts | 2 +- data/locale/nb.ts | 2 +- data/locale/nl.ts | 2 +- data/locale/oc.ts | 2 +- data/locale/pl.ts | 2 +- data/locale/pt.ts | 2 +- data/locale/ro.ts | 2 +- data/locale/ru.ts | 2 +- data/locale/sl.ts | 2 +- data/locale/sr.ts | 2 +- data/locale/sv.ts | 2 +- data/locale/tr.ts | 2 +- data/locale/uk.ts | 2 +- data/locale/zh_CN.ts | 2 +- data/locale/zh_TW.ts | 2 +- data/themes/default/edit_draw_small.png | Bin 0 -> 5367 bytes data/themes/default/tuning_tab.png | Bin 0 -> 7016 bytes include/InstrumentTrack.h | 4 +- include/InstrumentTrackWindow.h | 4 +- ...umentMiscView.h => InstrumentTuningView.h} | 22 ++++++---- src/gui/CMakeLists.txt | 2 +- src/gui/MainWindow.cpp | 19 +++------ src/gui/MicrotunerConfig.cpp | 20 +++++---- src/gui/instrument/InstrumentTrackWindow.cpp | 38 +++++++++--------- ...tMiscView.cpp => InstrumentTuningView.cpp} | 33 ++++++++++++--- 46 files changed, 122 insertions(+), 92 deletions(-) create mode 100644 data/themes/default/edit_draw_small.png create mode 100644 data/themes/default/tuning_tab.png rename include/{InstrumentMiscView.h => InstrumentTuningView.h} (72%) rename src/gui/instrument/{InstrumentMiscView.cpp => InstrumentTuningView.cpp} (69%) diff --git a/data/locale/ar.ts b/data/locale/ar.ts index 1f159c42a..0d44c22bf 100644 --- a/data/locale/ar.ts +++ b/data/locale/ar.ts @@ -6361,7 +6361,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/bs.ts b/data/locale/bs.ts index 506b401bd..7abf0baf1 100644 --- a/data/locale/bs.ts +++ b/data/locale/bs.ts @@ -3677,7 +3677,7 @@ You can remove and move mixer channels in the context menu, which is accessed by - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/ca.ts b/data/locale/ca.ts index 765cf3b60..0e27c39db 100644 --- a/data/locale/ca.ts +++ b/data/locale/ca.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/cs.ts b/data/locale/cs.ts index 0ed175022..022f55459 100644 --- a/data/locale/cs.ts +++ b/data/locale/cs.ts @@ -6361,7 +6361,7 @@ Ověřte si prosím, zda máte povolen zápis do souboru a do složky, ve které - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/de.ts b/data/locale/de.ts index 51ca7d562..7817857fd 100644 --- a/data/locale/de.ts +++ b/data/locale/de.ts @@ -6361,7 +6361,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/el.ts b/data/locale/el.ts index 320a6657f..07e61778f 100644 --- a/data/locale/el.ts +++ b/data/locale/el.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/en.ts b/data/locale/en.ts index e52ae39ab..15c3ab1f0 100644 --- a/data/locale/en.ts +++ b/data/locale/en.ts @@ -6362,7 +6362,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/eo.ts b/data/locale/eo.ts index 005ee8100..0dd9c405f 100644 --- a/data/locale/eo.ts +++ b/data/locale/eo.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/es.ts b/data/locale/es.ts index 4fc4951ef..3953ddc11 100644 --- a/data/locale/es.ts +++ b/data/locale/es.ts @@ -6361,7 +6361,7 @@ Asegúrate de tener permisos de escritura tanto del archivo como del directorio - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/eu.ts b/data/locale/eu.ts index 25c165f81..fe6495c0a 100644 --- a/data/locale/eu.ts +++ b/data/locale/eu.ts @@ -6641,7 +6641,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/fa.ts b/data/locale/fa.ts index 181ca0ca1..b376a8424 100644 --- a/data/locale/fa.ts +++ b/data/locale/fa.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/fr.ts b/data/locale/fr.ts index 2c65444a8..4862f4263 100644 --- a/data/locale/fr.ts +++ b/data/locale/fr.ts @@ -6645,7 +6645,7 @@ Veuillez vous assurez que vous avez les droits d'écriture sur le fichier e - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/gl.ts b/data/locale/gl.ts index cf04fd5d4..a1a9e6bf1 100644 --- a/data/locale/gl.ts +++ b/data/locale/gl.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/he.ts b/data/locale/he.ts index ee5a23613..fef0caa91 100644 --- a/data/locale/he.ts +++ b/data/locale/he.ts @@ -6361,7 +6361,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/hi_IN.ts b/data/locale/hi_IN.ts index 15550231f..82cf364e3 100644 --- a/data/locale/hi_IN.ts +++ b/data/locale/hi_IN.ts @@ -6362,7 +6362,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/hu_HU.ts b/data/locale/hu_HU.ts index 836059946..a0f1e4d45 100644 --- a/data/locale/hu_HU.ts +++ b/data/locale/hu_HU.ts @@ -6366,7 +6366,7 @@ Ellenőrizd, hogy rendelkezel-e a szükséges engedélyekkel és próbáld újra - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/id.ts b/data/locale/id.ts index e381ea726..c504740e9 100644 --- a/data/locale/id.ts +++ b/data/locale/id.ts @@ -6362,7 +6362,7 @@ Pastikan Anda memiliki izin menulis ke file dan direktori yang berisi berkas ter - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/it.ts b/data/locale/it.ts index ff146d471..d5a68e6e7 100644 --- a/data/locale/it.ts +++ b/data/locale/it.ts @@ -6366,7 +6366,7 @@ Si prega di controllare i permessi di scrittura sul file e la cartella che lo co - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/ja.ts b/data/locale/ja.ts index e10ca5118..14b38c698 100644 --- a/data/locale/ja.ts +++ b/data/locale/ja.ts @@ -6362,7 +6362,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/ka.ts b/data/locale/ka.ts index 1956d8d04..51eededf2 100644 --- a/data/locale/ka.ts +++ b/data/locale/ka.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/ko.ts b/data/locale/ko.ts index 7373b5ca9..43b99e7f4 100644 --- a/data/locale/ko.ts +++ b/data/locale/ko.ts @@ -6364,7 +6364,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/ms_MY.ts b/data/locale/ms_MY.ts index 209d51d10..ff3478421 100644 --- a/data/locale/ms_MY.ts +++ b/data/locale/ms_MY.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/nb.ts b/data/locale/nb.ts index 3675b7f58..659344d64 100644 --- a/data/locale/nb.ts +++ b/data/locale/nb.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/nl.ts b/data/locale/nl.ts index ad630a249..7ff3e8735 100644 --- a/data/locale/nl.ts +++ b/data/locale/nl.ts @@ -6362,7 +6362,7 @@ Zorg ervoor dat u schrijfbevoegdheid heeft voor het bestand en voor de map die h - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/oc.ts b/data/locale/oc.ts index 58c81c964..045eaf3ad 100644 --- a/data/locale/oc.ts +++ b/data/locale/oc.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/pl.ts b/data/locale/pl.ts index bb0c64ede..ff36a8dac 100644 --- a/data/locale/pl.ts +++ b/data/locale/pl.ts @@ -6646,7 +6646,7 @@ Upewnij się, że masz uprawnienia do zapisu do pliku i katalogu zawierającego - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/pt.ts b/data/locale/pt.ts index b375e289f..f8cfe7618 100644 --- a/data/locale/pt.ts +++ b/data/locale/pt.ts @@ -6363,7 +6363,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/ro.ts b/data/locale/ro.ts index eceb45a64..58abbba99 100644 --- a/data/locale/ro.ts +++ b/data/locale/ro.ts @@ -6361,7 +6361,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/ru.ts b/data/locale/ru.ts index 8235f291f..73b7e06ad 100644 --- a/data/locale/ru.ts +++ b/data/locale/ru.ts @@ -6375,7 +6375,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/sl.ts b/data/locale/sl.ts index 3ad55a4c0..e7bfbc308 100644 --- a/data/locale/sl.ts +++ b/data/locale/sl.ts @@ -6360,7 +6360,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/sr.ts b/data/locale/sr.ts index 9b90164ab..183936bc7 100644 --- a/data/locale/sr.ts +++ b/data/locale/sr.ts @@ -2956,7 +2956,7 @@ You can remove and move mixer channels in the context menu, which is accessed by - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/sv.ts b/data/locale/sv.ts index 4963b07a9..f5d4e0fb4 100644 --- a/data/locale/sv.ts +++ b/data/locale/sv.ts @@ -6644,7 +6644,7 @@ Se till att du har skrivbehörighet till filen och mappen som innehåller filen - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/tr.ts b/data/locale/tr.ts index 387be6d8b..b899337a5 100644 --- a/data/locale/tr.ts +++ b/data/locale/tr.ts @@ -6646,7 +6646,7 @@ Lütfen dosyaya ve dosyayı içeren dizine yazma izniniz olduğundan emin olun v - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/uk.ts b/data/locale/uk.ts index 50df10e4b..9fb6389c9 100644 --- a/data/locale/uk.ts +++ b/data/locale/uk.ts @@ -6361,7 +6361,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/zh_CN.ts b/data/locale/zh_CN.ts index 63b22df99..9b783b963 100644 --- a/data/locale/zh_CN.ts +++ b/data/locale/zh_CN.ts @@ -6370,7 +6370,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/locale/zh_TW.ts b/data/locale/zh_TW.ts index 791a45599..a3a727edb 100644 --- a/data/locale/zh_TW.ts +++ b/data/locale/zh_TW.ts @@ -6361,7 +6361,7 @@ Please make sure you have write permission to the file and the directory contain - InstrumentMiscView + InstrumentTuningView MASTER PITCH diff --git a/data/themes/default/edit_draw_small.png b/data/themes/default/edit_draw_small.png new file mode 100644 index 0000000000000000000000000000000000000000..9979c8223457ac27e70116b300062a3237b3272a GIT binary patch literal 5367 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 - * Copyright (c) 2020 Martin Pavelek + * Copyright (c) 2020-2022 Martin Pavelek * * This file is part of LMMS - https://lmms.io * @@ -24,11 +24,13 @@ * */ -#ifndef LMMS_GUI_INSTRUMENT_MISC_VIEW_H -#define LMMS_GUI_INSTRUMENT_MISC_VIEW_H +#ifndef LMMS_GUI_INSTRUMENT_TUNING_VIEW_H +#define LMMS_GUI_INSTRUMENT_TUNING_VIEW_H #include +class QLabel; + namespace lmms { @@ -42,15 +44,17 @@ class GroupBox; class LedCheckBox; -class InstrumentMiscView : public QWidget +class InstrumentTuningView : public QWidget { Q_OBJECT public: - InstrumentMiscView(InstrumentTrack *it, QWidget *parent); + InstrumentTuningView(InstrumentTrack *it, QWidget *parent); GroupBox *pitchGroupBox() {return m_pitchGroupBox;} GroupBox *microtunerGroupBox() {return m_microtunerGroupBox;} + QLabel *microtunerNotSupportedLabel() {return m_microtunerNotSupportedLabel;} + ComboBox *scaleCombo() {return m_scaleCombo;} ComboBox *keymapCombo() {return m_keymapCombo;} @@ -60,6 +64,8 @@ private: GroupBox *m_pitchGroupBox; GroupBox *m_microtunerGroupBox; + QLabel *m_microtunerNotSupportedLabel; + ComboBox *m_scaleCombo; ComboBox *m_keymapCombo; @@ -71,4 +77,4 @@ private: } // namespace lmms -#endif // LMMS_GUI_INSTRUMENT_MISC_VIEW_H +#endif // LMMS_GUI_INSTRUMENT_TUNING_VIEW_H diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 9f940c035..afed153f9 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -61,7 +61,7 @@ SET(LMMS_SRCS gui/instrument/EnvelopeAndLfoView.cpp gui/instrument/InstrumentFunctionViews.cpp gui/instrument/InstrumentMidiIOView.cpp - gui/instrument/InstrumentMiscView.cpp + gui/instrument/InstrumentTuningView.cpp gui/instrument/InstrumentSoundShapingView.cpp gui/instrument/InstrumentTrackWindow.cpp gui/instrument/InstrumentView.cpp diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 559756169..10805fe01 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -363,10 +363,12 @@ void MainWindow::finalize() } edit_menu->addSeparator(); - edit_menu->addAction( embed::getIconPixmap( "setup_general" ), - tr( "Settings" ), - this, SLOT(showSettingsDialog())); - connect( edit_menu, SIGNAL(aboutToShow()), this, SLOT(updateUndoRedoButtons())); + edit_menu->addAction(embed::getIconPixmap("microtuner"), tr("Scales and keymaps"), + this, SLOT(toggleMicrotunerWin())); + edit_menu->addAction(embed::getIconPixmap("setup_general"), tr("Settings"), + this, SLOT(showSettingsDialog())); + + connect(edit_menu, SIGNAL(aboutToShow()), this, SLOT(updateUndoRedoButtons())); m_viewMenu = new QMenu( this ); menuBar()->addMenu( m_viewMenu )->setText( tr( "&View" ) ); @@ -485,10 +487,6 @@ void MainWindow::finalize() tr("Show/hide project notes") + " (Ctrl+7)", this, SLOT(toggleProjectNotesWin()), m_toolBar); project_notes_window->setShortcut( Qt::CTRL + Qt::Key_7 ); - auto microtuner_window = new ToolButton(embed::getIconPixmap("microtuner"), - tr("Microtuner configuration") + " (Ctrl+8)", this, SLOT(toggleMicrotunerWin()), m_toolBar); - microtuner_window->setShortcut( Qt::CTRL + Qt::Key_8 ); - m_toolBarLayout->addWidget( song_editor_window, 1, 1 ); m_toolBarLayout->addWidget( pattern_editor_window, 1, 2 ); m_toolBarLayout->addWidget( piano_roll_window, 1, 3 ); @@ -496,7 +494,6 @@ void MainWindow::finalize() m_toolBarLayout->addWidget( mixer_window, 1, 5 ); m_toolBarLayout->addWidget( controllers_window, 1, 6 ); m_toolBarLayout->addWidget( project_notes_window, 1, 7 ); - m_toolBarLayout->addWidget( microtuner_window, 1, 8 ); m_toolBarLayout->setColumnStretch( 100, 1 ); // setup-dialog opened before? @@ -1100,10 +1097,6 @@ void MainWindow::updateViewMenu() tr( "Project Notes" ) + "\tCtrl+7", this, SLOT(toggleProjectNotesWin()) ); - m_viewMenu->addAction(embed::getIconPixmap( "microtuner" ), - tr( "Microtuner" ) + "\tCtrl+8", - this, SLOT(toggleMicrotunerWin()) - ); m_viewMenu->addSeparator(); diff --git a/src/gui/MicrotunerConfig.cpp b/src/gui/MicrotunerConfig.cpp index 7ab4cc0b1..4156b9e79 100644 --- a/src/gui/MicrotunerConfig.cpp +++ b/src/gui/MicrotunerConfig.cpp @@ -56,8 +56,8 @@ namespace lmms::gui MicrotunerConfig::MicrotunerConfig() : QWidget(), - m_scaleComboModel(nullptr, tr("Selected scale")), - m_keymapComboModel(nullptr, tr("Selected keymap")), + m_scaleComboModel(nullptr, tr("Selected scale slot")), + m_keymapComboModel(nullptr, tr("Selected keymap slot")), m_firstKeyModel(0, 0, NumKeys - 1, nullptr, tr("First key")), m_lastKeyModel(NumKeys - 1, 0, NumKeys - 1, nullptr, tr("Last key")), m_middleKeyModel(DefaultMiddleKey, 0, NumKeys - 1, nullptr, tr("Middle key")), @@ -75,7 +75,7 @@ MicrotunerConfig::MicrotunerConfig() : #endif setWindowIcon(embed::getIconPixmap("microtuner")); - setWindowTitle(tr("Microtuner")); + setWindowTitle(tr("Microtuner Configuration")); // Organize into 2 main columns: scales and keymaps auto microtunerLayout = new QGridLayout(); @@ -84,7 +84,7 @@ MicrotunerConfig::MicrotunerConfig() : // ---------------------------------- // Scale sub-column // - auto scaleLabel = new QLabel(tr("Scale:")); + auto scaleLabel = new QLabel(tr("Scale slot to edit:")); microtunerLayout->addWidget(scaleLabel, 0, 0, 1, 2, Qt::AlignBottom); for (unsigned int i = 0; i < MaxScaleCount; i++) @@ -102,6 +102,8 @@ MicrotunerConfig::MicrotunerConfig() : auto loadScaleButton = new QPushButton(tr("Load")); auto saveScaleButton = new QPushButton(tr("Save")); + loadScaleButton->setToolTip(tr("Load scale definition from a file.")); + saveScaleButton->setToolTip(tr("Save scale definition to a file.")); microtunerLayout->addWidget(loadScaleButton, 3, 0, 1, 1); microtunerLayout->addWidget(saveScaleButton, 3, 1, 1, 1); connect(loadScaleButton, &QPushButton::clicked, [=] {loadScaleFromFile();}); @@ -112,14 +114,15 @@ MicrotunerConfig::MicrotunerConfig() : m_scaleTextEdit->setToolTip(tr("Enter intervals on separate lines. Numbers containing a decimal point are treated as cents.\nOther inputs are treated as integer ratios and must be in the form of \'a/b\' or \'a\'.\nUnity (0.0 cents or ratio 1/1) is always present as a hidden first value; do not enter it manually.")); microtunerLayout->addWidget(m_scaleTextEdit, 4, 0, 2, 2); - auto applyScaleButton = new QPushButton(tr("Apply scale")); + auto applyScaleButton = new QPushButton(tr("Apply scale changes")); + applyScaleButton->setToolTip(tr("Verify and apply changes made to the selected scale. To use the scale, select it in the settings of a supported instrument.")); microtunerLayout->addWidget(applyScaleButton, 6, 0, 1, 2); connect(applyScaleButton, &QPushButton::clicked, [=] {applyScale();}); // ---------------------------------- // Mapping sub-column // - auto keymapLabel = new QLabel(tr("Keymap:")); + auto keymapLabel = new QLabel(tr("Keymap slot to edit:")); microtunerLayout->addWidget(keymapLabel, 0, 2, 1, 2, Qt::AlignBottom); for (unsigned int i = 0; i < MaxKeymapCount; i++) @@ -137,6 +140,8 @@ MicrotunerConfig::MicrotunerConfig() : auto loadKeymapButton = new QPushButton(tr("Load")); auto saveKeymapButton = new QPushButton(tr("Save")); + loadKeymapButton->setToolTip(tr("Load key mapping definition from a file.")); + saveKeymapButton->setToolTip(tr("Save key mapping definition to a file.")); microtunerLayout->addWidget(loadKeymapButton, 3, 2, 1, 1); microtunerLayout->addWidget(saveKeymapButton, 3, 3, 1, 1); connect(loadKeymapButton, &QPushButton::clicked, [=] {loadKeymapFromFile();}); @@ -181,7 +186,8 @@ MicrotunerConfig::MicrotunerConfig() : baseFreqSpin->setToolTip(tr("Base note frequency")); keymapRangeLayout->addWidget(baseFreqSpin, 1, 1, 1, 2); - auto applyKeymapButton = new QPushButton(tr("Apply keymap")); + auto applyKeymapButton = new QPushButton(tr("Apply keymap changes")); + applyKeymapButton->setToolTip(tr("Verify and apply changes made to the selected key mapping. To use the mapping, select it in the settings of a supported instrument.")); microtunerLayout->addWidget(applyKeymapButton, 6, 2, 1, 2); connect(applyKeymapButton, &QPushButton::clicked, [=] {applyKeymap();}); diff --git a/src/gui/instrument/InstrumentTrackWindow.cpp b/src/gui/instrument/InstrumentTrackWindow.cpp index 43cca0dac..28cd8c6c8 100644 --- a/src/gui/instrument/InstrumentTrackWindow.cpp +++ b/src/gui/instrument/InstrumentTrackWindow.cpp @@ -49,7 +49,7 @@ #include "InstrumentFunctions.h" #include "InstrumentFunctionViews.h" #include "InstrumentMidiIOView.h" -#include "InstrumentMiscView.h" +#include "InstrumentTuningView.h" #include "InstrumentSoundShapingView.h" #include "InstrumentTrack.h" #include "InstrumentTrackView.h" @@ -255,25 +255,25 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : instrumentFunctionsLayout->addStretch(); // MIDI tab - m_midiView = new InstrumentMidiIOView( m_tabWidget ); + m_midiView = new InstrumentMidiIOView(m_tabWidget); // FX tab - m_effectView = new EffectRackView( m_track->m_audioPort.effects(), m_tabWidget ); + m_effectView = new EffectRackView(m_track->m_audioPort.effects(), m_tabWidget); - // MISC tab - m_miscView = new InstrumentMiscView( m_track, m_tabWidget ); + // Tuning tab + m_tuningView = new InstrumentTuningView(m_track, m_tabWidget); - m_tabWidget->addTab( m_ssView, tr( "Envelope, filter & LFO" ), "env_lfo_tab", 1 ); - m_tabWidget->addTab( instrumentFunctions, tr( "Chord stacking & arpeggio" ), "func_tab", 2 ); - m_tabWidget->addTab( m_effectView, tr( "Effects" ), "fx_tab", 3 ); - m_tabWidget->addTab( m_midiView, tr( "MIDI" ), "midi_tab", 4 ); - m_tabWidget->addTab( m_miscView, tr( "Miscellaneous" ), "misc_tab", 5 ); + m_tabWidget->addTab(m_ssView, tr("Envelope, filter & LFO"), "env_lfo_tab", 1); + m_tabWidget->addTab(instrumentFunctions, tr("Chord stacking & arpeggio"), "func_tab", 2); + m_tabWidget->addTab(m_effectView, tr("Effects"), "fx_tab", 3); + m_tabWidget->addTab(m_midiView, tr("MIDI"), "midi_tab", 4); + m_tabWidget->addTab(m_tuningView, tr("Tuning and transposition"), "tuning_tab", 5); adjustTabSize(m_ssView); adjustTabSize(instrumentFunctions); m_effectView->resize(EffectRackView::DEFAULT_WIDTH, INSTRUMENT_HEIGHT - 4 - 1); adjustTabSize(m_midiView); - adjustTabSize(m_miscView); + adjustTabSize(m_tuningView); // setup piano-widget m_pianoView = new PianoView( this ); @@ -376,12 +376,14 @@ void InstrumentTrackWindow::modelChanged() if (m_track->instrument() && m_track->instrument()->flags().testFlag(Instrument::Flag::IsMidiBased)) { - m_miscView->microtunerGroupBox()->hide(); + m_tuningView->microtunerNotSupportedLabel()->show(); + m_tuningView->microtunerGroupBox()->hide(); m_track->m_microtuner.enabledModel()->setValue(false); } else { - m_miscView->microtunerGroupBox()->show(); + m_tuningView->microtunerNotSupportedLabel()->hide(); + m_tuningView->microtunerGroupBox()->show(); } m_ssView->setModel( &m_track->m_soundShaping ); @@ -389,11 +391,11 @@ void InstrumentTrackWindow::modelChanged() m_arpeggioView->setModel( &m_track->m_arpeggio ); m_midiView->setModel( &m_track->m_midiPort ); m_effectView->setModel( m_track->m_audioPort.effects() ); - m_miscView->pitchGroupBox()->setModel(&m_track->m_useMasterPitchModel); - m_miscView->microtunerGroupBox()->setModel(m_track->m_microtuner.enabledModel()); - m_miscView->scaleCombo()->setModel(m_track->m_microtuner.scaleModel()); - m_miscView->keymapCombo()->setModel(m_track->m_microtuner.keymapModel()); - m_miscView->rangeImportCheckbox()->setModel(m_track->m_microtuner.keyRangeImportModel()); + m_tuningView->pitchGroupBox()->setModel(&m_track->m_useMasterPitchModel); + m_tuningView->microtunerGroupBox()->setModel(m_track->m_microtuner.enabledModel()); + m_tuningView->scaleCombo()->setModel(m_track->m_microtuner.scaleModel()); + m_tuningView->keymapCombo()->setModel(m_track->m_microtuner.keymapModel()); + m_tuningView->rangeImportCheckbox()->setModel(m_track->m_microtuner.keyRangeImportModel()); updateName(); } diff --git a/src/gui/instrument/InstrumentMiscView.cpp b/src/gui/instrument/InstrumentTuningView.cpp similarity index 69% rename from src/gui/instrument/InstrumentMiscView.cpp rename to src/gui/instrument/InstrumentTuningView.cpp index 514db579c..355d7d18c 100644 --- a/src/gui/instrument/InstrumentMiscView.cpp +++ b/src/gui/instrument/InstrumentTuningView.cpp @@ -1,8 +1,8 @@ /* - * InstrumentMiscView.cpp - Miscellaneous instrument settings + * InstrumentTuningView.cpp - Instrument settings for tuning and transpositions * * Copyright (c) 2005-2014 Tobias Doerffel - * Copyright (c) 2020 Martin Pavelek + * Copyright (c) 2020-2022 Martin Pavelek * * This file is part of LMMS - https://lmms.io * @@ -23,24 +23,28 @@ * */ -#include "InstrumentMiscView.h" +#include "InstrumentTuningView.h" #include #include +#include #include #include "ComboBox.h" #include "GroupBox.h" +#include "GuiApplication.h" #include "gui_templates.h" #include "InstrumentTrack.h" #include "LedCheckBox.h" +#include "MainWindow.h" +#include "PixmapButton.h" namespace lmms::gui { -InstrumentMiscView::InstrumentMiscView(InstrumentTrack *it, QWidget *parent) : +InstrumentTuningView::InstrumentTuningView(InstrumentTrack *it, QWidget *parent) : QWidget(parent) { auto layout = new QVBoxLayout(this); @@ -60,6 +64,11 @@ InstrumentMiscView::InstrumentMiscView(InstrumentTrack *it, QWidget *parent) : masterPitchLayout->addWidget(tlabel); // Microtuner settings + m_microtunerNotSupportedLabel = new QLabel(tr("Microtuner is not available for MIDI-based instruments.")); + m_microtunerNotSupportedLabel->setWordWrap(true); + m_microtunerNotSupportedLabel->hide(); + layout->addWidget(m_microtunerNotSupportedLabel); + m_microtunerGroupBox = new GroupBox(tr("MICROTUNER")); m_microtunerGroupBox->setModel(it->m_microtuner.enabledModel()); layout->addWidget(m_microtunerGroupBox); @@ -67,8 +76,22 @@ InstrumentMiscView::InstrumentMiscView(InstrumentTrack *it, QWidget *parent) : auto microtunerLayout = new QVBoxLayout(m_microtunerGroupBox); microtunerLayout->setContentsMargins(8, 18, 8, 8); + auto scaleEditLayout = new QHBoxLayout(); + scaleEditLayout->setContentsMargins(0, 0, 4, 0); + microtunerLayout->addLayout(scaleEditLayout); + auto scaleLabel = new QLabel(tr("Active scale:")); - microtunerLayout->addWidget(scaleLabel); + scaleEditLayout->addWidget(scaleLabel); + + QPixmap editPixmap(embed::getIconPixmap("edit_draw_small")); + auto editPixButton = new PixmapButton(this, tr("Edit scales and keymaps")); + editPixButton->setToolTip(tr("Edit scales and keymaps")); + editPixButton->setInactiveGraphic(editPixmap); + editPixButton->setActiveGraphic(editPixmap); + editPixButton->setFixedSize(16, 16); + connect(editPixButton, SIGNAL(clicked()), getGUI()->mainWindow(), SLOT(toggleMicrotunerWin())); + + scaleEditLayout->addWidget(editPixButton); m_scaleCombo = new ComboBox(); m_scaleCombo->setModel(it->m_microtuner.scaleModel()); From 0768f5ad2febea80e4766b25547d42fa12b7bd9a Mon Sep 17 00:00:00 2001 From: Dalton Messmer <33463986+messmerd@users.noreply.github.com> Date: Sun, 3 Sep 2023 17:29:31 -0400 Subject: [PATCH 43/45] Fix a few memory issues found with ASan (#6843) * Fix LADSPA effects memory leak * Fix buffer overflow in PianoView * Avoid using invalid iterators in AutomationClip * Fix memory leaks in SimpleTextFloat * Handle potential case where QMap::lowerBound(...) returns end iterator * Implement suggestions from review --- plugins/LadspaEffect/caps/Descriptor.h | 3 +- src/core/AutomationClip.cpp | 27 +++---- src/gui/instrument/PianoView.cpp | 107 ++++++++++++------------- src/gui/widgets/SimpleTextFloat.cpp | 4 +- 4 files changed, 64 insertions(+), 77 deletions(-) diff --git a/plugins/LadspaEffect/caps/Descriptor.h b/plugins/LadspaEffect/caps/Descriptor.h index 12c5d1c88..c3e1c325e 100644 --- a/plugins/LadspaEffect/caps/Descriptor.h +++ b/plugins/LadspaEffect/caps/Descriptor.h @@ -53,7 +53,7 @@ class DescriptorStub PortCount = 0; } - ~DescriptorStub() + virtual ~DescriptorStub() { if (PortCount) { @@ -87,6 +87,7 @@ class Descriptor public: Descriptor() { setup(); } + ~Descriptor() override = default; void setup(); void autogen() diff --git a/src/core/AutomationClip.cpp b/src/core/AutomationClip.cpp index 906cb148c..3b36f6b49 100644 --- a/src/core/AutomationClip.cpp +++ b/src/core/AutomationClip.cpp @@ -1106,16 +1106,16 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate) { QMutexLocker m(&m_clipMutex); - if( m_timeMap.size() < 2 && numToGenerate > 0 ) + for (int i = 0; i < numToGenerate && it != m_timeMap.end(); ++i, ++it) { - it.value().setInTangent(0); - it.value().setOutTangent(0); - return; - } - - for( int i = 0; i < numToGenerate; i++ ) - { - if( it == m_timeMap.begin() ) + if (it + 1 == m_timeMap.end()) + { + // Previously, the last value's tangent was always set to 0. That logic was kept for both tangents + // of the last node + it.value().setInTangent(0); + it.value().setOutTangent(0); + } + else if (it == m_timeMap.begin()) { // On the first node there's no curve behind it, so we will only calculate the outTangent // and inTangent will be set to 0. @@ -1123,14 +1123,6 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate) it.value().setInTangent(0); it.value().setOutTangent(tangent); } - else if( it+1 == m_timeMap.end() ) - { - // Previously, the last value's tangent was always set to 0. That logic was kept for both tangents - // of the last node - it.value().setInTangent(0); - it.value().setOutTangent(0); - return; - } else { // When we are in a node that is in the middle of two other nodes, we need to check if we @@ -1159,7 +1151,6 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate) it.value().setOutTangent(outTangent); } } - it++; } } diff --git a/src/gui/instrument/PianoView.cpp b/src/gui/instrument/PianoView.cpp index 20db2e8e8..d20cbcac5 100644 --- a/src/gui/instrument/PianoView.cpp +++ b/src/gui/instrument/PianoView.cpp @@ -322,70 +322,65 @@ void PianoView::modelChanged() -// gets the key from the given mouse-position +// Gets the key from the given mouse position /*! \brief Get the key from the mouse position in the piano display * - * First we determine it roughly by the position of the point given in - * white key widths from our start. We then add in any black keys that - * might have been skipped over (they take a key number, but no 'white - * key' space). We then add in our starting key number. - * - * We then determine whether it was a black key that was pressed by - * checking whether it was within the vertical range of black keys. - * Black keys sit exactly between white keys on this keyboard, so - * we then shift the note down or up if we were in the left or right - * half of the white note. We only do this, of course, if the white - * note has a black key on that side, so to speak. - * - * This function returns const because there is a linear mapping from - * the point given to the key returned that never changes. - * - * \param _p The point that the mouse was pressed. + * \param p The point that the mouse was pressed. */ -int PianoView::getKeyFromMouse( const QPoint & _p ) const +int PianoView::getKeyFromMouse(const QPoint& p) const { - int offset = _p.x() % PW_WHITE_KEY_WIDTH; - if( offset < 0 ) offset += PW_WHITE_KEY_WIDTH; - int key_num = ( _p.x() - offset) / PW_WHITE_KEY_WIDTH; + // The left-most key visible in the piano display is always white + const int startingWhiteKey = m_pianoScroll->value(); - for( int i = 0; i <= key_num; ++i ) + // Adjust the mouse x position as if x == 0 was the left side of the lowest key + const int adjX = p.x() + (startingWhiteKey * PW_WHITE_KEY_WIDTH); + + // Can early return for notes too low + if (adjX <= 0) { return 0; } + + // Now we can calculate the key number (in only white keys) and the octave + const int whiteKey = adjX / PW_WHITE_KEY_WIDTH; + const int octave = whiteKey / Piano::WhiteKeysPerOctave; + + // Calculate for full octaves + int key = octave * KeysPerOctave; + + // Adjust for white notes in the current octave + // (WhiteKeys maps each white key to the number of notes to their left in the octave) + key += static_cast(WhiteKeys[whiteKey % Piano::WhiteKeysPerOctave]); + + // Might be a black key, which would require further adjustment + if (p.y() < PIANO_BASE + PW_BLACK_KEY_HEIGHT) { - if ( Piano::isBlackKey( m_startKey+i ) ) + // Maps white keys to neighboring black keys + static constexpr std::array neighboringKeyMap { + std::pair{ 0, 1 }, // C --> no B#; C# + std::pair{ 1, 1 }, // D --> C#; D# + std::pair{ 1, 0 }, // E --> D#; no E# + std::pair{ 0, 1 }, // F --> no E#; F# + std::pair{ 1, 1 }, // G --> F#; G# + std::pair{ 1, 1 }, // A --> G#; A# + std::pair{ 1, 0 }, // B --> A#; no B# + }; + + const auto neighboringBlackKeys = neighboringKeyMap[whiteKey % Piano::WhiteKeysPerOctave]; + const int offset = adjX - (whiteKey * PW_WHITE_KEY_WIDTH); // mouse X offset from white key + + if (offset < PW_BLACK_KEY_WIDTH / 2) { - ++key_num; + // At the location of a (possibly non-existent) black key on the left side + key -= neighboringBlackKeys.first; } - } - for( int i = 0; i >= key_num; --i ) - { - if ( Piano::isBlackKey( m_startKey+i ) ) + else if (offset > PW_WHITE_KEY_WIDTH - (PW_BLACK_KEY_WIDTH / 2)) { - --key_num; + // At the location of a (possibly non-existent) black key on the right side + key += neighboringBlackKeys.second; } + + // For white keys in between black keys, no further adjustment is needed } - key_num += m_startKey; - - // is it a black key? - if( _p.y() < PIANO_BASE + PW_BLACK_KEY_HEIGHT ) - { - // then do extra checking whether the mouse-cursor is over - // a black key - if( key_num > 0 && Piano::isBlackKey( key_num-1 ) && - offset <= ( PW_WHITE_KEY_WIDTH / 2 ) - - ( PW_BLACK_KEY_WIDTH / 2 ) ) - { - --key_num; - } - if( key_num < NumKeys - 1 && Piano::isBlackKey( key_num+1 ) && - offset >= ( PW_WHITE_KEY_WIDTH - - PW_BLACK_KEY_WIDTH / 2 ) ) - { - ++key_num; - } - } - - // some range-checking-stuff - return qBound( 0, key_num, NumKeys - 1 ); + return std::clamp(key, 0, NumKeys - 1); } @@ -396,12 +391,12 @@ int PianoView::getKeyFromMouse( const QPoint & _p ) const * * We need to update our start key position based on the new position. * - * \param _new_pos the new key position. + * \param newPos the new key position, counting only white keys. */ -void PianoView::pianoScrolled(int new_pos) +void PianoView::pianoScrolled(int newPos) { - m_startKey = static_cast(new_pos / Piano::WhiteKeysPerOctave) - + WhiteKeys[new_pos % Piano::WhiteKeysPerOctave]; + m_startKey = static_cast(newPos / Piano::WhiteKeysPerOctave) + + WhiteKeys[newPos % Piano::WhiteKeysPerOctave]; update(); } diff --git a/src/gui/widgets/SimpleTextFloat.cpp b/src/gui/widgets/SimpleTextFloat.cpp index df438423e..e37753229 100644 --- a/src/gui/widgets/SimpleTextFloat.cpp +++ b/src/gui/widgets/SimpleTextFloat.cpp @@ -46,11 +46,11 @@ SimpleTextFloat::SimpleTextFloat() : m_textLabel = new QLabel(this); layout->addWidget(m_textLabel); - m_showTimer = new QTimer(); + m_showTimer = new QTimer(this); m_showTimer->setSingleShot(true); QObject::connect(m_showTimer, &QTimer::timeout, this, &SimpleTextFloat::show); - m_hideTimer = new QTimer(); + m_hideTimer = new QTimer(this); m_hideTimer->setSingleShot(true); QObject::connect(m_hideTimer, &QTimer::timeout, this, &SimpleTextFloat::hide); } From fa05ce20b8578b77b19fd22d8c63eb59fe7ea0e1 Mon Sep 17 00:00:00 2001 From: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com> Date: Mon, 4 Sep 2023 07:29:29 +0530 Subject: [PATCH 44/45] Replace corrupted default samples with fixed ones. (#6752) * pulled from upstream * replaced broken sample files * Revert "pulled from upstream" This reverts commit 76967b5cc4e7fbc12086830bd6a3954146bdbb9f. * fixed shortened samples * will this fix exprtk submodule? This reverts commit 76967b5cc4e7fbc12086830bd6a3954146bdbb9f. --- data/samples/drums/kick04.ogg | Bin 7126 -> 12017 bytes data/samples/effects/scratch01.ogg | Bin 5516 -> 12370 bytes data/samples/effects/wind_chimes01.ogg | Bin 22654 -> 57873 bytes data/samples/instruments/harpsichord01.ogg | Bin 38144 -> 60978 bytes data/samples/misc/hit01.ogg | Bin 16606 -> 43603 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/samples/drums/kick04.ogg b/data/samples/drums/kick04.ogg index 567480abd683475c1ba4325469adc111dac42b8d..8f7dce5271252405b7adfe8bc2bb606479c342ce 100644 GIT binary patch literal 12017 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 MGw>pjd%^O*0FRxTr2qf` literal 7126 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 diff --git a/data/samples/effects/scratch01.ogg b/data/samples/effects/scratch01.ogg index 9f216038dc49095c6a07a217e28ec0ab95842f32..0b05505cd3615b2d450a59b7719793c315865a38 100644 GIT binary patch literal 12370 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 literal 5516 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 HhJyb9N{p{9 diff --git a/data/samples/effects/wind_chimes01.ogg b/data/samples/effects/wind_chimes01.ogg index 35d3374a2e2465f38380d7556b5ff4c6fecb6f12..7fb3c441a24c9184ea544604b474bcf998450dd0 100644 GIT binary patch literal 57873 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 AJ^%m! literal 22654 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�N8BI=>A&+m!_r3kJq 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�Vb`AKuTSXj;ait*miRnbc)i?K3Y`y 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 QFJbgN{C~2M8UKIz7tkX;xBvhE diff --git a/data/samples/instruments/harpsichord01.ogg b/data/samples/instruments/harpsichord01.ogg index 028bbd912596ea0492dbcbb23f0cab73e8b9f371..c84ffd7df44519fc3323c22aac403ca69a25a375 100644 GIT binary patch literal 60978 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

8H#vZ7^Yax`|9Q=JWLJ*QP(UD1| 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

f6AE{sO1O8|-| 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 WXje3ae0ayF!)1--1L*#*@BabsA+zTI literal 16606 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 From de062d6c54d48cc53d080fb25ed33a6c72d6e26f Mon Sep 17 00:00:00 2001 From: Martin Pavelek Date: Tue, 5 Sep 2023 01:33:42 +0200 Subject: [PATCH 45/45] Show detailed CPU load information in a tool-tip (#5970) * Profiler rework * Workaround for GCC bug * Rollback QFile removal * Use enum instead of a plain index to describe detailed stats * Use the GCC workaround code for all compilers to avoid redundancy * Update and fix comments * Implement suggestions from review * Split AudioEngine::renderNextBuffer() into separate functions, fix old formatting * Remove QFile include * Revert formatting changes * Apply suggestion from review (remove unnecessary template parameter) * Revert more formatting changes * Convert DetailType to enum class * DetailType enum class improvements suggested in review * Use std::atomic for m_detailLoad * RAII-style profiler probes * Apply suggestions from code review Co-authored-by: Dominic Clark * Fix namespace comment * Improve CPU load widget precision and use floats for load computations * Atomic m_cpuLoad * Add custom step size support for CPULoadWidget * Apply suggestions from review (convert the profiler probe into a nested class, other small changes) * Do not limit stored load averages to 100% --------- Co-authored-by: sakertooth Co-authored-by: Dominic Clark --- data/themes/classic/style.css | 1 + data/themes/default/style.css | 1 + include/AudioEngine.h | 9 ++++++ include/AudioEngineProfiler.h | 48 +++++++++++++++++++++++++++++- include/CPULoadWidget.h | 6 ++++ src/core/AudioEngine.cpp | 49 ++++++++++++++++++++++++------- src/core/AudioEngineProfiler.cpp | 22 ++++++++++++-- src/gui/widgets/CPULoadWidget.cpp | 27 ++++++++++++----- 8 files changed, 140 insertions(+), 23 deletions(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index d1f4d0588..ac22a14ec 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -209,6 +209,7 @@ lmms--gui--Oscilloscope { lmms--gui--CPULoadWidget { border: none; background: url(resources:cpuload_bg.png); + qproperty-stepSize: 4; } /* scrollbar: trough */ diff --git a/data/themes/default/style.css b/data/themes/default/style.css index e88e51e5b..092332eee 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -241,6 +241,7 @@ lmms--gui--Oscilloscope { lmms--gui--CPULoadWidget { border: none; background: url(resources:cpuload_bg.png); + qproperty-stepSize: 1; } /* scrollbar: trough */ diff --git a/include/AudioEngine.h b/include/AudioEngine.h index 030c5bce3..f056c22e1 100644 --- a/include/AudioEngine.h +++ b/include/AudioEngine.h @@ -275,6 +275,11 @@ public: return m_profiler.cpuLoad(); } + int detailLoad(const AudioEngineProfiler::DetailType type) const + { + return m_profiler.detailLoad(type); + } + const qualitySettings & currentQualitySettings() const { return m_qualitySettings; @@ -401,6 +406,10 @@ private: AudioDevice * tryAudioDevices(); MidiClient * tryMidiClients(); + void renderStageNoteSetup(); + void renderStageInstruments(); + void renderStageEffects(); + void renderStageMix(); const surroundSampleFrame * renderNextBuffer(); diff --git a/include/AudioEngineProfiler.h b/include/AudioEngineProfiler.h index 7b5191e76..b0d62a1dc 100644 --- a/include/AudioEngineProfiler.h +++ b/include/AudioEngineProfiler.h @@ -25,6 +25,8 @@ #ifndef LMMS_AUDIO_ENGINE_PROFILER_H #define LMMS_AUDIO_ENGINE_PROFILER_H +#include +#include #include #include "lmms_basics.h" @@ -53,11 +55,55 @@ public: void setOutputFile( const QString& outputFile ); + enum class DetailType { + NoteSetup, + Instruments, + Effects, + Mixing, + Count + }; + + constexpr static auto DetailCount = static_cast(DetailType::Count); + + int detailLoad(const DetailType type) const + { + return m_detailLoad[static_cast(type)].load(std::memory_order_relaxed); + } + + class Probe + { + public: + Probe(AudioEngineProfiler& profiler, AudioEngineProfiler::DetailType type) + : m_profiler(profiler) + , m_type(type) + { + profiler.startDetail(type); + } + ~Probe() { m_profiler.finishDetail(m_type); } + Probe& operator=(const Probe&) = delete; + Probe(const Probe&) = delete; + Probe(Probe&&) = delete; + + private: + AudioEngineProfiler &m_profiler; + const AudioEngineProfiler::DetailType m_type; + }; private: + void startDetail(const DetailType type) { m_detailTimer[static_cast(type)].reset(); } + void finishDetail(const DetailType type) + { + m_detailTime[static_cast(type)] = m_detailTimer[static_cast(type)].elapsed(); + } + MicroTimer m_periodTimer; - int m_cpuLoad; + std::atomic m_cpuLoad; QFile m_outputFile; + + // Use arrays to avoid dynamic allocations in realtime code + std::array m_detailTimer; + std::array m_detailTime{0}; + std::array, DetailCount> m_detailLoad{0}; }; } // namespace lmms diff --git a/include/CPULoadWidget.h b/include/CPULoadWidget.h index 904445c67..dfa5bac73 100644 --- a/include/CPULoadWidget.h +++ b/include/CPULoadWidget.h @@ -26,6 +26,7 @@ #ifndef LMMS_GUI_CPU_LOAD_WIDGET_H #define LMMS_GUI_CPU_LOAD_WIDGET_H +#include #include #include #include @@ -40,6 +41,7 @@ namespace lmms::gui class CPULoadWidget : public QWidget { Q_OBJECT + Q_PROPERTY(int stepSize MEMBER m_stepSize) public: CPULoadWidget( QWidget * _parent ); ~CPULoadWidget() override = default; @@ -54,6 +56,8 @@ protected slots: private: + int stepSize() const { return std::max(1, m_stepSize); } + int m_currentLoad; QPixmap m_temp; @@ -64,6 +68,8 @@ private: QTimer m_updateTimer; + int m_stepSize; + } ; diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 21a9a3598..2d077babc 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -333,12 +333,9 @@ void AudioEngine::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames ) - -const surroundSampleFrame * AudioEngine::renderNextBuffer() +void AudioEngine::renderStageNoteSetup() { - m_profiler.startPeriod(); - - s_renderingThread = true; + AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::NoteSetup); if( m_clearSignal ) { @@ -387,9 +384,15 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer() m_newPlayHandles.free( e ); e = next; } +} - // STAGE 1: run and render all play handles - AudioEngineWorkerThread::fillJobQueue( m_playHandles ); + + +void AudioEngine::renderStageInstruments() +{ + AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::Instruments); + + AudioEngineWorkerThread::fillJobQueue(m_playHandles); AudioEngineWorkerThread::startAndWaitForJobs(); // removed all play handles which are done @@ -417,16 +420,28 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer() ++it; } } +} + + + +void AudioEngine::renderStageEffects() +{ + AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::Effects); // STAGE 2: process effects of all instrument- and sampletracks AudioEngineWorkerThread::fillJobQueue(m_audioPorts); AudioEngineWorkerThread::startAndWaitForJobs(); +} - // STAGE 3: do master mix in mixer + +void AudioEngine::renderStageMix() +{ + AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::Mixing); + + Mixer *mixer = Engine::mixer(); mixer->masterMix(m_outputBufferWrite); - emit nextAudioBuffer(m_outputBufferRead); runChangesInModel(); @@ -435,10 +450,22 @@ const surroundSampleFrame * AudioEngine::renderNextBuffer() EnvelopeAndLfoParameters::instances()->trigger(); Controller::triggerFrameCounter(); AutomatableModel::incrementPeriodCounter(); +} + + + +const surroundSampleFrame *AudioEngine::renderNextBuffer() +{ + m_profiler.startPeriod(); + s_renderingThread = true; + + renderStageNoteSetup(); // STAGE 0: clear old play handles and buffers, setup new play handles + renderStageInstruments(); // STAGE 1: run and render all play handles + renderStageEffects(); // STAGE 2: process effects of all instrument- and sampletracks + renderStageMix(); // STAGE 3: do master mix in mixer s_renderingThread = false; - - m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod ); + m_profiler.finishPeriod(processingSampleRate(), m_framesPerPeriod); return m_outputBufferRead; } diff --git a/src/core/AudioEngineProfiler.cpp b/src/core/AudioEngineProfiler.cpp index 9e05ff80a..82a412cbb 100644 --- a/src/core/AudioEngineProfiler.cpp +++ b/src/core/AudioEngineProfiler.cpp @@ -24,6 +24,8 @@ #include "AudioEngineProfiler.h" +#include + namespace lmms { @@ -38,10 +40,24 @@ AudioEngineProfiler::AudioEngineProfiler() : void AudioEngineProfiler::finishPeriod( sample_rate_t sampleRate, fpp_t framesPerPeriod ) { - int periodElapsed = m_periodTimer.elapsed(); + // Time taken to process all data and fill the audio buffer. + const unsigned int periodElapsed = m_periodTimer.elapsed(); + // Maximum time the processing can take before causing buffer underflow. Convert to us. + const uint64_t timeLimit = static_cast(1000000) * framesPerPeriod / sampleRate; - const float newCpuLoad = periodElapsed / 10000.0f * sampleRate / framesPerPeriod; - m_cpuLoad = std::clamp((newCpuLoad * 0.1f + m_cpuLoad * 0.9f), 0, 100); + // Compute new overall CPU load and apply exponential averaging. + // The result is used for overload detection in AudioEngine::criticalXRuns() + // → the weight of a new sample must be high enough to allow relatively fast changes! + const auto newCpuLoad = 100.f * periodElapsed / timeLimit; + m_cpuLoad = newCpuLoad * 0.1f + m_cpuLoad * 0.9f; + + // Compute detailed load analysis. Can use stronger averaging to get more stable readout. + for (std::size_t i = 0; i < DetailCount; i++) + { + const auto newLoad = 100.f * m_detailTime[i] / timeLimit; + const auto oldLoad = m_detailLoad[i].load(std::memory_order_relaxed); + m_detailLoad[i].store(newLoad * 0.05f + oldLoad * 0.95f, std::memory_order_relaxed); + } if( m_outputFile.isOpen() ) { diff --git a/src/gui/widgets/CPULoadWidget.cpp b/src/gui/widgets/CPULoadWidget.cpp index 799e037ef..db1f5cacc 100644 --- a/src/gui/widgets/CPULoadWidget.cpp +++ b/src/gui/widgets/CPULoadWidget.cpp @@ -24,6 +24,7 @@ */ +#include #include #include "AudioEngine.h" @@ -72,10 +73,9 @@ void CPULoadWidget::paintEvent( QPaintEvent * ) QPainter p( &m_temp ); p.drawPixmap( 0, 0, m_background ); - // as load-indicator consists of small 2-pixel wide leds with - // 1 pixel spacing, we have to make sure, only whole leds are - // shown which we achieve by the following formula - int w = ( m_leds.width() * m_currentLoad / 300 ) * 3; + // Normally the CPU load indicator moves smoothly, with 1 pixel resolution. However, some themes may want to + // draw discrete elements (like LEDs), so the stepSize property can be used to specify a larger step size. + int w = (m_leds.width() * std::min(m_currentLoad, 100) / (stepSize() * 100)) * stepSize(); if( w > 0 ) { p.drawPixmap( 23, 3, m_leds, 0, 0, w, @@ -91,10 +91,21 @@ void CPULoadWidget::paintEvent( QPaintEvent * ) void CPULoadWidget::updateCpuLoad() { - // smooth load-values a bit - int new_load = ( m_currentLoad + Engine::audioEngine()->cpuLoad() ) / 2; - if( new_load != m_currentLoad ) + // Additional display smoothing for the main load-value. Stronger averaging + // cannot be used directly in the profiler: cpuLoad() must react fast enough + // to be useful as overload indicator in AudioEngine::criticalXRuns(). + const int new_load = (m_currentLoad + Engine::audioEngine()->cpuLoad()) / 2; + + if (new_load != m_currentLoad) { + auto engine = Engine::audioEngine(); + setToolTip( + tr("DSP total: %1%").arg(new_load) + "\n" + + tr(" - Notes and setup: %1%").arg(engine->detailLoad(AudioEngineProfiler::DetailType::NoteSetup)) + "\n" + + tr(" - Instruments: %1%").arg(engine->detailLoad(AudioEngineProfiler::DetailType::Instruments)) + "\n" + + tr(" - Effects: %1%").arg(engine->detailLoad(AudioEngineProfiler::DetailType::Effects)) + "\n" + + tr(" - Mixing: %1%").arg(engine->detailLoad(AudioEngineProfiler::DetailType::Mixing)) + ); m_currentLoad = new_load; m_changed = true; update(); @@ -102,4 +113,4 @@ void CPULoadWidget::updateCpuLoad() } -} // namespace lmms::gui \ No newline at end of file +} // namespace lmms::gui