From f2c815b214dc02bc7292a56d2c7dace45cfa57c6 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:02:49 +0200 Subject: [PATCH 01/20] Remove term "blacklist" (#7365) About the PR: * We use "blocked" as an abstract term, when there may be different reasons * If there is a concrete reason, we use a more concrete word like "unstable" or "not useful" * Double negations like "don't block" or "block unstable" are avoided Besides this, this PR * Lets `Lv2Manager` hide the full `std::set` of plugin URIs * Fixes occurences of "BuffersizeLessThan32" - it is less or equal * Moves `enableBlockedPlugins` from Engine to `ConfigManager` --- include/ConfigManager.h | 1 + include/Engine.h | 2 -- include/Lv2Manager.h | 24 +++++++++++++------ include/PluginIssue.h | 2 +- src/core/ConfigManager.cpp | 8 ++++++- src/core/Engine.cpp | 11 +-------- src/core/PluginIssue.cpp | 8 +++---- src/core/lv2/Lv2Manager.cpp | 48 +++++++++++++++++++++++-------------- src/core/lv2/Lv2Proc.cpp | 25 +++++++++---------- 9 files changed, 74 insertions(+), 55 deletions(-) diff --git a/include/ConfigManager.h b/include/ConfigManager.h index f6239c29783..3cba834e198 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -230,6 +230,7 @@ class LMMS_EXPORT ConfigManager : public QObject QString defaultVersion() const; + static bool enableBlockedPlugins(); static QStringList availableVstEmbedMethods(); QString vstEmbedMethod() const; diff --git a/include/Engine.h b/include/Engine.h index ed4cbd93cbc..7e19e2e8483 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -81,8 +81,6 @@ class LMMS_EXPORT Engine : public QObject return s_projectJournal; } - static bool ignorePluginBlacklist(); - #ifdef LMMS_HAVE_LV2 static class Lv2Manager * getLv2Manager() { diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 58126a0a448..8a9f5168448 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -1,7 +1,7 @@ /* * Lv2Manager.h - Implementation of Lv2Manager class * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -131,14 +131,23 @@ class Lv2Manager AutoLilvNodes findNodes(const LilvNode *subject, const LilvNode *predicate, const LilvNode *object); - static const std::set& getPluginBlacklist() + static bool pluginIsUnstable(const char* pluginUri) { - return pluginBlacklist; + return unstablePlugins.find(pluginUri) != unstablePlugins.end(); } - static const std::set& getPluginBlacklistBuffersizeLessThan32() + static bool pluginIsOnlyUsefulWithUi(const char* pluginUri) { - return pluginBlacklistBuffersizeLessThan32; + return pluginsOnlyUsefulWithUi.find(pluginUri) != pluginsOnlyUsefulWithUi.end(); } + static bool pluginIsUnstableWithBuffersizeLessEqual32(const char* pluginUri) + { + return unstablePluginsBuffersizeLessEqual32.find(pluginUri) != + unstablePluginsBuffersizeLessEqual32.end(); + } + + //! Whether the user generally wants a UI (and we generally support that) + //! Since we do not generally support UI right now, this will always return false... + static bool wantUi(); private: // general data @@ -154,8 +163,9 @@ class Lv2Manager Lv2UridCache m_uridCache; // static - static const std::set - pluginBlacklist, pluginBlacklistBuffersizeLessThan32; + static const std::set unstablePlugins; + static const std::set pluginsOnlyUsefulWithUi; + static const std::set unstablePluginsBuffersizeLessEqual32; // functions bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); diff --git a/include/PluginIssue.h b/include/PluginIssue.h index 01a4268ecc9..b0b9b194612 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -57,7 +57,7 @@ enum class PluginIssueType FeatureNotSupported, //!< plugin requires functionality LMMS can't offer // misc BadPortType, //!< port type not supported - Blacklisted, + Blocked, NoIssue }; diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 19ce80ddb70..d3c973020bf 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -188,6 +188,12 @@ QString ConfigManager::defaultVersion() const return LMMS_VERSION; } +bool ConfigManager::enableBlockedPlugins() +{ + const char* envVar = getenv("LMMS_ENABLE_BLOCKED_PLUGINS"); + return (envVar && *envVar); +} + QStringList ConfigManager::availableVstEmbedMethods() { QStringList methods; @@ -512,7 +518,7 @@ void ConfigManager::loadConfigFile(const QString & configFile) cfg_file.close(); } - // Plugins are searched recursively, blacklist problematic locations + // Plugins are searched recursively, block problematic locations if( m_vstDir.isEmpty() || m_vstDir == QDir::separator() || m_vstDir == "/" || m_vstDir == ensureTrailingSlash( QDir::homePath() ) || !QDir( m_vstDir ).exists() ) diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index c1f609120e3..9435eb69c06 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -126,15 +126,6 @@ void Engine::destroy() -bool Engine::ignorePluginBlacklist() -{ - const char* envVar = getenv("LMMS_IGNORE_BLACKLIST"); - return (envVar && *envVar); -} - - - - float Engine::framesPerTick(sample_rate_t sampleRate) { return sampleRate * 60.0f * 4 / @@ -171,4 +162,4 @@ void *Engine::pickDndPluginKey() Engine * Engine::s_instanceOfMe = nullptr; -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index c9cf3400faa..b40c4723ba4 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -1,7 +1,7 @@ /* - * PluginIssue.h - PluginIssue class + * PluginIssue.cpp - PluginIssue class implementation * - * Copyright (c) 2019 Johannes Lorenz + * Copyright (c) 2019-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -68,8 +68,8 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) return "required feature not supported"; case PluginIssueType::BadPortType: return "unsupported port type"; - case PluginIssueType::Blacklisted: - return "blacklisted plugin"; + case PluginIssueType::Blocked: + return "blocked plugin"; case PluginIssueType::NoIssue: return nullptr; } diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 1633b862616..9807379e44c 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -1,7 +1,7 @@ /* * Lv2Manager.cpp - Implementation of Lv2Manager class * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -36,6 +36,7 @@ #include #include "AudioEngine.h" +#include "ConfigManager.h" #include "Engine.h" #include "Plugin.h" #include "Lv2ControlBase.h" @@ -47,7 +48,7 @@ namespace lmms { -const std::set Lv2Manager::pluginBlacklist = +const std::set Lv2Manager::unstablePlugins = { // github.com/calf-studio-gear/calf, #278 "http://calf.sourceforge.net/plugins/Analyzer", @@ -67,7 +68,13 @@ const std::set Lv2Manager::pluginBlacklist = "http://drobilla.net/plugins/blop/square", "http://drobilla.net/plugins/blop/triangle", - // Visualization, meters, and scopes etc., won't work until we have gui support + // unstable + "urn:juced:DrumSynth" +}; + +const std::set Lv2Manager::pluginsOnlyUsefulWithUi = +{ + // Visualization, meters, and scopes etc., won't work if UI is disabled "http://distrho.sf.net/plugins/ProM", "http://distrho.sf.net/plugins/glBars", "http://gareus.org/oss/lv2/meters#spectr30mono", @@ -132,13 +139,10 @@ const std::set Lv2Manager::pluginBlacklist = "urn:juce:TalFilter2", "urn:juce:Vex", "http://zynaddsubfx.sourceforge.net", - "http://geontime.com/geonkick/single", - - // unstable - "urn:juced:DrumSynth" + "http://geontime.com/geonkick/single" }; -const std::set Lv2Manager::pluginBlacklistBuffersizeLessThan32 = +const std::set Lv2Manager::unstablePluginsBuffersizeLessEqual32 = { "http://moddevices.com/plugins/mod-devel/2Voices", "http://moddevices.com/plugins/mod-devel/Capo", @@ -237,7 +241,7 @@ void Lv2Manager::initPlugins() QElapsedTimer timer; timer.start(); - unsigned blacklisted = 0; + unsigned blocked = 0; LILV_FOREACH(plugins, itr, plugins) { const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr); @@ -266,9 +270,9 @@ void Lv2Manager::initPlugins() { if(std::any_of(issues.begin(), issues.end(), [](const PluginIssue& iss) { - return iss.type() == PluginIssueType::Blacklisted; })) + return iss.type() == PluginIssueType::Blocked; })) { - ++blacklisted; + ++blocked; } } ++pluginCount; @@ -295,19 +299,19 @@ void Lv2Manager::initPlugins() } // TODO: might be better in the LMMS core - if(Engine::ignorePluginBlacklist()) + if(ConfigManager::enableBlockedPlugins()) { qWarning() << - "WARNING! Plugin blacklist disabled! If you want to use the blacklist,\n" - " please set environment variable \"LMMS_IGNORE_BLACKLIST\" to empty or\n" + "WARNING! Blocked plugins enabled! If you want to disable them,\n" + " please set environment variable \"LMMS_ENABLE_BLOCKED_PLUGINS\" to empty or\n" " do not set it."; } - else if(blacklisted > 0) + else if(blocked > 0) { qDebug() << - "Lv2 Plugins blacklisted:" << blacklisted << "of" << pluginCount << "\n" - " If you want to ignore the blacklist (dangerous!), please set\n" - " environment variable \"LMMS_IGNORE_BLACKLIST\" to nonempty."; + "Blocked Lv2 Plugins:" << blocked << "of" << pluginCount << "\n" + " If you want to enable them (dangerous!), please set\n" + " environment variable \"LMMS_ENABLE_BLOCKED_PLUGINS\" to nonempty."; } } @@ -331,6 +335,14 @@ AutoLilvNodes Lv2Manager::findNodes(const LilvNode *subject, +bool Lv2Manager::wantUi() +{ + return false; +} + + + + // unused + untested yet bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) { diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 27d18ef27eb..7cd9d3a509b 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -1,7 +1,7 @@ /* * Lv2Proc.cpp - Lv2 processor class * - * Copyright (c) 2019-2022 Johannes Lorenz + * Copyright (c) 2019-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -38,6 +38,7 @@ #include "AudioEngine.h" #include "AutomatableModel.h" #include "ComboBoxModel.h" +#include "ConfigManager.h" #include "Engine.h" #include "Lv2Features.h" #include "Lv2Manager.h" @@ -74,21 +75,21 @@ Plugin::Type Lv2Proc::check(const LilvPlugin *plugin, const char* pluginUri = lilv_node_as_uri(lilv_plugin_get_uri(plugin)); //qDebug() << "Checking plugin" << pluginUri << "..."; - // TODO: manage a global blacklist outside of the code + // TODO: manage a global list of blocked plugins outside of the code // for now, this will help // this is only a fix for the meantime - if (!Engine::ignorePluginBlacklist()) + if (!ConfigManager::enableBlockedPlugins()) { - const auto& pluginBlacklist = Lv2Manager::getPluginBlacklist(); - const auto& pluginBlacklist32 = Lv2Manager::getPluginBlacklistBuffersizeLessThan32(); - if(pluginBlacklist.find(pluginUri) != pluginBlacklist.end()) + if( // plugin unstable? + Lv2Manager::pluginIsUnstable(pluginUri) || + // plugins only useful with UI? + (!Lv2Manager::wantUi() && + Lv2Manager::pluginIsOnlyUsefulWithUi(pluginUri)) || + // plugin unstable with 32 or less fpp? + (Engine::audioEngine()->framesPerPeriod() <= 32 && + Lv2Manager::pluginIsUnstableWithBuffersizeLessEqual32(pluginUri)) ) { - issues.emplace_back(PluginIssueType::Blacklisted); - } - else if(Engine::audioEngine()->framesPerPeriod() <= 32 && - pluginBlacklist32.find(pluginUri) != pluginBlacklist32.end()) - { - issues.emplace_back(PluginIssueType::Blacklisted); // currently no special blacklist category + issues.emplace_back(PluginIssueType::Blocked); } } From 1420a1f7e81578aed80e0af96bff11ede98fcc45 Mon Sep 17 00:00:00 2001 From: saker Date: Tue, 9 Jul 2024 06:27:33 -0400 Subject: [PATCH 02/20] Define `fpp_t` and `f_cnt_t` to be of `size_t` (#7363) Defines `fpp_t` and `f_cnt_t` to be of `size_t` within `lmms_basics.h`. Most of the codebase used `fpp_t` to represent the size of an array of sample frames, which is incorrect, given that `fpp_t` was only 16 bits when sizes greater than 32767 are very possible. `f_cnt_t` was defined as `size_t` as well for consistency. --------- Co-authored-by: Dominic Clark --- include/InstrumentFunctions.h | 7 +- include/Piano.h | 4 +- include/SampleFrame.h | 3 +- include/lmms_basics.h | 7 +- .../AudioFileProcessor/AudioFileProcessor.cpp | 5 +- .../AudioFileProcessor/AudioFileProcessor.h | 3 +- .../AudioFileProcessorWaveView.cpp | 68 +++++++++---------- .../AudioFileProcessorWaveView.h | 20 +++--- plugins/Lb302/Lb302.cpp | 4 +- plugins/Lv2Instrument/Lv2Instrument.h | 3 +- plugins/MultitapEcho/MultitapEcho.cpp | 7 +- plugins/Sf2Player/Sf2Player.cpp | 2 +- plugins/Sf2Player/Sf2Player.h | 1 + plugins/Sfxr/Sfxr.h | 2 + plugins/Vibed/VibratingString.cpp | 3 +- plugins/ZynAddSubFx/LocalZynAddSubFx.h | 2 + src/core/AudioEngine.cpp | 2 +- src/core/Instrument.cpp | 3 +- src/core/audio/AudioSdl.cpp | 3 +- src/core/midi/MidiClient.cpp | 6 +- src/gui/widgets/LedCheckBox.cpp | 3 +- 21 files changed, 84 insertions(+), 74 deletions(-) diff --git a/include/InstrumentFunctions.h b/include/InstrumentFunctions.h index 59c651a68c4..aa2e6de6862 100644 --- a/include/InstrumentFunctions.h +++ b/include/InstrumentFunctions.h @@ -25,11 +25,12 @@ #ifndef LMMS_INSTRUMENT_FUNCTIONS_H #define LMMS_INSTRUMENT_FUNCTIONS_H -#include "JournallingObject.h" -#include "lmms_basics.h" +#include + #include "AutomatableModel.h" -#include "TempoSyncKnobModel.h" #include "ComboBoxModel.h" +#include "JournallingObject.h" +#include "TempoSyncKnobModel.h" namespace lmms { diff --git a/include/Piano.h b/include/Piano.h index 698d9c8febc..08dfda4ddbc 100644 --- a/include/Piano.h +++ b/include/Piano.h @@ -25,8 +25,10 @@ #ifndef LMMS_PIANO_H #define LMMS_PIANO_H -#include "Note.h" +#include + #include "Model.h" +#include "Note.h" namespace lmms { diff --git a/include/SampleFrame.h b/include/SampleFrame.h index 533c8abf442..238a85dead9 100644 --- a/include/SampleFrame.h +++ b/include/SampleFrame.h @@ -28,8 +28,9 @@ #include "lmms_basics.h" +#include #include -#include +#include namespace lmms diff --git a/include/lmms_basics.h b/include/lmms_basics.h index 4695b4f8a56..57ef647f50e 100644 --- a/include/lmms_basics.h +++ b/include/lmms_basics.h @@ -31,10 +31,7 @@ #include "lmmsconfig.h" #include -#include -#include -#include namespace lmms @@ -49,8 +46,8 @@ using sample_t = float; // standard sample-type using int_sample_t = int16_t; // 16-bit-int-sample using sample_rate_t = uint32_t; // sample-rate -using fpp_t = int16_t; // frames per period (0-16384) -using f_cnt_t = int32_t; // standard frame-count +using fpp_t = size_t; // frames per period (0-16384) +using f_cnt_t = size_t; // standard frame-count using ch_cnt_t = uint8_t; // channel-count (0-DEFAULT_CHANNELS) using bpm_t = uint16_t; // tempo (MIN_BPM to MAX_BPM) using bitrate_t = uint16_t; // bitrate in kbps diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index 767d15e735f..b10c065b29d 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -30,6 +30,7 @@ #include "SampleLoader.h" #include "Song.h" +#include "lmms_basics.h" #include "plugin_export.h" #include @@ -276,7 +277,7 @@ QString AudioFileProcessor::nodeName() const -auto AudioFileProcessor::beatLen(NotePlayHandle* note) const -> int +auto AudioFileProcessor::beatLen(NotePlayHandle* note) const -> f_cnt_t { // If we can play indefinitely, use the default beat note duration if (static_cast(m_loopModel.value()) != Sample::Loop::Off) { return 0; } @@ -292,7 +293,7 @@ auto AudioFileProcessor::beatLen(NotePlayHandle* note) const -> int : m_nextPlayStartPoint; const auto duration = m_sample.endFrame() - startFrame; - return static_cast(std::floor(duration * freqFactor)); + return static_cast(std::floor(duration * freqFactor)); } diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.h b/plugins/AudioFileProcessor/AudioFileProcessor.h index b39342fd1fa..acdbc45f722 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.h +++ b/plugins/AudioFileProcessor/AudioFileProcessor.h @@ -32,6 +32,7 @@ #include "Instrument.h" #include "Sample.h" +#include "lmms_basics.h" namespace lmms @@ -54,7 +55,7 @@ class AudioFileProcessor : public Instrument QString nodeName() const override; - auto beatLen(NotePlayHandle* note) const -> int override; + auto beatLen(NotePlayHandle* note) const -> f_cnt_t override; float desiredReleaseTimeMs() const override { diff --git a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp index 1742ee3a7cd..2a07e5f77a1 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp @@ -44,23 +44,23 @@ void AudioFileProcessorWaveView::updateSampleRange() { if (m_sample->sampleSize() > 1) { - const f_cnt_t marging = (m_sample->endFrame() - m_sample->startFrame()) * 0.1; + const auto marging = (m_sample->endFrame() - m_sample->startFrame()) * 0.1; setFrom(m_sample->startFrame() - marging); setTo(m_sample->endFrame() + marging); } } -void AudioFileProcessorWaveView::setTo(f_cnt_t to) +void AudioFileProcessorWaveView::setTo(int to) { - m_to = std::min(to, static_cast(m_sample->sampleSize())); + m_to = std::min(to, static_cast(m_sample->sampleSize())); } -void AudioFileProcessorWaveView::setFrom(f_cnt_t from) +void AudioFileProcessorWaveView::setFrom(int from) { m_from = std::max(from, 0); } -f_cnt_t AudioFileProcessorWaveView::range() const +int AudioFileProcessorWaveView::range() const { return m_to - m_from; } @@ -196,7 +196,7 @@ void AudioFileProcessorWaveView::paintEvent(QPaintEvent * pe) p.drawPixmap(s_padding, s_padding, m_graph); const QRect graph_rect(s_padding, s_padding, width() - 2 * s_padding, height() - 2 * s_padding); - const f_cnt_t frames = range(); + const auto frames = range(); m_startFrameX = graph_rect.x() + (m_sample->startFrame() - m_from) * double(graph_rect.width()) / frames; m_endFrameX = graph_rect.x() + (m_sample->endFrame() - m_from) * @@ -341,31 +341,32 @@ void AudioFileProcessorWaveView::updateGraph() void AudioFileProcessorWaveView::zoom(const bool out) { - const f_cnt_t start = m_sample->startFrame(); - const f_cnt_t end = m_sample->endFrame(); - const f_cnt_t frames = m_sample->sampleSize(); - const f_cnt_t d_from = start - m_from; - const f_cnt_t d_to = m_to - end; + const auto start = m_sample->startFrame(); + const auto end = m_sample->endFrame(); + const auto frames = m_sample->sampleSize(); - const f_cnt_t step = qMax(1, qMax(d_from, d_to) / 10); - const f_cnt_t step_from = (out ? - step : step); - const f_cnt_t step_to = (out ? step : - step); + const auto dFrom = start - m_from; + const auto dTo = m_to - end; - const double comp_ratio = double(qMin(d_from, d_to)) - / qMax(1, qMax(d_from, d_to)); + const auto step = std::max(1.0, std::max(dFrom, dTo) / 10.0); + const auto stepFrom = out ? -step : step; + const auto stepTo = out ? step : -step; - const auto boundedFrom = std::clamp(m_from + step_from, 0, start); - const auto boundedTo = std::clamp(m_to + step_to, end, frames); + const auto boundedFrom = std::clamp(m_from + stepFrom, 0.0, static_cast(start)); + const auto boundedTo = std::clamp(m_to + stepTo, static_cast(end), static_cast(frames)); - const auto toStep = static_cast(step_from * (boundedTo == m_to ? 1 : comp_ratio)); - const auto newFrom - = (out && d_from < d_to) || (!out && d_to < d_from) ? boundedFrom : std::clamp(m_from + toStep, 0, start); + const auto compRatio = std::min(dFrom, dTo) / static_cast(std::max(1, std::max(dFrom, dTo))); + const auto toStep = stepFrom * (boundedTo == m_to ? 1 : compRatio); + const auto newFrom = (out && dFrom < dTo) || (!out && dTo < dFrom) + ? boundedFrom + : std::clamp(m_from + toStep, 0.0, static_cast(start)); - const auto fromStep = static_cast(step_to * (boundedFrom == m_from ? 1 : comp_ratio)); - const auto newTo - = (out && d_from < d_to) || (!out && d_to < d_from) ? std::clamp(m_to + fromStep, end, frames) : boundedTo; + const auto fromStep = stepTo * (boundedFrom == m_from ? 1 : compRatio); + const auto newTo = (out && dFrom < dTo) || (!out && dTo < dFrom) + ? std::clamp(m_to + fromStep, static_cast(end), static_cast(frames)) + : boundedTo; - if (static_cast(newTo - newFrom) / m_sample->sampleRate() > 0.05) + if ((newTo - newFrom) / m_sample->sampleRate() > 0.05) { setFrom(newFrom); setTo(newTo); @@ -375,16 +376,11 @@ void AudioFileProcessorWaveView::zoom(const bool out) void AudioFileProcessorWaveView::slide(int px) { const double fact = qAbs(double(px) / width()); - f_cnt_t step = range() * fact; - if (px > 0) - { - step = -step; - } + auto step = range() * fact * (px > 0 ? -1 : 1); - f_cnt_t step_from = qBound(0, m_from + step, m_sample->sampleSize()) - m_from; - f_cnt_t step_to = qBound(m_from + 1, m_to + step, m_sample->sampleSize()) - m_to; - - step = qAbs(step_from) < qAbs(step_to) ? step_from : step_to; + const auto stepFrom = std::clamp(m_from + step, 0.0, static_cast(m_sample->sampleSize())) - m_from; + const auto stepTo = std::clamp(m_to + step, m_from + 1.0, static_cast(m_sample->sampleSize())) - m_to; + step = std::abs(stepFrom) < std::abs(stepTo) ? stepFrom : stepTo; setFrom(m_from + step); setTo(m_to + step); @@ -465,10 +461,8 @@ void AudioFileProcessorWaveView::reverse() - m_sample->startFrame() ); - const f_cnt_t from = m_from; setFrom(m_sample->sampleSize() - m_to); - setTo(m_sample->sampleSize() - from); - + setTo(m_sample->sampleSize() - m_from); m_reversed = ! m_reversed; } diff --git a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h index f40b69d121f..8081d20ca63 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h +++ b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h @@ -125,17 +125,17 @@ public slots: Sample const* m_sample; QPixmap m_graph; - f_cnt_t m_from; - f_cnt_t m_to; - f_cnt_t m_last_from; - f_cnt_t m_last_to; + int m_from; + int m_to; + int m_last_from; + int m_last_to; float m_last_amp; knob* m_startKnob; knob* m_endKnob; knob* m_loopKnob; - f_cnt_t m_startFrameX; - f_cnt_t m_endFrameX; - f_cnt_t m_loopFrameX; + int m_startFrameX; + int m_endFrameX; + int m_loopFrameX; bool m_isDragging; QPoint m_draggingLastPoint; DraggingType m_draggingType; @@ -152,9 +152,9 @@ public slots: void updateSampleRange(); private: - void setTo(f_cnt_t to); - void setFrom(f_cnt_t from); - f_cnt_t range() const; + void setTo(int to); + void setFrom(int from); + int range() const; void zoom(const bool out = false); void slide(int px); void slideSamplePointByPx(Point point, int px); diff --git a/plugins/Lb302/Lb302.cpp b/plugins/Lb302/Lb302.cpp index 02038239ae1..0041b0c5127 100644 --- a/plugins/Lb302/Lb302.cpp +++ b/plugins/Lb302/Lb302.cpp @@ -750,8 +750,8 @@ void Lb302Synth::playNote( NotePlayHandle * _n, SampleFrame* _working_buffer ) m_notes.prepend( _n ); } m_notesMutex.unlock(); - - release_frame = qMax( release_frame, _n->framesLeft() + _n->offset() ); + + release_frame = std::max(release_frame, static_cast(_n->framesLeft()) + static_cast(_n->offset())); } diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index 268e7fd10c1..9fbc7b7f699 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -26,12 +26,13 @@ #define LV2_INSTRUMENT_H #include +#include #include "Instrument.h" #include "InstrumentView.h" -#include "Note.h" #include "Lv2ControlBase.h" #include "Lv2ViewBase.h" +#include "Note.h" // whether to use MIDI vs playHandle // currently only MIDI works diff --git a/plugins/MultitapEcho/MultitapEcho.cpp b/plugins/MultitapEcho/MultitapEcho.cpp index e779f228364..6d8da26e396 100644 --- a/plugins/MultitapEcho/MultitapEcho.cpp +++ b/plugins/MultitapEcho/MultitapEcho.cpp @@ -25,6 +25,7 @@ #include "MultitapEcho.h" #include "embed.h" +#include "lmms_basics.h" #include "plugin_export.h" namespace lmms @@ -118,8 +119,8 @@ bool MultitapEchoEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frame } // add dry buffer - never swap inputs for dry - m_buffer.writeAddingMultiplied( buf, 0, frames, dryGain ); - + m_buffer.writeAddingMultiplied(buf, f_cnt_t{0}, frames, dryGain); + // swapped inputs? if( swapInputs ) { @@ -176,4 +177,4 @@ PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* parent, void* data ) } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/plugins/Sf2Player/Sf2Player.cpp b/plugins/Sf2Player/Sf2Player.cpp index fd1d6b1a3ad..14868bf0e69 100644 --- a/plugins/Sf2Player/Sf2Player.cpp +++ b/plugins/Sf2Player/Sf2Player.cpp @@ -900,7 +900,7 @@ void Sf2Instrument::renderFrames( f_cnt_t frames, SampleFrame* buf ) } if( src_data.output_frames_gen > frames ) { - qCritical( "Sf2Instrument: not enough frames: %ld / %d", src_data.output_frames_gen, frames ); + qCritical("Sf2Instrument: not enough frames: %ld / %zu", src_data.output_frames_gen, frames); } } else diff --git a/plugins/Sf2Player/Sf2Player.h b/plugins/Sf2Player/Sf2Player.h index b752cd9c35f..b63dd48e988 100644 --- a/plugins/Sf2Player/Sf2Player.h +++ b/plugins/Sf2Player/Sf2Player.h @@ -27,6 +27,7 @@ #ifndef SF2_PLAYER_H #define SF2_PLAYER_H +#include #include #include #include diff --git a/plugins/Sfxr/Sfxr.h b/plugins/Sfxr/Sfxr.h index b14fe2de095..c245ca64cc2 100644 --- a/plugins/Sfxr/Sfxr.h +++ b/plugins/Sfxr/Sfxr.h @@ -28,6 +28,8 @@ #ifndef SFXR_H #define SFXR_H +#include + #include "AutomatableModel.h" #include "Instrument.h" #include "InstrumentView.h" diff --git a/plugins/Vibed/VibratingString.cpp b/plugins/Vibed/VibratingString.cpp index 216a5fbf333..e4bb760e255 100644 --- a/plugins/Vibed/VibratingString.cpp +++ b/plugins/Vibed/VibratingString.cpp @@ -26,6 +26,7 @@ #include "interpolation.h" #include "AudioEngine.h" #include "Engine.h" +#include "lmms_basics.h" #include #include @@ -99,7 +100,7 @@ void VibratingString::resample(const float* src, f_cnt_t srcFrames, f_cnt_t dstF { 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); + const f_cnt_t srcFrame = std::clamp(static_cast(srcFrameFloat), f_cnt_t{1}, srcFrames - 3); m_impulse[frame] = cubicInterpolate( src[srcFrame - 1], src[srcFrame + 0], diff --git a/plugins/ZynAddSubFx/LocalZynAddSubFx.h b/plugins/ZynAddSubFx/LocalZynAddSubFx.h index 096534d5ebd..5ef735f6472 100644 --- a/plugins/ZynAddSubFx/LocalZynAddSubFx.h +++ b/plugins/ZynAddSubFx/LocalZynAddSubFx.h @@ -25,6 +25,8 @@ #ifndef LOCAL_ZYNADDSUBFX_H #define LOCAL_ZYNADDSUBFX_H +#include + #include "Note.h" class Master; diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 8e4cc8e29c7..157b6fe65a7 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -283,7 +283,7 @@ void AudioEngine::pushInputFrames( SampleFrame* _ab, const f_cnt_t _frames ) requestChangeInModel(); f_cnt_t frames = m_inputBufferFrames[ m_inputBufferWrite ]; - int size = m_inputBufferSize[ m_inputBufferWrite ]; + auto size = m_inputBufferSize[m_inputBufferWrite]; SampleFrame* buf = m_inputBuffer[ m_inputBufferWrite ]; if( frames + _frames > size ) diff --git a/src/core/Instrument.cpp b/src/core/Instrument.cpp index 893fccbf502..9237ad70dbe 100644 --- a/src/core/Instrument.cpp +++ b/src/core/Instrument.cpp @@ -28,6 +28,7 @@ #include "DummyInstrument.h" #include "InstrumentTrack.h" +#include "lmms_basics.h" #include "lmms_constants.h" @@ -185,7 +186,7 @@ void Instrument::applyRelease( SampleFrame* buf, const NotePlayHandle * _n ) const auto releaseFrames = desiredReleaseFrames(); const auto endFrame = _n->framesLeft(); - const auto startFrame = std::max(0, endFrame - releaseFrames); + const auto startFrame = endFrame - std::min(endFrame, releaseFrames); for (auto f = startFrame; f < endFrame && f < fpp; f++) { diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index 2a541404956..f5876672580 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -23,6 +23,7 @@ */ #include "AudioSdl.h" +#include "lmms_basics.h" #ifdef LMMS_HAVE_SDL @@ -69,7 +70,7 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : // to convert the buffers #endif m_audioHandle.channels = channels(); - m_audioHandle.samples = std::max(1024, audioEngine()->framesPerPeriod() * 2); + m_audioHandle.samples = std::max(f_cnt_t{1024}, audioEngine()->framesPerPeriod() * 2); m_audioHandle.callback = sdlAudioCallback; m_audioHandle.userdata = this; diff --git a/src/core/midi/MidiClient.cpp b/src/core/midi/MidiClient.cpp index 030384c5e1e..a4e6ff097b2 100644 --- a/src/core/midi/MidiClient.cpp +++ b/src/core/midi/MidiClient.cpp @@ -24,8 +24,10 @@ */ #include "MidiClient.h" -#include "MidiPort.h" +#include + +#include "MidiPort.h" namespace lmms { @@ -309,4 +311,4 @@ int MidiClientRaw::eventLength( const unsigned char event ) return 1; } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/src/gui/widgets/LedCheckBox.cpp b/src/gui/widgets/LedCheckBox.cpp index 42e49a8ae2b..c26e2103948 100644 --- a/src/gui/widgets/LedCheckBox.cpp +++ b/src/gui/widgets/LedCheckBox.cpp @@ -22,11 +22,12 @@ * */ +#include "LedCheckBox.h" #include #include +#include -#include "LedCheckBox.h" #include "DeprecationHelper.h" #include "embed.h" #include "gui_templates.h" From 12632e6b5e821b852ca878fae9ab5f0bb8b83e0f Mon Sep 17 00:00:00 2001 From: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com> Date: Sun, 14 Jul 2024 02:31:31 +0530 Subject: [PATCH 03/20] Remove `BufferManager::clear` (#7378) --- include/BufferManager.h | 4 ---- src/core/AudioEngine.cpp | 2 +- src/core/BufferManager.cpp | 4 ---- src/core/Mixer.cpp | 8 +++----- src/core/Oscillator.cpp | 2 +- src/core/PlayHandle.cpp | 2 +- src/core/RemotePlugin.cpp | 6 +++--- src/core/audio/AudioPort.cpp | 2 +- src/gui/widgets/Oscilloscope.cpp | 2 +- 9 files changed, 11 insertions(+), 21 deletions(-) diff --git a/include/BufferManager.h b/include/BufferManager.h index 98f6703db02..84602f1215d 100644 --- a/include/BufferManager.h +++ b/include/BufferManager.h @@ -39,10 +39,6 @@ class LMMS_EXPORT BufferManager public: static void init( fpp_t fpp ); static SampleFrame* acquire(); - // audio-buffer-mgm - static void clear( SampleFrame* ab, const f_cnt_t frames, - const f_cnt_t offset = 0 ); - static void release( SampleFrame* buf ); private: diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 157b6fe65a7..42e7079b016 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -95,7 +95,7 @@ AudioEngine::AudioEngine( bool renderOnly ) : m_inputBufferFrames[i] = 0; m_inputBufferSize[i] = DEFAULT_BUFFER_SIZE * 100; m_inputBuffer[i] = new SampleFrame[ DEFAULT_BUFFER_SIZE * 100 ]; - BufferManager::clear( m_inputBuffer[i], m_inputBufferSize[i] ); + zeroSampleFrames(m_inputBuffer[i], m_inputBufferSize[i]); } // determine FIFO size and number of frames per period diff --git a/src/core/BufferManager.cpp b/src/core/BufferManager.cpp index a7a051e260b..47598c63325 100644 --- a/src/core/BufferManager.cpp +++ b/src/core/BufferManager.cpp @@ -47,10 +47,6 @@ SampleFrame* BufferManager::acquire() return new SampleFrame[s_framesPerPeriod]; } -void BufferManager::clear( SampleFrame* ab, const f_cnt_t frames, const f_cnt_t offset ) -{ - zeroSampleFrames(ab + offset, frames); -} void BufferManager::release( SampleFrame* buf ) diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 0b0689eb54d..ba02296f84b 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -74,7 +74,7 @@ MixerChannel::MixerChannel( int idx, Model * _parent ) : m_queued( false ), m_dependenciesMet(0) { - BufferManager::clear( m_buffer, Engine::audioEngine()->framesPerPeriod() ); + zeroSampleFrames(m_buffer, Engine::audioEngine()->framesPerPeriod()); } @@ -612,8 +612,7 @@ void Mixer::mixToChannel( const SampleFrame* _buf, mix_ch_t _ch ) void Mixer::prepareMasterMix() { - BufferManager::clear( m_mixerChannels[0]->m_buffer, - Engine::audioEngine()->framesPerPeriod() ); + zeroSampleFrames(m_mixerChannels[0]->m_buffer, Engine::audioEngine()->framesPerPeriod()); } @@ -685,8 +684,7 @@ void Mixer::masterMix( SampleFrame* _buf ) // reset channel process state for( int i = 0; i < numChannels(); ++i) { - BufferManager::clear( m_mixerChannels[i]->m_buffer, - Engine::audioEngine()->framesPerPeriod() ); + zeroSampleFrames(m_mixerChannels[i]->m_buffer, Engine::audioEngine()->framesPerPeriod()); m_mixerChannels[i]->reset(); m_mixerChannels[i]->m_queued = false; // also reset hasInput diff --git a/src/core/Oscillator.cpp b/src/core/Oscillator.cpp index d24e82d9888..4cbd9ccb451 100644 --- a/src/core/Oscillator.cpp +++ b/src/core/Oscillator.cpp @@ -81,7 +81,7 @@ void Oscillator::update(SampleFrame* ab, const fpp_t frames, const ch_cnt_t chnl { if (m_freq >= Engine::audioEngine()->outputSampleRate() / 2) { - BufferManager::clear(ab, frames); + zeroSampleFrames(ab, frames); return; } // If this oscillator is used to PM or PF modulate another oscillator, take a note. diff --git a/src/core/PlayHandle.cpp b/src/core/PlayHandle.cpp index eb90f6b65b1..134fcd31100 100644 --- a/src/core/PlayHandle.cpp +++ b/src/core/PlayHandle.cpp @@ -55,7 +55,7 @@ void PlayHandle::doProcessing() if( m_usesBuffer ) { m_bufferReleased = false; - BufferManager::clear(m_playHandleBuffer, Engine::audioEngine()->framesPerPeriod()); + zeroSampleFrames(m_playHandleBuffer, Engine::audioEngine()->framesPerPeriod()); play( buffer() ); } else diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index 4cfcc313c5e..dc26bf2b554 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -333,7 +333,7 @@ bool RemotePlugin::process( const SampleFrame* _in_buf, SampleFrame* _out_buf ) { if( _out_buf != nullptr ) { - BufferManager::clear( _out_buf, frames ); + zeroSampleFrames(_out_buf, frames); } return false; } @@ -352,7 +352,7 @@ bool RemotePlugin::process( const SampleFrame* _in_buf, SampleFrame* _out_buf ) } if( _out_buf != nullptr ) { - BufferManager::clear( _out_buf, frames ); + zeroSampleFrames(_out_buf, frames); } return false; } @@ -426,7 +426,7 @@ bool RemotePlugin::process( const SampleFrame* _in_buf, SampleFrame* _out_buf ) { auto o = (SampleFrame*)(m_audioBuffer.get() + m_inputCount * frames); // clear buffer, if plugin didn't fill up both channels - BufferManager::clear( _out_buf, frames ); + zeroSampleFrames(_out_buf, frames); for (ch_cnt_t ch = 0; ch < std::min(DEFAULT_CHANNELS, outputs); ++ch) diff --git a/src/core/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index 7bae3db1cf5..8efdd2c11a7 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -113,7 +113,7 @@ void AudioPort::doProcessing() const fpp_t fpp = Engine::audioEngine()->framesPerPeriod(); // clear the buffer - BufferManager::clear( m_portBuffer, fpp ); + zeroSampleFrames(m_portBuffer, fpp); //qDebug( "Playhandles: %d", m_playHandles.size() ); for( PlayHandle * ph : m_playHandles ) // now we mix all playhandle buffers into the audioport buffer diff --git a/src/gui/widgets/Oscilloscope.cpp b/src/gui/widgets/Oscilloscope.cpp index 8cb840592a6..3178d7ef254 100644 --- a/src/gui/widgets/Oscilloscope.cpp +++ b/src/gui/widgets/Oscilloscope.cpp @@ -57,7 +57,7 @@ Oscilloscope::Oscilloscope( QWidget * _p ) : const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); m_buffer = new SampleFrame[frames]; - BufferManager::clear( m_buffer, frames ); + zeroSampleFrames(m_buffer, frames); setToolTip(tr("Oscilloscope")); From 9c0fc8fc6994b87ece6e706104877b715966af9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petar=20Kati=C4=87?= Date: Sat, 20 Jul 2024 13:06:51 +0200 Subject: [PATCH 04/20] Made it so ZynAddSubFx isn't lowpassed by default (#7381) In this repo, the only changes made are updating the according submodule and changing the FREQ knob in ZynAddSubFx's LMMS GUI. --- plugins/ZynAddSubFx/ZynAddSubFx.cpp | 2 +- plugins/ZynAddSubFx/zynaddsubfx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/ZynAddSubFx/ZynAddSubFx.cpp b/plugins/ZynAddSubFx/ZynAddSubFx.cpp index c0287cd8bb7..77b242199f8 100644 --- a/plugins/ZynAddSubFx/ZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/ZynAddSubFx.cpp @@ -108,7 +108,7 @@ ZynAddSubFxInstrument::ZynAddSubFxInstrument( m_plugin( nullptr ), m_remotePlugin( nullptr ), m_portamentoModel( 0, 0, 127, 1, this, tr( "Portamento" ) ), - m_filterFreqModel( 64, 0, 127, 1, this, tr( "Filter frequency" ) ), + m_filterFreqModel( 127, 0, 127, 1, this, tr( "Filter frequency" ) ), m_filterQModel( 64, 0, 127, 1, this, tr( "Filter resonance" ) ), m_bandwidthModel( 64, 0, 127, 1, this, tr( "Bandwidth" ) ), m_fmGainModel( 127, 0, 127, 1, this, tr( "FM gain" ) ), diff --git a/plugins/ZynAddSubFx/zynaddsubfx b/plugins/ZynAddSubFx/zynaddsubfx index aac04bc55c4..9499523f703 160000 --- a/plugins/ZynAddSubFx/zynaddsubfx +++ b/plugins/ZynAddSubFx/zynaddsubfx @@ -1 +1 @@ -Subproject commit aac04bc55c4114460009897686b597f98adfaa65 +Subproject commit 9499523f70322b6034673566898300e7543fdf3e From 851c884f58155275e6adbde40f2611d076edd345 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 21 Jul 2024 22:34:34 +0100 Subject: [PATCH 05/20] Re-enable disabled GCC warnings where possible (#7379) --- CMakeLists.txt | 18 +++--- include/AudioEngineWorkerThread.h | 4 +- include/AudioPortAudio.h | 10 +--- include/LocklessAllocator.h | 4 +- include/MidiEvent.h | 4 +- include/Mixer.h | 2 +- include/Oscillator.h | 16 ++--- include/RemotePlugin.h | 4 +- include/RmsHelper.h | 8 +-- include/Track.h | 2 +- include/fft_helpers.h | 2 +- include/lmms_math.h | 5 +- .../AudioFileProcessor/AudioFileProcessor.cpp | 4 +- plugins/Bitcrush/Bitcrush.cpp | 8 +-- plugins/CarlaBase/Carla.cpp | 24 ++++---- plugins/CarlaBase/Carla.h | 6 +- plugins/CrossoverEQ/CrossoverEQ.cpp | 12 ++-- plugins/Eq/EqSpectrumView.cpp | 2 +- plugins/FreeBoy/FreeBoy.cpp | 12 ++-- plugins/GigPlayer/CMakeLists.txt | 5 -- plugins/GigPlayer/GigPlayer.cpp | 4 +- .../GranularPitchShifterEffect.cpp | 2 +- .../GranularPitchShifterEffect.h | 2 +- plugins/LadspaEffect/LadspaEffect.cpp | 2 +- plugins/Monstro/Monstro.cpp | 4 +- plugins/Monstro/Monstro.h | 2 +- plugins/MultitapEcho/MultitapEcho.cpp | 4 +- plugins/Organic/Organic.cpp | 2 +- .../PeakControllerEffect.cpp | 6 +- plugins/ReverbSC/base.c | 2 +- plugins/Sf2Player/Sf2Player.cpp | 58 ++++++++++++------- plugins/Sid/SidInstrument.cpp | 14 +++-- plugins/SlicerT/SlicerT.cpp | 16 ++--- plugins/SlicerT/SlicerTWaveform.cpp | 6 +- plugins/SpectrumAnalyzer/SaProcessor.cpp | 8 ++- plugins/SpectrumAnalyzer/SaSpectrumView.cpp | 2 +- plugins/StereoEnhancer/StereoEnhancer.cpp | 2 +- plugins/Vibed/Vibed.cpp | 4 +- plugins/VstBase/RemoteVstPlugin.cpp | 13 ++--- plugins/Xpressive/ExprSynth.cpp | 9 +-- plugins/Xpressive/Xpressive.cpp | 4 +- plugins/ZynAddSubFx/CMakeLists.txt | 6 +- src/core/AudioEngineWorkerThread.cpp | 2 +- src/core/ControllerConnection.cpp | 7 +-- src/core/DataFile.cpp | 2 +- src/core/EnvelopeAndLfoParameters.cpp | 5 -- src/core/InstrumentSoundShaping.cpp | 10 ++-- src/core/LocklessAllocator.cpp | 5 +- src/core/Microtuner.cpp | 8 +-- src/core/Mixer.cpp | 21 +++---- src/core/RingBuffer.cpp | 2 - src/core/Sample.cpp | 4 +- src/core/SampleDecoder.cpp | 2 +- src/core/Song.cpp | 6 +- src/core/Track.cpp | 6 +- src/core/audio/AudioOss.cpp | 7 +-- src/core/audio/AudioPortAudio.cpp | 8 +-- src/gui/MainApplication.cpp | 2 +- src/gui/MicrotunerConfig.cpp | 8 +-- src/gui/SampleWaveform.cpp | 10 ++-- src/gui/clips/ClipView.cpp | 5 +- src/gui/editors/PatternEditor.cpp | 4 +- src/gui/editors/PianoRoll.cpp | 2 +- .../instrument/InstrumentSoundShapingView.cpp | 4 +- src/gui/modals/ExportProjectDialog.cpp | 4 +- src/gui/tracks/TrackContentWidget.cpp | 2 +- src/gui/widgets/Oscilloscope.cpp | 2 +- src/tracks/MidiClip.cpp | 1 - 68 files changed, 222 insertions(+), 241 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9dbda4ccbd..9a6d97e8d87 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -646,22 +646,26 @@ option(USE_WERROR "Treat compiler warnings as errors" OFF) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") set(COMPILE_ERROR_FLAGS "-Wall" # Enable most warnings by default - "-Werror=unused-function" # Unused functions are an error - # TODO: Fix the code and remove the following: - "-Wno-sign-compare" # Permit comparisons between signed and unsigned integers - "-Wno-strict-overflow" # Permit optimisations assuming no signed overflow ) set(THIRD_PARTY_COMPILE_ERROR_FLAGS "-w" # Disable all warnings ) - # Due to a regression in gcc-4.8.X, we need to disable array-bounds check - # TODO: Is this still necessary? if(CMAKE_COMPILER_IS_GNUCXX) list(APPEND COMPILE_ERROR_FLAGS + # The following warning generates false positives that are difficult + # to work around, in particular when inlining calls to standard + # algorithms performed on single-element arrays. See, for example, + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111273. "-Wno-array-bounds" # Permit out-of-bounds array subscripts - "-Wno-attributes" # Permit unrecognised attributes ) + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11") + list(APPEND COMPILE_ERROR_FLAGS + # This has the same problems described above for "array-bounds". + "-Wno-stringop-overread" # Permit string functions overreading the source + ) + endif() endif() if(USE_WERROR) diff --git a/include/AudioEngineWorkerThread.h b/include/AudioEngineWorkerThread.h index b76235aa125..60a5ef8104d 100644 --- a/include/AudioEngineWorkerThread.h +++ b/include/AudioEngineWorkerThread.h @@ -71,8 +71,8 @@ class AudioEngineWorkerThread : public QThread private: std::atomic m_items[JOB_QUEUE_SIZE]; - std::atomic_int m_writeIndex; - std::atomic_int m_itemsDone; + std::atomic_size_t m_writeIndex; + std::atomic_size_t m_itemsDone; OperationMode m_opMode; } ; diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index dbe750c4f82..fbfa9b60dd6 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -84,11 +84,7 @@ class AudioPortAudio : public AudioDevice return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PortAudio" ); } - - int process_callback( const float *_inputBuffer, - float * _outputBuffer, - unsigned long _framesPerBuffer ); - + int process_callback(const float* _inputBuffer, float* _outputBuffer, f_cnt_t _framesPerBuffer); class setupWidget : public gui::AudioDeviceSetupWidget { @@ -151,8 +147,8 @@ class AudioPortAudio : public AudioDevice bool m_wasPAInitError; SampleFrame* m_outBuf; - int m_outBufPos; - int m_outBufSize; + std::size_t m_outBufPos; + fpp_t m_outBufSize; bool m_stopped; diff --git a/include/LocklessAllocator.h b/include/LocklessAllocator.h index d44b9954374..1652ac71d66 100644 --- a/include/LocklessAllocator.h +++ b/include/LocklessAllocator.h @@ -50,8 +50,8 @@ class LocklessAllocator std::atomic_int * m_freeState; size_t m_freeStateSets; - std::atomic_int m_available; - std::atomic_int m_startIndex; + std::atomic_size_t m_available; + std::atomic_size_t m_startIndex; } ; diff --git a/include/MidiEvent.h b/include/MidiEvent.h index 9a14e427c44..453f6541098 100644 --- a/include/MidiEvent.h +++ b/include/MidiEvent.h @@ -190,6 +190,8 @@ class MidiEvent setParam( 0, pitchBend ); } + auto sysExData() const -> const char* { return m_sysExData; } + Source source() const { return m_source; @@ -212,7 +214,7 @@ class MidiEvent int32_t m_sysExDataLen; // len of m_sysExData } m_data; - [[maybe_unused]] const char* m_sysExData; + const char* m_sysExData; const void* m_sourcePort; // Stores the source of the MidiEvent: Internal or External (hardware controllers). diff --git a/include/Mixer.h b/include/Mixer.h index e65bde010b9..d74f9c11c9b 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -79,7 +79,7 @@ class MixerChannel : public ThreadableJob auto color() const -> const std::optional& { return m_color; } void setColor(const std::optional& color) { m_color = color; } - std::atomic_int m_dependenciesMet; + std::atomic_size_t m_dependenciesMet; void incrementDeps(); void processed(); diff --git a/include/Oscillator.h b/include/Oscillator.h index f537044dc93..0a5e166dc9e 100644 --- a/include/Oscillator.h +++ b/include/Oscillator.h @@ -170,12 +170,8 @@ class LMMS_EXPORT Oscillator { if (buffer == nullptr || buffer->size() == 0) { return 0; } const auto frames = buffer->size(); - const auto frame = sample * frames; - auto f1 = static_cast(frame) % frames; - if (f1 < 0) - { - f1 += frames; - } + const auto frame = absFraction(sample) * frames; + const auto f1 = static_cast(frame); return linearInterpolate(buffer->data()[f1][0], buffer->data()[(f1 + 1) % frames][0], fraction(frame)); } @@ -190,12 +186,8 @@ class LMMS_EXPORT Oscillator inline wtSampleControl getWtSampleControl(const float sample) const { wtSampleControl control; - control.frame = sample * OscillatorConstants::WAVETABLE_LENGTH; - control.f1 = static_cast(control.frame) % OscillatorConstants::WAVETABLE_LENGTH; - if (control.f1 < 0) - { - control.f1 += OscillatorConstants::WAVETABLE_LENGTH; - } + control.frame = absFraction(sample) * OscillatorConstants::WAVETABLE_LENGTH; + control.f1 = static_cast(control.frame); control.f2 = control.f1 < OscillatorConstants::WAVETABLE_LENGTH - 1 ? control.f1 + 1 : 0; diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index d4676b52e79..a2e2e08d62b 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -189,13 +189,11 @@ private slots: void processErrored(QProcess::ProcessError err ); } ; - -LMMS_EXPORT inline std::string QSTR_TO_STDSTR(QString const& qstr) +inline std::string QSTR_TO_STDSTR(QString const& qstr) { return qstr.toStdString(); } - } // namespace lmms #endif // LMMS_REMOTE_PLUGIN_H diff --git a/include/RmsHelper.h b/include/RmsHelper.h index fd2c0f9bbd2..a50d5ff6d2b 100644 --- a/include/RmsHelper.h +++ b/include/RmsHelper.h @@ -36,7 +36,7 @@ namespace lmms class RmsHelper { public: - RmsHelper( int size ) : + RmsHelper(std::size_t size) : m_buffer( nullptr ) { setSize( size ); @@ -46,7 +46,7 @@ class RmsHelper if( m_buffer ) delete[] m_buffer; } - inline void setSize( int size ) + void setSize(std::size_t size) { if( m_buffer ) { @@ -90,8 +90,8 @@ class RmsHelper private: float * m_buffer; float m_sum; - unsigned int m_pos; - unsigned int m_size; + std::size_t m_pos; + std::size_t m_size; float m_sizef; }; diff --git a/include/Track.h b/include/Track.h index b801bb1828b..db33900f5da 100644 --- a/include/Track.h +++ b/include/Track.h @@ -127,7 +127,7 @@ class LMMS_EXPORT Track : public Model, public JournallingObject void deleteClips(); int numOfClips(); - Clip * getClip( int clipNum ); + auto getClip(std::size_t clipNum) -> Clip*; int getClipNum(const Clip* clip ); const clipVector & getClips() const diff --git a/include/fft_helpers.h b/include/fft_helpers.h index cd4e5f88db4..80c69a0a35a 100644 --- a/include/fft_helpers.h +++ b/include/fft_helpers.h @@ -37,7 +37,7 @@ namespace lmms // NOTE: FFT_BUFFER_SIZE should be considered deprecated! // It is used by Eq plugin and some older code here, but this should be a user // switchable parameter, not a constant. Use a value from FFT_BLOCK_SIZES -const unsigned int FFT_BUFFER_SIZE = 2048; +constexpr auto FFT_BUFFER_SIZE = std::size_t{2048}; // Allowed FFT block sizes. Ranging from barely useful to barely acceptable // because of performance and latency reasons. diff --git a/include/lmms_math.h b/include/lmms_math.h index bf5e53a2b53..0da3b9b3dc9 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -61,10 +61,9 @@ static inline float fraction( const float _x ) * If the result is interpreted as a phase of an oscillator, it makes that negative phases are * converted to positive phases. */ -static inline float absFraction( const float _x ) +static inline float absFraction(const float x) { - return( _x - ( _x >= 0.0f ? static_cast( _x ) : - static_cast( _x ) - 1 ) ); + return x - std::floor(x); } /*! diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index b10c065b29d..4cc14ba9cdb 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -123,7 +123,7 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, if( !_n->m_pluginData ) { - if (m_stutterModel.value() == true && m_nextPlayStartPoint >= m_sample.endFrame()) + if (m_stutterModel.value() == true && m_nextPlayStartPoint >= static_cast(m_sample.endFrame())) { // Restart playing the note if in stutter mode, not in loop mode, // and we're at the end of the sample. @@ -288,7 +288,7 @@ auto AudioFileProcessor::beatLen(NotePlayHandle* note) const -> f_cnt_t * Engine::audioEngine()->outputSampleRate() / Engine::audioEngine()->baseSampleRate(); - const auto startFrame = m_nextPlayStartPoint >= m_sample.endFrame() + const auto startFrame = m_nextPlayStartPoint >= static_cast(m_sample.endFrame()) ? m_sample.startFrame() : m_nextPlayStartPoint; const auto duration = m_sample.endFrame() - startFrame; diff --git a/plugins/Bitcrush/Bitcrush.cpp b/plugins/Bitcrush/Bitcrush.cpp index 76e8b9681d9..ea1c43acba1 100644 --- a/plugins/Bitcrush/Bitcrush.cpp +++ b/plugins/Bitcrush/Bitcrush.cpp @@ -153,7 +153,7 @@ bool BitcrushEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) // read input buffer and write it to oversampled buffer if( m_rateEnabled ) // rate crushing enabled so do that { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { for( int o = 0; o < OS_RATE; ++o ) { @@ -180,7 +180,7 @@ bool BitcrushEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) } else // rate crushing disabled: simply oversample with zero-order hold { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { for( int o = 0; o < OS_RATE; ++o ) { @@ -196,7 +196,7 @@ bool BitcrushEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) // the oversampled buffer is now written, so filter it to reduce aliasing - for( int f = 0; f < frames * OS_RATE; ++f ) + for (auto f = std::size_t{0}; f < frames * OS_RATE; ++f) { if( qMax( qAbs( m_buffer[f][0] ), qAbs( m_buffer[f][1] ) ) >= 1.0e-10f ) { @@ -225,7 +225,7 @@ bool BitcrushEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) double outSum = 0.0; const float d = dryLevel(); const float w = wetLevel(); - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { float lsum = 0.0f; float rsum = 0.0f; diff --git a/plugins/CarlaBase/Carla.cpp b/plugins/CarlaBase/Carla.cpp index f5d5aa574a8..e81e655501a 100644 --- a/plugins/CarlaBase/Carla.cpp +++ b/plugins/CarlaBase/Carla.cpp @@ -205,9 +205,9 @@ CarlaInstrument::CarlaInstrument(InstrumentTrack* const instrumentTrack, const D m_paramsCompleter->setCompletionMode(QCompleter::PopupCompletion); // Add static amount of CarlaParamFloatModel's. - int paramCount = fDescriptor->get_parameter_count(fHandle); + const auto paramCount = fDescriptor->get_parameter_count(fHandle); m_paramModels.reserve(paramCount); - for (int i=0; i < paramCount; ++i) + for (auto i = std::size_t{0}; i < paramCount; ++i) { m_paramModels.push_back(new CarlaParamFloatModel(this)); connect(m_paramModels[i], &CarlaParamFloatModel::dataChanged, @@ -274,7 +274,7 @@ const NativeTimeInfo* CarlaInstrument::handleGetTimeInfo() const void CarlaInstrument::handleUiParameterChanged(const uint32_t index, const float value) const { - if (m_paramModels.count() > index) + if (m_paramModels.size() > index) { m_paramModels[index]->setValue(value); } @@ -369,7 +369,7 @@ void CarlaInstrument::saveSettings(QDomDocument& doc, QDomElement& parent) std::free(state); #if CARLA_VERSION_HEX >= CARLA_MIN_PARAM_VERSION - for (uint32_t index = 0; index < m_paramModels.count(); ++index) + for (uint32_t index = 0; index < m_paramModels.size(); ++index) { QString idStr = CARLA_SETTING_PREFIX + QString::number(index); m_paramModels[index]->saveSettings(doc, parent, idStr); @@ -439,7 +439,7 @@ void CarlaInstrument::refreshParams(bool init) void CarlaInstrument::clearParamModels() { //Delete the models, this also disconnects all connections (automation and controller connections) - for (uint32_t index=0; index < m_paramModels.count(); ++index) + for (uint32_t index = 0; index < m_paramModels.size(); ++index) { delete m_paramModels[index]; } @@ -899,7 +899,7 @@ CarlaParamsView::~CarlaParamsView() m_carlaInstrumentView->m_paramsView = nullptr; // Clear models - if (m_carlaInstrument->m_paramModels.isEmpty() == false) + if (!m_carlaInstrument->m_paramModels.empty()) { m_carlaInstrument->clearParamModels(); } @@ -930,7 +930,7 @@ void CarlaParamsView::filterKnobs() m_maxColumns = m_inputScrollArea->width() / maxKnobWidth; QString text = m_paramsFilterLineEdit->text(); - for (uint32_t i=0; i < m_knobs.count(); ++i) + for (uint32_t i = 0; i < m_knobs.size(); ++i) { // Don't show disabled (unused) knobs. if (!m_carlaInstrument->m_paramModels[i]->enabled()) @@ -975,7 +975,7 @@ void CarlaParamsView::filterKnobs() void CarlaParamsView::refreshKnobs() { // Make sure all the knobs are deleted. - for (uint32_t i=0; i < m_knobs.count(); ++i) + for (uint32_t i = 0; i < m_knobs.size(); ++i) { delete m_knobs[i]; // Delete knob widgets itself. } @@ -996,15 +996,15 @@ void CarlaParamsView::refreshKnobs() m_maxKnobWidthPerGroup[i] = 0; } - if (!m_carlaInstrument->m_paramModels.count()) { return; } + if (m_carlaInstrument->m_paramModels.empty()) { return; } // Make room in QList m_knobs - m_knobs.reserve(m_carlaInstrument->m_paramModels.count()); + m_knobs.reserve(m_carlaInstrument->m_paramModels.size()); QStringList groupNameList; groupNameList.reserve(m_carlaInstrument->m_paramGroupCount); - for (uint32_t i=0; i < m_carlaInstrument->m_paramModels.count(); ++i) + for (uint32_t i = 0; i < m_carlaInstrument->m_paramModels.size(); ++i) { bool enabled = m_carlaInstrument->m_paramModels[i]->enabled(); m_knobs.push_back(new Knob(KnobType::Dark28, m_inputScrollAreaWidgetContent)); @@ -1105,7 +1105,7 @@ void CarlaParamsView::addKnob(uint32_t index) void CarlaParamsView::clearKnobs() { // Remove knobs from layout. - for (uint16_t i=0; i < m_knobs.count(); ++i) + for (uint16_t i = 0; i < m_knobs.size(); ++i) { m_knobs[i]->close(); } diff --git a/plugins/CarlaBase/Carla.h b/plugins/CarlaBase/Carla.h index d8eab5fc43f..f833c4668e6 100644 --- a/plugins/CarlaBase/Carla.h +++ b/plugins/CarlaBase/Carla.h @@ -29,6 +29,8 @@ #define CARLA_MIN_PARAM_VERSION 0x020090 #define CARLA_VERSION_HEX_3 0x30000 +#include + // qt #include #include @@ -223,7 +225,7 @@ private slots: QMutex fMutex; uint8_t m_paramGroupCount; - QList m_paramModels; + std::vector m_paramModels; QDomElement m_settingsElem; QCompleter* m_paramsCompleter; @@ -351,7 +353,7 @@ private slots: CarlaInstrument* const m_carlaInstrument; CarlaInstrumentView* const m_carlaInstrumentView; - QList m_knobs; + std::vector m_knobs; // Keep track of the biggest knob width per group QList m_maxKnobWidthPerGroup; diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp index 2784f9b6bba..2142d040c76 100644 --- a/plugins/CrossoverEQ/CrossoverEQ.cpp +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -142,7 +142,7 @@ bool CrossoverEQEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames zeroSampleFrames(m_work, frames); // run temp bands - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_tmp1[f][0] = m_lp2.update( buf[f][0], 0 ); m_tmp1[f][1] = m_lp2.update( buf[f][1], 1 ); @@ -153,7 +153,7 @@ bool CrossoverEQEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames // run band 1 if( mute1 ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_work[f][0] += m_lp1.update( m_tmp1[f][0], 0 ) * m_gain1; m_work[f][1] += m_lp1.update( m_tmp1[f][1], 1 ) * m_gain1; @@ -163,7 +163,7 @@ bool CrossoverEQEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames // run band 2 if( mute2 ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_work[f][0] += m_hp2.update( m_tmp1[f][0], 0 ) * m_gain2; m_work[f][1] += m_hp2.update( m_tmp1[f][1], 1 ) * m_gain2; @@ -173,7 +173,7 @@ bool CrossoverEQEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames // run band 3 if( mute3 ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_work[f][0] += m_lp3.update( m_tmp2[f][0], 0 ) * m_gain3; m_work[f][1] += m_lp3.update( m_tmp2[f][1], 1 ) * m_gain3; @@ -183,7 +183,7 @@ bool CrossoverEQEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames // run band 4 if( mute4 ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_work[f][0] += m_hp4.update( m_tmp2[f][0], 0 ) * m_gain4; m_work[f][1] += m_hp4.update( m_tmp2[f][1], 1 ) * m_gain4; @@ -193,7 +193,7 @@ bool CrossoverEQEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames const float d = dryLevel(); const float w = wetLevel(); double outSum = 0.0; - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { buf[f][0] = d * buf[f][0] + w * m_work[f][0]; buf[f][1] = d * buf[f][1] + w * m_work[f][1]; diff --git a/plugins/Eq/EqSpectrumView.cpp b/plugins/Eq/EqSpectrumView.cpp index 75be486975c..99df328efec 100644 --- a/plugins/Eq/EqSpectrumView.cpp +++ b/plugins/Eq/EqSpectrumView.cpp @@ -54,7 +54,7 @@ EqAnalyser::EqAnalyser() : const float a2 = 0.14128f; const float a3 = 0.01168f; - for (int i = 0; i < FFT_BUFFER_SIZE; i++) + for (auto i = std::size_t{0}; i < FFT_BUFFER_SIZE; i++) { m_fftWindow[i] = (a0 - a1 * cos(2 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0)) + a2 * cos(4 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0)) diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index d801e82d465..e9acddeeb15 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -380,9 +380,8 @@ void FreeBoyInstrument::playNote(NotePlayHandle* nph, SampleFrame* workingBuffer papu->writeRegister(0xff23, 128); } - constexpr int bufSize = 2048; - int framesLeft = frames; - int dataLen = 0; + constexpr auto bufSize = f_cnt_t{2048}; + auto framesLeft = frames; auto buf = std::array{}; while (framesLeft > 0) { @@ -392,12 +391,11 @@ void FreeBoyInstrument::playNote(NotePlayHandle* nph, SampleFrame* workingBuffer papu->endFrame(FRAME_LENGTH); avail = papu->samplesAvail(); } - dataLen = framesLeft > avail ? avail : framesLeft; - dataLen = dataLen > bufSize ? bufSize : dataLen; + const auto dataLen = std::min({static_cast(avail), framesLeft, bufSize}); - long count = papu->readSamples(buf.data(), dataLen * 2) / 2; + const auto count = static_cast(papu->readSamples(buf.data(), dataLen * 2) / 2); - for (fpp_t frame = 0; frame < count; ++frame) + for (auto frame = std::size_t{0}; frame < count; ++frame) { for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch) { diff --git a/plugins/GigPlayer/CMakeLists.txt b/plugins/GigPlayer/CMakeLists.txt index 69ac1b80cdf..ccec8c87336 100644 --- a/plugins/GigPlayer/CMakeLists.txt +++ b/plugins/GigPlayer/CMakeLists.txt @@ -6,11 +6,6 @@ if(LMMS_HAVE_GIG) # Required for not crashing loading files with libgig add_compile_options("-fexceptions") - # disable deprecated check for mingw-x-libgig - if(LMMS_BUILD_WIN32) - add_compile_options("-Wno-deprecated") - endif(LMMS_BUILD_WIN32) - link_directories(${GIG_LIBRARY_DIRS}) link_libraries(${GIG_LIBRARIES}) build_plugin(gigplayer diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index b6613de7f70..b72e30b3335 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -323,7 +323,7 @@ void GigInstrument::playNote( NotePlayHandle * _n, SampleFrame* ) void GigInstrument::play( SampleFrame* _working_buffer ) { const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); - const int rate = Engine::audioEngine()->outputSampleRate(); + const auto rate = Engine::audioEngine()->outputSampleRate(); // Initialize to zeros std::memset( &_working_buffer[0][0], 0, DEFAULT_CHANNELS * frames * sizeof( float ) ); @@ -1216,7 +1216,7 @@ bool GigSample::convertSampleRate( SampleFrame & oldBuf, SampleFrame & newBuf, return false; } - if( src_data.output_frames_gen > 0 && src_data.output_frames_gen < newSize ) + if (src_data.output_frames_gen > 0 && static_cast(src_data.output_frames_gen) < newSize) { qCritical() << "GigInstrument: not enough frames, wanted" << newSize << "generated" << src_data.output_frames_gen; diff --git a/plugins/GranularPitchShifter/GranularPitchShifterEffect.cpp b/plugins/GranularPitchShifter/GranularPitchShifterEffect.cpp index e5af1e8bf72..992d05304ca 100755 --- a/plugins/GranularPitchShifter/GranularPitchShifterEffect.cpp +++ b/plugins/GranularPitchShifter/GranularPitchShifterEffect.cpp @@ -258,7 +258,7 @@ void GranularPitchShifterEffect::changeSampleRate() m_ringBufLength = m_sampleRate * ringBufLength; m_ringBuf.resize(m_ringBufLength); - for (size_t i = 0; i < m_ringBufLength; ++i) + for (size_t i = 0; i < static_cast(m_ringBufLength); ++i) { m_ringBuf[i][0] = 0; m_ringBuf[i][1] = 0; diff --git a/plugins/GranularPitchShifter/GranularPitchShifterEffect.h b/plugins/GranularPitchShifter/GranularPitchShifterEffect.h index c904b4c1389..0f94168b7b6 100755 --- a/plugins/GranularPitchShifter/GranularPitchShifterEffect.h +++ b/plugins/GranularPitchShifter/GranularPitchShifterEffect.h @@ -58,7 +58,7 @@ class GranularPitchShifterEffect : public Effect // double index and fraction are required for good quality float getHermiteSample(double index, int ch) { - const int index_floor = static_cast(index); + const auto index_floor = static_cast(index); const double fraction = index - index_floor; float v0, v1, v2, v3; diff --git a/plugins/LadspaEffect/LadspaEffect.cpp b/plugins/LadspaEffect/LadspaEffect.cpp index 26901c65f6e..75c79ad214b 100644 --- a/plugins/LadspaEffect/LadspaEffect.cpp +++ b/plugins/LadspaEffect/LadspaEffect.cpp @@ -139,7 +139,7 @@ bool LadspaEffect::processAudioBuffer( SampleFrame* _buf, return( false ); } - int frames = _frames; + auto frames = _frames; SampleFrame* o_buf = nullptr; QVarLengthArray sBuf(_frames); diff --git a/plugins/Monstro/Monstro.cpp b/plugins/Monstro/Monstro.cpp index 7063be0e265..874b5d8d13a 100644 --- a/plugins/Monstro/Monstro.cpp +++ b/plugins/Monstro/Monstro.cpp @@ -685,7 +685,7 @@ void MonstroSynth::renderOutput( fpp_t _frames, SampleFrame* _buf ) } -inline void MonstroSynth::updateModulators( float * env1, float * env2, float * lfo1, float * lfo2, int frames ) +inline void MonstroSynth::updateModulators(float * env1, float * env2, float * lfo1, float * lfo2, f_cnt_t frames) { // frames played before const f_cnt_t tfp = m_nph->totalFramesPlayed(); @@ -790,7 +790,7 @@ inline void MonstroSynth::updateModulators( float * env1, float * env2, float * // attack for( f_cnt_t f = 0; f < frames; ++f ) { - if( tfp + f < m_lfoatt[i] ) lfo[i][f] *= ( static_cast( tfp ) / m_lfoatt[i] ); + if (tfp + f < static_cast(m_lfoatt[i])) { lfo[i][f] *= static_cast(tfp) / m_lfoatt[i]; } } diff --git a/plugins/Monstro/Monstro.h b/plugins/Monstro/Monstro.h index da705b9ff9f..a1ae72a381b 100644 --- a/plugins/Monstro/Monstro.h +++ b/plugins/Monstro/Monstro.h @@ -184,7 +184,7 @@ class MonstroSynth MonstroInstrument * m_parent; NotePlayHandle * m_nph; - inline void updateModulators( float * env1, float * env2, float * lfo1, float * lfo2, int frames ); + inline void updateModulators(float * env1, float * env2, float * lfo1, float * lfo2, f_cnt_t frames); // linear interpolation /* inline sample_t interpolate( sample_t s1, sample_t s2, float x ) diff --git a/plugins/MultitapEcho/MultitapEcho.cpp b/plugins/MultitapEcho/MultitapEcho.cpp index 6d8da26e396..ecc1d8f304e 100644 --- a/plugins/MultitapEcho/MultitapEcho.cpp +++ b/plugins/MultitapEcho/MultitapEcho.cpp @@ -86,7 +86,7 @@ void MultitapEchoEffect::updateFilters( int begin, int end ) void MultitapEchoEffect::runFilter( SampleFrame* dst, SampleFrame* src, StereoOnePole & filter, const fpp_t frames ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { dst[f][0] = filter.update( src[f][0], 0 ); dst[f][1] = filter.update( src[f][1], 1 ); @@ -152,7 +152,7 @@ bool MultitapEchoEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frame // pop the buffer and mix it into output m_buffer.pop( m_work ); - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { buf[f][0] = d * buf[f][0] + w * m_work[f][0]; buf[f][1] = d * buf[f][1] + w * m_work[f][1]; diff --git a/plugins/Organic/Organic.cpp b/plugins/Organic/Organic.cpp index 54c36a510f0..c167bcec259 100644 --- a/plugins/Organic/Organic.cpp +++ b/plugins/Organic/Organic.cpp @@ -302,7 +302,7 @@ void OrganicInstrument::playNote( NotePlayHandle * _n, // fxKnob is [0;1] float t = m_fx1Model.value(); - for (int i=0 ; i < frames + offset ; i++) + for (auto i = std::size_t{0}; i < frames + offset; i++) { _working_buffer[i][0] = waveshape( _working_buffer[i][0], t ) * m_volModel.value() / 100.0f; diff --git a/plugins/PeakControllerEffect/PeakControllerEffect.cpp b/plugins/PeakControllerEffect/PeakControllerEffect.cpp index af64d0095c9..886036095f8 100644 --- a/plugins/PeakControllerEffect/PeakControllerEffect.cpp +++ b/plugins/PeakControllerEffect/PeakControllerEffect.cpp @@ -110,7 +110,7 @@ bool PeakControllerEffect::processAudioBuffer( SampleFrame* _buf, if( c.m_absModel.value() ) { - for( int i = 0; i < _frames; ++i ) + for (auto i = std::size_t{0}; i < _frames; ++i) { // absolute value is achieved because the squares are > 0 sum += _buf[i][0]*_buf[i][0] + _buf[i][1]*_buf[i][1]; @@ -118,7 +118,7 @@ bool PeakControllerEffect::processAudioBuffer( SampleFrame* _buf, } else { - for( int i = 0; i < _frames; ++i ) + for (auto i = std::size_t{0}; i < _frames; ++i) { // the value is absolute because of squaring, // so we need to correct it @@ -131,7 +131,7 @@ bool PeakControllerEffect::processAudioBuffer( SampleFrame* _buf, // this will mute the output after the values were measured if( c.m_muteModel.value() ) { - for( int i = 0; i < _frames; ++i ) + for (auto i = std::size_t{0}; i < _frames; ++i) { _buf[i][0] = _buf[i][1] = 0.0f; } diff --git a/plugins/ReverbSC/base.c b/plugins/ReverbSC/base.c index c8d1dff679e..d5c52b0bd1a 100644 --- a/plugins/ReverbSC/base.c +++ b/plugins/ReverbSC/base.c @@ -189,7 +189,7 @@ int sp_set(sp_param *p, SPFLOAT val) { int sp_out(sp_data *sp, uint32_t chan, SPFLOAT val) { - if(chan > sp->nchan - 1) { + if (chan >= (uint32_t) sp->nchan) { fprintf(stderr, "sp_out: Invalid channel\n"); return SP_NOT_OK; } diff --git a/plugins/Sf2Player/Sf2Player.cpp b/plugins/Sf2Player/Sf2Player.cpp index 14868bf0e69..13dab7b8abd 100644 --- a/plugins/Sf2Player/Sf2Player.cpp +++ b/plugins/Sf2Player/Sf2Player.cpp @@ -499,44 +499,58 @@ void Sf2Instrument::updateGain() fluid_synth_set_gain( m_synth, m_gain.value() ); } - - +#define FLUIDSYNTH_VERSION_HEX ((FLUIDSYNTH_VERSION_MAJOR << 16) \ + | (FLUIDSYNTH_VERSION_MINOR << 8) \ + | FLUIDSYNTH_VERSION_MICRO) +#define USE_NEW_EFFECT_API (FLUIDSYNTH_VERSION_HEX >= 0x020200) void Sf2Instrument::updateReverbOn() { - fluid_synth_set_reverb_on( m_synth, m_reverbOn.value() ? 1 : 0 ); +#if USE_NEW_EFFECT_API + fluid_synth_reverb_on(m_synth, -1, m_reverbOn.value() ? 1 : 0); +#else + fluid_synth_set_reverb_on(m_synth, m_reverbOn.value() ? 1 : 0); +#endif } - - - void Sf2Instrument::updateReverb() { - fluid_synth_set_reverb( m_synth, m_reverbRoomSize.value(), +#if USE_NEW_EFFECT_API + fluid_synth_set_reverb_group_roomsize(m_synth, -1, m_reverbRoomSize.value()); + fluid_synth_set_reverb_group_damp(m_synth, -1, m_reverbDamping.value()); + fluid_synth_set_reverb_group_width(m_synth, -1, m_reverbWidth.value()); + fluid_synth_set_reverb_group_level(m_synth, -1, m_reverbLevel.value()); +#else + fluid_synth_set_reverb(m_synth, m_reverbRoomSize.value(), m_reverbDamping.value(), m_reverbWidth.value(), - m_reverbLevel.value() ); + m_reverbLevel.value()); +#endif } - - - -void Sf2Instrument::updateChorusOn() +void Sf2Instrument::updateChorusOn() { - fluid_synth_set_chorus_on( m_synth, m_chorusOn.value() ? 1 : 0 ); +#if USE_NEW_EFFECT_API + fluid_synth_chorus_on(m_synth, -1, m_chorusOn.value() ? 1 : 0); +#else + fluid_synth_set_chorus_on(m_synth, m_chorusOn.value() ? 1 : 0); +#endif } - - - -void Sf2Instrument::updateChorus() +void Sf2Instrument::updateChorus() { - fluid_synth_set_chorus( m_synth, static_cast( m_chorusNum.value() ), +#if USE_NEW_EFFECT_API + fluid_synth_set_chorus_group_nr(m_synth, -1, static_cast(m_chorusNum.value())); + fluid_synth_set_chorus_group_level(m_synth, -1, m_chorusLevel.value()); + fluid_synth_set_chorus_group_speed(m_synth, -1, m_chorusSpeed.value()); + fluid_synth_set_chorus_group_depth(m_synth, -1, m_chorusDepth.value()); + fluid_synth_set_chorus_group_type(m_synth, -1, FLUID_CHORUS_MOD_SINE); +#else + fluid_synth_set_chorus(m_synth, static_cast(m_chorusNum.value()), m_chorusLevel.value(), m_chorusSpeed.value(), - m_chorusDepth.value(), 0 ); + m_chorusDepth.value(), FLUID_CHORUS_MOD_SINE); +#endif } - - void Sf2Instrument::updateTuning() { if (instrumentTrack()->microtuner()->enabledModel()->value()) @@ -898,7 +912,7 @@ void Sf2Instrument::renderFrames( f_cnt_t frames, SampleFrame* buf ) { qCritical( "Sf2Instrument: error while resampling: %s", src_strerror( error ) ); } - if( src_data.output_frames_gen > frames ) + if (static_cast(src_data.output_frames_gen) < frames) { qCritical("Sf2Instrument: not enough frames: %ld / %zu", src_data.output_frames_gen, frames); } diff --git a/plugins/Sid/SidInstrument.cpp b/plugins/Sid/SidInstrument.cpp index 4f2b1765a6d..4d21abd4dea 100644 --- a/plugins/Sid/SidInstrument.cpp +++ b/plugins/Sid/SidInstrument.cpp @@ -305,8 +305,11 @@ void SidInstrument::playNote( NotePlayHandle * _n, auto sid = static_cast(_n->m_pluginData); int delta_t = clockrate * frames / samplerate + 4; - // avoid variable length array for msvc compat - auto buf = reinterpret_cast(_working_buffer + offset); +#ifndef _MSC_VER + short buf[frames]; +#else + const auto buf = static_cast(_alloca(frames * sizeof(short))); +#endif auto sidreg = std::array{}; for (auto& reg : sidreg) @@ -407,12 +410,13 @@ void SidInstrument::playNote( NotePlayHandle * _n, sidreg[24] = data8&0x00FF; - int num = sid_fillbuffer(sidreg.data(), sid, delta_t, buf, frames); - if(num!=frames) + const auto num = static_cast(sid_fillbuffer(sidreg.data(), sid, delta_t, buf, frames)); + if (num != frames) { printf("!!!Not enough samples\n"); + } // loop backwards to avoid overwriting data in the short-to-float conversion - for( fpp_t frame = frames - 1; frame >= 0; frame-- ) + for (auto frame = std::size_t{0}; frame < frames; ++frame) { sample_t s = float(buf[frame])/32768.0; for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) diff --git a/plugins/SlicerT/SlicerT.cpp b/plugins/SlicerT/SlicerT.cpp index 3644060edb2..dcfbf6bc551 100644 --- a/plugins/SlicerT/SlicerT.cpp +++ b/plugins/SlicerT/SlicerT.cpp @@ -96,7 +96,7 @@ void SlicerT::playNote(NotePlayHandle* handle, SampleFrame* workingBuffer) sliceStart = 0; sliceEnd = 1; } - else if (noteIndex > 0 && noteIndex < m_slicePoints.size()) + else if (noteIndex > 0 && static_cast(noteIndex) < m_slicePoints.size()) { noteIndex -= 1; sliceStart = m_slicePoints[noteIndex]; @@ -134,9 +134,9 @@ void SlicerT::playNote(NotePlayHandle* handle, SampleFrame* workingBuffer) // exponential fade out, applyRelease() not used since it extends the note length int fadeOutFrames = m_fadeOutFrames.value() / 1000.0f * Engine::audioEngine()->outputSampleRate(); int noteFramesLeft = noteLeft * m_originalSample.sampleSize() * speedRatio; - for (int i = 0; i < frames; i++) + for (auto i = std::size_t{0}; i < frames; i++) { - float fadeValue = static_cast(noteFramesLeft - i) / fadeOutFrames; + float fadeValue = static_cast(noteFramesLeft - static_cast(i)) / fadeOutFrames; fadeValue = std::clamp(fadeValue, 0.0f, 1.0f); fadeValue = cosinusInterpolate(0, 1, fadeValue); @@ -170,7 +170,7 @@ void SlicerT::findSlices() float maxMag = -1; std::vector singleChannel(m_originalSample.sampleSize(), 0); - for (int i = 0; i < m_originalSample.sampleSize(); i++) + for (auto i = std::size_t{0}; i < m_originalSample.sampleSize(); i++) { singleChannel[i] = (m_originalSample.data()[i][0] + m_originalSample.data()[i][1]) / 2; maxMag = std::max(maxMag, singleChannel[i]); @@ -179,7 +179,7 @@ void SlicerT::findSlices() // normalize and find 0 crossings std::vector zeroCrossings; float lastValue = 1; - for (int i = 0; i < singleChannel.size(); i++) + for (auto i = std::size_t{0}; i < singleChannel.size(); i++) { singleChannel[i] /= maxMag; if (sign(lastValue) != sign(singleChannel[i])) @@ -199,7 +199,7 @@ void SlicerT::findSlices() float spectralFlux = 0; float prevFlux = 1E-10f; // small value, no divison by zero - for (int i = 0; i < singleChannel.size() - windowSize; i += windowSize) + for (int i = 0; i < static_cast(singleChannel.size()) - windowSize; i += windowSize) { // fft std::copy_n(singleChannel.data() + i, windowSize, fftIn.data()); @@ -300,7 +300,7 @@ std::vector SlicerT::getMidi() float totalTicks = outFrames / framesPerTick; float lastEnd = 0; - for (int i = 0; i < m_slicePoints.size() - 1; i++) + for (auto i = std::size_t{0}; i < m_slicePoints.size() - 1; i++) { float sliceStart = lastEnd; float sliceEnd = totalTicks * m_slicePoints[i + 1]; @@ -342,7 +342,7 @@ void SlicerT::saveSettings(QDomDocument& document, QDomElement& element) } element.setAttribute("totalSlices", static_cast(m_slicePoints.size())); - for (int i = 0; i < m_slicePoints.size(); i++) + for (auto i = std::size_t{0}; i < m_slicePoints.size(); i++) { element.setAttribute(tr("slice_%1").arg(i), m_slicePoints[i]); } diff --git a/plugins/SlicerT/SlicerTWaveform.cpp b/plugins/SlicerT/SlicerTWaveform.cpp index 3793ed2f159..808b81c399b 100644 --- a/plugins/SlicerT/SlicerTWaveform.cpp +++ b/plugins/SlicerT/SlicerTWaveform.cpp @@ -195,11 +195,11 @@ void SlicerTWaveform::drawEditor() brush.setPen(QPen(s_sliceColor, 2)); - for (int i = 0; i < m_slicerTParent->m_slicePoints.size(); i++) + for (auto i = std::size_t{0}; i < m_slicerTParent->m_slicePoints.size(); i++) { float xPos = (m_slicerTParent->m_slicePoints.at(i) - startFrame) / numFramesToDraw * m_editorWidth; - if (i == m_closestSlice) + if (i == static_cast(m_closestSlice)) { brush.setPen(QPen(s_sliceHighlightColor, 2)); brush.drawLine(xPos, 0, xPos, m_editorHeight); @@ -268,7 +268,7 @@ void SlicerTWaveform::updateClosest(QMouseEvent* me) m_closestSlice = -1; float startFrame = m_seekerStart; float endFrame = m_seekerEnd; - for (int i = 0; i < m_slicerTParent->m_slicePoints.size(); i++) + for (auto i = std::size_t{0}; i < m_slicerTParent->m_slicePoints.size(); i++) { float sliceIndex = m_slicerTParent->m_slicePoints.at(i); float xPos = (sliceIndex - startFrame) / (endFrame - startFrame); diff --git a/plugins/SpectrumAnalyzer/SaProcessor.cpp b/plugins/SpectrumAnalyzer/SaProcessor.cpp index e0eef8b595a..d9e7ac8a494 100644 --- a/plugins/SpectrumAnalyzer/SaProcessor.cpp +++ b/plugins/SpectrumAnalyzer/SaProcessor.cpp @@ -232,7 +232,7 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) if (band_end - band_start > 1.0) { // band spans multiple pixels: draw all pixels it covers - for (int target = std::max(static_cast(band_start), 0); + for (auto target = static_cast(std::max(band_start, 0.f)); target < band_end && target < waterfallWidth(); target++) { pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); @@ -259,7 +259,9 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) accL += ((int)band_end - band_start) * m_normSpectrumL[i]; accR += ((int)band_end - band_start) * m_normSpectrumR[i]; - if (target >= 0 && target < waterfallWidth()) {pixel[target] = makePixel(accL, accR);} + if (target >= 0 && static_cast(target) < waterfallWidth()) { + pixel[target] = makePixel(accL, accR); + } // save remaining portion of the band for the following band / pixel accL = (band_end - (int)band_end) * m_normSpectrumL[i]; @@ -270,7 +272,7 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) else { // Linear: always draws one or more pixels per band - for (int target = std::max(static_cast(band_start), 0); + for (auto target = static_cast(std::max(band_start, 0.f)); target < band_end && target < waterfallWidth(); target++) { pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); diff --git a/plugins/SpectrumAnalyzer/SaSpectrumView.cpp b/plugins/SpectrumAnalyzer/SaSpectrumView.cpp index 452e1e90fe4..2d15da7f47b 100644 --- a/plugins/SpectrumAnalyzer/SaSpectrumView.cpp +++ b/plugins/SpectrumAnalyzer/SaSpectrumView.cpp @@ -302,7 +302,7 @@ void SaSpectrumView::refreshPaths() // part new, part old. At reasonable frame rate, such difference is invisible.. void SaSpectrumView::updateBuffers(const float *spectrum, float *displayBuffer, float *peakBuffer) { - for (int n = 0; n < m_processor->binCount(); n++) + for (auto n = std::size_t{0}; n < m_processor->binCount(); n++) { // Update the exponential average if enabled, or simply copy the value. if (!m_controls->m_pauseModel.value()) diff --git a/plugins/StereoEnhancer/StereoEnhancer.cpp b/plugins/StereoEnhancer/StereoEnhancer.cpp index d5ed8d99d6d..261c897df2f 100644 --- a/plugins/StereoEnhancer/StereoEnhancer.cpp +++ b/plugins/StereoEnhancer/StereoEnhancer.cpp @@ -145,7 +145,7 @@ bool StereoEnhancerEffect::processAudioBuffer( SampleFrame* _buf, void StereoEnhancerEffect::clearMyBuffer() { - for (int i = 0; i < DEFAULT_BUFFER_SIZE; i++) + for (auto i = std::size_t{0}; i < DEFAULT_BUFFER_SIZE; i++) { m_delayBuffer[i][0] = 0.0f; m_delayBuffer[i][1] = 0.0f; diff --git a/plugins/Vibed/Vibed.cpp b/plugins/Vibed/Vibed.cpp index c4dd08afa1c..19b8ab1c06b 100644 --- a/plugins/Vibed/Vibed.cpp +++ b/plugins/Vibed/Vibed.cpp @@ -72,11 +72,11 @@ class Vibed::StringContainer ~StringContainer() = default; - void addString(int harm, float pick, float pickup, const float* impulse, float randomize, + void addString(std::size_t harm, float pick, float pickup, const float* impulse, float randomize, float stringLoss, float detune, int oversample, bool state, int id) { 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()); + assert(harm < octave.size()); m_strings[id] = VibratingString{m_pitch * octave[harm], pick, pickup, impulse, m_bufferLength, m_sampleRate, oversample, randomize, stringLoss, detune, state}; diff --git a/plugins/VstBase/RemoteVstPlugin.cpp b/plugins/VstBase/RemoteVstPlugin.cpp index ef31cfdb8eb..62cb7cfe5e1 100644 --- a/plugins/VstBase/RemoteVstPlugin.cpp +++ b/plugins/VstBase/RemoteVstPlugin.cpp @@ -265,7 +265,7 @@ class RemoteVstPlugin : public RemotePluginClient void saveChunkToFile( const std::string & _file ); // restore settings chunk of plugin from file - void loadChunkFromFile( const std::string & _file, int _len ); + void loadChunkFromFile(const std::string& _file, std::size_t _len); // restore settings chunk of plugin from file void loadPresetFile( const std::string & _file ); @@ -1303,7 +1303,7 @@ void RemoteVstPlugin::saveChunkToFile( const std::string & _file ) "Error opening file for saving chunk.\n" ); return; } - if ( fwrite( chunk, 1, len, fp ) != len ) + if (fwrite(chunk, 1, len, fp) != static_cast(len)) { fprintf( stderr, "Error saving chunk to file.\n" ); @@ -1541,7 +1541,7 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) unsigned int toUInt; float * pFloat; - if (m_plugin->uniqueID != pBank->fxID) { + if (static_cast(m_plugin->uniqueID) != pBank->fxID) { sendMessage( message( IdVstCurrentProgramName ). addString( "Error: Plugin UniqID not match" ) ); fclose( stream ); @@ -1577,7 +1577,7 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) else { auto toUIntArray = reinterpret_cast(chunk); - for (int i = 0; i < pBank->numPrograms; i++ ) + for (auto i = 0u; i < pBank->numPrograms; i++) { toUInt = endian_swap( toUIntArray[ i ] ); pFloat = ( float* ) &toUInt; @@ -1625,10 +1625,7 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) delete[] (char*)chunk; } - - - -void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len ) +void RemoteVstPlugin::loadChunkFromFile(const std::string& _file, std::size_t _len) { auto chunk = new char[_len]; diff --git a/plugins/Xpressive/ExprSynth.cpp b/plugins/Xpressive/ExprSynth.cpp index ef5d3dbf111..c48b94ec8dc 100644 --- a/plugins/Xpressive/ExprSynth.cpp +++ b/plugins/Xpressive/ExprSynth.cpp @@ -147,13 +147,8 @@ struct LastSampleFunction : public exprtk::ifunction inline T operator()(const T& x) override { - if (!std::isnan(x) && !std::isinf(x)) - { - const int ix=(int)x; - if (ix>=1 && ix<=m_history_size) - { - return m_samples[(ix + m_pivot_last) % m_history_size]; - } + if (!std::isnan(x) && x >= 1 && x <= m_history_size) { + return m_samples[(static_cast(x) + m_pivot_last) % m_history_size]; } return 0; } diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index 3fb1fc5f2f6..23a76b22820 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -579,9 +579,9 @@ void XpressiveView::expressionChanged() { if (parse_ok) { e->exprValid().setValue(0); - const int length = m_raw_graph->length(); + const auto length = static_cast(m_raw_graph->length()); auto const samples = new float[length]; - for (i = 0; i < length; i++) { + for (auto i = std::size_t{0}; i < length; i++) { t = i / (float) length; samples[i] = expr.evaluate(); if (std::isinf(samples[i]) != 0 || std::isnan(samples[i]) != 0) diff --git a/plugins/ZynAddSubFx/CMakeLists.txt b/plugins/ZynAddSubFx/CMakeLists.txt index b3490a50fb1..cbd7326fe6b 100644 --- a/plugins/ZynAddSubFx/CMakeLists.txt +++ b/plugins/ZynAddSubFx/CMakeLists.txt @@ -25,13 +25,9 @@ endif() # build ZynAddSubFX with full optimizations if(NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wno-write-strings -Wno-deprecated-declarations -fpermissive") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -fpermissive") endif() -IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND NOT "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "6.0.0") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-misleading-indentation -Wno-format-truncation") -ENDIF() - IF(MINGW_PREFIX) SET(FLTK_FLUID_EXECUTABLE "${MINGW_PREFIX}/bin/fluid") ENDIF() diff --git a/src/core/AudioEngineWorkerThread.cpp b/src/core/AudioEngineWorkerThread.cpp index ae459c5e484..70f7760419f 100644 --- a/src/core/AudioEngineWorkerThread.cpp +++ b/src/core/AudioEngineWorkerThread.cpp @@ -79,7 +79,7 @@ void AudioEngineWorkerThread::JobQueue::run() while (processedJob && m_itemsDone < m_writeIndex) { processedJob = false; - for( int i = 0; i < m_writeIndex && i < JOB_QUEUE_SIZE; ++i ) + for (auto i = std::size_t{0}; i < m_writeIndex && i < JOB_QUEUE_SIZE; ++i) { ThreadableJob * job = m_items[i].exchange(nullptr); if( job ) diff --git a/src/core/ControllerConnection.cpp b/src/core/ControllerConnection.cpp index fea907942af..934ccc3f8ca 100644 --- a/src/core/ControllerConnection.cpp +++ b/src/core/ControllerConnection.cpp @@ -159,11 +159,10 @@ inline void ControllerConnection::setTargetName( const QString & _name ) */ void ControllerConnection::finalizeConnections() { - for( int i = 0; i < s_connections.size(); ++i ) + for (auto i = std::size_t{0}; i < s_connections.size(); ++i) { ControllerConnection * c = s_connections[i]; - if ( !c->isFinalized() && c->m_controllerId < - Engine::getSong()->controllers().size() ) + if (!c->isFinalized() && static_cast(c->m_controllerId) < Engine::getSong()->controllers().size()) { c->setController( Engine::getSong()-> controllers().at( c->m_controllerId ) ); @@ -221,7 +220,7 @@ void ControllerConnection::loadSettings( const QDomElement & _this ) if (!Engine::getSong()->isLoadingProject() && m_controllerId != -1 - && m_controllerId < Engine::getSong()->controllers().size()) + && static_cast(m_controllerId) < Engine::getSong()->controllers().size()) { setController( Engine::getSong()-> controllers().at( m_controllerId ) ); diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index a55dbd82b95..b664387bd42 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1184,7 +1184,7 @@ static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) int syncmode = el.attribute( "syncmode" ).toInt(); QStringList names; QDomNamedNodeMap atts = el.attributes(); - for( uint i = 0; i < atts.length(); i++ ) + for (auto i = 0; i < atts.length(); i++) { QString name = atts.item( i ).nodeName(); if( name.endsWith( "_numerator" ) ) diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index 9c0a4596a11..a3c3bcf9184 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -299,11 +299,6 @@ void EnvelopeAndLfoParameters::fillLevel( float * _buf, f_cnt_t _frame, { QMutexLocker m(&m_paramMutex); - if( _frame < 0 || _release_begin < 0 ) - { - return; - } - fillLfoLevel( _buf, _frame, _frames ); for( fpp_t offset = 0; offset < _frames; ++offset, ++_buf, ++_frame ) diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index a57ce6ce5b2..eca06f9a2bb 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -66,7 +66,7 @@ InstrumentSoundShaping::InstrumentSoundShaping( m_filterCutModel( 14000.0, 1.0, 14000.0, 1.0, this, tr( "Cutoff frequency" ) ), m_filterResModel(0.5f, BasicFilters<>::minQ(), 10.f, 0.01f, this, tr("Q/Resonance")) { - for( int i = 0; i < NumTargets; ++i ) + for (auto i = std::size_t{0}; i < NumTargets; ++i) { float value_for_zero_amount = 0.0; if( static_cast(i) == Target::Volume ) @@ -279,7 +279,7 @@ f_cnt_t InstrumentSoundShaping::envFrames( const bool _only_vol ) const if( _only_vol == false ) { - for( int i = static_cast(Target::Volume)+1; i < NumTargets; ++i ) + for (auto i = static_cast(Target::Volume) + 1; i < NumTargets; ++i) { if( m_envLfoParameters[i]->isUsed() && m_envLfoParameters[i]->PAHD_Frames() > ret_val ) @@ -313,7 +313,7 @@ f_cnt_t InstrumentSoundShaping::releaseFrames() const return m_envLfoParameters[static_cast(Target::Volume)]->releaseFrames(); } - for( int i = static_cast(Target::Volume)+1; i < NumTargets; ++i ) + for (auto i = static_cast(Target::Volume) + 1; i < NumTargets; ++i) { if( m_envLfoParameters[i]->isUsed() ) { @@ -333,7 +333,7 @@ void InstrumentSoundShaping::saveSettings( QDomDocument & _doc, QDomElement & _t m_filterResModel.saveSettings( _doc, _this, "fres" ); m_filterEnabledModel.saveSettings( _doc, _this, "fwet" ); - for( int i = 0; i < NumTargets; ++i ) + for (auto i = std::size_t{0}; i < NumTargets; ++i) { m_envLfoParameters[i]->saveState( _doc, _this ).setTagName( m_envLfoParameters[i]->nodeName() + @@ -356,7 +356,7 @@ void InstrumentSoundShaping::loadSettings( const QDomElement & _this ) { if( node.isElement() ) { - for( int i = 0; i < NumTargets; ++i ) + for (auto i = std::size_t{0}; i < NumTargets; ++i) { if( node.nodeName() == m_envLfoParameters[i]->nodeName() + diff --git a/src/core/LocklessAllocator.cpp b/src/core/LocklessAllocator.cpp index 919093906c3..92eadd75128 100644 --- a/src/core/LocklessAllocator.cpp +++ b/src/core/LocklessAllocator.cpp @@ -71,8 +71,7 @@ LocklessAllocator::LocklessAllocator( size_t nmemb, size_t size ) LocklessAllocator::~LocklessAllocator() { - int available = m_available; - if( available != m_capacity ) + if (m_available != m_capacity) { fprintf( stderr, "LocklessAllocator: " "Destroying with elements still allocated\n" ); @@ -110,7 +109,7 @@ void * LocklessAllocator::alloc() // Some of these CAS loops could probably use relaxed atomics, as discussed // in http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange. // Let's use sequentially-consistent ops to be safe for now. - int available = m_available.load(); + auto available = m_available.load(); do { if( !available ) diff --git a/src/core/Microtuner.cpp b/src/core/Microtuner.cpp index 9c5ab871bc8..46d83017fa2 100644 --- a/src/core/Microtuner.cpp +++ b/src/core/Microtuner.cpp @@ -126,14 +126,14 @@ int Microtuner::octaveSize() const */ void Microtuner::updateScaleList(int index) { - if (index >= 0 && index < MaxScaleCount) + if (index >= 0 && static_cast(index) < MaxScaleCount) { m_scaleModel.replaceItem(index, QString::number(index) + ": " + Engine::getSong()->getScale(index)->getDescription()); } else { - for (int i = 0; i < MaxScaleCount; i++) + for (auto i = std::size_t{0}; i < MaxScaleCount; i++) { m_scaleModel.replaceItem(i, QString::number(i) + ": " + Engine::getSong()->getScale(i)->getDescription()); @@ -147,14 +147,14 @@ void Microtuner::updateScaleList(int index) */ void Microtuner::updateKeymapList(int index) { - if (index >= 0 && index < MaxKeymapCount) + if (index >= 0 && static_cast(index) < MaxKeymapCount) { m_keymapModel.replaceItem(index, QString::number(index) + ": " + Engine::getSong()->getKeymap(index)->getDescription()); } else { - for (int i = 0; i < MaxKeymapCount; i++) + for (auto i = std::size_t{0}; i < MaxKeymapCount; i++) { m_keymapModel.replaceItem(i, QString::number(i) + ": " + Engine::getSong()->getKeymap(i)->getDescription()); diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index ba02296f84b..7e3fc1f60c5 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -99,7 +99,7 @@ inline void MixerChannel::processed() void MixerChannel::incrementDeps() { - int i = m_dependenciesMet++ + 1; + const auto i = m_dependenciesMet++ + 1; if( i >= m_receives.size() && ! m_queued ) { m_queued = true; @@ -235,7 +235,7 @@ int Mixer::createChannel() void Mixer::activateSolo() { - for (int i = 1; i < m_mixerChannels.size(); ++i) + for (auto i = std::size_t{1}; i < m_mixerChannels.size(); ++i) { m_mixerChannels[i]->m_muteBeforeSolo = m_mixerChannels[i]->m_muteModel.value(); m_mixerChannels[i]->m_muteModel.setValue( true ); @@ -244,7 +244,7 @@ void Mixer::activateSolo() void Mixer::deactivateSolo() { - for (int i = 1; i < m_mixerChannels.size(); ++i) + for (auto i = std::size_t{1}; i < m_mixerChannels.size(); ++i) { m_mixerChannels[i]->m_muteModel.setValue( m_mixerChannels[i]->m_muteBeforeSolo ); } @@ -260,7 +260,7 @@ void Mixer::toggledSolo() m_mixerChannels[m_lastSoloed]->m_soloModel.setValue( false ); } //determine the soloed channel - for (int i = 0; i < m_mixerChannels.size(); ++i) + for (auto i = std::size_t{0}; i < m_mixerChannels.size(); ++i) { if (m_mixerChannels[i]->m_soloModel.value() == true) soloedChan = i; @@ -355,7 +355,7 @@ void Mixer::deleteChannel( int index ) m_mixerChannels.erase(m_mixerChannels.begin() + index); delete ch; - for( int i = index; i < m_mixerChannels.size(); ++i ) + for (auto i = static_cast(index); i < m_mixerChannels.size(); ++i) { validateChannelName( i, i + 1 ); @@ -381,7 +381,7 @@ void Mixer::deleteChannel( int index ) void Mixer::moveChannelLeft( int index ) { // can't move master or first channel - if( index <= 1 || index >= m_mixerChannels.size() ) + if (index <= 1 || static_cast(index) >= m_mixerChannels.size()) { return; } @@ -744,7 +744,7 @@ void Mixer::clearChannel(mix_ch_t index) void Mixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) { // save channels - for( int i = 0; i < m_mixerChannels.size(); ++i ) + for (auto i = std::size_t{0}; i < m_mixerChannels.size(); ++i) { MixerChannel * ch = m_mixerChannels[i]; @@ -755,7 +755,7 @@ void Mixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) ch->m_volumeModel.saveSettings( _doc, mixch, "volume" ); ch->m_muteModel.saveSettings( _doc, mixch, "muted" ); ch->m_soloModel.saveSettings( _doc, mixch, "soloed" ); - mixch.setAttribute( "num", i ); + mixch.setAttribute("num", static_cast(i)); mixch.setAttribute( "name", ch->m_name ); if (const auto& color = ch->color()) { mixch.setAttribute("color", color->name()); } @@ -774,7 +774,8 @@ void Mixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) // make sure we have at least num channels void Mixer::allocateChannelsTo(int num) { - while( num > m_mixerChannels.size() - 1 ) + if (num <= 0) { return; } + while (static_cast(num) > m_mixerChannels.size() - 1) { createChannel(); @@ -813,7 +814,7 @@ void Mixer::loadSettings( const QDomElement & _this ) // mixer sends QDomNodeList chData = mixch.childNodes(); - for( unsigned int i=0; iresampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio); advance(state, resampleResult.inputFramesUsed, loopMode); - const auto outputFrames = resampleResult.outputFramesGenerated; + const auto outputFrames = static_cast(resampleResult.outputFramesGenerated); if (outputFrames < numFrames) { std::fill_n(dst + outputFrames, numFrames - outputFrames, SampleFrame{}); } if (!typeInfo::isEqual(m_amplification, 1.0f)) { - for (int i = 0; i < numFrames; ++i) + for (auto i = std::size_t{0}; i < numFrames; ++i) { dst[i][0] *= m_amplification; dst[i][1] *= m_amplification; diff --git a/src/core/SampleDecoder.cpp b/src/core/SampleDecoder.cpp index 1e1ddb545cf..d3ee091f42a 100644 --- a/src/core/SampleDecoder.cpp +++ b/src/core/SampleDecoder.cpp @@ -174,7 +174,7 @@ auto decodeSampleOggVorbis(const QString& audioFile) -> std::optional(totalSamplesRead / numChannels); - for (int i = 0; i < result.size(); ++i) + for (auto i = std::size_t{0}; i < result.size(); ++i) { if (numChannels == 1) { result[i] = {buffer[i], buffer[i]}; } else if (numChannels > 1) { result[i] = {buffer[i * numChannels], buffer[i * numChannels + 1]}; } diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 392e9e2566c..92cb2ba3051 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -847,7 +847,7 @@ void Song::clearProject() stop(); } - for( int i = 0; i < PlayModeCount; i++ ) + for (auto i = std::size_t{0}; i < PlayModeCount; i++) { setPlayPos( 0, ( PlayMode )i ); } @@ -1349,7 +1349,7 @@ void Song::restoreScaleStates(const QDomElement &element) { QDomNode node = element.firstChild(); - for (int i = 0; i < MaxScaleCount && !node.isNull() && !isCancelled(); i++) + for (auto i = std::size_t{0}; i < MaxScaleCount && !node.isNull() && !isCancelled(); i++) { m_scales[i]->restoreState(node.toElement()); node = node.nextSibling(); @@ -1374,7 +1374,7 @@ void Song::restoreKeymapStates(const QDomElement &element) { QDomNode node = element.firstChild(); - for (int i = 0; i < MaxKeymapCount && !node.isNull() && !isCancelled(); i++) + for (auto i = std::size_t{0}; i < MaxKeymapCount && !node.isNull() && !isCancelled(); i++) { m_keymaps[i]->restoreState(node.toElement()); node = node.nextSibling(); diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 6c4ba465e4b..7ff73e71891 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -388,14 +388,14 @@ int Track::numOfClips() * \todo if we create a Clip here, should we somehow attach it to the * track? */ -Clip * Track::getClip( int clipNum ) +auto Track::getClip(std::size_t clipNum) -> Clip* { if( clipNum < m_clips.size() ) { return m_clips[clipNum]; } - printf( "called Track::getClip( %d ), " - "but Clip %d doesn't exist\n", clipNum, clipNum ); + printf( "called Track::getClip( %zu ), " + "but Clip %zu doesn't exist\n", clipNum, clipNum ); return createClip( clipNum * TimePos::ticksPerBar() ); } diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index 1321a8a247a..bd427523520 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -99,10 +99,9 @@ AudioOss::AudioOss( bool & _success_ful, AudioEngine* _audioEngine ) : fcntl( m_audioFD, F_SETFD, fcntl( m_audioFD, F_GETFD ) | FD_CLOEXEC ); int frag_spec; - for( frag_spec = 0; static_cast( 0x01 << frag_spec ) < - audioEngine()->framesPerPeriod() * channels() * - BYTES_PER_INT_SAMPLE; - ++frag_spec ) + for (frag_spec = 0; + 1u << frag_spec < audioEngine()->framesPerPeriod() * channels() * BYTES_PER_INT_SAMPLE; + ++frag_spec) { } diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index 00bbec2e491..eb5058bc6b5 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -229,10 +229,7 @@ void AudioPortAudio::stopProcessing() } -int AudioPortAudio::process_callback( - const float *_inputBuffer, - float * _outputBuffer, - unsigned long _framesPerBuffer ) +int AudioPortAudio::process_callback(const float* _inputBuffer, float* _outputBuffer, f_cnt_t _framesPerBuffer) { if( supportsCapture() ) { @@ -261,8 +258,7 @@ int AudioPortAudio::process_callback( } m_outBufSize = frames; } - const int min_len = std::min(static_cast(_framesPerBuffer), - m_outBufSize - m_outBufPos); + const auto min_len = std::min(_framesPerBuffer, m_outBufSize - m_outBufPos); for( fpp_t frame = 0; frame < min_len; ++frame ) { diff --git a/src/gui/MainApplication.cpp b/src/gui/MainApplication.cpp index d33ede4d260..48c400a24dd 100644 --- a/src/gui/MainApplication.cpp +++ b/src/gui/MainApplication.cpp @@ -92,7 +92,7 @@ bool MainApplication::winEventFilter(MSG* msg, long* result) switch(msg->message) { case WM_STYLECHANGING: - if(msg->wParam == GWL_EXSTYLE) + if (msg->wParam == static_cast(GWL_EXSTYLE)) { // Prevent plugins making the main window transparent STYLESTRUCT * style = reinterpret_cast(msg->lParam); diff --git a/src/gui/MicrotunerConfig.cpp b/src/gui/MicrotunerConfig.cpp index 6bb8415bd45..20660df4110 100644 --- a/src/gui/MicrotunerConfig.cpp +++ b/src/gui/MicrotunerConfig.cpp @@ -224,14 +224,14 @@ MicrotunerConfig::MicrotunerConfig() : */ void MicrotunerConfig::updateScaleList(int index) { - if (index >= 0 && index < MaxScaleCount) + if (index >= 0 && static_cast(index) < MaxScaleCount) { m_scaleComboModel.replaceItem(index, QString::number(index) + ": " + Engine::getSong()->getScale(index)->getDescription()); } else { - for (int i = 0; i < MaxScaleCount; i++) + for (auto i = std::size_t{0}; i < MaxScaleCount; i++) { m_scaleComboModel.replaceItem(i, QString::number(i) + ": " + Engine::getSong()->getScale(i)->getDescription()); @@ -246,14 +246,14 @@ void MicrotunerConfig::updateScaleList(int index) */ void MicrotunerConfig::updateKeymapList(int index) { - if (index >= 0 && index < MaxKeymapCount) + if (index >= 0 && static_cast(index) < MaxKeymapCount) { m_keymapComboModel.replaceItem(index, QString::number(index) + ": " + Engine::getSong()->getKeymap(index)->getDescription()); } else { - for (int i = 0; i < MaxKeymapCount; i++) + for (auto i = std::size_t{0}; i < MaxKeymapCount; i++) { m_keymapComboModel.replaceItem(i, QString::number(i) + ": " + Engine::getSong()->getKeymap(i)->getDescription()); diff --git a/src/gui/SampleWaveform.cpp b/src/gui/SampleWaveform.cpp index 91b6e26c3b6..ca356e72713 100644 --- a/src/gui/SampleWaveform.cpp +++ b/src/gui/SampleWaveform.cpp @@ -51,12 +51,12 @@ void SampleWaveform::visualize(Parameters parameters, QPainter& painter, const Q const size_t maxFrames = numPixels * static_cast(framesPerPixel); - int pixelIndex = 0; + auto pixelIndex = std::size_t{0}; - for (int i = 0; i < maxFrames; i += static_cast(resolution)) + for (auto i = std::size_t{0}; i < maxFrames; i += static_cast(resolution)) { pixelIndex = i / framesPerPixel; - const int frameIndex = !parameters.reversed ? i : maxFrames - i; + const auto frameIndex = !parameters.reversed ? i : maxFrames - i; const auto& frame = parameters.buffer[frameIndex]; const auto value = frame.average(); @@ -75,11 +75,11 @@ void SampleWaveform::visualize(Parameters parameters, QPainter& painter, const Q pixelIndex++; } - for (int i = 0; i < numPixels; i++) + for (auto i = std::size_t{0}; i < numPixels; i++) { const int lineY1 = centerY - max[i] * halfHeight * parameters.amplification; const int lineY2 = centerY - min[i] * halfHeight * parameters.amplification; - const int lineX = i + x; + const int lineX = static_cast(i) + x; painter.drawLine(lineX, lineY1, lineX, lineY2); const float rms = std::sqrt(squared[i] / framesPerResolution); diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index a0f7f7c53c6..9eb6acb6b94 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -534,8 +534,9 @@ 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()); + const auto trackIt = std::find(tc->tracks().begin(), tc->tracks().end(), clipTrack); + assert(trackIt != tc->tracks().end()); + int trackIndex = std::distance(tc->tracks().begin(), trackIt); QDomElement clipElement = dataFile.createElement("clip"); clipElement.setAttribute( "trackIndex", trackIndex ); clipElement.setAttribute( "trackType", static_cast(clipTrack->type()) ); diff --git a/src/gui/editors/PatternEditor.cpp b/src/gui/editors/PatternEditor.cpp index 5237690a7e7..2915786981d 100644 --- a/src/gui/editors/PatternEditor.cpp +++ b/src/gui/editors/PatternEditor.cpp @@ -135,10 +135,10 @@ void PatternEditor::dropEvent(QDropEvent* de) // Ensure pattern clips exist bool hasValidPatternClips = false; - if (t->getClips().size() == m_ps->numOfPatterns()) + if (t->getClips().size() == static_cast(m_ps->numOfPatterns())) { hasValidPatternClips = true; - for (int i = 0; i < t->getClips().size(); ++i) + for (auto i = std::size_t{0}; i < t->getClips().size(); ++i) { if (t->getClips()[i]->startPosition() != TimePos(i, 0)) { diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 5811f956cc1..bda6894f3ee 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -219,7 +219,7 @@ PianoRoll::PianoRoll() : m_noteEditMenu = new QMenu( this ); m_noteEditMenu->clear(); - for( int i = 0; i < m_nemStr.size(); ++i ) + for (auto i = std::size_t{0}; i < m_nemStr.size(); ++i) { auto act = new QAction(m_nemStr.at(i), this); connect( act, &QAction::triggered, [this, i](){ changeNoteEditMode(i); } ); diff --git a/src/gui/instrument/InstrumentSoundShapingView.cpp b/src/gui/instrument/InstrumentSoundShapingView.cpp index a3a78e25670..7558c4c2637 100644 --- a/src/gui/instrument/InstrumentSoundShapingView.cpp +++ b/src/gui/instrument/InstrumentSoundShapingView.cpp @@ -48,7 +48,7 @@ InstrumentSoundShapingView::InstrumentSoundShapingView(QWidget* parent) : m_targetsTabWidget = new TabWidget(tr("TARGET"), this); - for (int i = 0; i < InstrumentSoundShaping::NumTargets; ++i) + for (auto i = std::size_t{0}; i < InstrumentSoundShaping::NumTargets; ++i) { m_envLfoViews[i] = new EnvelopeAndLfoView(m_targetsTabWidget); m_targetsTabWidget->addTab(m_envLfoViews[i], @@ -115,7 +115,7 @@ void InstrumentSoundShapingView::modelChanged() m_filterComboBox->setModel( &m_ss->m_filterModel ); m_filterCutKnob->setModel( &m_ss->m_filterCutModel ); m_filterResKnob->setModel( &m_ss->m_filterResModel ); - for( int i = 0; i < InstrumentSoundShaping::NumTargets; ++i ) + for (auto i = std::size_t{0}; i < InstrumentSoundShaping::NumTargets; ++i) { m_envLfoViews[i]->setModel( m_ss->m_envLfoParameters[i] ); } diff --git a/src/gui/modals/ExportProjectDialog.cpp b/src/gui/modals/ExportProjectDialog.cpp index 8dfda4981f6..1f989841f3b 100644 --- a/src/gui/modals/ExportProjectDialog.cpp +++ b/src/gui/modals/ExportProjectDialog.cpp @@ -56,7 +56,7 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name, } int cbIndex = 0; - for( int i = 0; i < ProjectRenderer::NumFileFormats; ++i ) + for (auto i = std::size_t{0}; i < ProjectRenderer::NumFileFormats; ++i) { if( ProjectRenderer::fileEncodeDevices[i].isAvailable() ) { @@ -268,7 +268,7 @@ void ExportProjectDialog::startBtnClicked() } // Find proper file extension. - for( int i = 0; i < ProjectRenderer::NumFileFormats; ++i ) + for (auto i = std::size_t{0}; i < ProjectRenderer::NumFileFormats; ++i) { if (m_ft == ProjectRenderer::fileEncodeDevices[i].m_fileFormat) { diff --git a/src/gui/tracks/TrackContentWidget.cpp b/src/gui/tracks/TrackContentWidget.cpp index e205a0c00ea..afea82ea41c 100644 --- a/src/gui/tracks/TrackContentWidget.cpp +++ b/src/gui/tracks/TrackContentWidget.cpp @@ -413,7 +413,7 @@ bool TrackContentWidget::canPasteSelection( TimePos clipPos, const QMimeData* md int finalTrackIndex = trackIndex + currentTrackIndex - initialTrackIndex; // Track must be in TrackContainer's tracks - if( finalTrackIndex < 0 || finalTrackIndex >= tracks.size() ) + if (finalTrackIndex < 0 || static_cast(finalTrackIndex) >= tracks.size()) { return false; } diff --git a/src/gui/widgets/Oscilloscope.cpp b/src/gui/widgets/Oscilloscope.cpp index 3178d7ef254..775bd96a8b8 100644 --- a/src/gui/widgets/Oscilloscope.cpp +++ b/src/gui/widgets/Oscilloscope.cpp @@ -190,7 +190,7 @@ void Oscilloscope::paintEvent( QPaintEvent * ) otherChannelsColor(); // Any other channel p.setPen(QPen(color, width)); - for( int frame = 0; frame < frames; ++frame ) + for (auto frame = std::size_t{0}; frame < frames; ++frame) { sample_t const clippedSample = AudioEngine::clip(m_buffer[frame][ch]); m_points[frame] = QPointF( diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index dfee9a5e610..d6e64ff7ed6 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -474,7 +474,6 @@ MidiClip * MidiClip::adjacentMidiClipByOffset(int offset) const { 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 2f5f12aaae8453863f62305cb54e4cfe779b661c Mon Sep 17 00:00:00 2001 From: saker Date: Wed, 24 Jul 2024 18:50:47 -0400 Subject: [PATCH 06/20] Switch to libsamplerate's callback API in `Sample` (#7361) --- include/AudioResampler.h | 65 --------- include/Sample.h | 52 +++----- include/SampleBuffer.h | 1 - .../AudioFileProcessor/AudioFileProcessor.cpp | 12 +- plugins/GigPlayer/GigPlayer.cpp | 2 +- plugins/GigPlayer/GigPlayer.h | 6 + src/core/AudioResampler.cpp | 69 ---------- src/core/CMakeLists.txt | 1 - src/core/Sample.cpp | 126 +++++------------- 9 files changed, 69 insertions(+), 265 deletions(-) delete mode 100644 include/AudioResampler.h delete mode 100644 src/core/AudioResampler.cpp diff --git a/include/AudioResampler.h b/include/AudioResampler.h deleted file mode 100644 index 6dd6fcc6039..00000000000 --- a/include/AudioResampler.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * AudioResampler.h - wrapper around libsamplerate - * - * Copyright (c) 2023 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_AUDIO_RESAMPLER_H -#define LMMS_AUDIO_RESAMPLER_H - -#include - -#include "lmms_export.h" - -namespace lmms { - -class LMMS_EXPORT AudioResampler -{ -public: - struct ProcessResult - { - int error; - long inputFramesUsed; - long outputFramesGenerated; - }; - - AudioResampler(int interpolationMode, int channels); - AudioResampler(const AudioResampler&) = delete; - AudioResampler(AudioResampler&&) = delete; - ~AudioResampler(); - - AudioResampler& operator=(const AudioResampler&) = delete; - AudioResampler& operator=(AudioResampler&&) = delete; - - auto resample(const float* in, long inputFrames, float* out, long outputFrames, double ratio) -> ProcessResult; - auto interpolationMode() const -> int { return m_interpolationMode; } - auto channels() const -> int { return m_channels; } - void setRatio(double ratio); - -private: - int m_interpolationMode = -1; - int m_channels = 0; - int m_error = 0; - SRC_STATE* m_state = nullptr; -}; -} // namespace lmms - -#endif // LMMS_AUDIO_RESAMPLER_H diff --git a/include/Sample.h b/include/Sample.h index 3fd5bc38ef1..75435036805 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -25,24 +25,18 @@ #ifndef LMMS_SAMPLE_H #define LMMS_SAMPLE_H -#include #include +#include -#include "AudioResampler.h" #include "Note.h" #include "SampleBuffer.h" +#include "lmms_basics.h" #include "lmms_export.h" namespace lmms { class LMMS_EXPORT Sample { public: - // values for buffer margins, used for various libsamplerate interpolation modes - // the array positions correspond to the converter_type parameter values in libsamplerate - // if there appears problems with playback on some interpolation mode, then the value for that mode - // may need to be higher - conversely, to optimize, some may work with lower values - static constexpr auto s_interpolationMargins = std::array{64, 64, 64, 4, 4}; - enum class Loop { Off, @@ -50,30 +44,25 @@ class LMMS_EXPORT Sample PingPong }; - class LMMS_EXPORT PlaybackState + struct LMMS_EXPORT PlaybackState { - public: - PlaybackState(bool varyingPitch = false, int interpolationMode = SRC_LINEAR) - : m_resampler(interpolationMode, DEFAULT_CHANNELS) - , m_varyingPitch(varyingPitch) + PlaybackState(int interpolationMode = SRC_LINEAR) + : resampleState(src_callback_new(&Sample::render, interpolationMode, DEFAULT_CHANNELS, &error, this)) { + assert(resampleState && src_strerror(error)); } - auto resampler() -> AudioResampler& { return m_resampler; } - auto frameIndex() const -> int { return m_frameIndex; } - auto varyingPitch() const -> bool { return m_varyingPitch; } - auto backwards() const -> bool { return m_backwards; } - - void setFrameIndex(int frameIndex) { m_frameIndex = frameIndex; } - void setVaryingPitch(bool varyingPitch) { m_varyingPitch = varyingPitch; } - void setBackwards(bool backwards) { m_backwards = backwards; } + ~PlaybackState() + { + src_delete(resampleState); + } - private: - AudioResampler m_resampler; - int m_frameIndex = 0; - bool m_varyingPitch = false; - bool m_backwards = false; - friend class Sample; + const Sample* sample = nullptr; + Loop* loop = nullptr; + SRC_STATE* resampleState = nullptr; + int frameIndex = 0; + int error = 0; + bool backwards = false; }; Sample() = default; @@ -87,7 +76,7 @@ class LMMS_EXPORT Sample auto operator=(const Sample&) -> Sample&; auto operator=(Sample&&) -> Sample&; - auto play(SampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency = DefaultBaseFreq, + auto play(SampleFrame* dst, PlaybackState* state, size_t numFrames, double frequency = DefaultBaseFreq, Loop loopMode = Loop::Off) const -> bool; auto sampleDuration() const -> std::chrono::milliseconds; @@ -117,17 +106,14 @@ class LMMS_EXPORT Sample void setReversed(bool reversed) { m_reversed.store(reversed, std::memory_order_relaxed); } private: - void playRaw(SampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const; - void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const; - -private: + static auto render(void* callbackData, float** data) -> long; std::shared_ptr m_buffer = SampleBuffer::emptyBuffer(); std::atomic m_startFrame = 0; std::atomic m_endFrame = 0; std::atomic m_loopStartFrame = 0; std::atomic m_loopEndFrame = 0; std::atomic m_amplification = 1.0f; - std::atomic m_frequency = DefaultBaseFreq; + std::atomic m_frequency = DefaultBaseFreq; std::atomic m_reversed = false; }; } // namespace lmms diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 8ec6c58863c..114634577ee 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -28,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index 4cc14ba9cdb..2e63b5178b3 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -144,9 +144,9 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, srcmode = SRC_SINC_MEDIUM_QUALITY; break; } - _n->m_pluginData = new Sample::PlaybackState(_n->hasDetuningInfo(), srcmode); - static_cast(_n->m_pluginData)->setFrameIndex(m_nextPlayStartPoint); - static_cast(_n->m_pluginData)->setBackwards(m_nextPlayBackwards); + _n->m_pluginData = new Sample::PlaybackState(srcmode); + static_cast(_n->m_pluginData)->frameIndex = m_nextPlayStartPoint; + static_cast(_n->m_pluginData)->backwards = m_nextPlayBackwards; // debug code /* qDebug( "frames %d", m_sample->frames() ); @@ -162,7 +162,7 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, static_cast(m_loopModel.value()))) { applyRelease( _working_buffer, _n ); - emit isPlaying(static_cast(_n->m_pluginData)->frameIndex()); + emit isPlaying(static_cast(_n->m_pluginData)->frameIndex); } else { @@ -176,8 +176,8 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, } if( m_stutterModel.value() == true ) { - m_nextPlayStartPoint = static_cast(_n->m_pluginData)->frameIndex(); - m_nextPlayBackwards = static_cast(_n->m_pluginData)->backwards(); + m_nextPlayStartPoint = static_cast(_n->m_pluginData)->frameIndex; + m_nextPlayBackwards = static_cast(_n->m_pluginData)->backwards; } } diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index b72e30b3335..061df7bd506 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -437,7 +437,7 @@ void GigInstrument::play( SampleFrame* _working_buffer ) if (sample.region->PitchTrack == true) { freq_factor *= sample.freqFactor; } // We need a bit of margin so we don't get glitching - samples = frames / freq_factor + Sample::s_interpolationMargins[m_interpolation]; + samples = frames / freq_factor + s_interpolationMargins[m_interpolation]; } // Load this note's data diff --git a/plugins/GigPlayer/GigPlayer.h b/plugins/GigPlayer/GigPlayer.h index 685c7f5469a..117178e54a1 100644 --- a/plugins/GigPlayer/GigPlayer.h +++ b/plugins/GigPlayer/GigPlayer.h @@ -240,6 +240,12 @@ class GigInstrument : public Instrument mapPropertyFromModel( int, getPatch, setPatch, m_patchNum ); public: + // values for buffer margins, used for various libsamplerate interpolation modes + // the array positions correspond to the converter_type parameter values in libsamplerate + // if there appears problems with playback on some interpolation mode, then the value for that mode + // may need to be higher - conversely, to optimize, some may work with lower values + static constexpr auto s_interpolationMargins = std::array{64, 64, 64, 4, 4}; + GigInstrument( InstrumentTrack * _instrument_track ); ~GigInstrument() override; diff --git a/src/core/AudioResampler.cpp b/src/core/AudioResampler.cpp deleted file mode 100644 index 8fb7d95a2aa..00000000000 --- a/src/core/AudioResampler.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * AudioResampler.cpp - wrapper for libsamplerate - * - * Copyright (c) 2023 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 "AudioResampler.h" - -#include -#include -#include - -namespace lmms { - -AudioResampler::AudioResampler(int interpolationMode, int channels) - : m_interpolationMode(interpolationMode) - , m_channels(channels) - , m_state(src_new(interpolationMode, channels, &m_error)) -{ - if (!m_state) - { - const auto errorMessage = std::string{src_strerror(m_error)}; - const auto fullMessage = std::string{"Failed to create an AudioResampler: "} + errorMessage; - throw std::runtime_error{fullMessage}; - } -} - -AudioResampler::~AudioResampler() -{ - src_delete(m_state); -} - -auto AudioResampler::resample(const float* in, long inputFrames, float* out, long outputFrames, double ratio) - -> ProcessResult -{ - auto data = SRC_DATA{}; - data.data_in = in; - data.input_frames = inputFrames; - data.data_out = out; - data.output_frames = outputFrames; - data.src_ratio = ratio; - data.end_of_input = 0; - return {src_process(m_state, &data), data.input_frames_used, data.output_frames_gen}; -} - -void AudioResampler::setRatio(double ratio) -{ - src_set_ratio(m_state, ratio); -} - -} // namespace lmms diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3608d28486a..9eeb33904ba 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -4,7 +4,6 @@ set(LMMS_SRCS core/AudioEngine.cpp core/AudioEngineProfiler.cpp core/AudioEngineWorkerThread.cpp - core/AudioResampler.cpp core/AutomatableModel.cpp core/AutomationClip.cpp core/AutomationNode.cpp diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index db99620c9d4..564e08201a4 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -26,6 +26,8 @@ #include +#include "MixHelpers.h" + namespace lmms { Sample::Sample(const QString& audioFile) @@ -116,43 +118,28 @@ auto Sample::operator=(Sample&& other) -> Sample& return *this; } -bool Sample::play(SampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) const +bool Sample::play(SampleFrame* dst, PlaybackState* state, size_t numFrames, double frequency, Loop loopMode) const { assert(numFrames > 0); - assert(desiredFrequency > 0); - - const auto pastBounds = state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards); - if (loopMode == Loop::Off && pastBounds) { return false; } + assert(frequency > 0); + if (m_buffer->empty()) { return false; } - const auto outputSampleRate = Engine::audioEngine()->outputSampleRate() * m_frequency / desiredFrequency; + const auto outputSampleRate = Engine::audioEngine()->outputSampleRate() * m_frequency / frequency; const auto inputSampleRate = m_buffer->sampleRate(); const auto resampleRatio = outputSampleRate / inputSampleRate; - const auto marginSize = s_interpolationMargins[state->resampler().interpolationMode()]; - - state->m_frameIndex = std::max(m_startFrame, state->m_frameIndex); - - auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); - playRaw(playBuffer.data(), playBuffer.size(), state, loopMode); - state->resampler().setRatio(resampleRatio); + state->frameIndex = std::max(m_startFrame, state->frameIndex); + state->sample = this; + state->loop = &loopMode; - const auto resampleResult - = state->resampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio); - advance(state, resampleResult.inputFramesUsed, loopMode); - - const auto outputFrames = static_cast(resampleResult.outputFramesGenerated); - if (outputFrames < numFrames) { std::fill_n(dst + outputFrames, numFrames - outputFrames, SampleFrame{}); } - - if (!typeInfo::isEqual(m_amplification, 1.0f)) + src_set_ratio(state->resampleState, resampleRatio); + if (src_callback_read(state->resampleState, resampleRatio, numFrames, &dst[0][0]) != 0) { - for (auto i = std::size_t{0}; i < numFrames; ++i) - { - dst[i][0] *= m_amplification; - dst[i][1] *= m_amplification; - } + MixHelpers::multiply(dst, m_amplification, numFrames); + return true; } - return true; + return false; } auto Sample::sampleDuration() const -> std::chrono::milliseconds @@ -170,82 +157,43 @@ void Sample::setAllPointFrames(int startFrame, int endFrame, int loopStartFrame, setLoopEndFrame(loopEndFrame); } -void Sample::playRaw(SampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const +long Sample::render(void* callbackData, float** data) { - if (m_buffer->size() < 1) { return; } - - auto index = state->m_frameIndex; - auto backwards = state->m_backwards; + const auto state = static_cast(callbackData); + const auto loop = *state->loop; + const auto sample = state->sample; + auto& index = state->frameIndex; + auto& backwards = state->backwards; - for (size_t i = 0; i < numFrames; ++i) - { - switch (loopMode) - { - case Loop::Off: - if (index < 0 || index >= m_endFrame) { return; } - break; - case Loop::On: - if (index < m_loopStartFrame && backwards) { index = m_loopEndFrame - 1; } - else if (index >= m_loopEndFrame) { index = m_loopStartFrame; } - break; - case Loop::PingPong: - if (index < m_loopStartFrame && backwards) - { - index = m_loopStartFrame; - backwards = false; - } - else if (index >= m_loopEndFrame) - { - index = m_loopEndFrame - 1; - backwards = true; - } - break; - default: - break; - } - - dst[i] = m_buffer->data()[m_reversed ? m_buffer->size() - index - 1 : index]; - backwards ? --index : ++index; - } -} - -void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const -{ - state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; - if (loopMode == Loop::Off) { return; } - - const auto distanceFromLoopStart = std::abs(state->m_frameIndex - m_loopStartFrame); - const auto distanceFromLoopEnd = std::abs(state->m_frameIndex - m_loopEndFrame); - const auto loopSize = m_loopEndFrame - m_loopStartFrame; - if (loopSize == 0) { return; } - - switch (loopMode) + switch (loop) { + case Loop::Off: + if (index < 0 || index >= sample->m_endFrame) { return 0; } + break; case Loop::On: - if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) - { - state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopStart % loopSize; - } - else if (state->m_frameIndex >= m_loopEndFrame) - { - state->m_frameIndex = m_loopStartFrame + distanceFromLoopEnd % loopSize; - } + if (index < sample->m_loopStartFrame && state->backwards) { index = sample->m_loopEndFrame - 1; } + else if (index >= sample->m_loopEndFrame) { index = sample->m_loopStartFrame; } break; case Loop::PingPong: - if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) + if (index < sample->m_loopStartFrame && state->backwards) { - state->m_frameIndex = m_loopStartFrame + distanceFromLoopStart % loopSize; - state->m_backwards = false; + index = sample->m_loopStartFrame; + backwards = false; } - else if (state->m_frameIndex >= m_loopEndFrame) + else if (index >= sample->m_loopEndFrame) { - state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopEnd % loopSize; - state->m_backwards = true; + index = sample->m_loopEndFrame - 1; + backwards = true; } break; default: break; } + + const auto srcIndex = sample->m_reversed ? sample->m_buffer->size() - index - 1 : index; + *data = const_cast(&sample->m_buffer->data()[srcIndex][0]); + backwards ? --index : ++index; + return 1; } } // namespace lmms From 99c30ea1ab7be264096fa76ccb96f43c7f98aeb1 Mon Sep 17 00:00:00 2001 From: szeli1 <143485814+szeli1@users.noreply.github.com> Date: Thu, 25 Jul 2024 01:00:28 +0200 Subject: [PATCH 07/20] Fixed alt + left or right causing a crash in PianoRoll (#7390) --- src/tracks/MidiClip.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index d6e64ff7ed6..409fb60aefe 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -473,8 +473,9 @@ MidiClip * MidiClip::nextMidiClip() const MidiClip * MidiClip::adjacentMidiClipByOffset(int offset) const { auto& clips = m_instrumentTrack->getClips(); - int clipNum = m_instrumentTrack->getClipNum(this); - return dynamic_cast(clips[clipNum + offset]); + int clipNum = m_instrumentTrack->getClipNum(this) + offset; + if (clipNum < 0 || static_cast(clipNum) >= clips.size()) { return nullptr; } + return dynamic_cast(clips[clipNum]); } From 592da1cac728d7eb2317bcab27426e1d08b40040 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 27 Jul 2024 14:32:05 +0200 Subject: [PATCH 08/20] Fix include of array in BasicFilters.h (#7398) Fix a missing include of `array` in `BasicFilters.h`. --- include/BasicFilters.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/BasicFilters.h b/include/BasicFilters.h index 25dcf834c66..8d21b3657ca 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -36,6 +36,7 @@ #endif #include +#include #include "lmms_basics.h" #include "lmms_constants.h" From 627209ad1dbdb9541341d89e7c50ed4dc11f8791 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sat, 27 Jul 2024 17:23:34 +0100 Subject: [PATCH 09/20] Apply warning flags to RemoteVstPlugin too (#7389) --- CMakeLists.txt | 69 +----------------- cmake/modules/ErrorFlags.cmake | 72 +++++++++++++++++++ plugins/VstBase/CMakeLists.txt | 1 + plugins/VstBase/RemoteVstPlugin.cpp | 3 +- .../VstBase/RemoteVstPlugin/CMakeLists.txt | 10 ++- 5 files changed, 85 insertions(+), 70 deletions(-) create mode 100644 cmake/modules/ErrorFlags.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a6d97e8d87..74b6d73b6f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -630,73 +630,6 @@ ENDIF(WANT_DEBUG_FPE) # check for libsamplerate FIND_PACKAGE(Samplerate 0.1.8 MODULE REQUIRED) -# Shim the SYSTEM property for older CMake versions -if(CMAKE_VERSION VERSION_LESS "3.25") - define_property(TARGET - PROPERTY SYSTEM - INHERITED - BRIEF_DOCS "Shim of built-in SYSTEM property for CMake versions less than 3.25" - FULL_DOCS "Non-functional, but allows the property to be inherited properly." - "See the CMake documentation at https://cmake.org/cmake/help/latest/prop_tgt/SYSTEM.html." - ) -endif() - -# Add warning and error flags -option(USE_WERROR "Treat compiler warnings as errors" OFF) -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - set(COMPILE_ERROR_FLAGS - "-Wall" # Enable most warnings by default - ) - set(THIRD_PARTY_COMPILE_ERROR_FLAGS - "-w" # Disable all warnings - ) - - if(CMAKE_COMPILER_IS_GNUCXX) - list(APPEND COMPILE_ERROR_FLAGS - # The following warning generates false positives that are difficult - # to work around, in particular when inlining calls to standard - # algorithms performed on single-element arrays. See, for example, - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111273. - "-Wno-array-bounds" # Permit out-of-bounds array subscripts - ) - - if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11") - list(APPEND COMPILE_ERROR_FLAGS - # This has the same problems described above for "array-bounds". - "-Wno-stringop-overread" # Permit string functions overreading the source - ) - endif() - endif() - - if(USE_WERROR) - list(APPEND COMPILE_ERROR_FLAGS - "-Werror" # Treat warnings as errors - ) - endif() -elseif(MSVC) - set(COMPILE_ERROR_FLAGS - "/W2" # Enable some warnings by default - "/external:W0" # Don't emit warnings for third-party code - "/external:anglebrackets" # Consider headers included with angle brackets to be third-party - "/external:templates-" # Still emit warnings from first-party instantiations of third-party templates - # Silence "class X needs to have DLL-interface to be used by clients of - # class Y" warnings. These aren't trivial to address, and don't pose a - # problem for us since we build all modules with the same compiler and - # options, and dynamically link the CRT. - "/wd4251" - ) - set(THIRD_PARTY_COMPILE_ERROR_FLAGS - "/W0" # Disable all warnings - ) - - if(USE_WERROR) - list(APPEND COMPILE_ERROR_FLAGS - "/WX" # Treat warnings as errors - ) - endif() -endif() -add_compile_options("$>,${THIRD_PARTY_COMPILE_ERROR_FLAGS},${COMPILE_ERROR_FLAGS}>") - IF(NOT CMAKE_BUILD_TYPE) message(STATUS "Setting build type to 'Release' as none was specified.") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) @@ -778,6 +711,8 @@ add_sanitizer(memory "Clang" WANT_DEBUG_MSAN STATUS_DEBUG_MSAN -fno-omit-frame-p # not being found by PeakController add_sanitizer(undefined "GNU|Clang" WANT_DEBUG_UBSAN STATUS_DEBUG_UBSAN -fno-sanitize=vptr) +# Add warning and error flags +include(ErrorFlags) # use ccache include(CompileCache) diff --git a/cmake/modules/ErrorFlags.cmake b/cmake/modules/ErrorFlags.cmake new file mode 100644 index 00000000000..57cc6ad49f8 --- /dev/null +++ b/cmake/modules/ErrorFlags.cmake @@ -0,0 +1,72 @@ +# Shim the SYSTEM property for older CMake versions - we rely on this property +# to determine which set of error flags to use. +if(CMAKE_VERSION VERSION_LESS "3.25") + define_property(TARGET + PROPERTY SYSTEM + INHERITED + BRIEF_DOCS "Shim of built-in SYSTEM property for CMake versions less than 3.25" + FULL_DOCS "Non-functional, but allows the property to be inherited properly." + "See the CMake documentation at https://cmake.org/cmake/help/latest/prop_tgt/SYSTEM.html." + ) +endif() + +# Allow the user to control whether to treat warnings as errors +option(USE_WERROR "Treat compiler warnings as errors" OFF) + +# Compute the appropriate flags for the current compiler and options +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set(COMPILE_ERROR_FLAGS + "-Wall" # Enable most warnings by default + ) + set(THIRD_PARTY_COMPILE_ERROR_FLAGS + "-w" # Disable all warnings + ) + + if(CMAKE_COMPILER_IS_GNUCXX) + list(APPEND COMPILE_ERROR_FLAGS + # The following warning generates false positives that are difficult + # to work around, in particular when inlining calls to standard + # algorithms performed on single-element arrays. See, for example, + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111273. + "-Wno-array-bounds" # Permit out-of-bounds array subscripts + ) + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11") + list(APPEND COMPILE_ERROR_FLAGS + # This has the same problems described above for "array-bounds". + "-Wno-stringop-overread" # Permit string functions overreading the source + ) + endif() + endif() + + if(USE_WERROR) + list(APPEND COMPILE_ERROR_FLAGS + "-Werror" # Treat warnings as errors + ) + endif() +elseif(MSVC) + set(COMPILE_ERROR_FLAGS + "/W2" # Enable some warnings by default + "/external:W0" # Don't emit warnings for third-party code + "/external:anglebrackets" # Consider headers included with angle brackets to be third-party + "/external:templates-" # Still emit warnings from first-party instantiations of third-party templates + # Silence "class X needs to have DLL-interface to be used by clients of + # class Y" warnings. These aren't trivial to address, and don't pose a + # problem for us since we build all modules with the same compiler and + # options, and dynamically link the CRT. + "/wd4251" + ) + set(THIRD_PARTY_COMPILE_ERROR_FLAGS + "/W0" # Disable all warnings + ) + + if(USE_WERROR) + list(APPEND COMPILE_ERROR_FLAGS + "/WX" # Treat warnings as errors + ) + endif() +endif() + +# Add the flags to the whole directory tree. We use the third-party flags for +# targets whose SYSTEM property is true, and the normal flags otherwise. +add_compile_options("$>,${THIRD_PARTY_COMPILE_ERROR_FLAGS},${COMPILE_ERROR_FLAGS}>") diff --git a/plugins/VstBase/CMakeLists.txt b/plugins/VstBase/CMakeLists.txt index 046f515ea75..15d47200c96 100644 --- a/plugins/VstBase/CMakeLists.txt +++ b/plugins/VstBase/CMakeLists.txt @@ -33,6 +33,7 @@ set(export_variables "LMMS_BUILD_WIN32" "PLUGIN_DIR" "LMMS_HAVE_LIBRT" + "USE_WERROR" ) SET(EXTERNALPROJECT_CMAKE_ARGS diff --git a/plugins/VstBase/RemoteVstPlugin.cpp b/plugins/VstBase/RemoteVstPlugin.cpp index 62cb7cfe5e1..f8bf39a3755 100644 --- a/plugins/VstBase/RemoteVstPlugin.cpp +++ b/plugins/VstBase/RemoteVstPlugin.cpp @@ -842,7 +842,6 @@ void RemoteVstPlugin::initEditor() #endif #else - XEvent e; Atom prop_atom, val_atom; if (m_display == nullptr) @@ -2301,7 +2300,7 @@ void RemoteVstPlugin::guiEventLoop() { XNextEvent(m_display, &e); - if (e.type == ClientMessage && e.xclient.data.l[0] == m_wmDeleteMessage) + if (e.type == ClientMessage && static_cast(e.xclient.data.l[0]) == m_wmDeleteMessage) { hideEditor(); } diff --git a/plugins/VstBase/RemoteVstPlugin/CMakeLists.txt b/plugins/VstBase/RemoteVstPlugin/CMakeLists.txt index dd15bb0bd53..3f861e2d566 100644 --- a/plugins/VstBase/RemoteVstPlugin/CMakeLists.txt +++ b/plugins/VstBase/RemoteVstPlugin/CMakeLists.txt @@ -1,4 +1,9 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.13) + +if(POLICY "CMP0092") + cmake_policy(SET CMP0092 NEW) # MSVC warning flags are not in CMAKE__FLAGS by default. +endif() + project(RemoteVstPlugin LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) @@ -22,6 +27,9 @@ FOREACH( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) SET("CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") ENDFOREACH() +# Add warning and error flags +include(ErrorFlags) + # Import of windows.h breaks min()/max() add_definitions(-DNOMINMAX) From 0d3c43d237fb283ec156b074300ea576ad13d9cf Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sun, 28 Jul 2024 15:54:17 +0200 Subject: [PATCH 10/20] Fixup of #7381 (#7401) Fixup of commit 9c0fc8fc6994b87ece6e706104877b715966af9b --- plugins/ZynAddSubFx/ZynAddSubFx.cpp | 5 +++++ plugins/ZynAddSubFx/zynaddsubfx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/ZynAddSubFx/ZynAddSubFx.cpp b/plugins/ZynAddSubFx/ZynAddSubFx.cpp index 77b242199f8..51610d87786 100644 --- a/plugins/ZynAddSubFx/ZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/ZynAddSubFx.cpp @@ -142,6 +142,11 @@ ZynAddSubFxInstrument::ZynAddSubFxInstrument( connect( instrumentTrack()->pitchRangeModel(), SIGNAL( dataChanged() ), this, SLOT( updatePitchRange() ), Qt::DirectConnection ); + + // ZynAddSubFX's internal value that LMMS's FREQ knob controls + // isn't set properly when the instrument is first loaded in, + // and doesn't update until the FREQ knob is moved + updateFilterFreq(); } diff --git a/plugins/ZynAddSubFx/zynaddsubfx b/plugins/ZynAddSubFx/zynaddsubfx index 9499523f703..d958c3668cc 160000 --- a/plugins/ZynAddSubFx/zynaddsubfx +++ b/plugins/ZynAddSubFx/zynaddsubfx @@ -1 +1 @@ -Subproject commit 9499523f70322b6034673566898300e7543fdf3e +Subproject commit d958c3668cc163805d581e97eb4d742168b6aad9 From 1c865843f7f9087a1d1a58c0f3b320228f824d86 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sun, 28 Jul 2024 15:55:41 +0200 Subject: [PATCH 11/20] Lv2: Improve plugin description (#7357) --- include/Lv2Basics.h | 10 +- src/core/lv2/Lv2SubPluginFeatures.cpp | 195 +++++++++++++++++++------- 2 files changed, 150 insertions(+), 55 deletions(-) diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h index 53489e30d10..5b286586875 100644 --- a/include/Lv2Basics.h +++ b/include/Lv2Basics.h @@ -1,7 +1,7 @@ /* * Lv2Basics.h - basic Lv2 utils * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -37,6 +37,12 @@ namespace lmms { +template +struct LilvPtrDeleter +{ + void operator()(T* n) { lilv_free(static_cast(n)); } +}; + struct LilvNodeDeleter { void operator()(LilvNode* n) { lilv_node_free(n); } @@ -52,6 +58,8 @@ struct LilvScalePointsDeleter void operator()(LilvScalePoints* s) { lilv_scale_points_free(s); } }; +template +using AutoLilvPtr = std::unique_ptr>; using AutoLilvNode = std::unique_ptr; using AutoLilvNodes = std::unique_ptr; using AutoLilvScalePoints = std::unique_ptr; diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index 135da3e2a10..66abe2f2e5a 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -3,7 +3,7 @@ * Plugin::Descriptor::SubPluginFeatures for * hosting LV2 plugins * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -28,8 +28,10 @@ #ifdef LMMS_HAVE_LV2 +#include #include #include +#include #include "Engine.h" #include "Lv2Basics.h" @@ -39,8 +41,8 @@ namespace lmms { -const LilvPlugin *Lv2SubPluginFeatures::getPlugin( - const Plugin::Descriptor::SubPluginFeatures::Key &k) +const LilvPlugin* Lv2SubPluginFeatures::getPlugin( + const Plugin::Descriptor::SubPluginFeatures::Key& k) { const LilvPlugin* result = Engine::getLv2Manager()-> getPlugin(k.attributes["uri"]); @@ -51,7 +53,7 @@ const LilvPlugin *Lv2SubPluginFeatures::getPlugin( -QString Lv2SubPluginFeatures::pluginName(const LilvPlugin *plug) +QString Lv2SubPluginFeatures::pluginName(const LilvPlugin* plug) { return qStringFromPluginNode(plug, lilv_plugin_get_name); } @@ -67,61 +69,132 @@ Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::Type type) : -void Lv2SubPluginFeatures::fillDescriptionWidget(QWidget *parent, - const Key *k) const +static void addHbox(QWidget* parent, QString left, QString right) { - const LilvPlugin *plug = getPlugin(*k); + if (right.isEmpty()) { return; } - auto label = new QLabel(parent); - label->setText(QWidget::tr("Name: ") + pluginName(plug)); - - auto label2 = new QLabel(parent); - label2->setText(QWidget::tr("URI: ") + - lilv_node_as_uri(lilv_plugin_get_uri(plug))); - - auto maker = new QWidget(parent); - auto l = new QHBoxLayout(maker); + auto container = new QWidget(parent); + auto l = new QHBoxLayout(container); l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); - auto maker_label = new QLabel(maker); - maker_label->setText(QWidget::tr("Maker: ")); - maker_label->setAlignment(Qt::AlignTop); + auto leftLabel = new QLabel(container); + leftLabel->setText(left); + leftLabel->setAlignment(Qt::AlignTop); - auto maker_content = new QLabel(maker); - maker_content->setText( - qStringFromPluginNode(plug, lilv_plugin_get_author_name)); - maker_content->setWordWrap(true); + auto rightLabel = new QLabel(container); + if (right.startsWith("http") && !right.contains(' ') && !right.contains('\n')) + { + right = QString("%1").arg(right); + rightLabel->setTextInteractionFlags(rightLabel->textInteractionFlags() + | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + rightLabel->setTextFormat(Qt::RichText); + rightLabel->setOpenExternalLinks(true); + } + rightLabel->setText(right); + rightLabel->setWordWrap(true); - l->addWidget(maker_label); - l->addWidget(maker_content, 1); + l->addWidget(leftLabel); + l->addWidget(rightLabel, 1); +} - auto copyright = new QWidget(parent); - l = new QHBoxLayout(copyright); - l->setContentsMargins(0, 0, 0, 0); - l->setSpacing(0); - copyright->setMinimumWidth(parent->minimumWidth()); - auto copyright_label = new QLabel(copyright); - copyright_label->setText(QWidget::tr("Copyright: ")); - copyright_label->setAlignment(Qt::AlignTop); - auto copyright_content = new QLabel(copyright); - copyright_content->setText(""); - copyright_content->setWordWrap(true); - l->addWidget(copyright_label); - l->addWidget(copyright_content, 1); - AutoLilvNodes extensions(lilv_plugin_get_extension_data(plug)); - (void)extensions; - // possibly TODO: version, project, plugin type, number of channels +static void addLabel(QWidget* parent, QString left, QString right) +{ + auto lbl = new QLabel(parent); + if (right.isEmpty()) { return; } + if (right.startsWith("http") && !right.contains(' ') && !right.contains('\n')) + { + right = QString("%1").arg(right); + lbl->setTextInteractionFlags(lbl->textInteractionFlags() + | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + lbl->setTextFormat(Qt::RichText); + lbl->setOpenExternalLinks(true); + } + lbl->setText(left + right); +} + + + + +AutoLilvNodes pluginGetValues(const LilvPlugin* plug, const char* valueUri) +{ + assert(plug); + AutoLilvNode valueUriNode{Engine::getLv2Manager()->uri(valueUri)}; + return AutoLilvNodes{lilv_plugin_get_value(plug, valueUriNode.get())}; +} + + + + +void Lv2SubPluginFeatures::fillDescriptionWidget(QWidget* parent, + const Key* k) const +{ + const LilvPlugin* plug = getPlugin(*k); + + QString pluginNameAndVersion = "" + pluginName(plug) + ""; + { + AutoLilvNodes minorVersions = pluginGetValues(plug, LILV_NS_LV2 "minorVersion"); + AutoLilvNodes microVersions = pluginGetValues(plug, LILV_NS_LV2 "microVersion"); + if (minorVersions && microVersions) + { + QString min = lilv_node_as_string(lilv_nodes_get_first(minorVersions.get())); + QString mic = lilv_node_as_string(lilv_nodes_get_first(microVersions.get())); + pluginNameAndVersion += QString(" v%1.%2").arg(min).arg(mic); + } + } + + (new QLabel(parent))->setText(pluginNameAndVersion); + { + AutoLilvNodes comments = pluginGetValues(plug, LILV_NS_RDFS "comment"); + if (comments) + { + QString description{lilv_node_as_string(lilv_nodes_get_first(comments.get()))}; + auto descLabel = new QLabel(parent); + descLabel->setText("" + description.trimmed() + ""); + descLabel->setWordWrap(true); + } + } + + addLabel(parent, QObject::tr("URI: "), lilv_node_as_uri(lilv_plugin_get_uri(plug))); + addHbox(parent, QObject::tr("Project: "), + qStringFromPluginNode(plug, lilv_plugin_get_project)); + addHbox(parent, QObject::tr("Maker: "), + qStringFromPluginNode(plug, lilv_plugin_get_author_name)); + { + AutoLilvNodes homepages = pluginGetValues(plug, LILV_NS_DOAP "homepage"); + if (homepages) + { + const char* homepage = lilv_node_as_uri(lilv_nodes_get_first(homepages.get())); + QString homepageStr{homepage}; + addLabel(parent, QObject::tr("Homepage: "), homepageStr); + } + } + { + AutoLilvNodes licenses = pluginGetValues(plug, LILV_NS_DOAP "license"); + addLabel(parent, QObject::tr("License: "), + licenses + ? lilv_node_as_uri(lilv_nodes_get_first(licenses.get())) + : ""); + } + { + const LilvNode* libraryUriNode = lilv_plugin_get_bundle_uri(plug); + const char* libraryUri = lilv_node_as_uri(libraryUriNode); + auto filename = AutoLilvPtr(lilv_file_uri_parse(libraryUri, nullptr)); + if (filename) + { + new QLabel(QObject::tr("File: %1").arg(filename.get()), parent); + } + } } QString Lv2SubPluginFeatures::additionalFileExtensions( - const Plugin::Descriptor::SubPluginFeatures::Key &k) const + const Plugin::Descriptor::SubPluginFeatures::Key& k) const { (void)k; // lv2 only loads .lv2 files @@ -133,7 +206,7 @@ QString Lv2SubPluginFeatures::additionalFileExtensions( QString Lv2SubPluginFeatures::displayName( - const Plugin::Descriptor::SubPluginFeatures::Key &k) const + const Plugin::Descriptor::SubPluginFeatures::Key& k) const { return pluginName(getPlugin(k)); } @@ -142,30 +215,44 @@ QString Lv2SubPluginFeatures::displayName( QString Lv2SubPluginFeatures::description( - const Plugin::Descriptor::SubPluginFeatures::Key &k) const + const Plugin::Descriptor::SubPluginFeatures::Key& k) const { - (void)k; - return QString::fromUtf8("description not implemented yet"); // TODO + auto mgr = Engine::getLv2Manager(); + const LilvPlugin* plug = mgr->getPlugin(k.attributes["uri"]); + if (plug) + { + QString result; + AutoLilvNode rdfs_comment{mgr->uri(LILV_NS_RDFS "comment")}; + AutoLilvNodes comments{lilv_plugin_get_value(plug, rdfs_comment.get())}; + if (comments) + { + result += lilv_node_as_string(lilv_nodes_get_first(comments.get())); + result += "\n\n"; + } + result += lilv_node_as_uri(lilv_plugin_get_uri(plug)); + return result.trimmed(); + } + return QObject::tr("failed to load description"); } -const PixmapLoader *Lv2SubPluginFeatures::logo( - const Plugin::Descriptor::SubPluginFeatures::Key &k) const +const PixmapLoader* Lv2SubPluginFeatures::logo( + const Plugin::Descriptor::SubPluginFeatures::Key& k) const { - (void)k; // TODO - return nullptr; + (void)k; + return nullptr; // Lv2 currently does not support this } -void Lv2SubPluginFeatures::listSubPluginKeys(const Plugin::Descriptor *desc, - KeyList &kl) const +void Lv2SubPluginFeatures::listSubPluginKeys(const Plugin::Descriptor* desc, + KeyList& kl) const { - Lv2Manager *lv2Mgr = Engine::getLv2Manager(); - for (const auto &uriInfoPair : *lv2Mgr) + Lv2Manager* lv2Mgr = Engine::getLv2Manager(); + for (const auto& uriInfoPair : *lv2Mgr) { if (uriInfoPair.second.type() == m_type && uriInfoPair.second.isValid()) { From ce17c956368789d6add77c57bba5e0a8bb63942a Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Sun, 4 Aug 2024 04:30:42 -0400 Subject: [PATCH 12/20] Add Continuous Auto-Scrolling (#7396) * Initial Commit * Refactor code and add new icons * Fix logical error * Add smooth scrolling to Song Editor * Remove unused variable * Fix scrolling speed and scrollbar width * Remove QDebug and re-add a newline I deleted in an unrelated file * Forgot to add files to commit * Remove unused variable * Fix Styling * Fix Styling Again * Fix Styling Again * Fix Styling Again * Add icons for classic theme * Accidentally committed varying scroll speed with zoom -- removing * Change abs to std::abs Co-authored-by: saker * Change qMax to std::max and use static_cast Co-authored-by: saker * Simplify stepped auto scrolling Co-authored-by: saker * Remove unnecessary parentheses * Remove return statement causing the play head line to stop * Add specific tooltips to auto scrolling button states * Remove `== true` from SongEditor.cpp Co-authored-by: saker * Make tooltips translatable Co-authored-by: Dominic Clark * Fix zooming position calculation * Rename bars to ticks * Fix rubberband rect size --------- Co-authored-by: saker Co-authored-by: Dominic Clark --- .../classic/autoscroll_continuous_on.png | Bin 0 -> 9733 bytes data/themes/classic/autoscroll_stepped_on.png | Bin 0 -> 11905 bytes .../default/autoscroll_continuous_on.png | Bin 0 -> 7966 bytes data/themes/default/autoscroll_stepped_on.png | Bin 0 -> 8791 bytes include/TimeLineWidget.h | 5 +- src/gui/editors/PianoRoll.cpp | 32 ++++++---- src/gui/editors/SongEditor.cpp | 57 +++++++++--------- src/gui/editors/TimeLineWidget.cpp | 7 ++- src/gui/tracks/TrackContentWidget.cpp | 5 +- 9 files changed, 59 insertions(+), 47 deletions(-) create mode 100644 data/themes/classic/autoscroll_continuous_on.png create mode 100644 data/themes/classic/autoscroll_stepped_on.png create mode 100644 data/themes/default/autoscroll_continuous_on.png create mode 100644 data/themes/default/autoscroll_stepped_on.png diff --git a/data/themes/classic/autoscroll_continuous_on.png b/data/themes/classic/autoscroll_continuous_on.png new file mode 100644 index 0000000000000000000000000000000000000000..0140d5cc51733dd1475c2b9d3cb7b8f4b32ba4db GIT binary patch literal 9733 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 U&Y);L+HjjuHgC)}+eZ1d;&3q7iU_s6|wq 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 L1phd7e)iu1QwSX@ literal 0 HcmV?d00001 diff --git a/data/themes/default/autoscroll_stepped_on.png b/data/themes/default/autoscroll_stepped_on.png new file mode 100644 index 0000000000000000000000000000000000000000..c58fa97b2cfe6eb6a0b582b80b7c1d04f45f80c8 GIT binary patch literal 8791 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 H|4jd1D+9KA literal 0 HcmV?d00001 diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 5c683cfd9f9..0eb33bad935 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -73,7 +73,8 @@ class TimeLineWidget : public QWidget enum class AutoScrollState { - Enabled, + Stepped, + Continuous, Disabled }; @@ -212,7 +213,7 @@ public slots: QCursor m_cursorSelectLeft = QCursor{embed::getIconPixmap("cursor_select_left"), 0, 16}; QCursor m_cursorSelectRight = QCursor{embed::getIconPixmap("cursor_select_right"), 32, 16}; - AutoScrollState m_autoScroll = AutoScrollState::Enabled; + AutoScrollState m_autoScroll = AutoScrollState::Stepped; // Width of the unused region on the widget's left (above track labels or piano) int m_xOffset; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index bda6894f3ee..1c89f6a0b34 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -4062,7 +4062,7 @@ void PianoRoll::stop() { Engine::getSong()->stop(); m_recording = false; - m_scrollBack = ( m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ); + m_scrollBack = m_timeLine->autoScroll() != TimeLineWidget::AutoScrollState::Disabled; } @@ -4463,30 +4463,36 @@ bool PianoRoll::deleteSelectedNotes() void PianoRoll::autoScroll( const TimePos & t ) { const int w = width() - m_whiteKeyWidth; - if( t > m_currentPosition + w * TimePos::ticksPerBar() / m_ppb ) + if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Stepped) { - m_leftRightScroll->setValue( t.getBar() * TimePos::ticksPerBar() ); + if (t > m_currentPosition + w * TimePos::ticksPerBar() / m_ppb) + { + m_leftRightScroll->setValue(t.getBar() * TimePos::ticksPerBar()); + } + else if (t < m_currentPosition) + { + TimePos t2 = std::max(t - w * TimePos::ticksPerBar() * + TimePos::ticksPerBar() / m_ppb, static_cast(0)); + m_leftRightScroll->setValue(t2.getBar() * TimePos::ticksPerBar()); + } } - else if( t < m_currentPosition ) + else if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Continuous) { - TimePos t2 = qMax( t - w * TimePos::ticksPerBar() * - TimePos::ticksPerBar() / m_ppb, (tick_t) 0 ); - m_leftRightScroll->setValue( t2.getBar() * TimePos::ticksPerBar() ); + m_leftRightScroll->setValue(std::max(t.getTicks() - w * TimePos::ticksPerBar() / m_ppb / 2, 0)); } m_scrollBack = false; } - -void PianoRoll::updatePosition( const TimePos & t ) +void PianoRoll::updatePosition(const TimePos & t) { - if( ( Engine::getSong()->isPlaying() + if ((Engine::getSong()->isPlaying() && Engine::getSong()->playMode() == Song::PlayMode::MidiClip - && m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled - ) || m_scrollBack ) + && m_timeLine->autoScroll() != TimeLineWidget::AutoScrollState::Disabled + ) || m_scrollBack) { - autoScroll( t ); + autoScroll(t); } // ticks relative to m_currentPosition // < 0 = outside viewport left diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 9ba9ac083d4..f043e715acc 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -231,10 +231,10 @@ SongEditor::SongEditor( Song * song ) : static_cast( layout() )->insertWidget( 0, m_timeLine ); m_leftRightScroll = new QScrollBar( Qt::Horizontal, this ); - m_leftRightScroll->setMinimum( 0 ); - m_leftRightScroll->setMaximum( 0 ); - m_leftRightScroll->setSingleStep( 1 ); - m_leftRightScroll->setPageStep( 20 ); + m_leftRightScroll->setMinimum(0); + m_leftRightScroll->setMaximum(0); + m_leftRightScroll->setSingleStep(1); + m_leftRightScroll->setPageStep(20 * TimePos::ticksPerBar()); static_cast( layout() )->addWidget( m_leftRightScroll ); connect( m_leftRightScroll, SIGNAL(valueChanged(int)), this, SLOT(scrolled(int))); @@ -325,7 +325,7 @@ QString SongEditor::getSnapSizeString() const void SongEditor::scrolled( int new_pos ) { update(); - emit positionChanged( m_currentPosition = TimePos( new_pos, 0 ) ); + emit positionChanged(m_currentPosition = TimePos(new_pos)); } @@ -384,7 +384,7 @@ void SongEditor::updateRubberband() } //take care of the scrollbar position - int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerBar(); + int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerBar() / TimePos::ticksPerBar(); int vs = contentWidget()->verticalScrollBar()->value() - m_scrollPos.y(); //the adjusted origin point @@ -522,8 +522,8 @@ void SongEditor::wheelEvent( QWheelEvent * we ) if ((we->modifiers() & Qt::ControlModifier) && (position(we).x() > m_trackHeadWidth)) { int x = position(we).x() - m_trackHeadWidth; - // bar based on the mouse x-position where the scroll wheel was used - int bar = x / pixelsPerBar(); + // tick based on the mouse x-position where the scroll wheel was used + int tick = x / pixelsPerBar() * TimePos::ticksPerBar(); // move zoom slider (pixelsPerBar will change automatically) int step = we->modifiers() & Qt::ShiftModifier ? 1 : 5; @@ -531,9 +531,9 @@ void SongEditor::wheelEvent( QWheelEvent * we ) 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); + // scroll to zooming around cursor's tick + int newTick = static_cast(x / pixelsPerBar() * TimePos::ticksPerBar()); + m_leftRightScroll->setValue(m_leftRightScroll->value() + tick - newTick); // update timeline m_timeLine->setPixelsPerBar(pixelsPerBar()); @@ -542,15 +542,15 @@ void SongEditor::wheelEvent( QWheelEvent * we ) } // FIXME: Reconsider if determining orientation is necessary in Qt6. - else if(abs(we->angleDelta().x()) > abs(we->angleDelta().y())) // scrolling is horizontal + else if (std::abs(we->angleDelta().x()) > std::abs(we->angleDelta().y())) // scrolling is horizontal { - m_leftRightScroll->setValue(m_leftRightScroll->value() - - we->angleDelta().x() /30); + m_leftRightScroll->setValue(m_leftRightScroll->value() + - we->angleDelta().x()); } - else if(we->modifiers() & Qt::ShiftModifier) + else if (we->modifiers() & Qt::ShiftModifier) { - m_leftRightScroll->setValue(m_leftRightScroll->value() - - we->angleDelta().y() / 30); + m_leftRightScroll->setValue(m_leftRightScroll->value() + - we->angleDelta().y()); } else { @@ -711,9 +711,9 @@ void SongEditor::hideMasterPitchFloat( void ) -void SongEditor::updateScrollBar( int len ) +void SongEditor::updateScrollBar(int len) { - m_leftRightScroll->setMaximum( len ); + m_leftRightScroll->setMaximum(len * TimePos::ticksPerBar()); } @@ -756,22 +756,25 @@ void SongEditor::updatePosition( const TimePos & t ) const auto widgetWidth = compactTrackButtons ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT : DEFAULT_SETTINGS_WIDGET_WIDTH; const auto trackOpWidth = compactTrackButtons ? TRACK_OP_WIDTH_COMPACT : TRACK_OP_WIDTH; - if( ( m_song->isPlaying() && m_song->m_playMode == Song::PlayMode::Song - && m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled) || - m_scrollBack == true ) + if ((m_song->isPlaying() && m_song->m_playMode == Song::PlayMode::Song) + || m_scrollBack) { m_smoothScroll = ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt(); const int w = width() - widgetWidth - trackOpWidth - contentWidget()->verticalScrollBar()->width(); // width of right scrollbar - if( t > m_currentPosition + w * TimePos::ticksPerBar() / - pixelsPerBar() ) + + if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Stepped) { - animateScroll( m_leftRightScroll, t.getBar(), m_smoothScroll ); + const auto nextPosition = m_currentPosition + w * TimePos::ticksPerBar() / pixelsPerBar(); + if (t > nextPosition || t < m_currentPosition) + { + animateScroll(m_leftRightScroll, t.getTicks(), m_smoothScroll); + } } - else if( t < m_currentPosition ) + else if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Continuous) { - animateScroll( m_leftRightScroll, t.getBar(), m_smoothScroll ); + animateScroll(m_leftRightScroll, std::max(t.getTicks() - w * TimePos::ticksPerBar() / pixelsPerBar() / 2, 0.0f), m_smoothScroll); } m_scrollBack = false; } diff --git a/src/gui/editors/TimeLineWidget.cpp b/src/gui/editors/TimeLineWidget.cpp index 7657e2916cd..f75b1cabfac 100644 --- a/src/gui/editors/TimeLineWidget.cpp +++ b/src/gui/editors/TimeLineWidget.cpp @@ -88,9 +88,10 @@ void TimeLineWidget::setXOffset(const int x) void TimeLineWidget::addToolButtons( QToolBar * _tool_bar ) { auto autoScroll = new NStateButton(_tool_bar); - autoScroll->setGeneralToolTip( tr( "Auto scrolling" ) ); - autoScroll->addState( embed::getIconPixmap( "autoscroll_on" ) ); - autoScroll->addState( embed::getIconPixmap( "autoscroll_off" ) ); + autoScroll->setGeneralToolTip(tr("Auto scrolling")); + autoScroll->addState(embed::getIconPixmap("autoscroll_stepped_on"), tr("Stepped auto scrolling")); + autoScroll->addState(embed::getIconPixmap("autoscroll_continuous_on"), tr("Continuous auto scrolling")); + autoScroll->addState(embed::getIconPixmap("autoscroll_off"), tr("Auto scrolling disabled")); connect( autoScroll, SIGNAL(changedState(int)), this, SLOT(toggleAutoScroll(int))); diff --git a/src/gui/tracks/TrackContentWidget.cpp b/src/gui/tracks/TrackContentWidget.cpp index afea82ea41c..1926ffeef92 100644 --- a/src/gui/tracks/TrackContentWidget.cpp +++ b/src/gui/tracks/TrackContentWidget.cpp @@ -292,6 +292,7 @@ void TrackContentWidget::changePosition( const TimePos & newPos ) setUpdatesEnabled( true ); // redraw background + updateBackground(); // update(); } @@ -628,8 +629,8 @@ void TrackContentWidget::paintEvent( QPaintEvent * pe ) // Don't draw background on Pattern Editor if (m_trackView->trackContainerView() != getGUI()->patternEditor()->m_editor) { - p.drawTiledPixmap( rect(), m_background, QPoint( - tcv->currentPosition().getBar() * ppb, 0 ) ); + p.drawTiledPixmap(rect(), m_background, QPoint( + tcv->currentPosition().getTicks() * ppb / TimePos::ticksPerBar(), 0)); } } From b7548b7b7acb02f3eff84bdef5dfa03b6c61bfe5 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 4 Aug 2024 15:30:42 +0200 Subject: [PATCH 13/20] Warn about LADSPA problems (#7267) Introduce a method which checks if a file that's loaded has the LADSPA controls saved in an old format that was written with a version before commit e99efd541a9. The method is run at the end so that problems in all file versions are detected. If a real upgrade was to be implemented it would have to run between `DataFile::upgrade_0_4_0_rc2` and `DataFile::upgrade_1_0_99`. See #5738 for more details. If a problematic file is encountered a warning dialog that provides the number of affected LADSPA plugins is shown. --- include/DataFile.h | 1 + src/core/DataFile.cpp | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/include/DataFile.h b/include/DataFile.h index 5005c11bc3c..7f5f5b88824 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -133,6 +133,7 @@ class LMMS_EXPORT DataFile : public QDomDocument void upgrade_noteTypes(); void upgrade_fixCMTDelays(); void upgrade_fixBassLoopsTypo(); + void findProblematicLadspaPlugins(); // List of all upgrade methods static const std::vector UPGRADE_METHODS; diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index b664387bd42..3e7d6d8b691 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -85,6 +85,7 @@ const std::vector DataFile::UPGRADE_METHODS = { &DataFile::upgrade_sampleAndHold , &DataFile::upgrade_midiCCIndexing, &DataFile::upgrade_loopsRename , &DataFile::upgrade_noteTypes, &DataFile::upgrade_fixCMTDelays , &DataFile::upgrade_fixBassLoopsTypo, + &DataFile::findProblematicLadspaPlugins }; // Vector of all versions that have upgrade routines. @@ -1072,7 +1073,6 @@ void DataFile::upgrade_0_4_0_rc2() } } - void DataFile::upgrade_1_0_99() { jo_id_t last_assigned_id = 0; @@ -1979,6 +1979,39 @@ void DataFile::upgrade_midiCCIndexing() } } +void DataFile::findProblematicLadspaPlugins() +{ + // This is not an upgrade but a check for potentially problematic LADSPA + // controls. See #5738 for more details. + + const QDomNodeList ladspacontrols = elementsByTagName("ladspacontrols"); + + uint numberOfProblematicPlugins = 0; + + for (int i = 0; i < ladspacontrols.size(); ++i) + { + const QDomElement ladspacontrol = ladspacontrols.item(i).toElement(); + + const auto attributes = ladspacontrol.attributes(); + for (int j = 0; j < attributes.length(); ++j) + { + const auto attribute = attributes.item(j); + const auto name = attribute.nodeName(); + if (name != "ports" && name.startsWith("port")) + { + ++numberOfProblematicPlugins; + break; + } + } + } + + if (numberOfProblematicPlugins > 0) + { + QMessageBox::warning(nullptr, QObject::tr("LADSPA plugins"), + QObject::tr("The project contains %1 LADSPA plugin(s) which might have not been restored correctly! Please check the project.").arg(numberOfProblematicPlugins)); + } +} + void DataFile::upgrade_fixBassLoopsTypo() { static const QMap replacementMap = { From 1f224ad765fa71e3e538a0f7225d7c58d0d41f88 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sun, 4 Aug 2024 15:47:43 +0200 Subject: [PATCH 14/20] Add yamllint checks (#7424) --- .github/FUNDING.yml | 1 + .github/ISSUE_TEMPLATE/bug_report.yml | 3 ++- .github/ISSUE_TEMPLATE/config.yml | 7 ++++--- .github/ISSUE_TEMPLATE/feature_request.yml | 3 ++- .github/no-response.yml | 1 + .github/workflows/build.yml | 2 ++ .github/workflows/checks.yml | 7 +++++++ .yamllint | 3 +++ 8 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 .yamllint diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 8a8b03635cb..702d581fe54 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ +--- custom: https://lmms.io/get-involved/#donate diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 4cd1464d67b..fe3d60ff219 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -78,7 +78,8 @@ body: label: Screenshots / Minimum Reproducible Project description: | - Upload any screenshots showing the bug in action. - - If possible, also include a .mmp/.mmpz project containing the simplest possible setup needed to reproduce the bug. + - If possible, also include a .mmp/.mmpz project containing the simplest possible + setup needed to reproduce the bug. ***Note:** To upload a project file to GitHub, it will need to be placed in a .zip archive.* - type: checkboxes diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2c51f276e3d..735942ffba8 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,5 @@ +--- contact_links: -- name: Get help on Discord - url: https://lmms.io/chat/ - about: Need help? Have a question? Reach out to other LMMS users on our Discord server! + - name: Get help on Discord + url: https://lmms.io/chat/ + about: Need help? Have a question? Reach out to other LMMS users on our Discord server! diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 1f11b4eb37c..cc233a6367a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -10,7 +10,8 @@ body: label: Enhancement Summary description: | - Briefly describe the enhancement. - - Explain why you believe the proposed enhancement to be a good idea, and (if applicable) how it helps overcome a limitation of LMMS you are currently facing. + - Explain why you believe the proposed enhancement to be a good idea, and (if applicable) how it helps + overcome a limitation of LMMS you are currently facing. validations: required: true - type: textarea diff --git a/.github/no-response.yml b/.github/no-response.yml index 4761654082e..f6c32953977 100644 --- a/.github/no-response.yml +++ b/.github/no-response.yml @@ -1,2 +1,3 @@ +--- # Label requiring a response responseRequiredLabel: "response required" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 14c617b6566..2c2e8dd73b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -260,12 +260,14 @@ jobs: - name: Cache ccache data uses: actions/cache@v3 with: + # yamllint disable rule:line-length key: "ccache-${{ github.job }}-${{ matrix.arch }}-${{ github.ref }}\ -${{ github.run_id }}" restore-keys: | ccache-${{ github.job }}-${{ matrix.arch }}-${{ github.ref }}- ccache-${{ github.job }}-${{ matrix.arch }}- path: ~\AppData\Local\ccache + # yamllint enable rule:line-length - name: Install tools run: choco install ccache - name: Install Qt diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 3f770067466..228f383c127 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -31,3 +31,10 @@ jobs: $(find "./cmake/" -type f -name '*.sh' -o -name "*.sh.in") \ doc/bash-completion/lmms \ buildtools/update_locales + yamllint: + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v3 + - name: Run yamllint + run: for i in $(git ls-files '*.yml'); do yamllint $i; done diff --git a/.yamllint b/.yamllint new file mode 100644 index 00000000000..a7aa0ae1ed5 --- /dev/null +++ b/.yamllint @@ -0,0 +1,3 @@ +rules: + line-length: + max: 120 # be conforming to LMMS coding rules From 735e483d9fc1d5072b8ec9ccf122f97339e7d6c2 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:21:34 +0200 Subject: [PATCH 15/20] Fix memleaks in help/version (#7423) * Fix memleaks in help/version These memory leaks caused help and version to crash at the end, due to rpmalloc's memleak detection. --- src/core/main.cpp | 106 +++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/src/core/main.cpp b/src/core/main.cpp index b970997eff5..395f58c3d1c 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -257,6 +257,57 @@ int main( int argc, char * * argv ) { using namespace lmms; + bool coreOnly = false; + bool fullscreen = true; + bool exitAfterImport = false; + bool allowRoot = false; + bool renderLoop = false; + bool renderTracks = false; + QString fileToLoad, fileToImport, renderOut, profilerOutputFile, configFile; + + // first of two command-line parsing stages + for (int i = 1; i < argc; ++i) + { + QString arg = argv[i]; + + if (arg == "--help" || arg == "-h") + { + printHelp(); + return EXIT_SUCCESS; + } + else if (arg == "--version" || arg == "-v") + { + printVersion(argv[0]); + return EXIT_SUCCESS; + } + else if (arg == "render" || arg == "--render" || arg == "-r" ) + { + coreOnly = true; + } + else if (arg == "rendertracks" || arg == "--rendertracks") + { + coreOnly = true; + renderTracks = true; + } + else if (arg == "--allowroot") + { + allowRoot = true; + } + else if (arg == "--geometry" || arg == "-geometry") + { + if (arg == "--geometry") + { + // Delete the first "-" so Qt recognize the option + strcpy(argv[i], "-geometry"); + } + // option -geometry is filtered by Qt later, + // so we need to check its presence now to + // determine, if the application should run in + // fullscreen mode (default, no -geometry given). + fullscreen = false; + } + } + #ifdef LMMS_DEBUG_FPE // Enable exceptions for certain floating point results // FE_UNDERFLOW is disabled for the time being @@ -314,49 +365,6 @@ int main( int argc, char * * argv ) disable_denormals(); - bool coreOnly = false; - bool fullscreen = true; - bool exitAfterImport = false; - bool allowRoot = false; - bool renderLoop = false; - bool renderTracks = false; - QString fileToLoad, fileToImport, renderOut, profilerOutputFile, configFile; - - // first of two command-line parsing stages - for( int i = 1; i < argc; ++i ) - { - QString arg = argv[i]; - - if( arg == "--help" || arg == "-h" || - arg == "--version" || arg == "-v" || - arg == "render" || arg == "--render" || arg == "-r" ) - { - coreOnly = true; - } - else if( arg == "rendertracks" || arg == "--rendertracks" ) - { - coreOnly = true; - renderTracks = true; - } - else if( arg == "--allowroot" ) - { - allowRoot = true; - } - else if( arg == "--geometry" || arg == "-geometry") - { - if( arg == "--geometry" ) - { - // Delete the first "-" so Qt recognize the option - strcpy(argv[i], "-geometry"); - } - // option -geometry is filtered by Qt later, - // so we need to check its presence now to - // determine, if the application should run in - // fullscreen mode (default, no -geometry given). - fullscreen = false; - } - } - #if !defined(LMMS_BUILD_WIN32) && !defined(LMMS_BUILD_HAIKU) if ( ( getuid() == 0 || geteuid() == 0 ) && !allowRoot ) { @@ -382,17 +390,7 @@ int main( int argc, char * * argv ) { QString arg = argv[i]; - if( arg == "--version" || arg == "-v" ) - { - printVersion( argv[0] ); - return EXIT_SUCCESS; - } - else if( arg == "--help" || arg == "-h" ) - { - printHelp(); - return EXIT_SUCCESS; - } - else if( arg == "upgrade" || arg == "--upgrade" || arg == "-u") + if (arg == "upgrade" || arg == "--upgrade" || arg == "-u") { ++i; From 5b366cfe3cc4f22b06f7385b63d00117f7c9529d Mon Sep 17 00:00:00 2001 From: saker Date: Sun, 4 Aug 2024 11:01:26 -0400 Subject: [PATCH 16/20] Revert "Switch to libsamplerate's callback API in `Sample` (#7361)" (#7410) This reverts commit 2f5f12aaae8453863f62305cb54e4cfe779b661c. --- include/AudioResampler.h | 65 +++++++++ include/Sample.h | 52 +++++--- include/SampleBuffer.h | 1 + .../AudioFileProcessor/AudioFileProcessor.cpp | 12 +- plugins/GigPlayer/GigPlayer.cpp | 2 +- plugins/GigPlayer/GigPlayer.h | 6 - src/core/AudioResampler.cpp | 69 ++++++++++ src/core/CMakeLists.txt | 1 + src/core/Sample.cpp | 126 +++++++++++++----- 9 files changed, 265 insertions(+), 69 deletions(-) create mode 100644 include/AudioResampler.h create mode 100644 src/core/AudioResampler.cpp diff --git a/include/AudioResampler.h b/include/AudioResampler.h new file mode 100644 index 00000000000..6dd6fcc6039 --- /dev/null +++ b/include/AudioResampler.h @@ -0,0 +1,65 @@ +/* + * AudioResampler.h - wrapper around libsamplerate + * + * Copyright (c) 2023 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_AUDIO_RESAMPLER_H +#define LMMS_AUDIO_RESAMPLER_H + +#include + +#include "lmms_export.h" + +namespace lmms { + +class LMMS_EXPORT AudioResampler +{ +public: + struct ProcessResult + { + int error; + long inputFramesUsed; + long outputFramesGenerated; + }; + + AudioResampler(int interpolationMode, int channels); + AudioResampler(const AudioResampler&) = delete; + AudioResampler(AudioResampler&&) = delete; + ~AudioResampler(); + + AudioResampler& operator=(const AudioResampler&) = delete; + AudioResampler& operator=(AudioResampler&&) = delete; + + auto resample(const float* in, long inputFrames, float* out, long outputFrames, double ratio) -> ProcessResult; + auto interpolationMode() const -> int { return m_interpolationMode; } + auto channels() const -> int { return m_channels; } + void setRatio(double ratio); + +private: + int m_interpolationMode = -1; + int m_channels = 0; + int m_error = 0; + SRC_STATE* m_state = nullptr; +}; +} // namespace lmms + +#endif // LMMS_AUDIO_RESAMPLER_H diff --git a/include/Sample.h b/include/Sample.h index 75435036805..3fd5bc38ef1 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -25,18 +25,24 @@ #ifndef LMMS_SAMPLE_H #define LMMS_SAMPLE_H +#include #include -#include +#include "AudioResampler.h" #include "Note.h" #include "SampleBuffer.h" -#include "lmms_basics.h" #include "lmms_export.h" namespace lmms { class LMMS_EXPORT Sample { public: + // values for buffer margins, used for various libsamplerate interpolation modes + // the array positions correspond to the converter_type parameter values in libsamplerate + // if there appears problems with playback on some interpolation mode, then the value for that mode + // may need to be higher - conversely, to optimize, some may work with lower values + static constexpr auto s_interpolationMargins = std::array{64, 64, 64, 4, 4}; + enum class Loop { Off, @@ -44,25 +50,30 @@ class LMMS_EXPORT Sample PingPong }; - struct LMMS_EXPORT PlaybackState + class LMMS_EXPORT PlaybackState { - PlaybackState(int interpolationMode = SRC_LINEAR) - : resampleState(src_callback_new(&Sample::render, interpolationMode, DEFAULT_CHANNELS, &error, this)) + public: + PlaybackState(bool varyingPitch = false, int interpolationMode = SRC_LINEAR) + : m_resampler(interpolationMode, DEFAULT_CHANNELS) + , m_varyingPitch(varyingPitch) { - assert(resampleState && src_strerror(error)); } - ~PlaybackState() - { - src_delete(resampleState); - } + auto resampler() -> AudioResampler& { return m_resampler; } + auto frameIndex() const -> int { return m_frameIndex; } + auto varyingPitch() const -> bool { return m_varyingPitch; } + auto backwards() const -> bool { return m_backwards; } - const Sample* sample = nullptr; - Loop* loop = nullptr; - SRC_STATE* resampleState = nullptr; - int frameIndex = 0; - int error = 0; - bool backwards = false; + void setFrameIndex(int frameIndex) { m_frameIndex = frameIndex; } + void setVaryingPitch(bool varyingPitch) { m_varyingPitch = varyingPitch; } + void setBackwards(bool backwards) { m_backwards = backwards; } + + private: + AudioResampler m_resampler; + int m_frameIndex = 0; + bool m_varyingPitch = false; + bool m_backwards = false; + friend class Sample; }; Sample() = default; @@ -76,7 +87,7 @@ class LMMS_EXPORT Sample auto operator=(const Sample&) -> Sample&; auto operator=(Sample&&) -> Sample&; - auto play(SampleFrame* dst, PlaybackState* state, size_t numFrames, double frequency = DefaultBaseFreq, + auto play(SampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency = DefaultBaseFreq, Loop loopMode = Loop::Off) const -> bool; auto sampleDuration() const -> std::chrono::milliseconds; @@ -106,14 +117,17 @@ class LMMS_EXPORT Sample void setReversed(bool reversed) { m_reversed.store(reversed, std::memory_order_relaxed); } private: - static auto render(void* callbackData, float** data) -> long; + void playRaw(SampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const; + void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const; + +private: std::shared_ptr m_buffer = SampleBuffer::emptyBuffer(); std::atomic m_startFrame = 0; std::atomic m_endFrame = 0; std::atomic m_loopStartFrame = 0; std::atomic m_loopEndFrame = 0; std::atomic m_amplification = 1.0f; - std::atomic m_frequency = DefaultBaseFreq; + std::atomic m_frequency = DefaultBaseFreq; std::atomic m_reversed = false; }; } // namespace lmms diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 114634577ee..8ec6c58863c 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index 2e63b5178b3..4cc14ba9cdb 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -144,9 +144,9 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, srcmode = SRC_SINC_MEDIUM_QUALITY; break; } - _n->m_pluginData = new Sample::PlaybackState(srcmode); - static_cast(_n->m_pluginData)->frameIndex = m_nextPlayStartPoint; - static_cast(_n->m_pluginData)->backwards = m_nextPlayBackwards; + _n->m_pluginData = new Sample::PlaybackState(_n->hasDetuningInfo(), srcmode); + static_cast(_n->m_pluginData)->setFrameIndex(m_nextPlayStartPoint); + static_cast(_n->m_pluginData)->setBackwards(m_nextPlayBackwards); // debug code /* qDebug( "frames %d", m_sample->frames() ); @@ -162,7 +162,7 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, static_cast(m_loopModel.value()))) { applyRelease( _working_buffer, _n ); - emit isPlaying(static_cast(_n->m_pluginData)->frameIndex); + emit isPlaying(static_cast(_n->m_pluginData)->frameIndex()); } else { @@ -176,8 +176,8 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, } if( m_stutterModel.value() == true ) { - m_nextPlayStartPoint = static_cast(_n->m_pluginData)->frameIndex; - m_nextPlayBackwards = static_cast(_n->m_pluginData)->backwards; + m_nextPlayStartPoint = static_cast(_n->m_pluginData)->frameIndex(); + m_nextPlayBackwards = static_cast(_n->m_pluginData)->backwards(); } } diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 061df7bd506..b72e30b3335 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -437,7 +437,7 @@ void GigInstrument::play( SampleFrame* _working_buffer ) if (sample.region->PitchTrack == true) { freq_factor *= sample.freqFactor; } // We need a bit of margin so we don't get glitching - samples = frames / freq_factor + s_interpolationMargins[m_interpolation]; + samples = frames / freq_factor + Sample::s_interpolationMargins[m_interpolation]; } // Load this note's data diff --git a/plugins/GigPlayer/GigPlayer.h b/plugins/GigPlayer/GigPlayer.h index 117178e54a1..685c7f5469a 100644 --- a/plugins/GigPlayer/GigPlayer.h +++ b/plugins/GigPlayer/GigPlayer.h @@ -240,12 +240,6 @@ class GigInstrument : public Instrument mapPropertyFromModel( int, getPatch, setPatch, m_patchNum ); public: - // values for buffer margins, used for various libsamplerate interpolation modes - // the array positions correspond to the converter_type parameter values in libsamplerate - // if there appears problems with playback on some interpolation mode, then the value for that mode - // may need to be higher - conversely, to optimize, some may work with lower values - static constexpr auto s_interpolationMargins = std::array{64, 64, 64, 4, 4}; - GigInstrument( InstrumentTrack * _instrument_track ); ~GigInstrument() override; diff --git a/src/core/AudioResampler.cpp b/src/core/AudioResampler.cpp new file mode 100644 index 00000000000..8fb7d95a2aa --- /dev/null +++ b/src/core/AudioResampler.cpp @@ -0,0 +1,69 @@ +/* + * AudioResampler.cpp - wrapper for libsamplerate + * + * Copyright (c) 2023 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 "AudioResampler.h" + +#include +#include +#include + +namespace lmms { + +AudioResampler::AudioResampler(int interpolationMode, int channels) + : m_interpolationMode(interpolationMode) + , m_channels(channels) + , m_state(src_new(interpolationMode, channels, &m_error)) +{ + if (!m_state) + { + const auto errorMessage = std::string{src_strerror(m_error)}; + const auto fullMessage = std::string{"Failed to create an AudioResampler: "} + errorMessage; + throw std::runtime_error{fullMessage}; + } +} + +AudioResampler::~AudioResampler() +{ + src_delete(m_state); +} + +auto AudioResampler::resample(const float* in, long inputFrames, float* out, long outputFrames, double ratio) + -> ProcessResult +{ + auto data = SRC_DATA{}; + data.data_in = in; + data.input_frames = inputFrames; + data.data_out = out; + data.output_frames = outputFrames; + data.src_ratio = ratio; + data.end_of_input = 0; + return {src_process(m_state, &data), data.input_frames_used, data.output_frames_gen}; +} + +void AudioResampler::setRatio(double ratio) +{ + src_set_ratio(m_state, ratio); +} + +} // namespace lmms diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 9eeb33904ba..3608d28486a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -4,6 +4,7 @@ set(LMMS_SRCS core/AudioEngine.cpp core/AudioEngineProfiler.cpp core/AudioEngineWorkerThread.cpp + core/AudioResampler.cpp core/AutomatableModel.cpp core/AutomationClip.cpp core/AutomationNode.cpp diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index 564e08201a4..db99620c9d4 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -26,8 +26,6 @@ #include -#include "MixHelpers.h" - namespace lmms { Sample::Sample(const QString& audioFile) @@ -118,28 +116,43 @@ auto Sample::operator=(Sample&& other) -> Sample& return *this; } -bool Sample::play(SampleFrame* dst, PlaybackState* state, size_t numFrames, double frequency, Loop loopMode) const +bool Sample::play(SampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) const { assert(numFrames > 0); - assert(frequency > 0); - if (m_buffer->empty()) { return false; } + assert(desiredFrequency > 0); + + const auto pastBounds = state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards); + if (loopMode == Loop::Off && pastBounds) { return false; } - const auto outputSampleRate = Engine::audioEngine()->outputSampleRate() * m_frequency / frequency; + const auto outputSampleRate = Engine::audioEngine()->outputSampleRate() * m_frequency / desiredFrequency; const auto inputSampleRate = m_buffer->sampleRate(); const auto resampleRatio = outputSampleRate / inputSampleRate; + const auto marginSize = s_interpolationMargins[state->resampler().interpolationMode()]; + + state->m_frameIndex = std::max(m_startFrame, state->m_frameIndex); + + auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); + playRaw(playBuffer.data(), playBuffer.size(), state, loopMode); - state->frameIndex = std::max(m_startFrame, state->frameIndex); - state->sample = this; - state->loop = &loopMode; + state->resampler().setRatio(resampleRatio); - src_set_ratio(state->resampleState, resampleRatio); - if (src_callback_read(state->resampleState, resampleRatio, numFrames, &dst[0][0]) != 0) + const auto resampleResult + = state->resampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio); + advance(state, resampleResult.inputFramesUsed, loopMode); + + const auto outputFrames = static_cast(resampleResult.outputFramesGenerated); + if (outputFrames < numFrames) { std::fill_n(dst + outputFrames, numFrames - outputFrames, SampleFrame{}); } + + if (!typeInfo::isEqual(m_amplification, 1.0f)) { - MixHelpers::multiply(dst, m_amplification, numFrames); - return true; + for (auto i = std::size_t{0}; i < numFrames; ++i) + { + dst[i][0] *= m_amplification; + dst[i][1] *= m_amplification; + } } - return false; + return true; } auto Sample::sampleDuration() const -> std::chrono::milliseconds @@ -157,43 +170,82 @@ void Sample::setAllPointFrames(int startFrame, int endFrame, int loopStartFrame, setLoopEndFrame(loopEndFrame); } -long Sample::render(void* callbackData, float** data) +void Sample::playRaw(SampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const { - const auto state = static_cast(callbackData); - const auto loop = *state->loop; - const auto sample = state->sample; - auto& index = state->frameIndex; - auto& backwards = state->backwards; + if (m_buffer->size() < 1) { return; } + + auto index = state->m_frameIndex; + auto backwards = state->m_backwards; - switch (loop) + for (size_t i = 0; i < numFrames; ++i) + { + switch (loopMode) + { + case Loop::Off: + if (index < 0 || index >= m_endFrame) { return; } + break; + case Loop::On: + if (index < m_loopStartFrame && backwards) { index = m_loopEndFrame - 1; } + else if (index >= m_loopEndFrame) { index = m_loopStartFrame; } + break; + case Loop::PingPong: + if (index < m_loopStartFrame && backwards) + { + index = m_loopStartFrame; + backwards = false; + } + else if (index >= m_loopEndFrame) + { + index = m_loopEndFrame - 1; + backwards = true; + } + break; + default: + break; + } + + dst[i] = m_buffer->data()[m_reversed ? m_buffer->size() - index - 1 : index]; + backwards ? --index : ++index; + } +} + +void Sample::advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const +{ + state->m_frameIndex += (state->m_backwards ? -1 : 1) * advanceAmount; + if (loopMode == Loop::Off) { return; } + + const auto distanceFromLoopStart = std::abs(state->m_frameIndex - m_loopStartFrame); + const auto distanceFromLoopEnd = std::abs(state->m_frameIndex - m_loopEndFrame); + const auto loopSize = m_loopEndFrame - m_loopStartFrame; + if (loopSize == 0) { return; } + + switch (loopMode) { - case Loop::Off: - if (index < 0 || index >= sample->m_endFrame) { return 0; } - break; case Loop::On: - if (index < sample->m_loopStartFrame && state->backwards) { index = sample->m_loopEndFrame - 1; } - else if (index >= sample->m_loopEndFrame) { index = sample->m_loopStartFrame; } + if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) + { + state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopStart % loopSize; + } + else if (state->m_frameIndex >= m_loopEndFrame) + { + state->m_frameIndex = m_loopStartFrame + distanceFromLoopEnd % loopSize; + } break; case Loop::PingPong: - if (index < sample->m_loopStartFrame && state->backwards) + if (state->m_frameIndex < m_loopStartFrame && state->m_backwards) { - index = sample->m_loopStartFrame; - backwards = false; + state->m_frameIndex = m_loopStartFrame + distanceFromLoopStart % loopSize; + state->m_backwards = false; } - else if (index >= sample->m_loopEndFrame) + else if (state->m_frameIndex >= m_loopEndFrame) { - index = sample->m_loopEndFrame - 1; - backwards = true; + state->m_frameIndex = m_loopEndFrame - 1 - distanceFromLoopEnd % loopSize; + state->m_backwards = true; } break; default: break; } - - const auto srcIndex = sample->m_reversed ? sample->m_buffer->size() - index - 1 : index; - *data = const_cast(&sample->m_buffer->data()[srcIndex][0]); - backwards ? --index : ++index; - return 1; } } // namespace lmms From c16616cca4c12ac558ea393f76146ea3e5a58751 Mon Sep 17 00:00:00 2001 From: szeli1 <143485814+szeli1@users.noreply.github.com> Date: Mon, 5 Aug 2024 13:16:03 +0200 Subject: [PATCH 17/20] Fix duplication of solo state when cloning tracks (#7391) --- src/gui/tracks/TrackOperationsWidget.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gui/tracks/TrackOperationsWidget.cpp b/src/gui/tracks/TrackOperationsWidget.cpp index de119c64f64..6cb74e2c5b7 100644 --- a/src/gui/tracks/TrackOperationsWidget.cpp +++ b/src/gui/tracks/TrackOperationsWidget.cpp @@ -236,6 +236,12 @@ void TrackOperationsWidget::cloneTrack() tcView->moveTrackView( newTrackView, i - 1 ); i--; } + + if (m_soloBtn->model()->value()) + { + // if this track was solo, make the new track the new solo + newTrack->toggleSolo(); + } } From 828cefb4eacf460f558b7cfafb6b9210da54d7ac Mon Sep 17 00:00:00 2001 From: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com> Date: Wed, 7 Aug 2024 07:33:10 +0530 Subject: [PATCH 18/20] Remove `typeInfo` struct from `lmms_basics.h` (#7380) * remove typeInfo struct from lmms_basics.h * Code review Co-authored-by: saker * converted epsilon to a constant * renamed to approximatelyEqual and moved to top --------- Co-authored-by: saker --- include/InstrumentTrack.h | 2 ++ include/lmms_basics.h | 52 ---------------------------- include/lmms_constants.h | 2 ++ include/lmms_math.h | 5 +++ plugins/Eq/EqCurve.cpp | 3 +- src/core/AutomatableModel.cpp | 5 ++- src/core/Effect.cpp | 3 +- src/core/NotePlayHandle.cpp | 2 +- src/core/Oscillator.cpp | 2 +- src/core/PresetPreviewPlayHandle.cpp | 2 +- src/tracks/InstrumentTrack.cpp | 2 +- 11 files changed, 19 insertions(+), 61 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 45060f05504..1e46fb0cb8c 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -26,6 +26,8 @@ #ifndef LMMS_INSTRUMENT_TRACK_H #define LMMS_INSTRUMENT_TRACK_H +#include + #include "AudioPort.h" #include "InstrumentFunctions.h" #include "InstrumentSoundShaping.h" diff --git a/include/lmms_basics.h b/include/lmms_basics.h index 57ef647f50e..63a2bf3adc2 100644 --- a/include/lmms_basics.h +++ b/include/lmms_basics.h @@ -26,7 +26,6 @@ #define LMMS_TYPES_H #include -#include #include "lmmsconfig.h" @@ -55,57 +54,6 @@ using mix_ch_t = uint16_t; // Mixer-channel (0 to MAX_CHANNEL) using jo_id_t = uint32_t; // (unique) ID of a journalling object -// windows headers define "min" and "max" macros, breaking the methods bwloe -#undef min -#undef max - -template -struct typeInfo -{ - static inline T min() - { - return std::numeric_limits::min(); - } - - static inline T max() - { - return std::numeric_limits::max(); - } - - static inline T minEps() - { - return 1; - } - - static inline bool isEqual( T x, T y ) - { - return x == y; - } - - static inline T absVal( T t ) - { - return t >= 0 ? t : -t; - } -} ; - - -template<> -inline float typeInfo::minEps() -{ - return 1.0e-10f; -} - -template<> -inline bool typeInfo::isEqual( float x, float y ) -{ - if( x == y ) - { - return true; - } - return absVal( x - y ) < minEps(); -} - - constexpr ch_cnt_t DEFAULT_CHANNELS = 2; diff --git a/include/lmms_constants.h b/include/lmms_constants.h index 0c2ee175351..4390b81eaec 100644 --- a/include/lmms_constants.h +++ b/include/lmms_constants.h @@ -56,6 +56,8 @@ constexpr float F_E = (float) LD_E; constexpr float F_E_R = (float) LD_E_R; constexpr float F_SQRT_2 = (float) LD_SQRT_2; +constexpr float F_EPSILON = 1.0e-10f; // 10^-10 + // Microtuner constexpr unsigned int MaxScaleCount = 10; //!< number of scales per project constexpr unsigned int MaxKeymapCount = 10; //!< number of keyboard mappings per project diff --git a/include/lmms_math.h b/include/lmms_math.h index 0da3b9b3dc9..369a89b6ea6 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -37,6 +37,11 @@ namespace lmms { +static inline bool approximatelyEqual(float x, float y) +{ + return x == y ? true : std::abs(x - y) < F_EPSILON; +} + #ifdef __INTEL_COMPILER static inline float absFraction( const float _x ) diff --git a/plugins/Eq/EqCurve.cpp b/plugins/Eq/EqCurve.cpp index fcf78609bc9..df17f71ffbf 100644 --- a/plugins/Eq/EqCurve.cpp +++ b/plugins/Eq/EqCurve.cpp @@ -31,6 +31,7 @@ #include "embed.h" #include "Engine.h" #include "lmms_constants.h" +#include "lmms_math.h" namespace lmms::gui @@ -65,7 +66,7 @@ QRectF EqHandle::boundingRect() const float EqHandle::freqToXPixel( float freq , int w ) { - if (typeInfo::isEqual(freq, 0.0f)) { return 0.0f; } + if (approximatelyEqual(freq, 0.0f)) { return 0.0f; } float min = log10f( 20 ); float max = log10f( 20000 ); float range = max - min; diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index abda0b43ed3..5ce259dc2b0 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -354,8 +354,7 @@ float AutomatableModel::inverseScaledValue( float value ) const template void roundAt( T& value, const T& where, const T& step_size ) { - if (std::abs(value - where) - < typeInfo::minEps() * std::abs(step_size)) + if (std::abs(value - where) < F_EPSILON * std::abs(step_size)) { value = where; } @@ -583,7 +582,7 @@ float AutomatableModel::controllerValue( int frameOffset ) const "lacks implementation for a scale type"); break; } - if( typeInfo::isEqual( m_step, 1 ) && m_hasStrictStepSize ) + if (approximatelyEqual(m_step, 1) && m_hasStrictStepSize) { return std::round(v); } diff --git a/src/core/Effect.cpp b/src/core/Effect.cpp index 61680be2a6a..b6b2840515a 100644 --- a/src/core/Effect.cpp +++ b/src/core/Effect.cpp @@ -32,6 +32,7 @@ #include "ConfigManager.h" #include "SampleFrame.h" +#include "lmms_constants.h" namespace lmms { @@ -154,7 +155,7 @@ void Effect::checkGate( double _out_sum ) // Check whether we need to continue processing input. Restart the // counter if the threshold has been exceeded. - if( _out_sum - gate() <= typeInfo::minEps() ) + if (_out_sum - gate() <= F_EPSILON) { incrementBufferCount(); if( bufferCount() > timeout() ) diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 4ee30054afd..7d649e1dd9f 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -568,7 +568,7 @@ void NotePlayHandle::processTimePos(const TimePos& time, float pitchValue, bool else { const float v = detuning()->automationClip()->valueAt(time - songGlobalParentOffset() - pos()); - if (!typeInfo::isEqual(v, m_baseDetuning->value())) + if (!approximatelyEqual(v, m_baseDetuning->value())) { m_baseDetuning->setValue(v); updateFrequency(); diff --git a/src/core/Oscillator.cpp b/src/core/Oscillator.cpp index 4cbd9ccb451..a875cf2d4f4 100644 --- a/src/core/Oscillator.cpp +++ b/src/core/Oscillator.cpp @@ -535,7 +535,7 @@ void Oscillator::updateFM( SampleFrame* _ab, const fpp_t _frames, // should be called every time phase-offset is changed... inline void Oscillator::recalcPhase() { - if( !typeInfo::isEqual( m_phaseOffset, m_ext_phaseOffset ) ) + if (!approximatelyEqual(m_phaseOffset, m_ext_phaseOffset)) { m_phase -= m_phaseOffset; m_phaseOffset = m_ext_phaseOffset; diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index 8db5644c61d..e7e67185ed2 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -175,7 +175,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, // create note-play-handle for it m_previewNote = NotePlayHandleManager::acquire( s_previewTC->previewInstrumentTrack(), 0, - typeInfo::max() / 2, + std::numeric_limits::max() / 2, Note( 0, 0, DefaultKey, 100 ) ); setAudioPort( s_previewTC->previewInstrumentTrack()->audioPort() ); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 14cf19153fe..be18fc9e4ca 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -342,7 +342,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim NotePlayHandle* nph = NotePlayHandleManager::acquire( this, offset, - typeInfo::max() / 2, + std::numeric_limits::max() / 2, Note(TimePos(), Engine::getSong()->getPlayPos(Engine::getSong()->playMode()), event.key(), event.volume(midiPort()->baseVelocity())), nullptr, event.channel(), From 6c7fecd8d7b729b4ad83187f511f7b93ab1430ff Mon Sep 17 00:00:00 2001 From: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com> Date: Wed, 7 Aug 2024 08:53:17 +0530 Subject: [PATCH 19/20] Fix build regression from #7380 (#7437) --- src/core/Sample.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index db99620c9d4..3a1dbfcb256 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -24,6 +24,8 @@ #include "Sample.h" +#include "lmms_math.h" + #include namespace lmms { @@ -143,7 +145,7 @@ bool Sample::play(SampleFrame* dst, PlaybackState* state, size_t numFrames, floa const auto outputFrames = static_cast(resampleResult.outputFramesGenerated); if (outputFrames < numFrames) { std::fill_n(dst + outputFrames, numFrames - outputFrames, SampleFrame{}); } - if (!typeInfo::isEqual(m_amplification, 1.0f)) + if (!approximatelyEqual(m_amplification, 1.0f)) { for (auto i = std::size_t{0}; i < numFrames; ++i) { From 632966caeecbc169625fe8f9dc6dbdb607606659 Mon Sep 17 00:00:00 2001 From: saker Date: Wed, 7 Aug 2024 13:12:37 -0400 Subject: [PATCH 20/20] Do not put the main thread in realtime or high priority (#7436) --- src/core/main.cpp | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/core/main.cpp b/src/core/main.cpp index 395f58c3d1c..3e6c2c85f06 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -43,10 +43,6 @@ #include #endif -#ifdef LMMS_HAVE_SCHED_H -#include "sched.h" -#endif - #ifdef LMMS_HAVE_PROCESS_H #include #endif @@ -741,29 +737,6 @@ int main( int argc, char * * argv ) // override it with bundled/custom one, if exists loadTranslation(QString("qt_") + pos, ConfigManager::inst()->localeDir()); - - // try to set realtime priority -#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_FREEBSD) -#ifdef LMMS_HAVE_SCHED_H -#ifndef __OpenBSD__ - struct sched_param sparam; - sparam.sched_priority = ( sched_get_priority_max( SCHED_FIFO ) + - sched_get_priority_min( SCHED_FIFO ) ) / 2; - if( sched_setscheduler( 0, SCHED_FIFO, &sparam ) == -1 ) - { - printf( "Notice: could not set realtime priority.\n" ); - } -#endif -#endif // LMMS_HAVE_SCHED_H -#endif - -#ifdef LMMS_BUILD_WIN32 - if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) - { - printf( "Notice: could not set high priority.\n" ); - } -#endif - #if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE struct sigaction sa; sa.sa_handler = SIG_IGN;