From 4908bfe1a646ce1b9687c83dd05b4a9d30471ac3 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 16 Jul 2023 14:11:24 +0200 Subject: [PATCH 01/33] Scalable Setup Dialog Make the setup dialog resizable and use widgets that dynamically adjust to the font size set by the user. Current status of the tabs: * General: Done * Performance: Plugins group needs adjustments * Audio: Individual settings dialogs need adjustments * MIDI: Not started yet * Paths: Done The "Autosave" and "Buffer size" tabs have been slightly redesigned so that the revert/reset button is next to the slider. Technical details ------------------ Setting a fixed size has been removed from the setup dialog so that it can be resized. The settings widget (`settings_w`) now has a layout to which the sub dialogs are added. This is done so that the layout sizes are propagated upwards, i.e. all widgets along the chain now have a layout so that size hints are determined correctly. Instances of `TabWidget` are replaced with instances of `QGroupBox`. Each group box has a layout to which its children, e.g. check boxes, are added. These group boxes are then added to the layouts of their parents. Doing so also removes the need to count how many instances have been added and calculations on where to put the children. Instances of `LedCheckBox` are replaced with instances of `QCheckBox`. This is done in the new helper lambda `addCheckBox`. It's very similar to the previously used `addLedCheckBox` but creates a `QCheckBox` instead of a `LedCheckBox`. It returns the created `QCheckBox` as a result. If a layout is provided the created check box is also added to the layout before it is returned. The helper lambda `addPathEntry` has been adjusted to create a `QGroupBox` and to put all its children in a layout. The helper method `labelWidget` has been adjusted to not set the font size of the label. The method `TabBar::addTab` had to be extended with a new default parameter which controls if the added widget is fixed to the size of it's parent or not. In the case of the setup dialog we do not want this. So far the method is only used by the setup dialog and the `LadspaBrowser` class. So once the `LadspaBrowser` has been made resizable the default parameter can be removed again and treated as always being set to false. Add `QCheckBox` to the `style.css` so that it does not get a green font due to the palette magic that's done. --- data/themes/default/style.css | 2 +- include/SetupDialog.h | 5 +- include/TabBar.h | 7 +- src/gui/modals/SetupDialog.cpp | 254 ++++++++++++++++++--------------- src/gui/widgets/TabBar.cpp | 12 +- 5 files changed, 153 insertions(+), 127 deletions(-) diff --git a/data/themes/default/style.css b/data/themes/default/style.css index a9646cfe4..ee5681f43 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -3,7 +3,7 @@ ********************/ /* most foreground text items */ -QLabel, QTreeWidget, QListWidget, QGroupBox, QMenuBar { +QLabel, QTreeWidget, QListWidget, QGroupBox, QMenuBar, QCheckBox { color: #d1d8e4; } diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 27a4ce4f9..7fa59d244 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -40,6 +40,7 @@ class QComboBox; class QLabel; class QLineEdit; class QSlider; +class QCheckBox; namespace lmms::gui @@ -155,8 +156,8 @@ private: bool m_enableRunningAutoSave; QSlider * m_saveIntervalSlider; QLabel * m_saveIntervalLbl; - LedCheckBox * m_autoSave; - LedCheckBox * m_runningAutoSave; + QCheckBox * m_autoSave; + QCheckBox * m_runningAutoSave; bool m_smoothScroll; bool m_animateAFP; QLabel * m_vstEmbedLbl; diff --git a/include/TabBar.h b/include/TabBar.h index fa2703287..29c100e0c 100644 --- a/include/TabBar.h +++ b/include/TabBar.h @@ -49,7 +49,12 @@ public: TabButton * addTab( QWidget * _w, const QString & _text, int _id, bool _add_stretch = false, - bool _text_is_tooltip = false ); + bool _text_is_tooltip = false, + // TODO Remove fixWidgetToParentSize once it is used + // with false everywhere. + // At the time of writing it is only used in + // LadspaBrowser with default parameters. + bool fixWidgetToParentSize = true ); void removeTab( int _id ); inline void setExclusive( bool _on ) diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 33505c399..97de349c4 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "AudioDeviceSetupWidget.h" #include "AudioEngine.h" @@ -79,13 +81,12 @@ inline void labelWidget(QWidget * w, const QString & txt) auto title = new QLabel(txt, w); QFont f = title->font(); f.setBold(true); - title->setFont(pointSize<12>(f)); + title->setFont(f); + QBoxLayout * boxLayout = dynamic_cast(w->layout()); + assert(boxLayout); - assert(dynamic_cast(w->layout()) != nullptr); - - dynamic_cast(w->layout())->addSpacing(5); - dynamic_cast(w->layout())->addWidget(title); + boxLayout->addWidget(title); } @@ -162,7 +163,6 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : // TODO: Equivalent to the new setWindowFlag(Qt::WindowContextHelpButtonHint, false) setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setModal(true); - setFixedSize(454, 400); Engine::projectJournal()->setJournalling(false); @@ -191,7 +191,8 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : // Settings widget. auto settings_w = new QWidget(main_w); - settings_w->setFixedSize(360, 360); + + QVBoxLayout * settingsLayout = new QVBoxLayout(settings_w); // General widget. auto general_w = new QWidget(settings_w); @@ -212,6 +213,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : auto generalControlsLayout = new QVBoxLayout; generalControlsLayout->setSpacing(10); + // TODO Remove once it is not used anymore auto addLedCheckBox = [&XDelta, &YDelta, this](const QString& ledText, TabWidget* tw, int& counter, bool initialState, const char* toggledSlot, bool showRestartWarning) { auto checkBox = new LedCheckBox(ledText, tw); @@ -225,63 +227,77 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : } }; - int counter = 0; + auto addCheckBox = [this](const QString& ledText, QWidget* parent, QBoxLayout * layout, + bool initialState, const char* toggledSlot, bool showRestartWarning) -> QCheckBox * { + auto checkBox = new QCheckBox(ledText, parent); + checkBox->setChecked(initialState); + connect(checkBox, SIGNAL(toggled(bool)), this, toggledSlot); + + if (showRestartWarning) + { + connect(checkBox, SIGNAL(toggled(bool)), this, SLOT(showRestartWarning())); + } + + if (layout) + { + layout->addWidget(checkBox); + } + + return checkBox; + }; // GUI tab. - auto gui_tw = new TabWidget(tr("Graphical user interface (GUI)"), generalControls); + QGroupBox * guiGroupBox = new QGroupBox(tr("Graphical user interface (GUI)"), generalControls); + QVBoxLayout * guiGroupLayout = new QVBoxLayout(guiGroupBox); - addLedCheckBox(tr("Display volume as dBFS "), gui_tw, counter, + addCheckBox(tr("Display volume as dBFS "), guiGroupBox, guiGroupLayout, m_displaydBFS, SLOT(toggleDisplaydBFS(bool)), true); - addLedCheckBox(tr("Enable tooltips"), gui_tw, counter, + addCheckBox(tr("Enable tooltips"), guiGroupBox, guiGroupLayout, m_tooltips, SLOT(toggleTooltips(bool)), true); - addLedCheckBox(tr("Enable master oscilloscope by default"), gui_tw, counter, + addCheckBox(tr("Enable master oscilloscope by default"), guiGroupBox, guiGroupLayout, m_displayWaveform, SLOT(toggleDisplayWaveform(bool)), true); - addLedCheckBox(tr("Enable all note labels in piano roll"), gui_tw, counter, + addCheckBox(tr("Enable all note labels in piano roll"), guiGroupBox, guiGroupLayout, m_printNoteLabels, SLOT(toggleNoteLabels(bool)), false); - addLedCheckBox(tr("Enable compact track buttons"), gui_tw, counter, + addCheckBox(tr("Enable compact track buttons"), guiGroupBox, guiGroupLayout, m_compactTrackButtons, SLOT(toggleCompactTrackButtons(bool)), true); - addLedCheckBox(tr("Enable one instrument-track-window mode"), gui_tw, counter, + addCheckBox(tr("Enable one instrument-track-window mode"), guiGroupBox, guiGroupLayout, m_oneInstrumentTrackWindow, SLOT(toggleOneInstrumentTrackWindow(bool)), true); - addLedCheckBox(tr("Show sidebar on the right-hand side"), gui_tw, counter, + addCheckBox(tr("Show sidebar on the right-hand side"), guiGroupBox, guiGroupLayout, m_sideBarOnRight, SLOT(toggleSideBarOnRight(bool)), true); - addLedCheckBox(tr("Let sample previews continue when mouse is released"), gui_tw, counter, + addCheckBox(tr("Let sample previews continue when mouse is released"), guiGroupBox, guiGroupLayout, m_letPreviewsFinish, SLOT(toggleLetPreviewsFinish(bool)), false); - addLedCheckBox(tr("Mute automation tracks during solo"), gui_tw, counter, + addCheckBox(tr("Mute automation tracks during solo"), guiGroupBox, guiGroupLayout, m_soloLegacyBehavior, SLOT(toggleSoloLegacyBehavior(bool)), false); - addLedCheckBox(tr("Show warning when deleting tracks"), gui_tw, counter, + addCheckBox(tr("Show warning when deleting tracks"), guiGroupBox, guiGroupLayout, m_trackDeletionWarning, SLOT(toggleTrackDeletionWarning(bool)), false); - addLedCheckBox(tr("Show warning when deleting a mixer channel that is in use"), gui_tw, counter, + addCheckBox(tr("Show warning when deleting a mixer channel that is in use"), guiGroupBox, guiGroupLayout, m_mixerChannelDeletionWarning, SLOT(toggleMixerChannelDeletionWarning(bool)), false); - gui_tw->setFixedHeight(YDelta + YDelta * counter); + generalControlsLayout->addWidget(guiGroupBox); - generalControlsLayout->addWidget(gui_tw); generalControlsLayout->addSpacing(10); - - counter = 0; - // Projects tab. - auto projects_tw = new TabWidget(tr("Projects"), generalControls); + QGroupBox * projectsGroupBox = new QGroupBox(tr("Projects"), generalControls); + QVBoxLayout * projectsGroupLayout = new QVBoxLayout(projectsGroupBox); - addLedCheckBox(tr("Compress project files by default"), projects_tw, counter, + addCheckBox(tr("Compress project files by default"), projectsGroupBox, projectsGroupLayout, m_MMPZ, SLOT(toggleMMPZ(bool)), true); - addLedCheckBox(tr("Create a backup file when saving a project"), projects_tw, counter, + addCheckBox(tr("Create a backup file when saving a project"), projectsGroupBox, projectsGroupLayout, m_disableBackup, SLOT(toggleDisableBackup(bool)), false); - addLedCheckBox(tr("Reopen last project on startup"), projects_tw, counter, + addCheckBox(tr("Reopen last project on startup"), projectsGroupBox, projectsGroupLayout, m_openLastProject, SLOT(toggleOpenLastProject(bool)), false); - projects_tw->setFixedHeight(YDelta + YDelta * counter); + generalControlsLayout->addWidget(projectsGroupBox); - generalControlsLayout->addWidget(projects_tw); generalControlsLayout->addSpacing(10); - // Language tab. - auto lang_tw = new TabWidget(tr("Language"), generalControls); - lang_tw->setFixedHeight(48); - auto changeLang = new QComboBox(lang_tw); - changeLang->move(XDelta, 20); + QGroupBox * languageGroupBox = new QGroupBox(tr("Language"), generalControls); + QVBoxLayout * languageGroupLayout = new QVBoxLayout(languageGroupBox); + + auto changeLang = new QComboBox(languageGroupBox); + languageGroupLayout->addWidget(changeLang); QDir dir(ConfigManager::inst()->localeDir()); QStringList fileNames = dir.entryList(QStringList("*.qm")); @@ -333,7 +349,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : connect(changeLang, SIGNAL(currentIndexChanged(int)), this, SLOT(showRestartWarning())); - generalControlsLayout->addWidget(lang_tw); + generalControlsLayout->addWidget(languageGroupBox); generalControlsLayout->addSpacing(10); // General layout ordering. @@ -341,7 +357,9 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : generalControls->setLayout(generalControlsLayout); generalScroll->setWidget(generalControls); generalScroll->setWidgetResizable(true); - general_layout->addWidget(generalScroll); + general_layout->addWidget(generalScroll, 1); + + // TODO Does not really seem to be needed general_layout->addStretch(); @@ -357,61 +375,54 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : // Autosave tab. - auto auto_save_tw = new TabWidget(tr("Autosave"), performance_w); - auto_save_tw->setFixedHeight(106); + QGroupBox * autoSaveBox = new QGroupBox(tr("Autosave"), performance_w); + QVBoxLayout * autoSaveLayout = new QVBoxLayout(autoSaveBox); + QHBoxLayout * autoSaveSubLayout = new QHBoxLayout(); - m_saveIntervalSlider = new QSlider(Qt::Horizontal, auto_save_tw); + m_saveIntervalSlider = new QSlider(Qt::Horizontal, autoSaveBox); m_saveIntervalSlider->setValue(m_saveInterval); m_saveIntervalSlider->setRange(1, 20); m_saveIntervalSlider->setTickInterval(1); m_saveIntervalSlider->setPageStep(1); - m_saveIntervalSlider->setGeometry(10, 18, 340, 18); m_saveIntervalSlider->setTickPosition(QSlider::TicksBelow); connect(m_saveIntervalSlider, SIGNAL(valueChanged(int)), this, SLOT(setAutoSaveInterval(int))); - m_saveIntervalLbl = new QLabel(auto_save_tw); - m_saveIntervalLbl->setGeometry(10, 40, 200, 24); - setAutoSaveInterval(m_saveIntervalSlider->value()); - - m_autoSave = new LedCheckBox( - tr("Enable autosave"), auto_save_tw); - m_autoSave->move(10, 70); - m_autoSave->setChecked(m_enableAutoSave); - connect(m_autoSave, SIGNAL(toggled(bool)), - this, SLOT(toggleAutoSave(bool))); - - m_runningAutoSave = new LedCheckBox( - tr("Allow autosave while playing"), auto_save_tw); - m_runningAutoSave->move(20, 88); - m_runningAutoSave->setChecked(m_enableRunningAutoSave); - connect(m_runningAutoSave, SIGNAL(toggled(bool)), - this, SLOT(toggleRunningAutoSave(bool))); - - auto autoSaveResetBtn = new QPushButton(embed::getIconPixmap("reload"), "", auto_save_tw); - autoSaveResetBtn->setGeometry(320, 70, 28, 28); + auto autoSaveResetBtn = new QPushButton(embed::getIconPixmap("reload"), "", autoSaveBox); connect(autoSaveResetBtn, SIGNAL(clicked()), - this, SLOT(resetAutoSave())); + this, SLOT(resetAutoSave())); + + autoSaveSubLayout->addWidget(m_saveIntervalSlider); + autoSaveSubLayout->addWidget(autoSaveResetBtn); + + autoSaveLayout->addLayout(autoSaveSubLayout); + + m_saveIntervalLbl = new QLabel(autoSaveBox); + setAutoSaveInterval(m_saveIntervalSlider->value()); + autoSaveLayout->addWidget(m_saveIntervalLbl); + + m_autoSave = addCheckBox(tr("Enable autosave"), autoSaveBox, autoSaveLayout, + m_enableAutoSave, SLOT(toggleAutoSave(bool)), false); + + m_runningAutoSave = addCheckBox(tr("Allow autosave while playing"), autoSaveBox, autoSaveLayout, + m_enableRunningAutoSave, SLOT(toggleRunningAutoSave(bool)), false); m_saveIntervalSlider->setEnabled(m_enableAutoSave); m_runningAutoSave->setVisible(m_enableAutoSave); - counter = 0; - // UI effect vs. performance tab. - auto ui_fx_tw = new TabWidget(tr("User interface (UI) effects vs. performance"), performance_w); + QGroupBox * uiFxBox = new QGroupBox(tr("User interface (UI) effects vs. performance"), performance_w); + QVBoxLayout * uiFxLayout = new QVBoxLayout(uiFxBox); - addLedCheckBox(tr("Smooth scroll in song editor"), ui_fx_tw, counter, + addCheckBox(tr("Smooth scroll in song editor"), uiFxBox, uiFxLayout, m_smoothScroll, SLOT(toggleSmoothScroll(bool)), false); - addLedCheckBox(tr("Display playback cursor in AudioFileProcessor"), ui_fx_tw, counter, + addCheckBox(tr("Display playback cursor in AudioFileProcessor"), uiFxBox, uiFxLayout, m_animateAFP, SLOT(toggleAnimateAFP(bool)), false); - ui_fx_tw->setFixedHeight(YDelta + YDelta * counter); - - counter = 0; + int counter = 0; // Plugins tab. auto plugins_tw = new TabWidget(tr("Plugins"), performance_w); @@ -458,8 +469,8 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : // Performance layout ordering. - performance_layout->addWidget(auto_save_tw); - performance_layout->addWidget(ui_fx_tw); + performance_layout->addWidget(autoSaveBox); + performance_layout->addWidget(uiFxBox); performance_layout->addWidget(plugins_tw); performance_layout->addStretch(); @@ -473,13 +484,12 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : labelWidget(audio_w, tr("Audio")); - // Audio interface tab. - auto audioiface_tw = new TabWidget(tr("Audio interface"), audio_w); - audioiface_tw->setFixedHeight(56); - - m_audioInterfaces = new QComboBox(audioiface_tw); - m_audioInterfaces->setGeometry(10, 20, 240, 28); + // Audio interface group + QGroupBox * audioInterfaceBox = new QGroupBox(tr("Audio interface"), audio_w); + QVBoxLayout * audioInterfaceLayout = new QVBoxLayout(audioInterfaceBox); + m_audioInterfaces = new QComboBox(audioInterfaceBox); + audioInterfaceLayout->addWidget(m_audioInterfaces); // Ifaces-settings-widget. auto as_w = new QWidget(audio_w); @@ -569,24 +579,20 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : useNaNHandler->setChecked(m_NaNHandler); } - // HQ mode LED. - auto hqaudio = new LedCheckBox(tr("HQ mode for output audio device"), audio_w); - hqaudio->move(10, 0); - hqaudio->setChecked(m_hqAudioDev); - connect(hqaudio, SIGNAL(toggled(bool)), - this, SLOT(toggleHQAudioDev(bool))); + // HQ mode checkbox + auto hqaudio = addCheckBox(tr("HQ mode for output audio device"), audioInterfaceBox, nullptr, + m_hqAudioDev, SLOT(toggleHQAudioDev(bool)), false); + // Buffer size group + QGroupBox * bufferSizeBox = new QGroupBox(tr("Buffer size"), audio_w); + QVBoxLayout * bufferSizeLayout = new QVBoxLayout(bufferSizeBox); + QHBoxLayout * bufferSizeSubLayout = new QHBoxLayout(); - // Buffer size tab. - auto bufferSize_tw = new TabWidget(tr("Buffer size"), audio_w); - bufferSize_tw->setFixedHeight(76); - - m_bufferSizeSlider = new QSlider(Qt::Horizontal, bufferSize_tw); + m_bufferSizeSlider = new QSlider(Qt::Horizontal, bufferSizeBox); m_bufferSizeSlider->setRange(1, 128); m_bufferSizeSlider->setTickInterval(8); m_bufferSizeSlider->setPageStep(8); m_bufferSizeSlider->setValue(m_bufferSize / BUFFERSIZE_RESOLUTION); - m_bufferSizeSlider->setGeometry(10, 18, 340, 18); m_bufferSizeSlider->setTickPosition(QSlider::TicksBelow); connect(m_bufferSizeSlider, SIGNAL(valueChanged(int)), @@ -594,23 +600,28 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : connect(m_bufferSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(showRestartWarning())); - m_bufferSizeLbl = new QLabel(bufferSize_tw); - m_bufferSizeLbl->setGeometry(10, 40, 200, 24); + bufferSizeSubLayout->addWidget(m_bufferSizeSlider, 1); + + auto bufferSize_reset_btn = new QPushButton(embed::getIconPixmap("reload"), "", bufferSizeBox); + connect(bufferSize_reset_btn, SIGNAL(clicked()), + this, SLOT(resetBufferSize())); + bufferSize_reset_btn->setToolTip( + tr("Reset to default value")); + + bufferSizeSubLayout->addWidget(bufferSize_reset_btn); + bufferSizeLayout->addLayout(bufferSizeSubLayout); + + m_bufferSizeLbl = new QLabel(bufferSizeBox); setBufferSize(m_bufferSizeSlider->value()); - auto bufferSize_reset_btn = new QPushButton(embed::getIconPixmap("reload"), "", bufferSize_tw); - bufferSize_reset_btn->setGeometry(320, 40, 28, 28); - connect(bufferSize_reset_btn, SIGNAL(clicked()), - this, SLOT(resetBufferSize())); - bufferSize_reset_btn->setToolTip( - tr("Reset to default value")); + bufferSizeLayout->addWidget(m_bufferSizeLbl); // Audio layout ordering. - audio_layout->addWidget(audioiface_tw); + audio_layout->addWidget(audioInterfaceBox); audio_layout->addWidget(as_w); audio_layout->addWidget(hqaudio); - audio_layout->addWidget(bufferSize_tw); + audio_layout->addWidget(bufferSizeBox); audio_layout->addStretch(); @@ -749,29 +760,28 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : // Path selectors widget. auto pathSelectors = new QWidget(paths_w); - const int txtLength = 284; - const int btnStart = 300; - // Path selectors layout. auto pathSelectorsLayout = new QVBoxLayout; pathSelectorsLayout->setSpacing(10); auto addPathEntry = [&](const QString& caption, const QString& content, const char* setSlot, const char* openSlot, QLineEdit*& lineEdit, const char* pixmap = "project_open") { - auto newTw = new TabWidget(caption, pathSelectors); - newTw->setFixedHeight(48); + auto pathEntryGroupBox = new QGroupBox(caption, pathSelectors); + QHBoxLayout * pathEntryLayout = new QHBoxLayout(pathEntryGroupBox); - lineEdit = new QLineEdit(content, newTw); - lineEdit->setGeometry(10, 20, txtLength, 16); + lineEdit = new QLineEdit(content, pathEntryGroupBox); connect(lineEdit, SIGNAL(textChanged(const QString&)), this, setSlot); - auto selectBtn = new QPushButton(embed::getIconPixmap(pixmap, 16, 16), "", newTw); + pathEntryLayout->addWidget(lineEdit, 1); + + auto selectBtn = new QPushButton(embed::getIconPixmap(pixmap, 16, 16), "", pathEntryGroupBox); selectBtn->setFixedSize(24, 24); - selectBtn->move(btnStart, 16); connect(selectBtn, SIGNAL(clicked()), this, openSlot); - pathSelectorsLayout->addWidget(newTw); + pathEntryLayout->addWidget(selectBtn, 0); + + pathSelectorsLayout->addWidget(pathEntryGroupBox); pathSelectorsLayout->addSpacing(10); }; @@ -820,21 +830,29 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : paths_layout->addWidget(pathsScroll); paths_layout->addStretch(); + // Add all main widgets to the layout of the settings widget + // This is needed so that we automatically get the correct sizes. + settingsLayout->addWidget(general_w); + settingsLayout->addWidget(performance_w); + settingsLayout->addWidget(audio_w); + settingsLayout->addWidget(midi_w); + settingsLayout->addWidget(paths_w); + // Major tabs ordering. m_tabBar->addTab(general_w, - tr("General"), 0, false, true)->setIcon( + tr("General"), 0, false, true, false)->setIcon( embed::getIconPixmap("setup_general")); m_tabBar->addTab(performance_w, - tr("Performance"), 1, false, true)->setIcon( + tr("Performance"), 1, false, true, false)->setIcon( embed::getIconPixmap("setup_performance")); m_tabBar->addTab(audio_w, - tr("Audio"), 2, false, true)->setIcon( + tr("Audio"), 2, false, true, false)->setIcon( embed::getIconPixmap("setup_audio")); m_tabBar->addTab(midi_w, - tr("MIDI"), 3, false, true)->setIcon( + tr("MIDI"), 3, false, true, false)->setIcon( embed::getIconPixmap("setup_midi")); m_tabBar->addTab(paths_w, - tr("Paths"), 4, true, true)->setIcon( + tr("Paths"), 4, true, true, false)->setIcon( embed::getIconPixmap("setup_directories")); m_tabBar->setActiveTab(tab_to_open); @@ -877,9 +895,9 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : extras_layout->addSpacing(10); // Vertical layout ordering. - vlayout->addWidget(main_w); + vlayout->addWidget(main_w, 0); vlayout->addSpacing(10); - vlayout->addWidget(extras_w); + vlayout->addWidget(extras_w), 1; vlayout->addSpacing(10); show(); diff --git a/src/gui/widgets/TabBar.cpp b/src/gui/widgets/TabBar.cpp index 806a93252..e29494551 100644 --- a/src/gui/widgets/TabBar.cpp +++ b/src/gui/widgets/TabBar.cpp @@ -44,7 +44,7 @@ TabBar::TabBar( QWidget * _parent, QBoxLayout::Direction _dir ) : } TabButton * TabBar::addTab( QWidget * _w, const QString & _text, int _id, - bool _add_stretch, bool _text_is_tooltip ) + bool _add_stretch, bool _text_is_tooltip, bool fixWidgetToParentSize ) { // already tab with id? if( m_tabs.contains( _id ) ) @@ -83,10 +83,12 @@ TabButton * TabBar::addTab( QWidget * _w, const QString & _text, int _id, m_layout->addStretch(); } - - // we assume, parent-widget is a widget acting as widget-stack so all - // widgets have the same size and only the one on the top is visible - _w->setFixedSize( _w->parentWidget()->size() ); + if (fixWidgetToParentSize) + { + // we assume, parent-widget is a widget acting as widget-stack so all + // widgets have the same size and only the one on the top is visible + _w->setFixedSize( _w->parentWidget()->size() ); + } b->setFont( pointSize<8>( b->font() ) ); From c5c459f54f576d99a5c80094b1f36a5a13c29424 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 16 Jul 2023 20:02:11 +0200 Subject: [PATCH 02/33] Adjust Plugins group/tab Adjust the plugins group to also use a QGroupBox and QCheckBoxes. This finishes the "Performance" page and also finally enables the removal of the local variables `XDelta` and `YDelta` as well as the lambda `addLedCheckBox`. Other changes -------------- Add a TODO to some unused code for some advanced setting. Fix layout of the main widget and the buttons widget. Remove an erroneous ",1" after a statement. --- include/SetupDialog.h | 3 +- src/gui/modals/SetupDialog.cpp | 56 +++++++++------------------------- 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 7fa59d244..a68fe89cf 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -30,7 +30,6 @@ #include "AudioDevice.h" #include "AudioDeviceSetupWidget.h" -#include "LedCheckBox.h" #include "lmmsconfig.h" #include "MidiClient.h" #include "MidiSetupWidget.h" @@ -163,7 +162,7 @@ private: QLabel * m_vstEmbedLbl; QComboBox* m_vstEmbedComboBox; QString m_vstEmbedMethod; - LedCheckBox * m_vstAlwaysOnTopCheckBox; + QCheckBox * m_vstAlwaysOnTopCheckBox; bool m_vstAlwaysOnTop; bool m_disableAutoQuit; diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 97de349c4..b9a92ab78 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -39,6 +39,7 @@ #include "Engine.h" #include "FileDialog.h" #include "gui_templates.h" +#include "LedCheckBox.h" #include "MainWindow.h" #include "MidiSetupWidget.h" #include "ProjectJournal.h" @@ -167,10 +168,6 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : Engine::projectJournal()->setJournalling(false); - // Constants for positioning LED check boxes. - const int XDelta = 10; - const int YDelta = 18; - // Main widget. auto main_w = new QWidget(this); @@ -213,20 +210,6 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : auto generalControlsLayout = new QVBoxLayout; generalControlsLayout->setSpacing(10); - // TODO Remove once it is not used anymore - auto addLedCheckBox = [&XDelta, &YDelta, this](const QString& ledText, TabWidget* tw, int& counter, - bool initialState, const char* toggledSlot, bool showRestartWarning) { - auto checkBox = new LedCheckBox(ledText, tw); - counter++; - checkBox->move(XDelta, YDelta * counter); - checkBox->setChecked(initialState); - connect(checkBox, SIGNAL(toggled(bool)), this, toggledSlot); - if (showRestartWarning) - { - connect(checkBox, SIGNAL(toggled(bool)), this, SLOT(showRestartWarning())); - } - }; - auto addCheckBox = [this](const QString& ledText, QWidget* parent, QBoxLayout * layout, bool initialState, const char* toggledSlot, bool showRestartWarning) -> QCheckBox * { auto checkBox = new QCheckBox(ledText, parent); @@ -422,17 +405,15 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : m_animateAFP, SLOT(toggleAnimateAFP(bool)), false); - int counter = 0; + // Plugins group + QGroupBox * pluginsBox = new QGroupBox(tr("Plugins"), performance_w); + QVBoxLayout * pluginsLayout = new QVBoxLayout(pluginsBox); - // Plugins tab. - auto plugins_tw = new TabWidget(tr("Plugins"), performance_w); - - m_vstEmbedLbl = new QLabel(plugins_tw); - m_vstEmbedLbl->move(XDelta, YDelta * ++counter); + m_vstEmbedLbl = new QLabel(pluginsBox); m_vstEmbedLbl->setText(tr("VST plugins embedding:")); + pluginsLayout->addWidget(m_vstEmbedLbl); - m_vstEmbedComboBox = new QComboBox(plugins_tw); - m_vstEmbedComboBox->move(XDelta, YDelta * ++counter); + m_vstEmbedComboBox = new QComboBox(pluginsBox); QStringList embedMethods = ConfigManager::availableVstEmbedMethods(); m_vstEmbedComboBox->addItem(tr("No embedding"), "none"); @@ -451,27 +432,19 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : m_vstEmbedComboBox->setCurrentIndex(m_vstEmbedComboBox->findData(m_vstEmbedMethod)); connect(m_vstEmbedComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(vstEmbedMethodChanged())); + pluginsLayout->addWidget(m_vstEmbedComboBox); - counter += 2; + m_vstAlwaysOnTopCheckBox = addCheckBox(tr("Keep plugin windows on top when not embedded"), pluginsBox, pluginsLayout, + m_vstAlwaysOnTop, SLOT(toggleVSTAlwaysOnTop(bool)), false); - m_vstAlwaysOnTopCheckBox = new LedCheckBox( - tr("Keep plugin windows on top when not embedded"), plugins_tw); - m_vstAlwaysOnTopCheckBox->move(20, 66); - m_vstAlwaysOnTopCheckBox->setChecked(m_vstAlwaysOnTop); - m_vstAlwaysOnTopCheckBox->setVisible(m_vstEmbedMethod == "none"); - connect(m_vstAlwaysOnTopCheckBox, SIGNAL(toggled(bool)), - this, SLOT(toggleVSTAlwaysOnTop(bool))); - - addLedCheckBox(tr("Keep effects running even without input"), plugins_tw, counter, + addCheckBox(tr("Keep effects running even without input"), pluginsBox, pluginsLayout, m_disableAutoQuit, SLOT(toggleDisableAutoQuit(bool)), false); - plugins_tw->setFixedHeight(YDelta + YDelta * counter); - // Performance layout ordering. performance_layout->addWidget(autoSaveBox); performance_layout->addWidget(uiFxBox); - performance_layout->addWidget(plugins_tw); + performance_layout->addWidget(pluginsBox); performance_layout->addStretch(); @@ -575,6 +548,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : // Advanced setting, hidden for now if(false) { + // TODO Handle or remove. auto useNaNHandler = new LedCheckBox(tr("Use built-in NaN handler"), audio_w); useNaNHandler->setChecked(m_NaNHandler); } @@ -895,9 +869,9 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : extras_layout->addSpacing(10); // Vertical layout ordering. - vlayout->addWidget(main_w, 0); + vlayout->addWidget(main_w, 1); vlayout->addSpacing(10); - vlayout->addWidget(extras_w), 1; + vlayout->addWidget(extras_w); vlayout->addSpacing(10); show(); From cbea8f30fc4d1a821e99f67b78ab2d124431fbea Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 16 Jul 2023 20:04:55 +0200 Subject: [PATCH 03/33] Make "Paths" scroll area expandable Make the "Paths" scroll area take as much space as there is. --- src/gui/modals/SetupDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index b9a92ab78..37c5db748 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -801,7 +801,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : pathsScroll->setWidget(pathSelectors); pathsScroll->setWidgetResizable(true); - paths_layout->addWidget(pathsScroll); + paths_layout->addWidget(pathsScroll, 1); paths_layout->addStretch(); // Add all main widgets to the layout of the settings widget From c06d9d488319f88eaa698a33f61495f77722235f Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 16 Jul 2023 23:04:09 +0200 Subject: [PATCH 04/33] First adjustments to MIDI page Use group boxes and layouts for the "MIDI interface" and automatic assignment tabs. The configuration/information dialogs for the different drivers still need adjustments. --- src/gui/modals/SetupDialog.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 37c5db748..0565b8238 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -605,15 +605,14 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : auto midi_layout = new QVBoxLayout(midi_w); midi_layout->setSpacing(10); midi_layout->setContentsMargins(0, 0, 0, 0); - labelWidget(midi_w, - tr("MIDI")); + labelWidget(midi_w, tr("MIDI")); - // MIDI interface tab. - auto midiiface_tw = new TabWidget(tr("MIDI interface"), midi_w); - midiiface_tw->setFixedHeight(56); + // MIDI interface group + QGroupBox * midiInterfaceBox = new QGroupBox(tr("MIDI interface"), midi_w); + QVBoxLayout * midiInterfaceLayout = new QVBoxLayout(midiInterfaceBox); - m_midiInterfaces = new QComboBox(midiiface_tw); - m_midiInterfaces->setGeometry(10, 20, 240, 28); + m_midiInterfaces = new QComboBox(midiInterfaceBox); + midiInterfaceLayout->addWidget(m_midiInterfaces); // Ifaces-settings-widget. auto ms_w = new QWidget(midi_w); @@ -687,12 +686,12 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : this, SLOT(midiInterfaceChanged(const QString&))); - // MIDI autoassign tab. - auto midiAutoAssign_tw = new TabWidget(tr("Automatically assign MIDI controller to selected track"), midi_w); - midiAutoAssign_tw->setFixedHeight(56); + // MIDI autoassign group + QGroupBox * midiAutoAssignBox = new QGroupBox(tr("Automatically assign MIDI controller to selected track"), midi_w); + QVBoxLayout * midiAutoAssignLayout = new QVBoxLayout(midiAutoAssignBox); - m_assignableMidiDevices = new QComboBox(midiAutoAssign_tw); - m_assignableMidiDevices->setGeometry(10, 20, 240, 28); + m_assignableMidiDevices = new QComboBox(midiAutoAssignBox); + midiAutoAssignLayout->addWidget(m_assignableMidiDevices); m_assignableMidiDevices->addItem("none"); if ( !Engine::audioEngine()->midiClient()->isRaw() ) { @@ -709,9 +708,9 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : } // MIDI layout ordering. - midi_layout->addWidget(midiiface_tw); + midi_layout->addWidget(midiInterfaceBox); midi_layout->addWidget(ms_w); - midi_layout->addWidget(midiAutoAssign_tw); + midi_layout->addWidget(midiAutoAssignBox); midi_layout->addStretch(); From b53290344b358380b40d273f3c9dc0f31fdf13ef Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 22 Jul 2023 22:55:02 +0200 Subject: [PATCH 05/33] Adjust most audio driver settings dialogs Adjust the dialogs of most audio driver settings dialogs by showing them in a QGroupBox and adjusting their layouts. All dialogs now use `QFormLayout` to layout their labels and input widgets. Technical details ------------------ Introduce `AudioDeviceSetupGroupWidget` which is intended to functionally replace `AudioDeviceSetupWidget` in the long run. This is likely a temporary replacement in the sense that `AudioDeviceSetupGroupWidget` will be renamed to `AudioDeviceSetupWidget` once the latter has been replaced everywhere. Both classes are very similar and the only difference is that the former inherits from `QGroupBox` instead of `TabWidget`. Adjust the using of AswMap so that it is now defined as a map from `QString` to `AudioDeviceSetupGroupWidget`. Use `QFormLayout` to layout the widgets of the setup dialogs instead of hard coding geometries and placement. TODOs ------ Adjust the widgets for the SoundIO and SndIo cases. These will be a bit more effort as they are not compiled on my machine. --- include/AudioAlsaSetupWidget.h | 4 +- include/AudioDeviceSetupGroupWidget.h | 49 +++++++++++++++++++++++++ include/AudioDummy.h | 6 +-- include/AudioJack.h | 4 +- include/AudioOss.h | 4 +- include/AudioPortAudio.h | 4 +- include/AudioPulseAudio.h | 4 +- include/AudioSdl.h | 4 +- include/SetupDialog.h | 4 +- src/core/audio/AudioJack.cpp | 15 ++++---- src/core/audio/AudioOss.cpp | 16 ++++---- src/core/audio/AudioPortAudio.cpp | 20 ++++------ src/core/audio/AudioPulseAudio.cpp | 19 ++++------ src/core/audio/AudioSdl.cpp | 12 +++--- src/gui/AudioAlsaSetupWidget.cpp | 14 +++---- src/gui/AudioDeviceSetupGroupWidget.cpp | 42 +++++++++++++++++++++ src/gui/CMakeLists.txt | 1 + src/gui/modals/SetupDialog.cpp | 5 ++- 18 files changed, 152 insertions(+), 75 deletions(-) create mode 100644 include/AudioDeviceSetupGroupWidget.h create mode 100644 src/gui/AudioDeviceSetupGroupWidget.cpp diff --git a/include/AudioAlsaSetupWidget.h b/include/AudioAlsaSetupWidget.h index cbe99ba01..9795a3588 100644 --- a/include/AudioAlsaSetupWidget.h +++ b/include/AudioAlsaSetupWidget.h @@ -29,7 +29,7 @@ #ifdef LMMS_HAVE_ALSA -#include "AudioDeviceSetupWidget.h" +#include "AudioDeviceSetupGroupWidget.h" #include "AudioAlsa.h" @@ -41,7 +41,7 @@ namespace lmms::gui class LcdSpinBox; -class AudioAlsaSetupWidget : public AudioDeviceSetupWidget +class AudioAlsaSetupWidget : public AudioDeviceSetupGroupWidget { Q_OBJECT diff --git a/include/AudioDeviceSetupGroupWidget.h b/include/AudioDeviceSetupGroupWidget.h new file mode 100644 index 000000000..8980d0a65 --- /dev/null +++ b/include/AudioDeviceSetupGroupWidget.h @@ -0,0 +1,49 @@ +/* + * AudioDeviceSetupGroupWidget.h - Base class for audio device setup widgets using group box + * + * Copyright (c) 2004-2015 Tobias Doerffel + * Copyright (c) 2004-2015 Michael Gregorius + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GUI_AUDIO_DEVICE_SETUP_GROUP_WIDGET_H +#define LMMS_GUI_AUDIO_DEVICE_SETUP_GROUP_WIDGET_H + +#include + +namespace lmms::gui +{ + +class AudioDeviceSetupGroupWidget : public QGroupBox +{ + Q_OBJECT +public: + AudioDeviceSetupGroupWidget( const QString & _caption, QWidget * _parent ); + + ~AudioDeviceSetupGroupWidget() override = default; + + virtual void saveSettings() = 0; + + virtual void show(); +}; + +} // namespace lmms::gui + +#endif // LMMS_GUI_AUDIO_DEVICE_SETUP_GROUP_WIDGET_H diff --git a/include/AudioDummy.h b/include/AudioDummy.h index e34260171..c7ac3d67f 100644 --- a/include/AudioDummy.h +++ b/include/AudioDummy.h @@ -26,7 +26,7 @@ #define LMMS_AUDIO_DUMMY_H #include "AudioDevice.h" -#include "AudioDeviceSetupWidget.h" +#include "AudioDeviceSetupGroupWidget.h" #include "AudioEngine.h" #include "MicroTimer.h" @@ -54,11 +54,11 @@ public: } - class setupWidget : public gui::AudioDeviceSetupWidget + class setupWidget : public gui::AudioDeviceSetupGroupWidget { public: setupWidget( QWidget * _parent ) : - gui::AudioDeviceSetupWidget( AudioDummy::name(), _parent ) + gui::AudioDeviceSetupGroupWidget( AudioDummy::name(), _parent ) { } diff --git a/include/AudioJack.h b/include/AudioJack.h index 2ef0f665c..4135dc5e0 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -38,7 +38,7 @@ #include #include "AudioDevice.h" -#include "AudioDeviceSetupWidget.h" +#include "AudioDeviceSetupGroupWidget.h" class QLineEdit; @@ -74,7 +74,7 @@ public: } -class setupWidget : public gui::AudioDeviceSetupWidget +class setupWidget : public gui::AudioDeviceSetupGroupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioOss.h b/include/AudioOss.h index 55f64de85..953c72a4f 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -32,7 +32,7 @@ #include #include "AudioDevice.h" -#include "AudioDeviceSetupWidget.h" +#include "AudioDeviceSetupGroupWidget.h" class QLineEdit; @@ -61,7 +61,7 @@ public: static QString probeDevice(); -class setupWidget : public gui::AudioDeviceSetupWidget +class setupWidget : public gui::AudioDeviceSetupGroupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index 01b8f3fd7..a926b1bf0 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -35,7 +35,7 @@ # include # include "AudioDevice.h" -# include "AudioDeviceSetupWidget.h" +# include "AudioDeviceSetupGroupWidget.h" # if defined paNeverDropInput || defined paNonInterleaved # define PORTAUDIO_V19 @@ -90,7 +90,7 @@ public: unsigned long _framesPerBuffer ); - class setupWidget : public gui::AudioDeviceSetupWidget + class setupWidget : public gui::AudioDeviceSetupGroupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index b6a998274..22e47896d 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -34,7 +34,7 @@ #include #include "AudioDevice.h" -#include "AudioDeviceSetupWidget.h" +#include "AudioDeviceSetupGroupWidget.h" class QLineEdit; @@ -62,7 +62,7 @@ public: static QString probeDevice(); - class setupWidget : public gui::AudioDeviceSetupWidget + class setupWidget : public gui::AudioDeviceSetupGroupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioSdl.h b/include/AudioSdl.h index 62db8b68a..5f29596a5 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -37,7 +37,7 @@ #endif #include "AudioDevice.h" -#include "AudioDeviceSetupWidget.h" +#include "AudioDeviceSetupGroupWidget.h" class QLineEdit; @@ -57,7 +57,7 @@ public: } - class setupWidget : public gui::AudioDeviceSetupWidget + class setupWidget : public gui::AudioDeviceSetupGroupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/SetupDialog.h b/include/SetupDialog.h index a68fe89cf..7097b3206 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -29,7 +29,7 @@ #include #include "AudioDevice.h" -#include "AudioDeviceSetupWidget.h" +#include "AudioDeviceSetupGroupWidget.h" #include "lmmsconfig.h" #include "MidiClient.h" #include "MidiSetupWidget.h" @@ -166,7 +166,7 @@ private: bool m_vstAlwaysOnTop; bool m_disableAutoQuit; - using AswMap = QMap; + using AswMap = QMap; using MswMap = QMap; using trMap = QMap; diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 222ebf10d..00024de63 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -27,8 +27,8 @@ #ifdef LMMS_HAVE_JACK #include -#include #include +#include #include "Engine.h" #include "GuiApplication.h" @@ -452,19 +452,18 @@ void AudioJack::shutdownCallback( void * _udata ) AudioJack::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupWidget( AudioJack::name(), _parent ) + AudioDeviceSetupGroupWidget( AudioJack::name(), _parent ) { + QFormLayout * form = new QFormLayout(this); + QString cn = ConfigManager::inst()->value( "audiojack", "clientname" ); if( cn.isEmpty() ) { cn = "lmms"; } m_clientName = new QLineEdit( cn, this ); - m_clientName->setGeometry( 10, 20, 160, 20 ); - auto cn_lbl = new QLabel(tr("Client name"), this); - cn_lbl->setFont( pointSize<7>( cn_lbl->font() ) ); - cn_lbl->setGeometry( 10, 40, 160, 10 ); + form->addRow(tr("Client name"), m_clientName); auto m = new gui::LcdSpinBoxModel(/* this */); m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); @@ -474,8 +473,8 @@ AudioJack::setupWidget::setupWidget( QWidget * _parent ) : m_channels = new gui::LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "Channels" ) ); - m_channels->move( 180, 20 ); + + form->addRow(tr("Channels"), m_channels); } diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index 5166bad79..5e0a8fc89 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -27,7 +27,7 @@ #ifdef LMMS_HAVE_OSS #include -#include +#include > #include #include "endian_handling.h" @@ -318,14 +318,13 @@ void AudioOss::run() AudioOss::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupWidget( AudioOss::name(), _parent ) + AudioDeviceSetupGroupWidget( AudioOss::name(), _parent ) { - m_device = new QLineEdit( probeDevice(), this ); - m_device->setGeometry( 10, 20, 160, 20 ); + QFormLayout * form = new QFormLayout(this); - auto dev_lbl = new QLabel(tr("Device"), this); - dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); - dev_lbl->setGeometry( 10, 40, 160, 10 ); + m_device = new QLineEdit( probeDevice(), this ); + + form->addRow(tr("Device"), m_device); auto m = new gui::LcdSpinBoxModel(/* this */); m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); @@ -335,9 +334,8 @@ AudioOss::setupWidget::setupWidget( QWidget * _parent ) : m_channels = new gui::LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "Channels" ) ); - m_channels->move( 180, 20 ); + form->addRow(tr("Channels"), m_channels); } diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index 0f5a4122f..cb0e4616c 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -49,7 +49,7 @@ void AudioPortAudioSetupUtil::updateChannels() #ifdef LMMS_HAVE_PORTAUDIO -#include +#include #include "Engine.h" #include "ConfigManager.h" @@ -415,23 +415,17 @@ void AudioPortAudioSetupUtil::updateChannels() AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupWidget( AudioPortAudio::name(), _parent ) + AudioDeviceSetupGroupWidget( AudioPortAudio::name(), _parent ) { using gui::ComboBox; - m_backend = new ComboBox( this, "BACKEND" ); - m_backend->setGeometry( 64, 15, 260, ComboBox::DEFAULT_HEIGHT ); + QFormLayout * form = new QFormLayout(this); - auto backend_lbl = new QLabel(tr("Backend"), this); - backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) ); - backend_lbl->move( 8, 18 ); + m_backend = new ComboBox( this, "BACKEND" ); + form->addRow(tr("Backend"), m_backend); m_device = new ComboBox( this, "DEVICE" ); - m_device->setGeometry( 64, 35, 260, ComboBox::DEFAULT_HEIGHT ); - - auto dev_lbl = new QLabel(tr("Device"), this); - dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); - dev_lbl->move( 8, 38 ); + form->addRow(tr("Device"), m_device); /* LcdSpinBoxModel * m = new LcdSpinBoxModel( ); m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); @@ -505,7 +499,7 @@ void AudioPortAudio::setupWidget::show() m_setupUtil.m_deviceModel.setValue( i ); } - AudioDeviceSetupWidget::show(); + AudioDeviceSetupGroupWidget::show(); } } // namespace lmms diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index bac997075..24913c60f 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -23,7 +23,7 @@ */ #include -#include +#include #include "AudioPulseAudio.h" @@ -310,26 +310,23 @@ void AudioPulseAudio::signalConnected( bool connected ) AudioPulseAudio::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupWidget( AudioPulseAudio::name(), _parent ) + AudioDeviceSetupGroupWidget( AudioPulseAudio::name(), _parent ) { + QFormLayout * form = new QFormLayout(this); + m_device = new QLineEdit( AudioPulseAudio::probeDevice(), this ); - m_device->setGeometry( 10, 20, 160, 20 ); + form->addRow(tr("Device"), m_device); - auto dev_lbl = new QLabel(tr("Device"), this); - dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); - dev_lbl->setGeometry( 10, 40, 160, 10 ); - - auto m = new gui::LcdSpinBoxModel(/* this */); + auto m = new gui::LcdSpinBoxModel(); m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); m->setStep( 2 ); m->setValue( ConfigManager::inst()->value( "audiopa", - "channels" ).toInt() ); + "channels" ).toInt() ); m_channels = new gui::LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "Channels" ) ); - m_channels->move( 180, 20 ); + form->addRow(tr("Channels"), m_channels); } diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index e73ccadc1..071877f0d 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -26,7 +26,7 @@ #ifdef LMMS_HAVE_SDL -#include +#include > #include #include @@ -325,16 +325,14 @@ void AudioSdl::sdlInputAudioCallback(Uint8 *_buf, int _len) { #endif AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupWidget( AudioSdl::name(), _parent ) + AudioDeviceSetupGroupWidget( AudioSdl::name(), _parent ) { + QFormLayout * form = new QFormLayout(this); + QString dev = ConfigManager::inst()->value( "audiosdl", "device" ); m_device = new QLineEdit( dev, this ); - m_device->setGeometry( 10, 20, 160, 20 ); - - auto dev_lbl = new QLabel(tr("Device"), this); - dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); - dev_lbl->setGeometry( 10, 40, 160, 10 ); + form->addRow(tr("Device"), m_device); } diff --git a/src/gui/AudioAlsaSetupWidget.cpp b/src/gui/AudioAlsaSetupWidget.cpp index 4ea6d4c58..015f4e8ad 100644 --- a/src/gui/AudioAlsaSetupWidget.cpp +++ b/src/gui/AudioAlsaSetupWidget.cpp @@ -23,7 +23,7 @@ */ #include -#include +#include #include "AudioAlsaSetupWidget.h" @@ -37,9 +37,11 @@ namespace lmms::gui { AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : - AudioDeviceSetupWidget( AudioAlsa::name(), _parent ), + AudioDeviceSetupGroupWidget( AudioAlsa::name(), _parent ), m_selectedDevice(-1) { + QFormLayout * form = new QFormLayout(this); + m_deviceInfos = AudioAlsa::getAvailableDevices(); QString deviceText = ConfigManager::inst()->value( "audioalsa", "device" ); @@ -62,14 +64,11 @@ AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : m_selectedDevice = m_deviceComboBox->currentIndex(); - m_deviceComboBox->setGeometry( 10, 20, 160, 20 ); connect(m_deviceComboBox, SIGNAL(currentIndexChanged(int)), SLOT(onCurrentIndexChanged(int))); - auto dev_lbl = new QLabel(tr("DEVICE"), this); - dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); - dev_lbl->setGeometry( 10, 40, 160, 10 ); + form->addRow(tr("Device"), m_deviceComboBox); auto m = new LcdSpinBoxModel(/* this */); m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); @@ -79,9 +78,8 @@ AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : m_channels = new LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "CHANNELS" ) ); - m_channels->move( 180, 20 ); + form->addRow(tr("Channels"), m_channels); } diff --git a/src/gui/AudioDeviceSetupGroupWidget.cpp b/src/gui/AudioDeviceSetupGroupWidget.cpp new file mode 100644 index 000000000..f84c8bb8b --- /dev/null +++ b/src/gui/AudioDeviceSetupGroupWidget.cpp @@ -0,0 +1,42 @@ +/* + * AudioDeviceSetupGroupWidget.cpp - Base class for audio device setup widgets using group box + * + * Copyright (c) 2004-2015 Tobias Doerffel + * Copyright (c) 2004-2015 Michael Gregorius + * + * 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 "AudioDeviceSetupGroupWidget.h" + +namespace lmms::gui +{ + +AudioDeviceSetupGroupWidget::AudioDeviceSetupGroupWidget(const QString & caption, QWidget * parent) : + QGroupBox(QGroupBox::tr("Settings for %1").arg(tr(caption.toUtf8())), parent) +{ +} + +void AudioDeviceSetupGroupWidget::show() +{ + parentWidget()->show(); + QWidget::show(); +} + +} // namespace lmms::gui diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 9f940c035..3ae16c573 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -3,6 +3,7 @@ SET(LMMS_SRCS gui/ActionGroup.cpp gui/AudioAlsaSetupWidget.cpp gui/AudioDeviceSetupWidget.cpp + gui/AudioDeviceSetupGroupWidget.cpp gui/AutomatableModelView.cpp gui/ControlLayout.cpp gui/ControllerDialog.cpp diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 0565b8238..d1375c033 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -466,7 +466,6 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : // Ifaces-settings-widget. auto as_w = new QWidget(audio_w); - as_w->setFixedHeight(60); auto as_w_layout = new QHBoxLayout(as_w); as_w_layout->setSpacing(0); @@ -492,6 +491,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : new AudioPortAudio::setupWidget(as_w); #endif +// TODO !!! #ifdef LMMS_HAVE_SOUNDIO m_audioIfaceSetupWidgets[AudioSoundIo::name()] = new AudioSoundIo::setupWidget(as_w); @@ -507,6 +507,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : new AudioOss::setupWidget(as_w); #endif +// TODO !!! #ifdef LMMS_HAVE_SNDIO m_audioIfaceSetupWidgets[AudioSndio::name()] = new AudioSndio::setupWidget(as_w); @@ -520,7 +521,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : it != m_audioIfaceSetupWidgets.end(); ++it) { m_audioIfaceNames[ - AudioDeviceSetupWidget::tr(it.key().toUtf8())] = it.key(); + AudioDeviceSetupGroupWidget::tr(it.key().toUtf8())] = it.key(); } for(trMap::iterator it = m_audioIfaceNames.begin(); it != m_audioIfaceNames.end(); ++it) From 345886954ecd2e1639cf46af9b9b747237d66b3a Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 22 Jul 2023 23:03:31 +0200 Subject: [PATCH 06/33] Adjust the MIDI setup dialog Adjust the MIDI setup dialog: * Show it in a QGroupBox * Make it use a `QFormLayout` to layout the label and line edit. Remove the hard coded height. --- include/MidiSetupWidget.h | 4 ++-- src/gui/MidiSetupWidget.cpp | 13 ++++++------- src/gui/modals/SetupDialog.cpp | 1 - 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/include/MidiSetupWidget.h b/include/MidiSetupWidget.h index a61b606ac..7b6606017 100644 --- a/include/MidiSetupWidget.h +++ b/include/MidiSetupWidget.h @@ -25,7 +25,7 @@ #ifndef LMMS_GUI_MIDI_SETUP_WIDGET_H #define LMMS_GUI_MIDI_SETUP_WIDGET_H -#include "TabWidget.h" +#include class QLineEdit; @@ -33,7 +33,7 @@ namespace lmms::gui { -class MidiSetupWidget : public TabWidget +class MidiSetupWidget : public QGroupBox { Q_OBJECT MidiSetupWidget( const QString & caption, const QString & configSection, diff --git a/src/gui/MidiSetupWidget.cpp b/src/gui/MidiSetupWidget.cpp index 4f620fb0e..d18c19bab 100644 --- a/src/gui/MidiSetupWidget.cpp +++ b/src/gui/MidiSetupWidget.cpp @@ -24,7 +24,7 @@ #include "MidiSetupWidget.h" -#include +#include > #include #include "ConfigManager.h" @@ -37,7 +37,7 @@ namespace lmms::gui MidiSetupWidget::MidiSetupWidget(const QString & caption, const QString & configSection, const QString & devName, QWidget * parent) : - TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent), + QGroupBox(QGroupBox::tr("Settings for %1").arg(tr(caption.toUtf8())), parent), m_configSection(configSection), m_device(nullptr) { @@ -45,12 +45,11 @@ MidiSetupWidget::MidiSetupWidget(const QString & caption, const QString & config // to indicate that there is no editable device field if (!devName.isNull()) { - m_device = new QLineEdit(devName, this); - m_device->setGeometry(10, 20, 160, 20); + QFormLayout * form = new QFormLayout(this); - auto dev_lbl = new QLabel(tr("Device"), this); - dev_lbl->setFont(pointSize<7>(dev_lbl->font())); - dev_lbl->setGeometry(10, 40, 160, 10); + m_device = new QLineEdit(devName, this); + + form->addRow(tr("Device"), m_device); } } diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index d1375c033..2b6c199ba 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -617,7 +617,6 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : // Ifaces-settings-widget. auto ms_w = new QWidget(midi_w); - ms_w->setFixedHeight(60); auto ms_w_layout = new QHBoxLayout(ms_w); ms_w_layout->setSpacing(0); From 095b2066352171909b7810e05062c0d08f3cbabe Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Tue, 25 Jul 2023 17:36:48 +0200 Subject: [PATCH 07/33] Remove unused include Remove an unused include of "AudioDeviceSetupWidget.h". --- src/gui/modals/SetupDialog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 2b6c199ba..87a54a993 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -32,7 +32,6 @@ #include #include -#include "AudioDeviceSetupWidget.h" #include "AudioEngine.h" #include "debug.h" #include "embed.h" From 9282e6c91bd212bd9cbcfd0618e97164f1b80786 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Tue, 25 Jul 2023 18:03:18 +0200 Subject: [PATCH 08/33] Remove temporary AudioDeviceSetupGroupWidget Remove the temporary class `AudioDeviceSetupGroupWidget` and move its functionality into `AudioDeviceSetupWidget`. This means to let `AudioDeviceSetupWidget` inherit from `QGroupBox` instead of `TabWidget`. Adjust all inheriting classes accordingly. Adjust usages in SetupDialog. The result should be that even the cases for `LMMS_HAVE_SOUNDIO` and `LMMS_HAVE_SNDIO` should compile again. Although their layout might look weird. --- include/AudioAlsaSetupWidget.h | 4 +- include/AudioDeviceSetupGroupWidget.h | 49 ------------------------- include/AudioDeviceSetupWidget.h | 5 ++- include/AudioDummy.h | 6 +-- include/AudioJack.h | 4 +- include/AudioOss.h | 4 +- include/AudioPortAudio.h | 4 +- include/AudioPulseAudio.h | 4 +- include/AudioSdl.h | 4 +- include/SetupDialog.h | 4 +- src/core/audio/AudioJack.cpp | 2 +- src/core/audio/AudioOss.cpp | 2 +- src/core/audio/AudioPortAudio.cpp | 4 +- src/core/audio/AudioPulseAudio.cpp | 2 +- src/core/audio/AudioSdl.cpp | 2 +- src/gui/AudioAlsaSetupWidget.cpp | 2 +- src/gui/AudioDeviceSetupGroupWidget.cpp | 42 --------------------- src/gui/AudioDeviceSetupWidget.cpp | 4 +- src/gui/CMakeLists.txt | 1 - src/gui/modals/SetupDialog.cpp | 2 +- 20 files changed, 30 insertions(+), 121 deletions(-) delete mode 100644 include/AudioDeviceSetupGroupWidget.h delete mode 100644 src/gui/AudioDeviceSetupGroupWidget.cpp diff --git a/include/AudioAlsaSetupWidget.h b/include/AudioAlsaSetupWidget.h index 9795a3588..cbe99ba01 100644 --- a/include/AudioAlsaSetupWidget.h +++ b/include/AudioAlsaSetupWidget.h @@ -29,7 +29,7 @@ #ifdef LMMS_HAVE_ALSA -#include "AudioDeviceSetupGroupWidget.h" +#include "AudioDeviceSetupWidget.h" #include "AudioAlsa.h" @@ -41,7 +41,7 @@ namespace lmms::gui class LcdSpinBox; -class AudioAlsaSetupWidget : public AudioDeviceSetupGroupWidget +class AudioAlsaSetupWidget : public AudioDeviceSetupWidget { Q_OBJECT diff --git a/include/AudioDeviceSetupGroupWidget.h b/include/AudioDeviceSetupGroupWidget.h deleted file mode 100644 index 8980d0a65..000000000 --- a/include/AudioDeviceSetupGroupWidget.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * AudioDeviceSetupGroupWidget.h - Base class for audio device setup widgets using group box - * - * Copyright (c) 2004-2015 Tobias Doerffel - * Copyright (c) 2004-2015 Michael Gregorius - * - * This file is part of LMMS - https://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#ifndef LMMS_GUI_AUDIO_DEVICE_SETUP_GROUP_WIDGET_H -#define LMMS_GUI_AUDIO_DEVICE_SETUP_GROUP_WIDGET_H - -#include - -namespace lmms::gui -{ - -class AudioDeviceSetupGroupWidget : public QGroupBox -{ - Q_OBJECT -public: - AudioDeviceSetupGroupWidget( const QString & _caption, QWidget * _parent ); - - ~AudioDeviceSetupGroupWidget() override = default; - - virtual void saveSettings() = 0; - - virtual void show(); -}; - -} // namespace lmms::gui - -#endif // LMMS_GUI_AUDIO_DEVICE_SETUP_GROUP_WIDGET_H diff --git a/include/AudioDeviceSetupWidget.h b/include/AudioDeviceSetupWidget.h index f56fa07a6..acc99602d 100644 --- a/include/AudioDeviceSetupWidget.h +++ b/include/AudioDeviceSetupWidget.h @@ -2,6 +2,7 @@ * AudioDeviceSetupWidget.h - Base class for audio device setup widgets * * Copyright (c) 2004-2015 Tobias Doerffel + * Copyright (c) 2023- Michael Gregorius * * This file is part of LMMS - https://lmms.io * @@ -25,12 +26,12 @@ #ifndef LMMS_GUI_AUDIO_DEVICE_SETUP_WIDGET_H #define LMMS_GUI_AUDIO_DEVICE_SETUP_WIDGET_H -#include "TabWidget.h" +#include namespace lmms::gui { -class AudioDeviceSetupWidget : public TabWidget +class AudioDeviceSetupWidget : public QGroupBox { Q_OBJECT public: diff --git a/include/AudioDummy.h b/include/AudioDummy.h index c7ac3d67f..e34260171 100644 --- a/include/AudioDummy.h +++ b/include/AudioDummy.h @@ -26,7 +26,7 @@ #define LMMS_AUDIO_DUMMY_H #include "AudioDevice.h" -#include "AudioDeviceSetupGroupWidget.h" +#include "AudioDeviceSetupWidget.h" #include "AudioEngine.h" #include "MicroTimer.h" @@ -54,11 +54,11 @@ public: } - class setupWidget : public gui::AudioDeviceSetupGroupWidget + class setupWidget : public gui::AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ) : - gui::AudioDeviceSetupGroupWidget( AudioDummy::name(), _parent ) + gui::AudioDeviceSetupWidget( AudioDummy::name(), _parent ) { } diff --git a/include/AudioJack.h b/include/AudioJack.h index 4135dc5e0..2ef0f665c 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -38,7 +38,7 @@ #include #include "AudioDevice.h" -#include "AudioDeviceSetupGroupWidget.h" +#include "AudioDeviceSetupWidget.h" class QLineEdit; @@ -74,7 +74,7 @@ public: } -class setupWidget : public gui::AudioDeviceSetupGroupWidget +class setupWidget : public gui::AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioOss.h b/include/AudioOss.h index 953c72a4f..55f64de85 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -32,7 +32,7 @@ #include #include "AudioDevice.h" -#include "AudioDeviceSetupGroupWidget.h" +#include "AudioDeviceSetupWidget.h" class QLineEdit; @@ -61,7 +61,7 @@ public: static QString probeDevice(); -class setupWidget : public gui::AudioDeviceSetupGroupWidget +class setupWidget : public gui::AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index a926b1bf0..01b8f3fd7 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -35,7 +35,7 @@ # include # include "AudioDevice.h" -# include "AudioDeviceSetupGroupWidget.h" +# include "AudioDeviceSetupWidget.h" # if defined paNeverDropInput || defined paNonInterleaved # define PORTAUDIO_V19 @@ -90,7 +90,7 @@ public: unsigned long _framesPerBuffer ); - class setupWidget : public gui::AudioDeviceSetupGroupWidget + class setupWidget : public gui::AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index 22e47896d..b6a998274 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -34,7 +34,7 @@ #include #include "AudioDevice.h" -#include "AudioDeviceSetupGroupWidget.h" +#include "AudioDeviceSetupWidget.h" class QLineEdit; @@ -62,7 +62,7 @@ public: static QString probeDevice(); - class setupWidget : public gui::AudioDeviceSetupGroupWidget + class setupWidget : public gui::AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/AudioSdl.h b/include/AudioSdl.h index 5f29596a5..62db8b68a 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -37,7 +37,7 @@ #endif #include "AudioDevice.h" -#include "AudioDeviceSetupGroupWidget.h" +#include "AudioDeviceSetupWidget.h" class QLineEdit; @@ -57,7 +57,7 @@ public: } - class setupWidget : public gui::AudioDeviceSetupGroupWidget + class setupWidget : public gui::AudioDeviceSetupWidget { public: setupWidget( QWidget * _parent ); diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 7097b3206..a68fe89cf 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -29,7 +29,7 @@ #include #include "AudioDevice.h" -#include "AudioDeviceSetupGroupWidget.h" +#include "AudioDeviceSetupWidget.h" #include "lmmsconfig.h" #include "MidiClient.h" #include "MidiSetupWidget.h" @@ -166,7 +166,7 @@ private: bool m_vstAlwaysOnTop; bool m_disableAutoQuit; - using AswMap = QMap; + using AswMap = QMap; using MswMap = QMap; using trMap = QMap; diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 00024de63..75d253671 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -452,7 +452,7 @@ void AudioJack::shutdownCallback( void * _udata ) AudioJack::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupGroupWidget( AudioJack::name(), _parent ) + AudioDeviceSetupWidget( AudioJack::name(), _parent ) { QFormLayout * form = new QFormLayout(this); diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index 5e0a8fc89..b1b50907b 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -318,7 +318,7 @@ void AudioOss::run() AudioOss::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupGroupWidget( AudioOss::name(), _parent ) + AudioDeviceSetupWidget( AudioOss::name(), _parent ) { QFormLayout * form = new QFormLayout(this); diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index cb0e4616c..c52ae8640 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -415,7 +415,7 @@ void AudioPortAudioSetupUtil::updateChannels() AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupGroupWidget( AudioPortAudio::name(), _parent ) + AudioDeviceSetupWidget( AudioPortAudio::name(), _parent ) { using gui::ComboBox; @@ -499,7 +499,7 @@ void AudioPortAudio::setupWidget::show() m_setupUtil.m_deviceModel.setValue( i ); } - AudioDeviceSetupGroupWidget::show(); + AudioDeviceSetupWidget::show(); } } // namespace lmms diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index 24913c60f..390bd0ba8 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -310,7 +310,7 @@ void AudioPulseAudio::signalConnected( bool connected ) AudioPulseAudio::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupGroupWidget( AudioPulseAudio::name(), _parent ) + AudioDeviceSetupWidget( AudioPulseAudio::name(), _parent ) { QFormLayout * form = new QFormLayout(this); diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index 071877f0d..794c486de 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -325,7 +325,7 @@ void AudioSdl::sdlInputAudioCallback(Uint8 *_buf, int _len) { #endif AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : - AudioDeviceSetupGroupWidget( AudioSdl::name(), _parent ) + AudioDeviceSetupWidget( AudioSdl::name(), _parent ) { QFormLayout * form = new QFormLayout(this); diff --git a/src/gui/AudioAlsaSetupWidget.cpp b/src/gui/AudioAlsaSetupWidget.cpp index 015f4e8ad..7db822b4b 100644 --- a/src/gui/AudioAlsaSetupWidget.cpp +++ b/src/gui/AudioAlsaSetupWidget.cpp @@ -37,7 +37,7 @@ namespace lmms::gui { AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : - AudioDeviceSetupGroupWidget( AudioAlsa::name(), _parent ), + AudioDeviceSetupWidget( AudioAlsa::name(), _parent ), m_selectedDevice(-1) { QFormLayout * form = new QFormLayout(this); diff --git a/src/gui/AudioDeviceSetupGroupWidget.cpp b/src/gui/AudioDeviceSetupGroupWidget.cpp deleted file mode 100644 index f84c8bb8b..000000000 --- a/src/gui/AudioDeviceSetupGroupWidget.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * AudioDeviceSetupGroupWidget.cpp - Base class for audio device setup widgets using group box - * - * Copyright (c) 2004-2015 Tobias Doerffel - * Copyright (c) 2004-2015 Michael Gregorius - * - * 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 "AudioDeviceSetupGroupWidget.h" - -namespace lmms::gui -{ - -AudioDeviceSetupGroupWidget::AudioDeviceSetupGroupWidget(const QString & caption, QWidget * parent) : - QGroupBox(QGroupBox::tr("Settings for %1").arg(tr(caption.toUtf8())), parent) -{ -} - -void AudioDeviceSetupGroupWidget::show() -{ - parentWidget()->show(); - QWidget::show(); -} - -} // namespace lmms::gui diff --git a/src/gui/AudioDeviceSetupWidget.cpp b/src/gui/AudioDeviceSetupWidget.cpp index b78800cec..98d03638f 100644 --- a/src/gui/AudioDeviceSetupWidget.cpp +++ b/src/gui/AudioDeviceSetupWidget.cpp @@ -28,7 +28,7 @@ namespace lmms::gui { AudioDeviceSetupWidget::AudioDeviceSetupWidget(const QString & caption, QWidget * parent) : - TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toUtf8())), parent) + QGroupBox(QGroupBox::tr("Settings for %1").arg(tr(caption.toUtf8())), parent) { } @@ -38,4 +38,4 @@ void AudioDeviceSetupWidget::show() QWidget::show(); } -} // namespace lmms::gui \ No newline at end of file +} // namespace lmms::gui diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 3ae16c573..9f940c035 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -3,7 +3,6 @@ SET(LMMS_SRCS gui/ActionGroup.cpp gui/AudioAlsaSetupWidget.cpp gui/AudioDeviceSetupWidget.cpp - gui/AudioDeviceSetupGroupWidget.cpp gui/AutomatableModelView.cpp gui/ControlLayout.cpp gui/ControllerDialog.cpp diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 87a54a993..23e1fe5d5 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -520,7 +520,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : it != m_audioIfaceSetupWidgets.end(); ++it) { m_audioIfaceNames[ - AudioDeviceSetupGroupWidget::tr(it.key().toUtf8())] = it.key(); + AudioDeviceSetupWidget::tr(it.key().toUtf8())] = it.key(); } for(trMap::iterator it = m_audioIfaceNames.begin(); it != m_audioIfaceNames.end(); ++it) From a429c2f94f338579176a81f230f0253b13e84650 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Tue, 25 Jul 2023 18:15:37 +0200 Subject: [PATCH 09/33] Adjustment of AudioSndio Adjust the layout that's created in AudioSndio::setupWidget::setupWidget by using a QFormLayout. This was done in a "blind" fashion as I am not able to compile this code. Adjustments are very similar to the ones done in AudioPulseAudio::setupWidget::setupWidget with commit b53290344b3 though. --- src/core/audio/AudioSndio.cpp | 13 +++++-------- src/gui/modals/SetupDialog.cpp | 1 - 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/core/audio/AudioSndio.cpp b/src/core/audio/AudioSndio.cpp index a8ea34ce1..8a1e00653 100644 --- a/src/core/audio/AudioSndio.cpp +++ b/src/core/audio/AudioSndio.cpp @@ -28,7 +28,7 @@ #ifdef LMMS_HAVE_SNDIO #include -#include +#include #include #include "endian_handling.h" @@ -183,12 +183,10 @@ void AudioSndio::run() AudioSndio::setupWidget::setupWidget( QWidget * _parent ) : AudioDeviceSetupWidget( AudioSndio::name(), _parent ) { - m_device = new QLineEdit( "", this ); - m_device->setGeometry( 10, 20, 160, 20 ); + QFormLayout * form = new QFormLayout(this); - QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); - dev_lbl->setFont( pointSize<6>( dev_lbl->font() ) ); - dev_lbl->setGeometry( 10, 40, 160, 10 ); + m_device = new QLineEdit( "", this ); + form->addRow(tr("Device"), m_device); gui::LcdSpinBoxModel * m = new gui::LcdSpinBoxModel( /* this */ ); m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); @@ -198,9 +196,8 @@ AudioSndio::setupWidget::setupWidget( QWidget * _parent ) : m_channels = new gui::LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "Channels" ) ); - m_channels->move( 180, 20 ); + form->addRow(tr("Channels"), m_channels); } diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 23e1fe5d5..7da234d07 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -506,7 +506,6 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : new AudioOss::setupWidget(as_w); #endif -// TODO !!! #ifdef LMMS_HAVE_SNDIO m_audioIfaceSetupWidgets[AudioSndio::name()] = new AudioSndio::setupWidget(as_w); From 0759da2776139c59c98f16753d6ec1664cab44e8 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Tue, 25 Jul 2023 18:28:04 +0200 Subject: [PATCH 10/33] Layout adjustments for AudioSoundIo Use a QFormLayout in AudioSoundIo::setupWidget::setupWidget. Note: Changes have been done "blindly" as I am not able to compile this code. --- src/core/audio/AudioSoundIo.cpp | 16 +++++----------- src/gui/modals/SetupDialog.cpp | 1 - 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp index 633808204..23385960f 100644 --- a/src/core/audio/AudioSoundIo.cpp +++ b/src/core/audio/AudioSoundIo.cpp @@ -26,7 +26,7 @@ #ifdef LMMS_HAVE_SOUNDIO -#include +#include #include #include "Engine.h" @@ -451,19 +451,13 @@ AudioSoundIo::setupWidget::setupWidget( QWidget * _parent ) : { m_setupUtil.m_setupWidget = this; - m_backend = new gui::ComboBox( this, "BACKEND" ); - m_backend->setGeometry( 64, 15, 260, 20 ); + QFormLayout * form = new QFormLayout(this); - QLabel * backend_lbl = new QLabel( tr( "Backend" ), this ); - backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) ); - backend_lbl->move( 8, 18 ); + m_backend = new gui::ComboBox( this, "BACKEND" ); + form->addRow(tr("Backend"), m_backend); m_device = new gui::ComboBox( this, "DEVICE" ); - m_device->setGeometry( 64, 35, 260, 20 ); - - QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); - dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); - dev_lbl->move( 8, 38 ); + form->addRow(tr("Device"), m_device); // Setup models m_soundio = soundio_create(); diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 7da234d07..892155280 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -490,7 +490,6 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : new AudioPortAudio::setupWidget(as_w); #endif -// TODO !!! #ifdef LMMS_HAVE_SOUNDIO m_audioIfaceSetupWidgets[AudioSoundIo::name()] = new AudioSoundIo::setupWidget(as_w); From 4fac4c6c4a9a32a0d02dfd855c7685bcf1514fbb Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Tue, 25 Jul 2023 19:07:57 +0200 Subject: [PATCH 11/33] Fix a problem with some includes Remove a trailing ">" for some includes of `QFormLayout`. My local compiler did not complain but mingw rightfully does. --- src/core/audio/AudioOss.cpp | 2 +- src/core/audio/AudioSdl.cpp | 2 +- src/gui/MidiSetupWidget.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index b1b50907b..0b7812fe6 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -27,7 +27,7 @@ #ifdef LMMS_HAVE_OSS #include -#include > +#include #include #include "endian_handling.h" diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index 794c486de..fa801f890 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -26,7 +26,7 @@ #ifdef LMMS_HAVE_SDL -#include > +#include #include #include diff --git a/src/gui/MidiSetupWidget.cpp b/src/gui/MidiSetupWidget.cpp index d18c19bab..2385def02 100644 --- a/src/gui/MidiSetupWidget.cpp +++ b/src/gui/MidiSetupWidget.cpp @@ -24,7 +24,7 @@ #include "MidiSetupWidget.h" -#include > +#include #include #include "ConfigManager.h" From 49c713df5bad0c7285dd04d236ea3f5e9f3c34ef Mon Sep 17 00:00:00 2001 From: saker Date: Sat, 23 Sep 2023 19:33:20 -0400 Subject: [PATCH 12/33] Use resid submodule from libsidplayfp (#6883) --- .gitmodules | 6 +-- CMakeLists.txt | 24 ++++++++--- plugins/Sid/CMakeLists.txt | 47 +++------------------- plugins/Sid/SidInstrument.cpp | 24 +++++------ plugins/Sid/resid | 1 - plugins/Sid/resid/CMakeLists.txt | 68 ++++++++++++++++++++++++++++++++ plugins/Sid/resid/resid | 1 + 7 files changed, 108 insertions(+), 63 deletions(-) delete mode 160000 plugins/Sid/resid create mode 100644 plugins/Sid/resid/CMakeLists.txt create mode 160000 plugins/Sid/resid/resid diff --git a/.gitmodules b/.gitmodules index ee6e7eac9..fa6980ac5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -40,9 +40,9 @@ [submodule "plugins/CarlaBase/carla"] path = plugins/CarlaBase/carla url = https://github.com/falktx/carla -[submodule "plugins/Sid/resid"] - path = plugins/Sid/resid - url = https://github.com/simonowen/resid +[submodule "plugins/Sid/resid/resid"] + path = plugins/Sid/resid/resid + url = https://github.com/libsidplayfp/resid [submodule "src/3rdparty/jack2"] path = src/3rdparty/jack2 url = https://github.com/jackaudio/jack2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c3770f31..4163ca5cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ OPTION(WANT_SOUNDIO "Include libsoundio support" ON) OPTION(WANT_SDL "Include SDL (Simple DirectMedia Layer) support" ON) OPTION(WANT_SF2 "Include SoundFont2 player plugin" ON) OPTION(WANT_GIG "Include GIG player plugin" ON) +option(WANT_SID "Include Sid instrument" ON) OPTION(WANT_STK "Include Stk (Synthesis Toolkit) support" ON) OPTION(WANT_SWH "Include Steve Harris's LADSPA plugins" ON) OPTION(WANT_TAP "Include Tom's Audio Processing LADSPA plugins" ON) @@ -211,6 +212,13 @@ CHECK_CXX_SOURCE_COMPILES( LMMS_HAVE_SF_COMPLEVEL ) +# check for perl +if(LMMS_BUILD_APPLE) + # Prefer system perl over Homebrew, MacPorts, etc + set(Perl_ROOT "/usr/bin") +endif() +find_package(Perl) + IF(WANT_LV2) IF(PKG_CONFIG_FOUND) PKG_CHECK_MODULES(LV2 lv2) @@ -273,11 +281,6 @@ ELSE(WANT_CMT) ENDIF(WANT_CMT) IF(WANT_SWH) - 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") @@ -349,6 +352,16 @@ IF(WANT_SDL AND NOT LMMS_HAVE_SDL2) ENDIF() ENDIF() +# check for Sid +if(WANT_SID) + if(PERL_FOUND) + set(LMMS_HAVE_SID TRUE) + set(STATUS_SID "OK") + else() + set(STATUS_SID "not found, please install perl if you require the Sid instrument") + endif() +endif() + # check for Stk IF(WANT_STK) FIND_PACKAGE(STK) @@ -816,6 +829,7 @@ MESSAGE( "* ZynAddSubFX instrument : ${STATUS_ZYN}\n" "* Carla Patchbay & Rack : ${STATUS_CARLA}\n" "* SoundFont2 player : ${STATUS_FLUIDSYNTH}\n" +"* Sid instrument : ${STATUS_SID}\n" "* Stk Mallets : ${STATUS_STK}\n" "* VST-instrument hoster : ${STATUS_VST}\n" "* VST-effect hoster : ${STATUS_VST}\n" diff --git a/plugins/Sid/CMakeLists.txt b/plugins/Sid/CMakeLists.txt index c9fce7bb7..c771fc66d 100644 --- a/plugins/Sid/CMakeLists.txt +++ b/plugins/Sid/CMakeLists.txt @@ -1,51 +1,14 @@ INCLUDE(BuildPlugin) -INCLUDE_DIRECTORIES(resid) +if(NOT LMMS_HAVE_SID) + return() +endif() BUILD_PLUGIN(sid SidInstrument.cpp SidInstrument.h - resid/envelope.h - resid/extfilt.h - resid/filter.h - resid/pot.h - resid/siddefs.h - resid/sid.h - resid/spline.h - resid/voice.h - resid/wave.h - resid/envelope.cc - resid/extfilt.cc - resid/filter.cc - resid/pot.cc - resid/sid.cc - resid/version.cc - resid/voice.cc - resid/wave6581_PS_.cc - resid/wave6581_PST.cc - resid/wave6581_P_T.cc - resid/wave6581__ST.cc - resid/wave8580_PS_.cc - resid/wave8580_PST.cc - resid/wave8580_P_T.cc - resid/wave8580__ST.cc - resid/wave.cc MOCFILES SidInstrument.h EMBEDDED_RESOURCES *.png) -# Parse VERSION -FILE(READ "resid/CMakeLists.txt" lines) -STRING(REGEX MATCH "set\\(MAJOR_VER [A-Za-z0-9_]*\\)" MAJOR_RAW ${lines}) -STRING(REGEX MATCH "set\\(MINOR_VER [A-Za-z0-9_]*\\)" MINOR_RAW ${lines}) -STRING(REGEX MATCH "set\\(PATCH_VER [A-Za-z0-9_]*\\)" PATCH_RAW ${lines}) -SEPARATE_ARGUMENTS(MAJOR_RAW) -SEPARATE_ARGUMENTS(MINOR_RAW) -SEPARATE_ARGUMENTS(PATCH_RAW) -LIST(GET MAJOR_RAW 1 MAJOR_RAW) -LIST(GET MINOR_RAW 1 MINOR_RAW) -LIST(GET PATCH_RAW 1 PATCH_RAW) -STRING(REPLACE ")" "" MAJOR_VER "${MAJOR_RAW}") -STRING(REPLACE ")" "" MINOR_VER "${MINOR_RAW}") -STRING(REPLACE ")" "" PATCH_VER "${PATCH_RAW}") - -TARGET_COMPILE_DEFINITIONS(sid PRIVATE VERSION="${MAJOR_VER}.${MINOR_VER}.${PATCH_VER}") +add_subdirectory(resid) +target_link_libraries(sid resid) diff --git a/plugins/Sid/SidInstrument.cpp b/plugins/Sid/SidInstrument.cpp index 143003f98..7f9edf13f 100644 --- a/plugins/Sid/SidInstrument.cpp +++ b/plugins/Sid/SidInstrument.cpp @@ -239,7 +239,7 @@ f_cnt_t SidInstrument::desiredReleaseFrames() const -static int sid_fillbuffer(unsigned char* sidreg, SID *sid, int tdelta, short *ptr, int samples) +static int sid_fillbuffer(unsigned char* sidreg, reSID::SID *sid, int tdelta, short *ptr, int samples) { int tdelta2; int result; @@ -302,9 +302,9 @@ void SidInstrument::playNote( NotePlayHandle * _n, if (!_n->m_pluginData) { - SID *sid = new SID(); - sid->set_sampling_parameters( clockrate, SAMPLE_FAST, samplerate ); - sid->set_chip_model( MOS8580 ); + auto sid = new reSID::SID(); + sid->set_sampling_parameters(clockrate, reSID::SAMPLE_FAST, samplerate); + sid->set_chip_model(reSID::MOS8580); sid->enable_filter( true ); sid->reset(); _n->m_pluginData = sid; @@ -312,7 +312,7 @@ void SidInstrument::playNote( NotePlayHandle * _n, const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); - SID *sid = static_cast( _n->m_pluginData ); + 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); @@ -325,20 +325,20 @@ void SidInstrument::playNote( NotePlayHandle * _n, if( (ChipModel)m_chipModel.value() == ChipModel::MOS6581 ) { - sid->set_chip_model( MOS6581 ); + sid->set_chip_model(reSID::MOS6581); } else { - sid->set_chip_model( MOS8580 ); + sid->set_chip_model(reSID::MOS8580); } // voices - reg8 data8 = 0; - reg8 data16 = 0; - reg8 base = 0; + reSID::reg8 data8 = 0; + reSID::reg16 data16 = 0; + size_t base = 0; float freq = 0.0; float note = 0.0; - for( reg8 i = 0 ; i < 3 ; ++i ) + for (size_t i = 0; i < 3; ++i) { base = i*7; // freq ( Fn = Fout / Fclk * 16777216 ) + coarse detuning @@ -436,7 +436,7 @@ void SidInstrument::playNote( NotePlayHandle * _n, void SidInstrument::deleteNotePluginData( NotePlayHandle * _n ) { - delete static_cast( _n->m_pluginData ); + delete static_cast(_n->m_pluginData); } diff --git a/plugins/Sid/resid b/plugins/Sid/resid deleted file mode 160000 index 02afcc5ce..000000000 --- a/plugins/Sid/resid +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 02afcc5cefac34bd0c665dc0fa6b748d238c1831 diff --git a/plugins/Sid/resid/CMakeLists.txt b/plugins/Sid/resid/CMakeLists.txt new file mode 100644 index 000000000..bb39e3d16 --- /dev/null +++ b/plugins/Sid/resid/CMakeLists.txt @@ -0,0 +1,68 @@ +# These are the defaults +set(RESID_INLINING 1) +set(RESID_INLINE inline) +set(RESID_BRANCH_HINTS 1) +set(NEW_8580_FILTER 0) + +set(HAVE_BOOL 1) +set(HAVE_LOG1P 1) + +if(CMAKE_CXX_COMPILER_ID MATCHES "GCC|Clang") + set(HAVE_BUILTIN_EXPECT 1) +else() + set(HAVE_BUILTIN_EXPECT 0) +endif() + +configure_file(resid/siddefs.h.in resid/siddefs.h @ONLY) + +add_library(resid_objects OBJECT + resid/sid.cc + resid/voice.cc + resid/wave.cc + resid/envelope.cc + resid/filter.cc + resid/dac.cc + resid/extfilt.cc + resid/pot.cc + resid/version.cc +) + +target_include_directories(resid_objects PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/resid" + "${CMAKE_CURRENT_BINARY_DIR}/resid" +) +target_compile_definitions(resid_objects PUBLIC VERSION="1.0") + +set(RESID_WAVES + wave6581_PST + wave6581_PS_ + wave6581_P_T + wave6581__ST + wave8580_PST + wave8580_PS_ + wave8580_P_T + wave8580__ST +) + +set(RESID_SAMP2SRC_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/resid/samp2src.pl) +foreach(WAVE_DATA IN LISTS RESID_WAVES) + set(WAVE_DATA_IN ${CMAKE_CURRENT_SOURCE_DIR}/resid/${WAVE_DATA}.dat) + set(WAVE_SRC_OUT ${CMAKE_CURRENT_BINARY_DIR}/resid/${WAVE_DATA}.h) + set(WAVE_COMMAND + "${PERL_EXECUTABLE}" + "${RESID_SAMP2SRC_SCRIPT}" + "${WAVE_DATA}" + "${WAVE_DATA_IN}" + "${WAVE_SRC_OUT}" + ) + add_custom_command(OUTPUT ${WAVE_SRC_OUT} COMMAND ${WAVE_COMMAND} VERBATIM) + target_sources(resid_objects PUBLIC ${WAVE_SRC_OUT}) +endforeach() + +# TODO CMake 3.12: Use target_link_libraries() to propagate usage requirements directly to sid plugin +add_library(resid INTERFACE) + +target_sources(resid INTERFACE $) + +get_target_property(resid_includes resid_objects INCLUDE_DIRECTORIES) +target_include_directories(resid INTERFACE ${resid_includes}) diff --git a/plugins/Sid/resid/resid b/plugins/Sid/resid/resid new file mode 160000 index 000000000..ef72462f5 --- /dev/null +++ b/plugins/Sid/resid/resid @@ -0,0 +1 @@ +Subproject commit ef72462f5fa0682d099413512b764ae479e77f9b From 006c43820bfb7867c84abffc96bf178bf12437a0 Mon Sep 17 00:00:00 2001 From: Lost Robot <34612565+LostRobotMusic@users.noreply.github.com> Date: Sun, 24 Sep 2023 04:19:19 -0700 Subject: [PATCH 13/33] Fix Compressor zero divisions (#6887) * Fix threshold zero division * Fix RMS zero division --- plugins/Compressor/Compressor.cpp | 3 ++- plugins/Compressor/CompressorControlDialog.cpp | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/plugins/Compressor/Compressor.cpp b/plugins/Compressor/Compressor.cpp index 33571606e..0fe139420 100755 --- a/plugins/Compressor/Compressor.cpp +++ b/plugins/Compressor/Compressor.cpp @@ -177,7 +177,8 @@ void CompressorEffect::calcRange() void CompressorEffect::resizeRMS() { - m_rmsTimeConst = exp(-1.f / (m_compressorControls.m_rmsModel.value() * 0.001f * m_sampleRate)); + const float rmsValue = m_compressorControls.m_rmsModel.value(); + m_rmsTimeConst = (rmsValue > 0) ? exp(-1.f / (rmsValue * 0.001f * m_sampleRate)) : 0; } void CompressorEffect::calcLookaheadLength() diff --git a/plugins/Compressor/CompressorControlDialog.cpp b/plugins/Compressor/CompressorControlDialog.cpp index 114980a7d..1516456a2 100755 --- a/plugins/Compressor/CompressorControlDialog.cpp +++ b/plugins/Compressor/CompressorControlDialog.cpp @@ -497,10 +497,12 @@ void CompressorControlDialog::redrawKnee() float actualRatio = m_controls->m_limiterModel.value() ? 0 : m_controls->m_effect->m_ratioVal; // Calculate endpoints for the two straight lines - float kneePoint1 = m_controls->m_effect->m_thresholdVal - m_controls->m_effect->m_kneeVal; - float kneePoint2X = m_controls->m_effect->m_thresholdVal + m_controls->m_effect->m_kneeVal; - float kneePoint2Y = (m_controls->m_effect->m_thresholdVal + (-m_controls->m_effect->m_thresholdVal * (actualRatio * (m_controls->m_effect->m_kneeVal / -m_controls->m_effect->m_thresholdVal)))); - float ratioPoint = m_controls->m_effect->m_thresholdVal + (-m_controls->m_effect->m_thresholdVal * actualRatio); + const float thresholdVal = m_controls->m_effect->m_thresholdVal; + const float kneeVal = m_controls->m_effect->m_kneeVal; + float kneePoint1 = thresholdVal - kneeVal; + float kneePoint2X = thresholdVal + kneeVal; + float kneePoint2Y = thresholdVal + kneeVal * actualRatio; + float ratioPoint = thresholdVal + (-thresholdVal * actualRatio); // Draw two straight lines m_p.drawLine(0, m_kneeWindowSizeY, dbfsToXPoint(kneePoint1), dbfsToYPoint(kneePoint1)); @@ -510,7 +512,7 @@ void CompressorControlDialog::redrawKnee() } // Draw knee section - if (m_controls->m_effect->m_kneeVal) + if (kneeVal) { m_p.setPen(QPen(m_kneeColor2, 3)); @@ -522,8 +524,8 @@ void CompressorControlDialog::redrawKnee() { newPoint[0] = linearInterpolate(kneePoint1, kneePoint2X, (i + 1) / (float)COMP_KNEE_LINES); - const float temp = newPoint[0] - m_controls->m_effect->m_thresholdVal + m_controls->m_effect->m_kneeVal; - newPoint[1] = (newPoint[0] + (actualRatio - 1) * temp * temp / (4 * m_controls->m_effect->m_kneeVal)); + const float temp = newPoint[0] - thresholdVal + kneeVal; + newPoint[1] = (newPoint[0] + (actualRatio - 1) * temp * temp / (4 * kneeVal)); m_p.drawLine(dbfsToXPoint(prevPoint[0]), dbfsToYPoint(prevPoint[1]), dbfsToXPoint(newPoint[0]), dbfsToYPoint(newPoint[1])); @@ -768,4 +770,4 @@ void CompressorControlDialog::resetCompressorView() } -} // namespace lmms::gui \ No newline at end of file +} // namespace lmms::gui From c309d8bd11659a3f14557960a5e83366374d0ec2 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Sep 2023 23:54:07 +0200 Subject: [PATCH 14/33] Lv2ViewProc: Improve variable names --- include/Lv2ViewBase.h | 2 +- src/gui/Lv2ViewBase.cpp | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/Lv2ViewBase.h b/include/Lv2ViewBase.h index f7d0e9bcb..3c8f1bc3f 100644 --- a/include/Lv2ViewBase.h +++ b/include/Lv2ViewBase.h @@ -56,7 +56,7 @@ class Lv2ViewProc : public LinkedModelGroupView { public: //! @param colNum numbers of columns for the controls - Lv2ViewProc(QWidget *parent, Lv2Proc *ctrlBase, int colNum); + Lv2ViewProc(QWidget *parent, Lv2Proc *proc, int colNum); ~Lv2ViewProc() override = default; private: diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 3fd1d44b1..12be5e069 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -51,13 +51,13 @@ namespace lmms::gui { -Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : - LinkedModelGroupView (parent, ctrlBase, colNum) +Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* proc, int colNum) : + LinkedModelGroupView (parent, proc, colNum) { - class SetupWidget : public Lv2Ports::ConstVisitor + class SetupTheWidget : public Lv2Ports::ConstVisitor { public: - QWidget* m_par; // input + QWidget* m_parent; // input const LilvNode* m_commentUri; // input Control* m_control = nullptr; // output void visit(const Lv2Ports::Control& port) override @@ -69,20 +69,20 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : switch (port.m_vis) { case PortVis::Generic: - m_control = new KnobControl(m_par); + m_control = new KnobControl(m_parent); break; case PortVis::Integer: { sample_rate_t sr = Engine::audioEngine()->processingSampleRate(); m_control = new LcdControl((port.max(sr) <= 9.0f) ? 1 : 2, - m_par); + m_parent); break; } case PortVis::Enumeration: - m_control = new ComboControl(m_par); + m_control = new ComboControl(m_parent); break; case PortVis::Toggled: - m_control = new CheckControl(m_par); + m_control = new CheckControl(m_parent); break; } m_control->setText(port.name()); @@ -100,14 +100,14 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* ctrlBase, int colNum) : }; AutoLilvNode commentUri = uri(LILV_NS_RDFS "comment"); - ctrlBase->foreach_port( + proc->foreach_port( [this, &commentUri](const Lv2Ports::PortBase* port) { if(!lilv_port_has_property(port->m_plugin, port->m_port, uri(LV2_PORT_PROPS__notOnGUI).get())) { - SetupWidget setup; - setup.m_par = this; + SetupTheWidget setup; + setup.m_parent = this; setup.m_commentUri = commentUri.get(); port->accept(setup); From d97377b640c3f288ed68860a3ffcf60ad7b5c7d9 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Sep 2023 23:58:52 +0200 Subject: [PATCH 15/33] Lv2: Improve LED widget's digit calculation This improves the way digits are calculated for display in `Lv2ViewProc`: - More than 2 digits are now recognized - Minus signs are now recognized - Tests have been added --- include/lmms_math.h | 26 ++++++++++++++++++ src/gui/Lv2ViewBase.cpp | 7 +++-- tests/CMakeLists.txt | 1 + tests/src/core/MathTest.cpp | 53 +++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 tests/src/core/MathTest.cpp diff --git a/include/lmms_math.h b/include/lmms_math.h index b62da81c2..ea0a75581 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -325,6 +325,32 @@ static inline T absMin( T a, T b ) return std::abs(a) < std::abs(b) ? a : b; } +// @brief Calculate number of digits which LcdSpinBox would show for a given number +// @note Once we upgrade to C++20, we could probably use std::formatted_size +static inline int numDigitsAsInt(float f) +{ + // use rounding: + // LcdSpinBox sometimes uses roundf(), sometimes cast rounding + // we use rounding to be on the "safe side" + const float rounded = roundf(f); + int asInt = static_cast(rounded); + int digits = 1; // always at least 1 + if(asInt < 0) + { + ++digits; + asInt = -asInt; + } + // "asInt" is positive from now + int32_t power = 1; + for(int32_t i = 1; i<10; ++i) + { + power *= 10; + if(static_cast(asInt) >= power) { ++digits; } // 2 digits for >=10, 3 for >=100 + else { break; } + } + return digits; +} + } // namespace lmms diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 12be5e069..830a994c8 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -39,6 +39,7 @@ #include "GuiApplication.h" #include "embed.h" #include "gui_templates.h" +#include "lmms_math.h" #include "Lv2ControlBase.h" #include "Lv2Manager.h" #include "Lv2Proc.h" @@ -74,8 +75,10 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* proc, int colNum) : case PortVis::Integer: { sample_rate_t sr = Engine::audioEngine()->processingSampleRate(); - m_control = new LcdControl((port.max(sr) <= 9.0f) ? 1 : 2, - m_parent); + auto pMin = port.min(sr); + auto pMax = port.max(sr); + int numDigits = std::max(numDigitsAsInt(pMin), numDigitsAsInt(pMax)); + m_control = new LcdControl(numDigits, m_parent); break; } case PortVis::Enumeration: diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 514bf7815..ddf9e2962 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -21,6 +21,7 @@ ADD_EXECUTABLE(tests src/core/ArrayVectorTest.cpp src/core/AutomatableModelTest.cpp + src/core/MathTest.cpp src/core/ProjectVersionTest.cpp src/core/RelativePathsTest.cpp diff --git a/tests/src/core/MathTest.cpp b/tests/src/core/MathTest.cpp new file mode 100644 index 000000000..2b6404cfd --- /dev/null +++ b/tests/src/core/MathTest.cpp @@ -0,0 +1,53 @@ +/* + * MathTest.cpp + * + * Copyright (c) 2023 Johannes Lorenz + * + * 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 "QTestSuite.h" + +#include "lmms_math.h" + +#include + +class MathTest : QTestSuite +{ + Q_OBJECT +private slots: + void NumDigitsTest() + { + using namespace lmms; + QCOMPARE(numDigitsAsInt(1.f), 1); + QCOMPARE(numDigitsAsInt(9.9f), 2); + QCOMPARE(numDigitsAsInt(10.f), 2); + QCOMPARE(numDigitsAsInt(0.f), 1); + QCOMPARE(numDigitsAsInt(-100.f), 4); + QCOMPARE(numDigitsAsInt(-99.f), 3); + QCOMPARE(numDigitsAsInt(-0.4f), 1); // there is no "-0" for LED spinbox + QCOMPARE(numDigitsAsInt(-0.99f), 2); + QCOMPARE(numDigitsAsInt(1000000000), 10); + QCOMPARE(numDigitsAsInt(-1000000000), 11); + QCOMPARE(numDigitsAsInt(900000000), 9); + QCOMPARE(numDigitsAsInt(-900000000), 10); + } +} MathTests; + +#include "MathTest.moc" From 9c46370234fe45437f0f64fab963a232cb3e9a46 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 23 Sep 2023 00:00:02 +0200 Subject: [PATCH 16/33] Support LV2_BUF_SIZE__powerOf2BlockLength --- src/core/lv2/Lv2Manager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 9c62703e0..df6e5a7c1 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -35,6 +35,7 @@ #include #include +#include "AudioEngine.h" #include "Engine.h" #include "Plugin.h" #include "Lv2ControlBase.h" @@ -156,6 +157,10 @@ Lv2Manager::Lv2Manager() : m_supportedFeatureURIs.insert(LV2_BUF_SIZE__boundedBlockLength); // block length is only changed initially in AudioEngine CTOR m_supportedFeatureURIs.insert(LV2_BUF_SIZE__fixedBlockLength); + if (const auto fpp = Engine::audioEngine()->framesPerPeriod(); (fpp & (fpp - 1)) == 0) // <=> ffp is power of 2 (for ffp > 0) + { + m_supportedFeatureURIs.insert(LV2_BUF_SIZE__powerOf2BlockLength); + } auto supportOpt = [this](Lv2UridCache::Id id) { From 61b612634d1690ba5ac21c4a9fea455e8a9e6f64 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Sep 2023 23:38:57 +0200 Subject: [PATCH 17/33] Add plugin blacklist for buffersize<=32 --- include/Lv2Features.h | 3 ++- include/Lv2Manager.h | 20 ++++++++++---------- src/core/lv2/Lv2Features.cpp | 6 +++--- src/core/lv2/Lv2Manager.cpp | 31 +++++++++++++++++++++---------- src/core/lv2/Lv2Proc.cpp | 16 ++++++++++++---- 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/include/Lv2Features.h b/include/Lv2Features.h index b5bc284c8..69a456bbd 100644 --- a/include/Lv2Features.h +++ b/include/Lv2Features.h @@ -30,6 +30,7 @@ #ifdef LMMS_HAVE_LV2 #include +#include #include #include "Lv2Manager.h" @@ -78,7 +79,7 @@ private: //! pointers to m_features, required for lilv_plugin_instantiate std::vector m_featurePointers; //! features + data, ordered by URI - std::map m_featureByUri; + std::map m_featureByUri; }; diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 909dba560..58126a0a4 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -31,6 +31,7 @@ #include #include +#include #include #include "Lv2Basics.h" @@ -120,15 +121,9 @@ public: Iterator begin() { return m_lv2InfoMap.begin(); } Iterator end() { return m_lv2InfoMap.end(); } - //! strcmp based key comparator for std::set and std::map - struct CmpStr - { - bool operator()(char const *a, char const *b) const; - }; - UridMap& uridMap() { return m_uridMap; } const Lv2UridCache& uridCache() const { return m_uridCache; } - const std::set& supportedFeatureURIs() const + const std::set& supportedFeatureURIs() const { return m_supportedFeatureURIs; } @@ -136,17 +131,21 @@ public: AutoLilvNodes findNodes(const LilvNode *subject, const LilvNode *predicate, const LilvNode *object); - static const std::set& getPluginBlacklist() + static const std::set& getPluginBlacklist() { return pluginBlacklist; } + static const std::set& getPluginBlacklistBuffersizeLessThan32() + { + return pluginBlacklistBuffersizeLessThan32; + } private: // general data bool m_debug; //!< if set, debug output will be printed LilvWorld* m_world; Lv2InfoMap m_lv2InfoMap; - std::set m_supportedFeatureURIs; + std::set m_supportedFeatureURIs; // feature data that are common for all Lv2Proc UridMap m_uridMap; @@ -155,7 +154,8 @@ private: Lv2UridCache m_uridCache; // static - static const std::set pluginBlacklist; + static const std::set + pluginBlacklist, pluginBlacklistBuffersizeLessThan32; // functions bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); diff --git a/src/core/lv2/Lv2Features.cpp b/src/core/lv2/Lv2Features.cpp index 6e74a8936..c8fc05465 100644 --- a/src/core/lv2/Lv2Features.cpp +++ b/src/core/lv2/Lv2Features.cpp @@ -48,7 +48,7 @@ Lv2Features::Lv2Features() { const Lv2Manager* man = Engine::getLv2Manager(); // create (yet empty) map feature URI -> feature - for(const char* uri : man->supportedFeatureURIs()) + for(auto uri : man->supportedFeatureURIs()) { m_featureByUri.emplace(uri, nullptr); } @@ -71,7 +71,7 @@ void Lv2Features::initCommon() void Lv2Features::createFeatureVectors() { // create vector of features - for(std::pair& pr : m_featureByUri) + for(const auto& [uri, feature] : m_featureByUri) { /* If pr.second is nullptr here, this means that the LV2_feature @@ -82,7 +82,7 @@ void Lv2Features::createFeatureVectors() vector creation (This can be done in Lv2Proc::initPluginSpecificFeatures or in Lv2Features::initCommon) */ - m_features.push_back(LV2_Feature { pr.first, pr.second }); + m_features.push_back(LV2_Feature{(const char*)uri.data(), (void*)feature}); } // create pointer vector (for lilv_plugin_instantiate) diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index df6e5a7c1..489e613b7 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -47,7 +46,7 @@ namespace lmms { -const std::set Lv2Manager::pluginBlacklist = +const std::set Lv2Manager::pluginBlacklist = { // github.com/calf-studio-gear/calf, #278 "http://calf.sourceforge.net/plugins/Analyzer", @@ -138,6 +137,26 @@ const std::set Lv2Manager::pluginBlacklist = "urn:juced:DrumSynth" }; +const std::set Lv2Manager::pluginBlacklistBuffersizeLessThan32 = +{ + "http://moddevices.com/plugins/mod-devel/2Voices", + "http://moddevices.com/plugins/mod-devel/Capo", + "http://moddevices.com/plugins/mod-devel/Drop", + "http://moddevices.com/plugins/mod-devel/Harmonizer", + "http://moddevices.com/plugins/mod-devel/Harmonizer2", + "http://moddevices.com/plugins/mod-devel/HarmonizerCS", + "http://moddevices.com/plugins/mod-devel/SuperCapo", + "http://moddevices.com/plugins/mod-devel/SuperWhammy", + "http://moddevices.com/plugins/mod-devel/Gx2Voices", + "http://moddevices.com/plugins/mod-devel/GxCapo", + "http://moddevices.com/plugins/mod-devel/GxDrop", + "http://moddevices.com/plugins/mod-devel/GxHarmonizer", + "http://moddevices.com/plugins/mod-devel/GxHarmonizer2", + "http://moddevices.com/plugins/mod-devel/GxHarmonizerCS", + "http://moddevices.com/plugins/mod-devel/GxSuperCapo", + "http://moddevices.com/plugins/mod-devel/GxSuperWhammy" +}; + @@ -293,14 +312,6 @@ void Lv2Manager::initPlugins() -bool Lv2Manager::CmpStr::operator()(const char *a, const char *b) const -{ - return std::strcmp(a, b) < 0; -} - - - - bool Lv2Manager::isFeatureSupported(const char *featName) const { return m_supportedFeatureURIs.find(featName) != m_supportedFeatureURIs.end(); diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index e0541b948..6776a26ed 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -75,11 +75,19 @@ Plugin::Type Lv2Proc::check(const LilvPlugin *plugin, // TODO: manage a global blacklist outside of the code // for now, this will help // this is only a fix for the meantime - const auto& pluginBlacklist = Lv2Manager::getPluginBlacklist(); - if (!Engine::ignorePluginBlacklist() && - pluginBlacklist.find(pluginUri) != pluginBlacklist.end()) + if (!Engine::ignorePluginBlacklist()) { - issues.emplace_back(PluginIssueType::Blacklisted); + const auto& pluginBlacklist = Lv2Manager::getPluginBlacklist(); + const auto& pluginBlacklist32 = Lv2Manager::getPluginBlacklistBuffersizeLessThan32(); + if(pluginBlacklist.find(pluginUri) != pluginBlacklist.end()) + { + 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 + } } for (unsigned portNum = 0; portNum < maxPorts; ++portNum) From 7aca8ae726ab425b47802721703fb5589fe8289a Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Sep 2023 23:56:45 +0200 Subject: [PATCH 18/33] SetupDialog: Warn of unusual buffersizes This displays a warning dialog if the users requests unusual buffersizes: - buffersizes less than 32 - buffersizes which are not a (natural number) power of 2 This commit also replaces some `setGeometry` stuff by `QBoxLayout`. --- include/SetupDialog.h | 2 ++ src/gui/modals/SetupDialog.cpp | 38 ++++++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/include/SetupDialog.h b/include/SetupDialog.h index de4cdd9dd..fa41325db 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -102,6 +102,7 @@ private slots: // Audio settings widget. void audioInterfaceChanged(const QString & driver); void toggleHQAudioDev(bool enabled); + void updateBufferSizeWarning(int value); void setBufferSize(int value); void resetBufferSize(); @@ -179,6 +180,7 @@ private: int m_bufferSize; QSlider * m_bufferSizeSlider; QLabel * m_bufferSizeLbl; + QLabel * m_bufferSizeWarnLbl; // MIDI settings widgets. QComboBox * m_midiInterfaces; diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 63b84506e..0266285a7 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -579,32 +579,39 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : // Buffer size tab. auto bufferSize_tw = new TabWidget(tr("Buffer size"), audio_w); - bufferSize_tw->setFixedHeight(76); + auto bufferSize_layout = new QVBoxLayout(bufferSize_tw); + bufferSize_layout->setSpacing(10); + bufferSize_layout->setContentsMargins(10, 18, 10, 10); m_bufferSizeSlider = new QSlider(Qt::Horizontal, bufferSize_tw); m_bufferSizeSlider->setRange(1, 128); m_bufferSizeSlider->setTickInterval(8); m_bufferSizeSlider->setPageStep(8); m_bufferSizeSlider->setValue(m_bufferSize / BUFFERSIZE_RESOLUTION); - m_bufferSizeSlider->setGeometry(10, 18, 340, 18); m_bufferSizeSlider->setTickPosition(QSlider::TicksBelow); + m_bufferSizeLbl = new QLabel(bufferSize_tw); + + m_bufferSizeWarnLbl = new QLabel(bufferSize_tw); + m_bufferSizeWarnLbl->setWordWrap(true); + connect(m_bufferSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(setBufferSize(int))); connect(m_bufferSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(showRestartWarning())); - - m_bufferSizeLbl = new QLabel(bufferSize_tw); - m_bufferSizeLbl->setGeometry(10, 40, 200, 24); setBufferSize(m_bufferSizeSlider->value()); auto bufferSize_reset_btn = new QPushButton(embed::getIconPixmap("reload"), "", bufferSize_tw); - bufferSize_reset_btn->setGeometry(320, 40, 28, 28); connect(bufferSize_reset_btn, SIGNAL(clicked()), this, SLOT(resetBufferSize())); bufferSize_reset_btn->setToolTip( tr("Reset to default value")); + bufferSize_layout->addWidget(m_bufferSizeSlider); + bufferSize_layout->addWidget(m_bufferSizeLbl); + bufferSize_layout->addWidget(m_bufferSizeWarnLbl); + bufferSize_layout->addWidget(bufferSize_reset_btn); + // Audio layout ordering. audio_layout->addWidget(audioiface_tw); @@ -1172,6 +1179,24 @@ void SetupDialog::audioInterfaceChanged(const QString & iface) } +void SetupDialog::updateBufferSizeWarning(int value) +{ + QString text = "
    "; + if((value & (value - 1)) != 0) // <=> value is not a power of 2 (for value > 0) + { + text += "
  • " + tr("The currently selected value is not a power of 2 " + "(32, 64, 128, 256, 512, 1024, ...). Some plugins may not be available.") + "
  • "; + } + if(value <= 32) + { + text += "
  • " + tr("The currently selected value is less than or equal to 32. " + "Some plugins may not be available.") + "
  • "; + } + text += "
"; + m_bufferSizeWarnLbl->setText(text); +} + + void SetupDialog::setBufferSize(int value) { const int step = DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION; @@ -1197,6 +1222,7 @@ void SetupDialog::setBufferSize(int value) m_bufferSize = value * BUFFERSIZE_RESOLUTION; m_bufferSizeLbl->setText(tr("Frames: %1\nLatency: %2 ms").arg(m_bufferSize).arg( 1000.0f * m_bufferSize / Engine::audioEngine()->processingSampleRate(), 0, 'f', 1)); + updateBufferSizeWarning(m_bufferSize); } From 94608eaad1da0622a5eb1fa6d169f8e92d06a6aa Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Sep 2023 23:42:45 +0200 Subject: [PATCH 19/33] Lv2Proc: Sort scale point maps (#6859) lilv can return the scale points in randomized order, and so the linked models (of stereo effects wit 2 `Lv2Proc`) may have different scale points. This would mean that the same combo enumeration value would have different float values in linked models. This problem is fixed by sorting the scale values. Also, it is more natural for users if scale points are enumerated in a numerical order. --- include/Lv2Basics.h | 6 ++++++ src/core/lv2/Lv2Proc.cpp | 28 +++++++++++++++++----------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h index 9a958d973..53489e30d 100644 --- a/include/Lv2Basics.h +++ b/include/Lv2Basics.h @@ -47,8 +47,14 @@ struct LilvNodesDeleter void operator()(LilvNodes* n) { lilv_nodes_free(n); } }; +struct LilvScalePointsDeleter +{ + void operator()(LilvScalePoints* s) { lilv_scale_points_free(s); } +}; + using AutoLilvNode = std::unique_ptr; using AutoLilvNodes = std::unique_ptr; +using AutoLilvScalePoints = std::unique_ptr; /** Return QString from a plugin's node, everything will be freed automatically diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 6776a26ed..a5fdf4041 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -574,19 +574,25 @@ void Lv2Proc::createPort(std::size_t portNum) break; case Lv2Ports::Vis::Enumeration: { - auto comboModel = new ComboBoxModel(nullptr, dispName); - LilvScalePoints* sps = - lilv_port_get_scale_points(m_plugin, lilvPort); - LILV_FOREACH(scale_points, i, sps) + ComboBoxModel* comboModel = new ComboBoxModel(nullptr, dispName); + { - const LilvScalePoint* sp = lilv_scale_points_get(sps, i); - ctrl->m_scalePointMap.push_back(lilv_node_as_float( - lilv_scale_point_get_value(sp))); - comboModel->addItem( - lilv_node_as_string( - lilv_scale_point_get_label(sp))); + AutoLilvScalePoints sps (static_cast(lilv_port_get_scale_points(m_plugin, lilvPort))); + // temporary map, since lilv may return scale points in random order + std::map scalePointMap; + LILV_FOREACH(scale_points, i, sps.get()) + { + const LilvScalePoint* sp = lilv_scale_points_get(sps.get(), i); + const float f = lilv_node_as_float(lilv_scale_point_get_value(sp)); + const char* s = lilv_node_as_string(lilv_scale_point_get_label(sp)); + scalePointMap[f] = s; + } + for (const auto& [f,s] : scalePointMap) + { + ctrl->m_scalePointMap.push_back(f); + comboModel->addItem(s); + } } - lilv_scale_points_free(sps); ctrl->m_connectedModel.reset(comboModel); // TODO: use default value on comboModel, too? break; From 83777dc1f73736ec58376590f09cba92331cd901 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Sep 2023 23:42:45 +0200 Subject: [PATCH 20/33] Lv2Proc: Set def val for ComboModel, too --- src/core/lv2/Lv2Proc.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index a5fdf4041..242f3d92b 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -593,8 +593,16 @@ void Lv2Proc::createPort(std::size_t portNum) comboModel->addItem(s); } } + for(std::size_t i = 0; i < ctrl->m_scalePointMap.size(); ++i) + { + if(meta.def() == ctrl->m_scalePointMap[i]) + { + comboModel->setValue(i); + comboModel->setInitValue(i); + break; + } + } ctrl->m_connectedModel.reset(comboModel); - // TODO: use default value on comboModel, too? break; } case Lv2Ports::Vis::Toggled: From 33d1baddc0284bd68df752a08ff9c673a706c4d7 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Sep 2023 23:27:02 +0200 Subject: [PATCH 21/33] Implement Lv2 Worker (#6484) --- include/AudioEngine.h | 1 + include/LmmsSemaphore.h | 93 ++++++++++++++++ include/LocklessRingBuffer.h | 3 +- include/Lv2Proc.h | 13 ++- include/Lv2Worker.h | 93 ++++++++++++++++ src/core/CMakeLists.txt | 2 + src/core/LmmsSemaphore.cpp | 143 ++++++++++++++++++++++++ src/core/lv2/Lv2Manager.cpp | 2 + src/core/lv2/Lv2Proc.cpp | 31 +++++- src/core/lv2/Lv2Worker.cpp | 203 +++++++++++++++++++++++++++++++++++ 10 files changed, 580 insertions(+), 4 deletions(-) create mode 100644 include/LmmsSemaphore.h create mode 100644 include/Lv2Worker.h create mode 100644 src/core/LmmsSemaphore.cpp create mode 100644 src/core/lv2/Lv2Worker.cpp diff --git a/include/AudioEngine.h b/include/AudioEngine.h index f056c22e1..d3d0d025f 100644 --- a/include/AudioEngine.h +++ b/include/AudioEngine.h @@ -197,6 +197,7 @@ public: // audio-device-stuff + bool renderOnly() const { return m_renderOnly; } // Returns the current audio device's name. This is not necessarily // the user's preferred audio device, in case you were thinking that. inline const QString & audioDevName() const diff --git a/include/LmmsSemaphore.h b/include/LmmsSemaphore.h new file mode 100644 index 000000000..4170eef6c --- /dev/null +++ b/include/LmmsSemaphore.h @@ -0,0 +1,93 @@ +/* + * Semaphore.h - Semaphore declaration + * + * Copyright (c) 2022-2022 Johannes Lorenz + * + * 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. + * + */ + +/* + * This code has been copied and adapted from https://github.com/drobilla/jalv + * File src/zix/sem.h + */ + +#ifndef LMMS_SEMAPHORE_H +#define LMMS_SEMAPHORE_H + +#include "lmmsconfig.h" + +#ifdef LMMS_BUILD_APPLE +# include +#elif defined(LMMS_BUILD_WIN32) +# include +#else +# include +#endif + +#include + +namespace lmms { + +/** + A counting semaphore. + + This is an integer that is always positive, and has two main operations: + increment (post) and decrement (wait). If a decrement can not be performed + (i.e. the value is 0) the caller will be blocked until another thread posts + and the operation can succeed. + + Semaphores can be created with any starting value, but typically this will + be 0 so the semaphore can be used as a simple signal where each post + corresponds to one wait. + + Semaphores are very efficient (much moreso than a mutex/cond pair). In + particular, at least on Linux, post is async-signal-safe, which means it + does not block and will not be interrupted. If you need to signal from + a realtime thread, this is the most appropriate primitive to use. + + @note Likely outdated with C++20's std::counting_semaphore + (though we have to check that this will be RT conforming on all platforms) +*/ +class Semaphore +{ +public: + Semaphore(unsigned initial); + Semaphore(const Semaphore&) = delete; + Semaphore& operator=(const Semaphore&) = delete; + Semaphore(Semaphore&&) = delete; + Semaphore& operator=(Semaphore&&) = delete; + ~Semaphore(); + + void post(); + void wait(); + bool tryWait(); + +private: +#ifdef LMMS_BUILD_APPLE + semaphore_t m_sem; +#elif defined(LMMS_BUILD_WIN32) + HANDLE m_sem; +#else + sem_t m_sem; +#endif +}; + +} // namespace lmms + +#endif // LMMS_SEMAPHORE_H diff --git a/include/LocklessRingBuffer.h b/include/LocklessRingBuffer.h index 99c48cc90..2d65badfe 100644 --- a/include/LocklessRingBuffer.h +++ b/include/LocklessRingBuffer.h @@ -51,13 +51,14 @@ public: std::size_t capacity() const {return m_buffer.maximum_eventual_write_space();} std::size_t free() const {return m_buffer.write_space();} void wakeAll() {m_notifier.wakeAll();} - std::size_t write(const sampleFrame *src, std::size_t cnt, bool notify = false) + std::size_t write(const T *src, std::size_t cnt, bool notify = false) { std::size_t written = LocklessRingBuffer::m_buffer.write(src, cnt); // Let all waiting readers know new data are available. if (notify) {LocklessRingBuffer::m_notifier.wakeAll();} return written; } + void mlock() { m_buffer.mlock(); } protected: ringbuffer_t m_buffer; diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 62070def7..76fa5eec2 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -1,7 +1,7 @@ /* * Lv2Proc.h - Lv2 processor class * - * Copyright (c) 2019-2020 Johannes Lorenz + * Copyright (c) 2019-2022 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -31,11 +31,14 @@ #include #include +#include +#include "LinkedModelGroups.h" +#include "LmmsSemaphore.h" #include "Lv2Basics.h" #include "Lv2Features.h" #include "Lv2Options.h" -#include "LinkedModelGroups.h" +#include "Lv2Worker.h" #include "Plugin.h" #include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h" #include "TimePos.h" @@ -174,8 +177,14 @@ private: const LilvPlugin* m_plugin; LilvInstance* m_instance; Lv2Features m_features; + + // options Lv2Options m_options; + // worker + std::optional m_worker; + Semaphore m_workLock; // this must be shared by different workers + // full list of ports std::vector> m_ports; // quick reference to specific, unique ports diff --git a/include/Lv2Worker.h b/include/Lv2Worker.h new file mode 100644 index 000000000..7931f8e7c --- /dev/null +++ b/include/Lv2Worker.h @@ -0,0 +1,93 @@ +/* + * Lv2Worker.h - Lv2Worker class + * + * Copyright (c) 2022-2022 Johannes Lorenz + * + * 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 LV2WORKER_H +#define LV2WORKER_H + +#include "lmmsconfig.h" + +#ifdef LMMS_HAVE_LV2 + +#include +#include +#include +#include + +#include "LocklessRingBuffer.h" +#include "LmmsSemaphore.h" + +namespace lmms +{ + +/** + Worker container +*/ +class Lv2Worker +{ +public: + // CTOR/DTOR/feature access + Lv2Worker(const LV2_Worker_Interface* iface, Semaphore* common_work_lock, bool threaded); + ~Lv2Worker(); + void setHandle(LV2_Handle handle) { m_handle = handle; } + LV2_Worker_Schedule* feature() { return &m_scheduleFeature; } + + // public API + void emitResponses(); + void notifyPluginThatRunFinished() + { + if(m_iface->end_run) { m_iface->end_run(m_scheduleFeature.handle); } + } + + // to be called only by static functions + LV2_Worker_Status scheduleWork(uint32_t size, const void* data); + LV2_Worker_Status respond(uint32_t size, const void* data); + +private: + // functions + void workerFunc(); + std::size_t bufferSize() const; //!< size of internal buffers + + // parameters + const LV2_Worker_Interface* m_iface; + bool m_threaded; + LV2_Handle m_handle; + LV2_Worker_Schedule m_scheduleFeature; + + // threading/synchronization + std::thread m_thread; + std::vector m_response; //!< buffer where single requests from m_requests are unpacked + LocklessRingBuffer m_requests, m_responses; //!< ringbuffer to queue multiple requests + LocklessRingBufferReader m_requestsReader, m_responsesReader; + std::atomic m_exit = false; //!< Whether the worker function should keep looping + Semaphore m_sem; + Semaphore* m_workLock; +}; + + +} // namespace lmms + +#endif // LMMS_HAVE_LV2 + +#endif // LV2WORKER_H + diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 319882af2..1155f5e0d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -70,6 +70,7 @@ set(LMMS_SRCS core/SamplePlayHandle.cpp core/SampleRecordHandle.cpp core/Scale.cpp + core/LmmsSemaphore.cpp core/SerializingObject.cpp core/Song.cpp core/TempoSyncKnobModel.cpp @@ -112,6 +113,7 @@ set(LMMS_SRCS core/lv2/Lv2SubPluginFeatures.cpp core/lv2/Lv2UridCache.cpp core/lv2/Lv2UridMap.cpp + core/lv2/Lv2Worker.cpp core/midi/MidiAlsaRaw.cpp core/midi/MidiAlsaSeq.cpp diff --git a/src/core/LmmsSemaphore.cpp b/src/core/LmmsSemaphore.cpp new file mode 100644 index 000000000..daa70a45b --- /dev/null +++ b/src/core/LmmsSemaphore.cpp @@ -0,0 +1,143 @@ +/* + * Semaphore.cpp - Semaphore implementation + * + * Copyright (c) 2022-2022 Johannes Lorenz + * + * 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. + * + */ + +/* + * This code has been copied and adapted from https://github.com/drobilla/jalv + * File src/zix/sem.h + */ + +#include "LmmsSemaphore.h" + +#if defined(LMMS_BUILD_WIN32) +# include +#else +# include +#endif + +#include + +namespace lmms { + +#ifdef LMMS_BUILD_APPLE +Semaphore::Semaphore(unsigned val) +{ + kern_return_t rval = semaphore_create(mach_task_self(), &m_sem, SYNC_POLICY_FIFO, val); + if(rval != 0) { + throw std::system_error(rval, std::system_category(), "Could not create semaphore"); + } +} + +Semaphore::~Semaphore() +{ + semaphore_destroy(mach_task_self(), m_sem); +} + +void Semaphore::post() +{ + semaphore_signal(m_sem); +} + +void Semaphore::wait() +{ + kern_return_t rval = semaphore_wait(m_sem); + if (rval != KERN_SUCCESS) { + throw std::system_error(rval, std::system_category(), "Waiting for semaphore failed"); + } +} + +bool Semaphore::tryWait() +{ + const mach_timespec_t zero = { 0, 0 }; + return semaphore_timedwait(m_sem, zero) == KERN_SUCCESS; +} + +#elif defined(LMMS_BUILD_WIN32) + +Semaphore::Semaphore(unsigned initial) +{ + if(CreateSemaphore(nullptr, initial, LONG_MAX, nullptr) == nullptr) { + throw std::system_error(GetLastError(), std::system_category(), "Could not create semaphore"); + } +} + +Semaphore::~Semaphore() +{ + CloseHandle(m_sem); +} + +void Semaphore::post() +{ + ReleaseSemaphore(m_sem, 1, nullptr); +} + +void Semaphore::wait() +{ + if (WaitForSingleObject(m_sem, INFINITE) != WAIT_OBJECT_0) { + throw std::system_error(GetLastError(), std::system_category(), "Waiting for semaphore failed"); + } +} + +bool Semaphore::tryWait() +{ + return WaitForSingleObject(m_sem, 0) == WAIT_OBJECT_0; +} + +#else /* !defined(LMMS_BUILD_APPLE) && !defined(LMMS_BUILD_WIN32) */ + +Semaphore::Semaphore(unsigned initial) +{ + if(sem_init(&m_sem, 0, initial) != 0) { + throw std::system_error(errno, std::generic_category(), "Could not create semaphore"); + } +} + +Semaphore::~Semaphore() +{ + sem_destroy(&m_sem); +} + +void Semaphore::post() +{ + sem_post(&m_sem); +} + +void Semaphore::wait() +{ + while (sem_wait(&m_sem) != 0) { + if (errno != EINTR) { + throw std::system_error(errno, std::generic_category(), "Waiting for semaphore failed"); + } + /* Otherwise, interrupted, so try again. */ + } +} + +bool Semaphore::tryWait() +{ + return (sem_trywait(&m_sem) == 0); +} + +#endif + +} // namespace lmms + diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 489e613b7..6a1b2a8af 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -172,6 +173,7 @@ Lv2Manager::Lv2Manager() : m_supportedFeatureURIs.insert(LV2_URID__map); m_supportedFeatureURIs.insert(LV2_URID__unmap); m_supportedFeatureURIs.insert(LV2_OPTIONS__options); + m_supportedFeatureURIs.insert(LV2_WORKER__schedule); // min/max is always passed in the options m_supportedFeatureURIs.insert(LV2_BUF_SIZE__boundedBlockLength); // block length is only changed initially in AudioEngine CTOR diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 242f3d92b..11290013e 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-2020 Johannes Lorenz + * Copyright (c) 2019-2022 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -170,6 +171,7 @@ Plugin::Type Lv2Proc::check(const LilvPlugin *plugin, Lv2Proc::Lv2Proc(const LilvPlugin *plugin, Model* parent) : LinkedModelGroup(parent), m_plugin(plugin), + m_workLock(1), m_midiInputBuf(m_maxMidiInputEvents), m_midiInputReader(m_midiInputBuf) { @@ -360,7 +362,19 @@ void Lv2Proc::copyBuffersToCore(sampleFrame* buf, void Lv2Proc::run(fpp_t frames) { + if (m_worker) + { + // Process any worker replies + m_worker->emitResponses(); + } + lilv_instance_run(m_instance, static_cast(frames)); + + if (m_worker) + { + // Notify the plugin the run() cycle is finished + m_worker->notifyPluginThatRunFinished(); + } } @@ -428,6 +442,9 @@ void Lv2Proc::initPlugin() if (m_instance) { + if(m_worker) { + m_worker->setHandle(lilv_instance_get_handle(m_instance)); + } for (std::size_t portNum = 0; portNum < m_ports.size(); ++portNum) connectPort(portNum); lilv_instance_activate(m_instance); @@ -504,8 +521,20 @@ void Lv2Proc::initMOptions() void Lv2Proc::initPluginSpecificFeatures() { + // options initMOptions(); m_features[LV2_OPTIONS__options] = const_cast(m_options.feature()); + + // worker (if plugin has worker extension) + Lv2Manager* mgr = Engine::getLv2Manager(); + if (lilv_plugin_has_extension_data(m_plugin, mgr->uri(LV2_WORKER__interface).get())) { + const auto iface = static_cast( + lilv_instance_get_extension_data(m_instance, LV2_WORKER__interface)); + bool threaded = !Engine::audioEngine()->renderOnly(); + m_worker.emplace(iface, &m_workLock, threaded); + m_features[LV2_WORKER__schedule] = m_worker->feature(); + // Note: m_worker::setHandle will still need to be called later + } } diff --git a/src/core/lv2/Lv2Worker.cpp b/src/core/lv2/Lv2Worker.cpp new file mode 100644 index 000000000..5af955ff7 --- /dev/null +++ b/src/core/lv2/Lv2Worker.cpp @@ -0,0 +1,203 @@ +/* + * Lv2Worker.cpp - Lv2Worker implementation + * + * Copyright (c) 2022-2022 Johannes Lorenz + * + * 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 "Lv2Worker.h" + +#include +#include + +#ifdef LMMS_HAVE_LV2 + +#include "Engine.h" + + +namespace lmms +{ + + +// static wrappers + +static LV2_Worker_Status +staticWorkerRespond(LV2_Worker_Respond_Handle handle, + uint32_t size, const void* data) +{ + Lv2Worker* worker = static_cast(handle); + return worker->respond(size, data); +} + + + + +std::size_t Lv2Worker::bufferSize() const +{ + // ardour uses this fixed size for ALSA: + return 8192 * 4; + // for jack, they use 4 * jack_port_type_get_buffer_size (..., JACK_DEFAULT_MIDI_TYPE) + // (possible extension for AudioDevice) +} + + + + +Lv2Worker::Lv2Worker(const LV2_Worker_Interface* iface, + Semaphore* common_work_lock, + bool threaded) : + m_iface(iface), + m_threaded(threaded), + m_response(bufferSize()), + m_requests(bufferSize()), + m_responses(bufferSize()), + m_requestsReader(m_requests), + m_responsesReader(m_responses), + m_sem(0), + m_workLock(common_work_lock) +{ + assert(iface); + m_scheduleFeature.handle = static_cast(this); + m_scheduleFeature.schedule_work = [](LV2_Worker_Schedule_Handle handle, + uint32_t size, const void* data) -> LV2_Worker_Status + { + Lv2Worker* worker = static_cast(handle); + return worker->scheduleWork(size, data); + }; + + if (threaded) { m_thread = std::thread(&Lv2Worker::workerFunc, this); } + + m_requests.mlock(); + m_responses.mlock(); +} + + + + +Lv2Worker::~Lv2Worker() +{ + m_exit = true; + if(m_threaded) { + m_sem.post(); + m_thread.join(); + } +} + + + + +// Let the worker send responses to the audio thread +LV2_Worker_Status Lv2Worker::respond(uint32_t size, const void* data) +{ + if(m_threaded) + { + if(m_responses.free() < sizeof(size) + size) + { + return LV2_WORKER_ERR_NO_SPACE; + } + else + { + m_responses.write((const char*)&size, sizeof(size)); + if(size && data) { m_responses.write((const char*)data, size); } + } + } + else + { + m_iface->work_response(m_handle, size, data); + } + return LV2_WORKER_SUCCESS; +} + + + + +// Let the worker receive work from the audio thread and "work" on it +void Lv2Worker::workerFunc() +{ + std::vector buf; + uint32_t size; + while (true) { + m_sem.wait(); + if (m_exit) { break; } + const std::size_t readSpace = m_requestsReader.read_space(); + if (readSpace <= sizeof(size)) { continue; } // (should not happen) + + m_requestsReader.read(sizeof(size)).copy((char*)&size, sizeof(size)); + assert(size <= readSpace - sizeof(size)); + if(size > buf.size()) { buf.resize(size); } + if(size) { m_requestsReader.read(size).copy(buf.data(), size); } + + m_workLock->wait(); + m_iface->work(m_handle, staticWorkerRespond, this, size, buf.data()); + m_workLock->post(); + } +} + + + + +// Let the audio thread schedule work for the worker +LV2_Worker_Status Lv2Worker::scheduleWork(uint32_t size, const void *data) +{ + if (m_threaded) + { + if(m_requests.free() < sizeof(size) + size) + { + return LV2_WORKER_ERR_NO_SPACE; + } + else + { + // Schedule a request to be executed by the worker thread + m_requests.write((const char*)&size, sizeof(size)); + if(size && data) { m_requests.write((const char*)data, size); } + m_sem.post(); + } + } + else + { + // Execute work immediately in this thread + m_workLock->wait(); + m_iface->work(m_handle, staticWorkerRespond, this, size, data); + m_workLock->post(); + } + + return LV2_WORKER_SUCCESS; +} + + + + +// Let the audio thread read incoming worker responses, and process it +void Lv2Worker::emitResponses() +{ + std::size_t read_space = m_responsesReader.read_space(); + uint32_t size; + while (read_space > sizeof(size)) { + m_responsesReader.read(sizeof(size)).copy((char*)&size, sizeof(size)); + if(size) { m_responsesReader.read(size).copy(m_response.data(), size); } + m_iface->work_response(m_handle, size, m_response.data()); + read_space -= sizeof(size) + size; + } +} + + +} // namespace lmms + +#endif // LMMS_HAVE_LV2 From f664698c80f8bf55992071a409cff4b24da84739 Mon Sep 17 00:00:00 2001 From: Bimal Poudel Date: Sun, 24 Sep 2023 16:58:56 -0600 Subject: [PATCH 22/33] Update copyright year (#6888) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4163ca5cc..0b6d3b4ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ INCLUDE(GenerateExportHeader) STRING(TOUPPER "${CMAKE_PROJECT_NAME}" PROJECT_NAME_UCASE) -SET(PROJECT_YEAR 2020) +SET(PROJECT_YEAR 2023) SET(PROJECT_AUTHOR "LMMS Developers") SET(PROJECT_URL "https://lmms.io") From 23ef89b4a15b6c7cccd7eae89f5fd512a460688a Mon Sep 17 00:00:00 2001 From: Bimal Poudel Date: Sun, 24 Sep 2023 19:03:29 -0600 Subject: [PATCH 23/33] Update SampleBuffer.cpp (#6892) File information corrected. --- src/core/SampleBuffer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 5e2d09c57..c96111bba 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -1389,7 +1389,7 @@ SampleBuffer * SampleBuffer::resample(const sample_rate_t srcSR, const sample_ra } else { - printf("Error: src_new() failed in sample_buffer.cpp!\n"); + printf("Error: src_new() failed in SampleBuffer.cpp!\n"); } dstSB->update(); return dstSB; @@ -1612,7 +1612,7 @@ SampleBuffer::handleState::handleState(bool varyingPitch, int interpolationMode) if ((m_resamplingData = src_new(interpolationMode, DEFAULT_CHANNELS, &error)) == nullptr) { - qDebug("Error: src_new() failed in sample_buffer.cpp!\n"); + qDebug("Error: src_new() failed in SampleBuffer.cpp!\n"); } } From 8fb9c3e6a26fa7a212bfe9f7dfd4f069001babab Mon Sep 17 00:00:00 2001 From: Dalton Messmer <33463986+messmerd@users.noreply.github.com> Date: Thu, 28 Sep 2023 20:23:35 -0400 Subject: [PATCH 24/33] Fix warnings in Clang build (#6893) * Fix unused variable warning * Fix implicit conversion warning * Fix unused lambda capture in DataFile.cpp * Fix implicit conversions in InstrumentFunctions.cpp * Fix operator precedence bug in Flags.h * Fix unused variable warning in Lv2UridMap.h * Fix unused lambda capture in MixerView.cpp * Fix unused lambda captures in SetupDialog.cpp * Fix unused lambda capture in TrackOperationsWidget.cpp * Fix MSVC build * Fix style * Remove unused member variable in Lv2UridMap.h --- include/Flags.h | 4 ++-- include/Lv2UridMap.h | 2 -- include/MidiEvent.h | 2 +- src/core/DataFile.cpp | 2 +- src/core/DrumSynth.cpp | 7 ++++--- src/core/InstrumentFunctions.cpp | 4 ++-- src/gui/MixerView.cpp | 2 +- src/gui/modals/SetupDialog.cpp | 6 +++--- src/gui/tracks/TrackOperationsWidget.cpp | 2 +- 9 files changed, 15 insertions(+), 16 deletions(-) diff --git a/include/Flags.h b/include/Flags.h index 76106dde6..62a5f8af8 100644 --- a/include/Flags.h +++ b/include/Flags.h @@ -48,8 +48,8 @@ public: m_value{value} {} - constexpr auto testAll(Flags flags) const -> bool { return *this & flags == flags; } - constexpr auto testAny(Flags flags) const -> bool { return *this & flags != Flags{}; } + constexpr auto testAll(Flags flags) const -> bool { return (*this & flags) == flags; } + constexpr auto testAny(Flags flags) const -> bool { return (*this & flags) != Flags{}; } constexpr auto testFlag(EnumType flag) const -> bool { return static_cast(*this & flag); } constexpr auto operator~() const -> Flags { return Flags{~m_value}; } diff --git a/include/Lv2UridMap.h b/include/Lv2UridMap.h index b8733023e..6c22aca3e 100644 --- a/include/Lv2UridMap.h +++ b/include/Lv2UridMap.h @@ -55,8 +55,6 @@ class UridMap LV2_URID_Map m_mapFeature; LV2_URID_Unmap m_unmapFeature; - LV2_URID m_lastUrid = 0; - public: //! constructor; will set up the features UridMap(); diff --git a/include/MidiEvent.h b/include/MidiEvent.h index 956c33fb3..9a14e427c 100644 --- a/include/MidiEvent.h +++ b/include/MidiEvent.h @@ -212,7 +212,7 @@ private: int32_t m_sysExDataLen; // len of m_sysExData } m_data; - const char* m_sysExData; + [[maybe_unused]] const char* m_sysExData; const void* m_sourcePort; // Stores the source of the MidiEvent: Internal or External (hardware controllers). diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 8d0a8dca4..90e819077 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -302,7 +302,7 @@ void DataFile::write( QTextStream & _strm ) bool DataFile::writeFile(const QString& filename, bool withResources) { // Small lambda function for displaying errors - auto showError = [this](QString title, QString body){ + auto showError = [](QString title, QString body){ if (gui::getGUI() != nullptr) { QMessageBox mb; diff --git a/src/core/DrumSynth.cpp b/src/core/DrumSynth.cpp index bc5455c96..decc6bfa2 100644 --- a/src/core/DrumSynth.cpp +++ b/src/core/DrumSynth.cpp @@ -273,7 +273,9 @@ int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sa //generation long Length, tpos=0, tplus, totmp, t, i, j; float x[3] = {0.f, 0.f, 0.f}; - float MasterTune, randmax, randmax2; + float MasterTune; + constexpr float randmax = 1.f / static_cast(RAND_MAX); + constexpr float randmax2 = 2.f / static_cast(RAND_MAX); int MainFilter, HighPass; long NON, NT, TON, DiON, TDroop=0, DStep; @@ -454,7 +456,6 @@ int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sa } //prepare envelopes - randmax = 1.f / RAND_MAX; randmax2 = 2.f * randmax; for (i=1;i<8;i++) { envData[i][NEXTT]=0; envData[i][PNT]=0; } Length = LongestEnv(); @@ -745,4 +746,4 @@ int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sa } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 431afd2fe..976363d3d 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -409,7 +409,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) // Skip notes randomly if( m_arpSkipModel.value() ) { - if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpSkipModel.value() ) + if (100 * static_cast(rand()) / (static_cast(RAND_MAX) + 1.0f) < m_arpSkipModel.value()) { // update counters frames_processed += arp_frames; @@ -425,7 +425,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) if( m_arpMissModel.value() ) { - if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpMissModel.value() ) + if (100 * static_cast(rand()) / (static_cast(RAND_MAX) + 1.0f) < m_arpMissModel.value()) { dir = ArpDirection::Random; } diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp index dff19ca3e..018e72c2b 100644 --- a/src/gui/MixerView.cpp +++ b/src/gui/MixerView.cpp @@ -464,7 +464,7 @@ bool MixerView::confirmRemoval(int index) QString messageTitleRemoveTrack = tr("Confirm removal"); QString askAgainText = tr("Don't ask again"); auto askAgainCheckBox = new QCheckBox(askAgainText, nullptr); - connect(askAgainCheckBox, &QCheckBox::stateChanged, [this](int state) { + connect(askAgainCheckBox, &QCheckBox::stateChanged, [](int state) { // Invert button state, if it's checked we *shouldn't* ask again ConfigManager::inst()->setValue("ui", "mixerchanneldeletionwarning", state ? "0" : "1"); }); diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 0266285a7..09d6ab92c 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -168,8 +168,8 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : // Constants for positioning LED check boxes. - const int XDelta = 10; - const int YDelta = 18; + constexpr int XDelta = 10; + constexpr int YDelta = 18; // Main widget. auto main_w = new QWidget(this); @@ -212,7 +212,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : auto generalControlsLayout = new QVBoxLayout; generalControlsLayout->setSpacing(10); - auto addLedCheckBox = [&XDelta, &YDelta, this](const QString& ledText, TabWidget* tw, int& counter, + auto addLedCheckBox = [&](const QString& ledText, TabWidget* tw, int& counter, bool initialState, const char* toggledSlot, bool showRestartWarning) { auto checkBox = new LedCheckBox(ledText, tw); counter++; diff --git a/src/gui/tracks/TrackOperationsWidget.cpp b/src/gui/tracks/TrackOperationsWidget.cpp index fa1a651f6..31edc4949 100644 --- a/src/gui/tracks/TrackOperationsWidget.cpp +++ b/src/gui/tracks/TrackOperationsWidget.cpp @@ -195,7 +195,7 @@ bool TrackOperationsWidget::confirmRemoval() QString messageTitleRemoveTrack = tr("Confirm removal"); QString askAgainText = tr("Don't ask again"); auto askAgainCheckBox = new QCheckBox(askAgainText, nullptr); - connect(askAgainCheckBox, &QCheckBox::stateChanged, [this](int state){ + connect(askAgainCheckBox, &QCheckBox::stateChanged, [](int state){ // Invert button state, if it's checked we *shouldn't* ask again ConfigManager::inst()->setValue("ui", "trackdeletionwarning", state ? "0" : "1"); }); From 7dc00524fa263a6aebef088f8ad9eb26a4884982 Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Fri, 29 Sep 2023 20:39:01 +0200 Subject: [PATCH 25/33] Mallets - Add random knob function (#6466) * Mallets - Add random knob function Implement randomness for the instrument. When Random is applied, Hardness and Position of instrument 1 - 9, or Modulator and Crossfade on instrument 10 (Tubular Bells), are nudged on every note to liven up the sound. With the modulated knobs placed at their center values, Random at max will select from the full range of possible values. Co-authored-by: saker Co-authored-by: Dominic Clark --- plugins/Stk/Mallets/Mallets.cpp | 44 ++++++++++++++++++++++++++++----- plugins/Stk/Mallets/Mallets.h | 2 ++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/plugins/Stk/Mallets/Mallets.cpp b/plugins/Stk/Mallets/Mallets.cpp index b746e9491..1bca0d40f 100644 --- a/plugins/Stk/Mallets/Mallets.cpp +++ b/plugins/Stk/Mallets/Mallets.cpp @@ -87,6 +87,7 @@ MalletsInstrument::MalletsInstrument( InstrumentTrack * _instrument_track ): m_strikeModel( true, this, tr( "Bowed" ) ), m_presetsModel(this), m_spreadModel(0, 0, 255, 1, this, tr( "Spread" )), + m_randomModel(0.0f, 0.0f, 1.0f, 0.01f, this, tr("Randomness")), m_versionModel( MALLETS_PRESET_VERSION, 0, MALLETS_PRESET_VERSION, this, "" ), m_isOldVersionModel( false, this, "" ), m_filesMissing( !QDir( ConfigManager::inst()->stkDir() ).exists() || @@ -155,6 +156,7 @@ void MalletsInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this ) m_presetsModel.saveSettings( _doc, _this, "preset" ); m_spreadModel.saveSettings( _doc, _this, "spread" ); + m_randomModel.saveSettings(_doc, _this, "randomness"); m_versionModel.saveSettings( _doc, _this, "version" ); m_isOldVersionModel.saveSettings( _doc, _this, "oldversion" ); } @@ -189,6 +191,7 @@ void MalletsInstrument::loadSettings( const QDomElement & _this ) m_presetsModel.loadSettings( _this, "preset" ); m_spreadModel.loadSettings( _this, "spread" ); + m_randomModel.loadSettings(_this, "randomness"); m_isOldVersionModel.loadSettings( _this, "oldversion" ); // To maintain backward compatibility @@ -284,7 +287,7 @@ void MalletsInstrument::playNote( NotePlayHandle * _n, } int p = m_presetsModel.value(); - + const float freq = _n->frequency(); if (!_n->m_pluginData) { @@ -293,6 +296,29 @@ void MalletsInstrument::playNote( NotePlayHandle * _n, m_isOldVersionModel.value() ? 100.0 : 200.0; const float vel = _n->getVolume() / velocityAdjust; + const float random = m_randomModel.value(); + float hardness = m_hardnessModel.value(); + float position = m_positionModel.value(); + float modulator = m_modulatorModel.value(); + float crossfade = m_crossfadeModel.value(); + + if (p < 9) + { + hardness += random * static_cast(fast_rand() % 128) - 64.0; + hardness = std::clamp(hardness, 0.0f, 128.0f); + + position += random * static_cast(fast_rand() % 64) - 32.0; + position = std::clamp(position, 0.0f, 64.0f); + } + else if (p == 9) + { + modulator += random * static_cast(fast_rand() % 128) - 64.0; + modulator = std::clamp(modulator, 0.0f, 128.0f); + + crossfade += random * static_cast(fast_rand() % 128) - 64.0; + crossfade = std::clamp(crossfade, 0.0f, 128.0f); + } + // critical section as STK is not thread-safe static QMutex m; m.lock(); @@ -301,8 +327,8 @@ void MalletsInstrument::playNote( NotePlayHandle * _n, _n->m_pluginData = new MalletsSynth( freq, vel, m_stickModel.value(), - m_hardnessModel.value(), - m_positionModel.value(), + hardness, + position, m_vibratoGainModel.value(), m_vibratoFreqModel.value(), p, @@ -315,8 +341,8 @@ void MalletsInstrument::playNote( NotePlayHandle * _n, vel, p, m_lfoDepthModel.value(), - m_modulatorModel.value(), - m_crossfadeModel.value(), + modulator, + crossfade, m_lfoSpeedModel.value(), m_adsrModel.value(), (uint8_t) m_spreadModel.value(), @@ -412,6 +438,11 @@ MalletsInstrumentView::MalletsInstrumentView( MalletsInstrument * _instrument, m_spreadKnob->move( 190, 140 ); m_spreadKnob->setHintText( tr( "Spread:" ), "" ); + m_randomKnob = new Knob(KnobType::Vintage32, this); + m_randomKnob->setLabel(tr("Random")); + m_randomKnob->move(190, 190); + m_randomKnob->setHintText(tr("Random:"), ""); + // try to inform user about missing Stk-installation if( _instrument->m_filesMissing && getGUI() != nullptr ) { @@ -467,7 +498,7 @@ QWidget * MalletsInstrumentView::setupModalBarControls( QWidget * _parent ) m_stickKnob->setLabel( tr( "Stick mix" ) ); m_stickKnob->move( 190, 90 ); m_stickKnob->setHintText( tr( "Stick mix:" ), "" ); - + return( widget ); } @@ -565,6 +596,7 @@ void MalletsInstrumentView::modelChanged() // m_strikeLED->setModel( &inst->m_strikeModel ); m_presetsCombo->setModel( &inst->m_presetsModel ); m_spreadKnob->setModel( &inst->m_spreadModel ); + m_randomKnob->setModel(&inst->m_randomModel); } diff --git a/plugins/Stk/Mallets/Mallets.h b/plugins/Stk/Mallets/Mallets.h index f66ac25d0..657d1538a 100644 --- a/plugins/Stk/Mallets/Mallets.h +++ b/plugins/Stk/Mallets/Mallets.h @@ -197,6 +197,7 @@ private: ComboBoxModel m_presetsModel; FloatModel m_spreadModel; + FloatModel m_randomModel; IntModel m_versionModel; BoolModel m_isOldVersionModel; @@ -255,6 +256,7 @@ private: ComboBox * m_presetsCombo; Knob * m_spreadKnob; + Knob * m_randomKnob; }; From b29a46edf8d27106d7e43b0b3c27d7edce5554b3 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 30 Sep 2023 12:12:04 +0200 Subject: [PATCH 26/33] Ensure minimum width for setup dialog Ensure that the dialog has a minimum width so that everything is shown. --- src/gui/modals/SetupDialog.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 892155280..effece1ba 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -870,6 +870,9 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : vlayout->addWidget(extras_w); vlayout->addSpacing(10); + // Ensure that we cannot make the dialog smaller than it wants to be + setMinimumWidth(width()); + show(); } From 4f94c3b13cec0b5c2326bc9ce08402f72fb0b2ac Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sat, 30 Sep 2023 21:15:10 +0200 Subject: [PATCH 27/33] Review change - Rectangular buttons Give the reset buttons for auto save and buffer size a rectangular shape. The size is fixed but the button and the pixmap scale with different scaling factors so that should be ok. --- src/gui/modals/SetupDialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index effece1ba..7faa9c934 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -372,6 +372,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : this, SLOT(setAutoSaveInterval(int))); auto autoSaveResetBtn = new QPushButton(embed::getIconPixmap("reload"), "", autoSaveBox); + autoSaveResetBtn->setFixedSize(32, 32); connect(autoSaveResetBtn, SIGNAL(clicked()), this, SLOT(resetAutoSave())); @@ -575,6 +576,7 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : bufferSizeSubLayout->addWidget(m_bufferSizeSlider, 1); auto bufferSize_reset_btn = new QPushButton(embed::getIconPixmap("reload"), "", bufferSizeBox); + bufferSize_reset_btn->setFixedSize(32, 32); connect(bufferSize_reset_btn, SIGNAL(clicked()), this, SLOT(resetBufferSize())); bufferSize_reset_btn->setToolTip( From 85310e75d3d6e658d3f7c101de884dd5194eb910 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 1 Oct 2023 09:57:13 +0200 Subject: [PATCH 28/33] Order of includes and forward declarations Fix the alphabetical order of all includes and forward declarations. --- include/SetupDialog.h | 2 +- src/core/audio/AudioJack.cpp | 2 +- src/core/audio/AudioPulseAudio.cpp | 2 +- src/gui/modals/SetupDialog.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/SetupDialog.h b/include/SetupDialog.h index f45b09dab..882ca2bed 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -35,11 +35,11 @@ #include "MidiSetupWidget.h" +class QCheckBox; class QComboBox; class QLabel; class QLineEdit; class QSlider; -class QCheckBox; namespace lmms::gui diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index f5c3c160f..83fa3c177 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -26,9 +26,9 @@ #ifdef LMMS_HAVE_JACK +#include #include #include -#include #include "Engine.h" #include "GuiApplication.h" diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index 2164a99f2..3ca8764cc 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -22,8 +22,8 @@ * */ -#include #include +#include #include "AudioPulseAudio.h" diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 00344b1a1..c27992fa1 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -23,14 +23,14 @@ */ +#include #include +#include #include #include #include #include #include -#include -#include #include "AudioEngine.h" #include "debug.h" From d12675af2f19dd999bfe8f9ab249f21b7171cbd2 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 1 Oct 2023 10:00:52 +0200 Subject: [PATCH 29/33] Remove unnecessary addStretch Remove an unnecessary call to `addStretch` and with that a TODO. --- src/gui/modals/SetupDialog.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index c27992fa1..06231d382 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -341,10 +341,6 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : generalScroll->setWidgetResizable(true); general_layout->addWidget(generalScroll, 1); - // TODO Does not really seem to be needed - general_layout->addStretch(); - - // Performance widget. From cc6a0e86b5942c90b4b5c538cbcd8227faaec60a Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 1 Oct 2023 10:17:08 +0200 Subject: [PATCH 30/33] Remove border/shadows from scroll area Remove the border/shadows from the QScrollAreas used in the context of the setup dialog via the style sheet. --- data/themes/default/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/themes/default/style.css b/data/themes/default/style.css index a49618b0a..344aa0519 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -464,6 +464,10 @@ lmms--gui--EffectSelectDialog QScrollArea { background: #262b30; } +lmms--gui--SetupDialog QScrollArea { + border: 0px; +} + /* the inner boxes in LADSPA effect windows */ lmms--gui--EffectControlDialog QGroupBox { From 1399d5906f06d2f384e873abb1f5b0716f64ef02 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 1 Oct 2023 10:21:34 +0200 Subject: [PATCH 31/33] Left align "General" and "Paths" Left align the scroll area content for "General" and "Paths" so that they look like the content on the other tabs. --- src/gui/modals/SetupDialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 06231d382..212ef92f0 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -208,6 +208,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : // Path selectors layout. auto generalControlsLayout = new QVBoxLayout; generalControlsLayout->setSpacing(10); + generalControlsLayout->setContentsMargins(0, 0, 0, 0); auto addCheckBox = [&](const QString& ledText, QWidget* parent, QBoxLayout * layout, bool initialState, const char* toggledSlot, bool showRestartWarning) -> QCheckBox * { @@ -734,6 +735,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : // Path selectors layout. auto pathSelectorsLayout = new QVBoxLayout; pathSelectorsLayout->setSpacing(10); + pathSelectorsLayout->setContentsMargins(0, 0, 0, 0); auto addPathEntry = [&](const QString& caption, const QString& content, const char* setSlot, const char* openSlot, QLineEdit*& lineEdit, const char* pixmap = "project_open") { From a54dc9a05f2ff73ea6c4f83012e9ec8a980a6979 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 1 Oct 2023 10:34:21 +0200 Subject: [PATCH 32/33] Remove LedCheckBox include Enable the removal of the `LedCheckBox` include by commenting out code instead of using an `if(false)` statement. The commented out code was adjusted so that it would work with the current usage of layouts. --- src/gui/modals/SetupDialog.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index 212ef92f0..e64d1c572 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -38,7 +38,6 @@ #include "Engine.h" #include "FileDialog.h" #include "gui_templates.h" -#include "LedCheckBox.h" #include "MainWindow.h" #include "MidiSetupWidget.h" #include "ProjectJournal.h" @@ -542,12 +541,10 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : this, SLOT(audioInterfaceChanged(const QString&))); // Advanced setting, hidden for now - if(false) - { - // TODO Handle or remove. - auto useNaNHandler = new LedCheckBox(tr("Use built-in NaN handler"), audio_w); - useNaNHandler->setChecked(m_NaNHandler); - } + // // TODO Handle or remove. + // auto useNaNHandler = new LedCheckBox(tr("Use built-in NaN handler"), audio_w); + // audio_layout->addWidget(useNaNHandler); + // useNaNHandler->setChecked(m_NaNHandler); // HQ mode checkbox auto hqaudio = addCheckBox(tr("HQ mode for output audio device"), audioInterfaceBox, nullptr, From e1cc63bb971825a9be38f7a235a401eb807ad314 Mon Sep 17 00:00:00 2001 From: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com> Date: Sun, 1 Oct 2023 19:25:25 +0530 Subject: [PATCH 33/33] Switched ladspaeffect/Cmakelists.txt to use `LMMS_HAVE_*` instead of `WANT_*` (#6903) * fixed cmakelists conditions * Fixed the order messup. Co-authored-by: Dominic Clark --------- Co-authored-by: Dominic Clark --- plugins/LadspaEffect/CMakeLists.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/LadspaEffect/CMakeLists.txt b/plugins/LadspaEffect/CMakeLists.txt index 951615ad4..a01eb950f 100644 --- a/plugins/LadspaEffect/CMakeLists.txt +++ b/plugins/LadspaEffect/CMakeLists.txt @@ -4,22 +4,22 @@ BUILD_PLUGIN(ladspaeffect LadspaEffect.cpp LadspaControls.cpp LadspaControlDialo SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ladspa") -IF(WANT_CAPS) +IF(LMMS_HAVE_CAPS) ADD_SUBDIRECTORY(caps) -ENDIF(WANT_CAPS) +ENDIF() -IF(WANT_TAP) +IF(LMMS_HAVE_TAP) ADD_SUBDIRECTORY(tap) -ENDIF(WANT_TAP) +ENDIF() -IF(WANT_SWH) +IF(LMMS_HAVE_SWH) ADD_SUBDIRECTORY(swh) -ENDIF(WANT_SWH) +ENDIF() -IF(WANT_CMT) +IF(LMMS_HAVE_CMT) ADD_SUBDIRECTORY(cmt) -ENDIF(WANT_CMT) +ENDIF() -IF(WANT_CALF) +IF(LMMS_HAVE_CALF) ADD_SUBDIRECTORY(calf) -ENDIF(WANT_CALF) +ENDIF()