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/22] 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 f6239c297..3cba834e1 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -230,6 +230,7 @@ public: QString defaultVersion() const; + static bool enableBlockedPlugins(); static QStringList availableVstEmbedMethods(); QString vstEmbedMethod() const; diff --git a/include/Engine.h b/include/Engine.h index ed4cbd93c..7e19e2e84 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -81,8 +81,6 @@ public: 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 58126a0a4..8a9f51684 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 @@ public: 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 @@ private: 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 01a4268ec..b0b9b1946 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 19ce80ddb..d3c973020 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 c1f609120..9435eb69c 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 c9cf3400f..b40c4723b 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 1633b8626..9807379e4 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 27d18ef27..7cd9d3a50 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/22] 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 59c651a68..aa2e6de68 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 698d9c8fe..08dfda4dd 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 533c8abf4..238a85dea 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 4695b4f8a..57ef647f5 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 767d15e73..b10c065b2 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 b39342fd1..acdbc45f7 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 @@ public: 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 1742ee3a7..2a07e5f77 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 f40b69d12..8081d20ca 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h +++ b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h @@ -125,17 +125,17 @@ private: 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: 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 02038239a..0041b0c51 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 268e7fd10..9fbc7b7f6 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 e779f2283..6d8da26e3 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 fd1d6b1a3..14868bf0e 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 b752cd9c3..b63dd48e9 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 b14fe2de0..c245ca64c 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 216a5fbf3..e4bb760e2 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 096534d5e..5ef735f64 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 8e4cc8e29..157b6fe65 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 893fccbf5..9237ad70d 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 2a5414049..f58766725 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 030384c5e..a4e6ff097 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 42e49a8ae..c26e21039 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/22] 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 98f6703db..84602f121 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 157b6fe65..42e7079b0 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 a7a051e26..47598c633 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 0b0689eb5..ba02296f8 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 d24e82d98..4cbd9ccb4 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 eb90f6b65..134fcd311 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 4cfcc313c..dc26bf2b5 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 7bae3db1c..8efdd2c11 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 8cb840592..3178d7ef2 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/22] 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 c0287cd8b..77b242199 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 aac04bc55..9499523f7 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/22] 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 a9dbda4cc..9a6d97e8d 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 b76235aa1..60a5ef810 100644 --- a/include/AudioEngineWorkerThread.h +++ b/include/AudioEngineWorkerThread.h @@ -71,8 +71,8 @@ public: 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 dbe750c4f..fbfa9b60d 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -84,11 +84,7 @@ public: 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 @@ private: 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 d44b99543..1652ac71d 100644 --- a/include/LocklessAllocator.h +++ b/include/LocklessAllocator.h @@ -50,8 +50,8 @@ private: 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 9a14e427c..453f65410 100644 --- a/include/MidiEvent.h +++ b/include/MidiEvent.h @@ -190,6 +190,8 @@ public: setParam( 0, pitchBend ); } + auto sysExData() const -> const char* { return m_sysExData; } + Source source() const { return m_source; @@ -212,7 +214,7 @@ private: 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 e65bde010..d74f9c11c 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 f537044dc..0a5e166dc 100644 --- a/include/Oscillator.h +++ b/include/Oscillator.h @@ -170,12 +170,8 @@ public: { 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 @@ public: 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 d4676b52e..a2e2e08d6 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 fd2c0f9bb..a50d5ff6d 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 @@ public: if( m_buffer ) delete[] m_buffer; } - inline void setSize( int size ) + void setSize(std::size_t size) { if( m_buffer ) { @@ -90,8 +90,8 @@ public: 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 b801bb182..db33900f5 100644 --- a/include/Track.h +++ b/include/Track.h @@ -127,7 +127,7 @@ public: 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 cd4e5f88d..80c69a0a3 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 bf5e53a2b..0da3b9b3d 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 b10c065b2..4cc14ba9c 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 76e8b9681..ea1c43acb 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 f5d5aa574..e81e65550 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 d8eab5fc4..f833c4668 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: QMutex fMutex; uint8_t m_paramGroupCount; - QList m_paramModels; + std::vector m_paramModels; QDomElement m_settingsElem; QCompleter* m_paramsCompleter; @@ -351,7 +353,7 @@ private: 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 2784f9b6b..2142d040c 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 75be48697..99df328ef 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 d801e82d4..e9acddeeb 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 69ac1b80c..ccec8c873 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 b6613de7f..b72e30b33 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 e5af1e8bf..992d05304 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 c904b4c13..0f94168b7 100755 --- a/plugins/GranularPitchShifter/GranularPitchShifterEffect.h +++ b/plugins/GranularPitchShifter/GranularPitchShifterEffect.h @@ -58,7 +58,7 @@ public: // 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 26901c65f..75c79ad21 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 7063be0e2..874b5d8d1 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 da705b9ff..a1ae72a38 100644 --- a/plugins/Monstro/Monstro.h +++ b/plugins/Monstro/Monstro.h @@ -184,7 +184,7 @@ private: 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 6d8da26e3..ecc1d8f30 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 54c36a510..c167bcec2 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 af64d0095..886036095 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 c8d1dff67..d5c52b0bd 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 14868bf0e..13dab7b8a 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 4f2b1765a..4d21abd4d 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 3644060ed..dcfbf6bc5 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 3793ed2f1..808b81c39 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 e0eef8b59..d9e7ac8a4 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 452e1e90f..2d15da7f4 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 d5ed8d99d..261c897df 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 c4dd08afa..19b8ab1c0 100644 --- a/plugins/Vibed/Vibed.cpp +++ b/plugins/Vibed/Vibed.cpp @@ -72,11 +72,11 @@ public: ~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 ef31cfdb8..62cb7cfe5 100644 --- a/plugins/VstBase/RemoteVstPlugin.cpp +++ b/plugins/VstBase/RemoteVstPlugin.cpp @@ -265,7 +265,7 @@ public: 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 ef5d3dbf1..c48b94ec8 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 3fb1fc5f2..23a76b228 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 b3490a50f..cbd7326fe 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 ae459c5e4..70f776041 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 fea907942..934ccc3f8 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 a55dbd82b..b664387bd 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 9c0a4596a..a3c3bcf91 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 a57ce6ce5..eca06f9a2 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 919093906..92eadd751 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 9c5ab871b..46d83017f 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 ba02296f8..7e3fc1f60 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 1e1ddb545..d3ee091f4 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 392e9e256..92cb2ba30 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 6c4ba465e..7ff73e718 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 1321a8a24..bd4275235 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 00bbec2e4..eb5058bc6 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 d33ede4d2..48c400a24 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 6bb8415bd..20660df41 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 91b6e26c3..ca356e727 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 a0f7f7c53..9eb6acb6b 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 5237690a7..291578698 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 5811f956c..bda6894f3 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 a3a78e256..7558c4c26 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 8dfda4981..1f989841f 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 e205a0c00..afea82ea4 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 3178d7ef2..775bd96a8 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 dfee9a5e6..d6e64ff7e 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/22] 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 6dd6fcc60..000000000 --- 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 3fd5bc38e..754350368 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 @@ public: 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; } + ~PlaybackState() + { + src_delete(resampleState); + } - 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; + 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 @@ public: 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 @@ public: 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 8ec6c5886..114634577 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 4cc14ba9c..2e63b5178 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 b72e30b33..061df7bd5 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 685c7f546..117178e54 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 8fb7d95a2..000000000 --- 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 3608d2848..9eeb33904 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 db99620c9..564e08201 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); + assert(frequency > 0); + if (m_buffer->empty()) { return false; } - 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 / 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); + state->frameIndex = std::max(m_startFrame, state->frameIndex); + state->sample = this; + state->loop = &loopMode; - auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); - playRaw(playBuffer.data(), playBuffer.size(), state, loopMode); - - state->resampler().setRatio(resampleRatio); - - 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; } + const auto state = static_cast(callbackData); + const auto loop = *state->loop; + const auto sample = state->sample; + auto& index = state->frameIndex; + auto& backwards = state->backwards; - auto index = state->m_frameIndex; - auto backwards = state->m_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/22] 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 d6e64ff7e..409fb60ae 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/22] 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 25dcf834c..8d21b3657 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/22] 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 9a6d97e8d..74b6d73b6 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 000000000..57cc6ad49 --- /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 046f515ea..15d47200c 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 62cb7cfe5..f8bf39a37 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 dd15bb0bd..3f861e2d5 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/22] 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 77b242199..51610d877 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 9499523f7..d958c3668 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/22] Lv2: Improve plugin description (#7357) --- include/Lv2Basics.h | 10 +- src/core/lv2/Lv2SubPluginFeatures.cpp | 213 ++++++++++++++++++-------- 2 files changed, 159 insertions(+), 64 deletions(-) diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h index 53489e30d..5b2865868 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 135da3e2a..66abe2f2e 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( + 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(leftLabel); + l->addWidget(rightLabel, 1); +} + + + + +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)); - maker_content->setWordWrap(true); - - l->addWidget(maker_label); - l->addWidget(maker_content, 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 + { + 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 +{ + 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 { (void)k; - return QString::fromUtf8("description not implemented yet"); // TODO + return nullptr; // Lv2 currently does not support this } -const PixmapLoader *Lv2SubPluginFeatures::logo( - const Plugin::Descriptor::SubPluginFeatures::Key &k) const +void Lv2SubPluginFeatures::listSubPluginKeys(const Plugin::Descriptor* desc, + KeyList& kl) const { - (void)k; // TODO - return nullptr; -} - - - - -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/22] 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 5c683cfd9..0eb33bad9 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -73,7 +73,8 @@ public: enum class AutoScrollState { - Enabled, + Stepped, + Continuous, Disabled }; @@ -212,7 +213,7 @@ private: 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 bda6894f3..1c89f6a0b 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 9ba9ac083..f043e715a 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 7657e2916..f75b1cabf 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 afea82ea4..1926ffeef 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/22] 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 5005c11bc..7f5f5b888 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -133,6 +133,7 @@ private: 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 b664387bd..3e7d6d8b6 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/22] 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 8a8b03635..702d581fe 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 4cd1464d6..fe3d60ff2 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 2c51f276e..735942ffb 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 1f11b4eb3..cc233a636 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 476165408..f6c329539 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 14c617b65..2c2e8dd73 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 3f7700674..228f383c1 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 000000000..a7aa0ae1e --- /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/22] 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 b970997ef..395f58c3d 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/22] 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 000000000..6dd6fcc60 --- /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 754350368..3fd5bc38e 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 @@ public: 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 @@ public: 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 @@ public: 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 114634577..8ec6c5886 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 2e63b5178..4cc14ba9c 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 061df7bd5..b72e30b33 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 117178e54..685c7f546 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 000000000..8fb7d95a2 --- /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 9eeb33904..3608d2848 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 564e08201..db99620c9 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 outputSampleRate = Engine::audioEngine()->outputSampleRate() * m_frequency / frequency; + 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 / desiredFrequency; const auto inputSampleRate = m_buffer->sampleRate(); const auto resampleRatio = outputSampleRate / inputSampleRate; + const auto marginSize = s_interpolationMargins[state->resampler().interpolationMode()]; - state->frameIndex = std::max(m_startFrame, state->frameIndex); - state->sample = this; - state->loop = &loopMode; + state->m_frameIndex = std::max(m_startFrame, state->m_frameIndex); - src_set_ratio(state->resampleState, resampleRatio); - if (src_callback_read(state->resampleState, resampleRatio, numFrames, &dst[0][0]) != 0) + auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); + playRaw(playBuffer.data(), playBuffer.size(), state, loopMode); + + state->resampler().setRatio(resampleRatio); + + 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; } - switch (loop) + auto index = state->m_frameIndex; + auto backwards = state->m_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) { - 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/22] 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 de119c64f..6cb74e2c5 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/22] 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 45060f055..1e46fb0cb 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 57ef647f5..63a2bf3ad 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 0c2ee1753..4390b81ea 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 0da3b9b3d..369a89b6e 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 fcf78609b..df17f71ff 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 abda0b43e..5ce259dc2 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 61680be2a..b6b284051 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 4ee30054a..7d649e1dd 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 4cbd9ccb4..a875cf2d4 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 8db5644c6..e7e67185e 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 14cf19153..be18fc9e4 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/22] 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 db99620c9..3a1dbfcb2 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/22] 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 395f58c3d..3e6c2c85f 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; From 44a8b038f5587600559f94e66d566abf279ec374 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Sat, 10 Aug 2024 05:30:06 +0200 Subject: [PATCH 21/22] Fix #5851: Implement `EffectRackView::sizeHint()` (#7428) * Fix #5851: Implement `EffectRackView::sizeHint()` This fixes `EffectRackView` to have a permanent size hint instead of resizing the widget once in `InstrumentTrackWindow`. The size hint tells the `InstrumentTrackWindow` to not increase with a growing number of effects in the `EffectRackView`. --- include/EffectRackView.h | 2 ++ src/gui/EffectRackView.cpp | 9 +++++++++ src/gui/instrument/InstrumentTrackWindow.cpp | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/EffectRackView.h b/include/EffectRackView.h index 4a90c6b7a..fec627a56 100644 --- a/include/EffectRackView.h +++ b/include/EffectRackView.h @@ -64,6 +64,8 @@ private slots: private: void modelChanged() override; + QSize sizeHint() const override; + QSize minimumSizeHint() const override { return sizeHint(); } inline EffectChain* fxChain() { diff --git a/src/gui/EffectRackView.cpp b/src/gui/EffectRackView.cpp index 478e117fe..eb8c6c43e 100644 --- a/src/gui/EffectRackView.cpp +++ b/src/gui/EffectRackView.cpp @@ -273,4 +273,13 @@ void EffectRackView::modelChanged() +QSize EffectRackView::sizeHint() const +{ + // Use the formula from InstrumentTrackWindow.cpp + return QSize{EffectRackView::DEFAULT_WIDTH, 254 /* INSTRUMENT_HEIGHT */ - 4 - 1}; +} + + + + } // namespace lmms::gui diff --git a/src/gui/instrument/InstrumentTrackWindow.cpp b/src/gui/instrument/InstrumentTrackWindow.cpp index 8b868bb50..1c9c93a09 100644 --- a/src/gui/instrument/InstrumentTrackWindow.cpp +++ b/src/gui/instrument/InstrumentTrackWindow.cpp @@ -265,7 +265,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : m_tabWidget->addTab(m_tuningView, tr("Tuning and transposition"), "tuning_tab", 5); adjustTabSize(m_ssView); adjustTabSize(instrumentFunctions); - m_effectView->resize(EffectRackView::DEFAULT_WIDTH, INSTRUMENT_HEIGHT - 4 - 1); + // EffectRackView has sizeHint to be QSize(EffectRackView::DEFAULT_WIDTH, INSTRUMENT_HEIGHT - 4 - 1) adjustTabSize(m_midiView); adjustTabSize(m_tuningView); From 74c73e5848651d244789f66c6ef98ce6976183b6 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 10 Aug 2024 22:33:52 +0200 Subject: [PATCH 22/22] SDL driver's input and output device configuration via combo box (#7421) * Enable configuration of input device for SDL Up to now the SDL audio driver attempted to use the default recording device. This might not be what users want or expect, especially since the actually used device is not visible anywhere. So if recording does not work for the users they have no way to find out what's wrong. Extend the settings screen of the SDL driver with a combo box that allows to select the input device to be used. Store the selected device name in a new attribute called "inputdevice" in the "audiosdl" section of the configuration file. Use the information from the configuration when attempting to inialize the input device. Fall back to the default device if that does not work. (cherry picked from commit 33139b9f4c7a4844d3e101cbf1887823fbec776a) * Provide a setting for system default input Provide the setting "[System Default]" which instructs the SDL driver to use the default device of the system as the input device. In the configuration file this option is represented as an empty string. This should play well with the current existing configuration of the users. (cherry picked from commit 29c43c2bb66033bd44787b3207e70ce0d31c845d) * Configuration of output device for SDL Let users configure the output device that's used by the SDL driver. Code-wise the implementation is very similar to the input device configuration. Use a `QComboBox` instead of a `QLineEdit` for `m_device` and rename it to `m_playbackDeviceComboBox`. Rename `s_defaultInputDevice` to `s_systemDefaultDevice` because it is used in the context of playback and input devices. (cherry picked from commit 1ab45e4994851d5a629db4a22d7825f79386ef5d) * Ensure label visibility Make sure that labels are always shown by setting the row wrap policy of the form layout to wrap long rows. (cherry picked from commit a123d0e3cb62745b83d9304747445ff73e1345fb) * Rename "Device" Rename "Device" to "Playback device" to make clear what the setting refers to. (cherry picked from commit 1f0cda4983f66a0d3a9f024ae3ee2d57c7bf59e6) * Remove repeated strings Introduce const expressions to get rid of repeated strings with a risk of typos. (cherry picked from commit f9ea9705b8780298d78e3010053ae026a64aacee) * Apply some more changes Apply some more changes that have been made to `AudioSdl` in the recording branch. * Conditional ternary operator Also use a conditional ternary operator for the input device setup. * Methods for population of combo boxes Move the population of the input and playback device combo boxes into the methods `populatePlaybackDeviceComboBox` and `populateInputDeviceComboBox`. * Sort devices in combo box Sort the devices names alphabetically in the input and playback combo boxes. The default devices is always shown as the first entry. * Code review fixes Use `AudioDeviceSetupWidget` instead of `QObject` to translate "[System Default]". Fix copy/paste error in comment. * Simplify some constexpr statements --- include/AudioSdl.h | 10 ++- src/core/audio/AudioSdl.cpp | 125 +++++++++++++++++++++++++++++++----- 2 files changed, 118 insertions(+), 17 deletions(-) diff --git a/include/AudioSdl.h b/include/AudioSdl.h index fcfe97318..c256815f0 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -39,7 +39,7 @@ #include "AudioDevice.h" #include "AudioDeviceSetupWidget.h" -class QLineEdit; +class QComboBox; namespace lmms { @@ -64,10 +64,16 @@ public: ~setupWidget() override = default; void saveSettings() override; + + private: + void populatePlaybackDeviceComboBox(); + void populateInputDeviceComboBox(); private: - QLineEdit * m_device; + QComboBox* m_playbackDeviceComboBox = nullptr; + QComboBox* m_inputDeviceComboBox = nullptr; + static QString s_systemDefaultDevice; } ; diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index f58766725..192fb62e3 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include "AudioEngine.h" @@ -37,6 +38,10 @@ namespace lmms { +constexpr auto SectionSDL = "audiosdl"; +constexpr auto PlaybackDeviceSDL = "device"; +constexpr auto InputDeviceSDL = "inputdevice"; + AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : AudioDevice( DEFAULT_CHANNELS, _audioEngine ), m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) @@ -78,11 +83,19 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : SDL_AudioSpec actual; #ifdef LMMS_HAVE_SDL2 - m_outputDevice = SDL_OpenAudioDevice (nullptr, - 0, - &m_audioHandle, - &actual, - 0); + const auto playbackDevice = ConfigManager::inst()->value(SectionSDL, PlaybackDeviceSDL).toStdString(); + const bool isDefaultPlayback = playbackDevice.empty(); + + // Try with the configured device + const auto playbackDeviceCStr = isDefaultPlayback ? nullptr : playbackDevice.c_str(); + m_outputDevice = SDL_OpenAudioDevice(playbackDeviceCStr, 0, &m_audioHandle, &actual, 0); + + // If we did not get a device ID try again with the default device if we did not try that before + if (m_outputDevice == 0 && !isDefaultPlayback) + { + m_outputDevice = SDL_OpenAudioDevice(nullptr, 0, &m_audioHandle, &actual, 0); + } + if (m_outputDevice == 0) { qCritical( "Couldn't open SDL-audio: %s\n", SDL_GetError() ); return; @@ -108,11 +121,19 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : m_inputAudioHandle = m_audioHandle; m_inputAudioHandle.callback = sdlInputAudioCallback; - m_inputDevice = SDL_OpenAudioDevice (nullptr, - 1, - &m_inputAudioHandle, - &actual, - 0); + const auto inputDevice = ConfigManager::inst()->value(SectionSDL, InputDeviceSDL).toStdString(); + const bool isDefaultInput = inputDevice.empty(); + + // Try with the configured device + const auto inputDeviceCStr = isDefaultInput ? nullptr : inputDevice.c_str(); + m_inputDevice = SDL_OpenAudioDevice (inputDeviceCStr, 1, &m_inputAudioHandle, &actual, 0); + + // If we did not get a device ID try again with the default device if we did not try that before + if (m_inputDevice == 0 && !isDefaultInput) + { + m_inputDevice = SDL_OpenAudioDevice(nullptr, 1, &m_inputAudioHandle, &actual, 0); + } + if (m_inputDevice != 0) { m_supportsCapture = true; } else { @@ -283,15 +304,25 @@ void AudioSdl::sdlInputAudioCallback(Uint8 *_buf, int _len) { #endif +QString AudioSdl::setupWidget::s_systemDefaultDevice = AudioDeviceSetupWidget::tr("[System Default]"); + AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : AudioDeviceSetupWidget( AudioSdl::name(), _parent ) { QFormLayout * form = new QFormLayout(this); + form->setRowWrapPolicy(QFormLayout::WrapLongRows); - QString dev = ConfigManager::inst()->value( "audiosdl", "device" ); - m_device = new QLineEdit( dev, this ); + m_playbackDeviceComboBox = new QComboBox(this); - form->addRow(tr("Device"), m_device); + populatePlaybackDeviceComboBox(); + + form->addRow(tr("Playback device"), m_playbackDeviceComboBox); + + m_inputDeviceComboBox = new QComboBox(this); + + populateInputDeviceComboBox(); + + form->addRow(tr("Input device"), m_inputDeviceComboBox); } @@ -299,8 +330,72 @@ AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : void AudioSdl::setupWidget::saveSettings() { - ConfigManager::inst()->setValue( "audiosdl", "device", - m_device->text() ); + const auto currentPlaybackDevice = m_playbackDeviceComboBox->currentText(); + if (currentPlaybackDevice == s_systemDefaultDevice) + { + // Represent the default playback device with an empty string + ConfigManager::inst()->setValue(SectionSDL, PlaybackDeviceSDL, ""); + } + else if (!currentPlaybackDevice.isEmpty()) + { + ConfigManager::inst()->setValue(SectionSDL, PlaybackDeviceSDL, currentPlaybackDevice); + } + + const auto currentInputDevice = m_inputDeviceComboBox->currentText(); + if (currentInputDevice == s_systemDefaultDevice) + { + // Represent the default input device with an empty string + ConfigManager::inst()->setValue(SectionSDL, InputDeviceSDL, ""); + } + else if (!currentInputDevice.isEmpty()) + { + ConfigManager::inst()->setValue(SectionSDL, InputDeviceSDL, currentInputDevice); + } +} + +void AudioSdl::setupWidget::populatePlaybackDeviceComboBox() +{ +#ifdef LMMS_HAVE_SDL2 + m_playbackDeviceComboBox->addItem(s_systemDefaultDevice); + + QStringList playbackDevices; + const int numberOfPlaybackDevices = SDL_GetNumAudioDevices(0); + for (int i = 0; i < numberOfPlaybackDevices; ++i) + { + const QString deviceName = SDL_GetAudioDeviceName(i, 0); + playbackDevices.append(deviceName); + } + + playbackDevices.sort(); + + m_playbackDeviceComboBox->addItems(playbackDevices); + + const auto playbackDevice = ConfigManager::inst()->value(SectionSDL, PlaybackDeviceSDL); + m_playbackDeviceComboBox->setCurrentText(playbackDevice.isEmpty() ? s_systemDefaultDevice : playbackDevice); +#endif +} + +void AudioSdl::setupWidget::populateInputDeviceComboBox() +{ +#ifdef LMMS_HAVE_SDL2 + m_inputDeviceComboBox->addItem(s_systemDefaultDevice); + + QStringList inputDevices; + const int numberOfInputDevices = SDL_GetNumAudioDevices(1); + for (int i = 0; i < numberOfInputDevices; ++i) + { + const QString deviceName = SDL_GetAudioDeviceName(i, 1); + inputDevices.append(deviceName); + } + + inputDevices.sort(); + + m_inputDeviceComboBox->addItems(inputDevices); + + // Set the current device to the one in the configuration + const auto inputDevice = ConfigManager::inst()->value(SectionSDL, InputDeviceSDL); + m_inputDeviceComboBox->setCurrentText(inputDevice.isEmpty() ? s_systemDefaultDevice : inputDevice); +#endif }