From 5674bb58e2554e3cfc9e01951de27a81c82f7c67 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 29 Jul 2023 09:58:40 +0200 Subject: [PATCH 01/29] Fix HiDPI issues for menus Menus now use the size that users have set globally for their applications. This is accomplished by removing the fixed font size definition (in points) for `QMenu` in `style.css`. In some places the menus had been set to hard coded font sizes. This code is also removed and applies to the following menus: * Combo box menus * Track operation widget (gear icon on tracks) * The MIDI port menu --- data/themes/default/style.css | 4 ---- src/gui/menus/MidiPortMenu.cpp | 1 - src/gui/tracks/TrackOperationsWidget.cpp | 1 - src/gui/widgets/ComboBox.cpp | 1 - 4 files changed, 7 deletions(-) diff --git a/data/themes/default/style.css b/data/themes/default/style.css index a9646cfe4..12b63cfec 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -115,7 +115,6 @@ QSplashScreen QLabel { QMenu { border-top: 2px solid #08993E; background-color: #15191c; - font-size: 11px; } QMenu::separator { @@ -133,15 +132,12 @@ QMenu::item { QMenu::item:selected { color: #d1d8e4; - font-weight: normal; background-color: #21272b; } QMenu::item:disabled { color: #515459; background-color: #262b30; - font-size: 12px; - font-weight: normal; padding: 4px 32px 4px 20px; } diff --git a/src/gui/menus/MidiPortMenu.cpp b/src/gui/menus/MidiPortMenu.cpp index b1ddf71c9..67ad01f87 100644 --- a/src/gui/menus/MidiPortMenu.cpp +++ b/src/gui/menus/MidiPortMenu.cpp @@ -34,7 +34,6 @@ MidiPortMenu::MidiPortMenu( MidiPort::Modes _mode ) : ModelView( nullptr, this ), m_mode( _mode ) { - setFont( pointSize<9>( font() ) ); connect( this, SIGNAL(triggered(QAction*)), this, SLOT(activatedPort(QAction*))); } diff --git a/src/gui/tracks/TrackOperationsWidget.cpp b/src/gui/tracks/TrackOperationsWidget.cpp index ddbd2eacd..3e3654974 100644 --- a/src/gui/tracks/TrackOperationsWidget.cpp +++ b/src/gui/tracks/TrackOperationsWidget.cpp @@ -64,7 +64,6 @@ TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) : "to begin a new drag'n'drop action." ).arg(UI_CTRL_KEY) ); auto toMenu = new QMenu(this); - toMenu->setFont( pointSize<9>( toMenu->font() ) ); connect( toMenu, SIGNAL(aboutToShow()), this, SLOT(updateMenu())); diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index bdf78ccce..2377a37ab 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -70,7 +70,6 @@ ComboBox::ComboBox( QWidget * _parent, const QString & _name ) : } setFont( pointSize<9>( font() ) ); - m_menu.setFont( pointSize<8>( m_menu.font() ) ); connect( &m_menu, SIGNAL(triggered(QAction*)), this, SLOT(setItem(QAction*))); From 67dcdf2c65a306933e2b34379a04c2cee85ed8ce Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 29 Jul 2023 10:19:47 +0200 Subject: [PATCH 02/29] Fix HiDPI issue of sample track window Fix one of the HiDPI issues of the sample track window by removing the hard coded font size of the name line edit. --- src/gui/SampleTrackWindow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/SampleTrackWindow.cpp b/src/gui/SampleTrackWindow.cpp index 6fe70bf41..06afdfd3d 100644 --- a/src/gui/SampleTrackWindow.cpp +++ b/src/gui/SampleTrackWindow.cpp @@ -74,7 +74,6 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : // setup line edit for changing sample track name m_nameLineEdit = new QLineEdit; - m_nameLineEdit->setFont(pointSize<9>(m_nameLineEdit->font())); connect(m_nameLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(textChanged(const QString&))); From 48cdaddd9dd1a9c1a3716995d752bed567b6c9ad Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 29 Jul 2023 16:21:05 +0200 Subject: [PATCH 03/29] HiDPI fixes for elements based on QTreeView Fix the HiDPI problems for elements that are based on QTreeView by removing the fixed point based font size from the style sheet. This affects: * The file browser * The headings of the plugin browser * The patch selection dialogs for GigPlayer and SoundFont player Also make the text size of the check boxes for user content and factory content based on a point size instead of a pixel size. --- data/themes/default/style.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/themes/default/style.css b/data/themes/default/style.css index a9646cfe4..52f60e4ed 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -9,7 +9,6 @@ QLabel, QTreeWidget, QListWidget, QGroupBox, QMenuBar { QTreeView { outline: none; - font-size: 12px; } QTreeWidget::item { @@ -42,7 +41,7 @@ QMdiArea { lmms--gui--FileBrowser QCheckBox { - font-size: 10px; + font-size: 8pt; color: white; } From d2a0780a6eca1fbddc7b3cd2dc050d283799883d Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Mon, 28 Aug 2023 20:12:09 +0200 Subject: [PATCH 04/29] Adjust classic style sheet Adjust the classic style sheet in the same way that the default style sheet was adjusted. The change also removes the usage of a bold font weight for selected menu items to achieve a more consistent feel. --- data/themes/classic/style.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 2880fe661..777dbc230 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -82,7 +82,6 @@ lmms--gui--TextFloat, lmms--gui--SimpleTextFloat { QMenu { border:1px solid #747474; background-color: #c9c9c9; - font-size:11px; } QMenu::separator { @@ -98,15 +97,12 @@ QMenu::item { QMenu::item:selected { color: white; - font-weight:bold; background-color: #747474; } QMenu::item:disabled { color: #747474; background-color: #c9c9c9; - font-size:12px; - font-weight: normal; padding: 4px 32px 4px 20px; } From b7196337a4fedec03566f39a220a42f154a5d806 Mon Sep 17 00:00:00 2001 From: Lost Robot <34612565+LostRobotMusic@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:43:23 -0700 Subject: [PATCH 05/29] Fix Compressor arithmetic exception in Debug mode (#6854) --- plugins/Compressor/Compressor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Compressor/Compressor.cpp b/plugins/Compressor/Compressor.cpp index 3c5ad6157..33571606e 100755 --- a/plugins/Compressor/Compressor.cpp +++ b/plugins/Compressor/Compressor.cpp @@ -320,8 +320,8 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames) float inputValue = feedback ? m_prevOut[i] : s[i]; // Calculate the crest factor of the audio by diving the peak by the RMS - m_crestPeakVal[i] = qMax(inputValue * inputValue, m_crestTimeConst * m_crestPeakVal[i] + (1 - m_crestTimeConst) * (inputValue * inputValue)); - m_crestRmsVal[i] = m_crestTimeConst * m_crestRmsVal[i] + ((1 - m_crestTimeConst) * (inputValue * inputValue)); + m_crestPeakVal[i] = qMax(qMax(COMP_NOISE_FLOOR, inputValue * inputValue), m_crestTimeConst * m_crestPeakVal[i] + (1 - m_crestTimeConst) * (inputValue * inputValue)); + m_crestRmsVal[i] = qMax(COMP_NOISE_FLOOR, m_crestTimeConst * m_crestRmsVal[i] + ((1 - m_crestTimeConst) * (inputValue * inputValue))); m_crestFactorVal[i] = m_crestPeakVal[i] / m_crestRmsVal[i]; m_rmsVal[i] = m_rmsTimeConst * m_rmsVal[i] + ((1 - m_rmsTimeConst) * (inputValue * inputValue)); From 10f1b21fb7ec2beeef42b43bef53e30fdb4e87eb Mon Sep 17 00:00:00 2001 From: consolegrl <5942029+consolegrl@users.noreply.github.com> Date: Thu, 7 Sep 2023 22:19:56 -0400 Subject: [PATCH 06/29] fixes #4049: Off-grid nodes not draggable (#6852) The quantPos arg of AutomationClip::setDragValue causes the node time to be quantized before its looked up in the timeMap iterator. This results in the node not being found and a new one being created inside the setDragValue function even though we had found one. Simply setting it to true causes this bug, simply setting it to false causes a new node to be created off grid/not snapped. So the fix is to quantize the position for the lookup only if we haven't found an existing node under the cursor. --- src/gui/editors/AutomationEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index a24165332..f98066bba 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -531,7 +531,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) : POS(clickedNode) ), level, - true, + clickedNode == tm.end(), mouseEvent->modifiers() & Qt::ControlModifier ); From b353cbea822b73ccef5f4f0ea7a22cf5e19162e5 Mon Sep 17 00:00:00 2001 From: saker Date: Fri, 8 Sep 2023 11:49:43 -0400 Subject: [PATCH 07/29] Add options to enable sanitizers (#6840) * Add options to enable sanitizers * Specify them as debugging options * Apply suggestions made by Dom * Fix linking issues There were linking issues, most likely because we were applying the sanitizer flags to unnecessary targets that are part of the build. Now, we only focus on adding the compiler flags to lmmsobjs as a PUBLIC dependency, and selectively choose what targets we need to apply the linker flags to as PRIVATE dependencies. * Add UBSan * Add status messages * Remove GNU as supported compiler for MSan * Revert to using add__options again * Fix sanitizer linkage within veal and cmt libraries I suspect that our sanitizer flags were removed for these two CMake targets. Instead of setting the properties directly, we call target_compile_options and target_link_options instead. * Remove TODO comment * Revert "Fix sanitizer linkage within veal and cmt libraries" This reverts commit b04dce8d8c0092353ec9fc462d237bac369a9eb6. * Use CMAKE__FLAGS_ and apply fixes * Remove quotes * Add support for additional flags * Disable vptr for UBSan * Print "Debug using" in status for clarity * Wrap ${supported_compilers} and ${status_flag} in quotes * Replace semicolons with spaces in additional_flags * Remove platform check for no-undefined flag The platform check was added as an attempt to make this branch compile with the veal and cmt libraries. However, it seems to work without it, so the problem must've been something else occuring in these files. * Undo change to FP exceptions status message * Use spaces instead of tabs * Remove -no-undefined flag for cmt and veal libraries --- CMakeLists.txt | 36 ++++++++++++++++++++++-- plugins/LadspaEffect/calf/CMakeLists.txt | 6 ++-- plugins/LadspaEffect/cmt/CMakeLists.txt | 6 ++-- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e20f13527..1b17d0e7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,10 @@ OPTION(WANT_VST_32 "Include 32-bit VST support" ON) OPTION(WANT_VST_64 "Include 64-bit VST support" ON) OPTION(WANT_WINMM "Include WinMM MIDI support" OFF) OPTION(WANT_DEBUG_FPE "Debug floating point exceptions" OFF) +option(WANT_DEBUG_ASAN "Enable AddressSanitizer" OFF) +option(WANT_DEBUG_TSAN "Enable ThreadSanitizer" OFF) +option(WANT_DEBUG_MSAN "Enable MemorySanitizer" OFF) +option(WANT_DEBUG_UBSAN "Enable UndefinedBehaviorSanitizer" OFF) OPTION(BUNDLE_QT_TRANSLATIONS "Install Qt translation files for LMMS" OFF) @@ -649,6 +653,31 @@ IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") ENDIF(WIN32) ENDIF() +# add enabled sanitizers +function(add_sanitizer sanitizer supported_compilers want_flag status_flag) + if(${want_flag}) + if(CMAKE_CXX_COMPILER_ID MATCHES "${supported_compilers}") + set("${status_flag}" "Enabled" PARENT_SCOPE) + string(REPLACE ";" " " additional_flags "${ARGN}") + # todo CMake 3.13: use add_compile_options/add_link_options instead + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fsanitize=${sanitizer} ${additional_flags}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=${sanitizer} ${additional_flags}" PARENT_SCOPE) + else() + set("${status_flag}" "Wanted but disabled due to unsupported compiler" PARENT_SCOPE) + endif() + else() + set("${status_flag}" "Disabled" PARENT_SCOPE) + endif() +endfunction() + +add_sanitizer(address "GNU|Clang|MSVC" WANT_DEBUG_ASAN STATUS_DEBUG_ASAN) +add_sanitizer(thread "GNU|Clang" WANT_DEBUG_TSAN STATUS_DEBUG_TSAN) +add_sanitizer(memory "Clang" WANT_DEBUG_MSAN STATUS_DEBUG_MSAN -fno-omit-frame-pointer) +# UBSan does not link with vptr enabled due to a problem with references from PeakControllerEffect +# not being found by PeakController +add_sanitizer(undefined "GNU|Clang" WANT_DEBUG_UBSAN STATUS_DEBUG_UBSAN -fno-sanitize=vptr) + + # use ccache include(CompileCache) @@ -717,7 +746,6 @@ ADD_CUSTOM_TARGET(uninstall COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}" -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/uninstall.cmake" ) - # # display configuration information # @@ -783,7 +811,11 @@ MESSAGE( MESSAGE( "Developer options\n" "-----------------------------------------\n" -"* Debug FP exceptions : ${STATUS_DEBUG_FPE}\n" +"* Debug FP exceptions : ${STATUS_DEBUG_FPE}\n" +"* Debug using AddressSanitizer : ${STATUS_DEBUG_ASAN}\n" +"* Debug using ThreadSanitizer : ${STATUS_DEBUG_TSAN}\n" +"* Debug using MemorySanitizer : ${STATUS_DEBUG_MSAN}\n" +"* Debug using UBSanitizer : ${STATUS_DEBUG_UBSAN}\n" ) MESSAGE( diff --git a/plugins/LadspaEffect/calf/CMakeLists.txt b/plugins/LadspaEffect/calf/CMakeLists.txt index 90f506417..0c9cd8fa9 100644 --- a/plugins/LadspaEffect/calf/CMakeLists.txt +++ b/plugins/LadspaEffect/calf/CMakeLists.txt @@ -36,9 +36,9 @@ TARGET_COMPILE_DEFINITIONS(veal PRIVATE DISABLE_OSC=1) SET(INLINE_FLAGS "") IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - SET(INLINE_FLAGS "-finline-functions-called-once -finline-limit=80") + SET(INLINE_FLAGS -finline-functions-called-once -finline-limit=80) ENDIF() -SET_TARGET_PROPERTIES(veal PROPERTIES COMPILE_FLAGS "-fexceptions -O2 -finline-functions ${INLINE_FLAGS}") +target_compile_options(veal PRIVATE -fexceptions -O2 -finline-functions ${INLINE_FLAGS}) if(LMMS_BUILD_WIN32) add_custom_command( @@ -50,5 +50,5 @@ if(LMMS_BUILD_WIN32) ) endif() IF(NOT LMMS_BUILD_APPLE AND NOT LMMS_BUILD_OPENBSD) - SET_TARGET_PROPERTIES(veal PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined") + target_link_libraries(veal PRIVATE -shared) ENDIF() diff --git a/plugins/LadspaEffect/cmt/CMakeLists.txt b/plugins/LadspaEffect/cmt/CMakeLists.txt index ded7b8958..75dba319d 100644 --- a/plugins/LadspaEffect/cmt/CMakeLists.txt +++ b/plugins/LadspaEffect/cmt/CMakeLists.txt @@ -5,7 +5,7 @@ ADD_LIBRARY(cmt MODULE ${SOURCES}) INSTALL(TARGETS cmt LIBRARY DESTINATION "${PLUGIN_DIR}/ladspa") SET_TARGET_PROPERTIES(cmt PROPERTIES PREFIX "") -SET_TARGET_PROPERTIES(cmt PROPERTIES COMPILE_FLAGS "-Wall -O3 -fno-strict-aliasing") +target_compile_options(cmt PRIVATE -Wall -O3 -fno-strict-aliasing) if(LMMS_BUILD_WIN32) add_custom_command( @@ -18,10 +18,10 @@ if(LMMS_BUILD_WIN32) endif() if(NOT LMMS_BUILD_WIN32) - set_target_properties(cmt PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS} -fPIC") + target_compile_options(cmt PRIVATE -fPIC) endif() IF(NOT LMMS_BUILD_APPLE AND NOT LMMS_BUILD_OPENBSD) - SET_TARGET_PROPERTIES(cmt PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined") + target_link_libraries(cmt PRIVATE -shared) ENDIF() From a8d765f8d57075217546282a55d3bb407512f4bf Mon Sep 17 00:00:00 2001 From: MrTopom Date: Sun, 10 Sep 2023 17:52:18 +0200 Subject: [PATCH 08/29] Make files visible when searching in ``FileBrowser`` (#6845) * Change the title for SideBarWidgets to be vertically centered related to icon and with no underlining * Update src/gui/SideBarWidget.cpp Co-authored-by: saker * Updating FileBrowser display when searching : showing matching files * Correcting increment and removing duplicated calls * Correcting reload tree when filter is activated --------- Co-authored-by: saker --- include/FileBrowser.h | 9 ++- src/gui/FileBrowser.cpp | 124 ++++++++++++++++++++++++++++------------ 2 files changed, 95 insertions(+), 38 deletions(-) diff --git a/include/FileBrowser.h b/include/FileBrowser.h index 3334a73f6..eafb827da 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -75,8 +75,7 @@ public: private slots: void reloadTree(); void expandItems( QTreeWidgetItem * item=nullptr, QList expandedDirs = QList() ); - // call with item=NULL to filter the entire tree - bool filterItems( const QString & filter, QTreeWidgetItem * item=nullptr ); + bool filterAndExpandItems(const QString & filter, QTreeWidgetItem * item = nullptr); void giveFocusToFilter(); private: @@ -84,6 +83,9 @@ private: void addItems( const QString & path ); + void saveDirectoriesStates(); + void restoreDirectoriesStates(); + FileBrowserTreeWidget * m_fileBrowserTreeWidget; QLineEdit * m_filterEdit; @@ -99,6 +101,8 @@ private: QCheckBox* m_showFactoryContent = nullptr; QString m_userDir; QString m_factoryDir; + QList m_savedExpandedDirs; + QString m_previousFilterValue; } ; @@ -115,7 +119,6 @@ public: //! that are expanded in the tree. QList expandedDirs( QTreeWidgetItem * item = nullptr ) const; - protected: void contextMenuEvent( QContextMenuEvent * e ) override; void mousePressEvent( QMouseEvent * me ) override; diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index c0763d542..181e67cd7 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -23,7 +23,6 @@ * */ - #include #include #include @@ -126,7 +125,7 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter, m_filterEdit->setPlaceholderText( tr("Search") ); m_filterEdit->setClearButtonEnabled( true ); connect( m_filterEdit, SIGNAL( textEdited( const QString& ) ), - this, SLOT( filterItems( const QString& ) ) ); + this, SLOT( filterAndExpandItems( const QString& ) ) ); auto reload_btn = new QPushButton(embed::getIconPixmap("reload"), QString(), searchWidget); reload_btn->setToolTip( tr( "Refresh list" ) ); @@ -145,53 +144,95 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter, auto filterFocusShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this, SLOT(giveFocusToFilter())); filterFocusShortcut->setContext(Qt::WidgetWithChildrenShortcut); + m_previousFilterValue = ""; + reloadTree(); show(); } -bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item ) +void FileBrowser::saveDirectoriesStates() +{ + m_savedExpandedDirs = m_fileBrowserTreeWidget->expandedDirs(); +} + +void FileBrowser::restoreDirectoriesStates() { - // call with item=NULL to filter the entire tree + expandItems(nullptr, m_savedExpandedDirs); +} + +bool FileBrowser::filterAndExpandItems(const QString & filter, QTreeWidgetItem * item) +{ + // Call with item = nullptr to filter the entire tree + + if (item == nullptr) + { + // First search character so need to save current expanded directories + if (m_previousFilterValue.isEmpty()) + { + saveDirectoriesStates(); + } + + m_previousFilterValue = filter; + } + + if (filter.isEmpty()) + { + // Restore previous expanded directories + if (item == nullptr) + { + restoreDirectoriesStates(); + } + + return false; + } + bool anyMatched = false; int numChildren = item ? item->childCount() : m_fileBrowserTreeWidget->topLevelItemCount(); - for( int i = 0; i < numChildren; ++i ) + + for (int i = 0; i < numChildren; ++i) { QTreeWidgetItem * it = item ? item->child( i ) : m_fileBrowserTreeWidget->topLevelItem(i); - // is directory? - if( it->childCount() ) + auto d = dynamic_cast(it); + if (d) { - // matches filter? - if( it->text( 0 ). - contains( filter, Qt::CaseInsensitive ) ) + if (it->text(0).contains(filter, Qt::CaseInsensitive)) { - // yes, then show everything below - it->setHidden( false ); - filterItems( QString(), it ); + it->setHidden(false); + it->setExpanded(true); + filterAndExpandItems(QString(), it); anyMatched = true; } else { - // only show if item below matches filter - bool didMatch = filterItems( filter, it ); - it->setHidden( !didMatch ); + // Expanding is required when recursive to load in its contents, even if it's collapsed right afterward + it->setExpanded(true); + + bool didMatch = filterAndExpandItems(filter, it); + it->setHidden(!didMatch); + it->setExpanded(didMatch); anyMatched = anyMatched || didMatch; } } - // a standard item (i.e. no file or directory item?) - else if( it->type() == QTreeWidgetItem::Type ) - { - // hide if there's any filter - it->setHidden( !filter.isEmpty() ); - } + else { - // file matches filter? - bool didMatch = it->text( 0 ). - contains( filter, Qt::CaseInsensitive ); - it->setHidden( !didMatch ); - anyMatched = anyMatched || didMatch; + auto f = dynamic_cast(it); + if (f) + { + // File + bool didMatch = it->text(0).contains(filter, Qt::CaseInsensitive); + it->setHidden(!didMatch); + anyMatched = anyMatched || didMatch; + } + + // A standard item (i.e. no file or directory item?) + else + { + // Hide if there's any filter + it->setHidden(!filter.isEmpty()); + } } } @@ -201,15 +242,20 @@ bool FileBrowser::filterItems( const QString & filter, QTreeWidgetItem * item ) void FileBrowser::reloadTree() { - QList expandedDirs = m_fileBrowserTreeWidget->expandedDirs(); - const QString text = m_filterEdit->text(); - m_filterEdit->clear(); + if (m_filterEdit->text().isEmpty()) + { + saveDirectoriesStates(); + } + m_fileBrowserTreeWidget->clear(); + QStringList paths = m_directories.split('*'); + if (m_showUserContent && !m_showUserContent->isChecked()) { paths.removeAll(m_userDir); } + if (m_showFactoryContent && !m_showFactoryContent->isChecked()) { paths.removeAll(m_factoryDir); @@ -222,9 +268,15 @@ void FileBrowser::reloadTree() addItems(path); } } - expandItems(nullptr, expandedDirs); - m_filterEdit->setText( text ); - filterItems( text ); + + if (m_filterEdit->text().isEmpty()) + { + restoreDirectoriesStates(); + } + else + { + filterAndExpandItems(m_filterEdit->text()); + } } @@ -240,12 +292,16 @@ void FileBrowser::expandItems(QTreeWidgetItem* item, QList expandedDirs { // Expanding is required when recursive to load in its contents, even if it's collapsed right afterward if (m_recurse) { d->setExpanded(true); } + d->setExpanded(expandedDirs.contains(d->fullName())); + if (m_recurse && it->childCount()) { expandItems(it, expandedDirs); } } + + it->setHidden(false); } } @@ -416,8 +472,6 @@ QList FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) con } - - void FileBrowserTreeWidget::keyPressEvent(QKeyEvent * ke ) { // Shorter names for some commonly used properties of the event From 296d5736c8371e365a877f14de8bd45f57ff720f Mon Sep 17 00:00:00 2001 From: Babakinha <59146844+Babakinha@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:57:36 -0300 Subject: [PATCH 09/29] Remember to check for Linux VST Effects (#6751) Remember to also check .so files and not only .dll --- plugins/VstEffect/VstSubPluginFeatures.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/VstEffect/VstSubPluginFeatures.cpp b/plugins/VstEffect/VstSubPluginFeatures.cpp index f929b5526..7eab7a9bf 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.cpp +++ b/plugins/VstEffect/VstSubPluginFeatures.cpp @@ -82,7 +82,11 @@ void VstSubPluginFeatures::addPluginsFromDir( QStringList* filenames, QString pa } } QStringList dlls = QDir( ConfigManager::inst()->vstDir() + path ). - entryList( QStringList() << "*.dll", + entryList( QStringList() << "*.dll" +#ifdef LMMS_BUILD_LINUX + << "*.so" +#endif + , QDir::Files, QDir::Name ); for( int i = 0; i < dlls.size(); i++ ) { From b64912cd57db7bfa2692be42bfa904adc1e72b65 Mon Sep 17 00:00:00 2001 From: consolegrl <5942029+consolegrl@users.noreply.github.com> Date: Tue, 12 Sep 2023 14:35:54 -0400 Subject: [PATCH 10/29] fixes #6354: Sample and Hold for LFO Controller (#6850) * fixes #6354: Sample and Hold for LFO Controller LFO controller's "white noise" wave shape didn't respect the frequency knob at all, so Sample-and-Hold was added to extend the functionality of the LFO Controller with this random waveshape. The original functionallity can still be accessed by setting the FREQ knob to minimum (0.01) --------- Co-authored-by: Kevin Zander Co-authored-by: saker --- include/DataFile.h | 1 + include/LfoController.h | 1 + src/core/DataFile.cpp | 18 ++++++++++++++++++ src/core/LfoController.cpp | 34 ++++++++++++++++++++++++++++++---- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/include/DataFile.h b/include/DataFile.h index 137f0156f..c19a00f7f 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -126,6 +126,7 @@ private: void upgrade_defaultTripleOscillatorHQ(); void upgrade_mixerRename(); void upgrade_bbTcoRename(); + void upgrade_sampleAndHold(); // List of all upgrade methods static const std::vector UPGRADE_METHODS; diff --git a/include/LfoController.h b/include/LfoController.h index 1c63ba698..109edbd3f 100644 --- a/include/LfoController.h +++ b/include/LfoController.h @@ -86,6 +86,7 @@ protected: sample_t (*m_sampleFunction)( const float ); private: + float m_heldSample; SampleBuffer * m_userDefSampleBuffer; protected slots: diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 5c98ec81c..d237d13ae 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -79,6 +79,7 @@ const std::vector DataFile::UPGRADE_METHODS = { &DataFile::upgrade_automationNodes , &DataFile::upgrade_extendedNoteRange, &DataFile::upgrade_defaultTripleOscillatorHQ, &DataFile::upgrade_mixerRename , &DataFile::upgrade_bbTcoRename, + &DataFile::upgrade_sampleAndHold , }; // Vector of all versions that have upgrade routines. @@ -1762,6 +1763,23 @@ void DataFile::upgrade_bbTcoRename() } +// Set LFO speed to 0.01 on projects made before sample-and-hold PR +void DataFile::upgrade_sampleAndHold() +{ + QDomNodeList elements = elementsByTagName("lfocontroller"); + for (int i = 0; i < elements.length(); ++i) + { + if (elements.item(i).isNull()) { continue; } + auto e = elements.item(i).toElement(); + // Correct old random wave LFO speeds + if (e.attribute("wave").toInt() == 6) + { + e.setAttribute("speed",0.01f); + } + } +} + + void DataFile::upgrade() { // Runs all necessary upgrade methods diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 23621b847..88f64803c 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -88,6 +88,7 @@ void LfoController::updateValueBuffer() { m_phaseOffset = m_phaseModel.value() / 360.0; float phase = m_currentPhase + m_phaseOffset; + float phasePrev = 0.0f; // roll phase up until we're in sync with period counter m_bufferLastUpdated++; @@ -102,20 +103,45 @@ void LfoController::updateValueBuffer() ValueBuffer *amountBuffer = m_amountModel.valueBuffer(); int amountInc = amountBuffer ? 1 : 0; float *amountPtr = amountBuffer ? &(amountBuffer->values()[ 0 ] ) : &amount; + Oscillator::WaveShape waveshape = static_cast(m_waveModel.value()); for( float& f : m_valueBuffer ) { - const float currentSample = m_sampleFunction != nullptr - ? m_sampleFunction( phase ) - : m_userDefSampleBuffer->userWaveSample( phase ); + float currentSample = 0; + switch (waveshape) + { + case Oscillator::WaveShape::WhiteNoise: + { + if (absFraction(phase) < absFraction(phasePrev)) + { + // Resample when phase period has completed + m_heldSample = m_sampleFunction(phase); + } + currentSample = m_heldSample; + break; + } + case Oscillator::WaveShape::UserDefined: + { + currentSample = m_userDefSampleBuffer->userWaveSample(phase); + break; + } + default: + { + if (m_sampleFunction != nullptr) + { + currentSample = m_sampleFunction(phase); + } + } + } f = std::clamp(m_baseModel.value() + (*amountPtr * currentSample / 2.0f), 0.0f, 1.0f); + phasePrev = phase; phase += 1.0 / m_duration; amountPtr += amountInc; } - m_currentPhase = absFraction( phase - m_phaseOffset ); + m_currentPhase = absFraction(phase - m_phaseOffset); m_bufferLastUpdated = s_periods; } From 89baab6b87df8401cea43118a067573c1e99d303 Mon Sep 17 00:00:00 2001 From: MrTopom Date: Tue, 12 Sep 2023 23:30:31 +0200 Subject: [PATCH 11/29] FileDialog : Adding mounted volumes on Linux (#6834) * FileDialog : Adding mounted volumes on Linux * Adding some file systems types and collapsing 2 'if' statements in one * Removing the foreach * Correcting PREPROCESSOR directive --- src/gui/modals/FileDialog.cpp | 58 ++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/src/gui/modals/FileDialog.cpp b/src/gui/modals/FileDialog.cpp index 512d7179f..a6cf4827a 100644 --- a/src/gui/modals/FileDialog.cpp +++ b/src/gui/modals/FileDialog.cpp @@ -26,11 +26,12 @@ #include #include #include +#include +#include #include "ConfigManager.h" #include "FileDialog.h" - namespace lmms::gui { @@ -45,19 +46,38 @@ FileDialog::FileDialog( QWidget *parent, const QString &caption, setOption( QFileDialog::DontUseNativeDialog ); - // Add additional locations to the sidebar +#ifdef LMMS_BUILD_LINUX + QList urls; +#else QList urls = sidebarUrls(); - urls << QUrl::fromLocalFile( QStandardPaths::writableLocation( QStandardPaths::DesktopLocation ) ); - // Find downloads directory - QDir downloadDir( QDir::homePath() + "/Downloads" ); - if ( ! downloadDir.exists() ) - downloadDir.setPath(QStandardPaths::writableLocation( QStandardPaths::DownloadLocation )); - if ( downloadDir.exists() ) - urls << QUrl::fromLocalFile( downloadDir.absolutePath() ); +#endif - urls << QUrl::fromLocalFile( QStandardPaths::writableLocation( QStandardPaths::MusicLocation ) ); - urls << QUrl::fromLocalFile( ConfigManager::inst()->workingDir() ); + QDir desktopDir; + desktopDir.setPath(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); + if (desktopDir.exists()) + { + urls << QUrl::fromLocalFile(desktopDir.absolutePath()); + } + + QDir downloadDir(QDir::homePath() + "/Downloads"); + if (!downloadDir.exists()) + { + downloadDir.setPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); + } + if (downloadDir.exists()) + { + urls << QUrl::fromLocalFile(downloadDir.absolutePath()); + } + QDir musicDir; + musicDir.setPath(QStandardPaths::writableLocation(QStandardPaths::MusicLocation)); + if (musicDir.exists()) + { + urls << QUrl::fromLocalFile(musicDir.absolutePath()); + } + + urls << QUrl::fromLocalFile(ConfigManager::inst()->workingDir()); + // Add `/Volumes` directory on OS X systems, this allows the user to browse // external disk drives. #ifdef LMMS_BUILD_APPLE @@ -66,6 +86,22 @@ FileDialog::FileDialog( QWidget *parent, const QString &caption, urls << QUrl::fromLocalFile( volumesDir.absolutePath() ); #endif +#ifdef LMMS_BUILD_LINUX + + // FileSystem types : https://www.javatpoint.com/linux-file-system + QStringList usableFileSystems = {"ext", "ext2", "ext3", "ext4", "jfs", "reiserfs", "ntfs3", "fuse.sshfs", "fuseblk"}; + + for(QStorageInfo storage : QStorageInfo::mountedVolumes()) + { + storage.refresh(); + + if (usableFileSystems.contains(QString(storage.fileSystemType()), Qt::CaseInsensitive) && storage.isValid() && storage.isReady()) + { + urls << QUrl::fromLocalFile(storage.rootPath()); + } + } +#endif + setSidebarUrls(urls); } From 3a0e68c0ac93a743cedca379c66de00d13647d69 Mon Sep 17 00:00:00 2001 From: consolegrl <5942029+consolegrl@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:08:06 -0400 Subject: [PATCH 12/29] fixes #6736: Sample tracks lose FX Channel (#6851) * fixes #6736: Sample tracks lose FX Channel * Styling fixes * Restyled remaining blocks of upgrade routine * Fixed hang due to sleepy typo * Update src/core/DataFile.cpp Co-authored-by: saker --------- Co-authored-by: saker --- src/core/DataFile.cpp | 46 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index d237d13ae..5915b25ef 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -1704,26 +1704,56 @@ void DataFile::upgrade_mixerRename() { // Change nodename to QDomNodeList fxmixer = elementsByTagName("fxmixer"); - for (int i = 0; !fxmixer.item(i).isNull(); ++i) + for (int i = 0; i < fxmixer.length(); ++i) { - fxmixer.item(i).toElement().setTagName("mixer"); + auto item = fxmixer.item(i).toElement(); + if (item.isNull()) + { + continue; + } + item.setTagName("mixer"); } // Change nodename to QDomNodeList fxchannel = elementsByTagName("fxchannel"); - for (int i = 0; !fxchannel.item(i).isNull(); ++i) + for (int i = 0; i < fxchannel.length(); ++i) { - fxchannel.item(i).toElement().setTagName("mixerchannel"); + auto item = fxchannel.item(i).toElement(); + if (item.isNull()) + { + continue; + } + item.setTagName("mixerchannel"); } // Change the attribute fxch of element to mixch QDomNodeList fxch = elementsByTagName("instrumenttrack"); - for(int i = 0; !fxch.item(i).isNull(); ++i) + for (int i = 0; i < fxch.length(); ++i) { - if(fxch.item(i).toElement().hasAttribute("fxch")) + auto item = fxch.item(i).toElement(); + if (item.isNull()) { - fxch.item(i).toElement().setAttribute("mixch", fxch.item(i).toElement().attribute("fxch")); - fxch.item(i).toElement().removeAttribute("fxch"); + continue; + } + if (item.hasAttribute("fxch")) + { + item.setAttribute("mixch", item.attribute("fxch")); + item.removeAttribute("fxch"); + } + } + // Change the attribute fxch of element to mixch + fxch = elementsByTagName("sampletrack"); + for (int i = 0; i < fxch.length(); ++i) + { + auto item = fxch.item(i).toElement(); + if (item.isNull()) + { + continue; + } + if (item.hasAttribute("fxch")) + { + item.setAttribute("mixch", item.attribute("fxch")); + item.removeAttribute("fxch"); } } } From 9b1933e544697bfa67d337ed12e0501d66a66ec0 Mon Sep 17 00:00:00 2001 From: Maxwell Voorhes <100326501+mvoorhes@users.noreply.github.com> Date: Thu, 14 Sep 2023 08:17:36 -0700 Subject: [PATCH 13/29] Added flats to array of keys (#6865) --- src/gui/editors/PianoRoll.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index b38335995..751fb9d2a 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -130,7 +130,7 @@ QPixmap* PianoRoll::s_toolKnife = nullptr; SimpleTextFloat * PianoRoll::s_textFloat = nullptr; -static std::array s_noteStrings {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; +static std::array s_noteStrings {"C", "C# / D\u266D", "D", "D# / E\u266D", "E", "F", "F# / G\u266D", "G", "G# / A\u266D", "A", "A# / B\u266D", "B"}; static QString getNoteString(int key) { From d3d710e0adac3798ae65f1e2b34fd582e4c02bc6 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Thu, 14 Sep 2023 23:12:22 +0200 Subject: [PATCH 14/29] Remove identical calls to `InstrumentTrack::processAudioBuffer` Remove identical calls to `InstrumentTrack::processAudioBuffer` which appear in the overridden implementations of `Instrument::play` and `Instrument::playNotes`. Instead the call to `processAudioBuffer` has been moved into `InstrumentPlayHandle::play` and `InstrumentTrack::playNote`. These two methods call the aforementioned methods of `Instrument`. Especially in the case of `InstrumentTrack::playNote` the previous implementation resulted in some unncessary "ping pong" where `InstrumentTrack` called a method on an `Instrument` which then in turn called a method on `InstrumentTrack`. And this was done in almost every instrument. In `InstrumentTrack::playNote` an additional check was added which only calls `processAudioBuffer` if the buffer is not `nullptr`. The reason is that under certain circumstances `PlayHandle::doProcessing` calls the `play` method by explicitly passing a `nullptr` as the buffer. This behavior was added with commit 7bc97f5d5bd. Because it is unknown if this was done for some side effects the code was adjusted so that it behaves identical in this case. Move the complex implementation for `InstrumentPlayHandle::play` and `InstrumentPlayHandle::isFromTrack` into the cpp file and optimize the includes. --- include/InstrumentPlayHandle.h | 38 +++--------------- .../AudioFileProcessor/AudioFileProcessor.cpp | 3 -- plugins/BitInvader/BitInvader.cpp | 2 - plugins/CarlaBase/Carla.cpp | 3 -- plugins/FreeBoy/FreeBoy.cpp | 1 - plugins/GigPlayer/GigPlayer.cpp | 2 - plugins/Kicker/Kicker.cpp | 2 - plugins/Lb302/Lb302.cpp | 1 - plugins/Lv2Instrument/Lv2Instrument.cpp | 2 - plugins/Monstro/Monstro.cpp | 2 - plugins/Nes/Nes.cpp | 2 - plugins/OpulenZ/OpulenZ.cpp | 4 -- plugins/Organic/Organic.cpp | 2 - plugins/Patman/Patman.cpp | 2 - plugins/Sf2Player/Sf2Player.cpp | 2 - plugins/Sfxr/Sfxr.cpp | 3 -- plugins/Sid/SidInstrument.cpp | 2 - plugins/Stk/Mallets/Mallets.cpp | 2 - plugins/TripleOscillator/TripleOscillator.cpp | 2 - plugins/Vestige/Vestige.cpp | 2 - plugins/Vibed/Vibed.cpp | 2 - plugins/Watsyn/Watsyn.cpp | 2 - plugins/Xpressive/Xpressive.cpp | 2 - plugins/ZynAddSubFx/ZynAddSubFx.cpp | 1 - src/core/InstrumentPlayHandle.cpp | 39 +++++++++++++++++++ src/tracks/InstrumentTrack.cpp | 7 ++++ 26 files changed, 51 insertions(+), 81 deletions(-) diff --git a/include/InstrumentPlayHandle.h b/include/InstrumentPlayHandle.h index bbf53d16c..1455134e6 100644 --- a/include/InstrumentPlayHandle.h +++ b/include/InstrumentPlayHandle.h @@ -26,13 +26,14 @@ #define LMMS_INSTRUMENT_PLAY_HANDLE_H #include "PlayHandle.h" -#include "Instrument.h" -#include "NotePlayHandle.h" #include "lmms_export.h" namespace lmms { +class Instrument; +class InstrumentTrack; + class LMMS_EXPORT InstrumentPlayHandle : public PlayHandle { public: @@ -40,46 +41,17 @@ public: ~InstrumentPlayHandle() override = default; - - void play( sampleFrame * _working_buffer ) override - { - // ensure that all our nph's have been processed first - ConstNotePlayHandleList nphv = NotePlayHandle::nphsOfInstrumentTrack( m_instrument->instrumentTrack(), true ); - - bool nphsLeft; - do - { - nphsLeft = false; - for( const NotePlayHandle * constNotePlayHandle : nphv ) - { - NotePlayHandle * notePlayHandle = const_cast( constNotePlayHandle ); - if( notePlayHandle->state() != ThreadableJob::ProcessingState::Done && - !notePlayHandle->isFinished()) - { - nphsLeft = true; - notePlayHandle->process(); - } - } - } - while( nphsLeft ); - - m_instrument->play( _working_buffer ); - } + void play( sampleFrame * _working_buffer ) override; bool isFinished() const override { return false; } - bool isFromTrack( const Track* _track ) const override - { - return m_instrument->isFromTrack( _track ); - } - + bool isFromTrack( const Track* _track ) const override; private: Instrument* m_instrument; - } ; diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index a941e773f..667102207 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -171,9 +171,6 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, static_cast( m_loopModel.value() ) ) ) { applyRelease( _working_buffer, _n ); - instrumentTrack()->processAudioBuffer( _working_buffer, - frames + offset, _n ); - emit isPlaying( ((handleState *)_n->m_pluginData)->frameIndex() ); } else diff --git a/plugins/BitInvader/BitInvader.cpp b/plugins/BitInvader/BitInvader.cpp index 98ef1e97c..4ea73dc71 100644 --- a/plugins/BitInvader/BitInvader.cpp +++ b/plugins/BitInvader/BitInvader.cpp @@ -307,8 +307,6 @@ void BitInvader::playNote( NotePlayHandle * _n, } applyRelease( _working_buffer, _n ); - - instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/CarlaBase/Carla.cpp b/plugins/CarlaBase/Carla.cpp index faff94b57..819736e92 100644 --- a/plugins/CarlaBase/Carla.cpp +++ b/plugins/CarlaBase/Carla.cpp @@ -508,7 +508,6 @@ void CarlaInstrument::play(sampleFrame* workingBuffer) if (fHandle == nullptr) { - instrumentTrack()->processAudioBuffer(workingBuffer, bufsize, nullptr); return; } @@ -556,8 +555,6 @@ void CarlaInstrument::play(sampleFrame* workingBuffer) workingBuffer[i][0] = buf1[i]; workingBuffer[i][1] = buf2[i]; } - - instrumentTrack()->processAudioBuffer(workingBuffer, bufsize, nullptr); } bool CarlaInstrument::handleMidiEvent(const MidiEvent& event, const TimePos&, f_cnt_t offset) diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index 6450253ee..f2dc95699 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -419,7 +419,6 @@ void FreeBoyInstrument::playNote(NotePlayHandle* nph, sampleFrame* workingBuffer } framesLeft -= count; } - instrumentTrack()->processAudioBuffer(workingBuffer, frames + offset, nph); } diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index c2e155a20..0713d3100 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -494,8 +494,6 @@ void GigInstrument::play( sampleFrame * _working_buffer ) _working_buffer[i][0] *= m_gain.value(); _working_buffer[i][1] *= m_gain.value(); } - - instrumentTrack()->processAudioBuffer( _working_buffer, frames, nullptr ); } diff --git a/plugins/Kicker/Kicker.cpp b/plugins/Kicker/Kicker.cpp index ef1d623c1..e6418e2da 100644 --- a/plugins/Kicker/Kicker.cpp +++ b/plugins/Kicker/Kicker.cpp @@ -197,8 +197,6 @@ void KickerInstrument::playNote( NotePlayHandle * _n, _working_buffer[f+offset][1] *= fac; } } - - instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/Lb302/Lb302.cpp b/plugins/Lb302/Lb302.cpp index b8fff2c0b..ee49442d5 100644 --- a/plugins/Lb302/Lb302.cpp +++ b/plugins/Lb302/Lb302.cpp @@ -790,7 +790,6 @@ void Lb302Synth::play( sampleFrame * _working_buffer ) const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); process( _working_buffer, frames ); - instrumentTrack()->processAudioBuffer( _working_buffer, frames, nullptr ); // release_frame = 0; //removed for issue # 1432 } diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 1e45f4e91..32f81d23c 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -197,8 +197,6 @@ void Lv2Instrument::play(sampleFrame *buf) copyModelsToLmms(); copyBuffersToLmms(buf, fpp); - - instrumentTrack()->processAudioBuffer(buf, fpp, nullptr); } diff --git a/plugins/Monstro/Monstro.cpp b/plugins/Monstro/Monstro.cpp index f588d6b78..2201e4ed9 100644 --- a/plugins/Monstro/Monstro.cpp +++ b/plugins/Monstro/Monstro.cpp @@ -1040,8 +1040,6 @@ void MonstroInstrument::playNote( NotePlayHandle * _n, ms->renderOutput( frames, _working_buffer + offset ); //applyRelease( _working_buffer, _n ); // we have our own release - - instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } void MonstroInstrument::deleteNotePluginData( NotePlayHandle * _n ) diff --git a/plugins/Nes/Nes.cpp b/plugins/Nes/Nes.cpp index a530ac19b..47122a0c6 100644 --- a/plugins/Nes/Nes.cpp +++ b/plugins/Nes/Nes.cpp @@ -561,8 +561,6 @@ void NesInstrument::playNote( NotePlayHandle * n, sampleFrame * workingBuffer ) nes->renderOutput( workingBuffer + offset, frames ); applyRelease( workingBuffer, n ); - - instrumentTrack()->processAudioBuffer( workingBuffer, frames + offset, n ); } diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index 64f609995..d90d5f343 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -412,10 +412,6 @@ void OpulenzInstrument::play( sampleFrame * _working_buffer ) } } emulatorMutex.unlock(); - - // Throw the data to the track... - instrumentTrack()->processAudioBuffer( _working_buffer, frameCount, nullptr ); - } diff --git a/plugins/Organic/Organic.cpp b/plugins/Organic/Organic.cpp index f8a2b0d13..a70da6421 100644 --- a/plugins/Organic/Organic.cpp +++ b/plugins/Organic/Organic.cpp @@ -312,8 +312,6 @@ void OrganicInstrument::playNote( NotePlayHandle * _n, } // -- -- - - instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/Patman/Patman.cpp b/plugins/Patman/Patman.cpp index a2b829940..668c7f461 100644 --- a/plugins/Patman/Patman.cpp +++ b/plugins/Patman/Patman.cpp @@ -157,8 +157,6 @@ void PatmanInstrument::playNote( NotePlayHandle * _n, play_freq, m_loopedModel.value() ? SampleBuffer::LoopMode::On : SampleBuffer::LoopMode::Off ) ) { applyRelease( _working_buffer, _n ); - instrumentTrack()->processAudioBuffer( _working_buffer, - frames + offset, _n ); } else { diff --git a/plugins/Sf2Player/Sf2Player.cpp b/plugins/Sf2Player/Sf2Player.cpp index d46af5e4f..79bd4b976 100644 --- a/plugins/Sf2Player/Sf2Player.cpp +++ b/plugins/Sf2Player/Sf2Player.cpp @@ -848,7 +848,6 @@ void Sf2Instrument::play( sampleFrame * _working_buffer ) if( m_playingNotes.isEmpty() ) { renderFrames( frames, _working_buffer ); - instrumentTrack()->processAudioBuffer( _working_buffer, frames, nullptr ); return; } @@ -906,7 +905,6 @@ void Sf2Instrument::play( sampleFrame * _working_buffer ) { renderFrames( frames - currentFrame, _working_buffer + currentFrame ); } - instrumentTrack()->processAudioBuffer( _working_buffer, frames, nullptr ); } diff --git a/plugins/Sfxr/Sfxr.cpp b/plugins/Sfxr/Sfxr.cpp index fc39ea0fa..e79b8e2ad 100644 --- a/plugins/Sfxr/Sfxr.cpp +++ b/plugins/Sfxr/Sfxr.cpp @@ -480,9 +480,6 @@ void SfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe delete[] pitchedBuffer; applyRelease( _working_buffer, _n ); - - instrumentTrack()->processAudioBuffer( _working_buffer, frameNum + offset, _n ); - } diff --git a/plugins/Sid/SidInstrument.cpp b/plugins/Sid/SidInstrument.cpp index f663c3b69..143003f98 100644 --- a/plugins/Sid/SidInstrument.cpp +++ b/plugins/Sid/SidInstrument.cpp @@ -429,8 +429,6 @@ void SidInstrument::playNote( NotePlayHandle * _n, _working_buffer[frame+offset][ch] = s; } } - - instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/Stk/Mallets/Mallets.cpp b/plugins/Stk/Mallets/Mallets.cpp index 4fb077de5..b746e9491 100644 --- a/plugins/Stk/Mallets/Mallets.cpp +++ b/plugins/Stk/Mallets/Mallets.cpp @@ -359,8 +359,6 @@ void MalletsInstrument::playNote( NotePlayHandle * _n, _working_buffer[frame][1] = ps->nextSampleRight() * ( m_scalers[p] + add_scale ); } - - instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/TripleOscillator/TripleOscillator.cpp b/plugins/TripleOscillator/TripleOscillator.cpp index f2340d3d6..5b8f6e8ad 100644 --- a/plugins/TripleOscillator/TripleOscillator.cpp +++ b/plugins/TripleOscillator/TripleOscillator.cpp @@ -380,8 +380,6 @@ void TripleOscillator::playNote( NotePlayHandle * _n, applyFadeIn(_working_buffer, _n); applyRelease( _working_buffer, _n ); - - instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp index dd8e9cbef..aff5b7fdc 100644 --- a/plugins/Vestige/Vestige.cpp +++ b/plugins/Vestige/Vestige.cpp @@ -409,8 +409,6 @@ void VestigeInstrument::play( sampleFrame * _buf ) m_plugin->process( nullptr, _buf ); - instrumentTrack()->processAudioBuffer( _buf, frames, nullptr ); - m_pluginMutex.unlock(); } diff --git a/plugins/Vibed/Vibed.cpp b/plugins/Vibed/Vibed.cpp index 3ed51fe79..ad6a3942a 100644 --- a/plugins/Vibed/Vibed.cpp +++ b/plugins/Vibed/Vibed.cpp @@ -251,8 +251,6 @@ void Vibed::playNote(NotePlayHandle* n, sampleFrame* workingBuffer) } } } - - instrumentTrack()->processAudioBuffer(workingBuffer, frames + offset, n); } void Vibed::deleteNotePluginData(NotePlayHandle* n) diff --git a/plugins/Watsyn/Watsyn.cpp b/plugins/Watsyn/Watsyn.cpp index 7603a9c1b..8e49942e1 100644 --- a/plugins/Watsyn/Watsyn.cpp +++ b/plugins/Watsyn/Watsyn.cpp @@ -445,8 +445,6 @@ void WatsynInstrument::playNote( NotePlayHandle * _n, } applyRelease( _working_buffer, _n ); - - instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); } diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index b1a17a1ce..babc37231 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -233,8 +233,6 @@ void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) { const f_cnt_t offset = nph->noteOffset(); ps->renderOutput(frames, working_buffer + offset); - - instrumentTrack()->processAudioBuffer(working_buffer, frames + offset, nph); } void Xpressive::deleteNotePluginData(NotePlayHandle* nph) { diff --git a/plugins/ZynAddSubFx/ZynAddSubFx.cpp b/plugins/ZynAddSubFx/ZynAddSubFx.cpp index 2ec864592..be38bcb79 100644 --- a/plugins/ZynAddSubFx/ZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/ZynAddSubFx.cpp @@ -341,7 +341,6 @@ void ZynAddSubFxInstrument::play( sampleFrame * _buf ) m_plugin->processAudio( _buf ); } m_pluginMutex.unlock(); - instrumentTrack()->processAudioBuffer( _buf, Engine::audioEngine()->framesPerPeriod(), nullptr ); } diff --git a/src/core/InstrumentPlayHandle.cpp b/src/core/InstrumentPlayHandle.cpp index e1a9d9d65..30f5fde38 100644 --- a/src/core/InstrumentPlayHandle.cpp +++ b/src/core/InstrumentPlayHandle.cpp @@ -24,7 +24,10 @@ #include "InstrumentPlayHandle.h" +#include "Instrument.h" #include "InstrumentTrack.h" +#include "Engine.h" +#include "AudioEngine.h" namespace lmms { @@ -37,5 +40,41 @@ InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentT setAudioPort( instrumentTrack->audioPort() ); } +void InstrumentPlayHandle::play( sampleFrame * _working_buffer ) +{ + InstrumentTrack * instrumentTrack = m_instrument->instrumentTrack(); + + // ensure that all our nph's have been processed first + ConstNotePlayHandleList nphv = NotePlayHandle::nphsOfInstrumentTrack(instrumentTrack, true ); + + bool nphsLeft; + do + { + nphsLeft = false; + for( const NotePlayHandle * constNotePlayHandle : nphv ) + { + NotePlayHandle * notePlayHandle = const_cast( constNotePlayHandle ); + if( notePlayHandle->state() != ThreadableJob::ProcessingState::Done && + !notePlayHandle->isFinished()) + { + nphsLeft = true; + notePlayHandle->process(); + } + } + } + while( nphsLeft ); + + m_instrument->play( _working_buffer ); + + // Process the audio buffer that the instrument has just worked on... + const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); + instrumentTrack->processAudioBuffer(_working_buffer, frames, nullptr); +} + +bool InstrumentPlayHandle::isFromTrack( const Track* _track ) const +{ + return m_instrument->isFromTrack( _track ); +} + } // namespace lmms \ No newline at end of file diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index a4de188a5..c9b87a4f2 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -579,6 +579,13 @@ void InstrumentTrack::playNote( NotePlayHandle* n, sampleFrame* workingBuffer ) { // all is done, so now lets play the note! m_instrument->playNote( n, workingBuffer ); + + if (workingBuffer != nullptr) + { + const fpp_t frames = n->framesLeftForCurrentPeriod(); + const f_cnt_t offset = n->noteOffset(); + this->processAudioBuffer(workingBuffer, frames + offset, n); + } } } From a6b6565687d658350fe84b448676ace5a1c466c7 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Thu, 14 Sep 2023 23:35:16 +0200 Subject: [PATCH 15/29] Remove unused variable Remove the unused variable `frames` in `VestigeInstrument::play` to fix the stricter GitHub build. --- plugins/Vestige/Vestige.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp index aff5b7fdc..a696a4b2d 100644 --- a/plugins/Vestige/Vestige.cpp +++ b/plugins/Vestige/Vestige.cpp @@ -399,8 +399,6 @@ void VestigeInstrument::play( sampleFrame * _buf ) { if (!m_pluginMutex.tryLock(Engine::getSong()->isExporting() ? -1 : 0)) {return;} - const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); - if( m_plugin == nullptr ) { m_pluginMutex.unlock(); From d25723cead8dbf46e9b4983e90813ab36325f5dc Mon Sep 17 00:00:00 2001 From: saker Date: Fri, 15 Sep 2023 22:25:54 -0400 Subject: [PATCH 16/29] Update display file name when opening ``PatmanView`` (#6870) * Update display file name when loading Patman * Conditionally update file name --- plugins/Patman/Patman.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/Patman/Patman.cpp b/plugins/Patman/Patman.cpp index a2b829940..e96431e3a 100644 --- a/plugins/Patman/Patman.cpp +++ b/plugins/Patman/Patman.cpp @@ -446,7 +446,7 @@ namespace gui PatmanView::PatmanView( Instrument * _instrument, QWidget * _parent ) : InstrumentViewFixedSize( _instrument, _parent ), - m_pi( nullptr ) + m_pi(castModel()) { setAutoFillBackground( true ); QPalette pal; @@ -487,7 +487,15 @@ PatmanView::PatmanView( Instrument * _instrument, QWidget * _parent ) : "tune_off" ) ); m_tuneButton->setToolTip(tr("Tune mode")); - m_displayFilename = tr( "No file selected" ); + + if (m_pi->m_patchFile.isEmpty()) + { + m_displayFilename = tr("No file selected"); + } + else + { + updateFilename(); + } setAcceptDrops( true ); } From 73f9f36c9afcf65150ae8b1f47d359f2472d3eae Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 16 Sep 2023 13:35:15 +0200 Subject: [PATCH 17/29] Code review changes Remove whitespace in methods and add it to the parenthesis of some statements. Remove underscores from parameter names. Remove usage of `this` pointer. Add `auto` keyword to shorten line length in `InstrumentPlayHandle::play`. Move `const_cast` of `NotePlayHandle` closer to the `process` method as this method is the reason that the cast is needed in the first place. One might also add a version of `nphsOfInstrumentTrack` that returns a non-const result but it seems to be a general problem of a missing clear responsibility so I keep it as is. --- include/InstrumentPlayHandle.h | 9 ++++----- src/core/InstrumentPlayHandle.cpp | 32 +++++++++++++++---------------- src/tracks/InstrumentTrack.cpp | 2 +- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/include/InstrumentPlayHandle.h b/include/InstrumentPlayHandle.h index 1455134e6..dc744b4ff 100644 --- a/include/InstrumentPlayHandle.h +++ b/include/InstrumentPlayHandle.h @@ -37,23 +37,22 @@ class InstrumentTrack; class LMMS_EXPORT InstrumentPlayHandle : public PlayHandle { public: - InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ); + InstrumentPlayHandle(Instrument * instrument, InstrumentTrack* instrumentTrack); ~InstrumentPlayHandle() override = default; - void play( sampleFrame * _working_buffer ) override; + void play(sampleFrame * working_buffer) override; bool isFinished() const override { return false; } - bool isFromTrack( const Track* _track ) const override; + bool isFromTrack(const Track* track) const override; private: Instrument* m_instrument; -} ; - +}; } // namespace lmms diff --git a/src/core/InstrumentPlayHandle.cpp b/src/core/InstrumentPlayHandle.cpp index 30f5fde38..097719ad8 100644 --- a/src/core/InstrumentPlayHandle.cpp +++ b/src/core/InstrumentPlayHandle.cpp @@ -33,48 +33,48 @@ namespace lmms { -InstrumentPlayHandle::InstrumentPlayHandle( Instrument * instrument, InstrumentTrack* instrumentTrack ) : - PlayHandle( Type::InstrumentPlayHandle ), - m_instrument( instrument ) +InstrumentPlayHandle::InstrumentPlayHandle(Instrument * instrument, InstrumentTrack* instrumentTrack) : + PlayHandle(Type::InstrumentPlayHandle), + m_instrument(instrument) { - setAudioPort( instrumentTrack->audioPort() ); + setAudioPort(instrumentTrack->audioPort()); } -void InstrumentPlayHandle::play( sampleFrame * _working_buffer ) +void InstrumentPlayHandle::play(sampleFrame * working_buffer) { InstrumentTrack * instrumentTrack = m_instrument->instrumentTrack(); // ensure that all our nph's have been processed first - ConstNotePlayHandleList nphv = NotePlayHandle::nphsOfInstrumentTrack(instrumentTrack, true ); + auto nphv = NotePlayHandle::nphsOfInstrumentTrack(instrumentTrack, true); bool nphsLeft; do { nphsLeft = false; - for( const NotePlayHandle * constNotePlayHandle : nphv ) + for (const NotePlayHandle * constNotePlayHandle : nphv) { - NotePlayHandle * notePlayHandle = const_cast( constNotePlayHandle ); - if( notePlayHandle->state() != ThreadableJob::ProcessingState::Done && - !notePlayHandle->isFinished()) + if (constNotePlayHandle->state() != ThreadableJob::ProcessingState::Done && + !constNotePlayHandle->isFinished()) { nphsLeft = true; + NotePlayHandle * notePlayHandle = const_cast(constNotePlayHandle); notePlayHandle->process(); } } } - while( nphsLeft ); + while (nphsLeft); - m_instrument->play( _working_buffer ); + m_instrument->play(working_buffer); // Process the audio buffer that the instrument has just worked on... const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); - instrumentTrack->processAudioBuffer(_working_buffer, frames, nullptr); + instrumentTrack->processAudioBuffer(working_buffer, frames, nullptr); } -bool InstrumentPlayHandle::isFromTrack( const Track* _track ) const +bool InstrumentPlayHandle::isFromTrack(const Track* track) const { - return m_instrument->isFromTrack( _track ); + return m_instrument->isFromTrack(track); } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index c9b87a4f2..0ffde61bc 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -584,7 +584,7 @@ void InstrumentTrack::playNote( NotePlayHandle* n, sampleFrame* workingBuffer ) { const fpp_t frames = n->framesLeftForCurrentPeriod(); const f_cnt_t offset = n->noteOffset(); - this->processAudioBuffer(workingBuffer, frames + offset, n); + processAudioBuffer(workingBuffer, frames + offset, n); } } } From 6c3ae30c8937173bd69ec3f12ccd5996189c3342 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 16 Sep 2023 19:19:37 +0200 Subject: [PATCH 18/29] Add comments about nullptr guard Add some comments which describe why we need to guard agains nullptr. --- src/tracks/InstrumentTrack.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 0ffde61bc..5dee1f3a5 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -570,6 +570,10 @@ f_cnt_t InstrumentTrack::beatLen( NotePlayHandle * _n ) const void InstrumentTrack::playNote( NotePlayHandle* n, sampleFrame* workingBuffer ) { + // Note: under certain circumstances the working buffer is a nullptr. + // These cases are triggered in PlayHandle::doProcessing when the play method is called with a nullptr. + // TODO: Find out if we can skip processing at a higher level if the buffer is nullptr. + // arpeggio- and chord-widget has to do its work -> adding sub-notes // for chords/arpeggios m_noteStacking.processNote( n ); @@ -580,6 +584,8 @@ void InstrumentTrack::playNote( NotePlayHandle* n, sampleFrame* workingBuffer ) // all is done, so now lets play the note! m_instrument->playNote( n, workingBuffer ); + // Calling processAudioBuffer with a nullptr leads to crashes when checking if the buffer represents silence. + // Therefore we must guard against a nullptr here. if (workingBuffer != nullptr) { const fpp_t frames = n->framesLeftForCurrentPeriod(); From 4348038b0ff2f334f85a37a1caf124213f6d3439 Mon Sep 17 00:00:00 2001 From: Austin <61636545+aakers24@users.noreply.github.com> Date: Sun, 17 Sep 2023 08:34:42 -0700 Subject: [PATCH 19/29] Added automatic dark note on light background for midi clips. (#6539) --- src/gui/clips/MidiClipView.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/clips/MidiClipView.cpp b/src/gui/clips/MidiClipView.cpp index 79c4cd73d..151df8d3c 100644 --- a/src/gui/clips/MidiClipView.cpp +++ b/src/gui/clips/MidiClipView.cpp @@ -523,7 +523,8 @@ void MidiClipView::paintEvent( QPaintEvent * ) p.scale(width(), height() - distanceToTop - 2 * notesBorder); // set colour based on mute status - QColor noteFillColor = muted ? getMutedNoteFillColor() : getNoteFillColor(); + QColor noteFillColor = muted ? getMutedNoteFillColor().lighter(200) + : (c.lightness() > 175 ? getNoteFillColor().darker(400) : getNoteFillColor()); QColor noteBorderColor = muted ? getMutedNoteBorderColor() : ( m_clip->hasColor() ? c.lighter( 200 ) : getNoteBorderColor() ); From e9125d3ad3c8d6da568ea64641896a0f71671534 Mon Sep 17 00:00:00 2001 From: Martin Pavelek Date: Sun, 17 Sep 2023 17:55:04 +0200 Subject: [PATCH 20/29] Fix LcdFloatSpinBox constructor and label alignment (#6686) --- src/gui/widgets/LcdFloatSpinBox.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/gui/widgets/LcdFloatSpinBox.cpp b/src/gui/widgets/LcdFloatSpinBox.cpp index 6391f314a..96f2b27e1 100644 --- a/src/gui/widgets/LcdFloatSpinBox.cpp +++ b/src/gui/widgets/LcdFloatSpinBox.cpp @@ -49,6 +49,7 @@ namespace lmms::gui LcdFloatSpinBox::LcdFloatSpinBox(int numWhole, int numFrac, const QString& name, QWidget* parent) : + QWidget(parent), FloatModelView(new FloatModel(0, 0, 0, 0, nullptr, name, true), this), m_wholeDisplay(numWhole, parent, name, false), m_fractionDisplay(numFrac, parent, name, true), @@ -62,6 +63,7 @@ LcdFloatSpinBox::LcdFloatSpinBox(int numWhole, int numFrac, const QString& name, LcdFloatSpinBox::LcdFloatSpinBox(int numWhole, int numFrac, const QString& style, const QString& name, QWidget* parent) : + QWidget(parent), FloatModelView(new FloatModel(0, 0, 0, 0, nullptr, name, true), this), m_wholeDisplay(numWhole, style, parent, name, false), m_fractionDisplay(numFrac, style, parent, name, true), @@ -101,6 +103,7 @@ void LcdFloatSpinBox::layoutSetup(const QString &style) outerLayout->setContentsMargins(0, 0, 0, 0); outerLayout->setSizeConstraint(QLayout::SetFixedSize); this->setLayout(outerLayout); + this->setFixedHeight(32); } @@ -240,9 +243,9 @@ void LcdFloatSpinBox::paintEvent(QPaintEvent*) { p.setFont(pointSizeF(p.font(), 6.5)); p.setPen(m_wholeDisplay.textShadowColor()); - p.drawText(width() / 2 - horizontalAdvance(p.fontMetrics(), m_label) / 2 + 1, height(), m_label); + p.drawText(width() / 2 - p.fontMetrics().boundingRect(m_label).width() / 2 + 1, height(), m_label); p.setPen(m_wholeDisplay.textColor()); - p.drawText(width() / 2 - horizontalAdvance(p.fontMetrics(), m_label) / 2, height() - 1, m_label); + p.drawText(width() / 2 - p.fontMetrics().boundingRect(m_label).width() / 2, height() - 1, m_label); } } From 733e0a1e542dec42325d1110688848d4fecf4f56 Mon Sep 17 00:00:00 2001 From: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com> Date: Mon, 18 Sep 2023 22:14:08 +0530 Subject: [PATCH 21/29] Fix invalid use of iterators in piano roll (#6869) --- src/gui/editors/PianoRoll.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 751fb9d2a..1da6aa3df 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1719,10 +1719,10 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) const NoteVector & notes = m_midiClip->notes(); // will be our iterator in the following loop - auto it = notes.begin() + notes.size() - 1; + auto it = notes.rbegin(); // loop through whole note-vector... - for( int i = 0; i < notes.size(); ++i ) + while (it != notes.rend()) { Note *note = *it; TimePos len = note->length(); @@ -1747,7 +1747,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) { break; } - --it; + ++it; } // first check whether the user clicked in note-edit- @@ -1769,7 +1769,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) Note * created_new_note = nullptr; // did it reach end of vector because // there's no note?? - if( it == notes.begin()-1 ) + if (it == notes.rend()) { is_new_note = true; m_midiClip->addJournalCheckPoint(); @@ -1816,8 +1816,8 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) // reset it so that it can be used for // ops (move, resize) after this // code-block - it = notes.begin(); - while( it != notes.end() && *it != created_new_note ) + it = notes.rbegin(); + while (it != notes.rend() && *it != created_new_note) { ++it; } @@ -1933,7 +1933,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) { // erase single note m_mouseDownRight = true; - if( it != notes.begin()-1 ) + if (it != notes.rend()) { m_midiClip->addJournalCheckPoint(); m_midiClip->removeNote( *it ); @@ -2513,7 +2513,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) bool altPressed = me->modifiers() & Qt::AltModifier; // We iterate from last note in MIDI clip to the first, // chronologically - auto it = notes.begin() + notes.size() - 1; + auto it = notes.rbegin(); for( int i = 0; i < notes.size(); ++i ) { Note* n = *it; @@ -2556,7 +2556,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } - --it; + ++it; } // Emit MIDI clip has changed @@ -2575,10 +2575,10 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) const NoteVector & notes = m_midiClip->notes(); // will be our iterator in the following loop - auto it = notes.begin() + notes.size() - 1; + auto it = notes.rbegin(); // loop through whole note-vector... - for( int i = 0; i < notes.size(); ++i ) + while (it != notes.rend()) { Note *note = *it; // and check whether the cursor is over an @@ -2591,12 +2591,12 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) { break; } - --it; + ++it; } // did it reach end of vector because there's // no note?? - if( it != notes.begin()-1 ) + if (it != notes.rend()) { Note *note = *it; // x coordinate of the right edge of the note From dc88040fbe59222620535a347fc209a020133c71 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Tue, 19 Sep 2023 00:18:28 +0100 Subject: [PATCH 22/29] Add instrument play handles unconditionally (#6875) --- src/core/AudioEngine.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 2d077babc..29c54647c 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -701,7 +701,10 @@ void AudioEngine::removeAudioPort(AudioPort * port) bool AudioEngine::addPlayHandle( PlayHandle* handle ) { - if( criticalXRuns() == false ) + // Only add play handles if we have the CPU capacity to process them. + // Instrument play handles are not added during playback, but when the + // associated instrument is created, so add those unconditionally. + if (handle->type() == PlayHandle::Type::InstrumentPlayHandle || !criticalXRuns()) { m_newPlayHandles.push( handle ); handle->audioPort()->addPlayHandle( handle ); From 94d0b9111b24687e44f65cdf1e5c691e249ee50d Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Wed, 20 Sep 2023 00:56:55 +0100 Subject: [PATCH 23/29] Use UTF-8 for MSVC source and execution charset (#6876) --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b17d0e7c..c1cf3406b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -651,6 +651,9 @@ IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") ELSE(WIN32) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -DPIC") ENDIF(WIN32) +elseif(MSVC) + # Use UTF-8 as the source and execution character set + add_compile_options("/utf-8") ENDIF() # add enabled sanitizers From 7e8c79a191e2dbb8ac5a2a85fd758b0782d37662 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Wed, 20 Sep 2023 19:52:59 +0200 Subject: [PATCH 24/29] Enable build under NixOS (#6855) Explicitly call the perl interpreter when building the SWH LADSPA plugins --- CMakeLists.txt | 20 ++++++++++++++++++-- plugins/LadspaEffect/swh/CMakeLists.txt | 3 ++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1cf3406b..7c3770f31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,13 @@ SET(LMMS_BINARY_DIR ${CMAKE_BINARY_DIR}) SET(LMMS_SOURCE_DIR ${CMAKE_SOURCE_DIR}) # CMAKE_POLICY Section +IF(COMMAND CMAKE_POLICY) + # TODO: Keep CMP0074 but remove this condition when cmake 3.12+ is guaranteed + IF(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.12) + # Needed for the SWH Ladspa plugins. See below. + CMAKE_POLICY(SET CMP0074 NEW) # find_package() uses _ROOT variables + ENDIF() +ENDIF(COMMAND CMAKE_POLICY) # Import of windows.h breaks min()/max() ADD_DEFINITIONS(-DNOMINMAX) @@ -266,8 +273,17 @@ ELSE(WANT_CMT) ENDIF(WANT_CMT) IF(WANT_SWH) - SET(LMMS_HAVE_SWH TRUE) - SET(STATUS_SWH "OK") + IF(LMMS_BUILD_APPLE) + # Prefer system perl over Homebrew, MacPorts, etc + SET(Perl_ROOT "/usr/bin") + ENDIF() + FIND_PACKAGE(Perl) + IF(PERL_FOUND) + SET(LMMS_HAVE_SWH TRUE) + SET(STATUS_SWH "OK") + ELSE() + SET(STATUS_SWH "Skipping, perl is missing") + ENDIF() ELSE(WANT_SWH) SET(STATUS_SWH "not built as requested") ENDIF(WANT_SWH) diff --git a/plugins/LadspaEffect/swh/CMakeLists.txt b/plugins/LadspaEffect/swh/CMakeLists.txt index aec01c22f..a83001177 100644 --- a/plugins/LadspaEffect/swh/CMakeLists.txt +++ b/plugins/LadspaEffect/swh/CMakeLists.txt @@ -16,6 +16,7 @@ SET(COMPILE_FLAGS "${COMPILE_FLAGS} ${PIC_FLAGS}") # Loop over every XML file FILE(GLOB XML_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ladspa/*.xml") LIST(SORT XML_SOURCES) + FOREACH(_item ${XML_SOURCES}) # Get library name and (soon to be) C file GET_FILENAME_COMPONENT(_plugin "${_item}" NAME_WE) @@ -24,7 +25,7 @@ FOREACH(_item ${XML_SOURCES}) # Coerce XML source file to C ADD_CUSTOM_COMMAND( OUTPUT "${_out_file}" - COMMAND ./makestub.pl "${_item}" > "${_out_file}" + COMMAND "${PERL_EXECUTABLE}" ./makestub.pl "${_item}" > "${_out_file}" DEPENDS "${_item}" WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/ladspa" VERBATIM From 3df28e30b68db4d696b50315577bf7a04cd391f7 Mon Sep 17 00:00:00 2001 From: Maxwell Voorhes <100326501+mvoorhes@users.noreply.github.com> Date: Wed, 20 Sep 2023 15:32:21 -0700 Subject: [PATCH 25/29] Changed sharps to music sharps unicode (#6873) --- src/gui/editors/PianoRoll.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 1da6aa3df..cef2205d2 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -130,7 +130,10 @@ QPixmap* PianoRoll::s_toolKnife = nullptr; SimpleTextFloat * PianoRoll::s_textFloat = nullptr; -static std::array s_noteStrings {"C", "C# / D\u266D", "D", "D# / E\u266D", "E", "F", "F# / G\u266D", "G", "G# / A\u266D", "A", "A# / B\u266D", "B"}; +static std::array s_noteStrings { + "C", "C\u266F / D\u266D", "D", "D\u266F / E\u266D", "E", "F", "F\u266F / G\u266D", + "G", "G\u266F / A\u266D", "A", "A\u266F / B\u266D", "B" +}; static QString getNoteString(int key) { From b6c80bd7366a789cab3f5a6a017636faf80a95ee Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Thu, 21 Sep 2023 19:24:06 +0200 Subject: [PATCH 26/29] Replace check for nullptr Replace the check for a `nullptr` buffer with a check that checks if the NotePlayHandle uses a buffer. This is effectively the same condition that's used by `PlayHandle::doProcessing` to decide if it should call play with a `nullptr` in the first place. Hence it should be the most fitting check. --- src/tracks/InstrumentTrack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 5dee1f3a5..29fda075e 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -584,9 +584,9 @@ void InstrumentTrack::playNote( NotePlayHandle* n, sampleFrame* workingBuffer ) // all is done, so now lets play the note! m_instrument->playNote( n, workingBuffer ); - // Calling processAudioBuffer with a nullptr leads to crashes when checking if the buffer represents silence. - // Therefore we must guard against a nullptr here. - if (workingBuffer != nullptr) + // This is effectively the same as checking if workingBuffer is not a nullptr. + // Calling processAudioBuffer with a nullptr leads to crashes. Hence the check. + if (n->usesBuffer()) { const fpp_t frames = n->framesLeftForCurrentPeriod(); const f_cnt_t offset = n->noteOffset(); From 07229b64048c118bd8a25904c2cbc1a603fad838 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Thu, 21 Sep 2023 20:18:23 -0300 Subject: [PATCH 27/29] Show error message when loading an invalid sample file (#6286) --- include/SampleClip.h | 2 +- src/core/SampleBuffer.cpp | 62 +++++++++++++++++++++++++++++++-------- src/core/SampleClip.cpp | 29 ++++++++++-------- 3 files changed, 66 insertions(+), 27 deletions(-) diff --git a/include/SampleClip.h b/include/SampleClip.h index c9e247328..5246787bd 100644 --- a/include/SampleClip.h +++ b/include/SampleClip.h @@ -77,7 +77,7 @@ public: public slots: void setSampleBuffer( lmms::SampleBuffer* sb ); - void setSampleFile( const QString & _sf ); + void setSampleFile( const QString & sf ); void updateLength(); void toggleRecord(); void playbackPositionChanged(); diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 775db125b..5e2d09c57 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -247,7 +247,15 @@ void SampleBuffer::update(bool keepSettings) const int fileSizeMax = 300; // MB const int sampleLengthMax = 90; // Minutes - bool fileLoadError = false; + enum class FileLoadError + { + None, + ReadPermissionDenied, + TooLarge, + Invalid + }; + FileLoadError fileLoadError = FileLoadError::None; + if (m_audioFile.isEmpty() && m_origData != nullptr && m_origFrames > 0) { // TODO: reverse- and amplification-property is not covered @@ -271,31 +279,40 @@ void SampleBuffer::update(bool keepSettings) m_frames = 0; const QFileInfo fileInfo(file); - if (fileInfo.size() > fileSizeMax * 1024 * 1024) + if (!fileInfo.isReadable()) { - fileLoadError = true; + fileLoadError = FileLoadError::ReadPermissionDenied; + } + else if (fileInfo.size() > fileSizeMax * 1024 * 1024) + { + fileLoadError = FileLoadError::TooLarge; } else { // Use QFile to handle unicode file names on Windows QFile f(file); - SNDFILE * sndFile; + SNDFILE * sndFile = nullptr; SF_INFO sfInfo; sfInfo.format = 0; + if (f.open(QIODevice::ReadOnly) && (sndFile = sf_open_fd(f.handle(), SFM_READ, &sfInfo, false))) { f_cnt_t frames = sfInfo.frames; int rate = sfInfo.samplerate; if (frames / rate > sampleLengthMax * 60) { - fileLoadError = true; + fileLoadError = FileLoadError::TooLarge; } sf_close(sndFile); } + else + { + fileLoadError = FileLoadError::Invalid; + } f.close(); } - if (!fileLoadError) + if (fileLoadError == FileLoadError::None) { #ifdef LMMS_HAVE_OGGVORBIS // workaround for a bug in libsndfile or our libsndfile decoder @@ -322,7 +339,7 @@ void SampleBuffer::update(bool keepSettings) } } - if (m_frames == 0 || fileLoadError) // if still no frames, bail + if (m_frames == 0 || fileLoadError != FileLoadError::None) // if still no frames, bail { // sample couldn't be decoded, create buffer containing // one sample-frame @@ -363,16 +380,35 @@ void SampleBuffer::update(bool keepSettings) } Oscillator::generateAntiAliasUserWaveTable(this); - if (fileLoadError) + if (fileLoadError != FileLoadError::None) { QString title = tr("Fail to open file"); - QString message = tr("Audio files are limited to %1 MB " - "in size and %2 minutes of playing time" - ).arg(fileSizeMax).arg(sampleLengthMax); + QString message; + + switch (fileLoadError) + { + case FileLoadError::None: + // present just to avoid a compiler warning + break; + + case FileLoadError::ReadPermissionDenied: + message = tr("Read permission denied"); + break; + + case FileLoadError::TooLarge: + message = tr("Audio files are limited to %1 MB " + "in size and %2 minutes of playing time" + ).arg(fileSizeMax).arg(sampleLengthMax); + break; + + case FileLoadError::Invalid: + message = tr("Invalid audio file"); + break; + } + if (gui::getGUI() != nullptr) { - QMessageBox::information(nullptr, - title, message, QMessageBox::Ok); + QMessageBox::information(nullptr, title, message, QMessageBox::Ok); } else { diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 592a63827..b09d7b3bb 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -143,23 +143,26 @@ void SampleClip::setSampleBuffer( SampleBuffer* sb ) -void SampleClip::setSampleFile( const QString & _sf ) +void SampleClip::setSampleFile(const QString & sf) { - int length; - if ( _sf.isEmpty() ) - { //When creating an empty sample clip make it a bar long - float nom = Engine::getSong()->getTimeSigModel().getNumerator(); - float den = Engine::getSong()->getTimeSigModel().getDenominator(); - length = DefaultTicksPerBar * ( nom / den ); - } - else - { //Otherwise set it to the sample's length - m_sampleBuffer->setAudioFile( _sf ); + int length = 0; + + if (!sf.isEmpty()) + { + m_sampleBuffer->setAudioFile(sf); length = sampleLength(); } - changeLength(length); - setStartTimeOffset( 0 ); + if (length == 0) + { + //If there is no sample, make the clip a bar long + float nom = Engine::getSong()->getTimeSigModel().getNumerator(); + float den = Engine::getSong()->getTimeSigModel().getDenominator(); + length = DefaultTicksPerBar * (nom / den); + } + + changeLength(length); + setStartTimeOffset(0); emit sampleChanged(); emit playbackPositionChanged(); From 93d3e8c82c4b2968c5949dde0fb8bb4a952bb54f Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Fri, 22 Sep 2023 18:16:45 +0200 Subject: [PATCH 28/29] Adjust the classic theme Adjust the classic theme so that it is consistent with the changes made to the default theme. It was only necessary to change the font-size of the check boxes in the file browser to points because the QTreeView already does not contain any font settings. --- data/themes/classic/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 2880fe661..4c266e064 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -132,7 +132,7 @@ QMenu::indicator:selected { lmms--gui--FileBrowser QCheckBox { - font-size: 10px; + font-size: 8pt; color: white; } From f0aa2862d7efe06a483f6056ba20de315d065597 Mon Sep 17 00:00:00 2001 From: Artur-Twardowski-Mobica <139768112+Artur-Twardowski-Mobica@users.noreply.github.com> Date: Fri, 22 Sep 2023 21:07:25 +0200 Subject: [PATCH 29/29] Updated MIDI CC handling not to count from 1 (#6774) --- include/DataFile.h | 1 + include/MidiController.h | 1 + src/core/DataFile.cpp | 23 ++++++++++++++++++- src/core/midi/MidiController.cpp | 11 ++++----- src/core/midi/MidiPort.cpp | 7 +++--- src/gui/modals/ControllerConnectionDialog.cpp | 6 ++--- 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/include/DataFile.h b/include/DataFile.h index c19a00f7f..dc82315ad 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -127,6 +127,7 @@ private: void upgrade_mixerRename(); void upgrade_bbTcoRename(); void upgrade_sampleAndHold(); + void upgrade_midiCCIndexing(); // List of all upgrade methods static const std::vector UPGRADE_METHODS; diff --git a/include/MidiController.h b/include/MidiController.h index c4ef49590..9f49627ac 100644 --- a/include/MidiController.h +++ b/include/MidiController.h @@ -48,6 +48,7 @@ class MidiController : public Controller, public MidiEventProcessor { Q_OBJECT public: + static constexpr int NONE = -1; MidiController( Model * _parent ); ~MidiController() override = default; diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 5915b25ef..8d0a8dca4 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -79,7 +79,7 @@ const std::vector DataFile::UPGRADE_METHODS = { &DataFile::upgrade_automationNodes , &DataFile::upgrade_extendedNoteRange, &DataFile::upgrade_defaultTripleOscillatorHQ, &DataFile::upgrade_mixerRename , &DataFile::upgrade_bbTcoRename, - &DataFile::upgrade_sampleAndHold , + &DataFile::upgrade_sampleAndHold , &DataFile::upgrade_midiCCIndexing }; // Vector of all versions that have upgrade routines. @@ -1809,6 +1809,27 @@ void DataFile::upgrade_sampleAndHold() } } +//! Update MIDI CC indexes, so that they are counted from 0. Older releases of LMMS +//! count the CCs from 1. +void DataFile::upgrade_midiCCIndexing() +{ + static constexpr std::array attributesToUpdate{"inputcontroller", "outputcontroller"}; + + QDomNodeList elements = elementsByTagName("Midicontroller"); + for(int i = 0; i < elements.length(); i++) + { + if (elements.item(i).isNull()) { continue; } + auto element = elements.item(i).toElement(); + for (const char* attrName : attributesToUpdate) + { + if (element.hasAttribute(attrName)) + { + int cc = element.attribute(attrName).toInt(); + element.setAttribute(attrName, cc - 1); + } + } + } +} void DataFile::upgrade() { diff --git a/src/core/midi/MidiController.cpp b/src/core/midi/MidiController.cpp index d7c89e940..0ae76d352 100644 --- a/src/core/midi/MidiController.cpp +++ b/src/core/midi/MidiController.cpp @@ -72,21 +72,20 @@ void MidiController::updateName() -void MidiController::processInEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) +void MidiController::processInEvent(const MidiEvent& event, const TimePos& time, f_cnt_t offset) { unsigned char controllerNum; - switch( event.type() ) + switch(event.type()) { case MidiControlChange: controllerNum = event.controllerNumber(); - if( m_midiPort.inputController() == controllerNum + 1 && - ( m_midiPort.inputChannel() == event.channel() + 1 || - m_midiPort.inputChannel() == 0 ) ) + if (m_midiPort.inputController() == controllerNum && + (m_midiPort.inputChannel() == event.channel() + 1 || m_midiPort.inputChannel() == 0)) { unsigned char val = event.controllerValue(); m_previousValue = m_lastValue; - m_lastValue = (float)( val ) / 127.0f; + m_lastValue = static_cast(val) / 127.0f; emit valueChanged(); } break; diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index c7c947e8e..24263f913 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -31,6 +31,7 @@ #include "MidiEventProcessor.h" #include "Note.h" #include "Song.h" +#include "MidiController.h" namespace lmms @@ -54,8 +55,8 @@ MidiPort::MidiPort( const QString& name, m_mode( mode ), m_inputChannelModel( 0, 0, MidiChannelCount, this, tr( "Input channel" ) ), m_outputChannelModel( 1, 0, MidiChannelCount, this, tr( "Output channel" ) ), - m_inputControllerModel( 0, 0, MidiControllerCount, this, tr( "Input controller" ) ), - m_outputControllerModel( 0, 0, MidiControllerCount, this, tr( "Output controller" ) ), + m_inputControllerModel(MidiController::NONE, MidiController::NONE, MidiControllerCount - 1, this, tr( "Input controller" )), + m_outputControllerModel(MidiController::NONE, MidiController::NONE, MidiControllerCount - 1, this, tr( "Output controller" )), m_fixedInputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed input velocity" ) ), m_fixedOutputVelocityModel( -1, -1, MidiMaxVelocity, this, tr( "Fixed output velocity" ) ), m_fixedOutputNoteModel( -1, -1, MidiMaxKey, this, tr( "Fixed output note" ) ), @@ -436,4 +437,4 @@ void MidiPort::invalidateCilent() } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/src/gui/modals/ControllerConnectionDialog.cpp b/src/gui/modals/ControllerConnectionDialog.cpp index 79daa25b5..4d1090d5c 100644 --- a/src/gui/modals/ControllerConnectionDialog.cpp +++ b/src/gui/modals/ControllerConnectionDialog.cpp @@ -54,7 +54,7 @@ public: AutoDetectMidiController( Model* parent ) : MidiController( parent ), m_detectedMidiChannel( 0 ), - m_detectedMidiController( 0 ) + m_detectedMidiController(NONE) { updateName(); } @@ -69,7 +69,7 @@ public: ( m_midiPort.inputChannel() == 0 || m_midiPort.inputChannel() == event.channel() + 1 ) ) { m_detectedMidiChannel = event.channel() + 1; - m_detectedMidiController = event.controllerNumber() + 1; + m_detectedMidiController = event.controllerNumber(); m_detectedMidiPort = Engine::audioEngine()->midiClient()->sourcePortName( event ); emit valueChanged(); @@ -152,7 +152,7 @@ ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent, m_midiControllerSpinBox = new LcdSpinBox( 3, m_midiGroupBox, tr( "Input controller" ) ); - m_midiControllerSpinBox->addTextForValue( 0, "---" ); + m_midiControllerSpinBox->addTextForValue(MidiController::NONE, "---" ); m_midiControllerSpinBox->setLabel( tr( "CONTROLLER" ) ); m_midiControllerSpinBox->move( 68, 24 );