From 7e1df5cec9275e54b2c07b317864fe72c5dfb3bd Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sun, 10 Mar 2019 14:47:05 +0100 Subject: [PATCH 001/160] Add issue templates Add Github issue templates for bug reports and feature requests --- .github/ISSUE_TEMPLATE/bug_report.md | 26 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 14 ++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..3ca71ce76 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +### Bug Summary + +#### Steps to reproduce + +#### Expected behavior + +#### Actual behavior + +#### Screenshot + +#### Logs +
+ Click to expand +
+# paste here
+
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..feacb8fdb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +### Enhancement Summary + +#### Justification + +#### Mockup From 3ada5b8a126b529c94cbe0c01b60a11ecb08dec8 Mon Sep 17 00:00:00 2001 From: Olivier Humbert Date: Fri, 14 Jun 2019 21:07:14 +0200 Subject: [PATCH 002/160] Update .mailmap (#5037) --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 71b6697c8..896202c5a 100644 --- a/.mailmap +++ b/.mailmap @@ -29,3 +29,4 @@ grejppi Johannes Lorenz Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Noah Brecht +Olivier Humbert From bdbea87149ee98e3bbe6bead94c6d5102abdfc50 Mon Sep 17 00:00:00 2001 From: Steffen Baranowsky Date: Fri, 26 Jul 2019 10:04:16 +0200 Subject: [PATCH 003/160] show BBEditor on clicking the TrackLabelButton (#5060) --- src/tracks/BBTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index a4217d23d..14b65f336 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -664,5 +664,5 @@ bool BBTrackView::close() void BBTrackView::clickedTrackLabel() { Engine::getBBTrackContainer()->setCurrentBB( m_bbTrack->index() ); - gui->getBBEditor()->show(); + gui->getBBEditor()->parentWidget()->show(); } From 0db83c55a0acef82d805abbbf6cd03bd96585797 Mon Sep 17 00:00:00 2001 From: Olivier Humbert Date: Wed, 31 Jul 2019 01:17:55 +0200 Subject: [PATCH 004/160] Better French translations in the menu item file (#4711) in order to stick with the original English meaning --- cmake/linux/lmms.desktop | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/linux/lmms.desktop b/cmake/linux/lmms.desktop index 2ef4c8148..94d171b5e 100644 --- a/cmake/linux/lmms.desktop +++ b/cmake/linux/lmms.desktop @@ -3,10 +3,10 @@ Name=LMMS GenericName=Music production suite GenericName[ca]=Programari de producció musical GenericName[de]=Software zur Musik-Produktion -GenericName[fr]=Ensemble pour la production musicale +GenericName[fr]=Suite de production musicale Comment=Music sequencer and synthesizer Comment[ca]=Producció fàcil de música per a tothom! -Comment[fr]=Production facile de musique pour tout le monde ! +Comment[fr]=Séquenceur et synthétiseur de musique Icon=lmms Exec=lmms %f Terminal=false From 2cdb9f2f38a0b20f67d024e292fe06ee4f868c77 Mon Sep 17 00:00:00 2001 From: Winnie Date: Sat, 24 Aug 2019 18:55:30 +0200 Subject: [PATCH 005/160] Extract RecentProjectsMenu class from MainWindow (#5148) * Extract RecentProjectsMenu class from MainWindow * Clean up updateRecentlyOpenedProjectsMenu * Remove m_recentlyOpenedProjectsMenu from MainWindow --- include/MainWindow.h | 4 -- include/RecentProjectsMenu.h | 17 ++++++ src/gui/CMakeLists.txt | 1 + src/gui/MainWindow.cpp | 62 ++-------------------- src/gui/menus/RecentProjectsMenu.cpp | 77 ++++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 63 deletions(-) create mode 100644 include/RecentProjectsMenu.h create mode 100644 src/gui/menus/RecentProjectsMenu.cpp diff --git a/include/MainWindow.h b/include/MainWindow.h index 22a29d0e2..74e569653 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -203,8 +203,6 @@ private: QWidget * m_toolBar; QGridLayout * m_toolBarLayout; - QMenu * m_recentlyOpenedProjectsMenu; - struct keyModifiers { keyModifiers() : @@ -237,9 +235,7 @@ private: private slots: void browseHelp(); - void openRecentlyOpenedProject( QAction * _action ); void showTool( QAction * _idx ); - void updateRecentlyOpenedProjectsMenu(); void updateViewMenu( void ); void updateConfig( QAction * _who ); void onToggleMetronome(); diff --git a/include/RecentProjectsMenu.h b/include/RecentProjectsMenu.h new file mode 100644 index 000000000..b3837ee2e --- /dev/null +++ b/include/RecentProjectsMenu.h @@ -0,0 +1,17 @@ +#ifndef RECENTPROJECTSMENU_H +#define RECENTPROJECTSMENU_H + +#include + +class RecentProjectsMenu : public QMenu +{ + Q_OBJECT +public: + RecentProjectsMenu(QWidget *parent = nullptr); + +private slots: + void fillMenu(); + void openProject(QAction * _action ); +}; + +#endif // RECENTPROJECTSMENU_H diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 84dc19935..af316fddb 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -43,6 +43,7 @@ SET(LMMS_SRCS gui/editors/PianoRoll.cpp gui/editors/SongEditor.cpp + gui/menus/RecentProjectsMenu.cpp gui/menus/TemplatesMenu.cpp gui/widgets/AutomatableButton.cpp diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 4021b68e0..0914d1685 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -57,6 +57,7 @@ #include "ProjectJournal.h" #include "ProjectNotes.h" #include "ProjectRenderer.h" +#include "RecentProjectsMenu.h" #include "RemotePlugin.h" #include "SetupDialog.h" #include "SideBar.h" @@ -89,7 +90,6 @@ void disableAutoKeyAccelerators(QWidget* mainWindow) MainWindow::MainWindow() : m_workspace( NULL ), - m_recentlyOpenedProjectsMenu( NULL ), m_toolsMenu( NULL ), m_autoSaveTimer( this ), m_viewMenu( NULL ), @@ -285,13 +285,7 @@ void MainWindow::finalize() this, SLOT( openProject() ), QKeySequence::Open ); - m_recentlyOpenedProjectsMenu = project_menu->addMenu( - embed::getIconPixmap( "project_open_recent" ), - tr( "&Recently Opened Projects" ) ); - connect( m_recentlyOpenedProjectsMenu, SIGNAL( aboutToShow() ), - this, SLOT( updateRecentlyOpenedProjectsMenu() ) ); - connect( m_recentlyOpenedProjectsMenu, SIGNAL( triggered( QAction * ) ), - this, SLOT( openRecentlyOpenedProject( QAction * ) ) ); + project_menu->addMenu(new RecentProjectsMenu(this)); project_menu->addAction( embed::getIconPixmap( "project_save" ), tr( "&Save" ), @@ -439,7 +433,7 @@ void MainWindow::finalize() embed::getIconPixmap( "project_open_recent" ), tr( "Recently opened projects" ), this, SLOT( emptySlot() ), m_toolBar ); - project_open_recent->setMenu( m_recentlyOpenedProjectsMenu ); + project_open_recent->setMenu( new RecentProjectsMenu(this) ); project_open_recent->setPopupMode( ToolButton::InstantPopup ); ToolButton * project_save = new ToolButton( @@ -829,56 +823,6 @@ void MainWindow::openProject() -void MainWindow::updateRecentlyOpenedProjectsMenu() -{ - m_recentlyOpenedProjectsMenu->clear(); - QStringList rup = ConfigManager::inst()->recentlyOpenedProjects(); - -// The file history goes 50 deep but we only show the 15 -// most recent ones that we can open and omit .mpt files. - int shownInMenu = 0; - for( QStringList::iterator it = rup.begin(); it != rup.end(); ++it ) - { - QFileInfo recentFile( *it ); - if ( recentFile.exists() && - *it != ConfigManager::inst()->recoveryFile() ) - { - if( recentFile.suffix().toLower() == "mpt" ) - { - continue; - } - - m_recentlyOpenedProjectsMenu->addAction( - embed::getIconPixmap( "project_file" ), it->replace("&", "&&") ); -#ifdef LMMS_BUILD_APPLE - m_recentlyOpenedProjectsMenu->actions().last()->setIconVisibleInMenu(false); // QTBUG-44565 workaround - m_recentlyOpenedProjectsMenu->actions().last()->setIconVisibleInMenu(true); -#endif - shownInMenu++; - if( shownInMenu >= 15 ) - { - return; - } - } - } -} - - - -void MainWindow::openRecentlyOpenedProject( QAction * _action ) -{ - if ( mayChangeProject(true) ) - { - const QString f = _action->text().replace("&&", "&"); - setCursor( Qt::WaitCursor ); - Engine::getSong()->loadProject( f ); - setCursor( Qt::ArrowCursor ); - } -} - - - - bool MainWindow::saveProject() { if( Engine::getSong()->projectFileName() == "" ) diff --git a/src/gui/menus/RecentProjectsMenu.cpp b/src/gui/menus/RecentProjectsMenu.cpp new file mode 100644 index 000000000..7c09c14aa --- /dev/null +++ b/src/gui/menus/RecentProjectsMenu.cpp @@ -0,0 +1,77 @@ +#include "RecentProjectsMenu.h" + +#include + +#include "ConfigManager.h" +#include "Engine.h" +#include "Song.h" + +#include "embed.h" +#include "GuiApplication.h" +#include "MainWindow.h" + +RecentProjectsMenu::RecentProjectsMenu(QWidget *parent) : + QMenu(tr( "&Recently Opened Projects" ), parent) +{ + setIcon(embed::getIconPixmap( "project_open_recent" )); + + connect( this, SIGNAL( aboutToShow() ), + this, SLOT(fillMenu() ) ); + connect( this, SIGNAL( triggered( QAction * ) ), + this, SLOT(openProject(QAction * ) ) ); +} + + + + +void RecentProjectsMenu::fillMenu() +{ + clear(); + QStringList rup = ConfigManager::inst()->recentlyOpenedProjects(); + + auto projectFileIcon = embed::getIconPixmap( "project_file" ); + + // The file history goes 50 deep but we only show the 15 + // most recent ones that we can open and omit .mpt files. + int shownInMenu = 0; + for(QString& fileName : rup) + { + QFileInfo recentFile(fileName); + if (!recentFile.exists() || + fileName == ConfigManager::inst()->recoveryFile() ) + { + continue; + } + + if( recentFile.suffix().toLower() == "mpt" ) + { + continue; + } + + addAction(projectFileIcon, fileName.replace("&", "&&") ); +#ifdef LMMS_BUILD_APPLE + actions().last()->setIconVisibleInMenu(false); // QTBUG-44565 workaround + actions().last()->setIconVisibleInMenu(true); +#endif + + shownInMenu++; + if( shownInMenu >= 15 ) + { + break; + } + } +} + + + + +void RecentProjectsMenu::openProject(QAction * _action ) +{ + if ( gui->mainWindow()->mayChangeProject(true) ) + { + const QString f = _action->text().replace("&&", "&"); + setCursor( Qt::WaitCursor ); + Engine::getSong()->loadProject( f ); + setCursor( Qt::ArrowCursor ); + } +} From a631c0c47e73f2e1ae0d91fd2ae1a8ba0428ab7f Mon Sep 17 00:00:00 2001 From: Artur Twardowski <32247490+artur-twardowski@users.noreply.github.com> Date: Mon, 26 Aug 2019 03:05:59 +0200 Subject: [PATCH 006/160] Fix invalid MIDI Program Change decoding (#5154) --- src/core/midi/MidiAlsaSeq.cpp | 10 +++++----- src/core/midi/MidiClient.cpp | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/core/midi/MidiAlsaSeq.cpp b/src/core/midi/MidiAlsaSeq.cpp index e420ebc08..56fd956d4 100644 --- a/src/core/midi/MidiAlsaSeq.cpp +++ b/src/core/midi/MidiAlsaSeq.cpp @@ -563,7 +563,7 @@ void MidiAlsaSeq::run() case SND_SEQ_EVENT_CONTROLLER: dest->processInEvent( MidiEvent( - MidiControlChange, + MidiControlChange, ev->data.control.channel, ev->data.control.param, ev->data.control.value, source ), @@ -572,11 +572,11 @@ void MidiAlsaSeq::run() case SND_SEQ_EVENT_PGMCHANGE: dest->processInEvent( MidiEvent( - MidiProgramChange, + MidiProgramChange, ev->data.control.channel, - ev->data.control.param, - ev->data.control.value, source ), - MidiTime() ); + ev->data.control.value, 0, + source ), + MidiTime() ); break; case SND_SEQ_EVENT_CHANPRESS: diff --git a/src/core/midi/MidiClient.cpp b/src/core/midi/MidiClient.cpp index b88c64db1..e22daf235 100644 --- a/src/core/midi/MidiClient.cpp +++ b/src/core/midi/MidiClient.cpp @@ -222,12 +222,16 @@ void MidiClientRaw::parseData( const unsigned char c ) case MidiNoteOff: case MidiNoteOn: case MidiKeyPressure: - case MidiProgramChange: case MidiChannelPressure: m_midiParseData.m_midiEvent.setKey( m_midiParseData.m_buffer[0] - KeysPerOctave ); m_midiParseData.m_midiEvent.setVelocity( m_midiParseData.m_buffer[1] ); break; + case MidiProgramChange: + m_midiParseData.m_midiEvent.setKey( m_midiParseData.m_buffer[0] ); + m_midiParseData.m_midiEvent.setVelocity( m_midiParseData.m_buffer[1] ); + break; + case MidiControlChange: m_midiParseData.m_midiEvent.setControllerNumber( m_midiParseData.m_buffer[0] ); m_midiParseData.m_midiEvent.setControllerValue( m_midiParseData.m_buffer[1] ); From 4c2d8941dc6661f31e899f0079cc8c35cd992d9d Mon Sep 17 00:00:00 2001 From: RossGammon Date: Mon, 26 Aug 2019 03:09:05 +0200 Subject: [PATCH 007/160] Fix some spelling errors in lmms (#5151) --- plugins/monstro/Monstro.cpp | 2 +- plugins/sid/envelope.cc | 2 +- src/core/audio/AudioPulseAudio.cpp | 2 +- src/core/main.cpp | 2 +- src/gui/widgets/EnvelopeAndLfoView.cpp | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index acfd59b29..efb351fc8 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -328,7 +328,7 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf ) float o1l_f; float o1r_f; float o1l_p = m_osc1l_phase + o1lpo; // we add phase offset here so we don't have to do it every frame - float o1r_p = m_osc1r_phase + o1rpo; // then substract it again after loop... + float o1r_p = m_osc1r_phase + o1rpo; // then subtract it again after loop... float o1_pw; // osc2 vars diff --git a/plugins/sid/envelope.cc b/plugins/sid/envelope.cc index 7be289c53..e466bd67d 100644 --- a/plugins/sid/envelope.cc +++ b/plugins/sid/envelope.cc @@ -134,7 +134,7 @@ reg16 EnvelopeGenerator::rate_counter_period[] = { // of ENV3 with another timed loop to fully synchronize with ENV3. // // At the first period when an exponential counter period larger than one -// is used (decay or relase), one extra cycle is spent before the envelope is +// is used (decay or release), one extra cycle is spent before the envelope is // decremented. The envelope output is then delayed one cycle until the state // is changed to attack. Now one cycle less will be spent before the envelope // is incremented, and the situation is normalized. diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index 050086c60..857ef981b 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -139,7 +139,7 @@ static void stream_state_callback( pa_stream *s, void * userdata ) case PA_STREAM_FAILED: default: - qCritical( "Stream errror: %s\n", + qCritical( "Stream error: %s\n", pa_strerror(pa_context_errno( pa_stream_get_context( s ) ) ) ); } diff --git a/src/core/main.cpp b/src/core/main.cpp index f579ec8f6..1a1d61d8c 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -165,7 +165,7 @@ void printHelp() " rendertracks [options...] Render each track to a different file\n" " upgrade [out] Upgrade file and save as \n" " Standard out is used if no output file\n" - " is specifed\n" + " is specified\n" "\nGlobal options:\n" " --allowroot Bypass root user startup check (use with\n" " caution).\n" diff --git a/src/gui/widgets/EnvelopeAndLfoView.cpp b/src/gui/widgets/EnvelopeAndLfoView.cpp index cdad83d4f..4635dc831 100644 --- a/src/gui/widgets/EnvelopeAndLfoView.cpp +++ b/src/gui/widgets/EnvelopeAndLfoView.cpp @@ -419,8 +419,8 @@ void EnvelopeAndLfoView::paintEvent( QPaintEvent * ) p.fillRect( x5, y_base - 1, 2, 2, end_points_color ); - int LFO_GRAPH_W = s_lfoGraph->width() - 3; // substract border - int LFO_GRAPH_H = s_lfoGraph->height() - 6; // substract border + int LFO_GRAPH_W = s_lfoGraph->width() - 3; // subtract border + int LFO_GRAPH_H = s_lfoGraph->height() - 6; // subtract border int graph_x_base = LFO_GRAPH_X + 2; int graph_y_base = LFO_GRAPH_Y + 3 + LFO_GRAPH_H / 2; From 605aef7afc01a2355d6a56373177e9c60dcca451 Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 26 Aug 2019 03:33:25 +0200 Subject: [PATCH 008/160] Fix loading state for recent projects menu (#5152) --- src/gui/menus/RecentProjectsMenu.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gui/menus/RecentProjectsMenu.cpp b/src/gui/menus/RecentProjectsMenu.cpp index 7c09c14aa..6f32b0df5 100644 --- a/src/gui/menus/RecentProjectsMenu.cpp +++ b/src/gui/menus/RecentProjectsMenu.cpp @@ -67,11 +67,12 @@ void RecentProjectsMenu::fillMenu() void RecentProjectsMenu::openProject(QAction * _action ) { - if ( gui->mainWindow()->mayChangeProject(true) ) + auto mainWindow = gui->mainWindow(); + if (mainWindow->mayChangeProject(true)) { const QString f = _action->text().replace("&&", "&"); - setCursor( Qt::WaitCursor ); + mainWindow->setCursor( Qt::WaitCursor ); Engine::getSong()->loadProject( f ); - setCursor( Qt::ArrowCursor ); + mainWindow->setCursor( Qt::ArrowCursor ); } } From 8568ae4eacdd3bbdae8e8eaeb1536bebce3cfeeb Mon Sep 17 00:00:00 2001 From: Spekular Date: Tue, 27 Aug 2019 15:24:13 +0200 Subject: [PATCH 009/160] Make splash screen text white (#5149) Make splash screen text white Closes #5023 --- data/themes/default/style.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/themes/default/style.css b/data/themes/default/style.css index cd74c349d..a9af1139f 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -81,6 +81,12 @@ TextFloat { } +/* splash screen text */ +QSplashScreen QLabel { + color: white; +} + + QMenu { border-top: 2px solid #08993E; background-color: #15191c; From e803a31a906d701a51724c211ca4aedee996452b Mon Sep 17 00:00:00 2001 From: Winnie Date: Wed, 28 Aug 2019 22:51:54 +0200 Subject: [PATCH 010/160] Simplify TemplatesMenu class (#5155) --- include/TemplatesMenu.h | 6 ++-- src/gui/menus/TemplatesMenu.cpp | 50 +++++++++++++++------------------ 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/include/TemplatesMenu.h b/include/TemplatesMenu.h index b686ee60e..049f1f47e 100644 --- a/include/TemplatesMenu.h +++ b/include/TemplatesMenu.h @@ -12,12 +12,10 @@ public: virtual ~TemplatesMenu() = default; private slots: - void createNewProjectFromTemplate( QAction * _idx ); + static void createNewProjectFromTemplate(QAction * _action); void fillTemplatesMenu(); - int addTemplatesFromDir( const QDir& dir ); + void addTemplatesFromDir( const QDir& dir ); -private: - int m_customTemplatesCount; }; #endif // TEMPLATESMENU_H diff --git a/src/gui/menus/TemplatesMenu.cpp b/src/gui/menus/TemplatesMenu.cpp index b1cca87ab..5fb740203 100644 --- a/src/gui/menus/TemplatesMenu.cpp +++ b/src/gui/menus/TemplatesMenu.cpp @@ -1,14 +1,15 @@ #include "TemplatesMenu.h" -#include "GuiApplication.h" + #include "ConfigManager.h" #include "Engine.h" -#include "embed.h" -#include "MainWindow.h" #include "Song.h" +#include "embed.h" +#include "GuiApplication.h" +#include "MainWindow.h" + TemplatesMenu::TemplatesMenu(QWidget *parent) : - QMenu(tr("New from template"), parent), - m_customTemplatesCount(0) + QMenu(tr("New from template"), parent) { connect( this, SIGNAL( aboutToShow() ), SLOT( fillTemplatesMenu() ) ); connect( this, SIGNAL( triggered( QAction * ) ), @@ -18,18 +19,12 @@ TemplatesMenu::TemplatesMenu(QWidget *parent) : -void TemplatesMenu::createNewProjectFromTemplate( QAction * _idx ) +void TemplatesMenu::createNewProjectFromTemplate(QAction * _action) { if( gui->mainWindow()->mayChangeProject(true) ) { - int indexOfTemplate = actions().indexOf( _idx ); - bool isFactoryTemplate = indexOfTemplate >= m_customTemplatesCount; - QString dirBase = isFactoryTemplate ? - ConfigManager::inst()->factoryTemplatesDir() : - ConfigManager::inst()->userTemplateDir(); - - const QString f = dirBase + _idx->text().replace("&&", "&") + ".mpt"; - Engine::getSong()->createNewProjectFromTemplate(f); + const QString& templateFilePath = _action->data().toString(); + Engine::getSong()->createNewProjectFromTemplate(templateFilePath); } } @@ -41,33 +36,32 @@ void TemplatesMenu::fillTemplatesMenu() { clear(); - m_customTemplatesCount = addTemplatesFromDir(ConfigManager::inst()->userTemplateDir() ); - addTemplatesFromDir( ConfigManager::inst()->factoryProjectsDir() + "templates" ); + addTemplatesFromDir(ConfigManager::inst()->userTemplateDir()); + addTemplatesFromDir(ConfigManager::inst()->factoryProjectsDir() + "templates"); } -int TemplatesMenu::addTemplatesFromDir( const QDir& dir ) { - QStringList templates = dir.entryList( QStringList( "*.mpt" ), +void TemplatesMenu::addTemplatesFromDir( const QDir& dir ) { + QFileInfoList templates = dir.entryInfoList( QStringList( "*.mpt" ), QDir::Files | QDir::Readable ); - if ( templates.size() && ! actions().isEmpty() ) + if (!templates.empty() && !actions().isEmpty()) { addSeparator(); } - for( QStringList::iterator it = templates.begin(); - it != templates.end(); ++it ) + auto projectFileIcon = embed::getIconPixmap( "project_file" ); + + for(const QFileInfo& templateFile : templates) { - addAction( - embed::getIconPixmap( "project_file" ), - ( *it ).left( ( *it ).length() - 4 ).replace("&", "&&") ); + auto action = addAction(projectFileIcon, + templateFile.completeBaseName().replace("&", "&&")); + action->setData(templateFile.absoluteFilePath()); #ifdef LMMS_BUILD_APPLE - actions().last()->setIconVisibleInMenu(false); // QTBUG-44565 workaround - actions().last()->setIconVisibleInMenu(true); + action->setIconVisibleInMenu(false); // QTBUG-44565 workaround + action->setIconVisibleInMenu(true); #endif } - - return templates.size(); } From c2eefe73d049ae92398da288acdc3d71c3592504 Mon Sep 17 00:00:00 2001 From: Lost Robot <34612565+DouglasDGI@users.noreply.github.com> Date: Fri, 30 Aug 2019 16:36:04 -0600 Subject: [PATCH 011/160] Prevent LMMS VisualizationWidget from showing clipping color at exactly 0 db (#5162) --- src/gui/widgets/VisualizationWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/widgets/VisualizationWidget.cpp b/src/gui/widgets/VisualizationWidget.cpp index 7234331df..635f96896 100644 --- a/src/gui/widgets/VisualizationWidget.cpp +++ b/src/gui/widgets/VisualizationWidget.cpp @@ -210,7 +210,7 @@ QColor const & VisualizationWidget::determineLineColor(float level) const { return normalColor(); } - else if( level < 1.0f ) + else if( level <= 1.0f ) { return warningColor(); } From 4fd8e08f3bd02f173e97bb721a30565f17cd1dbc Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 13 Jun 2018 16:32:57 +0200 Subject: [PATCH 012/160] Rewrite CMake dependency installation Replaces the hard-coded library paths by a method based on CMake's GetPrerequisites module which recursively finds a binary file's linked libraries. Advantage: Potentially works on any system without adaption as long as CMake supports it, so it could be used to create portable Linux packages as well. Disadvantage: "Potentially". Co-Authored-By: Hyunjin Song --- CMakeLists.txt | 5 +- cmake/install/CMakeLists.txt | 125 ++++++++++++++ cmake/install/excludelist-win | 23 +++ cmake/modules/BuildPlugin.cmake | 8 +- cmake/modules/InstallDependencies.cmake | 184 ++++++++++++++++++++ cmake/postinstall/CMakeLists.txt | 4 - plugins/zynaddsubfx/CMakeLists.txt | 2 + src/CMakeLists.txt | 220 ++---------------------- 8 files changed, 359 insertions(+), 212 deletions(-) create mode 100644 cmake/install/CMakeLists.txt create mode 100644 cmake/install/excludelist-win create mode 100644 cmake/modules/InstallDependencies.cmake delete mode 100644 cmake/postinstall/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index bd9d376e2..590a20c98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0050 OLD) ENDIF() CMAKE_POLICY(SET CMP0020 NEW) + CMAKE_POLICY(SET CMP0057 NEW) ENDIF(COMMAND CMAKE_POLICY) INCLUDE(CheckSubmodules) @@ -565,8 +566,8 @@ ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(data) ADD_SUBDIRECTORY(doc) -# post-install tasks -ADD_SUBDIRECTORY(cmake/postinstall) +# install tasks +ADD_SUBDIRECTORY(cmake/install) ADD_CUSTOM_COMMAND(OUTPUT "${CMAKE_BINARY_DIR}/lmms.1.gz" COMMAND gzip -c ${CMAKE_SOURCE_DIR}/doc/lmms.1 > ${CMAKE_BINARY_DIR}/lmms.1.gz diff --git a/cmake/install/CMakeLists.txt b/cmake/install/CMakeLists.txt new file mode 100644 index 000000000..3e37fa00e --- /dev/null +++ b/cmake/install/CMakeLists.txt @@ -0,0 +1,125 @@ + +FUNCTION(GET_COMPILER_SEARCH_DIR VAR) + SET(results "") + IF(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang|AppleClang)") + EXECUTE_PROCESS( + COMMAND ${CMAKE_CXX_COMPILER} --print-search-dirs + OUTPUT_VARIABLE out + ) + STRING(REPLACE "\n" ";" out "${out}") + FOREACH(line ${out}) + IF(line MATCHES "^.+:") + STRING(REPLACE " " ";" line "${line}") + LIST(GET line 1 paths) + # Remove "=" prefix + STRING(REGEX REPLACE "^=" "" paths "${paths}") + STRING(REPLACE ":" ";" paths "${paths}") + FOREACH(path ${paths}) + LIST(APPEND results ${path}) + ENDFOREACH() + ENDIF() + ENDFOREACH() + ENDIF() + + SET(paths "") + FOREACH(result ${results}) + GET_FILENAME_COMPONENT(result ${result} REALPATH) + IF(IS_DIRECTORY "${result}") + LIST(APPEND paths ${result}) + ENDIF() + ENDFOREACH() + LIST(REMOVE_DUPLICATES paths) + + SET(${VAR} ${paths} PARENT_SCOPE) +ENDFUNCTION() + +SET(PLUGIN_FILES "") +IF(LMMS_BUILD_WIN32) + INSTALL(FILES $ DESTINATION platforms) +ENDIF() + +IF(LMMS_BUILD_WIN32 OR LMMS_INSTALL_DEPENDENCIES) + # Collect directories to search for DLLs + GET_FILENAME_COMPONENT(QTBIN_DIR "${QT_QMAKE_EXECUTABLE}" PATH) + set(LIB_DIRS "${QTBIN_DIR}") + + GET_PROPERTY(PLUGINS_BUILT GLOBAL PROPERTY PLUGINS_BUILT) + + foreach(target lmms ${PLUGINS_BUILT}) + get_target_property(target_libs ${target} LINK_LIBRARIES) + + foreach(lib ${target_libs}) + if(TARGET ${lib} OR NOT IS_ABSOLUTE ${lib}) + continue() + endif() + + get_filename_component(lib_dir ${lib} PATH) + list(APPEND LIB_DIRS ${lib_dir}) + if(lib MATCHES ".(lib|dll.a)$") + if(IS_DIRECTORY ${lib_dir}/../bin) + list(APPEND LIB_DIRS ${lib_dir}/../bin) + endif() + if(IS_DIRECTORY ${lib_dir}/bin) + list(APPEND LIB_DIRS ${lib_dir}/bin) + endif() + endif() + endforeach() + endforeach() + + GET_COMPILER_SEARCH_DIR(COMPILER_SEARCH_DIRS) + LIST(APPEND LIB_DIRS ${COMPILER_SEARCH_DIRS}) + + LIST(REMOVE_DUPLICATES LIB_DIRS) + + # Collect plugin files to inspect + FOREACH(PLUGIN ${PLUGINS_BUILT}) + LIST(APPEND DEPLOY_TARGETS "$") + ENDFOREACH() + # Create the list of files using file(GENERATE) + SET(DEPLOY_LIST_FILE "${CMAKE_CURRENT_BINARY_DIR}/filelist.txt") + FILE(GENERATE OUTPUT "${DEPLOY_LIST_FILE}" CONTENT "${DEPLOY_TARGETS}") + + IF(LMMS_BUILD_LINUX) + FILE(DOWNLOAD "https://raw.githubusercontent.com/AppImage/AppImages/master/excludelist" + "${CMAKE_BINARY_DIR}/excludelist") + SET(additional_args INCLUDE_SYSTEM IGNORE_LIBS_FILE ${CMAKE_BINARY_DIR}/excludelist) + ELSEIF(LMMS_BUILD_WIN32) + SET(additional_args IGNORE_CASE IGNORE_LIBS_FILE ${CMAKE_CURRENT_LIST_DIR}/excludelist-win) + IF(CMAKE_CROSSCOMPILING) + SET(additional_args "${additional_args}" GP_TOOL objdump) + ENDIF() + ENDIF() + + IF(LMMS_BUILD_WIN32) + SET(LMMS_DEP_DESTINATION ${BIN_DIR}) + SET(PLUGIN_DEP_DESTINATION ${BIN_DIR}) + ELSE() + SET(LMMS_DEP_DESTINATION ${LIB_DIR}) + SET(PLUGIN_DEP_DESTINATION ${LIB_DIR}) + ENDIF() + + INSTALL(CODE " + INCLUDE(\"${CMAKE_SOURCE_DIR}/cmake/modules/InstallDependencies.cmake\") + # Install dependencies of lmms + INSTALL_DEPENDENCIES( + FILES ${BIN_DIR}/lmms${CMAKE_EXECUTABLE_SUFFIX} + DESTINATION ${LMMS_DEP_DESTINATION} + LIB_DIRS ${LIB_DIRS} + ${additional_args} + ) + # Install dependencies of plugins + FILE(READ \"${DEPLOY_LIST_FILE}\" DEPLOY_FILES) + INSTALL_DEPENDENCIES( + FILES \"\${DEPLOY_FILES}\" + DESTINATION \"${PLUGIN_DEP_DESTINATION}\" + LIB_DIRS ${LIB_DIRS} \"${BIN_DIR}\" \"${PLUGIN_DIR}\" + SEARCH_PATHS \"${BIN_DIR}\" \"${PLUGIN_DIR}\" + ${additional_args} + ) + ") +ENDIF() + +IF(LMMS_BUILD_APPLE) + INSTALL(CODE "EXECUTE_PROCESS(COMMAND chmod u+x ${CMAKE_BINARY_DIR}/install_apple.sh)") + INSTALL(CODE "EXECUTE_PROCESS(COMMAND ${CMAKE_BINARY_DIR}/install_apple.sh)") +ENDIF() diff --git a/cmake/install/excludelist-win b/cmake/install/excludelist-win new file mode 100644 index 000000000..17793a113 --- /dev/null +++ b/cmake/install/excludelist-win @@ -0,0 +1,23 @@ +# List of DLLs considered to be system libraries. +# This is needed when cross-compiling for Windows. +ADVAPI32.dll +COMCTL32.dll +comdlg32.dll +dwmapi.dll +GDI32.dll +IMM32.dll +KERNEL32.dll +MPR.DLL +msvcrt.dll +ole32.dll +OLEAUT32.dll +OPENGL32.DLL +SHELL32.dll +USER32.dll +UxTheme.dll +VERSION.dll +WINMM.DLL +WS2_32.dll +RPCRT4.dll +dsound.dll +SETUPAPI.dll diff --git a/cmake/modules/BuildPlugin.cmake b/cmake/modules/BuildPlugin.cmake index efa3e5b46..e285e05bb 100644 --- a/cmake/modules/BuildPlugin.cmake +++ b/cmake/modules/BuildPlugin.cmake @@ -62,7 +62,10 @@ MACRO(BUILD_PLUGIN PLUGIN_NAME) TARGET_LINK_LIBRARIES(${PLUGIN_NAME} lmms) ENDIF(LMMS_BUILD_WIN32) - INSTALL(TARGETS ${PLUGIN_NAME} DESTINATION "${PLUGIN_DIR}") + INSTALL(TARGETS ${PLUGIN_NAME} + LIBRARY DESTINATION "${PLUGIN_DIR}" + RUNTIME DESTINATION "${PLUGIN_DIR}" + ) IF(LMMS_BUILD_APPLE) IF ("${PLUGIN_LINK}" STREQUAL "SHARED") @@ -89,5 +92,8 @@ MACRO(BUILD_PLUGIN PLUGIN_NAME) TARGET_INCLUDE_DIRECTORIES(${PLUGIN_NAME} PUBLIC $ ) + + SET_PROPERTY(GLOBAL APPEND PROPERTY PLUGINS_BUILT ${PLUGIN_NAME}) + GET_PROPERTY(PLUGINS_BUILT GLOBAL PROPERTY PLUGINS_BUILT) ENDMACRO(BUILD_PLUGIN) diff --git a/cmake/modules/InstallDependencies.cmake b/cmake/modules/InstallDependencies.cmake new file mode 100644 index 000000000..791041bb2 --- /dev/null +++ b/cmake/modules/InstallDependencies.cmake @@ -0,0 +1,184 @@ +include(GetPrerequisites) +include(CMakeParseArguments) + +CMAKE_POLICY(SET CMP0011 NEW) +CMAKE_POLICY(SET CMP0057 NEW) + +function(make_absolute var) + get_filename_component(abs "${${var}}" ABSOLUTE BASE_DIR "${CMAKE_INSTALL_PREFIX}") + set(${var} ${abs} PARENT_SCOPE) +endfunction() + +# Reads lines of a file into a list, skipping '#' comment lines +function(READ_LIST_FILE FILE VAR) + file(STRINGS "${FILE}" list) + + set(result "") + foreach(item ${list}) + string(STRIP "${item}" item) + if(item STREQUAL "" OR item MATCHES "^\#") + continue() + endif() + list(APPEND result "${item}") + endforeach() + + set(${VAR} ${result} PARENT_SCOPE) +endfunction() + +function(make_all_absolute list_var) + set(result "") + foreach(file ${${list_var}}) + make_absolute(file) + list(APPEND result ${file}) + endforeach() + set(${list_var} ${result} PARENT_SCOPE) +endfunction() + +if(CMAKE_BINARY_DIR) + set(tmp_lib_dir "${CMAKE_BINARY_DIR}/bundled-libraries") +elseif(CMAKE_HOST_UNIX) + set(tmp_lib_dir "/tmp/bundled-libraries") +elseif(DEFINED ENV{TEMP}) + set(tmp_lib_dir "$ENV{TMP}/bundled-libraries") +else() + message(FATAL_ERROR "Can't find a temp dir for libraries") +endif() + +# Like file(INSTALL), but resolves symlinks +function(install_file_resolved file destination) + + get_filename_component(file_name "${file}" NAME) + if(IS_SYMLINK "${file}") + get_filename_component(real_path "${file}" REALPATH) + get_filename_component(real_name "${real_path}" NAME) + file(COPY "${real_path}" DESTINATION "${tmp_lib_dir}") + file(RENAME "${tmp_lib_dir}/${real_name}" "${tmp_lib_dir}/${file_name}") + set(file_path "${tmp_lib_dir}/${file_name}") + else() + set(file_path "${file}") + endif() + + file(INSTALL "${file_path}" DESTINATION "${destination}") +endfunction() + +function(install_resolved) + cmake_parse_arguments("" "" "DESTINATION" "FILES" ${ARGN}) + foreach(file ${_FILES}) + install_file_resolved("${file}" "${_DESTINATION}") + endforeach() +endfunction() + +if(CMAKE_CROSSCOMPILING) + # If we're cross-compiling, GetPrerequisites may not be able to find system libraries such as kernel32.dll because + # they're supplied by the toolchain. To suppress thousands of lines of warnings being printed to the console, we + # override gp_resolved_file_type to return "system" for any library in ${IGNORE_LIBS} without trying to resolve the + # file first. + # GetPrerequisites supports using an override function called gp_resolved_file_type_override, but it's not suited + # for our purpose because it's only called by gp_resolved_file_type *after* trying to resolve the file. + function(gp_resolved_file_type original_file file exepath dirs type_var) + set(file_find "${file}") + if(_IGNORE_CASE) + # On case-insensitive systems, convert to upper characters to respect it + string(TOUPPER "${file_find}" file_find) + endif() + SET(IGNORE_LIBS ${_IGNORE_LIBS} CACHE INTERNAL "Ignored library names" FORCE) + if(IGNORE_LIBS AND ${file_find} IN_LIST IGNORE_LIBS) + set(${type_var} system PARENT_SCOPE) + else() + #_gp_resolved_file_type(${ARGV}) + _gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "${dirs}" "${type_var}" ${ARGN}) + endif() + endfunction() +endif() + +function(INSTALL_DEPENDENCIES) + cmake_parse_arguments("" "INCLUDE_SYSTEM;IGNORE_CASE" "GP_TOOL;DESTINATION;IGNORE_LIBS_FILE" "FILES;LIB_DIRS;SEARCH_PATHS;IGNORE_LIBS" ${ARGN}) + + # Make paths absolute + make_absolute(_DESTINATION) + make_all_absolute(_FILES) + make_all_absolute(_LIB_DIRS) + make_all_absolute(_SEARCH_PATHS) + + if(_INCLUDE_SYSTEM) + set(EXCLUDE_SYSTEM 0) + else() + set(EXCLUDE_SYSTEM 1) + endif() + + if(_IGNORE_LIBS_FILE) + READ_LIST_FILE("${_IGNORE_LIBS_FILE}" _IGNORE_LIBS) + if(_IGNORE_CASE) + # On case-insensitive systems, convert to upper characters to respect it + string(TOUPPER "${_IGNORE_LIBS}" _IGNORE_LIBS) + endif() + SET(IGNORE_LIBS ${_IGNORE_LIBS} CACHE INTERNAL "Ignored library names" FORCE) + endif() + + if(_GP_TOOL) + set(gp_tool "${_GP_TOOL}") + endif() + + set(prereqs "") + foreach(file ${_FILES}) + get_filename_component(file_name "${file}" NAME) + message("-- Finding prerequisites of ${file_name}") + find_prerequisites("${file}" _prereqs + ${EXCLUDE_SYSTEM} # exclude system files + 1 # recurse + "" + "${_LIB_DIRS}" + "${_SEARCH_PATHS}" + "${_IGNORE_LIBS}" + ) + + list(APPEND prereqs ${_prereqs}) + endforeach() + + list(REMOVE_DUPLICATES prereqs) + + foreach(prereq ${prereqs}) + get_filename_component(prereq_name "${prereq}" NAME) + + foreach(rpath ${_SEARCH_PATHS}) + if(EXISTS "${rpath}/${prereq_name}") + list(REMOVE_ITEM prereqs "${prereq}") + break() + endif() + endforeach() + endforeach() + + #file(INSTALL ${prereqs} DESTINATION ${_DESTINATION}) + install_resolved(FILES ${prereqs} DESTINATION "${_DESTINATION}") +endfunction() + +# Like get_prerequisites, but returns full paths +function(FIND_PREREQUISITES target RESULT_VAR exclude_system recurse + exepath dirs rpaths) + set(RESULTS) + + get_prerequisites("${target}" _prereqs ${exclude_system} ${recurse} + "" "${dirs}" "${rpaths}") + + foreach(prereq ${_prereqs}) + get_filename_component(prereq_name "${prereq}" NAME) + if(_IGNORE_CASE) + # Windows is case insensitive. + # Use upper characters to respect it. + string(TOUPPER "${prereq_name}" prereq_name) + endif() + if("${prereq_name}" IN_LIST IGNORE_LIBS) + continue() + endif() + + gp_resolve_item("${LIB_DLL}" "${prereq}" "" "${dirs}" RESOLVED_PREREQ "${rpaths}") + + if(RESOLVED_PREREQ AND IS_ABSOLUTE ${RESOLVED_PREREQ} AND EXISTS ${RESOLVED_PREREQ}) + list(APPEND RESULTS ${RESOLVED_PREREQ}) + else() + message(FATAL_ERROR "Can't resolve dependency ${prereq}.") + endif() + endforeach() + + set(${RESULT_VAR} ${RESULTS} PARENT_SCOPE) +endfunction() diff --git a/cmake/postinstall/CMakeLists.txt b/cmake/postinstall/CMakeLists.txt deleted file mode 100644 index 434d1c54e..000000000 --- a/cmake/postinstall/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -IF(LMMS_BUILD_APPLE) - INSTALL(CODE "EXECUTE_PROCESS(COMMAND chmod u+x ${CMAKE_BINARY_DIR}/install_apple.sh)") - INSTALL(CODE "EXECUTE_PROCESS(COMMAND ${CMAKE_BINARY_DIR}/install_apple.sh)") -ENDIF() \ No newline at end of file diff --git a/plugins/zynaddsubfx/CMakeLists.txt b/plugins/zynaddsubfx/CMakeLists.txt index f1d37fa3e..d6fa9c727 100644 --- a/plugins/zynaddsubfx/CMakeLists.txt +++ b/plugins/zynaddsubfx/CMakeLists.txt @@ -162,6 +162,8 @@ SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) ADD_EXECUTABLE(RemoteZynAddSubFx RemoteZynAddSubFx.cpp "${WINRC}") INSTALL(TARGETS RemoteZynAddSubFx RUNTIME DESTINATION "${PLUGIN_DIR}") +# Needed to deploy dependencies of RemoteZynAddSubFx +SET_PROPERTY(GLOBAL APPEND PROPERTY PLUGINS_BUILT "RemoteZynAddSubFx") IF(LMMS_BUILD_WIN32) SET_TARGET_PROPERTIES(RemoteZynAddSubFx PROPERTIES LINK_FLAGS "${LINK_FLAGS} -mwindows") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4ac6bf133..10ce72ae6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -186,210 +186,20 @@ FOREACH(LIB ${LMMS_REQUIRED_LIBS}) ENDIF() ENDFOREACH() - -# Install -IF(NOT MSVC) - IF(LMMS_BUILD_WIN32) - SET_TARGET_PROPERTIES(lmms PROPERTIES - LINK_FLAGS "${LINK_FLAGS} -mwindows" - ENABLE_EXPORTS ON +IF(LMMS_BUILD_WIN32) + SET_TARGET_PROPERTIES(lmms PROPERTIES + ENABLE_EXPORTS ON + ) + IF(LMMS_BUILD_MSYS) + # ENABLE_EXPORTS property has no effect in some MSYS2 configurations. + # Add the linker flag manually to create liblmms.dll.a import library + SET_PROPERTY(TARGET lmms + APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--out-implib,liblmms.dll.a" ) + ENDIF() +ELSE() + INSTALL(FILES "${CMAKE_BINARY_DIR}/lmms.1.gz" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man1/" + PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) +ENDIF() - IF(LMMS_BUILD_MSYS) - # ENABLE_EXPORTS property has no effect in some MSYS2 configurations. - # Add the linker flag manually to create liblmms.dll.a import library - SET_PROPERTY(TARGET lmms - APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--out-implib,liblmms.dll.a" - ) - ENDIF() - - IF(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - ADD_CUSTOM_COMMAND(TARGET lmms POST_BUILD COMMAND "${STRIP}" "$") - ENDIF() - - INSTALL(TARGETS lmms RUNTIME DESTINATION "${BIN_DIR}") - INSTALL(FILES - "${MINGW_PREFIX}/bin/Qt5Core.dll" - "${MINGW_PREFIX}/bin/Qt5Gui.dll" - "${MINGW_PREFIX}/bin/Qt5Widgets.dll" - "${MINGW_PREFIX}/bin/Qt5Xml.dll" - DESTINATION .) - INSTALL(FILES - "${MINGW_PREFIX}/lib/qt5/plugins/platforms/qwindows.dll" - DESTINATION ./platforms) - INSTALL(FILES - "${MINGW_PREFIX}/bin/libsamplerate-0.dll" - "${MINGW_PREFIX}/bin/libsndfile-1.dll" - "${MINGW_PREFIX}/bin/libvorbis-0.dll" - "${MINGW_PREFIX}/bin/libvorbisenc-2.dll" - "${MINGW_PREFIX}/bin/libvorbisfile-3.dll" - "${MINGW_PREFIX}/bin/libjpeg-9.dll" - "${MINGW_PREFIX}/bin/libogg-0.dll" - "${MINGW_PREFIX}/bin/libmp3lame-0.dll" - "${MINGW_PREFIX}/bin/libfftw3f-3.dll" - "${MINGW_PREFIX}/bin/libFLAC-8.dll" - "${MINGW_PREFIX}/bin/libpng16-16.dll" - "${MINGW_PREFIX}/bin/SDL.dll" - "${MINGW_PREFIX}/bin/libglib-2.0-0.dll" - "${MINGW_PREFIX}/bin/libgthread-2.0-0.dll" - "${MINGW_PREFIX}/bin/zlib1.dll" - "${MINGW_PREFIX}/${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32/bin/libwinpthread-1.dll" - DESTINATION .) - IF(LMMS_BUILD_MSYS) - INSTALL(FILES - "${MINGW_PREFIX}/bin/libwinpthread-1.dll" - "${MINGW_PREFIX}/bin/libgcc_s_seh-1.dll" - "${MINGW_PREFIX}/bin/libstdc++-6.dll" - DESTINATION .) - ELSE() - INSTALL(FILES - "${MINGW_PREFIX}/${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32/bin/libwinpthread-1.dll" - DESTINATION .) - ENDIF() - IF(FLTK_FOUND) - INSTALL(FILES - "${MINGW_PREFIX}/bin/libfltk.dll" - DESTINATION .) - ENDIF() - IF(FLUIDSYNTH_FOUND) - INSTALL(FILES - "${MINGW_PREFIX}/bin/libfluidsynth.dll" - DESTINATION .) - ENDIF() - IF(GIG_FOUND) - # Handle libgig-*.dll - FILE(GLOB GIG_LIBRARY "${MINGW_PREFIX}/bin/libgig-*.dll") - INSTALL(FILES - ${GIG_LIBRARY} - DESTINATION .) - ENDIF() - IF(PORTAUDIO_FOUND) - INSTALL(FILES - "${MINGW_PREFIX}/bin/libportaudio-2.dll" - DESTINATION .) - ENDIF() - IF(SOUNDIO_FOUND) - INSTALL(FILES - "${MINGW_PREFIX}/lib/libsoundio.dll" - DESTINATION .) - ENDIF() - - ELSE(LMMS_BUILD_WIN32) - IF(NOT LMMS_BUILD_APPLE) - SET_TARGET_PROPERTIES(lmms PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,-E") - ENDIF(NOT LMMS_BUILD_APPLE) - - INSTALL(TARGETS lmms RUNTIME DESTINATION "${BIN_DIR}") - INSTALL(FILES "${CMAKE_BINARY_DIR}/lmms.1.gz" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man1/" PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) - - ENDIF(LMMS_BUILD_WIN32) -ELSE(NOT MSVC) - SET_TARGET_PROPERTIES(lmms PROPERTIES - ENABLE_EXPORTS ON - ) - INSTALL(TARGETS lmms RUNTIME DESTINATION "${BIN_DIR}") - - SET_TARGET_PROPERTIES(lmms PROPERTIES - LINK_FLAGS "${LINK_FLAGS} -mwindows" - ENABLE_EXPORTS ON - ) - - #CMAKE_FIND_ROOT_PATH - SET(VCPKG_ROOT ${CMAKE_FIND_ROOT_PATH}) - - INSTALL(TARGETS lmms RUNTIME DESTINATION "${BIN_DIR}") - - INSTALL(FILES - "${VCPKG_ROOT}/bin/Qt5Core.dll" - "${VCPKG_ROOT}/bin/Qt5Gui.dll" - "${VCPKG_ROOT}/bin/Qt5Widgets.dll" - "${VCPKG_ROOT}/bin/Qt5Xml.dll" - - "${VCPKG_ROOT}/bin/zlib1.dll" - "${VCPKG_ROOT}/bin/jpeg62.dll" - "${VCPKG_ROOT}/bin/libpng16.dll" - "${VCPKG_ROOT}/bin/gthread-2.dll" - "${VCPKG_ROOT}/bin/glib-2.dll" - "${VCPKG_ROOT}/bin/harfbuzz.dll" - "${VCPKG_ROOT}/bin/pcre2-16.dll" - "${VCPKG_ROOT}/bin/double-conversion.dll" - "${VCPKG_ROOT}/bin/freetype.dll" - "${VCPKG_ROOT}/bin/libbz2.dll" - "${VCPKG_ROOT}/bin/pcre.dll" - "${VCPKG_ROOT}/bin/libiconv.dll" - "${VCPKG_ROOT}/bin/libcharset.dll" - "${VCPKG_ROOT}/bin/libintl.dll" - DESTINATION .) - - INSTALL(FILES - "${VCPKG_ROOT}/plugins/platforms/qwindows.dll" - DESTINATION ./platforms) - - INSTALL(FILES - "${VCPKG_ROOT}/bin/libsndfile-1.dll" - "${VCPKG_ROOT}/bin/ogg.dll" - "${VCPKG_ROOT}/bin/vorbis.dll" - "${VCPKG_ROOT}/bin/vorbisenc.dll" - "${VCPKG_ROOT}/bin/FLAC.dll" - "${VCPKG_ROOT}/bin/vorbisfile.dll" - - "${VCPKG_ROOT}/bin/libsamplerate-0.dll" - "${VCPKG_ROOT}/bin/SDL2.dll" - "${VCPKG_ROOT}/bin/fftw3f.dll" - DESTINATION .) - - #not yet in vcpkg - #IF(LAME_FOUND) - # INSTALL(FILES - # "${VCPKG_ROOT}/bin/libmp3lame-0.dll" - # DESTINATION .) - #ENDIF(LAME_FOUND) - - IF(FLTK_FOUND) - INSTALL(FILES - "${VCPKG_ROOT}/bin/libfltk_SHARED.dll" - - "${VCPKG_ROOT}/bin/zlib1.dll" - "${VCPKG_ROOT}/bin/jpeg62.dll" - DESTINATION .) - ENDIF() - - IF(FLUIDSYNTH_FOUND) - INSTALL(FILES - "${VCPKG_ROOT}/bin/libfluidsynth-1.dll" - "${VCPKG_ROOT}/bin/glib-2.dll" - "${VCPKG_ROOT}/bin/pcre.dll" - "${VCPKG_ROOT}/bin/libiconv.dll" - "${VCPKG_ROOT}/bin/libcharset.dll" - "${VCPKG_ROOT}/bin/libintl.dll" - DESTINATION .) - ENDIF() - - #not yet included in vcpkg - #IF(GIG_FOUND) - # # Handle libgig-*.dll - # FILE(GLOB GIG_LIBRARY "${VCPKG_ROOT}/bin/libgig-*.dll") - # INSTALL(FILES - # ${GIG_LIBRARY} - # DESTINATION .) - #ENDIF() - - IF(PORTAUDIO_FOUND) - IF(LMMS_BUILD_WIN64) - INSTALL(FILES - "${VCPKG_ROOT}/bin/portaudio-x64.dll" - DESTINATION .) - ELSE(LMMS_BUILD_WIN64) - INSTALL(FILES - "${VCPKG_ROOT}/bin/portaudio-x86.dll" - DESTINATION .) - ENDIF(LMMS_BUILD_WIN64) - ENDIF() - - #not yet in vcpkg - #IF(SOUNDIO_FOUND) - # INSTALL(FILES - # "${VCPKG_ROOT}/bin/libsoundio.dll" - # DESTINATION .) - #ENDIF() -ENDIF(NOT MSVC) +INSTALL(TARGETS lmms RUNTIME DESTINATION "${BIN_DIR}") \ No newline at end of file From 8d169fa5290c1b8a8c3e0894060337083a7080ee Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 26 Nov 2018 12:50:37 +0900 Subject: [PATCH 013/160] CircleCI: create Windows installers --- .circleci/config.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4174ba1ef..99b987f86 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -59,6 +59,15 @@ jobs: - run: name: Build tests command: cd build && make tests + - run: + name: Build installer + command: | + cd build + make package + cp ./lmms-*.exe /tmp/artifacts/ + - store_artifacts: + path: /tmp/artifacts/ + destination: / - *ccache_stats - *save_cache mingw64: @@ -79,6 +88,15 @@ jobs: - run: name: Build tests command: cd build && make tests + - run: + name: Build installer + command: | + cd build + make package + cp ./lmms-*.exe /tmp/artifacts/ + - store_artifacts: + path: /tmp/artifacts/ + destination: / - *ccache_stats - *save_cache linux.gcc: From 7a23ee8ad1b194bc218f3be921ce16b12dcf461a Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 26 Nov 2018 12:53:34 +0900 Subject: [PATCH 014/160] AppVeyor: create Windows installers --- .appveyor.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index 3b632d1f3..7ca7d58c4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -19,5 +19,9 @@ build_script: - cmake -DUSE_COMPILE_CACHE=ON -DCACHE_TOOL=%APPVEYOR_BUILD_FOLDER%/clcache.4.1.0/clcache-4.1.0/clcache.exe -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_PREFIX_PATH=c:/Qt/5.12/msvc2017%QT_SUFFIX%;c:/tools/vcpkg/installed/%PLATFORM%-windows -DCMAKE_GENERATOR_PLATFORM="%CMAKE_PLATFORM%" .. - cmake --build . -- /maxcpucount:4 - cmake --build . --target tests + - cmake --build . --target package +artifacts: + - path: 'build\lmms-*.exe' + name: Installer cache: - c:/tools/vcpkg/installed From 72bdb23531c22d0f8c6fc22813ece640f3df03e4 Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Wed, 21 Aug 2019 23:27:58 +0300 Subject: [PATCH 015/160] CMake: Provide API for install-time variables with generator expression --- cmake/modules/CreateTempFile.cmake | 18 +++++++++++++++++ cmake/modules/DefineInstallVar.cmake | 30 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 cmake/modules/CreateTempFile.cmake create mode 100644 cmake/modules/DefineInstallVar.cmake diff --git a/cmake/modules/CreateTempFile.cmake b/cmake/modules/CreateTempFile.cmake new file mode 100644 index 000000000..ceefa4928 --- /dev/null +++ b/cmake/modules/CreateTempFile.cmake @@ -0,0 +1,18 @@ +function(CreateTempFilePath) + set(options) + set(oneValueArgs OUTPUT_VAR TAG) + set(multiValueArgs CONTENT) + cmake_parse_arguments(TEMP "${options}" "${oneValueArgs}" + "${multiValueArgs}" ${ARGN} ) + + # Use hash to create a unique identifier + # for this file. + string(SHA1 hashed_content "${TEMP_CONTENT}") + + set(file_name "${CMAKE_BINARY_DIR}/${TEMP_TAG}_${hashed_content}") + + file(GENERATE OUTPUT "${file_name}" + CONTENT "${TEMP_CONTENT}") + + set(${TEMP_OUTPUT_VAR} "${file_name}" PARENT_SCOPE) +endfunction() diff --git a/cmake/modules/DefineInstallVar.cmake b/cmake/modules/DefineInstallVar.cmake new file mode 100644 index 000000000..36f221410 --- /dev/null +++ b/cmake/modules/DefineInstallVar.cmake @@ -0,0 +1,30 @@ +# This functions forwards a variable to +# the install stage. +# Parameters: +# CONTENT: Variable content. +# NAME: Variable name. +# Options: +# GENERATOR_EXPRESSION: Support generator expression for CONTENT. +function(DEFINE_INSTALL_VAR) + set(options GENERATOR_EXPRESSION) + set(oneValueArgs NAME ) + set(multiValueArgs CONTENT) + cmake_parse_arguments(VAR "${options}" "${oneValueArgs}" + "${multiValueArgs}" ${ARGN} ) + + # install(CODE) does not support generator expression in ver<3.14 + if(VAR_GENERATOR_EXPRESSION AND ${CMAKE_VERSION} VERSION_LESS "3.14.0") + if(MSVC) + message(FATAL_ERROR "Installing is not supported with msvc and cmake<3.14") + endif() + + include(CreateTempFile) + CreateTempFilePath(OUTPUT_VAR file_path TAG "${VAR_NAME}" CONTENT "${VAR_CONTENT}") + install(CODE "file(READ \"${file_path}\" \"${VAR_NAME}\")") + else() + if(VAR_GENERATOR_EXPRESSION) + cmake_policy(SET CMP0087 NEW) + endif() + install(CODE "set(\"${VAR_NAME}\" \"${VAR_CONTENT}\")") + endif() +endfunction() From 13da2b957608fd1ae38d59c38280127ddfd007cd Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Mon, 12 Aug 2019 12:17:12 +0300 Subject: [PATCH 016/160] CMake: extract dll installation code into a portable function --- CMakeLists.txt | 2 + cmake/install/CMakeLists.txt | 114 +++--------------- cmake/modules/InstallTargetDependencies.cmake | 89 ++++++++++++++ 3 files changed, 106 insertions(+), 99 deletions(-) create mode 100644 cmake/modules/InstallTargetDependencies.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 590a20c98..36e35bf4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.3) PROJECT(lmms) SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules" ${CMAKE_MODULE_PATH}) +SET(LMMS_BINARY_DIR ${CMAKE_BINARY_DIR}) +SET(LMMS_SOURCE_DIR ${CMAKE_SOURCE_DIR}) IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0005 NEW) diff --git a/cmake/install/CMakeLists.txt b/cmake/install/CMakeLists.txt index 3e37fa00e..a3a81beeb 100644 --- a/cmake/install/CMakeLists.txt +++ b/cmake/install/CMakeLists.txt @@ -1,95 +1,17 @@ - -FUNCTION(GET_COMPILER_SEARCH_DIR VAR) - SET(results "") - IF(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang|AppleClang)") - EXECUTE_PROCESS( - COMMAND ${CMAKE_CXX_COMPILER} --print-search-dirs - OUTPUT_VARIABLE out - ) - STRING(REPLACE "\n" ";" out "${out}") - FOREACH(line ${out}) - IF(line MATCHES "^.+:") - STRING(REPLACE " " ";" line "${line}") - LIST(GET line 1 paths) - # Remove "=" prefix - STRING(REGEX REPLACE "^=" "" paths "${paths}") - STRING(REPLACE ":" ";" paths "${paths}") - FOREACH(path ${paths}) - LIST(APPEND results ${path}) - ENDFOREACH() - ENDIF() - ENDFOREACH() - ENDIF() - - SET(paths "") - FOREACH(result ${results}) - GET_FILENAME_COMPONENT(result ${result} REALPATH) - IF(IS_DIRECTORY "${result}") - LIST(APPEND paths ${result}) - ENDIF() - ENDFOREACH() - LIST(REMOVE_DUPLICATES paths) - - SET(${VAR} ${paths} PARENT_SCOPE) -ENDFUNCTION() - SET(PLUGIN_FILES "") IF(LMMS_BUILD_WIN32) INSTALL(FILES $ DESTINATION platforms) ENDIF() IF(LMMS_BUILD_WIN32 OR LMMS_INSTALL_DEPENDENCIES) + include(InstallTargetDependencies) + # Collect directories to search for DLLs GET_FILENAME_COMPONENT(QTBIN_DIR "${QT_QMAKE_EXECUTABLE}" PATH) set(LIB_DIRS "${QTBIN_DIR}") GET_PROPERTY(PLUGINS_BUILT GLOBAL PROPERTY PLUGINS_BUILT) - foreach(target lmms ${PLUGINS_BUILT}) - get_target_property(target_libs ${target} LINK_LIBRARIES) - - foreach(lib ${target_libs}) - if(TARGET ${lib} OR NOT IS_ABSOLUTE ${lib}) - continue() - endif() - - get_filename_component(lib_dir ${lib} PATH) - list(APPEND LIB_DIRS ${lib_dir}) - if(lib MATCHES ".(lib|dll.a)$") - if(IS_DIRECTORY ${lib_dir}/../bin) - list(APPEND LIB_DIRS ${lib_dir}/../bin) - endif() - if(IS_DIRECTORY ${lib_dir}/bin) - list(APPEND LIB_DIRS ${lib_dir}/bin) - endif() - endif() - endforeach() - endforeach() - - GET_COMPILER_SEARCH_DIR(COMPILER_SEARCH_DIRS) - LIST(APPEND LIB_DIRS ${COMPILER_SEARCH_DIRS}) - - LIST(REMOVE_DUPLICATES LIB_DIRS) - - # Collect plugin files to inspect - FOREACH(PLUGIN ${PLUGINS_BUILT}) - LIST(APPEND DEPLOY_TARGETS "$") - ENDFOREACH() - # Create the list of files using file(GENERATE) - SET(DEPLOY_LIST_FILE "${CMAKE_CURRENT_BINARY_DIR}/filelist.txt") - FILE(GENERATE OUTPUT "${DEPLOY_LIST_FILE}" CONTENT "${DEPLOY_TARGETS}") - - IF(LMMS_BUILD_LINUX) - FILE(DOWNLOAD "https://raw.githubusercontent.com/AppImage/AppImages/master/excludelist" - "${CMAKE_BINARY_DIR}/excludelist") - SET(additional_args INCLUDE_SYSTEM IGNORE_LIBS_FILE ${CMAKE_BINARY_DIR}/excludelist) - ELSEIF(LMMS_BUILD_WIN32) - SET(additional_args IGNORE_CASE IGNORE_LIBS_FILE ${CMAKE_CURRENT_LIST_DIR}/excludelist-win) - IF(CMAKE_CROSSCOMPILING) - SET(additional_args "${additional_args}" GP_TOOL objdump) - ENDIF() - ENDIF() - IF(LMMS_BUILD_WIN32) SET(LMMS_DEP_DESTINATION ${BIN_DIR}) SET(PLUGIN_DEP_DESTINATION ${BIN_DIR}) @@ -98,25 +20,19 @@ IF(LMMS_BUILD_WIN32 OR LMMS_INSTALL_DEPENDENCIES) SET(PLUGIN_DEP_DESTINATION ${LIB_DIR}) ENDIF() - INSTALL(CODE " - INCLUDE(\"${CMAKE_SOURCE_DIR}/cmake/modules/InstallDependencies.cmake\") - # Install dependencies of lmms - INSTALL_DEPENDENCIES( - FILES ${BIN_DIR}/lmms${CMAKE_EXECUTABLE_SUFFIX} - DESTINATION ${LMMS_DEP_DESTINATION} - LIB_DIRS ${LIB_DIRS} - ${additional_args} - ) - # Install dependencies of plugins - FILE(READ \"${DEPLOY_LIST_FILE}\" DEPLOY_FILES) - INSTALL_DEPENDENCIES( - FILES \"\${DEPLOY_FILES}\" - DESTINATION \"${PLUGIN_DEP_DESTINATION}\" - LIB_DIRS ${LIB_DIRS} \"${BIN_DIR}\" \"${PLUGIN_DIR}\" - SEARCH_PATHS \"${BIN_DIR}\" \"${PLUGIN_DIR}\" - ${additional_args} - ) - ") + INSTALL_TARGET_DEPENDENCIES( + NAME "main_binary" + TARGETS lmms + DESTINATION "${LMMS_DEP_DESTINATION}" + LIB_DIRS ${LIB_DIRS} + ) + + INSTALL_TARGET_DEPENDENCIES( + NAME "plugins" + TARGETS ${PLUGINS_BUILT} + DESTINATION ${PLUGIN_DEP_DESTINATION} + LIB_DIRS ${LIB_DIRS} "${PLUGIN_DIR}" + ) ENDIF() IF(LMMS_BUILD_APPLE) diff --git a/cmake/modules/InstallTargetDependencies.cmake b/cmake/modules/InstallTargetDependencies.cmake new file mode 100644 index 000000000..9665a0b87 --- /dev/null +++ b/cmake/modules/InstallTargetDependencies.cmake @@ -0,0 +1,89 @@ +include(DefineInstallVar) + +SET(DEFAULT_SEARCH_DIRECTORIES "${BIN_DIR}" "${LIB_DIR}" "${CMAKE_FIND_ROOT_PATH}" "${CMAKE_PREFIX_PATH}") +SET(DEFAULT_SEARCH_SUFFIXES "bin" "lib" "../bin") + +# Like INSTALL_DEPENDENCIES but can be called from regular cmake code +# (instead of install(CODE)), takes targets instead of files, +# takes care of configuring search paths, and other platform-specific tweaks. +# Arguments: +# TARGETS: list of cmake targets to install. +# NAME: unique string for this install. +# DESTINATION: directory path to install the binaries to. +# LIB_DIRS: list of paths for looking up dependencies. +# LIB_DIRS_SUFFIXES: list of possible suffixes for LIB_DIRS entries. +# NO_DEFAULT_PATHS: supply this value to avoid adding DEFAULT_SEARCH_DIRECTORIES +# to LIB_DIRS and DEFAULT_SEARCH_SUFFIXES to LIB_DIRS_SUFFIXES. +FUNCTION(INSTALL_TARGET_DEPENDENCIES) + set(options NO_DEFAULT_PATHS) + set(oneValueArgs NAME) + set(multiValueArgs TARGETS DESTINATION LIB_DIRS_SUFFIXES LIB_DIRS) + cmake_parse_arguments(DEPS "${options}" "${oneValueArgs}" + "${multiValueArgs}" ${ARGN} ) + + if(NOT DEPS_LIB_DIRS) + set(DEPS_LIB_DIRS "") + endif() + + # Set default values. + if(NOT DEPS_NO_DEFAULT_PATHS) + list(APPEND DEPS_LIB_DIRS ${DEFAULT_SEARCH_DIRECTORIES}) + set(DEPS_LIB_DIRS_SUFFIXES "${DEPS_LIB_DIRS_SUFFIXES}" ${DEFAULT_SEARCH_SUFFIXES}) + endif() + + FOREACH(TARGET ${DEPS_TARGETS}) + IF(NOT TARGET ${TARGET}) + message(FATAL_ERROR "Not a target: ${TARGET}") + ENDIF() + + # Collect target output files. + LIST(APPEND DEPLOY_TARGETS "$") + + # Collect target link directories + get_target_property(target_libs ${TARGET} LINK_LIBRARIES) + + foreach(lib ${target_libs}) + if(TARGET ${lib} OR NOT IS_ABSOLUTE ${lib}) + continue() + endif() + + get_filename_component(lib_dir ${lib} PATH) + list(APPEND DEPS_LIB_DIRS ${lib_dir}) + endforeach() + ENDFOREACH() + + LIST(APPEND DEPS_LIB_DIRS ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES}) + + FOREACH(LIB_PATH ${DEPS_LIB_DIRS}) + FOREACH(suffix ${DEPS_LIB_DIRS_SUFFIXES}) + list(APPEND DEPS_LIB_DIRS "${LIB_PATH}/${suffix}") + ENDFOREACH() + ENDFOREACH() + + DEFINE_INSTALL_VAR(NAME "DEPLOY_FILES" CONTENT "${DEPLOY_TARGETS}" GENERATOR_EXPRESSION) + + LIST(REMOVE_DUPLICATES DEPS_LIB_DIRS) + + IF(LMMS_BUILD_LINUX) + FILE(DOWNLOAD "https://raw.githubusercontent.com/AppImage/AppImages/master/excludelist" + "${CMAKE_BINARY_DIR}/excludelist") + SET(additional_args INCLUDE_SYSTEM IGNORE_LIBS_FILE ${CMAKE_BINARY_DIR}/excludelist) + ELSEIF(LMMS_BUILD_WIN32) + SET(additional_args IGNORE_CASE IGNORE_LIBS_FILE "${LMMS_SOURCE_DIR}/cmake/install/excludelist-win") + IF(CMAKE_CROSSCOMPILING) + SET(additional_args "${additional_args}" GP_TOOL objdump) + ENDIF() + ENDIF() + + INSTALL(CODE " + INCLUDE(\"${LMMS_SOURCE_DIR}/cmake/modules/InstallDependencies.cmake\") + + INSTALL_DEPENDENCIES( + FILES \"\${DEPLOY_FILES}\" + DESTINATION \"${DEPS_DESTINATION}\" + LIB_DIRS \"${DEPS_LIB_DIRS}\" + SEARCH_PATHS \"${DEPS_SEARCH_PATHS}\" + ${additional_args} + ) + ") +ENDFUNCTION() From 84d1091100d60708ba4d3def92c0b9fd17112d74 Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Thu, 5 Sep 2019 20:32:17 -0400 Subject: [PATCH 017/160] Rewrite the setup dialog (#3820) --- include/ConfigManager.h | 215 +-- include/SetupDialog.h | 218 ++- plugins/MidiImport/MidiImport.cpp | 4 +- plugins/sf2_player/sf2_player.cpp | 2 +- src/core/ConfigManager.cpp | 354 ++--- src/core/audio/AudioJack.cpp | 4 +- src/core/audio/AudioOss.cpp | 4 +- src/core/audio/AudioPortAudio.cpp | 6 +- src/core/audio/AudioPulseAudio.cpp | 4 +- src/core/audio/AudioSdl.cpp | 2 +- src/core/audio/AudioSndio.cpp | 4 +- src/core/audio/AudioSoundIo.cpp | 4 +- src/gui/AudioDeviceSetupWidget.cpp | 6 +- src/gui/GuiApplication.cpp | 4 +- src/gui/MainWindow.cpp | 14 +- src/gui/MidiSetupWidget.cpp | 24 +- src/gui/SetupDialog.cpp | 2180 ++++++++++++++-------------- 17 files changed, 1506 insertions(+), 1543 deletions(-) diff --git a/include/ConfigManager.h b/include/ConfigManager.h index dc5b9f485..556c455a0 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -36,6 +36,7 @@ #include "lmms_export.h" + class LmmsCore; @@ -57,61 +58,22 @@ class LMMS_EXPORT ConfigManager : public QObject public: static inline ConfigManager * inst() { - if( s_instanceOfMe == NULL ) + if(s_instanceOfMe == NULL ) { s_instanceOfMe = new ConfigManager(); } return s_instanceOfMe; } - const QString & dataDir() const - { - return m_dataDir; - } const QString & workingDir() const { return m_workingDir; } - QString userProjectsDir() const + const QString & dataDir() const { - return workingDir() + PROJECTS_PATH; - } - - QString userTemplateDir() const - { - return workingDir() + TEMPLATE_PATH; - } - - QString userPresetsDir() const - { - return workingDir() + PRESETS_PATH; - } - - QString userSamplesDir() const - { - return workingDir() + SAMPLES_PATH; - } - - QString userGigDir() const - { - return workingDir() + GIG_PATH; - } - - QString userSf2Dir() const - { - return workingDir() + SF2_PATH; - } - - QString userLadspaDir() const - { - return workingDir() + LADSPA_PATH; - } - - QString userVstDir() const - { - return m_vstDir; + return m_dataDir; } QString factoryProjectsDir() const @@ -134,37 +96,27 @@ public: return dataDir() + SAMPLES_PATH; } - QString defaultVersion() const; - QString defaultArtworkDir() const + QString userProjectsDir() const { - return m_dataDir + DEFAULT_THEME_PATH; + return workingDir() + PROJECTS_PATH; } - QString artworkDir() const + QString userTemplateDir() const { - return m_artworkDir; + return workingDir() + TEMPLATE_PATH; } - QString trackIconsDir() const + QString userPresetsDir() const { - return m_dataDir + TRACK_ICON_PATH; + return workingDir() + PRESETS_PATH; } - QString localeDir() const + QString userSamplesDir() const { - return m_dataDir + LOCALE_PATH; + return workingDir() + SAMPLES_PATH; } - const QString & gigDir() const - { - return m_gigDir; - } - - const QString & sf2Dir() const - { - return m_sf2Dir; - } const QString & vstDir() const { @@ -173,18 +125,20 @@ public: const QString & ladspaDir() const { - return m_ladDir; + return m_ladspaDir; } - const QString recoveryFile() const + const QString & sf2Dir() const { - return m_workingDir + "recover.mmp"; + return m_sf2Dir; } - - const QString & version() const + +#ifdef LMMS_HAVE_FLUIDSYNTH + const QString & sf2File() const { - return m_version; + return m_sf2File; } +#endif #ifdef LMMS_HAVE_STK const QString & stkDir() const @@ -193,16 +147,55 @@ public: } #endif -#ifdef LMMS_HAVE_FLUIDSYNTH - const QString & defaultSoundfont() const + const QString & gigDir() const { - return m_defaultSoundfont; + return m_gigDir; } -#endif - const QString & backgroundArtwork() const + + QString userVstDir() const { - return m_backgroundArtwork; + return m_vstDir; + } + + QString userLadspaDir() const + { + return workingDir() + LADSPA_PATH; + } + + QString userSf2Dir() const + { + return workingDir() + SF2_PATH; + } + + QString userGigDir() const + { + return workingDir() + GIG_PATH; + } + + QString defaultThemeDir() const + { + return m_dataDir + DEFAULT_THEME_PATH; + } + + QString themeDir() const + { + return m_themeDir; + } + + const QString & backgroundPicFile() const + { + return m_backgroundPicFile; + } + + QString trackIconsDir() const + { + return m_dataDir + TRACK_ICON_PATH; + } + + const QString recoveryFile() const + { + return m_workingDir + "recover.mmp"; } inline const QStringList & recentlyOpenedProjects() const @@ -210,39 +203,51 @@ public: return m_recentlyOpenedProjects; } + QString localeDir() const + { + return m_dataDir + LOCALE_PATH; + } + + const QString & version() const + { + return m_version; + } + + QString defaultVersion() const; + + static QStringList availabeVstEmbedMethods(); QString vstEmbedMethod() const; - // returns true if the working dir (e.g. ~/lmms) exists on disk + // Returns true if the working dir (e.g. ~/lmms) exists on disk. bool hasWorkingDir() const; - void addRecentlyOpenedProject( const QString & _file ); + void addRecentlyOpenedProject(const QString & _file); - const QString & value( const QString & cls, - const QString & attribute ) const; - const QString & value( const QString & cls, + const QString & value(const QString & cls, + const QString & attribute) const; + const QString & value(const QString & cls, const QString & attribute, - const QString & defaultVal ) const; - void setValue( const QString & cls, const QString & attribute, - const QString & value ); - void deleteValue( const QString & cls, const QString & attribute); + const QString & defaultVal) const; + void setValue(const QString & cls, const QString & attribute, + const QString & value); + void deleteValue(const QString & cls, const QString & attribute); - void loadConfigFile( const QString & configFile = "" ); + void loadConfigFile(const QString & configFile = ""); void saveConfigFile(); - void setWorkingDir( const QString & _wd ); - void setVSTDir( const QString & _vd ); - void setArtworkDir( const QString & _ad ); - void setLADSPADir( const QString & _fd ); - void setVersion( const QString & _cv ); - void setSTKDir( const QString & _fd ); - void setDefaultSoundfont( const QString & _sf ); - void setBackgroundArtwork( const QString & _ba ); - void setGIGDir( const QString & gd ); - void setSF2Dir( const QString & sfd ); + void setWorkingDir(const QString & workingDir); + void setVSTDir(const QString & vstDir); + void setLADSPADir(const QString & ladspaDir); + void setSF2Dir(const QString & sf2Dir); + void setSF2File(const QString & sf2File); + void setSTKDir(const QString & stkDir); + void setGIGDir(const QString & gigDir); + void setThemeDir(const QString & themeDir); + void setBackgroundPicFile(const QString & backgroundPicFile); - // creates the working directory & subdirectories on disk. + // Creates the working directory & subdirectories on disk. void createWorkingDir(); signals: @@ -252,29 +257,29 @@ private: static ConfigManager * s_instanceOfMe; ConfigManager(); - ConfigManager( const ConfigManager & _c ); + ConfigManager(const ConfigManager & _c); ~ConfigManager(); void upgrade_1_1_90(); void upgrade_1_1_91(); void upgrade(); - QString m_lmmsRcFile; QString m_workingDir; QString m_dataDir; - QString m_artworkDir; QString m_vstDir; - QString m_ladDir; - QString m_gigDir; + QString m_ladspaDir; QString m_sf2Dir; - QString m_version; +#ifdef LMMS_HAVE_FLUIDSYNTH + QString m_sf2File; +#endif #ifdef LMMS_HAVE_STK QString m_stkDir; #endif -#ifdef LMMS_HAVE_FLUIDSYNTH - QString m_defaultSoundfont; -#endif - QString m_backgroundArtwork; + QString m_gigDir; + QString m_themeDir; + QString m_backgroundPicFile; + QString m_lmmsRcFile; + QString m_version; QStringList m_recentlyOpenedProjects; typedef QVector > stringPairVector; @@ -283,7 +288,5 @@ private: friend class LmmsCore; - -} ; - +}; #endif diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 272ba7b09..4a2e5c025 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -1,4 +1,3 @@ - /* * SetupDialog.h - dialog for setting up LMMS * @@ -23,20 +22,20 @@ * */ + #ifndef SETUP_DIALOG_H #define SETUP_DIALOG_H #include #include +#include "AudioDevice.h" +#include "AudioDeviceSetupWidget.h" #include "LedCheckbox.h" #include "lmmsconfig.h" -#include "AudioDevice.h" #include "MidiClient.h" #include "MidiSetupWidget.h" -#include "AudioDeviceSetupWidget.h" - class QComboBox; class QLabel; @@ -48,17 +47,18 @@ class TabBar; class SetupDialog : public QDialog { Q_OBJECT + public: enum ConfigTabs { GeneralSettings, - PathSettings, PerformanceSettings, AudioSettings, - MidiSettings - } ; + MidiSettings, + PathsSettings + }; - SetupDialog( ConfigTabs _tab_to_open = GeneralSettings ); + SetupDialog(ConfigTabs tab_to_open = GeneralSettings); virtual ~SetupDialog(); @@ -67,148 +67,136 @@ protected slots: private slots: - // general settings widget - void setBufferSize( int _value ); - void resetBufSize(); + // General settings widget. + void toggleDisplaydBFS(bool enabled); + void toggleTooltips(bool enabled); + void toggleDisplayWaveform(bool enabled); + void toggleNoteLabels(bool enabled); + void toggleCompactTrackButtons(bool enabled); + void toggleOneInstrumentTrackWindow(bool enabled); + void toggleMMPZ(bool enabled); + void toggleDisableBackup(bool enabled); + void toggleOpenLastProject(bool enabled); + void setLanguage(int lang); - // path settings widget - void setWorkingDir( const QString & _wd ); - void setVSTDir( const QString & _vd ); - void setGIGDir( const QString & _gd ); - void setSF2Dir( const QString & _sfd ); - void setArtworkDir( const QString & _ad ); - void setLADSPADir( const QString & _ld ); - void setSTKDir( const QString & _sd ); - void setDefaultSoundfont( const QString & _sf ); - void setBackgroundArtwork( const QString & _ba ); - - // performance settings widget - void setAutoSaveInterval( int time ); + // Performance settings widget. + void setAutoSaveInterval(int time); void resetAutoSave(); - - // audio settings widget - void audioInterfaceChanged( const QString & _driver ); - - // MIDI settings widget - void midiInterfaceChanged( const QString & _driver ); - - - void toggleToolTips( bool _enabled ); - void toggleWarnAfterSetup( bool _enabled ); - void toggleDisplaydBFS( bool _enabled ); - void toggleMMPZ( bool _enabled ); - void toggleDisableBackup( bool _enabled ); - void toggleOpenLastProject( bool _enabled ); - void toggleHQAudioDev( bool _enabled ); - - void openWorkingDir(); - void openVSTDir(); - void openGIGDir(); - void openSF2Dir(); - void openArtworkDir(); - void openLADSPADir(); - void openSTKDir(); - void openDefaultSoundfont(); - void openBackgroundArtwork(); - - void toggleSmoothScroll( bool _enabled ); - void toggleAutoSave( bool _enabled ); - void toggleRunningAutoSave( bool _enabled ); - void toggleOneInstrumentTrackWindow( bool _enabled ); - void toggleCompactTrackButtons( bool _enabled ); - void toggleSyncVSTPlugins( bool _enabled ); - void toggleAnimateAFP( bool _enabled ); - void toggleNoteLabels( bool en ); - void toggleDisplayWaveform( bool en ); - void toggleDisableAutoquit( bool en ); - + void toggleAutoSave(bool enabled); + void toggleRunningAutoSave(bool enabled); + void toggleSmoothScroll(bool enabled); + void toggleAnimateAFP(bool enabled); + void toggleSyncVSTPlugins(bool enabled); void vstEmbedMethodChanged(); - void toggleVSTAlwaysOnTop( bool en ); + void toggleVSTAlwaysOnTop(bool en); + void toggleDisableAutoQuit(bool enabled); - void setLanguage( int lang ); + // Audio settings widget. + void audioInterfaceChanged(const QString & driver); + void toggleHQAudioDev(bool enabled); + void setBufferSize(int value); + void resetBufferSize(); + // MIDI settings widget. + void midiInterfaceChanged(const QString & driver); + + // Paths settings widget. + void openWorkingDir(); + void setWorkingDir(const QString & workingDir); + void openVSTDir(); + void setVSTDir(const QString & vstDir); + void openLADSPADir(); + void setLADSPADir(const QString & ladspaDir); + void openSF2Dir(); + void setSF2Dir(const QString & sf2Dir); + void openSF2File(); + void setSF2File(const QString & sf2File); + void openGIGDir(); + void setGIGDir(const QString & gigDir); + void openThemeDir(); + void setThemeDir(const QString & themeDir); + void openBackgroundPicFile(); + void setBackgroundPicFile(const QString & backgroundPicFile); + + void showRestartWarning(); private: TabBar * m_tabBar; - QSlider * m_bufSizeSlider; - QLabel * m_bufSizeLbl; - int m_bufferSize; - - bool m_toolTips; - bool m_warnAfterSetup; + // General settings widgets. bool m_displaydBFS; + bool m_tooltips; + bool m_displayWaveform; + bool m_printNoteLabels; + bool m_compactTrackButtons; + bool m_oneInstrumentTrackWindow; bool m_MMPZ; bool m_disableBackup; bool m_openLastProject; - bool m_NaNHandler; - bool m_hqAudioDev; QString m_lang; QStringList m_languages; - - QLineEdit * m_wdLineEdit; - QLineEdit * m_vdLineEdit; - QLineEdit * m_adLineEdit; - QLineEdit * m_ladLineEdit; - QLineEdit * m_gigLineEdit; - QLineEdit * m_sf2LineEdit; -#ifdef LMMS_HAVE_FLUIDSYNTH - QLineEdit * m_sfLineEdit; -#endif -#ifdef LMMS_HAVE_STK - QLineEdit * m_stkLineEdit; -#endif - QLineEdit * m_baLineEdit; - - QString m_workingDir; - QString m_vstDir; - QString m_artworkDir; - QString m_ladDir; - QString m_gigDir; - QString m_sf2Dir; -#ifdef LMMS_HAVE_FLUIDSYNTH - QString m_defaultSoundfont; -#endif -#ifdef LMMS_HAVE_STK - QString m_stkDir; -#endif - QString m_backgroundArtwork; - - bool m_smoothScroll; + // Performance settings widgets. + int m_saveInterval; bool m_enableAutoSave; bool m_enableRunningAutoSave; - int m_saveInterval; QSlider * m_saveIntervalSlider; QLabel * m_saveIntervalLbl; LedCheckBox * m_autoSave; LedCheckBox * m_runningAutoSave; - - bool m_oneInstrumentTrackWindow; - bool m_compactTrackButtons; - bool m_syncVSTPlugins; + bool m_smoothScroll; bool m_animateAFP; - bool m_printNoteLabels; - bool m_displayWaveform; + QLabel * m_vstEmbedLbl; + QComboBox* m_vstEmbedComboBox; + QString m_vstEmbedMethod; + LedCheckBox * m_vstAlwaysOnTopCheckBox; + bool m_vstAlwaysOnTop; + bool m_syncVSTPlugins; bool m_disableAutoQuit; + typedef QMap AswMap; typedef QMap MswMap; typedef QMap trMap; + // Audio settings widgets. QComboBox * m_audioInterfaces; AswMap m_audioIfaceSetupWidgets; trMap m_audioIfaceNames; + bool m_NaNHandler; + bool m_hqAudioDev; + int m_bufferSize; + QSlider * m_bufferSizeSlider; + QLabel * m_bufferSizeLbl; + // MIDI settings widgets. QComboBox * m_midiInterfaces; MswMap m_midiIfaceSetupWidgets; trMap m_midiIfaceNames; - QComboBox* m_vstEmbedComboBox; - QString m_vstEmbedMethod; - LedCheckBox * m_vstAlwaysOnTopCheckBox; - bool m_vstAlwaysOnTop; -} ; - - + // Paths settings widgets. + QString m_workingDir; + QString m_vstDir; + QString m_ladspaDir; + QString m_gigDir; + QString m_sf2Dir; +#ifdef LMMS_HAVE_FLUIDSYNTH + QString m_sf2File; +#endif + QString m_themeDir; + QString m_backgroundPicFile; + + QLineEdit * m_workingDirLineEdit; + QLineEdit * m_vstDirLineEdit; + QLineEdit * m_themeDirLineEdit; + QLineEdit * m_ladspaDirLineEdit; + QLineEdit * m_gigDirLineEdit; + QLineEdit * m_sf2DirLineEdit; +#ifdef LMMS_HAVE_FLUIDSYNTH + QLineEdit * m_sf2FileLineEdit; +#endif + QLineEdit * m_backgroundPicFileLineEdit; + + QLabel * restartWarningLbl; +}; #endif diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index e31c24508..1f2ab1f57 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -103,7 +103,7 @@ bool MidiImport::tryImport( TrackContainer* tc ) #ifdef LMMS_HAVE_FLUIDSYNTH if( gui != NULL && - ConfigManager::inst()->defaultSoundfont().isEmpty() ) + ConfigManager::inst()->sf2File().isEmpty() ) { QMessageBox::information( gui->mainWindow(), tr( "Setup incomplete" ), @@ -242,7 +242,7 @@ public: if( it_inst ) { isSF2 = true; - it_inst->loadFile( ConfigManager::inst()->defaultSoundfont() ); + it_inst->loadFile( ConfigManager::inst()->sf2File() ); it_inst->childModel( "bank" )->setValue( 0 ); it_inst->childModel( "patch" )->setValue( 0 ); } diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index cc2575d2b..99af22781 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -151,7 +151,7 @@ sf2Instrument::sf2Instrument( InstrumentTrack * _instrument_track ) : m_chorusDepth.setInitValue(settingVal); #endif - loadFile( ConfigManager::inst()->defaultSoundfont() ); + loadFile( ConfigManager::inst()->sf2File() ); updateSampleRate(); updateReverbOn(); diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index d8c783dd2..b8e8cd4ae 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -22,6 +22,7 @@ * */ + #include #include #include @@ -36,9 +37,10 @@ #include "lmmsversion.h" -static inline QString ensureTrailingSlash( const QString & s ) + +static inline QString ensureTrailingSlash(const QString & s ) { - if( ! s.isEmpty() && !s.endsWith('/') && !s.endsWith('\\') ) + if(! s.isEmpty() && !s.endsWith('/') && !s.endsWith('\\')) { return s + '/'; } @@ -50,14 +52,14 @@ ConfigManager * ConfigManager::s_instanceOfMe = NULL; ConfigManager::ConfigManager() : - m_lmmsRcFile( QDir::home().absolutePath() +"/.lmmsrc.xml" ), - m_workingDir( QStandardPaths::writableLocation( QStandardPaths::DocumentsLocation ) + "/lmms/"), - m_dataDir( "data:/" ), - m_artworkDir( defaultArtworkDir() ), - m_vstDir( m_workingDir + "vst/" ), - m_gigDir( m_workingDir + GIG_PATH ), - m_sf2Dir( m_workingDir + SF2_PATH ), - m_version( defaultVersion() ) + m_workingDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/lmms/"), + m_dataDir("data:/"), + m_vstDir(m_workingDir + "vst/"), + m_sf2Dir(m_workingDir + SF2_PATH), + m_gigDir(m_workingDir + GIG_PATH), + m_themeDir(defaultThemeDir()), + m_lmmsRcFile(QDir::home().absolutePath() +"/.lmmsrc.xml"), + m_version(defaultVersion()) { // Detect < 1.2.0 working directory as a courtesy if ( QFileInfo( QDir::home().absolutePath() + "/lmms/projects/" ).exists() ) @@ -125,19 +127,19 @@ ConfigManager::~ConfigManager() void ConfigManager::upgrade_1_1_90() { // Remove trailing " (bad latency!)" string which was once saved with PulseAudio - if( value( "mixer", "audiodev" ).startsWith( "PulseAudio (" ) ) + if(value("mixer", "audiodev").startsWith("PulseAudio (")) { setValue("mixer", "audiodev", "PulseAudio"); } // MidiAlsaRaw used to store the device info as "Device" instead of "device" - if ( value( "MidiAlsaRaw", "device" ).isNull() ) + if (value("MidiAlsaRaw", "device").isNull()) { // copy "device" = "Device" and then delete the old "Device" (further down) - QString oldDevice = value( "MidiAlsaRaw", "Device" ); + QString oldDevice = value("MidiAlsaRaw", "Device"); setValue("MidiAlsaRaw", "device", oldDevice); } - if ( !value( "MidiAlsaRaw", "device" ).isNull() ) + if (!value("MidiAlsaRaw", "device").isNull()) { // delete the old "Device" in the case that we just copied it to "device" // or if the user somehow set both the "Device" and "device" fields @@ -149,9 +151,9 @@ void ConfigManager::upgrade_1_1_90() void ConfigManager::upgrade_1_1_91() { // rename displaydbv to displaydbfs - if ( !value( "app", "displaydbv" ).isNull() ) { - setValue( "app", "displaydbfs", value( "app", "displaydbv" ) ); - deleteValue( "app", "displaydbv" ); + if (!value("app", "displaydbv").isNull()) { + setValue("app", "displaydbfs", value("app", "displaydbv")); + deleteValue("app", "displaydbv"); } } @@ -159,27 +161,27 @@ void ConfigManager::upgrade_1_1_91() void ConfigManager::upgrade() { // Skip the upgrade if versions match - if ( m_version == LMMS_VERSION ) + if (m_version == LMMS_VERSION) { return; } ProjectVersion createdWith = m_version; - if ( createdWith.setCompareType(ProjectVersion::Build) < "1.1.90" ) + if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.90") { upgrade_1_1_90(); } - if ( createdWith.setCompareType(ProjectVersion::Build) < "1.1.91" ) + if (createdWith.setCompareType(ProjectVersion::Build) < "1.1.91") { upgrade_1_1_91(); } // Don't use old themes as they break the UI (i.e. 0.4 != 1.0, etc) - if ( createdWith.setCompareType(ProjectVersion::Minor) != LMMS_VERSION ) + if (createdWith.setCompareType(ProjectVersion::Minor) != LMMS_VERSION) { - m_artworkDir = defaultArtworkDir(); + m_themeDir = defaultThemeDir(); } // Bump the version, now that we are upgraded @@ -221,107 +223,115 @@ QString ConfigManager::vstEmbedMethod() const bool ConfigManager::hasWorkingDir() const { - return QDir( m_workingDir ).exists(); + return QDir(m_workingDir).exists(); } -void ConfigManager::setWorkingDir( const QString & wd ) +void ConfigManager::setWorkingDir(const QString & workingDir) { - m_workingDir = ensureTrailingSlash( QDir::cleanPath( wd ) ); + m_workingDir = ensureTrailingSlash(QDir::cleanPath(workingDir)); } -void ConfigManager::setVSTDir( const QString & _vd ) +void ConfigManager::setVSTDir(const QString & vstDir) { - m_vstDir = ensureTrailingSlash( _vd ); + m_vstDir = ensureTrailingSlash(vstDir); } -void ConfigManager::setArtworkDir( const QString & _ad ) +void ConfigManager::setLADSPADir(const QString & ladspaDir) { - m_artworkDir = ensureTrailingSlash( _ad ); + m_ladspaDir = ladspaDir; } -void ConfigManager::setLADSPADir( const QString & _fd ) -{ - m_ladDir = _fd; -} - - - - -void ConfigManager::setSTKDir( const QString & _fd ) +void ConfigManager::setSTKDir(const QString & stkDir) { #ifdef LMMS_HAVE_STK - m_stkDir = ensureTrailingSlash( _fd ); + m_stkDir = ensureTrailingSlash(stkDir); #endif } -void ConfigManager::setDefaultSoundfont( const QString & _sf ) +void ConfigManager::setSF2Dir(const QString & sf2Dir) +{ + m_sf2Dir = sf2Dir; +} + + + + +void ConfigManager::setSF2File(const QString & sf2File) { #ifdef LMMS_HAVE_FLUIDSYNTH - m_defaultSoundfont = _sf; + m_sf2File = sf2File; #endif } -void ConfigManager::setBackgroundArtwork( const QString & _ba ) +void ConfigManager::setGIGDir(const QString & gigDir) { - m_backgroundArtwork = _ba; + m_gigDir = gigDir; } -void ConfigManager::setGIGDir(const QString &gd) + + + +void ConfigManager::setThemeDir(const QString & themeDir) { - m_gigDir = gd; + m_themeDir = ensureTrailingSlash(themeDir); } -void ConfigManager::setSF2Dir(const QString &sfd) + + + +void ConfigManager::setBackgroundPicFile(const QString & backgroundPicFile) { - m_sf2Dir = sfd; + m_backgroundPicFile = backgroundPicFile; } + + void ConfigManager::createWorkingDir() { - QDir().mkpath( m_workingDir ); + QDir().mkpath(m_workingDir); - QDir().mkpath( userProjectsDir() ); - QDir().mkpath( userTemplateDir() ); - QDir().mkpath( userSamplesDir() ); - QDir().mkpath( userPresetsDir() ); - QDir().mkpath( userGigDir() ); - QDir().mkpath( userSf2Dir() ); - QDir().mkpath( userVstDir() ); - QDir().mkpath( userLadspaDir() ); + QDir().mkpath(userProjectsDir()); + QDir().mkpath(userTemplateDir()); + QDir().mkpath(userSamplesDir()); + QDir().mkpath(userPresetsDir()); + QDir().mkpath(userGigDir()); + QDir().mkpath(userSf2Dir()); + QDir().mkpath(userVstDir()); + QDir().mkpath(userLadspaDir()); } -void ConfigManager::addRecentlyOpenedProject( const QString & file ) +void ConfigManager::addRecentlyOpenedProject(const QString & file) { - QFileInfo recentFile( file ); - if( recentFile.suffix().toLower() == "mmp" || + QFileInfo recentFile(file); + if(recentFile.suffix().toLower() == "mmp" || recentFile.suffix().toLower() == "mmpz" || - recentFile.suffix().toLower() == "mpt" ) + recentFile.suffix().toLower() == "mpt") { - m_recentlyOpenedProjects.removeAll( file ); - if( m_recentlyOpenedProjects.size() > 50 ) + m_recentlyOpenedProjects.removeAll(file); + if(m_recentlyOpenedProjects.size() > 50) { m_recentlyOpenedProjects.removeLast(); } - m_recentlyOpenedProjects.push_front( file ); + m_recentlyOpenedProjects.push_front(file); ConfigManager::inst()->saveConfigFile(); } } @@ -329,18 +339,18 @@ void ConfigManager::addRecentlyOpenedProject( const QString & file ) -const QString & ConfigManager::value( const QString & cls, - const QString & attribute ) const +const QString & ConfigManager::value(const QString & cls, + const QString & attribute) const { - if( m_settings.contains( cls ) ) + if(m_settings.contains(cls)) { - for( stringPairVector::const_iterator it = + for(stringPairVector::const_iterator it = m_settings[cls].begin(); - it != m_settings[cls].end(); ++it ) + it != m_settings[cls].end(); ++it) { - if( ( *it ).first == attribute ) + if((*it).first == attribute) { - return ( *it ).second ; + return (*it).second ; } } } @@ -350,49 +360,49 @@ const QString & ConfigManager::value( const QString & cls, -const QString & ConfigManager::value( const QString & cls, +const QString & ConfigManager::value(const QString & cls, const QString & attribute, - const QString & defaultVal ) const + const QString & defaultVal) const { - const QString & val = value( cls, attribute ); + const QString & val = value(cls, attribute); return val.isEmpty() ? defaultVal : val; } -void ConfigManager::setValue( const QString & cls, +void ConfigManager::setValue(const QString & cls, const QString & attribute, - const QString & value ) + const QString & value) { - if( m_settings.contains( cls ) ) + if(m_settings.contains(cls)) { - for( QPair& pair : m_settings[cls]) + for(QPair& pair : m_settings[cls]) { - if( pair.first == attribute ) + if(pair.first == attribute) { - if ( pair.second != value ) + if (pair.second != value) { pair.second = value; - emit valueChanged( cls, attribute, value ); + emit valueChanged(cls, attribute, value); } return; } } } // not in map yet, so we have to add it... - m_settings[cls].push_back( qMakePair( attribute, value ) ); + m_settings[cls].push_back(qMakePair(attribute, value)); } -void ConfigManager::deleteValue( const QString & cls, const QString & attribute) +void ConfigManager::deleteValue(const QString & cls, const QString & attribute) { - if( m_settings.contains( cls ) ) + if(m_settings.contains(cls)) { - for( stringPairVector::iterator it = m_settings[cls].begin(); - it != m_settings[cls].end(); ++it ) + for(stringPairVector::iterator it = m_settings[cls].begin(); + it != m_settings[cls].end(); ++it) { - if( ( *it ).first == attribute ) + if((*it).first == attribute) { m_settings[cls].erase(it); return; @@ -402,23 +412,23 @@ void ConfigManager::deleteValue( const QString & cls, const QString & attribute) } -void ConfigManager::loadConfigFile( const QString & configFile ) +void ConfigManager::loadConfigFile(const QString & configFile) { // read the XML file and create DOM tree // Allow configuration file override through --config commandline option - if ( !configFile.isEmpty() ) + if (!configFile.isEmpty()) { m_lmmsRcFile = configFile; } - QFile cfg_file( m_lmmsRcFile ); + QFile cfg_file(m_lmmsRcFile); QDomDocument dom_tree; - if( cfg_file.open( QIODevice::ReadOnly ) ) + if(cfg_file.open(QIODevice::ReadOnly)) { QString errorString; int errorLine, errorCol; - if( dom_tree.setContent( &cfg_file, false, &errorString, &errorLine, &errorCol ) ) + if(dom_tree.setContent(&cfg_file, false, &errorString, &errorLine, &errorCol)) { // get the head information from the DOM QDomElement root = dom_tree.documentElement(); @@ -426,41 +436,41 @@ void ConfigManager::loadConfigFile( const QString & configFile ) QDomNode node = root.firstChild(); // Cache the config version for upgrade() - if ( !root.attribute( "version" ).isNull() ) { - m_version = root.attribute( "version" ); + if (!root.attribute("version").isNull()) { + m_version = root.attribute("version"); } // create the settings-map out of the DOM - while( !node.isNull() ) + while(!node.isNull()) { - if( node.isElement() && - node.toElement().hasAttributes () ) + if(node.isElement() && + node.toElement().hasAttributes ()) { stringPairVector attr; QDomNamedNodeMap node_attr = node.toElement().attributes(); - for( int i = 0; i < node_attr.count(); - ++i ) + for(int i = 0; i < node_attr.count(); + ++i) { - QDomNode n = node_attr.item( i ); - if( n.isAttr() ) + QDomNode n = node_attr.item(i); + if(n.isAttr()) { - attr.push_back( qMakePair( n.toAttr().name(), - n.toAttr().value() ) ); + attr.push_back(qMakePair(n.toAttr().name(), + n.toAttr().value())); } } m_settings[node.nodeName()] = attr; } - else if( node.nodeName() == "recentfiles" ) + else if(node.nodeName() == "recentfiles") { m_recentlyOpenedProjects.clear(); QDomNode n = node.firstChild(); - while( !n.isNull() ) + while(!n.isNull()) { - if( n.isElement() && n.toElement().hasAttributes() ) + if(n.isElement() && n.toElement().hasAttributes()) { m_recentlyOpenedProjects << - n.toElement().attribute( "path" ); + n.toElement().attribute("path"); } n = n.nextSibling(); } @@ -468,45 +478,45 @@ void ConfigManager::loadConfigFile( const QString & configFile ) node = node.nextSibling(); } - if( value( "paths", "artwork" ) != "" ) + if(value("paths", "theme") != "") { - m_artworkDir = value( "paths", "artwork" ); + m_themeDir = value("paths", "theme"); #ifdef LMMS_BUILD_WIN32 // Detect a QDir/QFile hang on Windows // see issue #3417 on github - bool badPath = ( m_artworkDir == "/" || m_artworkDir == "\\" ); + bool badPath = (m_themeDir == "/" || m_themeDir == "\\"); #else bool badPath = false; #endif - if( badPath || !QDir( m_artworkDir ).exists() || - !QFile( m_artworkDir + "/style.css" ).exists() ) + if(badPath || !QDir(m_themeDir).exists() || + !QFile(m_themeDir + "/style.css").exists()) { - m_artworkDir = defaultArtworkDir(); + m_themeDir = defaultThemeDir(); } - m_artworkDir = ensureTrailingSlash(m_artworkDir); + m_themeDir = ensureTrailingSlash(m_themeDir); } - setWorkingDir( value( "paths", "workingdir" ) ); + setWorkingDir(value("paths", "workingdir")); - setGIGDir( value( "paths", "gigdir" ) == "" ? gigDir() : value( "paths", "gigdir" ) ); - setSF2Dir( value( "paths", "sf2dir" ) == "" ? sf2Dir() : value( "paths", "sf2dir" ) ); - setVSTDir( value( "paths", "vstdir" ) ); - setLADSPADir( value( "paths", "laddir" ) ); + setGIGDir(value("paths", "gigdir") == "" ? gigDir() : value("paths", "gigdir")); + setSF2Dir(value("paths", "sf2dir") == "" ? sf2Dir() : value("paths", "sf2dir")); + setVSTDir(value("paths", "vstdir")); + setLADSPADir(value("paths", "ladspadir")); #ifdef LMMS_HAVE_STK - setSTKDir( value( "paths", "stkdir" ) ); + setSTKDir(value("paths", "stkdir")); #endif #ifdef LMMS_HAVE_FLUIDSYNTH - setDefaultSoundfont( value( "paths", "defaultsf2" ) ); + setSF2File(value("paths", "defaultsf2")); #endif - setBackgroundArtwork( value( "paths", "backgroundartwork" ) ); + setBackgroundPicFile(value("paths", "backgroundtheme")); } - else if( gui ) + else if(gui) { - QMessageBox::warning( NULL, MainWindow::tr( "Configuration file" ), - MainWindow::tr( "Error while parsing configuration file at line %1:%2: %3" ). - arg( errorLine ). - arg( errorCol ). - arg( errorString ) ); + QMessageBox::warning(NULL, MainWindow::tr("Configuration file"), + MainWindow::tr("Error while parsing configuration file at line %1:%2: %3"). + arg(errorLine). + arg(errorCol). + arg(errorString)); } cfg_file.close(); } @@ -517,21 +527,21 @@ void ConfigManager::loadConfigFile( const QString & configFile ) !QDir( m_vstDir ).exists() ) { #ifdef LMMS_BUILD_WIN32 - QString programFiles = QString::fromLocal8Bit( getenv( "ProgramFiles" ) ); + QString programFiles = QString::fromLocal8Bit(getenv("ProgramFiles")); m_vstDir = programFiles + "/VstPlugins/"; #else m_vstDir = m_workingDir + "plugins/vst/"; #endif } - if( m_ladDir.isEmpty() ) + if(m_ladspaDir.isEmpty() ) { - m_ladDir = userLadspaDir(); + m_ladspaDir = userLadspaDir(); } #ifdef LMMS_HAVE_STK - if( m_stkDir.isEmpty() || m_stkDir == QDir::separator() || m_stkDir == "/" || - !QDir( m_stkDir ).exists() ) + if(m_stkDir.isEmpty() || m_stkDir == QDir::separator() || m_stkDir == "/" || + !QDir(m_stkDir).exists()) { #if defined(LMMS_BUILD_WIN32) m_stkDir = m_dataDir + "stk/rawwaves/"; @@ -557,11 +567,11 @@ void ConfigManager::loadConfigFile( const QString & configFile ) QStringList searchPaths; if(! qgetenv("LMMS_THEME_PATH").isNull()) searchPaths << qgetenv("LMMS_THEME_PATH"); - searchPaths << artworkDir() << defaultArtworkDir(); - QDir::setSearchPaths( "resources", searchPaths); + searchPaths << themeDir() << defaultThemeDir(); + QDir::setSearchPaths("resources", searchPaths); // Create any missing subdirectories in the working dir, but only if the working dir exists - if( hasWorkingDir() ) + if(hasWorkingDir()) { createWorkingDir(); } @@ -572,72 +582,72 @@ void ConfigManager::loadConfigFile( const QString & configFile ) void ConfigManager::saveConfigFile() { - setValue( "paths", "artwork", m_artworkDir ); - setValue( "paths", "workingdir", m_workingDir ); - setValue( "paths", "vstdir", m_vstDir ); - setValue( "paths", "gigdir", m_gigDir ); - setValue( "paths", "sf2dir", m_sf2Dir ); - setValue( "paths", "laddir", m_ladDir ); + setValue("paths", "theme", m_themeDir); + setValue("paths", "workingdir", m_workingDir); + setValue("paths", "vstdir", m_vstDir); + setValue("paths", "gigdir", m_gigDir); + setValue("paths", "sf2dir", m_sf2Dir); + setValue("paths", "ladspadir", m_ladspaDir); #ifdef LMMS_HAVE_STK - setValue( "paths", "stkdir", m_stkDir ); + setValue("paths", "stkdir", m_stkDir); #endif #ifdef LMMS_HAVE_FLUIDSYNTH - setValue( "paths", "defaultsf2", m_defaultSoundfont ); + setValue("paths", "defaultsf2", m_sf2File); #endif - setValue( "paths", "backgroundartwork", m_backgroundArtwork ); + setValue("paths", "backgroundtheme", m_backgroundPicFile); - QDomDocument doc( "lmms-config-file" ); + QDomDocument doc("lmms-config-file"); - QDomElement lmms_config = doc.createElement( "lmms" ); - lmms_config.setAttribute( "version", m_version ); - doc.appendChild( lmms_config ); + QDomElement lmms_config = doc.createElement("lmms"); + lmms_config.setAttribute("version", m_version); + doc.appendChild(lmms_config); - for( settingsMap::iterator it = m_settings.begin(); - it != m_settings.end(); ++it ) + for(settingsMap::iterator it = m_settings.begin(); + it != m_settings.end(); ++it) { - QDomElement n = doc.createElement( it.key() ); - for( stringPairVector::iterator it2 = ( *it ).begin(); - it2 != ( *it ).end(); ++it2 ) + QDomElement n = doc.createElement(it.key()); + for(stringPairVector::iterator it2 = (*it).begin(); + it2 != (*it).end(); ++it2) { - n.setAttribute( ( *it2 ).first, ( *it2 ).second ); + n.setAttribute((*it2).first, (*it2).second); } - lmms_config.appendChild( n ); + lmms_config.appendChild(n); } - QDomElement recent_files = doc.createElement( "recentfiles" ); + QDomElement recent_files = doc.createElement("recentfiles"); - for( QStringList::iterator it = m_recentlyOpenedProjects.begin(); - it != m_recentlyOpenedProjects.end(); ++it ) + for(QStringList::iterator it = m_recentlyOpenedProjects.begin(); + it != m_recentlyOpenedProjects.end(); ++it) { - QDomElement n = doc.createElement( "file" ); - n.setAttribute( "path", *it ); - recent_files.appendChild( n ); + QDomElement n = doc.createElement("file"); + n.setAttribute("path", *it); + recent_files.appendChild(n); } - lmms_config.appendChild( recent_files ); + lmms_config.appendChild(recent_files); - QString xml = "\n" + doc.toString( 2 ); + QString xml = "\n" + doc.toString(2); - QFile outfile( m_lmmsRcFile ); - if( !outfile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) + QFile outfile(m_lmmsRcFile); + if(!outfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { QString title, message; - title = MainWindow::tr( "Could not open file" ); - message = MainWindow::tr( "Could not open file %1 " + title = MainWindow::tr("Could not open file"); + message = MainWindow::tr("Could not open file %1 " "for writing.\nPlease make " "sure you have write " "permission to the file and " "the directory containing the " "file and try again!" - ).arg( m_lmmsRcFile ); - if( gui ) + ).arg(m_lmmsRcFile); + if(gui) { - QMessageBox::critical( NULL, title, message, + QMessageBox::critical(NULL, title, message, QMessageBox::Ok, - QMessageBox::NoButton ); + QMessageBox::NoButton); } return; } - outfile.write( xml.toUtf8() ); + outfile.write(xml.toUtf8()); outfile.close(); } diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index e149f5cbd..aebfe5e1c 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -454,7 +454,7 @@ AudioJack::setupWidget::setupWidget( QWidget * _parent ) : m_clientName = new QLineEdit( cn, this ); m_clientName->setGeometry( 10, 20, 160, 20 ); - QLabel * cn_lbl = new QLabel( tr( "CLIENT-NAME" ), this ); + QLabel * cn_lbl = new QLabel( tr( "Client name" ), this ); cn_lbl->setFont( pointSize<7>( cn_lbl->font() ) ); cn_lbl->setGeometry( 10, 40, 160, 10 ); @@ -466,7 +466,7 @@ AudioJack::setupWidget::setupWidget( QWidget * _parent ) : m_channels = new LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "CHANNELS" ) ); + m_channels->setLabel( tr( "Channels" ) ); m_channels->move( 180, 20 ); } diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index c9ad01801..bbd9a9507 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -327,7 +327,7 @@ AudioOss::setupWidget::setupWidget( QWidget * _parent ) : m_device = new QLineEdit( probeDevice(), this ); m_device->setGeometry( 10, 20, 160, 20 ); - QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); dev_lbl->setGeometry( 10, 40, 160, 10 ); @@ -339,7 +339,7 @@ AudioOss::setupWidget::setupWidget( QWidget * _parent ) : m_channels = new LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "CHANNELS" ) ); + m_channels->setLabel( tr( "Channels" ) ); m_channels->move( 180, 20 ); } diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index 61dca4a22..ad67277ab 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -412,14 +412,14 @@ AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) : m_backend = new ComboBox( this, "BACKEND" ); m_backend->setGeometry( 64, 15, 260, 20 ); - QLabel * backend_lbl = new QLabel( tr( "BACKEND" ), this ); + QLabel * backend_lbl = new QLabel( tr( "Backend" ), this ); backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) ); backend_lbl->move( 8, 18 ); m_device = new ComboBox( this, "DEVICE" ); m_device->setGeometry( 64, 35, 260, 20 ); - QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); dev_lbl->move( 8, 38 ); @@ -431,7 +431,7 @@ AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) : m_channels = new LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "CHANNELS" ) ); + m_channels->setLabel( tr( "Channels" ) ); m_channels->move( 308, 20 );*/ connect( &m_setupUtil.m_backendModel, SIGNAL( dataChanged() ), diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index 857ef981b..af14960a7 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -314,7 +314,7 @@ AudioPulseAudio::setupWidget::setupWidget( QWidget * _parent ) : m_device = new QLineEdit( AudioPulseAudio::probeDevice(), this ); m_device->setGeometry( 10, 20, 160, 20 ); - QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); dev_lbl->setGeometry( 10, 40, 160, 10 ); @@ -326,7 +326,7 @@ AudioPulseAudio::setupWidget::setupWidget( QWidget * _parent ) : m_channels = new LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "CHANNELS" ) ); + m_channels->setLabel( tr( "Channels" ) ); m_channels->move( 180, 20 ); } diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index 42adb9b33..b970ba630 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -325,7 +325,7 @@ AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : m_device = new QLineEdit( dev, this ); m_device->setGeometry( 10, 20, 160, 20 ); - QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); dev_lbl->setGeometry( 10, 40, 160, 10 ); diff --git a/src/core/audio/AudioSndio.cpp b/src/core/audio/AudioSndio.cpp index 853d734d5..24bef9246 100644 --- a/src/core/audio/AudioSndio.cpp +++ b/src/core/audio/AudioSndio.cpp @@ -193,7 +193,7 @@ AudioSndio::setupWidget::setupWidget( QWidget * _parent ) : m_device = new QLineEdit( "", this ); m_device->setGeometry( 10, 20, 160, 20 ); - QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); dev_lbl->setFont( pointSize<6>( dev_lbl->font() ) ); dev_lbl->setGeometry( 10, 40, 160, 10 ); @@ -205,7 +205,7 @@ AudioSndio::setupWidget::setupWidget( QWidget * _parent ) : m_channels = new LcdSpinBox( 1, this ); m_channels->setModel( m ); - m_channels->setLabel( tr( "CHANNELS" ) ); + m_channels->setLabel( tr( "Channels" ) ); m_channels->move( 180, 20 ); } diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp index cde14a1c5..2c3d493a6 100644 --- a/src/core/audio/AudioSoundIo.cpp +++ b/src/core/audio/AudioSoundIo.cpp @@ -426,14 +426,14 @@ AudioSoundIo::setupWidget::setupWidget( QWidget * _parent ) : m_backend = new ComboBox( this, "BACKEND" ); m_backend->setGeometry( 64, 15, 260, 20 ); - QLabel * backend_lbl = new QLabel( tr( "BACKEND" ), this ); + QLabel * backend_lbl = new QLabel( tr( "Backend" ), this ); backend_lbl->setFont( pointSize<7>( backend_lbl->font() ) ); backend_lbl->move( 8, 18 ); m_device = new ComboBox( this, "DEVICE" ); m_device->setGeometry( 64, 35, 260, 20 ); - QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + QLabel * dev_lbl = new QLabel( tr( "Device" ), this ); dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); dev_lbl->move( 8, 38 ); diff --git a/src/gui/AudioDeviceSetupWidget.cpp b/src/gui/AudioDeviceSetupWidget.cpp index 86800643e..fbec38c76 100644 --- a/src/gui/AudioDeviceSetupWidget.cpp +++ b/src/gui/AudioDeviceSetupWidget.cpp @@ -24,9 +24,9 @@ #include "AudioDeviceSetupWidget.h" -AudioDeviceSetupWidget::AudioDeviceSetupWidget( const QString & _caption, QWidget * _parent ) : - TabWidget( TabWidget::tr( "Settings for %1" ).arg(TabWidget::tr( _caption.toLatin1() ) ).toUpper(), - _parent ) + +AudioDeviceSetupWidget::AudioDeviceSetupWidget(const QString & caption, QWidget * parent) : + TabWidget(TabWidget::tr("Settings for %1").arg(TabWidget::tr(caption.toLatin1())), parent) { } diff --git a/src/gui/GuiApplication.cpp b/src/gui/GuiApplication.cpp index a7a3d1baa..fb2e3eae3 100644 --- a/src/gui/GuiApplication.cpp +++ b/src/gui/GuiApplication.cpp @@ -67,8 +67,8 @@ GuiApplication::GuiApplication() ConfigManager::inst()->createWorkingDir(); } // Init style and palette - QDir::addSearchPath("artwork", ConfigManager::inst()->artworkDir()); - QDir::addSearchPath("artwork", ConfigManager::inst()->defaultArtworkDir()); + QDir::addSearchPath("artwork", ConfigManager::inst()->themeDir()); + QDir::addSearchPath("artwork", ConfigManager::inst()->defaultThemeDir()); QDir::addSearchPath("artwork", ":/artwork"); LmmsStyle* lmmsstyle = new LmmsStyle(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 0914d1685..bdb3a7472 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -174,16 +174,16 @@ MainWindow::MainWindow() : m_workspace = new QMdiArea( splitter ); // Load background - emit initProgress(tr("Loading background artwork")); - QString bgArtwork = ConfigManager::inst()->backgroundArtwork(); - QImage bgImage; - if( !bgArtwork.isEmpty() ) + emit initProgress(tr("Loading background picture")); + QString backgroundPicFile = ConfigManager::inst()->backgroundPicFile(); + QImage backgroundPic; + if( !backgroundPicFile.isEmpty() ) { - bgImage = QImage( bgArtwork ); + backgroundPic = QImage( backgroundPicFile ); } - if( !bgImage.isNull() ) + if( !backgroundPicFile.isNull() ) { - m_workspace->setBackground( bgImage ); + m_workspace->setBackground( backgroundPic ); } else { diff --git a/src/gui/MidiSetupWidget.cpp b/src/gui/MidiSetupWidget.cpp index f3001b3af..0c34544d6 100644 --- a/src/gui/MidiSetupWidget.cpp +++ b/src/gui/MidiSetupWidget.cpp @@ -29,23 +29,22 @@ #include "ConfigManager.h" #include "gui_templates.h" -MidiSetupWidget::MidiSetupWidget( const QString & caption, const QString & configSection, - const QString & devName, QWidget * parent ) : - TabWidget( TabWidget::tr( "Settings for %1" ).arg( - tr( caption.toLatin1() ) ).toUpper(), parent ), +MidiSetupWidget::MidiSetupWidget(const QString & caption, const QString & configSection, + const QString & devName, QWidget * parent) : + TabWidget(TabWidget::tr("Settings for %1").arg(tr(caption.toLatin1())), parent), m_configSection(configSection), m_device(nullptr) { // supply devName=QString() (distinct from QString("")) - // to indicate that there is no editable DEVICE field + // 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 ); + m_device = new QLineEdit(devName, this); + m_device->setGeometry(10, 20, 160, 20); - QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); - dev_lbl->setFont( pointSize<7>( dev_lbl->font() ) ); - dev_lbl->setGeometry( 10, 40, 160, 10 ); + QLabel * dev_lbl = new QLabel(tr("Device"), this); + dev_lbl->setFont(pointSize<7>(dev_lbl->font())); + dev_lbl->setGeometry(10, 40, 160, 10); } } @@ -53,8 +52,8 @@ void MidiSetupWidget::saveSettings() { if (!m_configSection.isEmpty() && m_device) { - ConfigManager::inst()->setValue( m_configSection, "device", - m_device->text() ); + ConfigManager::inst()->setValue(m_configSection, "device", + m_device->text()); } } @@ -65,4 +64,3 @@ void MidiSetupWidget::show() parentWidget()->setVisible(visible); QWidget::setVisible(visible); } - diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 5ab9630b2..f23b5b271 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -22,6 +22,7 @@ * */ + #include #include #include @@ -30,291 +31,247 @@ #include #include +#include "debug.h" +#include "embed.h" +#include "Engine.h" +#include "FileDialog.h" +#include "gui_templates.h" +#include "MainWindow.h" +#include "Mixer.h" +#include "ProjectJournal.h" #include "SetupDialog.h" #include "TabBar.h" #include "TabButton.h" -#include "gui_templates.h" -#include "Mixer.h" -#include "MainWindow.h" -#include "ProjectJournal.h" -#include "embed.h" -#include "Engine.h" -#include "debug.h" #include "ToolTip.h" -#include "FileDialog.h" -// platform-specific audio-interface-classes +// Platform-specific audio-interface classes. #include "AudioAlsa.h" #include "AudioAlsaSetupWidget.h" +#include "AudioDummy.h" #include "AudioJack.h" #include "AudioOss.h" -#include "AudioSndio.h" #include "AudioPortAudio.h" -#include "AudioSoundIo.h" #include "AudioPulseAudio.h" #include "AudioSdl.h" -#include "AudioDummy.h" +#include "AudioSndio.h" +#include "AudioSoundIo.h" -// platform-specific midi-interface-classes +// Platform-specific midi-interface classes. #include "MidiAlsaRaw.h" #include "MidiAlsaSeq.h" +#include "MidiApple.h" +#include "MidiDummy.h" #include "MidiJack.h" #include "MidiOss.h" #include "MidiSndio.h" #include "MidiWinMM.h" -#include "MidiApple.h" -#include "MidiDummy.h" + constexpr int BUFFERSIZE_RESOLUTION = 32; -inline void labelWidget( QWidget * _w, const QString & _txt ) +inline void labelWidget(QWidget * w, const QString & txt) { - QLabel * title = new QLabel( _txt, _w ); + QLabel * title = new QLabel(txt, w); QFont f = title->font(); - f.setBold( true ); - title->setFont( pointSize<12>( f ) ); + f.setBold(true); + title->setFont(pointSize<12>(f)); - assert( dynamic_cast( _w->layout() ) != NULL ); + assert(dynamic_cast(w->layout()) != NULL); - dynamic_cast( _w->layout() )->addSpacing( 5 ); - dynamic_cast( _w->layout() )->addWidget( title ); - dynamic_cast( _w->layout() )->addSpacing( 10 ); + dynamic_cast(w->layout())->addSpacing(5); + dynamic_cast(w->layout())->addWidget(title); } -SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : - m_bufferSize( ConfigManager::inst()->value( "mixer", - "framesperaudiobuffer" ).toInt() ), - m_toolTips( !ConfigManager::inst()->value( "tooltips", - "disabled" ).toInt() ), - m_warnAfterSetup( !ConfigManager::inst()->value( "app", - "nomsgaftersetup" ).toInt() ), - m_displaydBFS( ConfigManager::inst()->value( "app", - "displaydbfs" ).toInt() ), - m_MMPZ( !ConfigManager::inst()->value( "app", "nommpz" ).toInt() ), - m_disableBackup( !ConfigManager::inst()->value( "app", - "disablebackup" ).toInt() ), - m_openLastProject( ConfigManager::inst()->value( "app", - "openlastproject" ).toInt() ), - m_NaNHandler( ConfigManager::inst()->value( "app", - "nanhandler", "1" ).toInt() ), - m_hqAudioDev( ConfigManager::inst()->value( "mixer", - "hqaudio" ).toInt() ), - m_lang( ConfigManager::inst()->value( "app", - "language" ) ), - m_workingDir( QDir::toNativeSeparators( ConfigManager::inst()->workingDir() ) ), - m_vstDir( QDir::toNativeSeparators( ConfigManager::inst()->vstDir() ) ), - m_artworkDir( QDir::toNativeSeparators( ConfigManager::inst()->artworkDir() ) ), - m_ladDir( QDir::toNativeSeparators( ConfigManager::inst()->ladspaDir() ) ), - m_gigDir( QDir::toNativeSeparators( ConfigManager::inst()->gigDir() ) ), - m_sf2Dir( QDir::toNativeSeparators( ConfigManager::inst()->sf2Dir() ) ), +SetupDialog::SetupDialog(ConfigTabs tab_to_open) : + m_displaydBFS(ConfigManager::inst()->value( + "app", "displaydbfs").toInt()), + m_tooltips(!ConfigManager::inst()->value( + "tooltips", "disabled").toInt()), + m_displayWaveform(ConfigManager::inst()->value( + "ui", "displaywaveform").toInt()), + m_printNoteLabels(ConfigManager::inst()->value( + "ui", "printnotelabels").toInt()), + m_compactTrackButtons(ConfigManager::inst()->value( + "ui", "compacttrackbuttons").toInt()), + m_oneInstrumentTrackWindow(ConfigManager::inst()->value( + "ui", "oneinstrumenttrackwindow").toInt()), + m_MMPZ(!ConfigManager::inst()->value( + "app", "nommpz").toInt()), + m_disableBackup(!ConfigManager::inst()->value( + "app", "disablebackup").toInt()), + m_openLastProject(ConfigManager::inst()->value( + "app", "openlastproject").toInt()), + m_lang(ConfigManager::inst()->value( + "app", "language")), + m_saveInterval( ConfigManager::inst()->value( + "ui", "saveinterval").toInt() < 1 ? + MainWindow::DEFAULT_SAVE_INTERVAL_MINUTES : + ConfigManager::inst()->value( + "ui", "saveinterval").toInt()), + m_enableAutoSave(ConfigManager::inst()->value( + "ui", "enableautosave", "1").toInt()), + m_enableRunningAutoSave(ConfigManager::inst()->value( + "ui", "enablerunningautosave", "0").toInt()), + m_smoothScroll(ConfigManager::inst()->value( + "ui", "smoothscroll").toInt()), + m_animateAFP(ConfigManager::inst()->value( + "ui", "animateafp", "1").toInt()), + m_vstEmbedMethod(ConfigManager::inst()->vstEmbedMethod()), + m_vstAlwaysOnTop(ConfigManager::inst()->value( + "ui", "vstalwaysontop").toInt()), + m_syncVSTPlugins(ConfigManager::inst()->value( + "ui", "syncvstplugins").toInt()), + m_disableAutoQuit(ConfigManager::inst()->value( + "ui", "disableautoquit").toInt()), + m_NaNHandler(ConfigManager::inst()->value( + "app", "nanhandler", "1").toInt()), + m_hqAudioDev(ConfigManager::inst()->value( + "mixer", "hqaudio").toInt()), + m_bufferSize(ConfigManager::inst()->value( + "mixer", "framesperaudiobuffer").toInt()), + m_workingDir(QDir::toNativeSeparators(ConfigManager::inst()->workingDir())), + m_vstDir(QDir::toNativeSeparators(ConfigManager::inst()->vstDir())), + m_ladspaDir(QDir::toNativeSeparators(ConfigManager::inst()->ladspaDir())), + m_gigDir(QDir::toNativeSeparators(ConfigManager::inst()->gigDir())), + m_sf2Dir(QDir::toNativeSeparators(ConfigManager::inst()->sf2Dir())), #ifdef LMMS_HAVE_FLUIDSYNTH - m_defaultSoundfont( QDir::toNativeSeparators( ConfigManager::inst()->defaultSoundfont() ) ), + m_sf2File(QDir::toNativeSeparators(ConfigManager::inst()->sf2File())), #endif -#ifdef LMMS_HAVE_STK - m_stkDir( QDir::toNativeSeparators( ConfigManager::inst()->stkDir() ) ), -#endif - m_backgroundArtwork( QDir::toNativeSeparators( ConfigManager::inst()->backgroundArtwork() ) ), - m_smoothScroll( ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt() ), - m_enableAutoSave( ConfigManager::inst()->value( "ui", "enableautosave", "1" ).toInt() ), - m_enableRunningAutoSave( ConfigManager::inst()->value( "ui", "enablerunningautosave", "0" ).toInt() ), - m_saveInterval( ConfigManager::inst()->value( "ui", "saveinterval" ).toInt() < 1 ? - MainWindow::DEFAULT_SAVE_INTERVAL_MINUTES : - ConfigManager::inst()->value( "ui", "saveinterval" ).toInt() ), - m_oneInstrumentTrackWindow( ConfigManager::inst()->value( "ui", - "oneinstrumenttrackwindow" ).toInt() ), - m_compactTrackButtons( ConfigManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt() ), - m_syncVSTPlugins( ConfigManager::inst()->value( "ui", - "syncvstplugins", "1" ).toInt() ), - m_animateAFP(ConfigManager::inst()->value( "ui", - "animateafp", "1" ).toInt() ), - m_printNoteLabels(ConfigManager::inst()->value( "ui", - "printnotelabels").toInt() ), - m_displayWaveform(ConfigManager::inst()->value( "ui", - "displaywaveform").toInt() ), - m_disableAutoQuit(ConfigManager::inst()->value( "ui", - "disableautoquit", "1" ).toInt() ), - m_vstEmbedMethod( ConfigManager::inst()->vstEmbedMethod() ), - m_vstAlwaysOnTop( ConfigManager::inst()->value( "ui", - "vstalwaysontop" ).toInt() ) + m_themeDir(QDir::toNativeSeparators(ConfigManager::inst()->themeDir())), + m_backgroundPicFile(QDir::toNativeSeparators(ConfigManager::inst()->backgroundPicFile())) { - setWindowIcon( embed::getIconPixmap( "setup_general" ) ); - setWindowTitle( tr( "Setup LMMS" ) ); - setModal( true ); - setFixedSize( 452, 570 ); + setWindowIcon(embed::getIconPixmap("setup_general")); + setWindowTitle(tr("Settings")); + // TODO: Equivalent to the new setWindowFlag(Qt::WindowContextHelpButtonHint, false) + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setModal(true); + setFixedSize(454, 400); - Engine::projectJournal()->setJournalling( false ); + Engine::projectJournal()->setJournalling(false); - QVBoxLayout * vlayout = new QVBoxLayout( this ); - vlayout->setSpacing( 0 ); - vlayout->setMargin( 0 ); - QWidget * settings = new QWidget( this ); - QHBoxLayout * hlayout = new QHBoxLayout( settings ); - hlayout->setSpacing( 0 ); - hlayout->setMargin( 0 ); - m_tabBar = new TabBar( settings, QBoxLayout::TopToBottom ); - m_tabBar->setExclusive( true ); - m_tabBar->setFixedWidth( 72 ); - - QWidget * ws = new QWidget( settings ); - int wsHeight = 420; -#ifdef LMMS_HAVE_STK - wsHeight += 50; -#endif -#ifdef LMMS_HAVE_FLUIDSYNTH - wsHeight += 50; -#endif - ws->setFixedSize( 360, wsHeight ); - QWidget * general = new QWidget( ws ); - general->setFixedSize( 360, 290 ); - QVBoxLayout * gen_layout = new QVBoxLayout( general ); - gen_layout->setSpacing( 0 ); - gen_layout->setMargin( 0 ); - labelWidget( general, tr( "General settings" ) ); - - TabWidget * bufsize_tw = new TabWidget( tr( "BUFFER SIZE" ), general ); - bufsize_tw->setFixedHeight( 80 ); - - m_bufSizeSlider = new QSlider( Qt::Horizontal, bufsize_tw ); - m_bufSizeSlider->setRange( 1, 128 ); - m_bufSizeSlider->setTickPosition( QSlider::TicksBelow ); - m_bufSizeSlider->setPageStep( 8 ); - m_bufSizeSlider->setTickInterval( 8 ); - m_bufSizeSlider->setGeometry( 10, 16, 340, 18 ); - m_bufSizeSlider->setValue( m_bufferSize / BUFFERSIZE_RESOLUTION ); - - connect( m_bufSizeSlider, SIGNAL( valueChanged( int ) ), this, - SLOT( setBufferSize( int ) ) ); - - m_bufSizeLbl = new QLabel( bufsize_tw ); - m_bufSizeLbl->setGeometry( 10, 40, 200, 32 ); - setBufferSize( m_bufSizeSlider->value() ); - - QPushButton * bufsize_reset_btn = new QPushButton( - embed::getIconPixmap( "reload" ), "", bufsize_tw ); - bufsize_reset_btn->setGeometry( 320, 40, 28, 28 ); - connect( bufsize_reset_btn, SIGNAL( clicked() ), this, - SLOT( resetBufSize() ) ); - ToolTip::add( bufsize_reset_btn, tr( "Reset to default value" ) ); - - TabWidget * misc_tw = new TabWidget( tr( "MISC" ), general ); + // Constants for positioning LED check boxes. const int XDelta = 10; const int YDelta = 18; - const int HeaderSize = 30; - int labelNumber = 0; - auto addLedCheckBox = [&XDelta, &YDelta, &misc_tw, &labelNumber, this]( + // Main widget. + QWidget * main_w = new QWidget(this); + + + // Vertical layout. + QVBoxLayout * vlayout = new QVBoxLayout(this); + vlayout->setSpacing(0); + vlayout->setMargin(0); + + // Horizontal layout. + QHBoxLayout * hlayout = new QHBoxLayout(main_w); + hlayout->setSpacing(0); + hlayout->setMargin(0); + + // Tab bar for the main tabs. + m_tabBar = new TabBar(main_w, QBoxLayout::TopToBottom); + m_tabBar->setExclusive(true); + m_tabBar->setFixedWidth(72); + + // Settings widget. + QWidget * settings_w = new QWidget(main_w); + settings_w->setFixedSize(360, 360); + + // General widget. + QWidget * general_w = new QWidget(settings_w); + QVBoxLayout * general_layout = new QVBoxLayout(general_w); + general_layout->setSpacing(10); + general_layout->setMargin(0); + labelWidget(general_w, tr("General")); + + + auto addLedCheckBox = [&XDelta, &YDelta, this]( const char* ledText, + TabWidget* tw, + int& counter, bool initialState, - const char* toggledSlot + const char* toggledSlot, + bool showRestartWarning ){ - LedCheckBox * checkBox = new LedCheckBox(tr(ledText), misc_tw); - labelNumber++; - checkBox->move(XDelta, YDelta*labelNumber); + LedCheckBox * checkBox = new LedCheckBox(tr(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())); + } }; - addLedCheckBox("Enable tooltips", - m_toolTips, SLOT(toggleToolTips(bool))); - addLedCheckBox("Show restart warning after changing settings", - m_warnAfterSetup, SLOT(toggleWarnAfterSetup(bool))); - addLedCheckBox("Display volume as dBFS ", - m_displaydBFS, SLOT(toggleDisplaydBFS(bool))); - addLedCheckBox("Compress project files per default", - m_MMPZ, SLOT(toggleMMPZ(bool))); - addLedCheckBox("One instrument track window mode", - m_oneInstrumentTrackWindow, - SLOT(toggleOneInstrumentTrackWindow(bool))); - addLedCheckBox("HQ-mode for output audio-device", - m_hqAudioDev, SLOT(toggleHQAudioDev(bool))); - addLedCheckBox("Compact track buttons", - m_compactTrackButtons, SLOT(toggleCompactTrackButtons(bool))); - addLedCheckBox("Sync VST plugins to host playback", - m_syncVSTPlugins, SLOT(toggleSyncVSTPlugins(bool))); - addLedCheckBox("Enable note labels in piano roll", - m_printNoteLabels, SLOT(toggleNoteLabels(bool))); - addLedCheckBox("Enable waveform display by default", - m_displayWaveform, SLOT(toggleDisplayWaveform(bool))); - addLedCheckBox("Keep effects running even without input", - m_disableAutoQuit, SLOT(toggleDisableAutoquit(bool))); - addLedCheckBox("Create backup file when saving a project", - m_disableBackup, SLOT(toggleDisableBackup(bool))); - addLedCheckBox("Reopen last project on start", - m_openLastProject, SLOT(toggleOpenLastProject(bool))); - misc_tw->setFixedHeight( YDelta*labelNumber + HeaderSize ); + int counter = 0; - // Advanced setting, hidden for now - if( false ) + // GUI tab. + TabWidget * gui_tw = new TabWidget( + tr("Graphical user interface (GUI)"), general_w); + + + addLedCheckBox("Display volume as dBFS ", gui_tw, counter, + m_displaydBFS, SLOT(toggleDisplaydBFS(bool)), true); + addLedCheckBox("Enable tooltips", gui_tw, counter, + m_tooltips, SLOT(toggleTooltips(bool)), true); + addLedCheckBox("Enable master oscilloscope by default", gui_tw, counter, + m_displayWaveform, SLOT(toggleDisplayWaveform(bool)), true); + addLedCheckBox("Enable all note labels in piano roll", gui_tw, counter, + m_printNoteLabels, SLOT(toggleNoteLabels(bool)), false); + addLedCheckBox("Enable compact track buttons", gui_tw, counter, + m_compactTrackButtons, SLOT(toggleCompactTrackButtons(bool)), true); + addLedCheckBox("Enable one instrument-track-window mode", gui_tw, counter, + m_oneInstrumentTrackWindow, SLOT(toggleOneInstrumentTrackWindow(bool)), true); + + gui_tw->setFixedHeight(YDelta + YDelta * counter); + + + counter = 0; + + // Projects tab. + TabWidget * projects_tw = new TabWidget( + tr("Projects"), general_w); + + + addLedCheckBox("Compress project files by default", projects_tw, counter, + m_MMPZ, SLOT(toggleMMPZ(bool)), true); + addLedCheckBox("Create a backup file when saving a project", projects_tw, counter, + m_disableBackup, SLOT(toggleDisableBackup(bool)), false); + addLedCheckBox("Reopen last project on startup", projects_tw, counter, + m_openLastProject, SLOT(toggleOpenLastProject(bool)), false); + + projects_tw->setFixedHeight(YDelta + YDelta * counter); + + // Language tab. + TabWidget * lang_tw = new TabWidget( + tr("Language"), general_w); + lang_tw->setFixedHeight(48); + QComboBox * changeLang = new QComboBox(lang_tw); + changeLang->move(XDelta, 20); + + QDir dir(ConfigManager::inst()->localeDir()); + QStringList fileNames = dir.entryList(QStringList("*.qm")); + for(int i = 0; i < fileNames.size(); ++i) { - LedCheckBox * useNaNHandler = new LedCheckBox( - tr( "Use built-in NaN handler" ), - misc_tw ); - useNaNHandler->setChecked( m_NaNHandler ); + // Get locale extracted by filename. + fileNames[i].truncate(fileNames[i].lastIndexOf('.')); + m_languages.append(fileNames[i]); + QString lang = QLocale(m_languages.last()).nativeLanguageName(); + changeLang->addItem(lang); } - TabWidget* embed_tw = new TabWidget( tr( "PLUGIN EMBEDDING" ), general); - embed_tw->setFixedHeight( 66 ); - m_vstEmbedComboBox = new QComboBox( embed_tw ); - m_vstEmbedComboBox->move( XDelta, YDelta ); - - QStringList embedMethods = ConfigManager::availabeVstEmbedMethods(); - m_vstEmbedComboBox->addItem( tr( "No embedding" ), "none" ); - if( embedMethods.contains("qt") ) + // If language unset, fallback to system language when available. + if(m_lang == "") { - m_vstEmbedComboBox->addItem( tr( "Embed using Qt API" ), "qt" ); - } - if( embedMethods.contains("win32") ) - { - m_vstEmbedComboBox->addItem( tr( "Embed using native Win32 API" ), "win32" ); - } - if( embedMethods.contains("xembed") ) - { - m_vstEmbedComboBox->addItem( tr( "Embed using XEmbed protocol" ), "xembed" ); - } - m_vstEmbedComboBox->setCurrentIndex( m_vstEmbedComboBox->findData( m_vstEmbedMethod ) ); - connect( m_vstEmbedComboBox, SIGNAL( currentIndexChanged( int ) ), - this, SLOT( vstEmbedMethodChanged() ) ); - - m_vstAlwaysOnTopCheckBox = new LedCheckBox( - tr( "Keep plugin windows on top when not embedded" ), - embed_tw ); - m_vstAlwaysOnTopCheckBox->move( 20, 44 ); - m_vstAlwaysOnTopCheckBox->setChecked( m_vstAlwaysOnTop ); - m_vstAlwaysOnTopCheckBox->setVisible( m_vstEmbedMethod == "none" ); - connect( m_vstAlwaysOnTopCheckBox, SIGNAL( toggled( bool ) ), - this, SLOT( toggleVSTAlwaysOnTop( bool ) ) ); - - TabWidget * lang_tw = new TabWidget( tr( "LANGUAGE" ), general ); - lang_tw->setFixedHeight( 48 ); - QComboBox * changeLang = new QComboBox( lang_tw ); - changeLang->move( XDelta, YDelta ); - - QDir dir( ConfigManager::inst()->localeDir() ); - QStringList fileNames = dir.entryList( QStringList( "*.qm" ) ); - for( int i = 0; i < fileNames.size(); ++i ) - { - // get locale extracted by filename - fileNames[i].truncate( fileNames[i].lastIndexOf( '.' ) ); - m_languages.append( fileNames[i] ); - QString lang = QLocale( m_languages.last() ).nativeLanguageName(); - changeLang->addItem( lang ); - } - connect( changeLang, SIGNAL( currentIndexChanged( int ) ), - this, SLOT( setLanguage( int ) ) ); - - //If language unset, fallback to system language when available - if( m_lang == "" ) - { - QString tmp = QLocale::system().name().left( 2 ); - if( m_languages.contains( tmp ) ) + QString tmp = QLocale::system().name().left(2); + if(m_languages.contains(tmp)) { m_lang = tmp; } @@ -324,69 +281,439 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : } } - for( int i = 0; i < changeLang->count(); ++i ) + for(int i = 0; i < changeLang->count(); ++i) { - if( m_lang == m_languages.at( i ) ) + if(m_lang == m_languages.at(i)) { - changeLang->setCurrentIndex( i ); + changeLang->setCurrentIndex(i); break; } } - gen_layout->addWidget( bufsize_tw ); - gen_layout->addSpacing( 10 ); - gen_layout->addWidget( misc_tw ); - gen_layout->addSpacing( 10 ); - gen_layout->addWidget( embed_tw ); - gen_layout->addSpacing( 10 ); - gen_layout->addWidget( lang_tw ); - gen_layout->addStretch(); + connect(changeLang, SIGNAL(currentIndexChanged(int)), + this, SLOT(setLanguage(int))); + connect(changeLang, SIGNAL(currentIndexChanged(int)), + this, SLOT(showRestartWarning())); + + + // General layout ordering. + general_layout->addWidget(gui_tw); + general_layout->addWidget(projects_tw); + general_layout->addWidget(lang_tw); + general_layout->addStretch(); - QWidget * paths = new QWidget( ws ); - int pathsHeight = 420; -#ifdef LMMS_HAVE_STK - pathsHeight += 55; + + // Performance widget. + QWidget * performance_w = new QWidget(settings_w); + QVBoxLayout * performance_layout = new QVBoxLayout(performance_w); + performance_layout->setSpacing(10); + performance_layout->setMargin(0); + labelWidget(performance_w, + tr("Performance")); + + + // Autosave tab. + TabWidget * auto_save_tw = new TabWidget( + tr("Autosave"), performance_w); + auto_save_tw->setFixedHeight(106); + + m_saveIntervalSlider = new QSlider(Qt::Horizontal, auto_save_tw); + 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))); + + QPushButton * autoSaveResetBtn = new QPushButton( + embed::getIconPixmap("reload"), "", auto_save_tw); + autoSaveResetBtn->setGeometry(320, 70, 28, 28); + connect(autoSaveResetBtn, SIGNAL(clicked()), + this, SLOT(resetAutoSave())); + + m_saveIntervalSlider->setEnabled(m_enableAutoSave); + m_runningAutoSave->setVisible(m_enableAutoSave); + + + counter = 0; + + // UI effect vs. performance tab. + TabWidget * ui_fx_tw = new TabWidget( + tr("User interface (UI) effects vs. performance"), performance_w); + + addLedCheckBox("Smooth scroll in song editor", ui_fx_tw, counter, + m_smoothScroll, SLOT(toggleSmoothScroll(bool)), false); + addLedCheckBox("Display playback cursor in AudioFileProcessor", ui_fx_tw, counter, + m_animateAFP, SLOT(toggleAnimateAFP(bool)), false); + + ui_fx_tw->setFixedHeight(YDelta + YDelta * counter); + + + counter = 0; + + // Plugins tab. + TabWidget * plugins_tw = new TabWidget( + tr("Plugins"), performance_w); + + m_vstEmbedLbl = new QLabel(plugins_tw); + m_vstEmbedLbl->move(XDelta, YDelta * ++counter); + m_vstEmbedLbl->setText(tr("VST plugins embedding:")); + + m_vstEmbedComboBox = new QComboBox(plugins_tw); + m_vstEmbedComboBox->move(XDelta, YDelta * ++counter); + + QStringList embedMethods = ConfigManager::availabeVstEmbedMethods(); + m_vstEmbedComboBox->addItem(tr("No embedding"), "none"); + if(embedMethods.contains("qt")) + { + m_vstEmbedComboBox->addItem(tr("Embed using Qt API"), "qt"); + } + if(embedMethods.contains("win32")) + { + m_vstEmbedComboBox->addItem(tr("Embed using native Win32 API"), "win32"); + } + if(embedMethods.contains("xembed")) + { + m_vstEmbedComboBox->addItem(tr("Embed using XEmbed protocol"), "xembed"); + } + m_vstEmbedComboBox->setCurrentIndex(m_vstEmbedComboBox->findData(m_vstEmbedMethod)); + connect(m_vstEmbedComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(vstEmbedMethodChanged())); + + counter += 2; + + 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("Sync VST plugins to host playback", plugins_tw, counter, + m_syncVSTPlugins, SLOT(toggleSyncVSTPlugins(bool)), false); + + addLedCheckBox("Keep effects running even without input", plugins_tw, counter, + m_disableAutoQuit, SLOT(toggleDisableAutoQuit(bool)), false); + + plugins_tw->setFixedHeight(YDelta + YDelta * counter); + + + // Performance layout ordering. + performance_layout->addWidget(auto_save_tw); + performance_layout->addWidget(ui_fx_tw); + performance_layout->addWidget(plugins_tw); + performance_layout->addStretch(); + + + + // Audio widget. + QWidget * audio_w = new QWidget(settings_w); + QVBoxLayout * audio_layout = new QVBoxLayout(audio_w); + audio_layout->setSpacing(10); + audio_layout->setMargin(0); + labelWidget(audio_w, + tr("Audio")); + + // Audio interface tab. + TabWidget * 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); + + + // Ifaces-settings-widget. + QWidget * as_w = new QWidget(audio_w); + as_w->setFixedHeight(60); + + QHBoxLayout * as_w_layout = new QHBoxLayout(as_w); + as_w_layout->setSpacing(0); + as_w_layout->setMargin(0); + +#ifdef LMMS_HAVE_JACK + m_audioIfaceSetupWidgets[AudioJack::name()] = + new AudioJack::setupWidget(as_w); #endif -#ifdef LMMS_HAVE_FLUIDSYNTH - pathsHeight += 55; + +#ifdef LMMS_HAVE_ALSA + m_audioIfaceSetupWidgets[AudioAlsa::name()] = + new AudioAlsaSetupWidget(as_w); #endif - paths->setFixedSize( 360, pathsHeight ); - QVBoxLayout * dir_layout = new QVBoxLayout( paths ); - dir_layout->setSpacing( 0 ); - dir_layout->setMargin( 0 ); - labelWidget( paths, tr( "Paths" ) ); - QLabel * title = new QLabel( tr( "Directories" ), paths ); - QFont f = title->font(); - f.setBold( true ); - title->setFont( pointSize<12>( f ) ); + +#ifdef LMMS_HAVE_PULSEAUDIO + m_audioIfaceSetupWidgets[AudioPulseAudio::name()] = + new AudioPulseAudio::setupWidget(as_w); +#endif + +#ifdef LMMS_HAVE_PORTAUDIO + m_audioIfaceSetupWidgets[AudioPortAudio::name()] = + new AudioPortAudio::setupWidget(as_w); +#endif + +#ifdef LMMS_HAVE_SOUNDIO + m_audioIfaceSetupWidgets[AudioSoundIo::name()] = + new AudioSoundIo::setupWidget(as_w); +#endif + +#ifdef LMMS_HAVE_SDL + m_audioIfaceSetupWidgets[AudioSdl::name()] = + new AudioSdl::setupWidget(as_w); +#endif + +#ifdef LMMS_HAVE_OSS + m_audioIfaceSetupWidgets[AudioOss::name()] = + new AudioOss::setupWidget(as_w); +#endif + +#ifdef LMMS_HAVE_SNDIO + m_audioIfaceSetupWidgets[AudioSndio::name()] = + new AudioSndio::setupWidget(as_w); +#endif + + m_audioIfaceSetupWidgets[AudioDummy::name()] = + new AudioDummy::setupWidget(as_w); - QScrollArea *pathScroll = new QScrollArea( paths ); + for(AswMap::iterator it = m_audioIfaceSetupWidgets.begin(); + it != m_audioIfaceSetupWidgets.end(); ++it) + { + m_audioIfaceNames[ + tr(it.key().toLatin1())] = it.key(); + } + for(trMap::iterator it = m_audioIfaceNames.begin(); + it != m_audioIfaceNames.end(); ++it) + { + QWidget * audioWidget = m_audioIfaceSetupWidgets[it.value()]; + audioWidget->hide(); + as_w_layout->addWidget(audioWidget); + m_audioInterfaces->addItem(it.key()); + } - QWidget *pathSelectors = new QWidget( ws ); - QVBoxLayout *pathSelectorLayout = new QVBoxLayout; - pathScroll->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); - pathScroll->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - pathScroll->resize( 362, pathsHeight - 50 ); - pathScroll->move( 0, 30 ); - pathSelectors->resize( 360, pathsHeight - 50 ); + // If no preferred audio device is saved, save the current one. + QString audioDevName = ConfigManager::inst()->value("mixer", "audiodev"); + if (m_audioInterfaces->findText(audioDevName) < 0) + { + audioDevName = Engine::mixer()->audioDevName(); + ConfigManager::inst()->setValue("mixer", "audiodev", audioDevName); + } + m_audioInterfaces-> + setCurrentIndex(m_audioInterfaces->findText(audioDevName)); + m_audioIfaceSetupWidgets[audioDevName]->show(); + + connect(m_audioInterfaces, SIGNAL(activated(const QString &)), + this, SLOT(audioInterfaceChanged(const QString &))); + + // Advanced setting, hidden for now + if(false) + { + LedCheckBox * useNaNHandler = new LedCheckBox( + tr("Use built-in NaN handler"), audio_w); + useNaNHandler->setChecked(m_NaNHandler); + } + + // HQ mode LED. + LedCheckBox * 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))); + + + // Buffer size tab. + TabWidget * bufferSize_tw = new TabWidget( + tr("Buffer size"), audio_w); + bufferSize_tw->setFixedHeight(76); + + 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); + + 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()); + + QPushButton * 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())); + ToolTip::add(bufferSize_reset_btn, + tr("Reset to default value")); + + + // Audio layout ordering. + audio_layout->addWidget(audioiface_tw); + audio_layout->addWidget(as_w); + audio_layout->addWidget(hqaudio); + audio_layout->addWidget(bufferSize_tw); + audio_layout->addStretch(); + + + + // MIDI widget. + QWidget * midi_w = new QWidget(settings_w); + QVBoxLayout * midi_layout = new QVBoxLayout(midi_w); + midi_layout->setSpacing(10); + midi_layout->setMargin(0); + labelWidget(midi_w, + tr("MIDI")); + + // MIDI interface tab. + TabWidget * midiiface_tw = new TabWidget( + tr("MIDI interface"), midi_w); + midiiface_tw->setFixedHeight(56); + + m_midiInterfaces = new QComboBox(midiiface_tw); + m_midiInterfaces->setGeometry(10, 20, 240, 28); + + // Ifaces-settings-widget. + QWidget * ms_w = new QWidget(midi_w); + ms_w->setFixedHeight(60); + + QHBoxLayout * ms_w_layout = new QHBoxLayout(ms_w); + ms_w_layout->setSpacing(0); + ms_w_layout->setMargin(0); + +#ifdef LMMS_HAVE_ALSA + m_midiIfaceSetupWidgets[MidiAlsaSeq::name()] = + MidiSetupWidget::create(ms_w); + m_midiIfaceSetupWidgets[MidiAlsaRaw::name()] = + MidiSetupWidget::create(ms_w); +#endif + +#ifdef LMMS_HAVE_JACK + m_midiIfaceSetupWidgets[MidiJack::name()] = + MidiSetupWidget::create(ms_w); +#endif + +#ifdef LMMS_HAVE_OSS + m_midiIfaceSetupWidgets[MidiOss::name()] = + MidiSetupWidget::create(ms_w); +#endif + +#ifdef LMMS_HAVE_SNDIO + m_midiIfaceSetupWidgets[MidiSndio::name()] = + MidiSetupWidget::create(ms_w); +#endif + +#ifdef LMMS_BUILD_WIN32 + m_midiIfaceSetupWidgets[MidiWinMM::name()] = + MidiSetupWidget::create(ms_w); +#endif + +#ifdef LMMS_BUILD_APPLE + m_midiIfaceSetupWidgets[MidiApple::name()] = + MidiSetupWidget::create(ms_w); +#endif + + m_midiIfaceSetupWidgets[MidiDummy::name()] = + MidiSetupWidget::create(ms_w); + + + for(MswMap::iterator it = m_midiIfaceSetupWidgets.begin(); + it != m_midiIfaceSetupWidgets.end(); ++it) + { + m_midiIfaceNames[ + tr(it.key().toLatin1())] = it.key(); + } + for(trMap::iterator it = m_midiIfaceNames.begin(); + it != m_midiIfaceNames.end(); ++it) + { + QWidget * midiWidget = m_midiIfaceSetupWidgets[it.value()]; + midiWidget->hide(); + ms_w_layout->addWidget(midiWidget); + m_midiInterfaces->addItem(it.key()); + } + + QString midiDevName = ConfigManager::inst()->value("mixer", "mididev"); + if (m_midiInterfaces->findText(midiDevName) < 0) + { + midiDevName = Engine::mixer()->midiClientName(); + ConfigManager::inst()->setValue("mixer", "mididev", midiDevName); + } + m_midiInterfaces->setCurrentIndex(m_midiInterfaces->findText(midiDevName)); + m_midiIfaceSetupWidgets[midiDevName]->show(); + + connect(m_midiInterfaces, SIGNAL(activated(const QString &)), + this, SLOT(midiInterfaceChanged(const QString &))); + + + // MIDI layout ordering. + midi_layout->addWidget(midiiface_tw); + midi_layout->addWidget(ms_w); + midi_layout->addStretch(); + + + + // Paths widget. + QWidget * paths_w = new QWidget(settings_w); + + QVBoxLayout * paths_layout = new QVBoxLayout(paths_w); + paths_layout->setSpacing(10); + paths_layout->setMargin(0); + + labelWidget(paths_w, tr("Paths")); + + + // Paths scroll area. + QScrollArea * pathsScroll = new QScrollArea(paths_w); + pathsScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + pathsScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + // Path selectors widget. + QWidget * pathSelectors = new QWidget(paths_w); const int txtLength = 284; - const int btnStart = 297; + const int btnStart = 300; + // Path selectors layout. + QVBoxLayout * pathSelectorsLayout = new QVBoxLayout; + pathSelectorsLayout->setSpacing(10); auto addPathEntry = [&](const char* caption, const QString& content, const char* setSlot, const char* openSlot, QLineEdit*& lineEdit, - QWidget* twParent, const char* pixmap = "project_open") { - TabWidget * newTw = new TabWidget(tr(caption).toUpper(), - twParent); + TabWidget * newTw = new TabWidget(tr(caption), + pathSelectors); newTw->setFixedHeight(48); lineEdit = new QLineEdit(content, newTw); @@ -401,397 +728,122 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : selectBtn->move(btnStart, 16); connect(selectBtn, SIGNAL(clicked()), this, openSlot); - pathSelectorLayout->addWidget(newTw); - pathSelectorLayout->addSpacing(10); + pathSelectorsLayout->addWidget(newTw); + pathSelectorsLayout->addSpacing(10); }; addPathEntry("LMMS working directory", m_workingDir, SLOT(setWorkingDir(const QString &)), SLOT(openWorkingDir()), - m_wdLineEdit, pathSelectors); - addPathEntry("GIG directory", m_gigDir, - SLOT(setGIGDir(const QString &)), - SLOT(openGIGDir()), - m_gigLineEdit, pathSelectors); + m_workingDirLineEdit); + addPathEntry("VST plugins directory", m_vstDir, + SLOT(setVSTDir(const QString &)), + SLOT(openVSTDir()), + m_vstDirLineEdit); + addPathEntry("LADSPA plugins directories", m_ladspaDir, + SLOT(setLADSPADir(const QString &)), + SLOT(openLADSPADir()), + m_ladspaDirLineEdit, "add_folder"); addPathEntry("SF2 directory", m_sf2Dir, SLOT(setSF2Dir(const QString &)), SLOT(openSF2Dir()), - m_sf2LineEdit, pathSelectors); - addPathEntry("VST-plugin directory", m_vstDir, - SLOT(setVSTDir(const QString &)), - SLOT(openVSTDir()), - m_vdLineEdit, pathSelectors); - addPathEntry("LADSPA plugin directories", m_ladDir, - SLOT(setLADSPADir(const QString &)), - SLOT(openLADSPADir()), - m_ladLineEdit, paths, - "add_folder"); -#ifdef LMMS_HAVE_STK - addPathEntry("STK rawwave directory", m_stkDir, - SLOT(setSTKDir(const QString &)), - SLOT(openSTKDir()), - m_stkLineEdit, paths); -#endif + m_sf2DirLineEdit); #ifdef LMMS_HAVE_FLUIDSYNTH - addPathEntry("Default Soundfont File", m_defaultSoundfont, - SLOT(setDefaultSoundfont(const QString &)), - SLOT(openDefaultSoundfont()), - m_sfLineEdit, paths); + addPathEntry("Default SF2", m_sf2File, + SLOT(setSF2File(const QString &)), + SLOT(openSF2File()), + m_sf2FileLineEdit); #endif - addPathEntry("Themes directory", m_artworkDir, - SLOT(setArtworkDir(const QString &)), - SLOT(openArtworkDir()), - m_adLineEdit, pathSelectors); - pathSelectorLayout->addStretch(); - addPathEntry("Background artwork", m_backgroundArtwork, - SLOT(setBackgroundArtwork(const QString &)), - SLOT(openBackgroundArtwork()), - m_baLineEdit, paths); - pathSelectors->setLayout(pathSelectorLayout); - - - dir_layout->addWidget(pathSelectors); - - pathScroll->setWidget(pathSelectors); - pathScroll->setWidgetResizable(true); - - - - QWidget * performance = new QWidget( ws ); - performance->setFixedSize( 360, 200 ); - QVBoxLayout * perf_layout = new QVBoxLayout( performance ); - perf_layout->setSpacing( 0 ); - perf_layout->setMargin( 0 ); - labelWidget( performance, tr( "Performance settings" ) ); - - - TabWidget * auto_save_tw = new TabWidget( - tr( "Auto save" ).toUpper(), performance ); - auto_save_tw->setFixedHeight( 110 ); - - m_saveIntervalSlider = new QSlider( Qt::Horizontal, auto_save_tw ); - m_saveIntervalSlider->setRange( 1, 20 ); - m_saveIntervalSlider->setTickPosition( QSlider::TicksBelow ); - m_saveIntervalSlider->setPageStep( 1 ); - m_saveIntervalSlider->setTickInterval( 1 ); - m_saveIntervalSlider->setGeometry( 10, 16, 340, 18 ); - m_saveIntervalSlider->setValue( m_saveInterval ); - - 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 auto-save" ), 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 auto-save while playing" ), auto_save_tw ); - m_runningAutoSave->move( 20, 90 ); - m_runningAutoSave->setChecked( m_enableRunningAutoSave ); - connect( m_runningAutoSave, SIGNAL( toggled( bool ) ), - this, SLOT( toggleRunningAutoSave( bool ) ) ); - - QPushButton * autoSaveResetBtn = new QPushButton( - embed::getIconPixmap( "reload" ), "", auto_save_tw ); - autoSaveResetBtn->setGeometry( 320, 70, 28, 28 ); - connect( autoSaveResetBtn, SIGNAL( clicked() ), this, - SLOT( resetAutoSave() ) ); - ToolTip::add( autoSaveResetBtn, tr( "Reset to default value" ) ); - - m_saveIntervalSlider->setEnabled( m_enableAutoSave ); - m_runningAutoSave->setVisible( m_enableAutoSave ); - - - perf_layout->addWidget( auto_save_tw ); - perf_layout->addSpacing( 10 ); - - - TabWidget * ui_fx_tw = new TabWidget( tr( "UI effects vs. " - "performance" ).toUpper(), - performance ); - ui_fx_tw->setFixedHeight( 70 ); - - LedCheckBox * smoothScroll = new LedCheckBox( - tr( "Smooth scroll in Song Editor" ), ui_fx_tw ); - smoothScroll->move( 10, 20 ); - smoothScroll->setChecked( m_smoothScroll ); - connect( smoothScroll, SIGNAL( toggled( bool ) ), - this, SLOT( toggleSmoothScroll( bool ) ) ); - - LedCheckBox * animAFP = new LedCheckBox( - tr( "Show playback cursor in AudioFileProcessor" ), - ui_fx_tw ); - animAFP->move( 10, 40 ); - animAFP->setChecked( m_animateAFP ); - connect( animAFP, SIGNAL( toggled( bool ) ), - this, SLOT( toggleAnimateAFP( bool ) ) ); - - - - perf_layout->addWidget( ui_fx_tw ); - perf_layout->addStretch(); - - - - QWidget * audio = new QWidget( ws ); - audio->setFixedSize( 360, 200 ); - QVBoxLayout * audio_layout = new QVBoxLayout( audio ); - audio_layout->setSpacing( 0 ); - audio_layout->setMargin( 0 ); - labelWidget( audio, tr( "Audio settings" ) ); - - TabWidget * audioiface_tw = new TabWidget( tr( "AUDIO INTERFACE" ), - audio ); - audioiface_tw->setFixedHeight( 60 ); - - m_audioInterfaces = new QComboBox( audioiface_tw ); - m_audioInterfaces->setGeometry( 10, 20, 240, 22 ); - - - // create ifaces-settings-widget - QWidget * asw = new QWidget( audio ); - asw->setFixedHeight( 60 ); - - QHBoxLayout * asw_layout = new QHBoxLayout( asw ); - asw_layout->setSpacing( 0 ); - asw_layout->setMargin( 0 ); - //asw_layout->setAutoAdd( true ); - -#ifdef LMMS_HAVE_JACK - m_audioIfaceSetupWidgets[AudioJack::name()] = - new AudioJack::setupWidget( asw ); -#endif - -#ifdef LMMS_HAVE_ALSA - m_audioIfaceSetupWidgets[AudioAlsa::name()] = - new AudioAlsaSetupWidget( asw ); -#endif - -#ifdef LMMS_HAVE_PULSEAUDIO - m_audioIfaceSetupWidgets[AudioPulseAudio::name()] = - new AudioPulseAudio::setupWidget( asw ); -#endif - -#ifdef LMMS_HAVE_PORTAUDIO - m_audioIfaceSetupWidgets[AudioPortAudio::name()] = - new AudioPortAudio::setupWidget( asw ); -#endif - -#ifdef LMMS_HAVE_SOUNDIO - m_audioIfaceSetupWidgets[AudioSoundIo::name()] = - new AudioSoundIo::setupWidget( asw ); -#endif - -#ifdef LMMS_HAVE_SDL - m_audioIfaceSetupWidgets[AudioSdl::name()] = - new AudioSdl::setupWidget( asw ); -#endif - -#ifdef LMMS_HAVE_OSS - m_audioIfaceSetupWidgets[AudioOss::name()] = - new AudioOss::setupWidget( asw ); -#endif - -#ifdef LMMS_HAVE_SNDIO - m_audioIfaceSetupWidgets[AudioSndio::name()] = - new AudioSndio::setupWidget( asw ); -#endif - m_audioIfaceSetupWidgets[AudioDummy::name()] = - new AudioDummy::setupWidget( asw ); - - - for( AswMap::iterator it = m_audioIfaceSetupWidgets.begin(); - it != m_audioIfaceSetupWidgets.end(); ++it ) - { - m_audioIfaceNames[tr( it.key().toLatin1())] = it.key(); - } - for( trMap::iterator it = m_audioIfaceNames.begin(); - it != m_audioIfaceNames.end(); ++it ) - { - QWidget * audioWidget = m_audioIfaceSetupWidgets[it.value()]; - audioWidget->hide(); - asw_layout->addWidget( audioWidget ); - m_audioInterfaces->addItem( it.key() ); - } - - // If no preferred audio device is saved, save the current one - QString audioDevName = - ConfigManager::inst()->value( "mixer", "audiodev" ); - if( m_audioInterfaces->findText(audioDevName) < 0 ) - { - audioDevName = Engine::mixer()->audioDevName(); - ConfigManager::inst()->setValue( - "mixer", "audiodev", audioDevName ); - } - m_audioInterfaces-> - setCurrentIndex( m_audioInterfaces->findText( audioDevName ) ); - m_audioIfaceSetupWidgets[audioDevName]->show(); - - connect( m_audioInterfaces, SIGNAL( activated( const QString & ) ), - this, SLOT( audioInterfaceChanged( const QString & ) ) ); - - - audio_layout->addWidget( audioiface_tw ); - audio_layout->addSpacing( 20 ); - audio_layout->addWidget( asw ); - audio_layout->addStretch(); - - - - QWidget * midi = new QWidget( ws ); - QVBoxLayout * midi_layout = new QVBoxLayout( midi ); - midi_layout->setSpacing( 0 ); - midi_layout->setMargin( 0 ); - labelWidget( midi, tr( "MIDI settings" ) ); - - TabWidget * midiiface_tw = new TabWidget( tr( "MIDI INTERFACE" ), - midi ); - midiiface_tw->setFixedHeight( 60 ); - - m_midiInterfaces = new QComboBox( midiiface_tw ); - m_midiInterfaces->setGeometry( 10, 20, 240, 22 ); - - - // create ifaces-settings-widget - QWidget * msw = new QWidget( midi ); - msw->setFixedHeight( 60 ); - - QHBoxLayout * msw_layout = new QHBoxLayout( msw ); - msw_layout->setSpacing( 0 ); - msw_layout->setMargin( 0 ); - //msw_layout->setAutoAdd( true ); - -#ifdef LMMS_HAVE_ALSA - m_midiIfaceSetupWidgets[MidiAlsaSeq::name()] = - MidiSetupWidget::create( msw ); - m_midiIfaceSetupWidgets[MidiAlsaRaw::name()] = - MidiSetupWidget::create( msw ); -#endif - -#ifdef LMMS_HAVE_JACK - m_midiIfaceSetupWidgets[MidiJack::name()] = - MidiSetupWidget::create( msw ); -#endif - -#ifdef LMMS_HAVE_OSS - m_midiIfaceSetupWidgets[MidiOss::name()] = - MidiSetupWidget::create( msw ); -#endif - -#ifdef LMMS_HAVE_SNDIO - m_midiIfaceSetupWidgets[MidiSndio::name()] = - MidiSetupWidget::create( msw ); -#endif - -#ifdef LMMS_BUILD_WIN32 - m_midiIfaceSetupWidgets[MidiWinMM::name()] = - MidiSetupWidget::create( msw ); -#endif - -#ifdef LMMS_BUILD_APPLE - m_midiIfaceSetupWidgets[MidiApple::name()] = - MidiSetupWidget::create( msw ); -#endif - - m_midiIfaceSetupWidgets[MidiDummy::name()] = - MidiSetupWidget::create( msw ); - - - for( MswMap::iterator it = m_midiIfaceSetupWidgets.begin(); - it != m_midiIfaceSetupWidgets.end(); ++it ) - { - m_midiIfaceNames[tr( it.key().toLatin1())] = it.key(); - } - for( trMap::iterator it = m_midiIfaceNames.begin(); - it != m_midiIfaceNames.end(); ++it ) - { - QWidget * midiWidget = m_midiIfaceSetupWidgets[it.value()]; - midiWidget->hide(); - msw_layout->addWidget( midiWidget ); - m_midiInterfaces->addItem( it.key() ); - } - - QString midiDevName = - ConfigManager::inst()->value( "mixer", "mididev" ); - if( m_midiInterfaces->findText(midiDevName) < 0 ) - { - midiDevName = Engine::mixer()->midiClientName(); - ConfigManager::inst()->setValue( - "mixer", "mididev", midiDevName ); - } - m_midiInterfaces->setCurrentIndex( - m_midiInterfaces->findText( midiDevName ) ); - m_midiIfaceSetupWidgets[midiDevName]->show(); - - connect( m_midiInterfaces, SIGNAL( activated( const QString & ) ), - this, SLOT( midiInterfaceChanged( const QString & ) ) ); - - - midi_layout->addWidget( midiiface_tw ); - midi_layout->addSpacing( 20 ); - midi_layout->addWidget( msw ); - midi_layout->addStretch(); - - - m_tabBar->addTab( general, tr( "General settings" ), 0, false, true - )->setIcon( embed::getIconPixmap( "setup_general" ) ); - m_tabBar->addTab( paths, tr( "Paths" ), 1, false, true - )->setIcon( embed::getIconPixmap( - "setup_directories" ) ); - m_tabBar->addTab( performance, tr( "Performance settings" ), 2, false, - true )->setIcon( embed::getIconPixmap( - "setup_performance" ) ); - m_tabBar->addTab( audio, tr( "Audio settings" ), 3, false, true - )->setIcon( embed::getIconPixmap( "setup_audio" ) ); - m_tabBar->addTab( midi, tr( "MIDI settings" ), 4, true, true - )->setIcon( embed::getIconPixmap( "setup_midi" ) ); - - - m_tabBar->setActiveTab( _tab_to_open ); - - hlayout->addWidget( m_tabBar ); - hlayout->addSpacing( 10 ); - hlayout->addWidget( ws ); - hlayout->addSpacing( 10 ); - hlayout->addStretch(); - - QWidget * buttons = new QWidget( this ); - QHBoxLayout * btn_layout = new QHBoxLayout( buttons ); - btn_layout->setSpacing( 0 ); - btn_layout->setMargin( 0 ); - QPushButton * ok_btn = new QPushButton( embed::getIconPixmap( "apply" ), - tr( "OK" ), buttons ); - connect( ok_btn, SIGNAL( clicked() ), this, SLOT( accept() ) ); - - QPushButton * cancel_btn = new QPushButton( embed::getIconPixmap( - "cancel" ), - tr( "Cancel" ), - buttons ); - connect( cancel_btn, SIGNAL( clicked() ), this, SLOT( reject() ) ); - - btn_layout->addStretch(); - btn_layout->addSpacing( 10 ); - btn_layout->addWidget( ok_btn ); - btn_layout->addSpacing( 10 ); - btn_layout->addWidget( cancel_btn ); - btn_layout->addSpacing( 10 ); - - vlayout->addWidget( settings ); - vlayout->addSpacing( 10 ); - vlayout->addWidget( buttons ); - vlayout->addSpacing( 10 ); - vlayout->addStretch(); + addPathEntry("GIG directory", m_gigDir, + SLOT(setGIGDir(const QString &)), + SLOT(openGIGDir()), + m_gigDirLineEdit); + addPathEntry("Theme directory", m_themeDir, + SLOT(setThemeDir(const QString &)), + SLOT(openThemeDir()), + m_themeDirLineEdit); + addPathEntry("Background artwork", m_backgroundPicFile, + SLOT(setBackgroundPicFile(const QString &)), + SLOT(openBackgroundPicFile()), + m_backgroundPicFileLineEdit); + + pathSelectorsLayout->addStretch(); + + pathSelectors->setLayout(pathSelectorsLayout); + + pathsScroll->setWidget(pathSelectors); + pathsScroll->setWidgetResizable(true); + + paths_layout->addWidget(pathsScroll); + paths_layout->addStretch(); + + // Major tabs ordering. + m_tabBar->addTab(general_w, + tr("General"), 0, false, true)->setIcon( + embed::getIconPixmap("setup_general")); + m_tabBar->addTab(performance_w, + tr("Performance"), 1, false, true)->setIcon( + embed::getIconPixmap("setup_performance")); + m_tabBar->addTab(audio_w, + tr("Audio"), 2, false, true)->setIcon( + embed::getIconPixmap("setup_audio")); + m_tabBar->addTab(midi_w, + tr("MIDI"), 3, false, true)->setIcon( + embed::getIconPixmap("setup_midi")); + m_tabBar->addTab(paths_w, + tr("Paths"), 4, true, true)->setIcon( + embed::getIconPixmap("setup_directories")); + + m_tabBar->setActiveTab(tab_to_open); + + // Horizontal layout ordering. + hlayout->addSpacing(2); + hlayout->addWidget(m_tabBar); + hlayout->addSpacing(10); + hlayout->addWidget(settings_w); + hlayout->addSpacing(10); + + // Extras widget and layout. + QWidget * extras_w = new QWidget(this); + QHBoxLayout * extras_layout = new QHBoxLayout(extras_w); + extras_layout->setSpacing(0); + extras_layout->setMargin(0); + + // Restart warning label. + restartWarningLbl = new QLabel( + tr("Some changes require restarting."), extras_w); + restartWarningLbl->hide(); + + // OK button. + QPushButton * ok_btn = new QPushButton( + embed::getIconPixmap("apply"), + tr("OK"), extras_w); + connect(ok_btn, SIGNAL(clicked()), + this, SLOT(accept())); + + // Cancel button. + QPushButton * cancel_btn = new QPushButton( + embed::getIconPixmap("cancel"), + tr("Cancel"), extras_w); + connect(cancel_btn, SIGNAL(clicked()), + this, SLOT(reject())); + + // Extras layout ordering. + extras_layout->addSpacing(10); + extras_layout->addWidget(restartWarningLbl); + extras_layout->addStretch(); + extras_layout->addWidget(ok_btn); + extras_layout->addSpacing(10); + extras_layout->addWidget(cancel_btn); + extras_layout->addSpacing(10); + + // Vertical layout ordering. + vlayout->addWidget(main_w); + vlayout->addSpacing(10); + vlayout->addWidget(extras_w); + vlayout->addSpacing(10); show(); - - } @@ -799,7 +851,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : SetupDialog::~SetupDialog() { - Engine::projectJournal()->setJournalling( true ); + Engine::projectJournal()->setJournalling(true); } @@ -807,286 +859,146 @@ SetupDialog::~SetupDialog() void SetupDialog::accept() { - if( m_warnAfterSetup ) - { - QMessageBox::information( NULL, tr( "Restart LMMS" ), - tr( "Please note that most changes " - "won't take effect until " - "you restart LMMS!" ), - QMessageBox::Ok ); - } - - // Hide dialog before setting values. This prevents an obscure bug - // where non-embedded VST windows would steal focus and prevent LMMS - // from taking mouse input, rendering the application unusable. + /* Hide dialog before setting values. This prevents an obscure bug + where non-embedded VST windows would steal focus and prevent LMMS + from taking mouse input, rendering the application unusable. */ QDialog::accept(); - ConfigManager::inst()->setValue( "mixer", "framesperaudiobuffer", - QString::number( m_bufferSize ) ); - ConfigManager::inst()->setValue( "mixer", "audiodev", - m_audioIfaceNames[m_audioInterfaces->currentText()] ); - ConfigManager::inst()->setValue( "mixer", "mididev", - m_midiIfaceNames[m_midiInterfaces->currentText()] ); - ConfigManager::inst()->setValue( "tooltips", "disabled", - QString::number( !m_toolTips ) ); - ConfigManager::inst()->setValue( "app", "nomsgaftersetup", - QString::number( !m_warnAfterSetup ) ); - ConfigManager::inst()->setValue( "app", "displaydbfs", - QString::number( m_displaydBFS ) ); - ConfigManager::inst()->setValue( "app", "nommpz", - QString::number( !m_MMPZ ) ); - ConfigManager::inst()->setValue( "app", "disablebackup", - QString::number( !m_disableBackup ) ); - ConfigManager::inst()->setValue( "app", "openlastproject", - QString::number( m_openLastProject ) ); - ConfigManager::inst()->setValue( "app", "nanhandler", - QString::number( m_NaNHandler ) ); - ConfigManager::inst()->setValue( "mixer", "hqaudio", - QString::number( m_hqAudioDev ) ); - ConfigManager::inst()->setValue( "ui", "smoothscroll", - QString::number( m_smoothScroll ) ); - ConfigManager::inst()->setValue( "ui", "enableautosave", - QString::number( m_enableAutoSave ) ); - ConfigManager::inst()->setValue( "ui", "saveinterval", - QString::number( m_saveInterval ) ); - ConfigManager::inst()->setValue( "ui", "enablerunningautosave", - QString::number( m_enableRunningAutoSave ) ); - ConfigManager::inst()->setValue( "ui", "oneinstrumenttrackwindow", - QString::number( m_oneInstrumentTrackWindow ) ); - ConfigManager::inst()->setValue( "ui", "compacttrackbuttons", - QString::number( m_compactTrackButtons ) ); - ConfigManager::inst()->setValue( "ui", "syncvstplugins", - QString::number( m_syncVSTPlugins ) ); - ConfigManager::inst()->setValue( "ui", "animateafp", - QString::number( m_animateAFP ) ); - ConfigManager::inst()->setValue( "ui", "printnotelabels", - QString::number( m_printNoteLabels ) ); - ConfigManager::inst()->setValue( "ui", "displaywaveform", - QString::number( m_displayWaveform ) ); - ConfigManager::inst()->setValue( "ui", "disableautoquit", - QString::number( m_disableAutoQuit ) ); - ConfigManager::inst()->setValue( "app", "language", m_lang ); - ConfigManager::inst()->setValue( "ui", "vstembedmethod", - m_vstEmbedMethod ); - ConfigManager::inst()->setValue( "ui", "vstalwaysontop", - QString::number( m_vstAlwaysOnTop ) ); + ConfigManager::inst()->setValue("app", "displaydbfs", + QString::number(m_displaydBFS)); + ConfigManager::inst()->setValue("tooltips", "disabled", + QString::number(!m_tooltips)); + ConfigManager::inst()->setValue("ui", "displaywaveform", + QString::number(m_displayWaveform)); + ConfigManager::inst()->setValue("ui", "printnotelabels", + QString::number(m_printNoteLabels)); + ConfigManager::inst()->setValue("ui", "compacttrackbuttons", + QString::number(m_compactTrackButtons)); + ConfigManager::inst()->setValue("ui", "oneinstrumenttrackwindow", + QString::number(m_oneInstrumentTrackWindow)); + ConfigManager::inst()->setValue("app", "nommpz", + QString::number(!m_MMPZ)); + ConfigManager::inst()->setValue("app", "disablebackup", + QString::number(!m_disableBackup)); + ConfigManager::inst()->setValue("app", "openlastproject", + QString::number(m_openLastProject)); + ConfigManager::inst()->setValue("app", "language", m_lang); + ConfigManager::inst()->setValue("ui", "saveinterval", + QString::number(m_saveInterval)); + ConfigManager::inst()->setValue("ui", "enableautosave", + QString::number(m_enableAutoSave)); + ConfigManager::inst()->setValue("ui", "enablerunningautosave", + QString::number(m_enableRunningAutoSave)); + ConfigManager::inst()->setValue("ui", "smoothscroll", + QString::number(m_smoothScroll)); + ConfigManager::inst()->setValue("ui", "animateafp", + QString::number(m_animateAFP)); + ConfigManager::inst()->setValue("ui", "vstembedmethod", + m_vstEmbedComboBox->currentData().toString()); + ConfigManager::inst()->setValue("ui", "vstalwaysontop", + QString::number(m_vstAlwaysOnTop)); + ConfigManager::inst()->setValue("ui", "syncvstplugins", + QString::number(m_syncVSTPlugins)); + ConfigManager::inst()->setValue("ui", "disableautoquit", + QString::number(m_disableAutoQuit)); + ConfigManager::inst()->setValue("mixer", "audiodev", + m_audioIfaceNames[m_audioInterfaces->currentText()]); + ConfigManager::inst()->setValue("app", "nanhandler", + QString::number(m_NaNHandler)); + ConfigManager::inst()->setValue("mixer", "hqaudio", + QString::number(m_hqAudioDev)); + ConfigManager::inst()->setValue("mixer", "framesperaudiobuffer", + QString::number(m_bufferSize)); + ConfigManager::inst()->setValue("mixer", "mididev", + m_midiIfaceNames[m_midiInterfaces->currentText()]); ConfigManager::inst()->setWorkingDir(QDir::fromNativeSeparators(m_workingDir)); ConfigManager::inst()->setVSTDir(QDir::fromNativeSeparators(m_vstDir)); - ConfigManager::inst()->setGIGDir(QDir::fromNativeSeparators(m_gigDir)); + ConfigManager::inst()->setLADSPADir(QDir::fromNativeSeparators(m_ladspaDir)); ConfigManager::inst()->setSF2Dir(QDir::fromNativeSeparators(m_sf2Dir)); - ConfigManager::inst()->setArtworkDir(QDir::fromNativeSeparators(m_artworkDir)); - ConfigManager::inst()->setLADSPADir(QDir::fromNativeSeparators(m_ladDir)); #ifdef LMMS_HAVE_FLUIDSYNTH - ConfigManager::inst()->setDefaultSoundfont( m_defaultSoundfont ); + ConfigManager::inst()->setSF2File(m_sf2File); #endif -#ifdef LMMS_HAVE_STK - ConfigManager::inst()->setSTKDir(QDir::fromNativeSeparators(m_stkDir)); -#endif - ConfigManager::inst()->setBackgroundArtwork( m_backgroundArtwork ); + ConfigManager::inst()->setGIGDir(QDir::fromNativeSeparators(m_gigDir)); + ConfigManager::inst()->setThemeDir(QDir::fromNativeSeparators(m_themeDir)); + ConfigManager::inst()->setBackgroundPicFile(m_backgroundPicFile); - // tell all audio-settings-widget to save their settings - for( AswMap::iterator it = m_audioIfaceSetupWidgets.begin(); - it != m_audioIfaceSetupWidgets.end(); ++it ) + // Tell all audio-settings-widgets to save their settings. + for(AswMap::iterator it = m_audioIfaceSetupWidgets.begin(); + it != m_audioIfaceSetupWidgets.end(); ++it) { it.value()->saveSettings(); } - // tell all MIDI-settings-widget to save their settings - for( MswMap::iterator it = m_midiIfaceSetupWidgets.begin(); - it != m_midiIfaceSetupWidgets.end(); ++it ) + // Tell all MIDI-settings-widgets to save their settings. + for(MswMap::iterator it = m_midiIfaceSetupWidgets.begin(); + it != m_midiIfaceSetupWidgets.end(); ++it) { it.value()->saveSettings(); } - ConfigManager::inst()->saveConfigFile(); } -void SetupDialog::setBufferSize( int _value ) +// General settings slots. + +void SetupDialog::toggleDisplaydBFS(bool enabled) { - const int step = DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION; - if( _value > step && _value % step ) - { - int mod_value = _value % step; - if( mod_value < step / 2 ) - { - m_bufSizeSlider->setValue( _value - mod_value ); - } - else - { - m_bufSizeSlider->setValue( _value + step - mod_value ); - } - return; - } - - if( m_bufSizeSlider->value() != _value ) - { - m_bufSizeSlider->setValue( _value ); - } - - m_bufferSize = _value * BUFFERSIZE_RESOLUTION; - m_bufSizeLbl->setText( tr( "Frames: %1\nLatency: %2 ms" ).arg( - m_bufferSize ).arg( - 1000.0f * m_bufferSize / - Engine::mixer()->processingSampleRate(), - 0, 'f', 1 ) ); + m_displaydBFS = enabled; } - - -void SetupDialog::resetBufSize() +void SetupDialog::toggleTooltips(bool enabled) { - setBufferSize( DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION ); + m_tooltips = enabled; } - - -void SetupDialog::toggleToolTips( bool _enabled ) +void SetupDialog::toggleDisplayWaveform(bool enabled) { - m_toolTips = _enabled; + m_displayWaveform = enabled; } - - -void SetupDialog::toggleWarnAfterSetup( bool _enabled ) +void SetupDialog::toggleNoteLabels(bool enabled) { - m_warnAfterSetup = _enabled; + m_printNoteLabels = enabled; } - - -void SetupDialog::toggleDisplaydBFS( bool _enabled ) +void SetupDialog::toggleCompactTrackButtons(bool enabled) { - m_displaydBFS = _enabled; + m_compactTrackButtons = enabled; } - - -void SetupDialog::toggleMMPZ( bool _enabled ) +void SetupDialog::toggleOneInstrumentTrackWindow(bool enabled) { - m_MMPZ = _enabled; + m_oneInstrumentTrackWindow = enabled; } - - -void SetupDialog::toggleDisableBackup( bool _enabled ) +void SetupDialog::toggleMMPZ(bool enabled) { - m_disableBackup = _enabled; + m_MMPZ = enabled; } - - -void SetupDialog::toggleOpenLastProject( bool _enabled ) +void SetupDialog::toggleDisableBackup(bool enabled) { - m_openLastProject = _enabled; + m_disableBackup = enabled; } - - -void SetupDialog::toggleHQAudioDev( bool _enabled ) +void SetupDialog::toggleOpenLastProject(bool enabled) { - m_hqAudioDev = _enabled; + m_openLastProject = enabled; } - - -void SetupDialog::toggleSmoothScroll( bool _enabled ) -{ - m_smoothScroll = _enabled; -} - - - - -void SetupDialog::toggleAutoSave( bool _enabled ) -{ - m_enableAutoSave = _enabled; - m_saveIntervalSlider->setEnabled( _enabled ); - m_runningAutoSave->setVisible( _enabled ); - setAutoSaveInterval( m_saveIntervalSlider->value() ); -} - - - - -void SetupDialog::toggleRunningAutoSave( bool _enabled ) -{ - m_enableRunningAutoSave = _enabled; -} - - - - -void SetupDialog::toggleCompactTrackButtons( bool _enabled ) -{ - m_compactTrackButtons = _enabled; -} - - - - - -void SetupDialog::toggleSyncVSTPlugins( bool _enabled ) -{ - m_syncVSTPlugins = _enabled; -} - -void SetupDialog::toggleAnimateAFP( bool _enabled ) -{ - m_animateAFP = _enabled; -} - - -void SetupDialog::toggleNoteLabels( bool en ) -{ - m_printNoteLabels = en; -} - - -void SetupDialog::toggleDisplayWaveform( bool en ) -{ - m_displayWaveform = en; -} - - -void SetupDialog::toggleDisableAutoquit( bool en ) -{ - m_disableAutoQuit = en; -} - - -void SetupDialog::toggleOneInstrumentTrackWindow( bool _enabled ) -{ - m_oneInstrumentTrackWindow = _enabled; -} - - -void SetupDialog::vstEmbedMethodChanged() -{ - m_vstEmbedMethod = m_vstEmbedComboBox->currentData().toString(); - m_vstAlwaysOnTopCheckBox->setVisible( m_vstEmbedMethod == "none" ); -} - - -void SetupDialog::toggleVSTAlwaysOnTop( bool en ) -{ - m_vstAlwaysOnTop = en; -} - - -void SetupDialog::setLanguage( int lang ) +void SetupDialog::setLanguage(int lang) { m_lang = m_languages[lang]; } @@ -1094,270 +1006,322 @@ void SetupDialog::setLanguage( int lang ) +// Performance settings slots. -void SetupDialog::openWorkingDir() -{ - QString new_dir = FileDialog::getExistingDirectory( this, - tr( "Choose LMMS working directory" ), m_workingDir ); - if( ! new_dir.isEmpty() ) - { - m_wdLineEdit->setText( new_dir ); - } -} - -void SetupDialog::openGIGDir() -{ - QString new_dir = FileDialog::getExistingDirectory( this, - tr( "Choose your GIG directory" ), - m_gigDir ); - if( ! new_dir.isEmpty() ) - { - m_gigLineEdit->setText( new_dir ); - } -} - -void SetupDialog::openSF2Dir() -{ - QString new_dir = FileDialog::getExistingDirectory( this, - tr( "Choose your SF2 directory" ), - m_sf2Dir ); - if( ! new_dir.isEmpty() ) - { - m_sf2LineEdit->setText( new_dir ); - } -} - - - - -void SetupDialog::setWorkingDir( const QString & _wd ) -{ - m_workingDir = _wd; -} - - - - -void SetupDialog::openVSTDir() -{ - QString new_dir = FileDialog::getExistingDirectory( this, - tr( "Choose your VST-plugin directory" ), - m_vstDir ); - if( ! new_dir.isEmpty() ) - { - m_vdLineEdit->setText( new_dir ); - } -} - - - - -void SetupDialog::setVSTDir( const QString & _vd ) -{ - m_vstDir = _vd; -} - -void SetupDialog::setGIGDir(const QString &_gd) -{ - m_gigDir = _gd; -} - -void SetupDialog::setSF2Dir(const QString &_sfd) -{ - m_sf2Dir = _sfd; -} - - - - -void SetupDialog::openArtworkDir() -{ - QString new_dir = FileDialog::getExistingDirectory( this, - tr( "Choose artwork-theme directory" ), - m_artworkDir ); - if( ! new_dir.isEmpty() ) - { - m_adLineEdit->setText( new_dir ); - } -} - - - - -void SetupDialog::setArtworkDir( const QString & _ad ) -{ - m_artworkDir = _ad; -} - - - - -void SetupDialog::openLADSPADir() -{ - QString new_dir = FileDialog::getExistingDirectory( this, - tr( "Choose LADSPA plugin directory" ), - m_ladDir ); - if( ! new_dir.isEmpty() ) - { - if( m_ladLineEdit->text() == "" ) - { - m_ladLineEdit->setText( new_dir ); - } - else - { - m_ladLineEdit->setText( m_ladLineEdit->text() + "," + - new_dir ); - } - } -} - - - -void SetupDialog::openSTKDir() -{ -#ifdef LMMS_HAVE_STK - QString new_dir = FileDialog::getExistingDirectory( this, - tr( "Choose STK rawwave directory" ), - m_stkDir ); - if( ! new_dir.isEmpty() ) - { - m_stkLineEdit->setText( new_dir ); - } -#endif -} - - - - -void SetupDialog::openDefaultSoundfont() -{ -#ifdef LMMS_HAVE_FLUIDSYNTH - QString new_file = FileDialog::getOpenFileName( this, - tr( "Choose default SoundFont" ), m_defaultSoundfont, - "SoundFont2 Files (*.sf2)" ); - - if( ! new_file.isEmpty() ) - { - m_sfLineEdit->setText( new_file ); - } -#endif -} - - - - -void SetupDialog::openBackgroundArtwork() -{ - QList fileTypesList = QImageReader::supportedImageFormats(); - QString fileTypes; - for( int i = 0; i < fileTypesList.count(); i++ ) - { - if( fileTypesList[i] != fileTypesList[i].toUpper() ) - { - if( !fileTypes.isEmpty() ) - { - fileTypes += " "; - } - fileTypes += "*." + QString( fileTypesList[i] ); - } - } - - QString dir = ( m_backgroundArtwork.isEmpty() ) ? - m_artworkDir : - m_backgroundArtwork; - QString new_file = FileDialog::getOpenFileName( this, - tr( "Choose background artwork" ), dir, - "Image Files (" + fileTypes + ")" ); - - if( ! new_file.isEmpty() ) - { - m_baLineEdit->setText( new_file ); - } -} - - - - -void SetupDialog::setLADSPADir( const QString & _fd ) -{ - m_ladDir = _fd; -} - - - - -void SetupDialog::setSTKDir( const QString & _fd ) -{ -#ifdef LMMS_HAVE_STK - m_stkDir = _fd; -#endif -} - - - - -void SetupDialog::setDefaultSoundfont( const QString & _sf ) -{ -#ifdef LMMS_HAVE_FLUIDSYNTH - m_defaultSoundfont = _sf; -#endif -} - - - - -void SetupDialog::setBackgroundArtwork( const QString & _ba ) -{ - m_backgroundArtwork = _ba; -} - - - - -void SetupDialog::setAutoSaveInterval( int value ) +void SetupDialog::setAutoSaveInterval(int value) { m_saveInterval = value; - m_saveIntervalSlider->setValue( m_saveInterval ); - QString minutes = m_saveInterval > 1 ? tr( "minutes" ) : tr( "minute" ); - minutes = QString( "%1 %2" ).arg( QString::number( m_saveInterval ), minutes ); - minutes = m_enableAutoSave ? minutes : tr( "Disabled" ); - m_saveIntervalLbl->setText( tr( "Auto-save interval: %1" ).arg( minutes ) ); + m_saveIntervalSlider->setValue(m_saveInterval); + QString minutes = m_saveInterval > 1 ? tr("minutes") : tr("minute"); + minutes = QString("%1 %2").arg(QString::number(m_saveInterval), minutes); + minutes = m_enableAutoSave ? minutes : tr("Disabled"); + m_saveIntervalLbl->setText( + tr("Autosave interval: %1").arg(minutes)); } +void SetupDialog::toggleAutoSave(bool enabled) +{ + m_enableAutoSave = enabled; + m_saveIntervalSlider->setEnabled(enabled); + m_runningAutoSave->setVisible(enabled); + setAutoSaveInterval(m_saveIntervalSlider->value()); +} + + +void SetupDialog::toggleRunningAutoSave(bool enabled) +{ + m_enableRunningAutoSave = enabled; +} void SetupDialog::resetAutoSave() { - setAutoSaveInterval( MainWindow::DEFAULT_SAVE_INTERVAL_MINUTES ); - m_autoSave->setChecked( true ); - m_runningAutoSave->setChecked( false ); + setAutoSaveInterval(MainWindow::DEFAULT_SAVE_INTERVAL_MINUTES); + m_autoSave->setChecked(true); + m_runningAutoSave->setChecked(false); +} + + +void SetupDialog::toggleSmoothScroll(bool enabled) +{ + m_smoothScroll = enabled; +} + + +void SetupDialog::toggleAnimateAFP(bool enabled) +{ + m_animateAFP = enabled; +} + + +void SetupDialog::toggleSyncVSTPlugins(bool enabled) +{ + m_syncVSTPlugins = enabled; +} + + +void SetupDialog::vstEmbedMethodChanged() +{ + m_vstEmbedMethod = m_vstEmbedComboBox->currentData().toString(); + m_vstAlwaysOnTopCheckBox->setVisible(m_vstEmbedMethod == "none"); +} + + +void SetupDialog::toggleVSTAlwaysOnTop(bool enabled) +{ + m_vstAlwaysOnTop = enabled; +} + + +void SetupDialog::toggleDisableAutoQuit(bool enabled) +{ + m_disableAutoQuit = enabled; } -void SetupDialog::audioInterfaceChanged( const QString & _iface ) +// Audio settings slots. + +void SetupDialog::toggleHQAudioDev(bool enabled) { - for( AswMap::iterator it = m_audioIfaceSetupWidgets.begin(); - it != m_audioIfaceSetupWidgets.end(); ++it ) + m_hqAudioDev = enabled; +} + + +void SetupDialog::audioInterfaceChanged(const QString & iface) +{ + for(AswMap::iterator it = m_audioIfaceSetupWidgets.begin(); + it != m_audioIfaceSetupWidgets.end(); ++it) { it.value()->hide(); } - m_audioIfaceSetupWidgets[m_audioIfaceNames[_iface]]->show(); + m_audioIfaceSetupWidgets[m_audioIfaceNames[iface]]->show(); } - - -void SetupDialog::midiInterfaceChanged( const QString & _iface ) +void SetupDialog::setBufferSize(int value) { - for( MswMap::iterator it = m_midiIfaceSetupWidgets.begin(); - it != m_midiIfaceSetupWidgets.end(); ++it ) + const int step = DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION; + if(value > step && value % step) + { + int mod_value = value % step; + if(mod_value < step / 2) + { + m_bufferSizeSlider->setValue(value - mod_value); + } + else + { + m_bufferSizeSlider->setValue(value + step - mod_value); + } + return; + } + + if(m_bufferSizeSlider->value() != value) + { + m_bufferSizeSlider->setValue(value); + } + + m_bufferSize = value * BUFFERSIZE_RESOLUTION; + m_bufferSizeLbl->setText(tr("Frames: %1\nLatency: %2 ms").arg(m_bufferSize).arg( + 1000.0f * m_bufferSize / Engine::mixer()->processingSampleRate(), 0, 'f', 1)); +} + + +void SetupDialog::resetBufferSize() +{ + setBufferSize(DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION); +} + + +// MIDI settings slots. + +void SetupDialog::midiInterfaceChanged(const QString & iface) +{ + for(MswMap::iterator it = m_midiIfaceSetupWidgets.begin(); + it != m_midiIfaceSetupWidgets.end(); ++it) { it.value()->hide(); } - m_midiIfaceSetupWidgets[m_midiIfaceNames[_iface]]->show(); + m_midiIfaceSetupWidgets[m_midiIfaceNames[iface]]->show(); +} + + +// Paths settings slots. + +void SetupDialog::openWorkingDir() +{ + QString new_dir = FileDialog::getExistingDirectory(this, + tr("Choose the LMMS working directory"), m_workingDir); + if (!new_dir.isEmpty()) + { + m_workingDirLineEdit->setText(new_dir); + } +} + + +void SetupDialog::setWorkingDir(const QString & workingDir) +{ + m_workingDir = workingDir; +} + + +void SetupDialog::openVSTDir() +{ + QString new_dir = FileDialog::getExistingDirectory(this, + tr("Choose your VST plugins directory"), m_vstDir); + if (!new_dir.isEmpty()) + { + m_vstDirLineEdit->setText(new_dir); + } +} + + +void SetupDialog::setVSTDir(const QString & vstDir) +{ + m_vstDir = vstDir; +} + + +void SetupDialog::openLADSPADir() +{ + QString new_dir = FileDialog::getExistingDirectory(this, + tr("Choose your LADSPA plugins directory"), m_ladspaDir); + if (!new_dir.isEmpty()) + { + if(m_ladspaDirLineEdit->text() == "") + { + m_ladspaDirLineEdit->setText(new_dir); + } + else + { + m_ladspaDirLineEdit->setText(m_ladspaDirLineEdit->text() + "," + + new_dir); + } + } +} + + +void SetupDialog::setLADSPADir(const QString & ladspaDir) +{ + m_ladspaDir = ladspaDir; +} + + +void SetupDialog::openSF2Dir() +{ + QString new_dir = FileDialog::getExistingDirectory(this, + tr("Choose your SF2 directory"), m_sf2Dir); + if (!new_dir.isEmpty()) + { + m_sf2DirLineEdit->setText(new_dir); + } +} + + +void SetupDialog::setSF2Dir(const QString & sf2Dir) +{ + m_sf2Dir = sf2Dir; +} + + +void SetupDialog::openSF2File() +{ +#ifdef LMMS_HAVE_FLUIDSYNTH + QString new_file = FileDialog::getOpenFileName(this, + tr("Choose your default SF2"), m_sf2File, "SoundFont 2 files (*.sf2)"); + + if (!new_file.isEmpty()) + { + m_sf2FileLineEdit->setText(new_file); + } +#endif +} + + +void SetupDialog::setSF2File(const QString & sf2File) +{ +#ifdef LMMS_HAVE_FLUIDSYNTH + m_sf2File = sf2File; +#endif +} + + +void SetupDialog::openGIGDir() +{ + QString new_dir = FileDialog::getExistingDirectory(this, + tr("Choose your GIG directory"), m_gigDir); + if(new_dir != QString::null) + { + m_gigDirLineEdit->setText(new_dir); + } +} + + +void SetupDialog::setGIGDir(const QString & gigDir) +{ + m_gigDir = gigDir; +} + + +void SetupDialog::openThemeDir() +{ + QString new_dir = FileDialog::getExistingDirectory(this, + tr("Choose your theme directory"), m_themeDir); + if(new_dir != QString::null) + { + m_themeDirLineEdit->setText(new_dir); + } +} + + +void SetupDialog::setThemeDir(const QString & themeDir) +{ + m_themeDir = themeDir; +} + + +void SetupDialog::openBackgroundPicFile() +{ + QList fileTypesList = QImageReader::supportedImageFormats(); + QString fileTypes; + for(int i = 0; i < fileTypesList.count(); i++) + { + if(fileTypesList[i] != fileTypesList[i].toUpper()) + { + if(!fileTypes.isEmpty()) + { + fileTypes += " "; + } + fileTypes += "*." + QString(fileTypesList[i]); + } + } + + QString dir = (m_backgroundPicFile.isEmpty()) ? + m_themeDir : + m_backgroundPicFile; + QString new_file = FileDialog::getOpenFileName(this, + tr("Choose your background picture"), dir, "Picture files (" + fileTypes + ")"); + + if(new_file != QString::null) + { + m_backgroundPicFileLineEdit->setText(new_file); + } +} + + +void SetupDialog::setBackgroundPicFile(const QString & backgroundPicFile) +{ + m_backgroundPicFile = backgroundPicFile; +} + + + + +void SetupDialog::showRestartWarning() +{ + restartWarningLbl->show(); } From e1236f53e2af1569182f01d6ec451c2cd1dd1bbb Mon Sep 17 00:00:00 2001 From: malcops Date: Sat, 7 Sep 2019 22:18:45 -0400 Subject: [PATCH 018/160] Remove maximize from Meter(Metro) menu (#5071) Fixes #4984. --- src/gui/widgets/TempoSyncKnob.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gui/widgets/TempoSyncKnob.cpp b/src/gui/widgets/TempoSyncKnob.cpp index 1e2e249f3..d769fd831 100644 --- a/src/gui/widgets/TempoSyncKnob.cpp +++ b/src/gui/widgets/TempoSyncKnob.cpp @@ -291,7 +291,11 @@ void TempoSyncKnob::showCustom() if( m_custom == NULL ) { m_custom = new MeterDialog( gui->mainWindow()->workspace() ); - gui->mainWindow()->addWindowedWidget( m_custom ); + QMdiSubWindow * subWindow = gui->mainWindow()->addWindowedWidget( m_custom ); + Qt::WindowFlags flags = subWindow->windowFlags(); + flags &= ~Qt::WindowMaximizeButtonHint; + subWindow->setWindowFlags( flags ); + subWindow->setFixedSize( subWindow->size() ); m_custom->setWindowTitle( "Meter" ); m_custom->setModel( &model()->m_custom ); } From 2e84cd3e0ce3b888d4dd9b87a85011af26e29767 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 8 Sep 2019 13:43:15 +0100 Subject: [PATCH 019/160] Fix stuck keys when dragging on piano view (#5127) * Fix stuck keys when dragging on piano view * Add comment Co-Authored-By: Shmuel H. --- src/core/NotePlayHandle.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 1e6ce01c9..d62857350 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -199,7 +199,12 @@ void NotePlayHandle::play( sampleFrame * _working_buffer ) lock(); - if( m_totalFramesPlayed == 0 && !m_hasMidiNote + /* It is possible for NotePlayHandle::noteOff to be called before NotePlayHandle::play, + * which results in a note-on message being sent without a subsequent note-off message. + * Therefore, we check here whether the note has already been released before sending + * the note-on message. */ + if( !m_released + && m_totalFramesPlayed == 0 && !m_hasMidiNote && ( hasParent() || ! m_instrumentTrack->isArpeggioEnabled() ) ) { m_hasMidiNote = true; From a0e7a88e67d560264da8cb31576db4fae62c52b6 Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Mon, 9 Sep 2019 00:22:43 -0400 Subject: [PATCH 020/160] Remove "What's This?" leftovers (#5173) --- data/themes/classic/style.css | 3 --- data/themes/default/style.css | 3 --- include/LmmsPalette.h | 6 ------ src/gui/LmmsPalette.cpp | 8 +------- 4 files changed, 1 insertion(+), 19 deletions(-) diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index 12d90981d..97d68f6e3 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -897,7 +897,4 @@ LmmsPalette { qproperty-brightText: #4afd85; qproperty-highlight: #202020; qproperty-highlightedText: #ffffff; - /* the next two are used for whatsthis dialogs */ - qproperty-toolTipText: #000; - qproperty-toolTipBase: #c9c9c9; } diff --git a/data/themes/default/style.css b/data/themes/default/style.css index 5d889295c..ff6d032c2 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -920,7 +920,4 @@ LmmsPalette { qproperty-brightText: #d1d8e4; qproperty-highlight: #262b30; qproperty-highlightedText: #d1d8e4; - /* the next two are used for whatsthis dialogs */ - qproperty-toolTipText: #d1d8e4; - qproperty-toolTipBase: #101213; } diff --git a/include/LmmsPalette.h b/include/LmmsPalette.h index 49b831346..a8ee5d1c8 100644 --- a/include/LmmsPalette.h +++ b/include/LmmsPalette.h @@ -43,8 +43,6 @@ class LMMS_EXPORT LmmsPalette : public QWidget Q_PROPERTY( QColor brightText READ brightText WRITE setBrightText ) Q_PROPERTY( QColor highlight READ highlight WRITE setHighlight ) Q_PROPERTY( QColor highlightedText READ highlightedText WRITE setHighlightedText ) - Q_PROPERTY( QColor toolTipText READ toolTipText WRITE setToolTipText ) - Q_PROPERTY( QColor toolTipBase READ toolTipBase WRITE setToolTipBase ) public: LmmsPalette( QWidget * parent, QStyle * stylearg ); @@ -65,8 +63,6 @@ public: ACCESSMET( brightText, setBrightText ) ACCESSMET( highlight, setHighlight ) ACCESSMET( highlightedText, setHighlightedText ) - ACCESSMET( toolTipText, setToolTipText ) - ACCESSMET( toolTipBase, setToolTipBase ) #undef ACCESSMET @@ -83,8 +79,6 @@ private: QColor m_brightText; QColor m_highlight; QColor m_highlightedText; - QColor m_toolTipText; - QColor m_toolTipBase; }; diff --git a/src/gui/LmmsPalette.cpp b/src/gui/LmmsPalette.cpp index e58e72cea..e0b356d7a 100644 --- a/src/gui/LmmsPalette.cpp +++ b/src/gui/LmmsPalette.cpp @@ -42,9 +42,7 @@ LmmsPalette::LmmsPalette( QWidget * parent, QStyle * stylearg ) : m_buttonText( 0,0,0 ), m_brightText( 74, 253, 133 ), m_highlight( 100, 100, 100 ), - m_highlightedText( 255, 255, 255 ), - m_toolTipText( 0, 0, 0 ), - m_toolTipBase( 128, 128, 128 ) + m_highlightedText( 255, 255, 255 ) { setStyle( stylearg ); stylearg->polish( this ); @@ -72,8 +70,6 @@ LmmsPalette::~LmmsPalette() ACCESSMET( brightText, setBrightText ) ACCESSMET( highlight, setHighlight ) ACCESSMET( highlightedText, setHighlightedText ) - ACCESSMET( toolTipText, setToolTipText ) - ACCESSMET( toolTipBase, setToolTipBase ) QPalette LmmsPalette::palette() const @@ -90,8 +86,6 @@ QPalette LmmsPalette::palette() const pal.setColor( QPalette::Shadow, shadow() ); pal.setColor( QPalette::Highlight, highlight() ); pal.setColor( QPalette::HighlightedText, highlightedText() ); - pal.setBrush( QPalette::ToolTipText, QBrush( toolTipText() ) ); - pal.setBrush( QPalette::ToolTipBase, QBrush( toolTipBase() ) ); return pal; } From 91cf5bd88d3cb0878fa7af50f75a15a51316542c Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Tue, 10 Sep 2019 15:17:55 +0100 Subject: [PATCH 021/160] Support compilation with MSVC 2019 (#5180) --- plugins/vst_base/RemoteVstPlugin32.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/vst_base/RemoteVstPlugin32.cmake b/plugins/vst_base/RemoteVstPlugin32.cmake index 9a8f04529..ec290ce96 100644 --- a/plugins/vst_base/RemoteVstPlugin32.cmake +++ b/plugins/vst_base/RemoteVstPlugin32.cmake @@ -13,7 +13,10 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) SET(MSVC_VER ${CMAKE_CXX_COMPILER_VERSION}) IF(NOT CMAKE_GENERATOR_32) - IF(MSVC_VER VERSION_GREATER 19.10 OR MSVC_VER VERSION_EQUAL 19.10) + IF(MSVC_VER VERSION_GREATER 19.20 OR MSVC_VER VERSION_EQUAL 19.20) + SET(CMAKE_GENERATOR_32 "Visual Studio 16 2019") + SET(MSVC_YEAR 2017) # Qt only provides binaries for MSVC 2017, but 2019 is binary compatible + ELSEIF(MSVC_VER VERSION_GREATER 19.10 OR MSVC_VER VERSION_EQUAL 19.10) SET(CMAKE_GENERATOR_32 "Visual Studio 15 2017") SET(MSVC_YEAR 2017) ELSEIF(MSVC_VER VERSION_GREATER 19.0 OR MSVC_VER VERSION_EQUAL 19.0) @@ -39,6 +42,7 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) ExternalProject_Add(RemoteVstPlugin32 "${EXTERNALPROJECT_ARGS}" CMAKE_GENERATOR "${CMAKE_GENERATOR_32}" + CMAKE_GENERATOR_PLATFORM Win32 #CMAKE_GENERATOR_TOOLSET "${CMAKE_GENERATOR_TOOLSET}" CMAKE_ARGS "${EXTERNALPROJECT_CMAKE_ARGS}" From c436e5ca578e98f5d9b8eadbb077a283978cd867 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Fri, 13 Sep 2019 07:54:48 +0900 Subject: [PATCH 022/160] Remove MIDI connections from factory .mmpz files (#5163) --- ...apDan-TwilightArea-OriginalByAlf42red.mmpz | Bin 51694 -> 51679 bytes .../CapDan-ZeroSumGame-OriginalByZakarra.mmpz | Bin 43076 -> 43070 bytes data/projects/demos/EsoXLB-CPU.mmpz | Bin 33938 -> 33924 bytes ...- Krem Kaakkuja (Second Flight Remix).mmpz | Bin 46102 -> 46086 bytes data/projects/demos/Impulslogik-Zen.mmpz | Bin 37337 -> 37190 bytes .../projects/demos/Jousboxx-BuzzerBeater.mmpz | Bin 43351 -> 43235 bytes data/projects/demos/Momo64-esp.mmpz | Bin 55497 -> 55428 bytes data/projects/demos/Namitryus-K-Project.mmpz | Bin 63584 -> 63445 bytes data/projects/demos/Oglsdl-Dr8v2.mmpz | Bin 43206 -> 43069 bytes data/projects/demos/Oglsdl-PpTrip.mmpz | Bin 26086 -> 26015 bytes .../demos/Popsip-Electric Dancer.mmpz | Bin 26235 -> 26220 bytes data/projects/demos/Root84-Initialize.mmpz | Bin 17157 -> 17017 bytes data/projects/demos/Saber-FinalStep.mmpz | Bin 45501 -> 45429 bytes .../demos/Settel-InnerRecreation.mmpz | Bin 35882 -> 35811 bytes .../Shovon-ProgressiveHousePluckDemo.mmpz | Bin 16145 -> 16014 bytes data/projects/demos/Skiessi/Skiessi-C64.mmpz | Bin 5033 -> 5029 bytes .../projects/demos/Skiessi/Skiessi-Onion.mmpz | Bin 26929 -> 26919 bytes .../Skiessi-RandomProjectNumber14253.mmpz | Bin 18142 -> 18137 bytes .../demos/Skiessi/Skiessi-TurningPoint.mmpz | Bin 20851 -> 20844 bytes data/projects/demos/Socceroos-Progress.mmpz | Bin 18487 -> 18425 bytes data/projects/demos/TameAnderson-MakeMe.mmpz | Bin 60674 -> 60609 bytes data/projects/demos/Thaledric-Armageddon.mmpz | Bin 53974 -> 53910 bytes .../demos/Thomasso-AxeFromThe80s.mmpz | Bin 15390 -> 15255 bytes data/projects/demos/TobyDox-Psycho.mmpz | Bin 5856 -> 5787 bytes data/projects/demos/unfa-Spoken.mmpz | Bin 212235 -> 212173 bytes data/projects/shorties/DirtyLove.mmpz | Bin 8660 -> 8595 bytes data/projects/shorties/Root84-TrancyLoop.mmpz | Bin 4464 -> 4386 bytes data/projects/shorties/Skiessi-222.mmpz | Bin 4825 -> 4747 bytes data/projects/shorties/Surrender-Main.mmpz | Bin 3176 -> 3110 bytes data/projects/shorties/sv-DnB-Startup.mmpz | Bin 3906 -> 3829 bytes data/projects/shorties/sv-Trance-Startup.mmpz | Bin 3924 -> 3855 bytes 31 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/projects/demos/CapDan/CapDan-TwilightArea-OriginalByAlf42red.mmpz b/data/projects/demos/CapDan/CapDan-TwilightArea-OriginalByAlf42red.mmpz index 1687e8c343327ead0d969653b68a562567aa0c00..b4a00e1ea86f149a9fa998ff8b062971c1c7079c 100644 GIT binary patch delta 17931 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 KfBU~%OgH~O%@C~s diff --git a/data/projects/demos/CapDan/CapDan-ZeroSumGame-OriginalByZakarra.mmpz b/data/projects/demos/CapDan/CapDan-ZeroSumGame-OriginalByZakarra.mmpz index 95a29daff1d211fd6cbb3162656afec54ceb7998..578c4f926d40ebc42e42664c1b72a5c356f25108 100644 GIT binary patch delta 32433 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 Ku#W}DqyGoJ6ParO diff --git a/data/projects/demos/EsoXLB-CPU.mmpz b/data/projects/demos/EsoXLB-CPU.mmpz index 1c2549027e43a6b8323abaa43ef5fc23be978d68..bc2445e8761f703419afd9d899fd64bfbdff937d 100644 GIT binary patch delta 17243 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 delta 18444 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 diff --git a/data/projects/demos/Impulslogik-Zen.mmpz b/data/projects/demos/Impulslogik-Zen.mmpz index 05fa375c49f21628b86169c557b625083f1315d4..774ce89e3d5c0190db819712eddbcc75945e3349 100644 GIT binary patch delta 9767 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 J`+uM_4)h4qK2ZPw delta 640 zcmV-`0)PF4u>;Am0|WpN`m?bFS=N7hc8vxSkA6>Eo(op77};;=ch`>XqzCE%X8?CC zy8Lq4u~u5giUPNPvZH}o#nC`kJN?oRn0~Uy1hsvj)OqIxy9I5{x@E1-x`#gA+t$vW zb{{_<{M$_C5_GCRYI+Vji#)V!`2nVD}XOC)XCQI)9ioz@6TT@9xnd(?0hg2v!^dv-zxtoM-7gPck{<@2dfo2)=0oShDvCCL>2|+=|NiNF z|C1jqyAOBWyZPsjt7z>Gdfk683LGo?WJfl2+8O)ur`d{{+fTDKiOij6{6WjCBJv!4 z=rAiKUKFdL`_n9pUR<&>Nmih1g&}3M`Tshu)A|29-&amVK8739B7dQAkbL|en47sJ!krf2@Izb1cQhg4sG(X>sp z`}{pt?q4X@-xpv^{zA=ZZMuJDZQ308uFYWScpoEw^OxS{`LFxWkA6fAB=?m|7E$ht zAg(3;=HnmH$4BbpzkhT*o2ue3v}V@y!$(?(b{TE7SKXrP`_BD}P-Fk%E<1XzVUuFJ z2CQ8cjjO?5{+;yL|0rz2qltsHfJS#;!2_ aidOA=ORD^?yIzMMcXj>rZ~qV0SAjd2AZB0y diff --git a/data/projects/demos/Namitryus-K-Project.mmpz b/data/projects/demos/Namitryus-K-Project.mmpz index 6d463cbb95497e8233878585ed60e9f1deced5c1..4d62826c9b4d5c78b7e3e34e4d4e8e6d262c575e 100644 GIT binary patch delta 7393 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 CAW!lD diff --git a/data/projects/demos/Oglsdl-PpTrip.mmpz b/data/projects/demos/Oglsdl-PpTrip.mmpz index d1baceeba51d3dd468e5dc5f008a9ac7ad5f7b04..776aeea2bdd76055b17e10357e4fc5a47852a46e 100644 GIT binary patch delta 24682 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 delta 24756 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 T4ARK8Z>$>t{q``Saj@|}aA{W! diff --git a/data/projects/demos/Popsip-Electric Dancer.mmpz b/data/projects/demos/Popsip-Electric Dancer.mmpz index fc93dd64a23dc693793182e58b473cea3afdc069..1c935dbe1264073b6a6acf4ca61e7041ba64a9e8 100644 GIT binary patch delta 1397 zcmV-*1&aFn%mM7o0R#XAI<>I`4Pk#~VJp7XzwH`%+dtQl8%({yx#!M!=VVfF_O{kr zO|!30P3z4uLYsqKB8G_2e~MxoM*bW(Qw(O*r*+P%X;6V%4cHtAv^qvhsE6p`qG{*3lH3S)!erSp^WHnKSymt69TT7*g!)(f_ROsQT z)LY|GU9XN~FPIJ7MYswwy?_EA`)NV7RSQ~<)Ph-wU9_N~TDFaXui%q8G;5=TUP241 zs-`H8BPyO$gRfW(IxngQov(jV4QksPzAz_7SF0PW0hzD{RLUB#(7x9)Lv;3%Gel=! z~77oAY*S2>}K+Mt+R>lojf*3n*cl4xJ$Br%a) z@~4BJe11NS5Q;bDo3one^lnJ9UiYZi+Oyuqtq)b*v5ZEX3qrOznn} zSgPr0I>I6J1`K-CzrKr0ZM%=HnG)q<0T$`G9-MgL}!R^a#oj{TJVRY&u8h%Y1u|uGE~Pv-BL@;HGDNj;`lZYrOQ` zAmlYQ@Yn4&{yYYiSKw^<^XT#o|2)_LoX<1?Nqsf#&ERpSydHn0wjS_we}kdnj%wW0 zRl3s>a$xg|qEJWP6y4U2ei;9c|NZX$$@R(q9{-Q`m+!8B{d79`@%rQW;M324KR)`+UxA7<~`NMdWRdy53o(Abx ze*?cC>Nvi27ykU|`+xt%i)L=%{`+6EuVd)0?-^Sr>(76WY2SWtD$0g{Gx;8-)WV(r zmA7d*aOeIVSdxfS9&Kw`xDl+u<0M>uuPnd(b@TZqOklzkzM6ZtN#To>Q})gAAIb3z zY+8Ol_5(0&@ahz8`!Fm$APT zY9kkJ=|(Q>te5x4eDQtnEtbjCU!Sq@JG6Fu6LHn??G58Qo+8GZeLwgwH(0sR(?Hd} z8>p~h`gUjT2e4rAJ!-Mpp~YX>JZ{y--^|2rZE1fD&gE=64_C9{G+cmd&2`sE4*HvH z^H)8yQ`R$Y8m19^!S@50*j~hsWvb2ol)3nW53u7KoQKn_#AenXYOM`Q%j#?D?iznp z{eHmrBr$AC=8Hu<^uy6SUXK2!n*!{Ai|@n7|JPkC|C*+QGxUln`8t=>x3hxEEjfOU z|Gs6)C1BTe*X4Zn{kPK^SkJ7>de$A?`o`Duc*L;`4s38N<^TL2{w0Ej)@TvGqr{EO zV7_GOWfsTcw_oPVIN2cP^QDsA)pxdclE?WmXBLMIeHjLK(2r@~AFnI=;ots$tIOQq D8QB6Z delta 1412 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 diff --git a/data/projects/demos/Saber-FinalStep.mmpz b/data/projects/demos/Saber-FinalStep.mmpz index 5e3d1d1ffc6a26e339f864341fb0b0e2434d9b40..05a5022a7d47b5c546f0eb6aaefcb8596077081a 100644 GIT binary patch delta 5720 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 Y?>!R2_UyTvO>~k``too8A7bbrZp;IFp#T5? diff --git a/data/projects/demos/Settel-InnerRecreation.mmpz b/data/projects/demos/Settel-InnerRecreation.mmpz index aaa01e0bcfe9f6df594bd08144199a33a020ac2b..78e1d611de830cc14cb3e86ec455a61f09f00658 100644 GIT binary patch delta 30409 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 delta 30388 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 BQ#Swr diff --git a/data/projects/demos/Shovon-ProgressiveHousePluckDemo.mmpz b/data/projects/demos/Shovon-ProgressiveHousePluckDemo.mmpz index 2d31bd7bdc25008624f6eb0b345c4ab635ba05c1..3ec6a2cffde81abf1a6b130e1ea1b8b4c68966ce 100644 GIT binary patch delta 764 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

6-+Nfxx{V%e{K3PY-yh?{$I% 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 Px=ihN9taMpnC<=_t^GU6 diff --git a/data/projects/demos/Skiessi/Skiessi-RandomProjectNumber14253.mmpz b/data/projects/demos/Skiessi/Skiessi-RandomProjectNumber14253.mmpz index d3c6e0f8d0ed65b18121ef34bc63c9ee9943b67f..bc2810567b911f68adbc8518df9fedabaa556e44 100644 GIT binary patch delta 10958 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 diff --git a/data/projects/demos/Skiessi/Skiessi-TurningPoint.mmpz b/data/projects/demos/Skiessi/Skiessi-TurningPoint.mmpz index ee5be4b553e9f3f9ba5cbc36d314bae47ed2fec4..47a0a3672c696a68fdc1f8fed34f0db45548a535 100644 GIT binary patch delta 8601 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 delta 8608 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 BfbakS diff --git a/data/projects/demos/Socceroos-Progress.mmpz b/data/projects/demos/Socceroos-Progress.mmpz index 74ff5774c94e30fd98d03372ae2690386d3fa967..854663787c30cabf73e38ea3e24820a925eeaaeb 100644 GIT binary patch literal 18425 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 literal 18487 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 Y%_3aUC@hlWvAAy9`S}n3AF{)gARr)h4gdfE diff --git a/data/projects/demos/TameAnderson-MakeMe.mmpz b/data/projects/demos/TameAnderson-MakeMe.mmpz index de0152818816daec384ba000e646b9808fa55129..b69a64e3afef4692accfad724b0263f4aa6d099c 100644 GIT binary patch delta 3116 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 Gzhql&g&KYU delta 3181 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 TCKnCU^m-wm%OCy^uo%{&q}D63 diff --git a/data/projects/demos/Thaledric-Armageddon.mmpz b/data/projects/demos/Thaledric-Armageddon.mmpz index 0e9d5f83fa9aa17ad0e6ece235c573a2357bf710..731ea55f7d52d44c3aab8204c082677dd519043b 100644 GIT binary patch delta 676 zcmV;V0$csor303w0|WpK35c--1JHlg{T<e-GDJZ_w>Tap>Cd2Pbx*HW}#4kH!*M&PQVfVk=X^@374>2G22CxO_PO(!=e} zMT(^I<|EoPM-59(_hnL_laGHYmPocH`tM>o?WONe9k47gY z_7FHPTDDYbb4cU;+*-Nb%U}NUD+nFSxBm05>DLhy*I#sf9cQ1P650M`)a`B?AS8c* zI<>OAzoI^^cX(G;xN!aVaj^bNcm4d=?dOLehAM)6<$-yOeGw*9l5co z=Ktsapf3w5ymd679rb^pMSpiQOS_#5dFJKrwA%3ILH>27 zSGk$d`f0-Gf2CQ7tBn`CYtPM6H={KG^SuL2g?f6YqG)+b@*j^idxJN)3+A0M@tp!l%!R6o|zqWAs)6GmT6V?@qEY6EmXX( zE_foTTN0S9SGR7WZu)RKPV`ORXFMdiQ~yH{E;{$Oo!i;@d+2}4c!O>yibL0qKRB@iC$53M{Aetp%6&9eAhxpE@g25V z#^5#^60VCkB6k77@IE3a#~D zwg2}gqmw{>G&(7{_>w+LFib%^`C!Dzm9*Pxc;K+>p1)T zl*slkqi%QG03rDc)Tx!_{T20Ty~De*!iDR?2d;IY#zaHNU**WB7Zc}QvHLYWW(jd@?+RAhCe0R8GnFc zhipNqui^hAnWF(fWBz~s5Bjp8!dssXE?FM*@icF(r#q8wvN!P7QJm)MqQAS@qPCq2 zxjEY<1{mHvDA?;%uW~b^^;6^gUunYUYU9Q3+H*6r9jyT0yK1`?6A-xaDYET>-s1bp` WfYYSotyaHh;NAve!OkdAl& diff --git a/data/projects/demos/Thomasso-AxeFromThe80s.mmpz b/data/projects/demos/Thomasso-AxeFromThe80s.mmpz index af68b8b8b114b9f7a6623f57d6637f27da47e4db..f7056d38c2217b78503cdc2228b41f319f31aad3 100644 GIT binary patch literal 15255 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 literal 15390 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 b7}hq4GUlki>}sDT|Mb}Wf#~?7mhJxmHE|BW diff --git a/data/projects/demos/TobyDox-Psycho.mmpz b/data/projects/demos/TobyDox-Psycho.mmpz index 09ec6b6a6b4bd4ca305cb54bafb657e3854c0258..c4fe7a1c428e416336a6ec65d2189e5ef8dcfcc8 100644 GIT binary patch literal 5787 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
Click to expand
-# paste here
+
 
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index feacb8fdb..f9a0ae192 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,8 +7,12 @@ assignees: '' --- +# Please search the issue tracker for existing feature requests before submitting your own. Delete this line to confirm no similar request has been posted yet. + ### Enhancement Summary #### Justification #### Mockup + + From 35daff9a841b39abea4f30febb4c9d497d87c190 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Tue, 29 Oct 2019 22:42:52 +0100 Subject: [PATCH 067/160] GitHub: Add Discord contact link to template chooser --- .github/ISSUE_TEMPLATE/config.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..2c51f276e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: +- name: Get help on Discord + url: https://lmms.io/chat/ + about: Need help? Have a question? Reach out to other LMMS users on our Discord server! From 200d1c209ce39b1b186a66389b22a9340c51dcdd Mon Sep 17 00:00:00 2001 From: Noah Brecht Date: Sat, 20 Jul 2019 21:03:48 -0400 Subject: [PATCH 068/160] more depricated qt functions --- src/gui/AutomationPatternView.cpp | 2 +- src/gui/widgets/GroupBox.cpp | 2 +- src/tracks/BBTrack.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index 6879b5bbd..a9033f37f 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -366,7 +366,7 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) } p.setRenderHints( QPainter::Antialiasing, false ); - p.resetMatrix(); + p.resetTransform(); // bar lines const int lineSize = 3; diff --git a/src/gui/widgets/GroupBox.cpp b/src/gui/widgets/GroupBox.cpp index 158390bb5..06b8e1c5c 100644 --- a/src/gui/widgets/GroupBox.cpp +++ b/src/gui/widgets/GroupBox.cpp @@ -90,7 +90,7 @@ void GroupBox::paintEvent( QPaintEvent * pe ) p.fillRect( 0, 0, width() - 1, height() - 1, p.background() ); // outer rect - p.setPen( p.background().color().dark( 150 ) ); + p.setPen( p.background().color().darker( 150 ) ); p.drawRect( 0, 0, width() - 1, height() - 1 ); // draw line below titlebar diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index 205a22087..779cd3c74 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -218,8 +218,8 @@ void BBTCOView::paintEvent( QPaintEvent * ) : ( m_bbTCO->m_useStyleColor ? painter.background().color() : m_bbTCO->colorObj() ) ); - lingrad.setColorAt( 0, c.light( 130 ) ); - lingrad.setColorAt( 1, c.light( 70 ) ); + lingrad.setColorAt( 0, c.lighter( 130 ) ); + lingrad.setColorAt( 1, c.lighter( 70 ) ); // paint a black rectangle under the pattern to prevent glitches with transparent backgrounds p.fillRect( rect(), QColor( 0, 0, 0 ) ); From 38f599b6e71f94fdc743c7f9bd2a95aa339aa0da Mon Sep 17 00:00:00 2001 From: Noah Brecht Date: Sat, 20 Jul 2019 21:08:49 -0400 Subject: [PATCH 069/160] setPath rather than operator= for qDir --- src/gui/dialogs/FileDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/dialogs/FileDialog.cpp b/src/gui/dialogs/FileDialog.cpp index 3ce10760a..54cc9d6e4 100644 --- a/src/gui/dialogs/FileDialog.cpp +++ b/src/gui/dialogs/FileDialog.cpp @@ -47,7 +47,7 @@ FileDialog::FileDialog( QWidget *parent, const QString &caption, // Find downloads directory QDir downloadDir( QDir::homePath() + "/Downloads" ); if ( ! downloadDir.exists() ) - downloadDir = QStandardPaths::writableLocation( QStandardPaths::DownloadLocation ); + downloadDir.setPath(QStandardPaths::writableLocation( QStandardPaths::DownloadLocation )); if ( downloadDir.exists() ) urls << QUrl::fromLocalFile( downloadDir.absolutePath() ); From 2c5bf2b9dd75d21c5a059d58de68a270666d533c Mon Sep 17 00:00:00 2001 From: Noah Brecht Date: Thu, 25 Jul 2019 11:01:29 -0400 Subject: [PATCH 070/160] lambdas instead of QSignalMapper --- src/gui/editors/PianoRoll.cpp | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 065234aa4..5d11088f3 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #ifndef __USE_XOPEN #define __USE_XOPEN @@ -204,20 +203,15 @@ PianoRoll::PianoRoll() : m_nemStr.push_back( tr( "Note Velocity" ) ); m_nemStr.push_back( tr( "Note Panning" ) ); - QSignalMapper * signalMapper = new QSignalMapper( this ); m_noteEditMenu = new QMenu( this ); m_noteEditMenu->clear(); for( int i = 0; i < m_nemStr.size(); ++i ) { QAction * act = new QAction( m_nemStr.at(i), this ); - connect( act, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - signalMapper->setMapping( act, i ); + connect( act, &QAction::triggered, [this, i](){ changeNoteEditMode(i); } ); m_noteEditMenu->addAction( act ); } - connect( signalMapper, SIGNAL(mapped(int)), - this, SLOT(changeNoteEditMode(int)) ); - signalMapper = new QSignalMapper( this ); m_semiToneMarkerMenu = new QMenu( this ); QAction* markSemitoneAction = new QAction( tr("Mark/unmark current semitone"), this ); @@ -227,19 +221,12 @@ PianoRoll::PianoRoll() : QAction* unmarkAllAction = new QAction( tr("Unmark all"), this ); QAction* copyAllNotesAction = new QAction( tr("Select all notes on this key"), this); - connect( markSemitoneAction, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - connect( markAllOctaveSemitonesAction, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - connect( markScaleAction, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - connect( markChordAction, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - connect( unmarkAllAction, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - connect( copyAllNotesAction, SIGNAL(triggered()), signalMapper, SLOT(map()) ); - - signalMapper->setMapping( markSemitoneAction, static_cast( stmaMarkCurrentSemiTone ) ); - signalMapper->setMapping( markAllOctaveSemitonesAction, static_cast( stmaMarkAllOctaveSemiTones ) ); - signalMapper->setMapping( markScaleAction, static_cast( stmaMarkCurrentScale ) ); - signalMapper->setMapping( markChordAction, static_cast( stmaMarkCurrentChord ) ); - signalMapper->setMapping( unmarkAllAction, static_cast( stmaUnmarkAll ) ); - signalMapper->setMapping( copyAllNotesAction, static_cast( stmaCopyAllNotesOnKey ) ); + connect( markSemitoneAction, &QAction::triggered, [this](){ markSemiTone(stmaMarkCurrentSemiTone); }); + connect( markAllOctaveSemitonesAction, &QAction::triggered, [this](){ markSemiTone(stmaMarkAllOctaveSemiTones); }); + connect( markScaleAction, &QAction::triggered, [this](){ markSemiTone(stmaMarkCurrentScale); }); + connect( markChordAction, &QAction::triggered, [this](){ markSemiTone(stmaMarkCurrentChord); }); + connect( unmarkAllAction, &QAction::triggered, [this](){ markSemiTone(stmaUnmarkAll); }); + connect( copyAllNotesAction, &QAction::triggered, [this](){ markSemiTone(stmaCopyAllNotesOnKey); }); markScaleAction->setEnabled( false ); markChordAction->setEnabled( false ); @@ -247,8 +234,6 @@ PianoRoll::PianoRoll() : connect( this, SIGNAL(semiToneMarkerMenuScaleSetEnabled(bool)), markScaleAction, SLOT(setEnabled(bool)) ); connect( this, SIGNAL(semiToneMarkerMenuChordSetEnabled(bool)), markChordAction, SLOT(setEnabled(bool)) ); - connect( signalMapper, SIGNAL(mapped(int)), this, SLOT(markSemiTone(int)) ); - m_semiToneMarkerMenu->addAction( markSemitoneAction ); m_semiToneMarkerMenu->addAction( markAllOctaveSemitonesAction ); m_semiToneMarkerMenu->addAction( markScaleAction ); From d9f1383ca9d5b1b2af89c415cab57363a4357d49 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Tue, 27 Aug 2019 15:19:42 +0200 Subject: [PATCH 071/160] Remove remaining usages of QSignalMapper --- include/PianoRoll.h | 1 - include/Track.h | 1 - src/tracks/InstrumentTrack.cpp | 11 +++++------ src/tracks/SampleTrack.cpp | 11 +++++------ 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index f56d791f4..7c2c53d42 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -46,7 +46,6 @@ class QPixmap; class QScrollBar; class QString; class QMenu; -class QSignalMapper; class ComboBox; class NotePlayHandle; diff --git a/include/Track.h b/include/Track.h index 40b32a61a..525699548 100644 --- a/include/Track.h +++ b/include/Track.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 730ab97bd..673996fa2 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1239,6 +1239,7 @@ void InstrumentTrackView::muteChanged() +//FIXME: This is identical to SampleTrackView::createFxMenu QMenu * InstrumentTrackView::createFxMenu(QString title, QString newFxLabel) { int channelIndex = model()->effectChannelModel()->value(); @@ -1253,8 +1254,6 @@ QMenu * InstrumentTrackView::createFxMenu(QString title, QString newFxLabel) QMenu *fxMenu = new QMenu( title ); - QSignalMapper * fxMenuSignalMapper = new QSignalMapper(fxMenu); - fxMenu->addAction( newFxLabel, this, SLOT( createFxLine() ) ); fxMenu->addSeparator(); @@ -1264,14 +1263,14 @@ QMenu * InstrumentTrackView::createFxMenu(QString title, QString newFxLabel) if ( currentChannel != fxChannel ) { + auto index = currentChannel->m_channelIndex; QString label = tr( "FX %1: %2" ).arg( currentChannel->m_channelIndex ).arg( currentChannel->m_name ); - QAction * action = fxMenu->addAction( label, fxMenuSignalMapper, SLOT( map() ) ); - fxMenuSignalMapper->setMapping(action, currentChannel->m_channelIndex); + fxMenu->addAction(label, [this, index](){ + assignFxLine(index); + }); } } - connect(fxMenuSignalMapper, SIGNAL(mapped(int)), this, SLOT(assignFxLine(int))); - return fxMenu; } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 4b51ef6ec..ba04f909b 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -847,6 +847,7 @@ SampleTrackView::~SampleTrackView() +//FIXME: This is identical to InstrumentTrackView::createFxMenu QMenu * SampleTrackView::createFxMenu(QString title, QString newFxLabel) { int channelIndex = model()->effectChannelModel()->value(); @@ -861,8 +862,6 @@ QMenu * SampleTrackView::createFxMenu(QString title, QString newFxLabel) QMenu *fxMenu = new QMenu(title); - QSignalMapper * fxMenuSignalMapper = new QSignalMapper(fxMenu); - fxMenu->addAction(newFxLabel, this, SLOT(createFxLine())); fxMenu->addSeparator(); @@ -872,14 +871,14 @@ QMenu * SampleTrackView::createFxMenu(QString title, QString newFxLabel) if (currentChannel != fxChannel) { + const auto index = currentChannel->m_channelIndex; QString label = tr("FX %1: %2").arg(currentChannel->m_channelIndex).arg(currentChannel->m_name); - QAction * action = fxMenu->addAction(label, fxMenuSignalMapper, SLOT(map())); - fxMenuSignalMapper->setMapping(action, currentChannel->m_channelIndex); + fxMenu->addAction(label, [this, index](){ + assignFxLine(index); + }); } } - connect(fxMenuSignalMapper, SIGNAL(mapped(int)), this, SLOT(assignFxLine(int))); - return fxMenu; } From b5b3b2e6a61d4eb97b6f5016069e56ff820c2b47 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 30 Oct 2019 11:13:18 +0100 Subject: [PATCH 072/160] CMake: Require Qt 5.6+ --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 22e241c92..2f310d27c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,7 +144,7 @@ CHECK_INCLUDE_FILES(locale.h LMMS_HAVE_LOCALE_H) LIST(APPEND CMAKE_PREFIX_PATH "${CMAKE_INSTALL_PREFIX}") -FIND_PACKAGE(Qt5 COMPONENTS Core Gui Widgets Xml REQUIRED) +FIND_PACKAGE(Qt5 5.6.0 COMPONENTS Core Gui Widgets Xml REQUIRED) FIND_PACKAGE(Qt5 COMPONENTS LinguistTools QUIET) INCLUDE_DIRECTORIES( From 400c8d8105b1ed3cb312d45c2c42f7b9b28da1c2 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 30 Oct 2019 12:12:53 +0100 Subject: [PATCH 073/160] Remove Travis MinGW builds We already run them on CircleCI with a newer Qt version. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ab2a8ae85..f94aa0846 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,6 @@ matrix: include: - env: TYPE=style - os: linux - - env: TARGET_OS=win32 - - env: TARGET_OS=win64 - env: TARGET_OS=debian-sid TARGET_DEPLOY=True git: depth: false From 9cb1f8e784b5000654f49504bae717ce1828f34c Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 31 Oct 2019 14:37:40 +0900 Subject: [PATCH 074/160] Change the docker tag for CircleCI Linux builds In response to https://github.com/LMMS/lmms-ci-docker/pull/7 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c1a8ea7ff..0dc146486 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -113,7 +113,7 @@ jobs: - *save_cache linux.gcc: docker: - - image: lmmsci/linux.gcc:xenial + - image: lmmsci/linux.gcc:16.04 environment: <<: *common_environment steps: From 488c4ea6034da306ee14932131c4d6536bfbf7af Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 31 Oct 2019 15:14:52 +0900 Subject: [PATCH 075/160] CircleCI: support Qt from a PPA on /opt --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0dc146486..0ab2c51a0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -122,7 +122,10 @@ jobs: - *restore_cache - run: name: Configure - command: mkdir build && cd build && cmake .. $CMAKE_OPTS -DCMAKE_INSTALL_PREFIX=./install + command: | + source /opt/qt5*/bin/qt5*-env.sh || true + mkdir build && cd build + cmake .. $CMAKE_OPTS -DCMAKE_INSTALL_PREFIX=./install - run: name: Build command: cd build && make From 53e6b645c8e550156aa312a1b8b0d4b085bf7eac Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Thu, 31 Oct 2019 15:13:25 +0100 Subject: [PATCH 076/160] Use "bar" instead of "tact" Closes #4865 --- include/AutomationEditor.h | 6 +- include/BBTrackContainer.h | 4 +- include/MidiTime.h | 36 +++--- include/PianoRoll.h | 2 +- include/Song.h | 26 ++--- include/StepRecorderWidget.h | 6 +- include/TimeLineWidget.h | 18 +-- include/Track.h | 8 +- include/TrackContainerView.h | 22 ++-- include/lmms_basics.h | 2 +- plugins/MidiImport/MidiImport.cpp | 14 +-- plugins/carlabase/carla.cpp | 4 +- src/core/AutomationPattern.cpp | 2 +- src/core/BBTrackContainer.cpp | 25 ++-- src/core/Engine.cpp | 8 +- src/core/Mixer.cpp | 8 +- src/core/Song.cpp | 44 +++---- src/core/Track.cpp | 138 +++++++++++----------- src/core/TrackContainer.cpp | 7 +- src/core/midi/MidiTime.cpp | 52 ++++----- src/gui/AutomationPatternView.cpp | 12 +- src/gui/TimeLineWidget.cpp | 30 ++--- src/gui/TrackContainerView.cpp | 6 +- src/gui/editors/AutomationEditor.cpp | 94 +++++++-------- src/gui/editors/PianoRoll.cpp | 142 +++++++++++------------ src/gui/editors/SongEditor.cpp | 42 +++---- src/gui/widgets/StepRecorderWidget.cpp | 10 +- src/gui/widgets/TimeDisplayWidget.cpp | 10 +- src/tracks/AutomationTrack.cpp | 6 +- src/tracks/BBTrack.cpp | 10 +- src/tracks/InstrumentTrack.cpp | 2 +- src/tracks/Pattern.cpp | 64 +++++----- src/tracks/SampleTrack.cpp | 18 +-- tests/src/tracks/AutomationTrackTest.cpp | 4 +- 34 files changed, 439 insertions(+), 443 deletions(-) diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 968bb34be..e39eaddd7 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -176,8 +176,8 @@ private: static const int TOP_MARGIN = 16; static const int DEFAULT_Y_DELTA = 6; - static const int DEFAULT_STEPS_PER_TACT = 16; - static const int DEFAULT_PPT = 12 * DEFAULT_STEPS_PER_TACT; + static const int DEFAULT_STEPS_PER_BAR = 16; + static const int DEFAULT_PPB = 12 * DEFAULT_STEPS_PER_BAR; static const int VALUES_WIDTH = 64; @@ -230,7 +230,7 @@ private: float m_drawLastLevel; tick_t m_drawLastTick; - int m_ppt; + int m_ppb; int m_y_delta; bool m_y_auto; diff --git a/include/BBTrackContainer.h b/include/BBTrackContainer.h index 236325157..e4895aa87 100644 --- a/include/BBTrackContainer.h +++ b/include/BBTrackContainer.h @@ -48,8 +48,8 @@ public: return "bbtrackcontainer"; } - tact_t lengthOfBB( int _bb ) const; - inline tact_t lengthOfCurrentBB() + bar_t lengthOfBB( int _bb ) const; + inline bar_t lengthOfCurrentBB() { return lengthOfBB( currentBB() ); } diff --git a/include/MidiTime.h b/include/MidiTime.h index 0e8015e04..952b4b6d5 100644 --- a/include/MidiTime.h +++ b/include/MidiTime.h @@ -32,10 +32,10 @@ #include "lmms_export.h" #include "lmms_basics.h" -// note: 1 "Tact" = 1 Measure -const int DefaultTicksPerTact = 192; -const int DefaultStepsPerTact = 16; -const int DefaultBeatsPerTact = DefaultTicksPerTact / DefaultStepsPerTact; +// note: a bar was erroneously called "tact" in older versions of LMMS +const int DefaultTicksPerBar = 192; +const int DefaultStepsPerBar = 16; +const int DefaultBeatsPerBar = DefaultTicksPerBar / DefaultStepsPerBar; class MeterModel; @@ -60,19 +60,19 @@ private: class LMMS_EXPORT MidiTime { public: - MidiTime( const tact_t tact, const tick_t ticks ); + MidiTime( const bar_t bar, const tick_t ticks ); MidiTime( const tick_t ticks = 0 ); MidiTime quantize(float) const; - MidiTime toAbsoluteTact() const; + MidiTime toAbsoluteBar() const; MidiTime& operator+=( const MidiTime& time ); MidiTime& operator-=( const MidiTime& time ); - // return the tact, rounded down and 0-based - tact_t getTact() const; - // return the tact, rounded up and 0-based - tact_t nextFullTact() const; + // return the bar, rounded down and 0-based + bar_t getBar() const; + // return the bar, rounded up and 0-based + bar_t nextFullBar() const; void setTicks( tick_t ticks ); tick_t getTicks() const; @@ -90,21 +90,21 @@ public: // calculate number of frame that are needed this time f_cnt_t frames( const float framesPerTick ) const; - double getTimeInMilliseconds(bpm_t beatsPerMinute) const; + double getTimeInMilliseconds( bpm_t beatsPerMinute ) const; static MidiTime fromFrames( const f_cnt_t frames, const float framesPerTick ); - static tick_t ticksPerTact(); - static tick_t ticksPerTact( const TimeSig &sig ); - static int stepsPerTact(); - static void setTicksPerTact( tick_t tpt ); + static tick_t ticksPerBar(); + static tick_t ticksPerBar( const TimeSig &sig ); + static int stepsPerBar(); + static void setTicksPerBar( tick_t tpt ); static MidiTime stepPosition( int step ); - static double ticksToMilliseconds(tick_t ticks, bpm_t beatsPerMinute); - static double ticksToMilliseconds(double ticks, bpm_t beatsPerMinute); + static double ticksToMilliseconds( tick_t ticks, bpm_t beatsPerMinute ); + static double ticksToMilliseconds( double ticks, bpm_t beatsPerMinute ); private: tick_t m_ticks; - static tick_t s_ticksPerTact; + static tick_t s_ticksPerBar; } ; diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 7c2c53d42..b600385c8 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -382,7 +382,7 @@ private: int m_oldNotesEditHeight; int m_notesEditHeight; - int m_ppt; // pixels per tact + int m_ppb; // pixels per bar int m_totalKeysToScroll; // remember these values to use them diff --git a/include/Song.h b/include/Song.h index 32ead7181..1e047caaa 100644 --- a/include/Song.h +++ b/include/Song.h @@ -45,7 +45,7 @@ class TimeLineWidget; const bpm_t MinTempo = 10; const bpm_t DefaultTempo = 140; const bpm_t MaxTempo = 999; -const tick_t MaxSongLength = 9999 * DefaultTicksPerTact; +const tick_t MaxSongLength = 9999 * DefaultTicksPerBar; class LMMS_EXPORT Song : public TrackContainer @@ -155,14 +155,14 @@ public: m_playPos[playMode].setTicks(ticks); } - inline int getTacts() const + inline int getBars() const { - return currentTact(); + return currentBar(); } - inline int ticksPerTact() const + inline int ticksPerBar() const { - return MidiTime::ticksPerTact(m_timeSigModel); + return MidiTime::ticksPerBar(m_timeSigModel); } // Returns the beat position inside the bar, 0-based @@ -254,7 +254,7 @@ public: } void updateLength(); - tact_t length() const + bar_t length() const { return m_length; } @@ -382,9 +382,9 @@ private: virtual ~Song(); - inline tact_t currentTact() const + inline bar_t currentBar() const { - return m_playPos[m_playMode].getTact(); + return m_playPos[m_playMode].getBar(); } inline tick_t currentTick() const @@ -415,7 +415,7 @@ private: IntModel m_tempoModel; MeterModel m_timeSigModel; - int m_oldTicksPerTact; + int m_oldTicksPerBar; IntModel m_masterVolumeModel; IntModel m_masterPitchModel; @@ -445,14 +445,14 @@ private: PlayModes m_playMode; PlayPos m_playPos[Mode_Count]; - tact_t m_length; + bar_t m_length; const Pattern* m_patternToPlay; bool m_loopPattern; double m_elapsedMilliSeconds[Mode_Count]; tick_t m_elapsedTicks; - tact_t m_elapsedTacts; + bar_t m_elapsedBars; VstSyncController m_vstSyncController; @@ -473,9 +473,9 @@ signals: void projectLoaded(); void playbackStateChanged(); void playbackPositionChanged(); - void lengthChanged( int tacts ); + void lengthChanged( int bars ); void tempoChanged( bpm_t newBPM ); - void timeSignatureChanged( int oldTicksPerTact, int ticksPerTact ); + void timeSignatureChanged( int oldTicksPerBar, int ticksPerBar ); void controllerAdded( Controller * ); void controllerRemoved( Controller * ); void updateSampleTracks(); diff --git a/include/StepRecorderWidget.h b/include/StepRecorderWidget.h index 0e4512169..cafce2b24 100644 --- a/include/StepRecorderWidget.h +++ b/include/StepRecorderWidget.h @@ -36,14 +36,14 @@ class StepRecorderWidget : public QWidget public: StepRecorderWidget( QWidget * parent, - const int ppt, + const int ppb, const int marginTop, const int marginBottom, const int marginLeft, const int marginRight); //API used by PianoRoll - void setPixelsPerTact(int ppt); + void setPixelsPerBar(int ppb); void setCurrentPosition(MidiTime currentPosition); void setBottomMargin(const int marginBottom); @@ -68,7 +68,7 @@ private: MidiTime m_curStepStartPos; MidiTime m_curStepEndPos; - int m_ppt; // pixels per tact + int m_ppb; // pixels per bar MidiTime m_currentPosition; // current position showed by on PianoRoll QColor m_colorLineStart; diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 7629694ca..374a26418 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -72,15 +72,15 @@ public: } ; - TimeLineWidget(int xoff, int yoff, float ppt, Song::PlayPos & pos, + TimeLineWidget(int xoff, int yoff, float ppb, Song::PlayPos & pos, const MidiTime & begin, Song::PlayModes mode, QWidget * parent); virtual ~TimeLineWidget(); inline QColor const & getBarLineColor() const { return m_barLineColor; } - inline void setBarLineColor(QColor const & tactLineColor) { m_barLineColor = tactLineColor; } + inline void setBarLineColor(QColor const & barLineColor) { m_barLineColor = barLineColor; } inline QColor const & getBarNumberColor() const { return m_barNumberColor; } - inline void setBarNumberColor(QColor const & tactNumberColor) { m_barNumberColor = tactNumberColor; } + inline void setBarNumberColor(QColor const & barNumberColor) { m_barNumberColor = barNumberColor; } inline QColor const & getInactiveLoopColor() const { return m_inactiveLoopColor; } inline void setInactiveLoopColor(QColor const & inactiveLoopColor) { m_inactiveLoopColor = inactiveLoopColor; } @@ -135,18 +135,18 @@ public: m_loopPos[0] : m_loopPos[1]; } - inline void savePos( const MidiTime & _pos ) + inline void savePos( const MidiTime & pos ) { - m_savedPos = _pos; + m_savedPos = pos; } inline const MidiTime & savedPos() const { return m_savedPos; } - inline void setPixelsPerTact( float _ppt ) + inline void setPixelsPerBar( float ppb ) { - m_ppt = _ppt; + m_ppb = ppb; update(); } @@ -163,7 +163,7 @@ public: inline int markerX( const MidiTime & _t ) const { return m_xOffset + static_cast( ( _t - m_begin ) * - m_ppt / MidiTime::ticksPerTact() ); + m_ppb / MidiTime::ticksPerBar() ); } signals: @@ -214,7 +214,7 @@ private: int m_xOffset; int m_posMarkerX; - float m_ppt; + float m_ppb; Song::PlayPos & m_pos; const MidiTime & m_begin; const Song::PlayModes m_mode; diff --git a/include/Track.h b/include/Track.h index 525699548..4adf6378e 100644 --- a/include/Track.h +++ b/include/Track.h @@ -266,7 +266,7 @@ protected: selectableObject::resizeEvent( re ); } - float pixelsPerTact(); + float pixelsPerBar(); DataFile createTCODataFiles(const QVector & tcos) const; @@ -547,10 +547,10 @@ public: void createTCOsForBB( int bb ); - void insertTact( const MidiTime & pos ); - void removeTact( const MidiTime & pos ); + void insertBar( const MidiTime & pos ); + void removeBar( const MidiTime & pos ); - tact_t length() const; + bar_t length() const; inline TrackContainer* trackContainer() const diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index 094cbfff0..cf79a0a21 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -54,25 +54,25 @@ public: QScrollArea * contentWidget() { - return( m_scrollArea ); + return m_scrollArea; } inline const MidiTime & currentPosition() const { - return( m_currentPosition ); + return m_currentPosition; } virtual bool fixedTCOs() const { - return( false ); + return false; } - inline float pixelsPerTact() const + inline float pixelsPerBar() const { - return( m_ppt ); + return m_ppb; } - void setPixelsPerTact( int _ppt ); + void setPixelsPerBar( int ppb ); const TrackView * trackViewAt( const int _y ) const; @@ -80,12 +80,12 @@ public: inline bool rubberBandActive() const { - return( m_rubberBand->isEnabled() && m_rubberBand->isVisible() ); + return m_rubberBand->isEnabled() && m_rubberBand->isVisible(); } inline QVector selectedObjects() { - return( m_rubberBand->selectedObjects() ); + return m_rubberBand->selectedObjects(); } @@ -118,7 +118,7 @@ public: virtual QString nodeName() const { - return( "trackcontainerview" ); + return "trackcontainerview"; } @@ -139,7 +139,7 @@ public slots: protected: - static const int DEFAULT_PIXELS_PER_TACT = 16; + static const int DEFAULT_PIXELS_PER_BAR = 16; virtual void resizeEvent( QResizeEvent * ); @@ -176,7 +176,7 @@ private: scrollArea * m_scrollArea; QVBoxLayout * m_scrollLayout; - float m_ppt; + float m_ppb; RubberBand * m_rubberBand; diff --git a/include/lmms_basics.h b/include/lmms_basics.h index cca04e97d..961810856 100644 --- a/include/lmms_basics.h +++ b/include/lmms_basics.h @@ -35,7 +35,7 @@ #endif -typedef int32_t tact_t; +typedef int32_t bar_t; typedef int32_t tick_t; typedef uint8_t volume_t; typedef int8_t panning_t; diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index b8653fe18..b3d01e790 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -188,9 +188,9 @@ public: smfMidiCC & putValue( MidiTime time, AutomatableModel * objModel, float value ) { - if( !ap || time > lastPos + DefaultTicksPerTact ) + if( !ap || time > lastPos + DefaultTicksPerBar ) { - MidiTime pPos = MidiTime( time.getTact(), 0 ); + MidiTime pPos = MidiTime( time.getBar(), 0 ); ap = dynamic_cast( at->createTCO(0) ); ap->movePosition( pPos ); @@ -200,7 +200,7 @@ public: lastPos = time; time = time - ap->startPosition(); ap->putValue( time, value, false ); - ap->changeLength( MidiTime( time.getTact() + 1, 0 ) ); + ap->changeLength( MidiTime( time.getBar() + 1, 0 ) ); return *this; } @@ -267,9 +267,9 @@ public: void addNote( Note & n ) { - if( !p || n.pos() > lastEnd + DefaultTicksPerTact ) + if( !p || n.pos() > lastEnd + DefaultTicksPerBar ) { - MidiTime pPos = MidiTime( n.pos().getTact(), 0 ); + MidiTime pPos = MidiTime( n.pos().getBar(), 0 ); p = dynamic_cast( it->createTCO( 0 ) ); p->movePosition( pPos ); } @@ -325,8 +325,8 @@ bool MidiImport::readSMF( TrackContainer* tc ) timeSigDenominatorPat->addObject(&timeSigMM.denominatorModel()); // TODO: adjust these to Time.Sig changes - double beatsPerTact = 4; - double ticksPerBeat = DefaultTicksPerTact / beatsPerTact; + double beatsPerBar = 4; + double ticksPerBeat = DefaultTicksPerBar / beatsPerBar; // Time-sig changes Alg_time_sigs * timeSigs = &seq->time_sig; diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index 3df97ce40..ba2dd085c 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -324,10 +324,10 @@ void CarlaInstrument::play(sampleFrame* workingBuffer) fTimeInfo.playing = s->isPlaying(); fTimeInfo.frame = s->getPlayPos(s->playMode()).frames(Engine::framesPerTick()); fTimeInfo.usecs = s->getMilliseconds()*1000; - fTimeInfo.bbt.bar = s->getTacts() + 1; + fTimeInfo.bbt.bar = s->getBars() + 1; fTimeInfo.bbt.beat = s->getBeat() + 1; fTimeInfo.bbt.tick = s->getBeatTicks(); - fTimeInfo.bbt.barStartTick = ticksPerBeat*s->getTimeSigModel().getNumerator()*s->getTacts(); + fTimeInfo.bbt.barStartTick = ticksPerBeat*s->getTimeSigModel().getNumerator()*s->getBars(); fTimeInfo.bbt.beatsPerBar = s->getTimeSigModel().getNumerator(); fTimeInfo.bbt.beatType = s->getTimeSigModel().getDenominator(); fTimeInfo.bbt.ticksPerBeat = ticksPerBeat; diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index e36838d80..b38c704ef 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -180,7 +180,7 @@ MidiTime AutomationPattern::timeMapLength() const { if( m_timeMap.isEmpty() ) return 0; timeMap::const_iterator it = m_timeMap.end(); - return MidiTime( MidiTime( (it-1).key() ).nextFullTact(), 0 ); + return MidiTime( MidiTime( (it-1).key() ).nextFullBar(), 0 ); } diff --git a/src/core/BBTrackContainer.cpp b/src/core/BBTrackContainer.cpp index e349c7b02..ac4b6cb1a 100644 --- a/src/core/BBTrackContainer.cpp +++ b/src/core/BBTrackContainer.cpp @@ -62,7 +62,7 @@ bool BBTrackContainer::play( MidiTime _start, fpp_t _frames, return false; } - _start = _start % ( lengthOfBB( _tco_num ) * MidiTime::ticksPerTact() ); + _start = _start % ( lengthOfBB( _tco_num ) * MidiTime::ticksPerBar() ); TrackList tl = tracks(); for( TrackList::iterator it = tl.begin(); it != tl.end(); ++it ) @@ -90,9 +90,9 @@ void BBTrackContainer::updateAfterTrackAdd() -tact_t BBTrackContainer::lengthOfBB( int _bb ) const +bar_t BBTrackContainer::lengthOfBB( int _bb ) const { - MidiTime max_length = MidiTime::ticksPerTact(); + MidiTime max_length = MidiTime::ticksPerBar(); const TrackList & tl = tracks(); for (Track* t : tl) @@ -104,7 +104,7 @@ tact_t BBTrackContainer::lengthOfBB( int _bb ) const } } - return max_length.nextFullTact(); + return max_length.nextFullBar(); } @@ -124,7 +124,7 @@ void BBTrackContainer::removeBB( int _bb ) for( TrackList::iterator it = tl.begin(); it != tl.end(); ++it ) { delete ( *it )->getTCO( _bb ); - ( *it )->removeTact( _bb * DefaultTicksPerTact ); + ( *it )->removeBar( _bb * DefaultTicksPerBar ); } if( _bb <= currentBB() ) { @@ -151,7 +151,7 @@ void BBTrackContainer::swapBB( int _bb1, int _bb2 ) void BBTrackContainer::updateBBTrack( TrackContentObject * _tco ) { BBTrack * t = BBTrack::findBBTrack( _tco->startPosition() / - DefaultTicksPerTact ); + DefaultTicksPerBar ); if( t != NULL ) { t->dataChanged(); @@ -247,16 +247,13 @@ AutomatedValueMap BBTrackContainer::automatedValuesAt(MidiTime time, int tcoNum) Q_ASSERT(tcoNum >= 0); Q_ASSERT(time.getTicks() >= 0); - auto length_tacts = lengthOfBB(tcoNum); - auto length_ticks = length_tacts * MidiTime::ticksPerTact(); - if (time > length_ticks) { + auto length_bars = lengthOfBB(tcoNum); + auto length_ticks = length_bars * MidiTime::ticksPerBar(); + if (time > length_ticks) + { time = length_ticks; } - return TrackContainer::automatedValuesAt(time + (MidiTime::ticksPerTact() * tcoNum), tcoNum); + return TrackContainer::automatedValuesAt(time + (MidiTime::ticksPerBar() * tcoNum), tcoNum); } - - - - diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index d2b4a9cc2..ce82310fa 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -105,10 +105,10 @@ void LmmsCore::destroy() delete ConfigManager::inst(); } -float LmmsCore::framesPerTick(sample_rate_t sample_rate) +float LmmsCore::framesPerTick(sample_rate_t sampleRate) { - return sample_rate * 60.0f * 4 / - DefaultTicksPerTact / s_song->getTempo(); + return sampleRate * 60.0f * 4 / + DefaultTicksPerBar / s_song->getTempo(); } @@ -117,7 +117,7 @@ float LmmsCore::framesPerTick(sample_rate_t sample_rate) void LmmsCore::updateFramesPerTick() { s_framesPerTick = s_mixer->processingSampleRate() * 60.0f * 4 / - DefaultTicksPerTact / s_song->getTempo(); + DefaultTicksPerBar / s_song->getTempo(); } diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index fd44b0459..3f22a22e1 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -361,12 +361,12 @@ const surroundSampleFrame * Mixer::renderNextBuffer() // Stop crash with metronome if empty project Engine::getSong()->countTracks() ) { - tick_t ticksPerTact = MidiTime::ticksPerTact(); - if ( p.getTicks() % (ticksPerTact / 1 ) == 0 ) + tick_t ticksPerBar = MidiTime::ticksPerBar(); + if ( p.getTicks() % ( ticksPerBar / 1 ) == 0 ) { addPlayHandle( new SamplePlayHandle( "misc/metronome02.ogg" ) ); } - else if ( p.getTicks() % (ticksPerTact / + else if ( p.getTicks() % ( ticksPerBar / song->getTimeSigModel().getNumerator() ) == 0 ) { addPlayHandle( new SamplePlayHandle( "misc/metronome01.ogg" ) ); @@ -1288,5 +1288,3 @@ void Mixer::fifoWriter::write( surroundSampleFrame * buffer ) m_mixer->m_doChangesMutex.unlock(); } - - diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 336aa3df2..78c9f422a 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -55,7 +55,7 @@ #include "PeakController.h" -tick_t MidiTime::s_ticksPerTact = DefaultTicksPerTact; +tick_t MidiTime::s_ticksPerBar = DefaultTicksPerBar; @@ -66,7 +66,7 @@ Song::Song() : this ) ) ), m_tempoModel( DefaultTempo, MinTempo, MaxTempo, this, tr( "Tempo" ) ), m_timeSigModel( this ), - m_oldTicksPerTact( DefaultTicksPerTact ), + m_oldTicksPerBar( DefaultTicksPerBar ), m_masterVolumeModel( 100, 0, 200, this, tr( "Master volume" ) ), m_masterPitchModel( 0, -12, 12, this, tr( "Master pitch" ) ), m_fileName(), @@ -86,7 +86,7 @@ Song::Song() : m_patternToPlay( NULL ), m_loopPattern( false ), m_elapsedTicks( 0 ), - m_elapsedTacts( 0 ), + m_elapsedBars( 0 ), m_loopRenderCount(1), m_loopRenderRemaining(1) { @@ -162,10 +162,10 @@ void Song::setTempo() void Song::setTimeSignature() { - MidiTime::setTicksPerTact( ticksPerTact() ); - emit timeSignatureChanged( m_oldTicksPerTact, ticksPerTact() ); + MidiTime::setTicksPerBar( ticksPerBar() ); + emit timeSignatureChanged( m_oldTicksPerBar, ticksPerBar() ); emit dataChanged(); - m_oldTicksPerTact = ticksPerTact(); + m_oldTicksPerBar = ticksPerBar(); m_vstSyncController.setTimeSignature( getTimeSigModel().getNumerator(), getTimeSigModel().getDenominator() ); @@ -286,20 +286,20 @@ void Song::processNextBuffer() int ticks = m_playPos[m_playMode].getTicks() + ( int )( currentFrame / framesPerTick ); - // did we play a whole tact? - if( ticks >= MidiTime::ticksPerTact() ) + // did we play a whole bar? + if( ticks >= MidiTime::ticksPerBar() ) { // per default we just continue playing even if // there's no more stuff to play // (song-play-mode) - int maxTact = m_playPos[m_playMode].getTact() + int maxBar = m_playPos[m_playMode].getBar() + 2; - // then decide whether to go over to next tact - // or to loop back to first tact + // then decide whether to go over to next bar + // or to loop back to first bar if( m_playMode == Mode_PlayBB ) { - maxTact = Engine::getBBTrackContainer() + maxBar = Engine::getBBTrackContainer() ->lengthOfCurrentBB(); } else if( m_playMode == Mode_PlayPattern && @@ -307,17 +307,17 @@ void Song::processNextBuffer() tl != NULL && tl->loopPointsEnabled() == false ) { - maxTact = m_patternToPlay->length() - .getTact(); + maxBar = m_patternToPlay->length() + .getBar(); } // end of played object reached? - if( m_playPos[m_playMode].getTact() + 1 - >= maxTact ) + if( m_playPos[m_playMode].getBar() + 1 + >= maxBar ) { // then start from beginning and keep // offset - ticks %= ( maxTact * MidiTime::ticksPerTact() ); + ticks %= ( maxBar * MidiTime::ticksPerBar() ); // wrap milli second counter setToTimeByTicks(ticks); @@ -407,8 +407,8 @@ void Song::processNextBuffer() m_playPos[m_playMode].setCurrentFrame( framesToPlay + currentFrame ); m_elapsedMilliSeconds[m_playMode] += MidiTime::ticksToMilliseconds(framesToPlay / framesPerTick, getTempo()); - m_elapsedTacts = m_playPos[Mode_PlaySong].getTact(); - m_elapsedTicks = ( m_playPos[Mode_PlaySong].getTicks() % ticksPerTact() ) / 48; + m_elapsedBars = m_playPos[Mode_PlaySong].getBar(); + m_elapsedTicks = ( m_playPos[Mode_PlaySong].getTicks() % ticksPerBar() ) / 48; } } @@ -619,7 +619,7 @@ void Song::updateLength() continue; } - const tact_t cur = ( *it )->length(); + const bar_t cur = ( *it )->length(); if( cur > m_length ) { m_length = cur; @@ -805,7 +805,7 @@ void Song::insertBar() for( TrackList::const_iterator it = tracks().begin(); it != tracks().end(); ++it ) { - ( *it )->insertTact( m_playPos[Mode_PlaySong] ); + ( *it )->insertBar( m_playPos[Mode_PlaySong] ); } m_tracksMutex.unlock(); } @@ -819,7 +819,7 @@ void Song::removeBar() for( TrackList::const_iterator it = tracks().begin(); it != tracks().end(); ++it ) { - ( *it )->removeTact( m_playPos[Mode_PlaySong] ); + ( *it )->removeBar( m_playPos[Mode_PlaySong] ); } m_tracksMutex.unlock(); } diff --git a/src/core/Track.cpp b/src/core/Track.cpp index fe66ab4a5..dedfcbc27 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -489,8 +489,8 @@ void TrackContentObjectView::updateLength() else { setFixedWidth( - static_cast( m_tco->length() * pixelsPerTact() / - MidiTime::ticksPerTact() ) + 1 /*+ + static_cast( m_tco->length() * pixelsPerBar() / + MidiTime::ticksPerBar() ) + 1 /*+ TCO_BORDER_WIDTH * 2-1*/ ); } m_trackView->trackContainerView()->update(); @@ -528,6 +528,7 @@ void TrackContentObjectView::dragEnterEvent( QDragEnterEvent * dee ) { TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); MidiTime tcoPos = MidiTime( m_tco->startPosition() ); + if( tcw->canPasteSelection( tcoPos, dee ) == false ) { dee->ignore(); @@ -567,6 +568,7 @@ void TrackContentObjectView::dropEvent( QDropEvent * de ) { TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); MidiTime tcoPos = MidiTime( m_tco->startPosition() ); + if( tcw->pasteSelection( tcoPos, de ) == true ) { de->accept(); @@ -656,7 +658,7 @@ DataFile TrackContentObjectView::createTCODataFiles( // initialTrackIndex is the index of the track that was touched metadata.setAttribute( "initialTrackIndex", initialTrackIndex ); metadata.setAttribute( "trackContainerId", tc->id() ); - // grabbedTCOPos is the pos of the tact containing the TCO we grabbed + // grabbedTCOPos is the pos of the bar containing the TCO we grabbed metadata.setAttribute( "grabbedTCOPos", m_tco->startPosition() ); dataFile.content().appendChild( metadata ); @@ -770,23 +772,23 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) { s_textFloat->setTitle( tr( "Current position" ) ); s_textFloat->setText( QString( "%1:%2" ). - arg( m_tco->startPosition().getTact() + 1 ). + arg( m_tco->startPosition().getBar() + 1 ). arg( m_tco->startPosition().getTicks() % - MidiTime::ticksPerTact() ) ); + MidiTime::ticksPerBar() ) ); } else if( m_action == Resize || m_action == ResizeLeft ) { s_textFloat->setTitle( tr( "Current length" ) ); s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). - arg( m_tco->length().getTact() ). + arg( m_tco->length().getBar() ). arg( m_tco->length().getTicks() % - MidiTime::ticksPerTact() ). - arg( m_tco->startPosition().getTact() + 1 ). + MidiTime::ticksPerBar() ). + arg( m_tco->startPosition().getBar() + 1 ). arg( m_tco->startPosition().getTicks() % - MidiTime::ticksPerTact() ). - arg( m_tco->endPosition().getTact() + 1 ). + MidiTime::ticksPerBar() ). + arg( m_tco->endPosition().getBar() + 1 ). arg( m_tco->endPosition().getTicks() % - MidiTime::ticksPerTact() ) ); + MidiTime::ticksPerBar() ) ); } // s_textFloat->reparent( this ); // setup text-float as if TCO was already moved/resized @@ -893,7 +895,7 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) m_hint = NULL; } - const float ppt = m_trackView->trackContainerView()->pixelsPerTact(); + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); if( m_action == Move ) { MidiTime newPos = draggedTCOPos( me ); @@ -903,9 +905,9 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) m_tco->movePosition( newPos ); m_trackView->getTrackContentWidget()->changePosition(); s_textFloat->setText( QString( "%1:%2" ). - arg( newPos.getTact() + 1 ). + arg( newPos.getBar() + 1 ). arg( newPos.getTicks() % - MidiTime::ticksPerTact() ) ); + MidiTime::ticksPerBar() ) ); s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2 ) ); } else if( m_action == MoveSelection ) @@ -945,12 +947,12 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) const bool unquantized = (me->modifiers() & Qt::ControlModifier) || (me->modifiers() & Qt::AltModifier); const float snapSize = gui->songEditor()->m_editor->getSnapSize(); // Length in ticks of one snap increment - const MidiTime snapLength = MidiTime( (int)(snapSize * MidiTime::ticksPerTact()) ); + const MidiTime snapLength = MidiTime( (int)(snapSize * MidiTime::ticksPerBar()) ); if( m_action == Resize ) { // The clip's new length - MidiTime l = static_cast( me->x() * MidiTime::ticksPerTact() / ppt ); + MidiTime l = static_cast( me->x() * MidiTime::ticksPerBar() / ppb ); if ( unquantized ) { // We want to preserve this adjusted offset, @@ -985,8 +987,8 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x(); MidiTime t = qMax( 0, (int) - m_trackView->trackContainerView()->currentPosition()+ - static_cast( x * MidiTime::ticksPerTact() / ppt ) ); + m_trackView->trackContainerView()->currentPosition() + + static_cast( x * MidiTime::ticksPerBar() / ppb ) ); if( unquantized ) { // We want to preserve this adjusted offset, @@ -1023,15 +1025,15 @@ void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me ) } } s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ). - arg( m_tco->length().getTact() ). + arg( m_tco->length().getBar() ). arg( m_tco->length().getTicks() % - MidiTime::ticksPerTact() ). - arg( m_tco->startPosition().getTact() + 1 ). + MidiTime::ticksPerBar() ). + arg( m_tco->startPosition().getBar() + 1 ). arg( m_tco->startPosition().getTicks() % - MidiTime::ticksPerTact() ). - arg( m_tco->endPosition().getTact() + 1 ). + MidiTime::ticksPerBar() ). + arg( m_tco->endPosition().getBar() + 1 ). arg( m_tco->endPosition().getTicks() % - MidiTime::ticksPerTact() ) ); + MidiTime::ticksPerBar() ) ); s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) ); } else @@ -1127,13 +1129,13 @@ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) -/*! \brief How many pixels a tact (bar) takes for this trackContentObjectView. +/*! \brief How many pixels a bar takes for this trackContentObjectView. * - * \return the number of pixels per tact (bar). + * \return the number of pixels per bar. */ -float TrackContentObjectView::pixelsPerTact() +float TrackContentObjectView::pixelsPerBar() { - return m_trackView->trackContainerView()->pixelsPerTact(); + return m_trackView->trackContainerView()->pixelsPerBar(); } @@ -1181,11 +1183,11 @@ bool TrackContentObjectView::mouseMovedDistance( QMouseEvent * me, int distance */ MidiTime TrackContentObjectView::draggedTCOPos( QMouseEvent * me ) { - //Pixels per tact - const float ppt = m_trackView->trackContainerView()->pixelsPerTact(); + //Pixels per bar + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); // The pixel distance that the mouse has moved const int mouseOff = mapToGlobal(me->pos()).x() - m_initialMouseGlobalPos.x(); - MidiTime newPos = m_initialTCOPos + mouseOff * MidiTime::ticksPerTact() / ppt; + MidiTime newPos = m_initialTCOPos + mouseOff * MidiTime::ticksPerBar() / ppb; MidiTime offset = newPos - m_initialTCOPos; // If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize if ( me->button() != Qt::NoButton @@ -1264,13 +1266,13 @@ TrackContentWidget::~TrackContentWidget() void TrackContentWidget::updateBackground() { - const int tactsPerBar = 4; const TrackContainerView * tcv = m_trackView->trackContainerView(); - // Assume even-pixels-per-tact. Makes sense, should be like this anyways - int ppt = static_cast( tcv->pixelsPerTact() ); + // Assume even-pixels-per-bar. Makes sense, should be like this anyways + int ppb = static_cast( tcv->pixelsPerBar() ); - int w = ppt * tactsPerBar; + // alternate between a darker and a lighter color every 4 bars + int w = ppb * 4; int h = height(); m_background = QPixmap( w * 2, height() ); QPainter pmp( &m_background ); @@ -1281,13 +1283,13 @@ void TrackContentWidget::updateBackground() // draw lines // vertical lines pmp.setPen( QPen( gridColor(), 1 ) ); - for( float x = 0; x < w * 2; x += ppt ) + for( float x = 0; x < w * 2; x += ppb ) { pmp.drawLine( QLineF( x, 0.0, x, h ) ); } pmp.setPen( QPen( embossColor(), 1 ) ); - for( float x = 1.0; x < w * 2; x += ppt ) + for( float x = 1.0; x < w * 2; x += ppb ) { pmp.drawLine( QLineF( x, 0.0, x, h ) ); } @@ -1382,7 +1384,7 @@ void TrackContentWidget::changePosition( const MidiTime & newPos ) it != m_tcoViews.end(); ++it ) { if( ( *it )->getTrackContentObject()-> - startPosition().getTact() == curBB ) + startPosition().getBar() == curBB ) { ( *it )->move( 0, ( *it )->y() ); ( *it )->raise(); @@ -1398,7 +1400,7 @@ void TrackContentWidget::changePosition( const MidiTime & newPos ) it != m_tcoViews.end(); ++it ) { if( ( *it )->getTrackContentObject()-> - startPosition().getTact() != curBB ) + startPosition().getBar() != curBB ) { ( *it )->hide(); } @@ -1415,7 +1417,7 @@ void TrackContentWidget::changePosition( const MidiTime & newPos ) const int begin = pos; const int end = endPosition( pos ); - const float ppt = m_trackView->trackContainerView()->pixelsPerTact(); + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); setUpdatesEnabled( false ); for( tcoViewVector::iterator it = m_tcoViews.begin(); @@ -1432,8 +1434,8 @@ void TrackContentWidget::changePosition( const MidiTime & newPos ) ( te >= begin && te <= end ) || ( ts <= begin && te >= end ) ) { - tcov->move( static_cast( ( ts - begin ) * ppt / - MidiTime::ticksPerTact() ), + tcov->move( static_cast( ( ts - begin ) * ppb / + MidiTime::ticksPerBar() ), tcov->y() ); if( !tcov->isVisible() ) { @@ -1454,7 +1456,7 @@ void TrackContentWidget::changePosition( const MidiTime & newPos ) -/*! \brief Return the position of the trackContentWidget in Tacts. +/*! \brief Return the position of the trackContentWidget in bars. * * \param mouseX the mouse's current X position in pixels. */ @@ -1463,8 +1465,8 @@ MidiTime TrackContentWidget::getPosition( int mouseX ) TrackContainerView * tv = m_trackView->trackContainerView(); return MidiTime( tv->currentPosition() + mouseX * - MidiTime::ticksPerTact() / - static_cast( tv->pixelsPerTact() ) ); + MidiTime::ticksPerBar() / + static_cast( tv->pixelsPerBar() ) ); } @@ -1477,7 +1479,7 @@ MidiTime TrackContentWidget::getPosition( int mouseX ) void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) { MidiTime tcoPos = getPosition( dee->pos().x() ); - if( canPasteSelection( tcoPos, dee ) == false ) + if( canPasteSelection( tcoPos, dee ) == false ) { dee->ignore(); } @@ -1518,7 +1520,7 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d QDomElement metadata = dataFile.content().firstChildElement( "copyMetadata" ); QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" ); MidiTime grabbedTCOPos = tcoPosAttr.value().toInt(); - MidiTime grabbedTCOTact = MidiTime( grabbedTCOPos.getTact(), 0 ); + MidiTime grabbedTCOBar = MidiTime( grabbedTCOPos.getBar(), 0 ); // Extract the track index that was originally clicked QDomAttr tiAttr = metadata.attributeNode( "initialTrackIndex" ); @@ -1528,10 +1530,10 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* d const TrackContainer::TrackList tracks = t->trackContainer()->tracks(); const int currentTrackIndex = tracks.indexOf( t ); - // Don't paste if we're on the same tact + // Don't paste if we're on the same bar auto sourceTrackContainerId = metadata.attributeNode( "trackContainerId" ).value().toUInt(); if( de->source() && sourceTrackContainerId == t->trackContainer()->id() && - tcoPos == grabbedTCOTact && currentTrackIndex == initialTrackIndex ) + tcoPos == grabbedTCOBar && currentTrackIndex == initialTrackIndex ) { return false; } @@ -1596,7 +1598,7 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" ); MidiTime grabbedTCOPos = tcoPosAttr.value().toInt(); - // Snap the mouse position to the beginning of the dropped tact, in ticks + // Snap the mouse position to the beginning of the dropped bar, in ticks const TrackContainer::TrackList tracks = getTrack()->trackContainer()->tracks(); const int currentTrackIndex = tracks.indexOf( getTrack() ); @@ -1631,7 +1633,7 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) // The new position is the old position plus the offset. MidiTime pos = tcoElement.attributeNode( "pos" ).value().toInt() + offset; // If we land on ourselves, offset by one snap - MidiTime shift = MidiTime::ticksPerTact() * gui->songEditor()->m_editor->getSnapSize(); + MidiTime shift = MidiTime::ticksPerBar() * gui->songEditor()->m_editor->getSnapSize(); if (offset == 0) { pos += shift; } TrackContentObject * tco = t->createTCO( pos ); @@ -1695,8 +1697,8 @@ void TrackContentWidget::mousePressEvent( QMouseEvent * me ) so.at( i )->setSelected( false); } getTrack()->addJournalCheckPoint(); - const MidiTime pos = getPosition( me->x() ).getTact() * - MidiTime::ticksPerTact(); + const MidiTime pos = getPosition( me->x() ).getBar() * + MidiTime::ticksPerBar(); TrackContentObject * tco = getTrack()->createTCO( pos ); tco->saveJournallingState( false ); @@ -1714,15 +1716,15 @@ void TrackContentWidget::mousePressEvent( QMouseEvent * me ) */ void TrackContentWidget::paintEvent( QPaintEvent * pe ) { - // Assume even-pixels-per-tact. Makes sense, should be like this anyways + // Assume even-pixels-per-bar. Makes sense, should be like this anyways const TrackContainerView * tcv = m_trackView->trackContainerView(); - int ppt = static_cast( tcv->pixelsPerTact() ); + int ppb = static_cast( tcv->pixelsPerBar() ); QPainter p( this ); // Don't draw background on BB-Editor if( m_trackView->trackContainerView() != gui->getBBEditor()->trackContainerView() ) { p.drawTiledPixmap( rect(), m_background, QPoint( - tcv->currentPosition().getTact() * ppt, 0 ) ); + tcv->currentPosition().getBar() * ppb, 0 ) ); } } @@ -1755,15 +1757,15 @@ Track * TrackContentWidget::getTrack() -/*! \brief Return the end position of the trackContentWidget in Tacts. +/*! \brief Return the end position of the trackContentWidget in Bars. * * \param posStart the starting position of the Widget (from getPosition()) */ MidiTime TrackContentWidget::endPosition( const MidiTime & posStart ) { - const float ppt = m_trackView->trackContainerView()->pixelsPerTact(); + const float ppb = m_trackView->trackContainerView()->pixelsPerBar(); const int w = width(); - return posStart + static_cast( w * MidiTime::ticksPerTact() / ppt ); + return posStart + static_cast( w * MidiTime::ticksPerBar() / ppb ); } @@ -2417,7 +2419,7 @@ TrackContentObject * Track::getTCO( int tcoNum ) } printf( "called Track::getTCO( %d ), " "but TCO %d doesn't exist\n", tcoNum, tcoNum ); - return createTCO( tcoNum * MidiTime::ticksPerTact() ); + return createTCO( tcoNum * MidiTime::ticksPerBar() ); } @@ -2525,17 +2527,17 @@ void Track::createTCOsForBB( int bb ) * in ascending order by TCO time, once we hit a TCO that was earlier * than the insert time, we could fall out of the loop early. */ -void Track::insertTact( const MidiTime & pos ) +void Track::insertBar( const MidiTime & pos ) { // we'll increase the position of every TCO, positioned behind pos, by - // one tact + // one bar for( tcoVector::iterator it = m_trackContentObjects.begin(); it != m_trackContentObjects.end(); ++it ) { if( ( *it )->startPosition() >= pos ) { ( *it )->movePosition( (*it)->startPosition() + - MidiTime::ticksPerTact() ); + MidiTime::ticksPerBar() ); } } } @@ -2547,17 +2549,17 @@ void Track::insertTact( const MidiTime & pos ) * * \param pos The time at which we want to remove the bar. */ -void Track::removeTact( const MidiTime & pos ) +void Track::removeBar( const MidiTime & pos ) { // we'll decrease the position of every TCO, positioned behind pos, by - // one tact + // one bar for( tcoVector::iterator it = m_trackContentObjects.begin(); it != m_trackContentObjects.end(); ++it ) { if( ( *it )->startPosition() >= pos ) { ( *it )->movePosition( qMax( ( *it )->startPosition() - - MidiTime::ticksPerTact(), 0 ) ); + MidiTime::ticksPerBar(), 0 ) ); } } } @@ -2571,7 +2573,7 @@ void Track::removeTact( const MidiTime & pos ) * keeping track of the latest time found in ticks. Then we return * that in bars by dividing by the number of ticks per bar. */ -tact_t Track::length() const +bar_t Track::length() const { // find last end-position tick_t last = 0; @@ -2591,7 +2593,7 @@ tact_t Track::length() const } } - return last / MidiTime::ticksPerTact(); + return last / MidiTime::ticksPerBar(); } diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index edea9aa14..95dd46f29 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -313,7 +313,7 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra MidiTime bbTime = time - tco->startPosition(); bbTime = std::min(bbTime, tco->length()); - bbTime = bbTime % (bbContainer->lengthOfBB(bbIndex) * MidiTime::ticksPerTact()); + bbTime = bbTime % (bbContainer->lengthOfBB(bbIndex) * MidiTime::ticksPerBar()); auto bbValues = bbContainer->automatedValuesAt(bbTime, bbIndex); for (auto it=bbValues.begin(); it != bbValues.end(); it++) @@ -344,8 +344,3 @@ DummyTrackContainer::DummyTrackContainer() : m_dummyInstrumentTrack->setJournalling( false ); } - - - - - diff --git a/src/core/midi/MidiTime.cpp b/src/core/midi/MidiTime.cpp index 82ed642ba..4e718a1d8 100644 --- a/src/core/midi/MidiTime.cpp +++ b/src/core/midi/MidiTime.cpp @@ -53,8 +53,8 @@ int TimeSig::denominator() const -MidiTime::MidiTime( const tact_t tact, const tick_t ticks ) : - m_ticks( tact * s_ticksPerTact + ticks ) +MidiTime::MidiTime( const bar_t bar, const tick_t ticks ) : + m_ticks( bar * s_ticksPerBar + ticks ) { } @@ -66,7 +66,7 @@ MidiTime::MidiTime( const tick_t ticks ) : MidiTime MidiTime::quantize(float bars) const { //The intervals we should snap to, our new position should be a factor of this - int interval = s_ticksPerTact * bars; + int interval = s_ticksPerBar * bars; //The lower position we could snap to int lowPos = m_ticks / interval; //Offset from the lower position @@ -78,9 +78,9 @@ MidiTime MidiTime::quantize(float bars) const } -MidiTime MidiTime::toAbsoluteTact() const +MidiTime MidiTime::toAbsoluteBar() const { - return getTact() * s_ticksPerTact; + return getBar() * s_ticksPerBar; } @@ -98,15 +98,15 @@ MidiTime& MidiTime::operator-=( const MidiTime& time ) } -tact_t MidiTime::getTact() const +bar_t MidiTime::getBar() const { - return m_ticks / s_ticksPerTact; + return m_ticks / s_ticksPerBar; } -tact_t MidiTime::nextFullTact() const +bar_t MidiTime::nextFullBar() const { - return (m_ticks + (s_ticksPerTact-1)) / s_ticksPerTact; + return ( m_ticks + ( s_ticksPerBar - 1 ) ) / s_ticksPerBar; } @@ -131,23 +131,23 @@ MidiTime::operator int() const tick_t MidiTime::ticksPerBeat( const TimeSig &sig ) const { // (number of ticks per bar) divided by (number of beats per bar) - return ticksPerTact(sig) / sig.numerator(); + return ticksPerBar(sig) / sig.numerator(); } tick_t MidiTime::getTickWithinBar( const TimeSig &sig ) const { - return m_ticks % ticksPerTact(sig); + return m_ticks % ticksPerBar( sig ); } tick_t MidiTime::getBeatWithinBar( const TimeSig &sig ) const { - return getTickWithinBar(sig) / ticksPerBeat(sig); + return getTickWithinBar( sig ) / ticksPerBeat( sig ); } tick_t MidiTime::getTickWithinBeat( const TimeSig &sig ) const { - return getTickWithinBar(sig) % ticksPerBeat(sig); + return getTickWithinBar( sig ) % ticksPerBeat( sig ); } @@ -160,9 +160,9 @@ f_cnt_t MidiTime::frames( const float framesPerTick ) const return 0; } -double MidiTime::getTimeInMilliseconds(bpm_t beatsPerMinute) const +double MidiTime::getTimeInMilliseconds( bpm_t beatsPerMinute ) const { - return ticksToMilliseconds(getTicks(), beatsPerMinute); + return ticksToMilliseconds( getTicks(), beatsPerMinute ); } MidiTime MidiTime::fromFrames( const f_cnt_t frames, const float framesPerTick ) @@ -171,39 +171,39 @@ MidiTime MidiTime::fromFrames( const f_cnt_t frames, const float framesPerTick ) } -tick_t MidiTime::ticksPerTact() +tick_t MidiTime::ticksPerBar() { - return s_ticksPerTact; + return s_ticksPerBar; } -tick_t MidiTime::ticksPerTact( const TimeSig &sig ) +tick_t MidiTime::ticksPerBar( const TimeSig &sig ) { - return DefaultTicksPerTact * sig.numerator() / sig.denominator(); + return DefaultTicksPerBar * sig.numerator() / sig.denominator(); } -int MidiTime::stepsPerTact() +int MidiTime::stepsPerBar() { - int steps = ticksPerTact() / DefaultBeatsPerTact; + int steps = ticksPerBar() / DefaultBeatsPerBar; return qMax( 1, steps ); } -void MidiTime::setTicksPerTact( tick_t tpt ) +void MidiTime::setTicksPerBar( tick_t tpb ) { - s_ticksPerTact = tpt; + s_ticksPerBar = tpb; } MidiTime MidiTime::stepPosition( int step ) { - return step * ticksPerTact() / stepsPerTact(); + return step * ticksPerBar() / stepsPerBar(); } -double MidiTime::ticksToMilliseconds(tick_t ticks, bpm_t beatsPerMinute) +double MidiTime::ticksToMilliseconds( tick_t ticks, bpm_t beatsPerMinute ) { - return MidiTime::ticksToMilliseconds(static_cast(ticks), beatsPerMinute); + return MidiTime::ticksToMilliseconds( static_cast(ticks), beatsPerMinute ); } double MidiTime::ticksToMilliseconds(double ticks, bpm_t beatsPerMinute) diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index a9033f37f..130d51a0e 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -278,10 +278,10 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) p.fillRect( rect(), c ); } - const float ppt = fixedTCOs() ? + const float ppb = fixedTCOs() ? ( parentWidget()->width() - 2 * TCO_BORDER_WIDTH ) - / (float) m_pat->timeMapLength().getTact() : - pixelsPerTact(); + / (float) m_pat->timeMapLength().getBar() : + pixelsPerBar(); const int x_base = TCO_BORDER_WIDTH; @@ -290,7 +290,7 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) const float y_scale = max - min; const float h = ( height() - 2 * TCO_BORDER_WIDTH ) / y_scale; - const float ppTick = ppt / MidiTime::ticksPerTact(); + const float ppTick = ppb / MidiTime::ticksPerBar(); p.translate( 0.0f, max * height() / y_scale - TCO_BORDER_WIDTH ); p.scale( 1.0f, -h ); @@ -372,9 +372,9 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) const int lineSize = 3; p.setPen( c.darker( 300 ) ); - for( tact_t t = 1; t < width() - TCO_BORDER_WIDTH; ++t ) + for( bar_t t = 1; t < width() - TCO_BORDER_WIDTH; ++t ) { - const int tx = x_base + static_cast( ppt * t ) - 2; + const int tx = x_base + static_cast( ppb * t ) - 2; p.drawLine( tx, TCO_BORDER_WIDTH, tx, TCO_BORDER_WIDTH + lineSize ); p.drawLine( tx, rect().bottom() - ( lineSize + TCO_BORDER_WIDTH ), tx, rect().bottom() - TCO_BORDER_WIDTH ); diff --git a/src/gui/TimeLineWidget.cpp b/src/gui/TimeLineWidget.cpp index dfa7e388f..bd196de7f 100644 --- a/src/gui/TimeLineWidget.cpp +++ b/src/gui/TimeLineWidget.cpp @@ -42,7 +42,7 @@ QPixmap * TimeLineWidget::s_posMarkerPixmap = NULL; -TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt, +TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppb, Song::PlayPos & pos, const MidiTime & begin, Song::PlayModes mode, QWidget * parent ) : QWidget( parent ), @@ -61,7 +61,7 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt, m_changedPosition( true ), m_xOffset( xoff ), m_posMarkerX( 0 ), - m_ppt( ppt ), + m_ppb( ppb ), m_pos( pos ), m_begin( begin ), m_mode( mode ), @@ -71,7 +71,7 @@ TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppt, m_moveXOff( 0 ) { m_loopPos[0] = 0; - m_loopPos[1] = DefaultTicksPerTact; + m_loopPos[1] = DefaultTicksPerBar; if( s_posMarkerPixmap == NULL ) { @@ -247,18 +247,18 @@ void TimeLineWidget::paintEvent( QPaintEvent * ) QColor const & barLineColor = getBarLineColor(); QColor const & barNumberColor = getBarNumberColor(); - tact_t barNumber = m_begin.getTact(); + bar_t barNumber = m_begin.getBar(); int const x = m_xOffset + s_posMarkerPixmap->width() / 2 - - ( ( static_cast( m_begin * m_ppt ) / MidiTime::ticksPerTact() ) % static_cast( m_ppt ) ); + ( ( static_cast( m_begin * m_ppb ) / MidiTime::ticksPerBar() ) % static_cast( m_ppb ) ); - for( int i = 0; x + i * m_ppt < width(); ++i ) + for( int i = 0; x + i * m_ppb < width(); ++i ) { ++barNumber; if( ( barNumber - 1 ) % qMax( 1, qRound( 1.0f / 3.0f * - MidiTime::ticksPerTact() / m_ppt ) ) == 0 ) + MidiTime::ticksPerBar() / m_ppb ) ) == 0 ) { - const int cx = x + qRound( i * m_ppt ); + const int cx = x + qRound( i * m_ppb ); p.setPen( barLineColor ); p.drawLine( cx, 5, cx, height() - 6 ); @@ -313,7 +313,7 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event ) else if( event->button() == Qt::RightButton ) { m_moveXOff = s_posMarkerPixmap->width() / 2; - const MidiTime t = m_begin + static_cast( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * MidiTime::ticksPerTact() / m_ppt ); + const MidiTime t = m_begin + static_cast( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * MidiTime::ticksPerBar() / m_ppb ); const MidiTime loopMid = ( m_loopPos[0] + m_loopPos[1] ) / 2; if( t < loopMid ) @@ -349,7 +349,7 @@ void TimeLineWidget::mousePressEvent( QMouseEvent* event ) void TimeLineWidget::mouseMoveEvent( QMouseEvent* event ) { parentWidget()->update(); // essential for widgets that this timeline had taken their mouse move event from. - const MidiTime t = m_begin + static_cast( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * MidiTime::ticksPerTact() / m_ppt ); + const MidiTime t = m_begin + static_cast( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * MidiTime::ticksPerBar() / m_ppb ); switch( m_action ) { @@ -387,10 +387,14 @@ void TimeLineWidget::mouseMoveEvent( QMouseEvent* event ) { // Note, swap 1 and 0 below and the behavior "skips" the other // marking instead of pushing it. - if( m_action == MoveLoopBegin ) - m_loopPos[0] -= MidiTime::ticksPerTact(); + if( m_action == MoveLoopBegin ) + { + m_loopPos[0] -= MidiTime::ticksPerBar(); + } else - m_loopPos[1] += MidiTime::ticksPerTact(); + { + m_loopPos[1] += MidiTime::ticksPerBar(); + } } update(); break; diff --git a/src/gui/TrackContainerView.cpp b/src/gui/TrackContainerView.cpp index bc3205730..9b51c76f2 100644 --- a/src/gui/TrackContainerView.cpp +++ b/src/gui/TrackContainerView.cpp @@ -54,7 +54,7 @@ TrackContainerView::TrackContainerView( TrackContainer * _tc ) : m_tc( _tc ), m_trackViews(), m_scrollArea( new scrollArea( this ) ), - m_ppt( DEFAULT_PIXELS_PER_TACT ), + m_ppb( DEFAULT_PIXELS_PER_BAR ), m_rubberBand( new RubberBand( m_scrollArea ) ) { m_tc->setHook( this ); @@ -305,9 +305,9 @@ bool TrackContainerView::allowRubberband() const -void TrackContainerView::setPixelsPerTact( int _ppt ) +void TrackContainerView::setPixelsPerBar( int ppb ) { - m_ppt = _ppt; + m_ppb = ppb; // tell all TrackContentWidgets to update their background tile pixmap for( trackViewList::Iterator it = m_trackViews.begin(); diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index bb00a1bd1..bf56e3039 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -92,7 +92,7 @@ AutomationEditor::AutomationEditor() : m_moveStartTick( 0 ), m_drawLastLevel( 0.0f ), m_drawLastTick( 0 ), - m_ppt( DEFAULT_PPT ), + m_ppb( DEFAULT_PPB ), m_y_delta( DEFAULT_Y_DELTA ), m_y_auto( true ), m_editMode( DRAW ), @@ -149,7 +149,7 @@ AutomationEditor::AutomationEditor() : } // add time-line - m_timeLine = new TimeLineWidget( VALUES_WIDTH, 0, m_ppt, + m_timeLine = new TimeLineWidget( VALUES_WIDTH, 0, m_ppb, Engine::getSong()->getPlayPos( Song::Mode_PlayAutomationPattern ), m_currentPosition, @@ -514,7 +514,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) x -= VALUES_WIDTH; // get tick in which the user clicked - int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt + + int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; // get time map of current pattern @@ -531,7 +531,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) if( pos_ticks >= it.key() && ( it+1==time_map.end() || pos_ticks <= (it+1).key() ) && - ( pos_ticks<= it.key() + MidiTime::ticksPerTact() *4 / m_ppt ) && + ( pos_ticks<= it.key() + MidiTime::ticksPerBar() *4 / m_ppb ) && ( level == it.value() || mouseEvent->button() == Qt::RightButton ) ) { break; @@ -583,7 +583,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) int aligned_x = (int)( (float)( ( it.key() - m_currentPosition ) * - m_ppt ) / MidiTime::ticksPerTact() ); + m_ppb ) / MidiTime::ticksPerBar() ); m_moveXOffset = x - aligned_x - 1; // set move-cursor QCursor c( Qt::SizeAllCursor ); @@ -740,7 +740,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) x -= m_moveXOffset; } - int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt + + int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; if( mouseEvent->buttons() & Qt::LeftButton && m_editMode == DRAW ) { @@ -872,7 +872,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } // get tick in which the cursor is posated - int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt + + int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; m_selectedTick = pos_ticks - m_selectStartTick; @@ -893,7 +893,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) // move selection + selected values // do horizontal move-stuff - int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt + + int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; int ticks_diff = pos_ticks - m_moveStartTick; @@ -918,8 +918,8 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } m_selectStartTick += ticks_diff; - int tact_diff = ticks_diff / MidiTime::ticksPerTact(); - ticks_diff = ticks_diff % MidiTime::ticksPerTact(); + int bar_diff = ticks_diff / MidiTime::ticksPerBar(); + ticks_diff = ticks_diff % MidiTime::ticksPerBar(); // do vertical move-stuff @@ -967,24 +967,24 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) MidiTime new_value_pos; if( it.key() ) { - int value_tact = + int value_bar = ( it.key() / - MidiTime::ticksPerTact() ) - + tact_diff; + MidiTime::ticksPerBar() ) + + bar_diff; int value_ticks = ( it.key() % - MidiTime::ticksPerTact() ) + MidiTime::ticksPerBar() ) + ticks_diff; // ensure value_ticks range - if( value_ticks / MidiTime::ticksPerTact() ) + if( value_ticks / MidiTime::ticksPerBar() ) { - value_tact += value_ticks - / MidiTime::ticksPerTact(); + value_bar += value_ticks + / MidiTime::ticksPerBar(); value_ticks %= - MidiTime::ticksPerTact(); + MidiTime::ticksPerBar(); } m_pattern->removeValue( it.key() ); - new_value_pos = MidiTime( value_tact, + new_value_pos = MidiTime( value_bar, value_ticks ); } new_selValuesForMove[ @@ -1032,7 +1032,7 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) } // get tick in which the cursor is posated - int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt + + int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; m_selectedTick = pos_ticks - @@ -1116,7 +1116,7 @@ inline void AutomationEditor::drawAutomationPoint( QPainter & p, timeMap::iterat { int x = xCoordOfTick( it.key() ); int y = yCoordOfLevel( it.value() ); - const int outerRadius = qBound( 3, ( m_ppt * AutomationPattern::quantization() ) / 576, 5 ); // man, getting this calculation right took forever + const int outerRadius = qBound( 3, ( m_ppb * AutomationPattern::quantization() ) / 576, 5 ); // man, getting this calculation right took forever p.setPen( QPen( vertexColor().lighter( 200 ) ) ); p.setBrush( QBrush( vertexColor() ) ); p.drawEllipse( x - outerRadius, y - outerRadius, outerRadius * 2, outerRadius * 2 ); @@ -1288,20 +1288,20 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) / static_cast( Engine::getSong()->getTimeSigModel().getDenominator() ); float zoomFactor = m_zoomXLevels[m_zoomingXModel.value()]; //the bars which disappears at the left side by scrolling - int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerTact(); + int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerBar(); //iterates the visible bars and draw the shading on uneven bars - for( int x = VALUES_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppt, ++barCount ) + for( int x = VALUES_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppb, ++barCount ) { if( ( barCount + leftBars ) % 2 != 0 ) { - p.fillRect( x - m_currentPosition * zoomFactor / timeSignature, TOP_MARGIN, m_ppt, + p.fillRect( x - m_currentPosition * zoomFactor / timeSignature, TOP_MARGIN, m_ppb, height() - ( SCROLLBAR_SIZE + TOP_MARGIN ), backgroundShade() ); } } // Draw the beat grid - int ticksPerBeat = DefaultTicksPerTact / + int ticksPerBeat = DefaultTicksPerBar / Engine::getSong()->getTimeSigModel().getDenominator(); for( tick = m_currentPosition - m_currentPosition % ticksPerBeat, @@ -1314,10 +1314,10 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) } // and finally bars - for( tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerTact(), + for( tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), x = xCoordOfTick( tick ); x<=width(); - tick += MidiTime::ticksPerTact(), x = xCoordOfTick( tick ) ) + tick += MidiTime::ticksPerBar(), x = xCoordOfTick( tick ) ) { p.setPen( barLineColor() ); p.drawLine( x, grid_bottom, x, x_line_end ); @@ -1452,9 +1452,9 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) } // now draw selection-frame - int x = ( sel_pos_start - m_currentPosition ) * m_ppt / - MidiTime::ticksPerTact(); - int w = ( sel_pos_end - sel_pos_start ) * m_ppt / MidiTime::ticksPerTact(); + int x = ( sel_pos_start - m_currentPosition ) * m_ppb / + MidiTime::ticksPerBar(); + int w = ( sel_pos_end - sel_pos_start ) * m_ppb / MidiTime::ticksPerBar(); int y, h; if( m_y_auto ) { @@ -1526,7 +1526,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) int AutomationEditor::xCoordOfTick(int tick ) { return VALUES_WIDTH + ( ( tick - m_currentPosition ) - * m_ppt / MidiTime::ticksPerTact() ); + * m_ppb / MidiTime::ticksPerBar() ); } @@ -1687,11 +1687,11 @@ void AutomationEditor::wheelEvent(QWheelEvent * we ) } x = qBound( 0, x, m_zoomingXModel.size() - 1 ); - int mouseX = (we->x() - VALUES_WIDTH)* MidiTime::ticksPerTact(); + int mouseX = (we->x() - VALUES_WIDTH)* MidiTime::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used - int ticks = mouseX / m_ppt; + int ticks = mouseX / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x - int newTicks = mouseX / (DEFAULT_PPT * m_zoomXLevels[x]); + int newTicks = mouseX / (DEFAULT_PPB * m_zoomXLevels[x]); // scroll so the tick "selected" by the mouse x doesn't move on the screen m_leftRightScroll->setValue(m_leftRightScroll->value() + ticks - newTicks); @@ -1958,7 +1958,7 @@ void AutomationEditor::getSelectedValues( timeMap & selected_values ) ++it ) { //TODO: Add constant - tick_t len_ticks = MidiTime::ticksPerTact() / 16; + tick_t len_ticks = MidiTime::ticksPerBar() / 16; float level = it.value(); tick_t pos_ticks = it.key(); @@ -2095,17 +2095,17 @@ void AutomationEditor::updatePosition(const MidiTime & t ) m_scrollBack == true ) { const int w = width() - VALUES_WIDTH; - if( t > m_currentPosition + w * MidiTime::ticksPerTact() / m_ppt ) + if( t > m_currentPosition + w * MidiTime::ticksPerBar() / m_ppb ) { - m_leftRightScroll->setValue( t.getTact() * - MidiTime::ticksPerTact() ); + m_leftRightScroll->setValue( t.getBar() * + MidiTime::ticksPerBar() ); } else if( t < m_currentPosition ) { - MidiTime t_ = qMax( t - w * MidiTime::ticksPerTact() * - MidiTime::ticksPerTact() / m_ppt, 0 ); - m_leftRightScroll->setValue( t_.getTact() * - MidiTime::ticksPerTact() ); + MidiTime t_ = qMax( t - w * MidiTime::ticksPerBar() * + MidiTime::ticksPerBar() / m_ppb, 0 ); + m_leftRightScroll->setValue( t_.getBar() * + MidiTime::ticksPerBar() ); } m_scrollBack = false; } @@ -2116,11 +2116,11 @@ void AutomationEditor::updatePosition(const MidiTime & t ) void AutomationEditor::zoomingXChanged() { - m_ppt = m_zoomXLevels[m_zoomingXModel.value()] * DEFAULT_PPT; + m_ppb = m_zoomXLevels[m_zoomingXModel.value()] * DEFAULT_PPB; - assert( m_ppt > 0 ); + assert( m_ppb > 0 ); - m_timeLine->setPixelsPerTact( m_ppt ); + m_timeLine->setPixelsPerBar( m_ppb ); update(); } @@ -2159,9 +2159,9 @@ void AutomationEditor::setQuantization() } else { - quantization = DefaultTicksPerTact; + quantization = DefaultTicksPerBar; } - quantization = DefaultTicksPerTact / quantization; + quantization = DefaultTicksPerBar / quantization; AutomationPattern::setQuantization( quantization ); update(); diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 5d11088f3..183735450 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -139,7 +139,7 @@ PianoRoll::PianoRollKeyTypes PianoRoll::prKeyOrder[] = } ; -const int DEFAULT_PR_PPT = KEY_LINE_HEIGHT * DefaultStepsPerTact; +const int DEFAULT_PR_PPB = KEY_LINE_HEIGHT * DefaultStepsPerBar; const QVector PianoRoll::m_zoomLevels = { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f }; @@ -168,8 +168,8 @@ PianoRoll::PianoRoll() : m_lastMouseY( 0 ), m_oldNotesEditHeight( 100 ), m_notesEditHeight( 100 ), - m_ppt( DEFAULT_PR_PPT ), - m_lenOfNewNotes( MidiTime( 0, DefaultTicksPerTact/4 ) ), + m_ppb( DEFAULT_PR_PPB ), + m_lenOfNewNotes( MidiTime( 0, DefaultTicksPerBar/4 ) ), m_lastNoteVolume( DefaultVolume ), m_lastNotePanning( DefaultPanning ), m_startKey( INITIAL_START_KEY ), @@ -178,7 +178,7 @@ PianoRoll::PianoRoll() : m_ctrlMode( ModeDraw ), m_mouseDownRight( false ), m_scrollBack( false ), - m_stepRecorderWidget(this, DEFAULT_PR_PPT, PR_TOP_MARGIN, PR_BOTTOM_MARGIN + m_notesEditHeight, WHITE_KEY_WIDTH, 0), + m_stepRecorderWidget(this, DEFAULT_PR_PPB, PR_TOP_MARGIN, PR_BOTTOM_MARGIN + m_notesEditHeight, WHITE_KEY_WIDTH, 0), m_stepRecorder(*this, m_stepRecorderWidget), m_barLineColor( 0, 0, 0 ), m_beatLineColor( 0, 0, 0 ), @@ -302,7 +302,7 @@ PianoRoll::PianoRoll() : setAttribute( Qt::WA_OpaquePaintEvent, true ); // add time-line - m_timeLine = new TimeLineWidget( WHITE_KEY_WIDTH, 0, m_ppt, + m_timeLine = new TimeLineWidget( WHITE_KEY_WIDTH, 0, m_ppb, Engine::getSong()->getPlayPos( Song::Mode_PlayPattern ), m_currentPosition, @@ -759,28 +759,28 @@ void PianoRoll::selectRegionFromPixels( int xStart, int xEnd ) xEnd -= WHITE_KEY_WIDTH; // select an area of notes - int pos_ticks = xStart * MidiTime::ticksPerTact() / m_ppt + + int posTicks = xStart * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; - int key_num = 0; - m_selectStartTick = pos_ticks; + int keyNum = 0; + m_selectStartTick = posTicks; m_selectedTick = 0; - m_selectStartKey = key_num; + m_selectStartKey = keyNum; m_selectedKeys = 1; // change size of selection // get tick in which the cursor is posated - pos_ticks = xEnd * MidiTime::ticksPerTact() / m_ppt + + posTicks = xEnd * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; - key_num = 120; + keyNum = 120; - m_selectedTick = pos_ticks - m_selectStartTick; + m_selectedTick = posTicks - m_selectStartTick; if( (int) m_selectStartTick + m_selectedTick < 0 ) { m_selectedTick = -static_cast( m_selectStartTick ); } - m_selectedKeys = key_num - m_selectStartKey; - if( key_num <= m_selectStartKey ) + m_selectedKeys = keyNum - m_selectStartKey; + if( keyNum <= m_selectStartKey ) { --m_selectedKeys; } @@ -1033,7 +1033,7 @@ void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x, for( timeMap::ConstIterator it = map.begin(); it != map.end(); ++it ) { int pos_ticks = it.key(); - int pos_x = _x + pos_ticks * m_ppt / MidiTime::ticksPerTact(); + int pos_x = _x + pos_ticks * m_ppb / MidiTime::ticksPerBar(); const float level = it.value(); @@ -1268,7 +1268,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) // Move selected notes by one bar to the left if (hasValidPattern()) { - shiftPos( direction * MidiTime::ticksPerTact() ); + shiftPos( direction * MidiTime::ticksPerBar() ); } } else if( ke->modifiers() & Qt::ShiftModifier && m_action == ActionNone) @@ -1572,7 +1572,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) x -= WHITE_KEY_WIDTH; // get tick in which the user clicked - int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt + + int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; @@ -1602,7 +1602,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) || ( edit_note && pos_ticks <= note->pos() + - NOTE_EDIT_LINE_WIDTH * MidiTime::ticksPerTact() / m_ppt ) + NOTE_EDIT_LINE_WIDTH * MidiTime::ticksPerBar() / m_ppb ) ) ) { @@ -1746,8 +1746,8 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) // clicked at the "tail" of the note? - if( pos_ticks * m_ppt / MidiTime::ticksPerTact() > - m_currentNote->endPos() * m_ppt / MidiTime::ticksPerTact() - RESIZE_AREA_WIDTH + if( pos_ticks * m_ppb / MidiTime::ticksPerBar() > + m_currentNote->endPos() * m_ppb / MidiTime::ticksPerBar() - RESIZE_AREA_WIDTH && m_currentNote->length() > 0 ) { m_pattern->addJournalCheckPoint(); @@ -1902,10 +1902,10 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) int pixel_range = 4; int x = me->x() - WHITE_KEY_WIDTH; const int ticks_start = ( x-pixel_range/2 ) * - MidiTime::ticksPerTact() / m_ppt + m_currentPosition; + MidiTime::ticksPerBar() / m_ppb + m_currentPosition; const int ticks_end = ( x+pixel_range/2 ) * - MidiTime::ticksPerTact() / m_ppt + m_currentPosition; - const int ticks_middle = x * MidiTime::ticksPerTact() / m_ppt + m_currentPosition; + MidiTime::ticksPerBar() / m_ppb + m_currentPosition; + const int ticks_middle = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; // go through notes to figure out which one we want to change bool altPressed = me->modifiers() & Qt::AltModifier; @@ -2300,9 +2300,9 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // convert to ticks so that we can check which notes // are in the range int ticks_start = ( x-pixel_range/2 ) * - MidiTime::ticksPerTact() / m_ppt + m_currentPosition; + MidiTime::ticksPerBar() / m_ppb + m_currentPosition; int ticks_end = ( x+pixel_range/2 ) * - MidiTime::ticksPerTact() / m_ppt + m_currentPosition; + MidiTime::ticksPerBar() / m_ppb + m_currentPosition; // get note-vector of current pattern const NoteVector & notes = m_pattern->notes(); @@ -2398,8 +2398,8 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // set move- or resize-cursor // get tick in which the cursor is posated - int pos_ticks = ( x * MidiTime::ticksPerTact() ) / - m_ppt + m_currentPosition; + int pos_ticks = ( x * MidiTime::ticksPerBar() ) / + m_ppb + m_currentPosition; // get note-vector of current pattern const NoteVector & notes = m_pattern->notes(); @@ -2431,7 +2431,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) Note *note = *it; // x coordinate of the right edge of the note int noteRightX = ( note->pos() + note->length() - - m_currentPosition) * m_ppt/MidiTime::ticksPerTact(); + m_currentPosition) * m_ppb/MidiTime::ticksPerBar(); // cursor at the "tail" of the note? bool atTail = note->length() > 0 && x > noteRightX - RESIZE_AREA_WIDTH; @@ -2470,7 +2470,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // change size of selection // get tick in which the cursor is posated - int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt + + int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; m_selectedTick = pos_ticks - m_selectStartTick; @@ -2492,7 +2492,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // any key if in erase mode // get tick in which the user clicked - int pos_ticks = x * MidiTime::ticksPerTact() / m_ppt + + int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; @@ -2523,8 +2523,8 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) ( edit_note && pos_ticks <= note->pos() + NOTE_EDIT_LINE_WIDTH * - MidiTime::ticksPerTact() / - m_ppt ) + MidiTime::ticksPerBar() / + m_ppb ) ) ) { @@ -2573,7 +2573,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } // get tick in which the cursor is posated - int pos_ticks = x * MidiTime::ticksPerTact()/ m_ppt + + int pos_ticks = x * MidiTime::ticksPerBar()/ m_ppb + m_currentPosition; m_selectedTick = pos_ticks - @@ -2634,7 +2634,7 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) // convert pixels to ticks and keys int off_x = x - m_moveStartX; - int off_ticks = off_x * MidiTime::ticksPerTact() / m_ppt; + int off_ticks = off_x * MidiTime::ticksPerBar() / m_ppb; int off_key = getKey( y ) - getKey( m_moveStartY ); // handle scroll changes while dragging @@ -2818,10 +2818,10 @@ void PianoRoll::dragNotes( int x, int y, bool alt, bool shift, bool ctrl ) Engine::getSong()->setModified(); } -int PianoRoll::xCoordOfTick(int tick ) +int PianoRoll::xCoordOfTick( int tick ) { return WHITE_KEY_WIDTH + ( ( tick - m_currentPosition ) - * m_ppt / MidiTime::ticksPerTact() ); + * m_ppb / MidiTime::ticksPerBar() ); } void PianoRoll::paintEvent(QPaintEvent * pe ) @@ -3118,20 +3118,20 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) / static_cast( Engine::getSong()->getTimeSigModel().getDenominator() ); float zoomFactor = m_zoomLevels[m_zoomingModel.value()]; //the bars which disappears at the left side by scrolling - int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerTact(); + int leftBars = m_currentPosition * zoomFactor / MidiTime::ticksPerBar(); //iterates the visible bars and draw the shading on uneven bars - for( int x = WHITE_KEY_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppt, ++barCount ) + for( int x = WHITE_KEY_WIDTH, barCount = leftBars; x < width() + m_currentPosition * zoomFactor / timeSignature; x += m_ppb, ++barCount ) { if( ( barCount + leftBars ) % 2 != 0 ) { - p.fillRect( x - m_currentPosition * zoomFactor / timeSignature, PR_TOP_MARGIN, m_ppt, + p.fillRect( x - m_currentPosition * zoomFactor / timeSignature, PR_TOP_MARGIN, m_ppb, height() - ( PR_BOTTOM_MARGIN + PR_TOP_MARGIN ), backgroundShade() ); } } // Draw the vertical beat lines - int ticksPerBeat = DefaultTicksPerTact / + int ticksPerBeat = DefaultTicksPerBar / Engine::getSong()->getTimeSigModel().getDenominator(); for( tick = m_currentPosition - m_currentPosition % ticksPerBeat, @@ -3143,9 +3143,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } // Draw the vertical bar lines - for( tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerTact(), + for( tick = m_currentPosition - m_currentPosition % MidiTime::ticksPerBar(), x = xCoordOfTick( tick ); x <= width(); - tick += MidiTime::ticksPerTact(), x = xCoordOfTick( tick ) ) + tick += MidiTime::ticksPerBar(), x = xCoordOfTick( tick ) ) { p.setPen( barLineColor() ); p.drawLine( x, PR_TOP_MARGIN, x, height() - PR_BOTTOM_MARGIN ); @@ -3217,9 +3217,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) int pos_ticks = note->pos(); - int note_width = len_ticks * m_ppt / MidiTime::ticksPerTact(); + int note_width = len_ticks * m_ppb / MidiTime::ticksPerBar(); const int x = ( pos_ticks - m_currentPosition ) * - m_ppt / MidiTime::ticksPerTact(); + m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) { @@ -3259,9 +3259,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) int pos_ticks = note->pos(); - int note_width = len_ticks * m_ppt / MidiTime::ticksPerTact(); + int note_width = len_ticks * m_ppb / MidiTime::ticksPerBar(); const int x = ( pos_ticks - m_currentPosition ) * - m_ppt / MidiTime::ticksPerTact(); + m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) { @@ -3347,9 +3347,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) int pos_ticks = note->pos(); - int note_width = len_ticks * m_ppt / MidiTime::ticksPerTact(); + int note_width = len_ticks * m_ppb / MidiTime::ticksPerBar(); const int x = ( pos_ticks - m_currentPosition ) * - m_ppt / MidiTime::ticksPerTact(); + m_ppb / MidiTime::ticksPerBar(); // skip this note if not in visible area at all if( !( x + note_width >= 0 && x <= width() - WHITE_KEY_WIDTH ) ) { @@ -3389,10 +3389,10 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) m_notesEditHeight - PR_BOTTOM_MARGIN ); // now draw selection-frame - int x = ( ( sel_pos_start - m_currentPosition ) * m_ppt ) / - MidiTime::ticksPerTact(); - int w = ( ( ( sel_pos_end - m_currentPosition ) * m_ppt ) / - MidiTime::ticksPerTact() ) - x; + int x = ( ( sel_pos_start - m_currentPosition ) * m_ppb ) / + MidiTime::ticksPerBar(); + int w = ( ( ( sel_pos_end - m_currentPosition ) * m_ppb ) / + MidiTime::ticksPerBar() ) - x; int y = (int) y_base - sel_key_start * KEY_LINE_HEIGHT; int h = (int) y_base - sel_key_end * KEY_LINE_HEIGHT - y; p.setPen( selectedNoteColor() ); @@ -3508,9 +3508,9 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) int pixel_range = 8; int x = we->x() - WHITE_KEY_WIDTH; int ticks_start = ( x - pixel_range / 2 ) * - MidiTime::ticksPerTact() / m_ppt + m_currentPosition; + MidiTime::ticksPerBar() / m_ppb + m_currentPosition; int ticks_end = ( x + pixel_range / 2 ) * - MidiTime::ticksPerTact() / m_ppt + m_currentPosition; + MidiTime::ticksPerBar() / m_ppb + m_currentPosition; // When alt is pressed we only edit the note under the cursor bool altPressed = we->modifiers() & Qt::AltModifier; @@ -3611,11 +3611,11 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) } z = qBound( 0, z, m_zoomingModel.size() - 1 ); - int x = (we->x() - WHITE_KEY_WIDTH)* MidiTime::ticksPerTact(); + int x = (we->x() - WHITE_KEY_WIDTH)* MidiTime::ticksPerBar(); // ticks based on the mouse x-position where the scroll wheel was used - int ticks = x / m_ppt; + int ticks = x / m_ppb; // what would be the ticks in the new zoom level on the very same mouse x - int newTicks = x / (DEFAULT_PR_PPT * m_zoomLevels[z]); + int newTicks = x / (DEFAULT_PR_PPB * m_zoomLevels[z]); // scroll so the tick "selected" by the mouse x doesn't move on the screen m_leftRightScroll->setValue(m_leftRightScroll->value() + ticks - newTicks); // update combobox with zooming-factor @@ -4039,7 +4039,7 @@ void PianoRoll::copyToClipboard( const NoteVector & notes ) const QDomElement note_list = dataFile.createElement( "note-list" ); dataFile.content().appendChild( note_list ); - MidiTime start_pos( notes.front()->pos().getTact(), 0 ); + MidiTime start_pos( notes.front()->pos().getBar(), 0 ); for( const Note *note : notes ) { Note clip_note( *note ); @@ -4200,15 +4200,15 @@ void PianoRoll::deleteSelectedNotes() void PianoRoll::autoScroll( const MidiTime & t ) { const int w = width() - WHITE_KEY_WIDTH; - if( t > m_currentPosition + w * MidiTime::ticksPerTact() / m_ppt ) + if( t > m_currentPosition + w * MidiTime::ticksPerBar() / m_ppb ) { - m_leftRightScroll->setValue( t.getTact() * MidiTime::ticksPerTact() ); + m_leftRightScroll->setValue( t.getBar() * MidiTime::ticksPerBar() ); } else if( t < m_currentPosition ) { - MidiTime t2 = qMax( t - w * MidiTime::ticksPerTact() * - MidiTime::ticksPerTact() / m_ppt, (tick_t) 0 ); - m_leftRightScroll->setValue( t2.getTact() * MidiTime::ticksPerTact() ); + MidiTime t2 = qMax( t - w * MidiTime::ticksPerBar() * + MidiTime::ticksPerBar() / m_ppb, (tick_t) 0 ); + m_leftRightScroll->setValue( t2.getBar() * MidiTime::ticksPerBar() ); } m_scrollBack = false; } @@ -4262,12 +4262,12 @@ void PianoRoll::updatePositionStepRecording( const MidiTime & t ) void PianoRoll::zoomingChanged() { - m_ppt = m_zoomLevels[m_zoomingModel.value()] * DEFAULT_PR_PPT; + m_ppb = m_zoomLevels[m_zoomingModel.value()] * DEFAULT_PR_PPB; - assert( m_ppt > 0 ); + assert( m_ppb > 0 ); - m_timeLine->setPixelsPerTact( m_ppt ); - m_stepRecorderWidget.setPixelsPerTact( m_ppt ); + m_timeLine->setPixelsPerBar( m_ppb ); + m_stepRecorderWidget.setPixelsPerBar( m_ppb ); update(); } @@ -4297,12 +4297,12 @@ int PianoRoll::quantization() const } else { - return DefaultTicksPerTact / 16; + return DefaultTicksPerBar / 16; } } QString text = m_quantizeModel.currentText(); - return DefaultTicksPerTact / text.right( text.length() - 2 ).toInt(); + return DefaultTicksPerBar / text.right( text.length() - 2 ).toInt(); } @@ -4370,7 +4370,7 @@ MidiTime PianoRoll::newNoteLen() const } QString text = m_noteLenModel.currentText(); - return DefaultTicksPerTact / text.right( text.length() - 2 ).toInt(); + return DefaultTicksPerBar / text.right( text.length() - 2 ).toInt(); } @@ -4398,7 +4398,7 @@ Note * PianoRoll::noteUnderMouse() int key_num = getKey( pos.y() ); int pos_ticks = ( pos.x() - WHITE_KEY_WIDTH ) * - MidiTime::ticksPerTact() / m_ppt + m_currentPosition; + MidiTime::ticksPerBar() / m_ppb + m_currentPosition; // loop through whole note-vector... for( Note* const& note : m_pattern->notes() ) diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index a077840d0..2c2485d00 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -95,7 +95,7 @@ SongEditor::SongEditor( Song * song ) : m_zoomingModel->setParent(this); m_snappingModel->setParent(this); m_timeLine = new TimeLineWidget( m_trackHeadWidth, 32, - pixelsPerTact(), + pixelsPerBar(), m_song->m_playPos[Song::Mode_PlaySong], m_currentPosition, Song::Mode_PlaySong, this ); @@ -374,7 +374,7 @@ void SongEditor::selectRegionFromPixels(int xStart, int xEnd) //calculate the song position where the mouse was clicked m_rubberbandStartMidipos = MidiTime((xStart - m_trackHeadWidth) - / pixelsPerTact() * MidiTime::ticksPerTact()) + / pixelsPerBar() * MidiTime::ticksPerBar()) + m_currentPosition; m_rubberBandStartTrackview = 0; } @@ -409,7 +409,7 @@ void SongEditor::updateRubberband() } //take care of the scrollbar position - int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerTact(); + int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerBar(); int vs = contentWidget()->verticalScrollBar()->value() - m_scrollPos.y(); //the adjusted origin point @@ -425,7 +425,7 @@ void SongEditor::updateRubberband() //the miditime the mouse is hover MidiTime rubberbandMidipos = MidiTime((qMin(m_mousePos.x(), width()) - m_trackHeadWidth) - / pixelsPerTact() * MidiTime::ticksPerTact()) + / pixelsPerBar() * MidiTime::ticksPerBar()) + m_currentPosition; //are tcos in the rect of selection? @@ -485,7 +485,7 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) } else if( ke->key() == Qt::Key_Left ) { - tick_t t = m_song->currentTick() - MidiTime::ticksPerTact(); + tick_t t = m_song->currentTick() - MidiTime::ticksPerBar(); if( t >= 0 ) { m_song->setPlayPos( t, Song::Mode_PlaySong ); @@ -493,7 +493,7 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) } else if( ke->key() == Qt::Key_Right ) { - tick_t t = m_song->currentTick() + MidiTime::ticksPerTact(); + tick_t t = m_song->currentTick() + MidiTime::ticksPerBar(); if( t < MaxSongLength ) { m_song->setPlayPos( t, Song::Mode_PlaySong ); @@ -549,20 +549,20 @@ void SongEditor::wheelEvent( QWheelEvent * we ) z = qBound( 0, z, m_zoomingModel->size() - 1 ); - int x = (we->x() - m_trackHeadWidth); - // tact based on the mouse x-position where the scroll wheel was used - int tact= x / pixelsPerTact(); - // what would be the tact in the new zoom level on the very same mouse x - int newTact = x / DEFAULT_PIXELS_PER_TACT / m_zoomLevels[z]; - // scroll so the tact "selected" by the mouse x doesn't move on the screen - m_leftRightScroll->setValue(m_leftRightScroll->value() + tact - newTact); + int x = we->x() - m_trackHeadWidth; + // bar based on the mouse x-position where the scroll wheel was used + int bar = x / pixelsPerBar(); + // what would be the bar in the new zoom level on the very same mouse x + int newBar = x / DEFAULT_PIXELS_PER_BAR / m_zoomLevels[z]; + // scroll so the bar "selected" by the mouse x doesn't move on the screen + m_leftRightScroll->setValue(m_leftRightScroll->value() + bar - newBar); // update combobox with zooming-factor m_zoomingModel->setValue( z ); // update timeline m_song->m_playPos[Song::Mode_PlaySong].m_timeLine-> - setPixelsPerTact( pixelsPerTact() ); + setPixelsPerBar( pixelsPerBar() ); // and make sure, all TCO's are resized and relocated realignTracks(); } @@ -614,7 +614,7 @@ void SongEditor::mousePressEvent(QMouseEvent *me) //the trackView(index) and the miditime where the mouse was clicked m_rubberBandStartTrackview = trackIndexFromSelectionPoint(me->y()); m_rubberbandStartMidipos = MidiTime((me->x() - m_trackHeadWidth) - / pixelsPerTact() * MidiTime::ticksPerTact()) + / pixelsPerBar() * MidiTime::ticksPerBar()) + m_currentPosition; } QWidget::mousePressEvent(me); @@ -791,14 +791,14 @@ void SongEditor::updatePosition( const MidiTime & t ) const int w = width() - widgetWidth - trackOpWidth - contentWidget()->verticalScrollBar()->width(); // width of right scrollbar - if( t > m_currentPosition + w * MidiTime::ticksPerTact() / - pixelsPerTact() ) + if( t > m_currentPosition + w * MidiTime::ticksPerBar() / + pixelsPerBar() ) { - animateScroll( m_leftRightScroll, t.getTact(), m_smoothScroll ); + animateScroll( m_leftRightScroll, t.getBar(), m_smoothScroll ); } else if( t < m_currentPosition ) { - animateScroll( m_leftRightScroll, t.getTact(), m_smoothScroll ); + animateScroll( m_leftRightScroll, t.getBar(), m_smoothScroll ); } m_scrollBack = false; } @@ -831,10 +831,10 @@ void SongEditor::updatePositionLine() void SongEditor::zoomingChanged() { - setPixelsPerTact( m_zoomLevels[m_zoomingModel->value()] * DEFAULT_PIXELS_PER_TACT ); + setPixelsPerBar( m_zoomLevels[m_zoomingModel->value()] * DEFAULT_PIXELS_PER_BAR ); m_song->m_playPos[Song::Mode_PlaySong].m_timeLine-> - setPixelsPerTact( pixelsPerTact() ); + setPixelsPerBar( pixelsPerBar() ); realignTracks(); updateRubberband(); } diff --git a/src/gui/widgets/StepRecorderWidget.cpp b/src/gui/widgets/StepRecorderWidget.cpp index f59e235fc..a546c2a2c 100644 --- a/src/gui/widgets/StepRecorderWidget.cpp +++ b/src/gui/widgets/StepRecorderWidget.cpp @@ -26,7 +26,7 @@ StepRecorderWidget::StepRecorderWidget( QWidget * parent, - const int ppt, + const int ppb, const int marginTop, const int marginBottom, const int marginLeft, @@ -42,15 +42,15 @@ StepRecorderWidget::StepRecorderWidget( m_colorLineStart = baseColor.darker(120); setAttribute(Qt::WA_NoSystemBackground, true); - setPixelsPerTact(ppt); + setPixelsPerBar(ppb); m_top = m_marginTop; m_left = m_marginLeft; } -void StepRecorderWidget::setPixelsPerTact(int ppt) +void StepRecorderWidget::setPixelsPerBar(int ppb) { - m_ppt = ppt; + m_ppb = ppb; } void StepRecorderWidget::setCurrentPosition(MidiTime currentPosition) @@ -125,7 +125,7 @@ void StepRecorderWidget::paintEvent(QPaintEvent * pe) int StepRecorderWidget::xCoordOfTick(int tick) { - return m_marginLeft + ((tick - m_currentPosition) * m_ppt / MidiTime::ticksPerTact()); + return m_marginLeft + ((tick - m_currentPosition) * m_ppb / MidiTime::ticksPerBar()); } diff --git a/src/gui/widgets/TimeDisplayWidget.cpp b/src/gui/widgets/TimeDisplayWidget.cpp index e9a831193..01604a9dc 100644 --- a/src/gui/widgets/TimeDisplayWidget.cpp +++ b/src/gui/widgets/TimeDisplayWidget.cpp @@ -100,11 +100,11 @@ void TimeDisplayWidget::updateTime() case BarsTicks: int tick; tick = s->getPlayPos().getTicks(); - m_majorLCD.setValue((int)(tick / s->ticksPerTact()) + 1); - m_minorLCD.setValue((tick % s->ticksPerTact()) / - (s->ticksPerTact() / s->getTimeSigModel().getNumerator() ) +1); - m_milliSecondsLCD.setValue((tick % s->ticksPerTact()) % - (s->ticksPerTact() / s->getTimeSigModel().getNumerator())); + m_majorLCD.setValue((int)(tick / s->ticksPerBar()) + 1); + m_minorLCD.setValue((tick % s->ticksPerBar()) / + (s->ticksPerBar() / s->getTimeSigModel().getNumerator() ) +1); + m_milliSecondsLCD.setValue((tick % s->ticksPerBar()) % + (s->ticksPerBar() / s->getTimeSigModel().getNumerator())); break; default: break; diff --git a/src/tracks/AutomationTrack.cpp b/src/tracks/AutomationTrack.cpp index 11c919f0e..430f54a56 100644 --- a/src/tracks/AutomationTrack.cpp +++ b/src/tracks/AutomationTrack.cpp @@ -121,9 +121,9 @@ void AutomationTrackView::dropEvent( QDropEvent * _de ) currentPosition() + ( _de->pos().x() - getTrackContentWidget()->x() ) * - MidiTime::ticksPerTact() / - static_cast( trackContainerView()->pixelsPerTact() ) ) - .toAbsoluteTact(); + MidiTime::ticksPerBar() / + static_cast( trackContainerView()->pixelsPerBar() ) ) + .toAbsoluteBar(); if( pos.getTicks() < 0 ) { diff --git a/src/tracks/BBTrack.cpp b/src/tracks/BBTrack.cpp index 779cd3c74..a779e2ea4 100644 --- a/src/tracks/BBTrack.cpp +++ b/src/tracks/BBTrack.cpp @@ -51,7 +51,7 @@ BBTCO::BBTCO( Track * _track ) : m_color( 128, 128, 128 ), m_useStyleColor( true ) { - tact_t t = Engine::getBBTrackContainer()->lengthOfBB( bbTrackIndex() ); + bar_t t = Engine::getBBTrackContainer()->lengthOfBB( bbTrackIndex() ); if( t > 0 ) { saveJournallingState( false ); @@ -237,12 +237,12 @@ void BBTCOView::paintEvent( QPaintEvent * ) const int lineSize = 3; p.setPen( c.darker( 200 ) ); - tact_t t = Engine::getBBTrackContainer()->lengthOfBB( m_bbTCO->bbTrackIndex() ); - if( m_bbTCO->length() > MidiTime::ticksPerTact() && t > 0 ) + bar_t t = Engine::getBBTrackContainer()->lengthOfBB( m_bbTCO->bbTrackIndex() ); + if( m_bbTCO->length() > MidiTime::ticksPerBar() && t > 0 ) { - for( int x = static_cast( t * pixelsPerTact() ); + for( int x = static_cast( t * pixelsPerBar() ); x < width() - 2; - x += static_cast( t * pixelsPerTact() ) ) + x += static_cast( t * pixelsPerBar() ) ) { p.drawLine( x, TCO_BORDER_WIDTH, x, TCO_BORDER_WIDTH + lineSize ); p.drawLine( x, rect().bottom() - ( TCO_BORDER_WIDTH + lineSize ), diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 673996fa2..b44d1b4fa 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -677,7 +677,7 @@ bool InstrumentTrack::play( const MidiTime & _start, const fpp_t _frames, if( cur_start > 0 ) { - // skip notes which are posated before start-tact + // skip notes which are posated before start-bar while( nit != notes.end() && ( *nit )->pos() < cur_start ) { ++nit; diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 32baf0b14..125e84a1f 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -56,7 +56,7 @@ Pattern::Pattern( InstrumentTrack * _instrument_track ) : TrackContentObject( _instrument_track ), m_instrumentTrack( _instrument_track ), m_patternType( BeatPattern ), - m_steps( MidiTime::stepsPerTact() ) + m_steps( MidiTime::stepsPerBar() ) { setName( _instrument_track->name() ); if( _instrument_track->trackContainer() @@ -161,7 +161,7 @@ void Pattern::updateLength() return; } - tick_t max_length = MidiTime::ticksPerTact(); + tick_t max_length = MidiTime::ticksPerBar(); for( NoteVector::ConstIterator it = m_notes.begin(); it != m_notes.end(); ++it ) @@ -172,8 +172,8 @@ void Pattern::updateLength() ( *it )->endPos() ); } } - changeLength( MidiTime( max_length ).nextFullTact() * - MidiTime::ticksPerTact() ); + changeLength( MidiTime( max_length ).nextFullBar() * + MidiTime::ticksPerBar() ); updateBBTrack(); } @@ -182,7 +182,7 @@ void Pattern::updateLength() MidiTime Pattern::beatPatternLength() const { - tick_t max_length = MidiTime::ticksPerTact(); + tick_t max_length = MidiTime::ticksPerBar(); for( NoteVector::ConstIterator it = m_notes.begin(); it != m_notes.end(); ++it ) @@ -194,13 +194,13 @@ MidiTime Pattern::beatPatternLength() const } } - if( m_steps != MidiTime::stepsPerTact() ) + if( m_steps != MidiTime::stepsPerBar() ) { - max_length = m_steps * MidiTime::ticksPerTact() / - MidiTime::stepsPerTact(); + max_length = m_steps * MidiTime::ticksPerBar() / + MidiTime::stepsPerBar(); } - return MidiTime( max_length ).nextFullTact() * MidiTime::ticksPerTact(); + return MidiTime( max_length ).nextFullBar() * MidiTime::ticksPerBar(); } @@ -298,7 +298,7 @@ void Pattern::clearNotes() Note * Pattern::addStepNote( int step ) { - return addNote( Note( MidiTime( -DefaultTicksPerTact ), + return addNote( Note( MidiTime( -DefaultTicksPerBar ), MidiTime::stepPosition( step ) ), false ); } @@ -417,7 +417,7 @@ void Pattern::loadSettings( const QDomElement & _this ) m_steps = _this.attribute( "steps" ).toInt(); if( m_steps == 0 ) { - m_steps = MidiTime::stepsPerTact(); + m_steps = MidiTime::stepsPerBar(); } checkType(); @@ -466,7 +466,7 @@ void Pattern::clear() void Pattern::addSteps() { - m_steps += MidiTime::stepsPerTact(); + m_steps += MidiTime::stepsPerBar(); updateLength(); emit dataChanged(); } @@ -497,7 +497,7 @@ void Pattern::cloneSteps() void Pattern::removeSteps() { - int n = MidiTime::stepsPerTact(); + int n = MidiTime::stepsPerBar(); if( n < m_steps ) { for( int i = m_steps - n; i < m_steps; ++i ) @@ -555,19 +555,19 @@ bool Pattern::empty() void Pattern::changeTimeSignature() { - MidiTime last_pos = MidiTime::ticksPerTact() - 1; + MidiTime last_pos = MidiTime::ticksPerBar() - 1; for( NoteVector::ConstIterator cit = m_notes.begin(); cit != m_notes.end(); ++cit ) { if( ( *cit )->length() < 0 && ( *cit )->pos() > last_pos ) { - last_pos = ( *cit )->pos()+MidiTime::ticksPerTact() / - MidiTime::stepsPerTact(); + last_pos = ( *cit )->pos()+MidiTime::ticksPerBar() / + MidiTime::stepsPerBar(); } } - last_pos = last_pos.nextFullTact() * MidiTime::ticksPerTact(); - m_steps = qMax( MidiTime::stepsPerTact(), - last_pos.getTact() * MidiTime::stepsPerTact() ); + last_pos = last_pos.nextFullBar() * MidiTime::ticksPerBar(); + m_steps = qMax( MidiTime::stepsPerBar(), + last_pos.getBar() * MidiTime::stepsPerBar() ); updateLength(); } @@ -715,7 +715,7 @@ void PatternView::mousePressEvent( QMouseEvent * _me ) { if( _me->button() == Qt::LeftButton && m_pat->m_patternType == Pattern::BeatPattern && - ( fixedTCOs() || pixelsPerTact() >= 96 ) && + ( fixedTCOs() || pixelsPerBar() >= 96 ) && _me->y() > height() - s_stepBtnOff->height() ) // when mouse button is pressed in beat/bassline -mode @@ -785,7 +785,7 @@ void PatternView::mouseDoubleClickEvent(QMouseEvent *_me) void PatternView::wheelEvent( QWheelEvent * _we ) { if( m_pat->m_patternType == Pattern::BeatPattern && - ( fixedTCOs() || pixelsPerTact() >= 96 ) && + ( fixedTCOs() || pixelsPerBar() >= 96 ) && _we->y() > height() - s_stepBtnOff->height() ) { // get the step number that was wheeled on and @@ -902,14 +902,14 @@ void PatternView::paintEvent( QPaintEvent * ) textBoxHeight = fontMetrics.height() + 2 * textTop; } - // Compute pixels per tact + // Compute pixels per bar const int baseWidth = fixedTCOs() ? parentWidget()->width() - 2 * TCO_BORDER_WIDTH : width() - TCO_BORDER_WIDTH; - const float pixelsPerTact = baseWidth / (float) m_pat->length().getTact(); + const float pixelsPerBar = baseWidth / (float) m_pat->length().getBar(); - // Length of one tact/beat in the [0,1] x [0,1] coordinate system - const float tactLength = 1. / m_pat->length().getTact(); - const float tickLength = tactLength / MidiTime::ticksPerTact(); + // Length of one bar/beat in the [0,1] x [0,1] coordinate system + const float barLength = 1. / m_pat->length().getBar(); + const float tickLength = barLength / MidiTime::ticksPerBar(); const int x_base = TCO_BORDER_WIDTH; @@ -1029,7 +1029,7 @@ void PatternView::paintEvent( QPaintEvent * ) } // beat pattern paint event - else if( beatPattern && ( fixedTCOs() || pixelsPerTact >= 96 ) ) + else if( beatPattern && ( fixedTCOs() || pixelsPerBar >= 96 ) ) { QPixmap stepon0; QPixmap stepon200; @@ -1097,14 +1097,14 @@ void PatternView::paintEvent( QPaintEvent * ) const int lineSize = 3; p.setPen( c.darker( 200 ) ); - for( tact_t t = 1; t < m_pat->length().getTact(); ++t ) + for( bar_t t = 1; t < m_pat->length().getBar(); ++t ) { - p.drawLine( x_base + static_cast( pixelsPerTact * t ) - 1, + p.drawLine( x_base + static_cast( pixelsPerBar * t ) - 1, TCO_BORDER_WIDTH, x_base + static_cast( - pixelsPerTact * t ) - 1, TCO_BORDER_WIDTH + lineSize ); - p.drawLine( x_base + static_cast( pixelsPerTact * t ) - 1, + pixelsPerBar * t ) - 1, TCO_BORDER_WIDTH + lineSize ); + p.drawLine( x_base + static_cast( pixelsPerBar * t ) - 1, rect().bottom() - ( lineSize + TCO_BORDER_WIDTH ), - x_base + static_cast( pixelsPerTact * t ) - 1, + x_base + static_cast( pixelsPerBar * t ) - 1, rect().bottom() - TCO_BORDER_WIDTH ); } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index ba04f909b..72f63bb05 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -157,7 +157,7 @@ void SampleTCO::setSampleFile( const QString & _sf ) { //When creating an empty sample pattern make it a bar long float nom = Engine::getSong()->getTimeSigModel().getNumerator(); float den = Engine::getSong()->getTimeSigModel().getDenominator(); - length = DefaultTicksPerTact * ( nom / den ); + length = DefaultTicksPerBar * ( nom / den ); } else { //Otherwise set it to the sample's length @@ -521,18 +521,18 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) p.setPen( !muted ? painter.pen().brush().color() : mutedColor() ); const int spacing = TCO_BORDER_WIDTH + 1; - const float ppt = fixedTCOs() ? + const float ppb = fixedTCOs() ? ( parentWidget()->width() - 2 * TCO_BORDER_WIDTH ) - / (float) m_tco->length().getTact() : - pixelsPerTact(); + / (float) m_tco->length().getBar() : + pixelsPerBar(); float nom = Engine::getSong()->getTimeSigModel().getNumerator(); float den = Engine::getSong()->getTimeSigModel().getDenominator(); - float ticksPerTact = DefaultTicksPerTact * nom / den; + float ticksPerBar = DefaultTicksPerBar * nom / den; - float offset = m_tco->startTimeOffset() / ticksPerTact * pixelsPerTact(); + float offset = m_tco->startTimeOffset() / ticksPerBar * pixelsPerBar(); QRect r = QRect( TCO_BORDER_WIDTH + offset, spacing, - qMax( static_cast( m_tco->sampleLength() * ppt / ticksPerTact ), 1 ), rect().bottom() - 2 * spacing ); + qMax( static_cast( m_tco->sampleLength() * ppb / ticksPerBar ), 1 ), rect().bottom() - 2 * spacing ); m_tco->m_sampleBuffer->visualize( p, r, pe->rect() ); QFileInfo fileInfo(m_tco->m_sampleBuffer->audioFile()); @@ -928,8 +928,8 @@ void SampleTrackView::dropEvent(QDropEvent *de) MidiTime tcoPos = trackContainerView()->fixedTCOs() ? MidiTime(0) - : MidiTime(((xPos - trackHeadWidth) / trackContainerView()->pixelsPerTact() - * MidiTime::ticksPerTact()) + trackContainerView()->currentPosition() + : MidiTime(((xPos - trackHeadWidth) / trackContainerView()->pixelsPerBar() + * MidiTime::ticksPerBar()) + trackContainerView()->currentPosition() ).quantize(1.0); SampleTCO * sTco = static_cast(getTrack()->createTCO(tcoPos)); diff --git a/tests/src/tracks/AutomationTrackTest.cpp b/tests/src/tracks/AutomationTrackTest.cpp index f9f77fac4..291ae293e 100644 --- a/tests/src/tracks/AutomationTrackTest.cpp +++ b/tests/src/tracks/AutomationTrackTest.cpp @@ -186,12 +186,12 @@ private slots: QVERIFY(! bbContainer->automatedValuesAt(5, bbTrack2.index()).size()); BBTCO tco(&bbTrack); - tco.changeLength(MidiTime::ticksPerTact() * 2); + tco.changeLength(MidiTime::ticksPerBar() * 2); tco.movePosition(0); QCOMPARE(song->automatedValuesAt(0)[&model], 0.0f); QCOMPARE(song->automatedValuesAt(5)[&model], 0.5f); - QCOMPARE(song->automatedValuesAt(MidiTime::ticksPerTact() + 5)[&model], 0.5f); + QCOMPARE(song->automatedValuesAt(MidiTime::ticksPerBar() + 5)[&model], 0.5f); } void testGlobalAutomation() From 46f5433732654d8090cc75ee21f3a00f93910439 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Sun, 27 Oct 2019 08:45:58 -0300 Subject: [PATCH 077/160] New BARS_PER_GROUP constant --- src/core/Track.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/Track.cpp b/src/core/Track.cpp index dedfcbc27..5c61dc5ab 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -73,6 +73,10 @@ */ const int RESIZE_GRIP_WIDTH = 4; +/*! Alternate between a darker and a lighter background color every 4 bars + */ +const int BARS_PER_GROUP = 4; + /*! A pointer for that text bubble used when moving segments, etc. * @@ -1271,8 +1275,7 @@ void TrackContentWidget::updateBackground() // Assume even-pixels-per-bar. Makes sense, should be like this anyways int ppb = static_cast( tcv->pixelsPerBar() ); - // alternate between a darker and a lighter color every 4 bars - int w = ppb * 4; + int w = ppb * BARS_PER_GROUP; int h = height(); m_background = QPixmap( w * 2, height() ); QPainter pmp( &m_background ); From fd203c3f7b8d2d7126c7dda51e47761d13de4972 Mon Sep 17 00:00:00 2001 From: Cyp Date: Wed, 30 Oct 2019 10:37:50 +0100 Subject: [PATCH 078/160] Fix crash due to calling QWidget::move from a non-GUI thread while exporting tracks. Calling via QMetaObject::invokeMethod should be thread safe. Crash callstack: QWidget::move SongEditor::updatePosition Song::stop Song::stopExport ProjectRenderer::run QThreadPrivate::start --- src/core/Song.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 6bd94a484..54f19e1d3 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -685,7 +685,7 @@ void Song::stop() if( gui && gui->songEditor() && ( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) ) { - gui->songEditor()->m_editor->updatePosition(0); + QMetaObject::invokeMethod(gui->songEditor()->m_editor, "updatePosition", Qt::AutoConnection, Q_ARG(MidiTime, 0)); } break; @@ -699,7 +699,7 @@ void Song::stop() if( gui && gui->songEditor() && ( tl->autoScroll() == TimeLineWidget::AutoScrollEnabled ) ) { - gui->songEditor()->m_editor->updatePosition( MidiTime(tl->savedPos().getTicks() ) ); + QMetaObject::invokeMethod(gui->songEditor()->m_editor, "updatePosition", Qt::AutoConnection, Q_ARG(MidiTime, tl->savedPos().getTicks())); } tl->savePos( -1 ); } From dac59a5fa056048ccc4655b06102406ac7c502ac Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Thu, 31 Oct 2019 19:05:33 +0000 Subject: [PATCH 079/160] C++11 inheritance updates Add `override` and remove `virtual` where applicable --- include/AudioAlsa.h | 8 +-- include/AudioAlsaSetupWidget.h | 2 +- include/AudioDummy.h | 10 +-- include/AudioFileMP3.h | 2 +- include/AudioFileOgg.h | 2 +- include/AudioFileWave.h | 2 +- include/AudioOss.h | 10 +-- include/AudioPort.h | 4 +- include/AudioPulseAudio.h | 10 +-- include/AudioSampleRecorder.h | 2 +- include/AudioSdl.h | 10 +-- include/AudioSndio.h | 10 +-- include/AutomatableButton.h | 10 +-- include/AutomatableModel.h | 6 +- include/AutomatableModelView.h | 2 +- include/AutomatableSlider.h | 10 +-- include/AutomationEditor.h | 34 +++++----- include/AutomationPattern.h | 8 +-- include/AutomationPatternView.h | 12 ++-- include/AutomationTrack.h | 16 ++--- include/BBEditor.h | 14 ++-- include/BBTrack.h | 30 ++++---- include/BBTrackContainer.h | 4 +- include/CPULoadWidget.h | 2 +- include/ComboBox.h | 8 +-- include/Controller.h | 6 +- include/ControllerConnection.h | 6 +- include/ControllerDialog.h | 2 +- include/ControllerRackView.h | 8 +-- include/ControllerView.h | 6 +- include/DetuningHelper.h | 6 +- include/DummyEffect.h | 14 ++-- include/DummyInstrument.h | 10 +-- include/DummyPlugin.h | 8 +-- include/Editor.h | 6 +- include/Effect.h | 8 +-- include/EffectChain.h | 6 +- include/EffectControlDialog.h | 2 +- include/EffectRackView.h | 2 +- include/EffectView.h | 6 +- include/EnvelopeAndLfoParameters.h | 6 +- include/EnvelopeAndLfoView.h | 10 +-- include/ExportFilter.h | 6 +- include/ExportProjectDialog.h | 6 +- include/FadeButton.h | 4 +- include/Fader.h | 14 ++-- include/FileBrowser.h | 10 +-- include/FxLine.h | 10 +-- include/FxLineLcdSpinBox.h | 4 +- include/FxMixer.h | 10 +-- include/FxMixerView.h | 8 +-- include/Graph.h | 14 ++-- include/GroupBox.h | 6 +- include/ImportFilter.h | 6 +- include/InlineAutomation.h | 4 +- include/Instrument.h | 2 +- include/InstrumentFunctionViews.h | 4 +- include/InstrumentFunctions.h | 12 ++-- include/InstrumentMidiIOView.h | 2 +- include/InstrumentPlayHandle.h | 6 +- include/InstrumentSoundShaping.h | 6 +- include/InstrumentSoundShapingView.h | 2 +- include/InstrumentTrack.h | 38 +++++------ include/JournallingObject.h | 4 +- include/Knob.h | 22 +++--- include/LadspaControl.h | 6 +- include/LcdSpinBox.h | 14 ++-- include/LcdWidget.h | 2 +- include/LedCheckbox.h | 2 +- include/LfoController.h | 14 ++-- include/LmmsStyle.h | 8 +-- include/MainApplication.h | 2 +- include/MainWindow.h | 10 +-- include/MeterDialog.h | 2 +- include/MidiAlsaRaw.h | 4 +- include/MidiAlsaSeq.h | 24 +++---- include/MidiClient.h | 4 +- include/MidiController.h | 14 ++-- include/MidiDummy.h | 2 +- include/MidiOss.h | 4 +- include/MidiPort.h | 6 +- include/MidiPortMenu.h | 2 +- include/MidiSndio.h | 4 +- include/Mixer.h | 2 +- include/MixerWorkerThread.h | 2 +- include/NStateButton.h | 2 +- include/Note.h | 6 +- include/NotePlayHandle.h | 10 +-- include/Pattern.h | 20 +++--- include/PeakController.h | 16 ++--- include/PianoRoll.h | 42 ++++++------ include/PianoView.h | 20 +++--- include/PixmapButton.h | 10 +-- include/PlayHandle.h | 4 +- include/Plugin.h | 2 +- include/PresetPreviewPlayHandle.h | 8 +-- include/ProjectNotes.h | 8 +-- include/ProjectRenderer.h | 2 +- include/RemotePlugin.h | 4 +- include/RenameDialog.h | 4 +- include/RowTableView.h | 4 +- include/Rubberband.h | 2 +- include/SamplePlayHandle.h | 8 +-- include/SampleRecordHandle.h | 6 +- include/SampleTrack.h | 54 +++++++-------- include/SendButtonIndicator.h | 2 +- include/SetupDialog.h | 2 +- include/SideBarWidget.h | 6 +- include/Song.h | 6 +- include/SongEditor.h | 38 +++++------ include/StepRecorderWidget.h | 2 +- include/SubWindow.h | 8 +-- include/TabWidget.h | 14 ++-- include/TempoSyncKnob.h | 4 +- include/TempoSyncKnobModel.h | 2 +- include/TextFloat.h | 4 +- include/TimeDisplayWidget.h | 2 +- include/TimeLineWidget.h | 14 ++-- include/Track.h | 68 +++++++++---------- include/TrackContainer.h | 6 +- include/TrackContainerView.h | 17 +++-- include/TrackLabelButton.h | 14 ++-- include/TrackRenameLineEdit.h | 2 +- include/VisualizationWidget.h | 4 +- plugins/VstEffect/VstEffectControlDialog.h | 4 +- .../peak_controller_effect.h | 4 +- .../peak_controller_effect_controls.h | 10 +-- plugins/vst_base/VstPlugin.h | 10 +-- src/core/PresetPreviewPlayHandle.cpp | 2 +- src/gui/ControllerConnectionDialog.cpp | 2 +- src/gui/FxMixerView.cpp | 2 +- src/gui/RowTableView.cpp | 4 +- src/gui/widgets/SideBar.cpp | 4 +- 133 files changed, 578 insertions(+), 579 deletions(-) diff --git a/include/AudioAlsa.h b/include/AudioAlsa.h index 77938e164..b1aa9647a 100644 --- a/include/AudioAlsa.h +++ b/include/AudioAlsa.h @@ -80,10 +80,10 @@ public: static DeviceInfoCollection getAvailableDevices(); private: - virtual void startProcessing(); - virtual void stopProcessing(); - virtual void applyQualitySettings(); - virtual void run(); + void startProcessing() override; + void stopProcessing() override; + void applyQualitySettings() override; + void run() override; int setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ); int setSWParams(); diff --git a/include/AudioAlsaSetupWidget.h b/include/AudioAlsaSetupWidget.h index f087d2fd7..db88558a7 100644 --- a/include/AudioAlsaSetupWidget.h +++ b/include/AudioAlsaSetupWidget.h @@ -46,7 +46,7 @@ public: AudioAlsaSetupWidget( QWidget * _parent ); virtual ~AudioAlsaSetupWidget(); - virtual void saveSettings(); + void saveSettings() override; public slots: void onCurrentIndexChanged(int index); diff --git a/include/AudioDummy.h b/include/AudioDummy.h index 5094caddc..0772c69eb 100644 --- a/include/AudioDummy.h +++ b/include/AudioDummy.h @@ -64,11 +64,11 @@ public: { } - virtual void saveSettings() + void saveSettings() override { } - virtual void show() + void show() override { parentWidget()->hide(); QWidget::show(); @@ -78,17 +78,17 @@ public: private: - virtual void startProcessing() + void startProcessing() override { start(); } - virtual void stopProcessing() + void stopProcessing() override { stopProcessingThread( this ); } - virtual void run() + void run() override { MicroTimer timer; while( true ) diff --git a/include/AudioFileMP3.h b/include/AudioFileMP3.h index 497208e20..057fd13a4 100644 --- a/include/AudioFileMP3.h +++ b/include/AudioFileMP3.h @@ -58,7 +58,7 @@ public: protected: virtual void writeBuffer( const surroundSampleFrame * /* _buf*/, const fpp_t /*_frames*/, - const float /*_master_gain*/ ); + const float /*_master_gain*/ ) override; private: void flushRemainingBuffers(); diff --git a/include/AudioFileOgg.h b/include/AudioFileOgg.h index 656a7174e..8082f3767 100644 --- a/include/AudioFileOgg.h +++ b/include/AudioFileOgg.h @@ -59,7 +59,7 @@ public: private: virtual void writeBuffer( const surroundSampleFrame * _ab, const fpp_t _frames, - const float _master_gain ); + const float _master_gain ) override; bool startEncoding(); void finishEncoding(); diff --git a/include/AudioFileWave.h b/include/AudioFileWave.h index 4d2778bad..7c8d54964 100644 --- a/include/AudioFileWave.h +++ b/include/AudioFileWave.h @@ -56,7 +56,7 @@ public: private: virtual void writeBuffer( const surroundSampleFrame * _ab, const fpp_t _frames, - float _master_gain ); + float _master_gain ) override; bool startEncoding(); void finishEncoding(); diff --git a/include/AudioOss.h b/include/AudioOss.h index bacfd9597..9e4787ff2 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -60,7 +60,7 @@ public: setupWidget( QWidget * _parent ); virtual ~setupWidget(); - virtual void saveSettings(); + void saveSettings() override; private: QLineEdit * m_device; @@ -70,10 +70,10 @@ public: private: - virtual void startProcessing(); - virtual void stopProcessing(); - virtual void applyQualitySettings(); - virtual void run(); + void startProcessing() override; + void stopProcessing() override; + void applyQualitySettings() override; + void run() override; int m_audioFD; diff --git a/include/AudioPort.h b/include/AudioPort.h index 2842c6a17..146bbd192 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -100,8 +100,8 @@ public: bool processEffects(); // ThreadableJob stuff - virtual void doProcessing(); - virtual bool requiresProcessing() const + void doProcessing() override; + bool requiresProcessing() const override { return true; } diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index 496746691..e65180a74 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -62,7 +62,7 @@ public: setupWidget( QWidget * _parent ); virtual ~setupWidget(); - virtual void saveSettings(); + void saveSettings() override; private: QLineEdit * m_device; @@ -80,10 +80,10 @@ public: private: - virtual void startProcessing(); - virtual void stopProcessing(); - virtual void applyQualitySettings(); - virtual void run(); + void startProcessing() override; + void stopProcessing() override; + void applyQualitySettings() override; + void run() override; volatile bool m_quit; diff --git a/include/AudioSampleRecorder.h b/include/AudioSampleRecorder.h index 69ac19490..0a82d2d96 100644 --- a/include/AudioSampleRecorder.h +++ b/include/AudioSampleRecorder.h @@ -48,7 +48,7 @@ public: private: virtual void writeBuffer( const surroundSampleFrame * _ab, const fpp_t _frames, - const float _master_gain ); + const float _master_gain ) override; typedef QList > BufferList; BufferList m_buffers; diff --git a/include/AudioSdl.h b/include/AudioSdl.h index fd8c544c2..93f23abed 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -60,9 +60,9 @@ public: { public: setupWidget( QWidget * _parent ); - virtual ~setupWidget(); + ~setupWidget() override; - virtual void saveSettings(); + void saveSettings() override; private: QLineEdit * m_device; @@ -71,9 +71,9 @@ public: private: - virtual void startProcessing(); - virtual void stopProcessing(); - virtual void applyQualitySettings(); + void startProcessing() override; + void stopProcessing() override; + void applyQualitySettings() override; static void sdlAudioCallback( void * _udata, Uint8 * _buf, int _len ); void sdlAudioCallback( Uint8 * _buf, int _len ); diff --git a/include/AudioSndio.h b/include/AudioSndio.h index d2bc5c074..f8cf56848 100644 --- a/include/AudioSndio.h +++ b/include/AudioSndio.h @@ -58,7 +58,7 @@ public: setupWidget( QWidget * _parent ); virtual ~setupWidget(); - virtual void saveSettings( void ); + void saveSettings( void ) override; private: QLineEdit * m_device; @@ -66,10 +66,10 @@ public: } ; private: - virtual void startProcessing( void ); - virtual void stopProcessing( void ); - virtual void applyQualitySettings( void ); - virtual void run( void ); + void startProcessing( void ) override; + void stopProcessing( void ) override; + void applyQualitySettings( void ) override; + void run( void ) override; struct sio_hdl *m_hdl; struct sio_par m_par; diff --git a/include/AutomatableButton.h b/include/AutomatableButton.h index 4b5065542..d7859a10c 100644 --- a/include/AutomatableButton.h +++ b/include/AutomatableButton.h @@ -48,7 +48,7 @@ public: model()->setJournalling( _on ); } - virtual void modelChanged(); + void modelChanged() override; public slots: @@ -62,9 +62,9 @@ public slots: protected: - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); + void contextMenuEvent( QContextMenuEvent * _me ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; private: @@ -92,7 +92,7 @@ public: void activateButton( AutomatableButton * _btn ); - virtual void modelChanged(); + void modelChanged() override; private slots: diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 3e0b6143d..6d8000804 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -255,7 +255,7 @@ public: specified DOM element using as attribute/node name */ virtual void loadSettings( const QDomElement& element, const QString& name ); - virtual QString nodeName() const + QString nodeName() const override { return "automatablemodel"; } @@ -337,12 +337,12 @@ private: static bool mustQuoteName(const QString &name); - virtual void saveSettings( QDomDocument& doc, QDomElement& element ) + void saveSettings( QDomDocument& doc, QDomElement& element ) override { saveSettings( doc, element, "value" ); } - virtual void loadSettings( const QDomElement& element ) + void loadSettings( const QDomElement& element ) override { loadSettings( element, "value" ); } diff --git a/include/AutomatableModelView.h b/include/AutomatableModelView.h index 964ffdc5d..1bcbd97d6 100644 --- a/include/AutomatableModelView.h +++ b/include/AutomatableModelView.h @@ -49,7 +49,7 @@ public: return castModel(); } - virtual void setModel( Model* model, bool isOldModelValid = true ); + void setModel( Model* model, bool isOldModelValid = true ) override; template inline T value() const diff --git a/include/AutomatableSlider.h b/include/AutomatableSlider.h index f58d4a059..b51ef1e3f 100644 --- a/include/AutomatableSlider.h +++ b/include/AutomatableSlider.h @@ -51,12 +51,12 @@ signals: protected: - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); - virtual void wheelEvent( QWheelEvent * _me ); + void contextMenuEvent( QContextMenuEvent * _me ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; + void wheelEvent( QWheelEvent * _me ) override; - virtual void modelChanged(); + void modelChanged() override; private: diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index e39eaddd7..60b894f1f 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -73,9 +73,9 @@ public: return m_pattern != nullptr; } - virtual void saveSettings(QDomDocument & doc, QDomElement & parent); - virtual void loadSettings(const QDomElement & parent); - QString nodeName() const + void saveSettings(QDomDocument & doc, QDomElement & parent) override; + void loadSettings(const QDomElement & parent) override; + QString nodeName() const override { return "automationeditor"; } @@ -114,14 +114,14 @@ public slots: protected: typedef AutomationPattern::timeMap timeMap; - virtual void keyPressEvent(QKeyEvent * ke); - virtual void leaveEvent(QEvent * e); - virtual void mousePressEvent(QMouseEvent * mouseEvent); - virtual void mouseReleaseEvent(QMouseEvent * mouseEvent); - virtual void mouseMoveEvent(QMouseEvent * mouseEvent); - virtual void paintEvent(QPaintEvent * pe); - virtual void resizeEvent(QResizeEvent * re); - virtual void wheelEvent(QWheelEvent * we); + void keyPressEvent(QKeyEvent * ke) override; + void leaveEvent(QEvent * e) override; + void mousePressEvent(QMouseEvent * mouseEvent) override; + void mouseReleaseEvent(QMouseEvent * mouseEvent) override; + void mouseMoveEvent(QMouseEvent * mouseEvent) override; + void paintEvent(QPaintEvent * pe) override; + void resizeEvent(QResizeEvent * re) override; + void wheelEvent(QWheelEvent * we) override; float getLevel( int y ); int xCoordOfTick( int tick ); @@ -282,14 +282,14 @@ public: void setCurrentPattern(AutomationPattern* pattern); const AutomationPattern* currentPattern(); - virtual void dropEvent( QDropEvent * _de ); - virtual void dragEnterEvent( QDragEnterEvent * _dee ); + void dropEvent( QDropEvent * _de ) override; + void dragEnterEvent( QDragEnterEvent * _dee ) override; void open(AutomationPattern* pattern); AutomationEditor* m_editor; - QSize sizeHint() const; + QSize sizeHint() const override; public slots: void clearCurrentPattern(); @@ -298,11 +298,11 @@ signals: void currentPatternChanged(); protected: - virtual void focusInEvent(QFocusEvent * event); + void focusInEvent(QFocusEvent * event) override; protected slots: - void play(); - void stop(); + void play() override; + void stop() override; private slots: void updateWindowTitle(); diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 070b6c669..cad9d0a1d 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -140,13 +140,13 @@ public: const QString name() const; // settings-management - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; static const QString classNodeName() { return "automationpattern"; } - QString nodeName() const { return classNodeName(); } + QString nodeName() const override { return classNodeName(); } - virtual TrackContentObjectView * createView( TrackView * _tv ); + TrackContentObjectView * createView( TrackView * _tv ) override; static bool isAutomated( const AutomatableModel * _m ); diff --git a/include/AutomationPatternView.h b/include/AutomationPatternView.h index 45aa9ef2a..3f019483a 100644 --- a/include/AutomationPatternView.h +++ b/include/AutomationPatternView.h @@ -44,7 +44,7 @@ public: public slots: /// Opens this view's pattern in the global automation editor void openInAutomationEditor(); - virtual void update(); + void update() override; protected slots: @@ -56,11 +56,11 @@ protected slots: void flipX(); protected: - virtual void constructContextMenu( QMenu * ); - virtual void mouseDoubleClickEvent(QMouseEvent * me ); - virtual void paintEvent( QPaintEvent * pe ); - virtual void dragEnterEvent( QDragEnterEvent * _dee ); - virtual void dropEvent( QDropEvent * _de ); + void constructContextMenu( QMenu * ) override; + void mouseDoubleClickEvent(QMouseEvent * me ) override; + void paintEvent( QPaintEvent * pe ) override; + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void dropEvent( QDropEvent * _de ) override; private: diff --git a/include/AutomationTrack.h b/include/AutomationTrack.h index 195c21e9d..92a50dd04 100644 --- a/include/AutomationTrack.h +++ b/include/AutomationTrack.h @@ -38,19 +38,19 @@ public: virtual ~AutomationTrack() = default; virtual bool play( const MidiTime & _start, const fpp_t _frames, - const f_cnt_t _frame_base, int _tco_num = -1 ); + const f_cnt_t _frame_base, int _tco_num = -1 ) override; - virtual QString nodeName() const + QString nodeName() const override { return "automationtrack"; } - virtual TrackView * createView( TrackContainerView* ); - virtual TrackContentObject * createTCO( const MidiTime & _pos ); + TrackView * createView( TrackContainerView* ) override; + TrackContentObject * createTCO( const MidiTime & _pos ) override; virtual void saveTrackSpecificSettings( QDomDocument & _doc, - QDomElement & _parent ); - virtual void loadTrackSpecificSettings( const QDomElement & _this ); + QDomElement & _parent ) override; + void loadTrackSpecificSettings( const QDomElement & _this ) override; private: friend class AutomationTrackView; @@ -65,8 +65,8 @@ public: AutomationTrackView( AutomationTrack* at, TrackContainerView* tcv ); virtual ~AutomationTrackView() = default; - virtual void dragEnterEvent( QDragEnterEvent * _dee ); - virtual void dropEvent( QDropEvent * _de ); + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void dropEvent( QDropEvent * _de ) override; } ; diff --git a/include/BBEditor.h b/include/BBEditor.h index 59b7142f4..ed54beaf1 100644 --- a/include/BBEditor.h +++ b/include/BBEditor.h @@ -42,7 +42,7 @@ public: BBEditor( BBTrackContainer * _tc ); ~BBEditor(); - QSize sizeHint() const; + QSize sizeHint() const override; const BBTrackContainerView* trackContainerView() const { return m_trackContainerView; @@ -54,8 +54,8 @@ public: void removeBBView( int bb ); public slots: - void play(); - void stop(); + void play() override; + void stop() override; private: BBTrackContainerView* m_trackContainerView; @@ -70,15 +70,15 @@ class BBTrackContainerView : public TrackContainerView public: BBTrackContainerView(BBTrackContainer* tc); - bool fixedTCOs() const + bool fixedTCOs() const override { return true; } void removeBBView(int bb); - void saveSettings(QDomDocument& doc, QDomElement& element); - void loadSettings(const QDomElement& element); + void saveSettings(QDomDocument& doc, QDomElement& element) override; + void loadSettings(const QDomElement& element) override; public slots: void addSteps(); @@ -88,7 +88,7 @@ public slots: void addAutomationTrack(); protected slots: - void dropEvent(QDropEvent * de ); + void dropEvent(QDropEvent * de ) override; void updatePosition(); private: diff --git a/include/BBTrack.h b/include/BBTrack.h index a906b54d2..70195f28d 100644 --- a/include/BBTrack.h +++ b/include/BBTrack.h @@ -43,9 +43,9 @@ public: BBTCO( Track * _track ); virtual ~BBTCO() = default; - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); - inline virtual QString nodeName() const + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; + inline QString nodeName() const override { return( "bbtco" ); } @@ -72,7 +72,7 @@ public: int bbTrackIndex(); - virtual TrackContentObjectView * createView( TrackView * _tv ); + TrackContentObjectView * createView( TrackView * _tv ) override; private: QColor m_color; @@ -99,7 +99,7 @@ public: void setColor( QColor _new_color ); public slots: - virtual void update(); + void update() override; protected slots: void openInBBEditor(); @@ -110,9 +110,9 @@ protected slots: protected: - virtual void paintEvent( QPaintEvent * pe ); - virtual void mouseDoubleClickEvent( QMouseEvent * _me ); - virtual void constructContextMenu( QMenu * ); + void paintEvent( QPaintEvent * pe ) override; + void mouseDoubleClickEvent( QMouseEvent * _me ) override; + void constructContextMenu( QMenu * ) override; private: @@ -133,13 +133,13 @@ public: virtual ~BBTrack(); virtual bool play( const MidiTime & _start, const fpp_t _frames, - const f_cnt_t _frame_base, int _tco_num = -1 ); - virtual TrackView * createView( TrackContainerView* tcv ); - virtual TrackContentObject * createTCO( const MidiTime & _pos ); + const f_cnt_t _frame_base, int _tco_num = -1 ) override; + TrackView * createView( TrackContainerView* tcv ) override; + TrackContentObject * createTCO( const MidiTime & _pos ) override; virtual void saveTrackSpecificSettings( QDomDocument & _doc, - QDomElement & _parent ); - virtual void loadTrackSpecificSettings( const QDomElement & _this ); + QDomElement & _parent ) override; + void loadTrackSpecificSettings( const QDomElement & _this ) override; static BBTrack * findBBTrack( int _bb_num ); static void swapBBTracks( Track * _track1, Track * _track2 ); @@ -184,7 +184,7 @@ public: } protected: - inline virtual QString nodeName() const + inline QString nodeName() const override { return( "bbtrack" ); } @@ -211,7 +211,7 @@ public: BBTrackView( BBTrack* bbt, TrackContainerView* tcv ); virtual ~BBTrackView(); - virtual bool close(); + bool close() override; const BBTrack * getBBTrack() const { diff --git a/include/BBTrackContainer.h b/include/BBTrackContainer.h index e4895aa87..17d6eb5fe 100644 --- a/include/BBTrackContainer.h +++ b/include/BBTrackContainer.h @@ -41,9 +41,9 @@ public: virtual bool play( MidiTime _start, const fpp_t _frames, const f_cnt_t _frame_base, int _tco_num = -1 ); - virtual void updateAfterTrackAdd() override; + void updateAfterTrackAdd() override; - inline virtual QString nodeName() const override + inline QString nodeName() const override { return "bbtrackcontainer"; } diff --git a/include/CPULoadWidget.h b/include/CPULoadWidget.h index 610403f57..2bc41283b 100644 --- a/include/CPULoadWidget.h +++ b/include/CPULoadWidget.h @@ -43,7 +43,7 @@ public: protected: - virtual void paintEvent( QPaintEvent * _ev ); + void paintEvent( QPaintEvent * _ev ) override; protected slots: diff --git a/include/ComboBox.h b/include/ComboBox.h index 927c87620..d530c9d92 100644 --- a/include/ComboBox.h +++ b/include/ComboBox.h @@ -57,10 +57,10 @@ public slots: protected: - virtual void contextMenuEvent( QContextMenuEvent* event ); - virtual void mousePressEvent( QMouseEvent* event ); - virtual void paintEvent( QPaintEvent* event ); - virtual void wheelEvent( QWheelEvent* event ); + void contextMenuEvent( QContextMenuEvent* event ) override; + void mousePressEvent( QMouseEvent* event ) override; + void paintEvent( QPaintEvent* event ) override; + void wheelEvent( QWheelEvent* event ) override; private: diff --git a/include/Controller.h b/include/Controller.h index f1e71ad8f..b60349463 100644 --- a/include/Controller.h +++ b/include/Controller.h @@ -101,9 +101,9 @@ public: } - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); - virtual void loadSettings( const QDomElement & _this ); - virtual QString nodeName() const; + void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; + void loadSettings( const QDomElement & _this ) override; + QString nodeName() const override; static Controller * create( ControllerTypes _tt, Model * _parent ); static Controller * create( const QDomElement & _this, diff --git a/include/ControllerConnection.h b/include/ControllerConnection.h index 5c4d5f20e..e57cd4db2 100644 --- a/include/ControllerConnection.h +++ b/include/ControllerConnection.h @@ -85,15 +85,15 @@ public: static void finalizeConnections(); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; + void loadSettings( const QDomElement & _this ) override; static inline const QString classNodeName() { return "connection"; } - virtual QString nodeName() const + QString nodeName() const override { return classNodeName(); } diff --git a/include/ControllerDialog.h b/include/ControllerDialog.h index 58a0a94b2..05e8f3bfe 100644 --- a/include/ControllerDialog.h +++ b/include/ControllerDialog.h @@ -47,7 +47,7 @@ signals: protected: - virtual void closeEvent( QCloseEvent * _ce ); + void closeEvent( QCloseEvent * _ce ) override; } ; diff --git a/include/ControllerRackView.h b/include/ControllerRackView.h index cb393f5ca..9ef2d9b7f 100644 --- a/include/ControllerRackView.h +++ b/include/ControllerRackView.h @@ -47,10 +47,10 @@ public: ControllerRackView(); virtual ~ControllerRackView(); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; - inline virtual QString nodeName() const + inline QString nodeName() const override { return "ControllerRackView"; } @@ -62,7 +62,7 @@ public slots: void onControllerRemoved( Controller * ); protected: - virtual void closeEvent( QCloseEvent * _ce ); + void closeEvent( QCloseEvent * _ce ) override; private slots: void addController(); diff --git a/include/ControllerView.h b/include/ControllerView.h index 4b215feca..d1284845e 100644 --- a/include/ControllerView.h +++ b/include/ControllerView.h @@ -68,9 +68,9 @@ signals: protected: - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void modelChanged(); - virtual void mouseDoubleClickEvent( QMouseEvent * event ); + void contextMenuEvent( QContextMenuEvent * _me ) override; + void modelChanged() override; + void mouseDoubleClickEvent( QMouseEvent * event ) override; private: diff --git a/include/DetuningHelper.h b/include/DetuningHelper.h index de0acc826..2157b6ba8 100644 --- a/include/DetuningHelper.h +++ b/include/DetuningHelper.h @@ -43,17 +43,17 @@ public: { } - virtual float defaultValue() const + float defaultValue() const override { return 0; } - virtual QString displayName() const + QString displayName() const override { return tr( "Note detuning" ); } - inline virtual QString nodeName() const + inline QString nodeName() const override { return "detuning"; } diff --git a/include/DummyEffect.h b/include/DummyEffect.h index 4f770af3c..5509131be 100644 --- a/include/DummyEffect.h +++ b/include/DummyEffect.h @@ -53,25 +53,25 @@ public: { } - virtual int controlCount() + int controlCount() override { return 0; } - virtual void saveSettings( QDomDocument &, QDomElement & ) + void saveSettings( QDomDocument &, QDomElement & ) override { } - virtual void loadSettings( const QDomElement & ) + void loadSettings( const QDomElement & ) override { } - virtual QString nodeName() const + QString nodeName() const override { return "DummyControls"; } - virtual EffectControlDialog * createView() + EffectControlDialog * createView() override { return new DummyEffectControlDialog( this ); } @@ -95,12 +95,12 @@ public: { } - virtual EffectControls * controls() + EffectControls * controls() override { return &m_controls; } - bool processAudioBuffer( sampleFrame *, const fpp_t ) + bool processAudioBuffer( sampleFrame *, const fpp_t ) override { return false; } diff --git a/include/DummyInstrument.h b/include/DummyInstrument.h index 87083f262..a37b089ae 100644 --- a/include/DummyInstrument.h +++ b/include/DummyInstrument.h @@ -47,26 +47,26 @@ public: { } - virtual void playNote( NotePlayHandle *, sampleFrame * buffer ) + void playNote( NotePlayHandle *, sampleFrame * buffer ) override { memset( buffer, 0, sizeof( sampleFrame ) * Engine::mixer()->framesPerPeriod() ); } - virtual void saveSettings( QDomDocument &, QDomElement & ) + void saveSettings( QDomDocument &, QDomElement & ) override { } - virtual void loadSettings( const QDomElement & ) + void loadSettings( const QDomElement & ) override { } - virtual QString nodeName() const + QString nodeName() const override { return "dummyinstrument"; } - virtual PluginView * instantiateView( QWidget * _parent ) + PluginView * instantiateView( QWidget * _parent ) override { return new InstrumentViewFixedSize( this, _parent ); } diff --git a/include/DummyPlugin.h b/include/DummyPlugin.h index 49475a2ac..ec26da0a3 100644 --- a/include/DummyPlugin.h +++ b/include/DummyPlugin.h @@ -42,22 +42,22 @@ public: { } - virtual void saveSettings( QDomDocument &, QDomElement & ) + void saveSettings( QDomDocument &, QDomElement & ) override { } - virtual void loadSettings( const QDomElement & ) + void loadSettings( const QDomElement & ) override { } - virtual QString nodeName() const + QString nodeName() const override { return "DummyPlugin"; } protected: - virtual PluginView * instantiateView( QWidget * _parent ) + PluginView * instantiateView( QWidget * _parent ) override { return new PluginView( this, _parent ); } diff --git a/include/Editor.h b/include/Editor.h index 26b70ec87..1c80e9f2f 100644 --- a/include/Editor.h +++ b/include/Editor.h @@ -47,7 +47,7 @@ protected: DropToolBar * addDropToolBar(Qt::ToolBarArea whereToAdd, QString const & windowTitle); DropToolBar * addDropToolBar(QWidget * parent, Qt::ToolBarArea whereToAdd, QString const & windowTitle); - virtual void closeEvent( QCloseEvent * _ce ); + void closeEvent( QCloseEvent * _ce ) override; protected slots: virtual void play() {} virtual void record() {} @@ -92,8 +92,8 @@ signals: void dropped(QDropEvent* event); protected: - void dragEnterEvent(QDragEnterEvent* event); - void dropEvent(QDropEvent* event); + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; }; diff --git a/include/Effect.h b/include/Effect.h index 4dc50e8a4..3874aa602 100644 --- a/include/Effect.h +++ b/include/Effect.h @@ -47,10 +47,10 @@ public: const Descriptor::SubPluginFeatures::Key * _key ); virtual ~Effect(); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; - inline virtual QString nodeName() const + inline QString nodeName() const override { return "effect"; } @@ -170,7 +170,7 @@ protected: */ void checkGate( double _out_sum ); - virtual PluginView * instantiateView( QWidget * ); + PluginView * instantiateView( QWidget * ) override; // some effects might not be capable of higher sample-rates so they can // sample it down before processing and back after processing diff --git a/include/EffectChain.h b/include/EffectChain.h index bc1e7df8c..9ebc4d534 100644 --- a/include/EffectChain.h +++ b/include/EffectChain.h @@ -40,10 +40,10 @@ public: EffectChain( Model * _parent ); virtual ~EffectChain(); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; - inline virtual QString nodeName() const + inline QString nodeName() const override { return "fxchain"; } diff --git a/include/EffectControlDialog.h b/include/EffectControlDialog.h index c0a60cfa6..4a59489da 100644 --- a/include/EffectControlDialog.h +++ b/include/EffectControlDialog.h @@ -48,7 +48,7 @@ signals: protected: - virtual void closeEvent( QCloseEvent * _ce ); + void closeEvent( QCloseEvent * _ce ) override; EffectControls * m_effectControls; diff --git a/include/EffectRackView.h b/include/EffectRackView.h index 0cfc04e15..698bad7fb 100644 --- a/include/EffectRackView.h +++ b/include/EffectRackView.h @@ -60,7 +60,7 @@ private slots: private: - virtual void modelChanged(); + void modelChanged() override; inline EffectChain* fxChain() { diff --git a/include/EffectView.h b/include/EffectView.h index 71a0e7128..6e994dd7e 100644 --- a/include/EffectView.h +++ b/include/EffectView.h @@ -73,9 +73,9 @@ signals: protected: - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void paintEvent( QPaintEvent * _pe ); - virtual void modelChanged(); + void contextMenuEvent( QContextMenuEvent * _me ) override; + void paintEvent( QPaintEvent * _pe ) override; + void modelChanged() override; private: diff --git a/include/EnvelopeAndLfoParameters.h b/include/EnvelopeAndLfoParameters.h index 4824062f3..0f691adb0 100644 --- a/include/EnvelopeAndLfoParameters.h +++ b/include/EnvelopeAndLfoParameters.h @@ -91,9 +91,9 @@ public: } - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); - virtual QString nodeName() const + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; + QString nodeName() const override { return "el"; } diff --git a/include/EnvelopeAndLfoView.h b/include/EnvelopeAndLfoView.h index 817b0a6ee..f6d4fd0a8 100644 --- a/include/EnvelopeAndLfoView.h +++ b/include/EnvelopeAndLfoView.h @@ -52,12 +52,12 @@ public: protected: - virtual void modelChanged(); + void modelChanged() override; - virtual void dragEnterEvent( QDragEnterEvent * _dee ); - virtual void dropEvent( QDropEvent * _de ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void paintEvent( QPaintEvent * _pe ); + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void dropEvent( QDropEvent * _de ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void paintEvent( QPaintEvent * _pe ) override; protected slots: diff --git a/include/ExportFilter.h b/include/ExportFilter.h index 950bacbcd..3124b477b 100644 --- a/include/ExportFilter.h +++ b/include/ExportFilter.h @@ -44,15 +44,15 @@ public: int tempo, int masterPitch, const QString &filename ) = 0; protected: - virtual void saveSettings( QDomDocument &, QDomElement & ) + void saveSettings( QDomDocument &, QDomElement & ) override { } - virtual void loadSettings( const QDomElement & ) + void loadSettings( const QDomElement & ) override { } - virtual QString nodeName() const + QString nodeName() const override { return "import_filter"; } diff --git a/include/ExportProjectDialog.h b/include/ExportProjectDialog.h index 0eedb9722..06c1e3011 100644 --- a/include/ExportProjectDialog.h +++ b/include/ExportProjectDialog.h @@ -41,14 +41,14 @@ public: ExportProjectDialog( const QString & _file_name, QWidget * _parent, bool multi_export ); protected: - virtual void reject( void ); - virtual void closeEvent( QCloseEvent * _ce ); + void reject( void ) override; + void closeEvent( QCloseEvent * _ce ) override; private slots: void startBtnClicked( void ); void updateTitleBar( int ); - void accept(); + void accept() override; void startExport(); void onFileFormatChanged(int); diff --git a/include/FadeButton.h b/include/FadeButton.h index 8f56a77b2..09a4c6457 100644 --- a/include/FadeButton.h +++ b/include/FadeButton.h @@ -50,8 +50,8 @@ public slots: protected: - virtual void customEvent( QEvent * ); - virtual void paintEvent( QPaintEvent * _pe ); + void customEvent( QEvent * ) override; + void paintEvent( QPaintEvent * _pe ) override; private: diff --git a/include/Fader.h b/include/Fader.h index 018f66e0c..207215445 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -109,13 +109,13 @@ public: } private: - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void mousePressEvent( QMouseEvent *ev ); - virtual void mouseDoubleClickEvent( QMouseEvent* mouseEvent ); - virtual void mouseMoveEvent( QMouseEvent *ev ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); - virtual void wheelEvent( QWheelEvent *ev ); - virtual void paintEvent( QPaintEvent *ev ); + void contextMenuEvent( QContextMenuEvent * _me ) override; + void mousePressEvent( QMouseEvent *ev ) override; + void mouseDoubleClickEvent( QMouseEvent* mouseEvent ) override; + void mouseMoveEvent( QMouseEvent *ev ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; + void wheelEvent( QWheelEvent *ev ) override; + void paintEvent( QPaintEvent *ev ) override; inline bool clips(float const & value) const { return value >= 1.0f; } diff --git a/include/FileBrowser.h b/include/FileBrowser.h index 4b92dd549..9b56a8dbd 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -61,7 +61,7 @@ private slots: void giveFocusToFilter(); private: - virtual void keyPressEvent( QKeyEvent * ke ); + void keyPressEvent( QKeyEvent * ke ) override; void addItems( const QString & path ); @@ -93,10 +93,10 @@ public: protected: - virtual void contextMenuEvent( QContextMenuEvent * e ); - virtual void mousePressEvent( QMouseEvent * me ); - virtual void mouseMoveEvent( QMouseEvent * me ); - virtual void mouseReleaseEvent( QMouseEvent * me ); + void contextMenuEvent( QContextMenuEvent * e ) override; + void mousePressEvent( QMouseEvent * me ) override; + void mouseMoveEvent( QMouseEvent * me ) override; + void mouseReleaseEvent( QMouseEvent * me ) override; private: diff --git a/include/FxLine.h b/include/FxLine.h index ef8313e74..c16dcd5f5 100644 --- a/include/FxLine.h +++ b/include/FxLine.h @@ -51,10 +51,10 @@ public: FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex); ~FxLine(); - virtual void paintEvent( QPaintEvent * ); - virtual void mousePressEvent( QMouseEvent * ); - virtual void mouseDoubleClickEvent( QMouseEvent * ); - virtual void contextMenuEvent( QContextMenuEvent * ); + void paintEvent( QPaintEvent * ) override; + void mousePressEvent( QMouseEvent * ) override; + void mouseDoubleClickEvent( QMouseEvent * ) override; + void contextMenuEvent( QContextMenuEvent * ) override; inline int channelIndex() { return m_channelIndex; } void setChannelIndex(int index); @@ -79,7 +79,7 @@ public: static const int FxLineHeight; - bool eventFilter (QObject *dist, QEvent *event); + bool eventFilter (QObject *dist, QEvent *event) override; private: void drawFxLine( QPainter* p, const FxLine *fxLine, bool isActive, bool sendToThis, bool receiveFromThis ); diff --git a/include/FxLineLcdSpinBox.h b/include/FxLineLcdSpinBox.h index fa001b2bb..eeb104c5c 100644 --- a/include/FxLineLcdSpinBox.h +++ b/include/FxLineLcdSpinBox.h @@ -42,8 +42,8 @@ public: void setTrackView(TrackView * tv); protected: - virtual void mouseDoubleClickEvent(QMouseEvent* event); - virtual void contextMenuEvent(QContextMenuEvent* event); + void mouseDoubleClickEvent(QMouseEvent* event) override; + void contextMenuEvent(QContextMenuEvent* event) override; private: TrackView * m_tv; diff --git a/include/FxMixer.h b/include/FxMixer.h index 2c7ef3c5a..68b69d9bc 100644 --- a/include/FxMixer.h +++ b/include/FxMixer.h @@ -67,7 +67,7 @@ class FxChannel : public ThreadableJob // pointers to other channels that send to this one FxRouteVector m_receives; - virtual bool requiresProcessing() const { return true; } + bool requiresProcessing() const override { return true; } void unmuteForSolo(); @@ -76,7 +76,7 @@ class FxChannel : public ThreadableJob void processed(); private: - virtual void doProcessing(); + void doProcessing() override; }; @@ -133,10 +133,10 @@ public: void prepareMasterMix(); void masterMix( sampleFrame * _buf ); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; - virtual QString nodeName() const + QString nodeName() const override { return "fxmixer"; } diff --git a/include/FxMixerView.h b/include/FxMixerView.h index 9b07637ed..a7662321a 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -64,10 +64,10 @@ public: FxMixerView(); virtual ~FxMixerView(); - virtual void keyPressEvent(QKeyEvent * e); + void keyPressEvent(QKeyEvent * e) override; - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; + void loadSettings( const QDomElement & _this ) override; inline FxLine * currentFxLine() { @@ -110,7 +110,7 @@ public slots: int addNewChannel(); protected: - virtual void closeEvent( QCloseEvent * _ce ); + void closeEvent( QCloseEvent * _ce ) override; private slots: void updateFaders(); diff --git a/include/Graph.h b/include/Graph.h index 1bee05c41..2a6fc4f8a 100644 --- a/include/Graph.h +++ b/include/Graph.h @@ -87,19 +87,19 @@ public: signals: void drawn(); protected: - virtual void paintEvent( QPaintEvent * _pe ); - virtual void dropEvent( QDropEvent * _de ); - virtual void dragEnterEvent( QDragEnterEvent * _dee ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseMoveEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); + void paintEvent( QPaintEvent * _pe ) override; + void dropEvent( QDropEvent * _de ) override; + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseMoveEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; protected slots: void updateGraph( int _startPos, int _endPos ); void updateGraph(); private: - virtual void modelChanged(); + void modelChanged() override; void changeSampleAt( int _x, int _y ); void drawLineAt( int _x, int _y, int _lastx ); diff --git a/include/GroupBox.h b/include/GroupBox.h index 8a857199f..88428b1aa 100644 --- a/include/GroupBox.h +++ b/include/GroupBox.h @@ -42,7 +42,7 @@ public: GroupBox( const QString & _caption, QWidget * _parent = NULL ); virtual ~GroupBox(); - virtual void modelChanged(); + void modelChanged() override; PixmapButton * ledButton() { @@ -56,8 +56,8 @@ public: protected: - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void paintEvent( QPaintEvent * _pe ); + void mousePressEvent( QMouseEvent * _me ) override; + void paintEvent( QPaintEvent * _pe ) override; private: diff --git a/include/ImportFilter.h b/include/ImportFilter.h index 166c1bfda..ccefd3db2 100644 --- a/include/ImportFilter.h +++ b/include/ImportFilter.h @@ -89,15 +89,15 @@ protected: m_file.ungetChar( _ch ); } - virtual void saveSettings( QDomDocument &, QDomElement & ) + void saveSettings( QDomDocument &, QDomElement & ) override { } - virtual void loadSettings( const QDomElement & ) + void loadSettings( const QDomElement & ) override { } - virtual QString nodeName() const + QString nodeName() const override { return "import_filter"; } diff --git a/include/InlineAutomation.h b/include/InlineAutomation.h index d70121a45..431ecbc81 100644 --- a/include/InlineAutomation.h +++ b/include/InlineAutomation.h @@ -79,8 +79,8 @@ public: return m_autoPattern; } - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; private: diff --git a/include/Instrument.h b/include/Instrument.h index 2179a1f72..438197cd8 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -110,7 +110,7 @@ public: return true; } - virtual QString fullDisplayName() const; + QString fullDisplayName() const override; // -------------------------------------------------------------------- // provided functions: diff --git a/include/InstrumentFunctionViews.h b/include/InstrumentFunctionViews.h index 8ac13e1b9..58f915b15 100644 --- a/include/InstrumentFunctionViews.h +++ b/include/InstrumentFunctionViews.h @@ -49,7 +49,7 @@ public: private: - virtual void modelChanged(); + void modelChanged() override; InstrumentFunctionNoteStacking * m_cc; @@ -72,7 +72,7 @@ public: private: - virtual void modelChanged(); + void modelChanged() override; InstrumentFunctionArpeggio * m_a; GroupBox * m_arpGroupBox; diff --git a/include/InstrumentFunctions.h b/include/InstrumentFunctions.h index 0055c6c97..b45484e71 100644 --- a/include/InstrumentFunctions.h +++ b/include/InstrumentFunctions.h @@ -54,10 +54,10 @@ public: void processNote( NotePlayHandle* n ); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; - inline virtual QString nodeName() const + inline QString nodeName() const override { return "chordcreator"; } @@ -176,10 +176,10 @@ public: void processNote( NotePlayHandle* n ); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; - inline virtual QString nodeName() const + inline QString nodeName() const override { return "arpeggiator"; } diff --git a/include/InstrumentMidiIOView.h b/include/InstrumentMidiIOView.h index 38f441942..e63b48425 100644 --- a/include/InstrumentMidiIOView.h +++ b/include/InstrumentMidiIOView.h @@ -47,7 +47,7 @@ public: private: - virtual void modelChanged(); + void modelChanged() override; GroupBox * m_midiInputGroupBox; LcdSpinBox * m_inputChannelSpinBox; diff --git a/include/InstrumentPlayHandle.h b/include/InstrumentPlayHandle.h index 426b413ce..ac5fc3222 100644 --- a/include/InstrumentPlayHandle.h +++ b/include/InstrumentPlayHandle.h @@ -40,7 +40,7 @@ public: } - virtual void play( sampleFrame * _working_buffer ) + void play( sampleFrame * _working_buffer ) override { // ensure that all our nph's have been processed first ConstNotePlayHandleList nphv = NotePlayHandle::nphsOfInstrumentTrack( m_instrument->instrumentTrack(), true ); @@ -65,12 +65,12 @@ public: m_instrument->play( _working_buffer ); } - virtual bool isFinished() const + bool isFinished() const override { return false; } - virtual bool isFromTrack( const Track* _track ) const + bool isFromTrack( const Track* _track ) const override { return m_instrument->isFromTrack( _track ); } diff --git a/include/InstrumentSoundShaping.h b/include/InstrumentSoundShaping.h index a159f8380..1b8df38d3 100644 --- a/include/InstrumentSoundShaping.h +++ b/include/InstrumentSoundShaping.h @@ -57,9 +57,9 @@ public: float volumeLevel( NotePlayHandle * _n, const f_cnt_t _frame ); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); - inline virtual QString nodeName() const + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; + inline QString nodeName() const override { return "eldata"; } diff --git a/include/InstrumentSoundShapingView.h b/include/InstrumentSoundShapingView.h index a409e8446..06d53232c 100644 --- a/include/InstrumentSoundShapingView.h +++ b/include/InstrumentSoundShapingView.h @@ -50,7 +50,7 @@ public: private: - virtual void modelChanged(); + void modelChanged() override; InstrumentSoundShaping * m_ss; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 72e0d04cc..d72331e52 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -80,8 +80,8 @@ public: MidiEvent applyMasterKey( const MidiEvent& event ); - virtual void processInEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ); - virtual void processOutEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ); + void processInEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ) override; + void processOutEvent( const MidiEvent& event, const MidiTime& time = MidiTime(), f_cnt_t offset = 0 ) override; // silence all running notes played by this track void silenceAllNotes( bool removeIPH = false ); @@ -111,7 +111,7 @@ public: void deleteNotePluginData( NotePlayHandle * _n ); // name-stuff - virtual void setName( const QString & _new_name ); + void setName( const QString & _new_name ) override; // translate given key of a note-event to absolute key (i.e. // add global master-pitch and base-note of this instrument track) @@ -131,18 +131,18 @@ public: // play everything in given frame-range - creates note-play-handles virtual bool play( const MidiTime & _start, const fpp_t _frames, - const f_cnt_t _frame_base, int _tco_num = -1 ); + const f_cnt_t _frame_base, int _tco_num = -1 ) override; // create new view for me - virtual TrackView * createView( TrackContainerView* tcv ); + TrackView * createView( TrackContainerView* tcv ) override; // create new track-content-object = pattern - virtual TrackContentObject * createTCO( const MidiTime & _pos ); + TrackContentObject * createTCO( const MidiTime & _pos ) override; // called by track virtual void saveTrackSpecificSettings( QDomDocument & _doc, - QDomElement & _parent ); - virtual void loadTrackSpecificSettings( const QDomElement & _this ); + QDomElement & _parent ) override; + void loadTrackSpecificSettings( const QDomElement & _this ) override; using Track::setJournalling; @@ -225,7 +225,7 @@ signals: protected: - virtual QString nodeName() const + QString nodeName() const override { return "instrumenttrack"; } @@ -317,12 +317,12 @@ public: static void cleanupWindowCache(); // Create a menu for assigning/creating channels for this track - QMenu * createFxMenu( QString title, QString newFxLabel ); + QMenu * createFxMenu( QString title, QString newFxLabel ) override; protected: - virtual void dragEnterEvent( QDragEnterEvent * _dee ); - virtual void dropEvent( QDropEvent * _de ); + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void dropEvent( QDropEvent * _de ) override; private slots: @@ -404,8 +404,8 @@ public: static void dragEnterEventGeneric( QDragEnterEvent * _dee ); - virtual void dragEnterEvent( QDragEnterEvent * _dee ); - virtual void dropEvent( QDropEvent * _de ); + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void dropEvent( QDropEvent * _de ) override; public slots: @@ -417,11 +417,11 @@ public slots: protected: // capture close-events for toggling instrument-track-button - virtual void closeEvent( QCloseEvent * _ce ); - virtual void focusInEvent( QFocusEvent * _fe ); + void closeEvent( QCloseEvent * _ce ) override; + void focusInEvent( QFocusEvent * _fe ) override; - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; + void loadSettings( const QDomElement & _this ) override; protected slots: @@ -430,7 +430,7 @@ protected slots: void viewPrevInstrument(); private: - virtual void modelChanged(); + void modelChanged() override; void viewInstrumentInDirection(int d); //! adjust size of any child widget of the main tab //! required to keep the old look when using a variable sized tab widget diff --git a/include/JournallingObject.h b/include/JournallingObject.h index 6974bef84..f4755994b 100644 --- a/include/JournallingObject.h +++ b/include/JournallingObject.h @@ -59,9 +59,9 @@ public: void addJournalCheckPoint(); virtual QDomElement saveState( QDomDocument & _doc, - QDomElement & _parent ); + QDomElement & _parent ) override; - virtual void restoreState( const QDomElement & _this ); + void restoreState( const QDomElement & _this ) override; inline bool isJournalling() const { diff --git a/include/Knob.h b/include/Knob.h index b8d460f45..4f8064731 100644 --- a/include/Knob.h +++ b/include/Knob.h @@ -124,16 +124,16 @@ signals: protected: - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void dragEnterEvent( QDragEnterEvent * _dee ); - virtual void dropEvent( QDropEvent * _de ); - virtual void focusOutEvent( QFocusEvent * _fe ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); - virtual void mouseMoveEvent( QMouseEvent * _me ); - virtual void mouseDoubleClickEvent( QMouseEvent * _me ); - virtual void paintEvent( QPaintEvent * _me ); - virtual void wheelEvent( QWheelEvent * _me ); + void contextMenuEvent( QContextMenuEvent * _me ) override; + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void dropEvent( QDropEvent * _de ) override; + void focusOutEvent( QFocusEvent * _fe ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; + void mouseMoveEvent( QMouseEvent * _me ) override; + void mouseDoubleClickEvent( QMouseEvent * _me ) override; + void paintEvent( QPaintEvent * _me ) override; + void wheelEvent( QWheelEvent * _me ) override; virtual float getValue( const QPoint & _p ); @@ -145,7 +145,7 @@ private slots: private: QString displayValue() const; - virtual void doConnections(); + void doConnections() override; QLineF calculateLine( const QPointF & _mid, float _radius, float _innerRadius = 1) const; diff --git a/include/LadspaControl.h b/include/LadspaControl.h index 2ad895b3f..34f6c9ae2 100644 --- a/include/LadspaControl.h +++ b/include/LadspaControl.h @@ -74,7 +74,7 @@ public: virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent, const QString & _name ); virtual void loadSettings( const QDomElement & _this, const QString & _name ); - inline virtual QString nodeName() const + inline QString nodeName() const override { return "port"; } @@ -92,13 +92,13 @@ protected slots: void linkStateChanged(); protected: - virtual void saveSettings( QDomDocument& doc, QDomElement& element ) + void saveSettings( QDomDocument& doc, QDomElement& element ) override { Q_UNUSED(doc) Q_UNUSED(element) } - virtual void loadSettings( const QDomElement& element ) + void loadSettings( const QDomElement& element ) override { Q_UNUSED(element) } diff --git a/include/LcdSpinBox.h b/include/LcdSpinBox.h index b63dfaa40..379b743ac 100644 --- a/include/LcdSpinBox.h +++ b/include/LcdSpinBox.h @@ -40,7 +40,7 @@ public: virtual ~LcdSpinBox() = default; - virtual void modelChanged() + void modelChanged() override { ModelView::modelChanged(); update(); @@ -65,12 +65,12 @@ public slots: protected: - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseMoveEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); - virtual void wheelEvent( QWheelEvent * _we ); - virtual void mouseDoubleClickEvent( QMouseEvent * _me ); + void contextMenuEvent( QContextMenuEvent * _me ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseMoveEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; + void wheelEvent( QWheelEvent * _we ) override; + void mouseDoubleClickEvent( QMouseEvent * _me ) override; private: bool m_mouseMoving; diff --git a/include/LcdWidget.h b/include/LcdWidget.h index db969dd33..f4c7d1579 100644 --- a/include/LcdWidget.h +++ b/include/LcdWidget.h @@ -71,7 +71,7 @@ public slots: protected: - virtual void paintEvent( QPaintEvent * pe ); + void paintEvent( QPaintEvent * pe ) override; virtual void updateSize(); diff --git a/include/LedCheckbox.h b/include/LedCheckbox.h index 723bae6a9..66d7ce07e 100644 --- a/include/LedCheckbox.h +++ b/include/LedCheckbox.h @@ -64,7 +64,7 @@ public: Q_PROPERTY( QString text READ text WRITE setText ) protected: - virtual void paintEvent( QPaintEvent * _pe ); + void paintEvent( QPaintEvent * _pe ) override; private: diff --git a/include/LfoController.h b/include/LfoController.h index 9dfbba671..8fc35fd09 100644 --- a/include/LfoController.h +++ b/include/LfoController.h @@ -49,18 +49,18 @@ public: virtual ~LfoController(); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); - virtual void loadSettings( const QDomElement & _this ); - virtual QString nodeName() const; + void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; + void loadSettings( const QDomElement & _this ) override; + QString nodeName() const override; public slots: - virtual ControllerDialog * createDialog( QWidget * _parent ); + ControllerDialog * createDialog( QWidget * _parent ) override; protected: // The internal per-controller value updating function - virtual void updateValueBuffer(); + void updateValueBuffer() override; FloatModel m_baseModel; TempoSyncKnobModel m_speedModel; @@ -98,8 +98,8 @@ public: protected: - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void modelChanged(); + void contextMenuEvent( QContextMenuEvent * _me ) override; + void modelChanged() override; LfoController * m_lfo; diff --git a/include/LmmsStyle.h b/include/LmmsStyle.h index 88b8a2112..ccf14396c 100644 --- a/include/LmmsStyle.h +++ b/include/LmmsStyle.h @@ -67,21 +67,21 @@ public: { } - virtual QPalette standardPalette( void ) const; + QPalette standardPalette( void ) const override; virtual void drawComplexControl( ComplexControl control, const QStyleOptionComplex * option, QPainter *painter, - const QWidget *widget ) const; + const QWidget *widget ) const override; virtual void drawPrimitive( PrimitiveElement element, const QStyleOption *option, QPainter *painter, - const QWidget *widget = 0 ) const; + const QWidget *widget = 0 ) const override; virtual int pixelMetric( PixelMetric metric, const QStyleOption * option = 0, - const QWidget * widget = 0 ) const; + const QWidget * widget = 0 ) const override; static QPalette * s_palette; diff --git a/include/MainApplication.h b/include/MainApplication.h index 41d670419..d28900213 100644 --- a/include/MainApplication.h +++ b/include/MainApplication.h @@ -42,7 +42,7 @@ class MainApplication : public QApplication { public: MainApplication(int& argc, char** argv); - bool event(QEvent* event); + bool event(QEvent* event) override; #ifdef LMMS_BUILD_WIN32 bool winEventFilter(MSG* msg, long* result); bool nativeEventFilter(const QByteArray& eventType, void* message, diff --git a/include/MainWindow.h b/include/MainWindow.h index 74e569653..5dc102321 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -176,11 +176,11 @@ private slots: void onExportProjectMidi(); protected: - virtual void closeEvent( QCloseEvent * _ce ); - virtual void focusOutEvent( QFocusEvent * _fe ); - virtual void keyPressEvent( QKeyEvent * _ke ); - virtual void keyReleaseEvent( QKeyEvent * _ke ); - virtual void timerEvent( QTimerEvent * _ev ); + void closeEvent( QCloseEvent * _ce ) override; + void focusOutEvent( QFocusEvent * _fe ) override; + void keyPressEvent( QKeyEvent * _ke ) override; + void keyReleaseEvent( QKeyEvent * _ke ) override; + void timerEvent( QTimerEvent * _ev ) override; private: diff --git a/include/MeterDialog.h b/include/MeterDialog.h index 5399c4a9a..da254c7c7 100644 --- a/include/MeterDialog.h +++ b/include/MeterDialog.h @@ -40,7 +40,7 @@ public: MeterDialog( QWidget * _parent, bool _simple = false ); virtual ~MeterDialog(); - virtual void modelChanged(); + void modelChanged() override; private: diff --git a/include/MidiAlsaRaw.h b/include/MidiAlsaRaw.h index 81f288c22..69f9366f1 100644 --- a/include/MidiAlsaRaw.h +++ b/include/MidiAlsaRaw.h @@ -62,8 +62,8 @@ public: protected: - virtual void sendByte( const unsigned char c ); - virtual void run(); + void sendByte( const unsigned char c ) override; + void run() override; private: diff --git a/include/MidiAlsaSeq.h b/include/MidiAlsaSeq.h index 0406b42b9..b6e498721 100644 --- a/include/MidiAlsaSeq.h +++ b/include/MidiAlsaSeq.h @@ -67,44 +67,44 @@ public: virtual void processOutEvent( const MidiEvent & _me, const MidiTime & _time, - const MidiPort * _port ); + const MidiPort * _port ) override; - virtual void applyPortMode( MidiPort * _port ); - virtual void applyPortName( MidiPort * _port ); + void applyPortMode( MidiPort * _port ) override; + void applyPortName( MidiPort * _port ) override; - virtual void removePort( MidiPort * _port ); + void removePort( MidiPort * _port ) override; // list seq-ports from ALSA - virtual QStringList readablePorts() const + QStringList readablePorts() const override { return m_readablePorts; } - virtual QStringList writablePorts() const + QStringList writablePorts() const override { return m_writablePorts; } // return name of port which specified MIDI event came from - virtual QString sourcePortName( const MidiEvent & ) const; + QString sourcePortName( const MidiEvent & ) const override; // (un)subscribe given MidiPort to/from destination-port virtual void subscribeReadablePort( MidiPort * _port, const QString & _dest, - bool _subscribe = true ); + bool _subscribe = true ) override; virtual void subscribeWritablePort( MidiPort * _port, const QString & _dest, - bool _subscribe = true ); + bool _subscribe = true ) override; virtual void connectRPChanged( QObject * _receiver, - const char * _member ) + const char * _member ) override { connect( this, SIGNAL( readablePortsChanged() ), _receiver, _member ); } virtual void connectWPChanged( QObject * _receiver, - const char * _member ) + const char * _member ) override { connect( this, SIGNAL( writablePortsChanged() ), _receiver, _member ); @@ -117,7 +117,7 @@ private slots: private: - virtual void run(); + void run() override; #ifdef LMMS_HAVE_ALSA QMutex m_seqMutex; diff --git a/include/MidiClient.h b/include/MidiClient.h index 293f2b3da..f06cac893 100644 --- a/include/MidiClient.h +++ b/include/MidiClient.h @@ -124,7 +124,7 @@ public: virtual ~MidiClientRaw(); // we are raw-clients for sure! - virtual bool isRaw() const + bool isRaw() const override { return true; } @@ -141,7 +141,7 @@ protected: private: // this does MIDI-event-process void processParsedEvent(); - virtual void processOutEvent( const MidiEvent& event, const MidiTime& time, const MidiPort* port ); + void processOutEvent( const MidiEvent& event, const MidiTime& time, const MidiPort* port ) override; // small helper function returning length of a certain event - this // is necessary for parsing raw-MIDI-data diff --git a/include/MidiController.h b/include/MidiController.h index d661b8d0f..43f928a25 100644 --- a/include/MidiController.h +++ b/include/MidiController.h @@ -44,30 +44,30 @@ public: virtual ~MidiController(); virtual void processInEvent( const MidiEvent & _me, - const MidiTime & _time, f_cnt_t offset = 0 ); + const MidiTime & _time, f_cnt_t offset = 0 ) override; virtual void processOutEvent( const MidiEvent& _me, - const MidiTime & _time, f_cnt_t offset = 0 ) + const MidiTime & _time, f_cnt_t offset = 0 ) override { // No output yet } - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); - virtual void loadSettings( const QDomElement & _this ); - virtual QString nodeName() const; + void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; + void loadSettings( const QDomElement & _this ) override; + QString nodeName() const override; // Used by controllerConnectionDialog to copy void subscribeReadablePorts( const MidiPort::Map & _map ); public slots: - virtual ControllerDialog * createDialog( QWidget * _parent ); + ControllerDialog * createDialog( QWidget * _parent ) override; void updateName(); protected: // The internal per-controller get-value function - virtual void updateValueBuffer(); + void updateValueBuffer() override; MidiPort m_midiPort; diff --git a/include/MidiDummy.h b/include/MidiDummy.h index dffd1ce51..f809d3c36 100644 --- a/include/MidiDummy.h +++ b/include/MidiDummy.h @@ -56,7 +56,7 @@ public: protected: - virtual void sendByte( const unsigned char ) + void sendByte( const unsigned char ) override { } diff --git a/include/MidiOss.h b/include/MidiOss.h index 27ebf0771..7e1f179ef 100644 --- a/include/MidiOss.h +++ b/include/MidiOss.h @@ -58,8 +58,8 @@ public: } protected: - virtual void sendByte( const unsigned char c ); - virtual void run(); + void sendByte( const unsigned char c ) override; + void run() override; private: diff --git a/include/MidiPort.h b/include/MidiPort.h index 07c61d788..e9cba39ed 100644 --- a/include/MidiPort.h +++ b/include/MidiPort.h @@ -103,10 +103,10 @@ public: void processOutEvent( const MidiEvent& event, const MidiTime& time = MidiTime() ); - virtual void saveSettings( QDomDocument& doc, QDomElement& thisElement ); - virtual void loadSettings( const QDomElement& thisElement ); + void saveSettings( QDomDocument& doc, QDomElement& thisElement ) override; + void loadSettings( const QDomElement& thisElement ) override; - virtual QString nodeName() const + QString nodeName() const override { return "midiport"; } diff --git a/include/MidiPortMenu.h b/include/MidiPortMenu.h index b963a7bd9..ce39c4aac 100644 --- a/include/MidiPortMenu.h +++ b/include/MidiPortMenu.h @@ -51,7 +51,7 @@ protected slots: private: - virtual void modelChanged(); + void modelChanged() override; MidiPort::Modes m_mode; diff --git a/include/MidiSndio.h b/include/MidiSndio.h index d115993fe..14ecfa0e9 100644 --- a/include/MidiSndio.h +++ b/include/MidiSndio.h @@ -59,8 +59,8 @@ public: protected: - virtual void sendByte(const unsigned char c); - virtual void run(void); + void sendByte(const unsigned char c) override; + void run(void) override; private: struct mio_hdl *m_hdl; diff --git a/include/Mixer.h b/include/Mixer.h index 69ea0d12d..32eeb8977 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -345,7 +345,7 @@ private: fifo * m_fifo; volatile bool m_writing; - virtual void run(); + void run() override; void write( surroundSampleFrame * buffer ); diff --git a/include/MixerWorkerThread.h b/include/MixerWorkerThread.h index 7c3792392..2d49dce09 100644 --- a/include/MixerWorkerThread.h +++ b/include/MixerWorkerThread.h @@ -106,7 +106,7 @@ public: private: - virtual void run(); + void run() override; static JobQueue globalJobQueue; static QWaitCondition * queueReadyWaitCond; diff --git a/include/NStateButton.h b/include/NStateButton.h index d9e56b892..95d36c253 100644 --- a/include/NStateButton.h +++ b/include/NStateButton.h @@ -61,7 +61,7 @@ signals: protected: - virtual void mousePressEvent( QMouseEvent * _me ); + void mousePressEvent( QMouseEvent * _me ) override; private: diff --git a/include/Note.h b/include/Note.h index 0eae8f6b0..30969b4c8 100644 --- a/include/Note.h +++ b/include/Note.h @@ -200,7 +200,7 @@ public: return "note"; } - inline virtual QString nodeName() const + inline QString nodeName() const override { return classNodeName(); } @@ -218,8 +218,8 @@ public: protected: - virtual void saveSettings( QDomDocument & doc, QDomElement & parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & doc, QDomElement & parent ) override; + void loadSettings( const QDomElement & _this ) override; private: diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index ae55c9ebb..3dba0f277 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -74,8 +74,8 @@ public: return p; } - virtual void setVolume( volume_t volume ); - virtual void setPanning( panning_t panning ); + void setVolume( volume_t volume ) override; + void setPanning( panning_t panning ) override; int midiKey() const; int midiChannel() const @@ -105,10 +105,10 @@ public: } /*! Renders one chunk using the attached instrument into the buffer */ - virtual void play( sampleFrame* buffer ); + void play( sampleFrame* buffer ) override; /*! Returns whether playback of note is finished and thus handle can be deleted */ - virtual bool isFinished() const + bool isFinished() const override { return m_released && framesLeft() <= 0; } @@ -120,7 +120,7 @@ public: fpp_t framesLeftForCurrentPeriod() const; /*! Returns whether the play handle plays on a certain track */ - virtual bool isFromTrack( const Track* _track ) const; + bool isFromTrack( const Track* _track ) const override; /*! Releases the note (and plays release frames */ void noteOff( const f_cnt_t offset = 0 ); diff --git a/include/Pattern.h b/include/Pattern.h index 3a1cc941c..5192da9fa 100644 --- a/include/Pattern.h +++ b/include/Pattern.h @@ -94,9 +94,9 @@ public: Pattern * nextPattern() const; // settings-management - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); - inline virtual QString nodeName() const + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; + inline QString nodeName() const override { return "pattern"; } @@ -109,7 +109,7 @@ public: bool empty(); - virtual TrackContentObjectView * createView( TrackView * _tv ); + TrackContentObjectView * createView( TrackView * _tv ) override; using Model::dataChanged; @@ -182,7 +182,7 @@ public: void setMutedNoteBorderColor(QColor const & color) { m_mutedNoteBorderColor = color; } public slots: - virtual void update(); + void update() override; protected slots: @@ -194,11 +194,11 @@ protected slots: protected: - virtual void constructContextMenu( QMenu * ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseDoubleClickEvent( QMouseEvent * _me ); - virtual void paintEvent( QPaintEvent * pe ); - virtual void wheelEvent( QWheelEvent * _we ); + void constructContextMenu( QMenu * ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseDoubleClickEvent( QMouseEvent * _me ) override; + void paintEvent( QPaintEvent * pe ) override; + void wheelEvent( QWheelEvent * _we ) override; private: diff --git a/include/PeakController.h b/include/PeakController.h index b2824f0ac..74a3aab59 100644 --- a/include/PeakController.h +++ b/include/PeakController.h @@ -46,9 +46,9 @@ public: virtual ~PeakController(); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); - virtual void loadSettings( const QDomElement & _this ); - virtual QString nodeName() const; + void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; + void loadSettings( const QDomElement & _this ) override; + QString nodeName() const override; static void initGetControllerBySetting(); static PeakController * getControllerBySetting( const QDomElement & _this ); @@ -57,13 +57,13 @@ public: public slots: - virtual ControllerDialog * createDialog( QWidget * _parent ); + ControllerDialog * createDialog( QWidget * _parent ) override; void handleDestroyedEffect( ); void updateCoeffs(); protected: // The internal per-controller get-value function - virtual void updateValueBuffer(); + void updateValueBuffer() override; PeakControllerEffect * m_peakEffect; @@ -91,9 +91,9 @@ public: virtual ~PeakControllerDialog(); protected: - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void paintEvent( QPaintEvent * _pe ); - virtual void modelChanged(); + void contextMenuEvent( QContextMenuEvent * _me ) override; + void paintEvent( QPaintEvent * _pe ) override; + void modelChanged() override; PeakController * m_peakController; diff --git a/include/PianoRoll.h b/include/PianoRoll.h index b600385c8..27a15149e 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -167,17 +167,17 @@ public: protected: - virtual void keyPressEvent( QKeyEvent * ke ); - virtual void keyReleaseEvent( QKeyEvent * ke ); - virtual void leaveEvent( QEvent * e ); - virtual void mousePressEvent( QMouseEvent * me ); - virtual void mouseDoubleClickEvent( QMouseEvent * me ); - virtual void mouseReleaseEvent( QMouseEvent * me ); - virtual void mouseMoveEvent( QMouseEvent * me ); - virtual void paintEvent( QPaintEvent * pe ); - virtual void resizeEvent( QResizeEvent * re ); - virtual void wheelEvent( QWheelEvent * we ); - virtual void focusOutEvent( QFocusEvent * ); + void keyPressEvent( QKeyEvent * ke ) override; + void keyReleaseEvent( QKeyEvent * ke ) override; + void leaveEvent( QEvent * e ) override; + void mousePressEvent( QMouseEvent * me ) override; + void mouseDoubleClickEvent( QMouseEvent * me ) override; + void mouseReleaseEvent( QMouseEvent * me ) override; + void mouseMoveEvent( QMouseEvent * me ) override; + void paintEvent( QPaintEvent * pe ) override; + void resizeEvent( QResizeEvent * re ) override; + void wheelEvent( QWheelEvent * we ) override; + void focusOutEvent( QFocusEvent * ) override; int getKey( int y ) const; static void drawNoteRect( QPainter & p, int x, int y, @@ -460,11 +460,11 @@ public: int quantization() const; - void play(); - void stop(); - void record(); - void recordAccompany(); - void toggleStepRecording(); + void play() override; + void stop() override; + void record() override; + void recordAccompany() override; + void toggleStepRecording() override; void stopRecording(); bool isRecording() const; @@ -474,15 +474,15 @@ public: using SerializingObject::saveState; using SerializingObject::restoreState; - virtual void saveSettings(QDomDocument & doc, QDomElement & de ); - virtual void loadSettings( const QDomElement & de ); + void saveSettings(QDomDocument & doc, QDomElement & de ) override; + void loadSettings( const QDomElement & de ) override; - inline virtual QString nodeName() const + inline QString nodeName() const override { return "pianoroll"; } - QSize sizeHint() const; + QSize sizeHint() const override; signals: void currentPatternChanged(); @@ -494,7 +494,7 @@ private slots: private: void patternRenamed(); - void focusInEvent(QFocusEvent * event); + void focusInEvent(QFocusEvent * event) override; void stopStepRecording(); void updateStepRecordingIcon(); diff --git a/include/PianoView.h b/include/PianoView.h index 2a362c584..b793ee768 100644 --- a/include/PianoView.h +++ b/include/PianoView.h @@ -44,19 +44,19 @@ public: public: - virtual void keyPressEvent( QKeyEvent * ke ); - virtual void keyReleaseEvent( QKeyEvent * ke ); + void keyPressEvent( QKeyEvent * ke ) override; + void keyReleaseEvent( QKeyEvent * ke ) override; protected: - virtual void modelChanged(); - virtual void contextMenuEvent( QContextMenuEvent * _me ); - virtual void paintEvent( QPaintEvent * ); - virtual void mousePressEvent( QMouseEvent * me ); - virtual void mouseReleaseEvent( QMouseEvent * me ); - virtual void mouseMoveEvent( QMouseEvent * me ); - virtual void focusOutEvent( QFocusEvent * _fe ); - virtual void resizeEvent( QResizeEvent * _event ); + void modelChanged() override; + void contextMenuEvent( QContextMenuEvent * _me ) override; + void paintEvent( QPaintEvent * ) override; + void mousePressEvent( QMouseEvent * me ) override; + void mouseReleaseEvent( QMouseEvent * me ) override; + void mouseMoveEvent( QMouseEvent * me ) override; + void focusOutEvent( QFocusEvent * _fe ) override; + void resizeEvent( QResizeEvent * _event ) override; private: diff --git a/include/PixmapButton.h b/include/PixmapButton.h index 6ee7bcdc8..e2fb58885 100644 --- a/include/PixmapButton.h +++ b/include/PixmapButton.h @@ -42,17 +42,17 @@ public: void setActiveGraphic( const QPixmap & _pm ); void setInactiveGraphic( const QPixmap & _pm, bool _update = true ); - QSize sizeHint() const; + QSize sizeHint() const override; signals: void doubleClicked(); protected: - virtual void paintEvent( QPaintEvent * _pe ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); - virtual void mouseDoubleClickEvent( QMouseEvent * _me ); + void paintEvent( QPaintEvent * _pe ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; + void mouseDoubleClickEvent( QMouseEvent * _me ) override; private: diff --git a/include/PlayHandle.h b/include/PlayHandle.h index 1760e1ec7..1ddd632d1 100644 --- a/include/PlayHandle.h +++ b/include/PlayHandle.h @@ -87,9 +87,9 @@ public: } // required for ThreadableJob - virtual void doProcessing(); + void doProcessing() override; - virtual bool requiresProcessing() const + bool requiresProcessing() const override { return !isFinished(); } diff --git a/include/Plugin.h b/include/Plugin.h index af42b0f10..cb8995bf2 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -239,7 +239,7 @@ public: virtual ~Plugin(); //! Return display-name out of sub plugin or descriptor - virtual QString displayName() const; + QString displayName() const override; //! Return logo out of sub plugin or descriptor const PixmapLoader *logo() const; diff --git a/include/PresetPreviewPlayHandle.h b/include/PresetPreviewPlayHandle.h index 57996fa17..a95b680ab 100644 --- a/include/PresetPreviewPlayHandle.h +++ b/include/PresetPreviewPlayHandle.h @@ -38,15 +38,15 @@ public: PresetPreviewPlayHandle( const QString& presetFile, bool loadByPlugin = false, DataFile *dataFile = 0 ); virtual ~PresetPreviewPlayHandle(); - virtual inline bool affinityMatters() const + inline bool affinityMatters() const override { return true; } - virtual void play( sampleFrame* buffer ); - virtual bool isFinished() const; + void play( sampleFrame* buffer ) override; + bool isFinished() const override; - virtual bool isFromTrack( const Track * _track ) const; + bool isFromTrack( const Track * _track ) const override; static void init(); static void cleanup(); diff --git a/include/ProjectNotes.h b/include/ProjectNotes.h index ab82e4eeb..fc97a8844 100644 --- a/include/ProjectNotes.h +++ b/include/ProjectNotes.h @@ -47,17 +47,17 @@ public: void clear(); void setText( const QString & _text ); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; - inline virtual QString nodeName() const + inline QString nodeName() const override { return "projectnotes"; } protected: - virtual void closeEvent( QCloseEvent * _ce ); + void closeEvent( QCloseEvent * _ce ) override; void setupActions(); diff --git a/include/ProjectRenderer.h b/include/ProjectRenderer.h index 4f932ad34..1af9d422d 100644 --- a/include/ProjectRenderer.h +++ b/include/ProjectRenderer.h @@ -86,7 +86,7 @@ signals: private: - virtual void run(); + void run() override; AudioFileDevice * m_fileDev; Mixer::qualitySettings m_qualitySettings; diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index 27c658a3d..862370d1f 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -766,7 +766,7 @@ public: } private: - virtual void run(); + void run() override; RemotePlugin * m_plugin; volatile bool m_quit; @@ -803,7 +803,7 @@ public: m_failed = waitForMessage( IdInitDone, _busyWaiting ).id != IdInitDone; } - virtual bool processMessage( const message & _m ); + bool processMessage( const message & _m ) override; bool process( const sampleFrame * _in_buf, sampleFrame * _out_buf ); diff --git a/include/RenameDialog.h b/include/RenameDialog.h index c1d24a8a9..8f526badd 100644 --- a/include/RenameDialog.h +++ b/include/RenameDialog.h @@ -42,8 +42,8 @@ public: protected: - void keyPressEvent( QKeyEvent * _ke ); - virtual void resizeEvent(QResizeEvent * event); + void keyPressEvent( QKeyEvent * _ke ) override; + void resizeEvent(QResizeEvent * event) override; protected slots: diff --git a/include/RowTableView.h b/include/RowTableView.h index 537792a05..a7b07c2c8 100644 --- a/include/RowTableView.h +++ b/include/RowTableView.h @@ -38,11 +38,11 @@ public: RowTableView( QWidget * parent = 0 ); virtual ~RowTableView(); - virtual void setModel( QAbstractItemModel * model ); + void setModel( QAbstractItemModel * model ) override; protected: - virtual void keyPressEvent( QKeyEvent * event ); + void keyPressEvent( QKeyEvent * event ) override; private: diff --git a/include/Rubberband.h b/include/Rubberband.h index bc9f3c6a2..eeb3c7e5b 100644 --- a/include/Rubberband.h +++ b/include/Rubberband.h @@ -83,7 +83,7 @@ public: protected: - virtual void resizeEvent( QResizeEvent * _re ); + void resizeEvent( QResizeEvent * _re ) override; private: diff --git a/include/SamplePlayHandle.h b/include/SamplePlayHandle.h index d10c44837..33f5ebe52 100644 --- a/include/SamplePlayHandle.h +++ b/include/SamplePlayHandle.h @@ -44,16 +44,16 @@ public: SamplePlayHandle( SampleTCO* tco ); virtual ~SamplePlayHandle(); - virtual inline bool affinityMatters() const + inline bool affinityMatters() const override { return true; } - virtual void play( sampleFrame * buffer ); - virtual bool isFinished() const; + void play( sampleFrame * buffer ) override; + bool isFinished() const override; - virtual bool isFromTrack( const Track * _track ) const; + bool isFromTrack( const Track * _track ) const override; f_cnt_t totalFrames() const; inline f_cnt_t framesDone() const diff --git a/include/SampleRecordHandle.h b/include/SampleRecordHandle.h index 22d9bf315..fc40d0622 100644 --- a/include/SampleRecordHandle.h +++ b/include/SampleRecordHandle.h @@ -44,10 +44,10 @@ public: SampleRecordHandle( SampleTCO* tco ); virtual ~SampleRecordHandle(); - virtual void play( sampleFrame * _working_buffer ); - virtual bool isFinished() const; + void play( sampleFrame * _working_buffer ) override; + bool isFinished() const override; - virtual bool isFromTrack( const Track * _track ) const; + bool isFromTrack( const Track * _track ) const override; f_cnt_t framesRecorded() const; void createSampleBuffer( SampleBuffer * * _sample_buf ); diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 9469669a1..2bad4d910 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -49,12 +49,12 @@ public: SampleTCO( Track * _track ); virtual ~SampleTCO(); - virtual void changeLength( const MidiTime & _length ); + void changeLength( const MidiTime & _length ) override; const QString & sampleFile() const; - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); - inline virtual QString nodeName() const + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; + inline QString nodeName() const override { return "sampletco"; } @@ -67,7 +67,7 @@ public: MidiTime sampleLength() const; void setSampleStartFrame( f_cnt_t startFrame ); void setSamplePlayLength( f_cnt_t length ); - virtual TrackContentObjectView * createView( TrackView * _tv ); + TrackContentObjectView * createView( TrackView * _tv ) override; bool isPlaying() const; @@ -112,13 +112,13 @@ public slots: protected: - virtual void contextMenuEvent( QContextMenuEvent * _cme ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); - virtual void dragEnterEvent( QDragEnterEvent * _dee ); - virtual void dropEvent( QDropEvent * _de ); - virtual void mouseDoubleClickEvent( QMouseEvent * ); - virtual void paintEvent( QPaintEvent * ); + void contextMenuEvent( QContextMenuEvent * _cme ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void dropEvent( QDropEvent * _de ) override; + void mouseDoubleClickEvent( QMouseEvent * ) override; + void paintEvent( QPaintEvent * ) override; private: @@ -137,14 +137,14 @@ public: virtual ~SampleTrack(); virtual bool play( const MidiTime & _start, const fpp_t _frames, - const f_cnt_t _frame_base, int _tco_num = -1 ); - virtual TrackView * createView( TrackContainerView* tcv ); - virtual TrackContentObject * createTCO(const MidiTime & pos); + const f_cnt_t _frame_base, int _tco_num = -1 ) override; + TrackView * createView( TrackContainerView* tcv ) override; + TrackContentObject * createTCO( const MidiTime & _pos ) override; virtual void saveTrackSpecificSettings( QDomDocument & _doc, - QDomElement & _parent ); - virtual void loadTrackSpecificSettings( const QDomElement & _this ); + QDomElement & _parent ) override; + void loadTrackSpecificSettings( const QDomElement & _this ) override; inline IntModel * effectChannelModel() { @@ -156,7 +156,7 @@ public: return &m_audioPort; } - virtual QString nodeName() const + QString nodeName() const override { return "sampletrack"; } @@ -204,7 +204,7 @@ public: } - virtual QMenu * createFxMenu( QString title, QString newFxLabel ); + QMenu * createFxMenu( QString title, QString newFxLabel ) override; public slots: @@ -212,14 +212,14 @@ public slots: protected: - void modelChanged(); - virtual QString nodeName() const + void modelChanged() override; + QString nodeName() const override { return "SampleTrackView"; } - void dragEnterEvent(QDragEnterEvent *dee); - void dropEvent(QDropEvent *de); + void dragEnterEvent(QDragEnterEvent *dee) override; + void dropEvent(QDropEvent *de) override; private slots: void assignFxLine( int channelIndex ); @@ -273,13 +273,13 @@ public slots: protected: // capture close-events for toggling sample-track-button - virtual void closeEvent(QCloseEvent * ce); + void closeEvent(QCloseEvent * ce) override; - virtual void saveSettings(QDomDocument & doc, QDomElement & element); - virtual void loadSettings(const QDomElement & element); + void saveSettings(QDomDocument & doc, QDomElement & element) override; + void loadSettings(const QDomElement & element) override; private: - virtual void modelChanged(); + void modelChanged() override; SampleTrack * m_track; SampleTrackView * m_stv; diff --git a/include/SendButtonIndicator.h b/include/SendButtonIndicator.h index 97acde1ba..b60113758 100644 --- a/include/SendButtonIndicator.h +++ b/include/SendButtonIndicator.h @@ -17,7 +17,7 @@ public: SendButtonIndicator( QWidget * _parent, FxLine * _owner, FxMixerView * _mv); - virtual void mousePressEvent( QMouseEvent * e ); + void mousePressEvent( QMouseEvent * e ) override; void updateLightStatus(); private: diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 4a2e5c025..9f9ae1b3f 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -63,7 +63,7 @@ public: protected slots: - virtual void accept(); + void accept() override; private slots: diff --git a/include/SideBarWidget.h b/include/SideBarWidget.h index 37acc9770..9972daa7c 100644 --- a/include/SideBarWidget.h +++ b/include/SideBarWidget.h @@ -52,9 +52,9 @@ signals: void closeButtonClicked(); protected: - virtual void paintEvent( QPaintEvent * _pe ); - virtual void resizeEvent( QResizeEvent * _re ); - virtual void contextMenuEvent( QContextMenuEvent * ) + void paintEvent( QPaintEvent * _pe ) override; + void resizeEvent( QResizeEvent * _re ) override; + void contextMenuEvent( QContextMenuEvent * ) override { } diff --git a/include/Song.h b/include/Song.h index 1e047caaa..d398a168c 100644 --- a/include/Song.h +++ b/include/Song.h @@ -261,7 +261,7 @@ public: bpm_t getTempo(); - virtual AutomationPattern * tempoAutomationPattern(); + AutomationPattern * tempoAutomationPattern() override; AutomationTrack * globalAutomationTrack() { @@ -269,7 +269,7 @@ public: } //TODO: Add Q_DECL_OVERRIDE when Qt4 is dropped - AutomatedValueMap automatedValuesAt(MidiTime time, int tcoNum = -1) const; + AutomatedValueMap automatedValuesAt(MidiTime time, int tcoNum = -1) const override; // file management void createNewProject(); @@ -305,7 +305,7 @@ public: return m_modified; } - virtual QString nodeName() const + QString nodeName() const override { return "song"; } diff --git a/include/SongEditor.h b/include/SongEditor.h index 6f39cc1d9..9621bcc23 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -51,7 +51,7 @@ public: positionLine( QWidget * parent ); private: - virtual void paintEvent( QPaintEvent * pe ); + void paintEvent( QPaintEvent * pe ) override; } ; @@ -69,8 +69,8 @@ public: SongEditor( Song * song ); ~SongEditor(); - void saveSettings( QDomDocument& doc, QDomElement& element ); - void loadSettings( const QDomElement& element ); + void saveSettings( QDomDocument& doc, QDomElement& element ) override; + void loadSettings( const QDomElement& element ) override; ComboBoxModel *zoomingModel() const; ComboBoxModel *snappingModel() const; @@ -93,10 +93,10 @@ public slots: void selectAllTcos( bool select ); protected: - virtual void closeEvent( QCloseEvent * ce ); - virtual void mousePressEvent(QMouseEvent * me); - virtual void mouseMoveEvent(QMouseEvent * me); - virtual void mouseReleaseEvent(QMouseEvent * me); + void closeEvent( QCloseEvent * ce ) override; + void mousePressEvent(QMouseEvent * me) override; + void mouseMoveEvent(QMouseEvent * me) override; + void mouseReleaseEvent(QMouseEvent * me) override; private slots: void setHighQuality( bool ); @@ -116,10 +116,10 @@ private slots: void zoomingChanged(); private: - virtual void keyPressEvent( QKeyEvent * ke ); - virtual void wheelEvent( QWheelEvent * we ); + void keyPressEvent( QKeyEvent * ke ) override; + void wheelEvent( QWheelEvent * we ) override; - virtual bool allowRubberband() const; + bool allowRubberband() const override; int trackIndexFromSelectionPoint(int yPos); int indexOfTrackView(const TrackView* tv); @@ -175,19 +175,19 @@ class SongEditorWindow : public Editor public: SongEditorWindow( Song* song ); - QSize sizeHint() const; + QSize sizeHint() const override; SongEditor* m_editor; protected: - virtual void resizeEvent( QResizeEvent * event ); - virtual void changeEvent( QEvent * ); + void resizeEvent( QResizeEvent * event ) override; + void changeEvent( QEvent * ) override; protected slots: - void play(); - void record(); - void recordAccompany(); - void stop(); + void play() override; + void record() override; + void recordAccompany() override; + void stop() override; void lostFocus(); void adjustUiAfterProjectLoad(); @@ -199,8 +199,8 @@ signals: void resized(); private: - virtual void keyPressEvent( QKeyEvent * ke ); - virtual void keyReleaseEvent( QKeyEvent * ke ); + void keyPressEvent( QKeyEvent * ke ) override; + void keyReleaseEvent( QKeyEvent * ke ) override; QAction* m_addBBTrackAction; QAction* m_addSampleTrackAction; diff --git a/include/StepRecorderWidget.h b/include/StepRecorderWidget.h index cafce2b24..14cfc2eed 100644 --- a/include/StepRecorderWidget.h +++ b/include/StepRecorderWidget.h @@ -55,7 +55,7 @@ public: void showHint(); private: - virtual void paintEvent(QPaintEvent * pe); + void paintEvent(QPaintEvent * pe) override; int xCoordOfTick(int tick); diff --git a/include/SubWindow.h b/include/SubWindow.h index 5d7a810c2..148cf2c99 100644 --- a/include/SubWindow.h +++ b/include/SubWindow.h @@ -67,10 +67,10 @@ public: protected: // hook the QWidget move/resize events to update the tracked geometry - virtual void moveEvent( QMoveEvent * event ); - virtual void resizeEvent( QResizeEvent * event ); - virtual void paintEvent( QPaintEvent * pe ); - virtual void changeEvent( QEvent * event ); + void moveEvent( QMoveEvent * event ) override; + void resizeEvent( QResizeEvent * event ) override; + void paintEvent( QPaintEvent * pe ) override; + void changeEvent( QEvent * event ) override; signals: void focusLost(); diff --git a/include/TabWidget.h b/include/TabWidget.h index 88ecf9034..0cf15155b 100644 --- a/include/TabWidget.h +++ b/include/TabWidget.h @@ -72,13 +72,13 @@ public: void setTabBorder( const QColor & c ); protected: - virtual bool event( QEvent * event ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void paintEvent( QPaintEvent * _pe ); - virtual void resizeEvent( QResizeEvent * _re ); - virtual void wheelEvent( QWheelEvent * _we ); - virtual QSize minimumSizeHint() const; - virtual QSize sizeHint() const; + bool event( QEvent * event ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void paintEvent( QPaintEvent * _pe ) override; + void resizeEvent( QResizeEvent * _re ) override; + void wheelEvent( QWheelEvent * _we ) override; + QSize minimumSizeHint() const override; + QSize sizeHint() const override; private: struct widgetDesc diff --git a/include/TempoSyncKnob.h b/include/TempoSyncKnob.h index 034e2b8f4..416abe1fc 100644 --- a/include/TempoSyncKnob.h +++ b/include/TempoSyncKnob.h @@ -52,7 +52,7 @@ public: return castModel(); } - virtual void modelChanged(); + void modelChanged() override; signals: @@ -61,7 +61,7 @@ signals: protected: - virtual void contextMenuEvent( QContextMenuEvent * _me ); + void contextMenuEvent( QContextMenuEvent * _me ) override; protected slots: diff --git a/include/TempoSyncKnobModel.h b/include/TempoSyncKnobModel.h index 9aaf48fea..b9512aa1a 100644 --- a/include/TempoSyncKnobModel.h +++ b/include/TempoSyncKnobModel.h @@ -52,7 +52,7 @@ public: const float _max, const float _step, const float _scale, Model * _parent, const QString & _display_name = QString() ); - virtual ~TempoSyncKnobModel() override; + ~TempoSyncKnobModel() override; void saveSettings( QDomDocument & _doc, QDomElement & _this, const QString& name ) override; void loadSettings( const QDomElement & _this, const QString& name ) override; diff --git a/include/TextFloat.h b/include/TextFloat.h index ed10516b5..8f940c591 100644 --- a/include/TextFloat.h +++ b/include/TextFloat.h @@ -66,8 +66,8 @@ public: protected: - virtual void paintEvent( QPaintEvent * _me ); - virtual void mousePressEvent( QMouseEvent * _me ); + void paintEvent( QPaintEvent * _me ) override; + void mousePressEvent( QMouseEvent * _me ) override; private: diff --git a/include/TimeDisplayWidget.h b/include/TimeDisplayWidget.h index 175f159c9..e7e5cb210 100644 --- a/include/TimeDisplayWidget.h +++ b/include/TimeDisplayWidget.h @@ -41,7 +41,7 @@ public: protected: - virtual void mousePressEvent( QMouseEvent* mouseEvent ); + void mousePressEvent( QMouseEvent* mouseEvent ) override; private slots: diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 374a26418..bc0881f82 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -153,9 +153,9 @@ public: void addToolButtons(QToolBar* _tool_bar ); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); - inline virtual QString nodeName() const + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; + inline QString nodeName() const override { return "timeline"; } @@ -184,10 +184,10 @@ public slots: protected: - virtual void paintEvent( QPaintEvent * _pe ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseMoveEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); + void paintEvent( QPaintEvent * _pe ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseMoveEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; private: diff --git a/include/Track.h b/include/Track.h index 4adf6378e..70e49a1c6 100644 --- a/include/Track.h +++ b/include/Track.h @@ -98,7 +98,7 @@ public: emit dataChanged(); } - virtual QString displayName() const + QString displayName() const override { return name(); } @@ -246,21 +246,21 @@ public slots: virtual bool close(); void cut(); void remove(); - virtual void update(); + void update() override; protected: virtual void constructContextMenu( QMenu * ) { } - virtual void contextMenuEvent( QContextMenuEvent * cme ); - virtual void dragEnterEvent( QDragEnterEvent * dee ); - virtual void dropEvent( QDropEvent * de ); - virtual void leaveEvent( QEvent * e ); - virtual void mousePressEvent( QMouseEvent * me ); - virtual void mouseMoveEvent( QMouseEvent * me ); - virtual void mouseReleaseEvent( QMouseEvent * me ); - virtual void resizeEvent( QResizeEvent * re ) + void contextMenuEvent( QContextMenuEvent * cme ) override; + void dragEnterEvent( QDragEnterEvent * dee ) override; + void dropEvent( QDropEvent * de ) override; + void leaveEvent( QEvent * e ) override; + void mousePressEvent( QMouseEvent * me ) override; + void mouseMoveEvent( QMouseEvent * me ) override; + void mouseReleaseEvent( QMouseEvent * me ) override; + void resizeEvent( QResizeEvent * re ) override { m_needsUpdate = true; selectableObject::resizeEvent( re ); @@ -381,24 +381,24 @@ public slots: void changePosition( const MidiTime & newPos = MidiTime( -1 ) ); protected: - virtual void dragEnterEvent( QDragEnterEvent * dee ); - virtual void dropEvent( QDropEvent * de ); - virtual void mousePressEvent( QMouseEvent * me ); - virtual void paintEvent( QPaintEvent * pe ); - virtual void resizeEvent( QResizeEvent * re ); + void dragEnterEvent( QDragEnterEvent * dee ) override; + void dropEvent( QDropEvent * de ) override; + void mousePressEvent( QMouseEvent * me ) override; + void paintEvent( QPaintEvent * pe ) override; + void resizeEvent( QResizeEvent * re ) override; - virtual QString nodeName() const + QString nodeName() const override { return "trackcontentwidget"; } - virtual void saveSettings( QDomDocument& doc, QDomElement& element ) + void saveSettings( QDomDocument& doc, QDomElement& element ) override { Q_UNUSED(doc) Q_UNUSED(element) } - virtual void loadSettings( const QDomElement& element ) + void loadSettings( const QDomElement& element ) override { Q_UNUSED(element) } @@ -435,8 +435,8 @@ public: protected: - virtual void mousePressEvent( QMouseEvent * me ); - virtual void paintEvent( QPaintEvent * pe ); + void mousePressEvent( QMouseEvent * me ) override; + void paintEvent( QPaintEvent * pe ) override; private slots: @@ -518,8 +518,8 @@ public: virtual void loadTrackSpecificSettings( const QDomElement & element ) = 0; - virtual void saveSettings( QDomDocument & doc, QDomElement & element ); - virtual void loadSettings( const QDomElement & element ); + void saveSettings( QDomDocument & doc, QDomElement & element ) override; + void loadSettings( const QDomElement & element ) override; void setSimpleSerializing() { @@ -564,7 +564,7 @@ public: return m_name; } - virtual QString displayName() const + QString displayName() const override { return name(); } @@ -692,32 +692,32 @@ public slots: protected: - virtual void modelChanged(); + void modelChanged() override; - virtual void saveSettings( QDomDocument& doc, QDomElement& element ) + void saveSettings( QDomDocument& doc, QDomElement& element ) override { Q_UNUSED(doc) Q_UNUSED(element) } - virtual void loadSettings( const QDomElement& element ) + void loadSettings( const QDomElement& element ) override { Q_UNUSED(element) } - virtual QString nodeName() const + QString nodeName() const override { return "trackview"; } - virtual void dragEnterEvent( QDragEnterEvent * dee ); - virtual void dropEvent( QDropEvent * de ); - virtual void mousePressEvent( QMouseEvent * me ); - virtual void mouseMoveEvent( QMouseEvent * me ); - virtual void mouseReleaseEvent( QMouseEvent * me ); - virtual void paintEvent( QPaintEvent * pe ); - virtual void resizeEvent( QResizeEvent * re ); + void dragEnterEvent( QDragEnterEvent * dee ) override; + void dropEvent( QDropEvent * de ) override; + void mousePressEvent( QMouseEvent * me ) override; + void mouseMoveEvent( QMouseEvent * me ) override; + void mouseReleaseEvent( QMouseEvent * me ) override; + void paintEvent( QPaintEvent * pe ) override; + void resizeEvent( QResizeEvent * re ) override; private: diff --git a/include/TrackContainer.h b/include/TrackContainer.h index 1caca922e..fd853a73c 100644 --- a/include/TrackContainer.h +++ b/include/TrackContainer.h @@ -51,9 +51,9 @@ public: TrackContainer(); virtual ~TrackContainer(); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; - virtual void loadSettings( const QDomElement & _this ); + void loadSettings( const QDomElement & _this ) override; virtual AutomationPattern * tempoAutomationPattern() @@ -124,7 +124,7 @@ public: { } - virtual QString nodeName() const + QString nodeName() const override { return "DummyTrackContainer"; } diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index cf79a0a21..6e952189b 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -49,8 +49,8 @@ public: TrackContainerView( TrackContainer* tc ); virtual ~TrackContainerView(); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); - virtual void loadSettings( const QDomElement & _this ); + void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; + void loadSettings( const QDomElement & _this ) override; QScrollArea * contentWidget() { @@ -116,7 +116,7 @@ public: void clearAllTracks(); - virtual QString nodeName() const + QString nodeName() const override { return "trackcontainerview"; } @@ -129,8 +129,8 @@ public slots: TrackView * createTrackView( Track * _t ); void deleteTrackView( TrackView * _tv ); - virtual void dropEvent( QDropEvent * _de ); - virtual void dragEnterEvent( QDragEnterEvent * _dee ); + void dropEvent( QDropEvent * _de ) override; + void dragEnterEvent( QDragEnterEvent * _dee ) override; /// /// \brief stopRubberBand @@ -141,8 +141,7 @@ public slots: protected: static const int DEFAULT_PIXELS_PER_BAR = 16; - - virtual void resizeEvent( QResizeEvent * ); + void resizeEvent( QResizeEvent * ) override; MidiTime m_currentPosition; @@ -161,7 +160,7 @@ private: virtual ~scrollArea(); protected: - virtual void wheelEvent( QWheelEvent * _we ); + void wheelEvent( QWheelEvent * _we ) override; private: TrackContainerView* m_trackContainerView; @@ -195,7 +194,7 @@ public: InstrumentLoaderThread( QObject *parent = 0, InstrumentTrack *it = 0, QString name = "" ); - void run(); + void run() override; private: InstrumentTrack *m_it; diff --git a/include/TrackLabelButton.h b/include/TrackLabelButton.h index f1059bdbf..7d9726feb 100644 --- a/include/TrackLabelButton.h +++ b/include/TrackLabelButton.h @@ -49,13 +49,13 @@ public slots: protected: - virtual void dragEnterEvent( QDragEnterEvent * _dee ); - virtual void dropEvent( QDropEvent * _de ); - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseDoubleClickEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); - virtual void paintEvent( QPaintEvent * _pe ); - virtual void resizeEvent( QResizeEvent * _re ); + void dragEnterEvent( QDragEnterEvent * _dee ) override; + void dropEvent( QDropEvent * _de ) override; + void mousePressEvent( QMouseEvent * _me ) override; + void mouseDoubleClickEvent( QMouseEvent * _me ) override; + void mouseReleaseEvent( QMouseEvent * _me ) override; + void paintEvent( QPaintEvent * _pe ) override; + void resizeEvent( QResizeEvent * _re ) override; private: diff --git a/include/TrackRenameLineEdit.h b/include/TrackRenameLineEdit.h index 6883b9b05..e681a2d21 100644 --- a/include/TrackRenameLineEdit.h +++ b/include/TrackRenameLineEdit.h @@ -37,7 +37,7 @@ public: void show(); protected: - virtual void keyPressEvent( QKeyEvent * ke ); + void keyPressEvent( QKeyEvent * ke ) override; private: QString m_oldName; diff --git a/include/VisualizationWidget.h b/include/VisualizationWidget.h index a178b1ad8..85825975a 100644 --- a/include/VisualizationWidget.h +++ b/include/VisualizationWidget.h @@ -61,8 +61,8 @@ public: protected: - virtual void paintEvent( QPaintEvent * _pe ); - virtual void mousePressEvent( QMouseEvent * _me ); + void paintEvent( QPaintEvent * _pe ) override; + void mousePressEvent( QMouseEvent * _me ) override; protected slots: diff --git a/plugins/VstEffect/VstEffectControlDialog.h b/plugins/VstEffect/VstEffectControlDialog.h index 3cd9af360..930f514c9 100644 --- a/plugins/VstEffect/VstEffectControlDialog.h +++ b/plugins/VstEffect/VstEffectControlDialog.h @@ -49,8 +49,8 @@ public: virtual ~VstEffectControlDialog(); protected: - virtual void paintEvent( QPaintEvent * _pe ); - virtual void showEvent( QShowEvent* _se ) override; + void paintEvent( QPaintEvent * _pe ) override; + void showEvent( QShowEvent* _se ) override; private: QWidget * m_pluginWidget; diff --git a/plugins/peak_controller_effect/peak_controller_effect.h b/plugins/peak_controller_effect/peak_controller_effect.h index 093b56437..a872e2b8b 100644 --- a/plugins/peak_controller_effect/peak_controller_effect.h +++ b/plugins/peak_controller_effect/peak_controller_effect.h @@ -36,9 +36,9 @@ public: const Descriptor::SubPluginFeatures::Key * _key ); virtual ~PeakControllerEffect(); virtual bool processAudioBuffer( sampleFrame * _buf, - const fpp_t _frames ); + const fpp_t _frames ) override; - virtual EffectControls * controls() + EffectControls * controls() override { return &m_peakControls; } diff --git a/plugins/peak_controller_effect/peak_controller_effect_controls.h b/plugins/peak_controller_effect/peak_controller_effect_controls.h index 784df4d8d..fe90eddca 100644 --- a/plugins/peak_controller_effect/peak_controller_effect_controls.h +++ b/plugins/peak_controller_effect/peak_controller_effect_controls.h @@ -41,18 +41,18 @@ public: { } - virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); - virtual void loadSettings( const QDomElement & _this ); - inline virtual QString nodeName() const + void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; + void loadSettings( const QDomElement & _this ) override; + inline QString nodeName() const override { return "peakcontrollereffectcontrols"; } - virtual int controlCount() + int controlCount() override { return 1; } - virtual EffectControlDialog * createView() + EffectControlDialog * createView() override { return new PeakControllerEffectControlDialog( this ); } diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index 5b459d40d..26e7fec36 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -49,7 +49,7 @@ public: void tryLoad( const QString &remoteVstPluginExecutable ); - virtual bool processMessage( const message & _m ); + bool processMessage( const message & _m ) override; inline bool hasEditor() const { @@ -99,17 +99,17 @@ public: QWidget * pluginWidget(); - virtual void loadSettings( const QDomElement & _this ); - virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); + void loadSettings( const QDomElement & _this ) override; + void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; - inline virtual QString nodeName() const + virtual QString nodeName() const override { return "vstplugin"; } virtual void createUI(QWidget *parent); - bool eventFilter(QObject *obj, QEvent *event); + bool eventFilter(QObject *obj, QEvent *event) override; QString embedMethod() const; diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index ca0e52194..11b145b22 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -54,7 +54,7 @@ public: { } - virtual QString nodeName() const + QString nodeName() const override { return "previewtrackcontainer"; } diff --git a/src/gui/ControllerConnectionDialog.cpp b/src/gui/ControllerConnectionDialog.cpp index 02857b034..f0d3d10e9 100644 --- a/src/gui/ControllerConnectionDialog.cpp +++ b/src/gui/ControllerConnectionDialog.cpp @@ -63,7 +63,7 @@ public: } - virtual void processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset = 0 ) + void processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset = 0 ) override { if( event.type() == MidiControlChange && ( m_midiPort.inputChannel() == 0 || m_midiPort.inputChannel() == event.channel() + 1 ) ) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 4ae2a630a..54bbff70f 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -115,7 +115,7 @@ FxMixerView::FxMixerView() : ChannelArea( QWidget * parent, FxMixerView * mv ) : QScrollArea( parent ), m_mv( mv ) {} ~ChannelArea() {} - virtual void keyPressEvent( QKeyEvent * e ) + void keyPressEvent( QKeyEvent * e ) override { m_mv->keyPressEvent( e ); } diff --git a/src/gui/RowTableView.cpp b/src/gui/RowTableView.cpp index a4daeb17f..9830354be 100644 --- a/src/gui/RowTableView.cpp +++ b/src/gui/RowTableView.cpp @@ -39,12 +39,12 @@ public: } virtual void paint( QPainter * painter, const QStyleOptionViewItem & option, - const QModelIndex & index ) const; + const QModelIndex & index ) const override; protected: virtual void initStyleOption( QStyleOptionViewItem * option, - const QModelIndex & index ) const; + const QModelIndex & index ) const override; private: diff --git a/src/gui/widgets/SideBar.cpp b/src/gui/widgets/SideBar.cpp index cc83abae4..36c417b93 100644 --- a/src/gui/widgets/SideBar.cpp +++ b/src/gui/widgets/SideBar.cpp @@ -49,7 +49,7 @@ public: return m_orientation; } - virtual QSize sizeHint() const + QSize sizeHint() const override { QSize s = QToolButton::sizeHint(); s.setWidth( s.width() + 8 ); @@ -62,7 +62,7 @@ public: protected: - virtual void paintEvent( QPaintEvent * ) + void paintEvent( QPaintEvent * ) override { QStylePainter p( this ); QStyleOptionToolButton opt; From 55b65527c3858fb82bf465de8786071752146e4d Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Fri, 1 Nov 2019 07:26:38 +0200 Subject: [PATCH 080/160] appimage: move launcher code into launch_lmms.sh. --- cmake/linux/launch_lmms.sh | 24 ++++++++++++++++++++++++ cmake/linux/package_linux.sh.in | 31 +++---------------------------- 2 files changed, 27 insertions(+), 28 deletions(-) create mode 100644 cmake/linux/launch_lmms.sh diff --git a/cmake/linux/launch_lmms.sh b/cmake/linux/launch_lmms.sh new file mode 100644 index 000000000..4a0eb30e2 --- /dev/null +++ b/cmake/linux/launch_lmms.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +export PATH="$PATH:/sbin" +if which carla > /dev/null 2>&1; then + CARLAPATH="$(which carla)" + CARLAPREFIX="${CARLAPATH%/bin*}" + echo "Carla appears to be installed on this system at $CARLAPREFIX/lib[64]/carla so we'll use it." + export LD_LIBRARY_PATH=$CARLAPREFIX/lib/carla:$CARLAPREFIX/lib64/carla:$LD_LIBRARY_PATH +else + echo "Carla does not appear to be installed. That's OK, please ignore any related library errors." +fi +export LD_LIBRARY_PATH=$DIR/usr/lib/:$DIR/usr/lib/lmms:$LD_LIBRARY_PATH +# Prevent segfault on VirualBox +if lsmod |grep vboxguest > /dev/null 2>&1; then + echo "VirtualBox detected. Forcing libgl software rendering." + export LIBGL_ALWAYS_SOFTWARE=1; +fi +if ldconfig -p | grep libjack.so.0 > /dev/null 2>&1; then + echo "Jack appears to be installed on this system, so we'll use it." +else + echo "Jack does not appear to be installed. That's OK, we'll use a dummy version instead." + export LD_LIBRARY_PATH=$DIR/usr/lib/lmms/optional:$LD_LIBRARY_PATH +fi +QT_X11_NO_NATIVE_MENUBAR=1 $DIR/usr/bin/lmms.real "$@" \ No newline at end of file diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index 7c0e2593b..cb3764dcb 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -96,37 +96,12 @@ cp -R /usr/share/stk/rawwaves/ "${APPDIR}usr/share/stk/" # Create a wrapper script which calls the lmms executable mv "${APPDIR}usr/bin/lmms" "${APPDIR}usr/bin/lmms.real" -# shellcheck disable=SC1083 -cat >"${APPDIR}usr/bin/lmms" < /dev/null 2>&1; then - CARLAPATH="\$(which carla)" - CARLAPREFIX="\${CARLAPATH%/bin*}" - echo "Carla appears to be installed on this system at \$CARLAPREFIX/lib[64]/carla so we'll use it." - export LD_LIBRARY_PATH=\$CARLAPREFIX/lib/carla:\$CARLAPREFIX/lib64/carla:\$LD_LIBRARY_PATH -else - echo "Carla does not appear to be installed. That's OK, please ignore any related library errors." -fi -export LD_LIBRARY_PATH=\$DIR/usr/lib/:\$DIR/usr/lib/lmms:\$LD_LIBRARY_PATH -# Prevent segfault on VirualBox -if lsmod |grep vboxguest > /dev/null 2>&1; then - echo "VirtualBox detected. Forcing libgl software rendering." - export LIBGL_ALWAYS_SOFTWARE=1; -fi -if ldconfig -p | grep libjack.so.0 > /dev/null 2>&1; then - echo "Jack appears to be installed on this system, so we'll use it." -else - echo "Jack does not appear to be installed. That's OK, we'll use a dummy version instead." - export LD_LIBRARY_PATH=\$DIR/usr/lib/lmms/optional:\$LD_LIBRARY_PATH -fi -QT_X11_NO_NATIVE_MENUBAR=1 \$DIR/usr/bin/lmms.real "\$@" -EOL + +cp "@CMAKE_CURRENT_SOURCE_DIR@/launch_lmms.sh" "${APPDIR}usr/bin/lmms" chmod +x "${APPDIR}usr/bin/lmms" -# Per https://github.com/probonopd/linuxdeployqt/issues/129 +# Per https://github.com/probonopd/linuxdeployqt/issues/129 unset LD_LIBRARY_PATH # Ensure linuxdeployqt can find shared objects From 02980e610c960eee97104ceefe0c9563cf0d7ec9 Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Fri, 1 Nov 2019 07:28:05 +0200 Subject: [PATCH 081/160] appimage: Use command -v instead of which (sc2230) --- cmake/linux/launch_lmms.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/linux/launch_lmms.sh b/cmake/linux/launch_lmms.sh index 4a0eb30e2..02cebd5a4 100644 --- a/cmake/linux/launch_lmms.sh +++ b/cmake/linux/launch_lmms.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash +alias standard_which="command -v" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export PATH="$PATH:/sbin" -if which carla > /dev/null 2>&1; then - CARLAPATH="$(which carla)" +if standard_which carla > /dev/null 2>&1; then + CARLAPATH="$(standard_which carla)" CARLAPREFIX="${CARLAPATH%/bin*}" echo "Carla appears to be installed on this system at $CARLAPREFIX/lib[64]/carla so we'll use it." export LD_LIBRARY_PATH=$CARLAPREFIX/lib/carla:$CARLAPREFIX/lib64/carla:$LD_LIBRARY_PATH From 08c7e8e8ddd8000131a583d4bf88d4f29d20b14d Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Fri, 1 Nov 2019 07:29:56 +0200 Subject: [PATCH 082/160] appimage: Escape $DIR to avoid word-splitting --- cmake/linux/launch_lmms.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/linux/launch_lmms.sh b/cmake/linux/launch_lmms.sh index 02cebd5a4..e6432aa3a 100644 --- a/cmake/linux/launch_lmms.sh +++ b/cmake/linux/launch_lmms.sh @@ -22,4 +22,4 @@ else echo "Jack does not appear to be installed. That's OK, we'll use a dummy version instead." export LD_LIBRARY_PATH=$DIR/usr/lib/lmms/optional:$LD_LIBRARY_PATH fi -QT_X11_NO_NATIVE_MENUBAR=1 $DIR/usr/bin/lmms.real "$@" \ No newline at end of file +QT_X11_NO_NATIVE_MENUBAR=1 "$DIR"/usr/bin/lmms.real "$@" \ No newline at end of file From ebf71003c7873d02da10ea44c60e69ca773c79f4 Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Fri, 1 Nov 2019 02:36:54 -0500 Subject: [PATCH 083/160] Fix vertical piano mouse click unresponsiveness `PianoRoll::mouseDoubleClickEvent` wasn't forwarding the event to the base class when not acting on the event. The base class calls `mousePressEvent`. Fixes #3005 --- src/gui/editors/PianoRoll.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 183735450..3b42bd9b2 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1948,6 +1948,10 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) enterValue( &nv ); } } + else + { + QWidget::mouseDoubleClickEvent(me); + } } From a8d91b10e8dd32157d25caeb97597eb018e9405c Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Fri, 1 Nov 2019 02:36:54 -0500 Subject: [PATCH 084/160] Fix vertical piano mouse click unresponsiveness `PianoRoll::mouseDoubleClickEvent` wasn't forwarding the event to the base class when not acting on the event. The base class calls `mousePressEvent`. Fixes #3005 --- src/gui/editors/PianoRoll.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 6c6acf1b0..f6bda682b 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1796,6 +1796,10 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) enterValue( &nv ); } } + else + { + QWidget::mouseDoubleClickEvent(me); + } } From 8a52ddb642d0789fd017fa36e491d187040de594 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 1 Nov 2019 09:56:39 +0100 Subject: [PATCH 085/160] CricleCI: Test merged pull request --- .circleci/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0ab2c51a0..a3be541b5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,6 +42,14 @@ shared: mkdir -p /tmp/artifacts # Workaround for failing submodule fetching git config --global --unset url."ssh://git@github.com".insteadOf || true + if [[ -n "${CIRCLE_PR_NUMBER}" ]] + then + echo "Fetching out merged pull request" + git fetch -u origin refs/pull/${CIRCLE_PR_NUMBER}/merge:pr/merge + git checkout pr/merge + else + echo "Not a pull request" + fi # Commmon environment variables common_environment: &common_environment From 4d9e5e36a6bcb75937b95210c4193ddd10192131 Mon Sep 17 00:00:00 2001 From: Andres Date: Tue, 5 Nov 2019 08:17:25 -0300 Subject: [PATCH 086/160] Sampletrack activity indicator --- include/FadeButton.h | 1 + include/SampleTrack.h | 7 ++++++ src/gui/widgets/FadeButton.cpp | 10 ++++++++ src/tracks/SampleTrack.cpp | 44 +++++++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/include/FadeButton.h b/include/FadeButton.h index 09a4c6457..d63b68cff 100644 --- a/include/FadeButton.h +++ b/include/FadeButton.h @@ -47,6 +47,7 @@ public: public slots: void activate(); void noteEnd(); + void notPlaying(); protected: diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 2bad4d910..c8f94bcd9 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -29,6 +29,7 @@ #include #include "AudioPort.h" +#include "FadeButton.h" #include "FxMixer.h" #include "FxLineLcdSpinBox.h" #include "Track.h" @@ -161,6 +162,10 @@ public: return "sampletrack"; } +signals: + void playing(); + void notPlaying(); + public slots: void updateTcos(); void setPlayingTcos( bool isPlaying ); @@ -171,6 +176,7 @@ private: FloatModel m_panningModel; IntModel m_effectChannelModel; AudioPort m_audioPort; + bool m_wasPlaying; @@ -230,6 +236,7 @@ private: SampleTrackWindow * m_window; Knob * m_volumeKnob; Knob * m_panningKnob; + FadeButton * m_activityIndicator; TrackLabelButton * m_tlb; diff --git a/src/gui/widgets/FadeButton.cpp b/src/gui/widgets/FadeButton.cpp index b633286b1..b1514aacb 100644 --- a/src/gui/widgets/FadeButton.cpp +++ b/src/gui/widgets/FadeButton.cpp @@ -100,6 +100,16 @@ void FadeButton::noteEnd() +void FadeButton::notPlaying() +{ + activeNotes = 0; + m_releaseTimer.restart(); + signalUpdate(); +} + + + + void FadeButton::customEvent(QEvent *) { update(); diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 72f63bb05..afc6b89e7 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -592,7 +592,8 @@ SampleTrack::SampleTrack( TrackContainer* tc ) : m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr( "Panning" ) ), m_effectChannelModel( 0, 0, 0, this, tr( "FX channel" ) ), - m_audioPort( tr( "Sample track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ) + m_audioPort( tr( "Sample track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ), + m_wasPlaying(false) { setName( tr( "Sample track" ) ); m_panningModel.setCenterValue( DefaultPanning ); @@ -617,11 +618,17 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, { m_audioPort.effects()->startRunning(); bool played_a_note = false; // will be return variable + bool is_playing = false; tcoVector tcos; ::BBTrack * bb_track = NULL; if( _tco_num >= 0 ) { + if (m_wasPlaying && _start > getTCO(_tco_num)->length()) + { + m_wasPlaying = false; + emit notPlaying(); + } if( _start != 0 ) { return false; @@ -630,6 +637,8 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, if (trackContainer() == (TrackContainer*)Engine::getBBTrackContainer()) { bb_track = BBTrack::findBBTrack( _tco_num ); + m_wasPlaying = true; + emit playing(); } } else @@ -641,6 +650,10 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, if( _start >= sTco->startPosition() && _start < sTco->endPosition() ) { + if (sTco->isPlaying()) + { + is_playing = true; + } if( sTco->isPlaying() == false && _start > sTco->startPosition() + sTco->startTimeOffset() ) { auto bufferFramesPerTick = Engine::framesPerTick (sTco->sampleBuffer ()->sampleRate ()); @@ -657,6 +670,7 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, sTco->setSamplePlayLength( samplePlayLength ); tcos.push_back( sTco ); sTco->setIsPlaying( true ); + is_playing = true; } } } @@ -665,6 +679,18 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, sTco->setIsPlaying( false ); } } + + if (is_playing && !m_wasPlaying) + { + m_wasPlaying = true; + emit playing(); + } + else if (!is_playing && m_wasPlaying) + { + m_wasPlaying = false; + emit notPlaying(); + } + } for( tcoVector::Iterator it = tcos.begin(); it != tcos.end(); ++it ) @@ -826,6 +852,22 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_panningKnob->setLabel( tr( "PAN" ) ); m_panningKnob->show(); + m_activityIndicator = new FadeButton(QApplication::palette().color(QPalette::Active, + QPalette::Background), + QApplication::palette().color(QPalette::Active, + QPalette::BrightText), + QApplication::palette().color(QPalette::Active, + QPalette::BrightText).darker(), + getTrackSettingsWidget()); + m_activityIndicator->setGeometry(settingsWidgetWidth-2*24-11, 2, 8, 28); + m_activityIndicator->show(); + connect(_t, SIGNAL(playing()), + m_activityIndicator, SLOT(activate())); + connect(_t, SIGNAL(notPlaying()), + m_activityIndicator, SLOT(notPlaying())); + connect(Engine::getSong(), SIGNAL(stopped()), + m_activityIndicator, SLOT(notPlaying())); + setModel( _t ); m_window = new SampleTrackWindow(this); From a24f1d779d2226104a69ff004e5ad83365cbec9b Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Wed, 6 Nov 2019 22:03:39 -0500 Subject: [PATCH 087/160] Do not include unused headers --- include/Pitch.h | 3 --- include/panning.h | 1 - include/volume.h | 3 --- 3 files changed, 7 deletions(-) diff --git a/include/Pitch.h b/include/Pitch.h index 5ef1c3d62..8a9b371b4 100644 --- a/include/Pitch.h +++ b/include/Pitch.h @@ -25,9 +25,6 @@ #ifndef PITCH_H #define PITCH_H -#include "lmms_basics.h" -#include "Midi.h" - typedef int16_t pitch_t; const pitch_t CentsPerSemitone = 100; diff --git a/include/panning.h b/include/panning.h index 8668ddf88..8994df9ab 100644 --- a/include/panning.h +++ b/include/panning.h @@ -27,7 +27,6 @@ #define PANNING_H #include "lmms_basics.h" -#include "volume.h" #include "panning_constants.h" #include "Midi.h" diff --git a/include/volume.h b/include/volume.h index f0f7708ad..9172395d6 100644 --- a/include/volume.h +++ b/include/volume.h @@ -26,10 +26,7 @@ #ifndef VOLUME_H #define VOLUME_H -#include "lmmsconfig.h" - #include "lmms_basics.h" -#include "Midi.h" const volume_t MinVolume = 0; const volume_t MaxVolume = 200; From b4459bed9e48f8dddc448da2f8bf6202dc87df5b Mon Sep 17 00:00:00 2001 From: nia <29542929+niacat@users.noreply.github.com> Date: Thu, 7 Nov 2019 04:47:20 +0000 Subject: [PATCH 088/160] Support NetBSD's OSS audio/midi implementation (#5277) --- src/core/audio/AudioOss.cpp | 2 +- src/core/midi/MidiOss.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index bbd9a9507..cbf9278a7 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -59,7 +59,7 @@ #ifndef _PATH_DEV_DSP -#ifdef __OpenBSD__ +#if defined(__NetBSD__) || defined(__OpenBSD__) #define _PATH_DEV_DSP "/dev/audio" #else #define _PATH_DEV_DSP "/dev/dsp" diff --git a/src/core/midi/MidiOss.cpp b/src/core/midi/MidiOss.cpp index a8e948efc..c1c990b0b 100644 --- a/src/core/midi/MidiOss.cpp +++ b/src/core/midi/MidiOss.cpp @@ -71,7 +71,11 @@ QString MidiOss::probeDevice() { return getenv( "MIDIDEV" ); } +#ifdef __NetBSD__ + return "/dev/rmidi0"; +#else return "/dev/midi"; +#endif } return dev; } From caaeb62a95a37249525de888a94613dfeb4df545 Mon Sep 17 00:00:00 2001 From: Andres Date: Thu, 7 Nov 2019 20:53:05 -0300 Subject: [PATCH 089/160] Moved the activity indicator mute code to parent class --- include/InstrumentTrack.h | 5 ++++- include/Track.h | 6 ++++++ src/core/Track.cpp | 25 +++++++++++++++++++++++++ src/tracks/InstrumentTrack.cpp | 17 ----------------- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index d72331e52..f2602686b 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -333,7 +333,6 @@ private slots: void midiInSelected(); void midiOutSelected(); void midiConfigChanged(); - void muteChanged(); void assignFxLine( int channelIndex ); void createFxLine(); @@ -357,6 +356,10 @@ private: QPoint m_lastPos; + FadeButton * getActivityIndicator() + { + return m_activityIndicator; + } friend class InstrumentTrackWindow; diff --git a/include/Track.h b/include/Track.h index 70e49a1c6..51705e942 100644 --- a/include/Track.h +++ b/include/Track.h @@ -39,6 +39,7 @@ #include "AutomatableModel.h" #include "ModelView.h" #include "DataFile.h" +#include "FadeButton.h" class QMenu; @@ -737,12 +738,17 @@ private: Actions m_action; + virtual FadeButton * getActivityIndicator() + { + return NULL; + } friend class TrackLabelButton; private slots: void createTCOView( TrackContentObject * tco ); + void muteChanged (); } ; diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 5c61dc5ab..7571a615a 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -2715,6 +2715,9 @@ TrackView::TrackView( Track * track, TrackContainerView * tcv ) : connect( &m_track->m_mutedModel, SIGNAL( dataChanged() ), &m_trackContentWidget, SLOT( update() ) ); + connect(&m_track->m_mutedModel, SIGNAL(dataChanged()), + this, SLOT(muteChanged())); + connect( &m_track->m_soloModel, SIGNAL( dataChanged() ), m_track, SLOT( toggleSolo() ), Qt::DirectConnection ); // create views for already existing TCOs @@ -3047,3 +3050,25 @@ void TrackView::createTCOView( TrackContentObject * tco ) } tco->selectViewOnCreate( false ); } + + + + +void TrackView::muteChanged() +{ + FadeButton * actind = getActivityIndicator(); + if (actind) + { + if (m_track->m_mutedModel.value()) + { + actind->setActiveColor(QApplication::palette().color( + QPalette::Active, + QPalette::Highlight)); + } else + { + actind->setActiveColor(QApplication::palette().color( + QPalette::Active, + QPalette::BrightText)); + } + } +} diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index b44d1b4fa..58890d111 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -994,7 +994,6 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV m_activityIndicator, SLOT( activate() ) ); connect( _it, SIGNAL( endNote() ), m_activityIndicator, SLOT( noteEnd() ) ); - connect( &_it->m_mutedModel, SIGNAL( dataChanged() ), this, SLOT( muteChanged() ) ); setModel( _it ); } @@ -1223,22 +1222,6 @@ void InstrumentTrackView::midiConfigChanged() -void InstrumentTrackView::muteChanged() -{ - if(model()->m_mutedModel.value() ) - { - m_activityIndicator->setActiveColor( QApplication::palette().color( QPalette::Active, - QPalette::Highlight ) ); - } else - { - m_activityIndicator->setActiveColor( QApplication::palette().color( QPalette::Active, - QPalette::BrightText ) ); - } -} - - - - //FIXME: This is identical to SampleTrackView::createFxMenu QMenu * InstrumentTrackView::createFxMenu(QString title, QString newFxLabel) { From e056ecb27b8193e95e714bbe645796ccfaf3ed15 Mon Sep 17 00:00:00 2001 From: Andres Date: Thu, 7 Nov 2019 21:02:55 -0300 Subject: [PATCH 090/160] Added to sampletrackview the code needed for the activity indicator mute behaviour --- include/SampleTrack.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/SampleTrack.h b/include/SampleTrack.h index c8f94bcd9..dcac2053d 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -240,6 +240,10 @@ private: TrackLabelButton * m_tlb; + FadeButton * getActivityIndicator() + { + return m_activityIndicator; + } friend class SampleTrackWindow; From ffce1d947a633881b78711062653c6eda1a6be86 Mon Sep 17 00:00:00 2001 From: Olivier Humbert Date: Fri, 15 Nov 2019 05:30:49 +0100 Subject: [PATCH 091/160] Debian packaging improvements (#5264) * d/control: http -> https * d/control: rearrange lists (one item per line + alphabetically ordered) * d/copyright: http -> https * Delete contributors.patch (integrated now) * d/series: removes contributors.patch --- debian/control | 38 +++++++++---- debian/copyright | 4 +- debian/patches/contributors.patch | 89 ------------------------------- debian/patches/series | 1 - 4 files changed, 30 insertions(+), 102 deletions(-) delete mode 100644 debian/patches/contributors.patch diff --git a/debian/control b/debian/control index 463353df0..880e0d89a 100644 --- a/debian/control +++ b/debian/control @@ -37,18 +37,28 @@ Build-Depends: qttools5-dev, wine64-tools [amd64] | wine32-tools [i386] Standards-Version: 4.2.1.4 -Homepage: http://lmms.io/ +Homepage: https://lmms.io/ Vcs-Browser: https://salsa.debian.org/debian-edu-pkg-team/lmms.git Package: lmms-bin Architecture: any -Depends: lmms-common (>= ${source:Version}), ${shlibs:Depends}, ${misc:Depends}, - stk -Recommends: tap-plugins, caps, +Depends: + lmms-common (>= ${source:Version}), + ${shlibs:Depends}, + ${misc:Depends}, + stk, +Recommends: + caps, lmms-vst-server:i386 (>= ${source:Version}), - lmms-vst-server:amd64 (>= ${source:Version}) -Suggests: fil-plugins, mcp-plugins, omins, freepats, fluid-soundfont-gm, - ladspa-plugin + lmms-vst-server:amd64 (>= ${source:Version}), + tap-plugins, +Suggests: + fil-plugins, + fluid-soundfont-gm, + freepats, + ladspa-plugin, + mcp-plugins, + omins, Replaces: lmms-common (<< 1.0.0-1) Breaks: lmms-common (<< 1.0.0-1) Multi-Arch: allowed @@ -67,7 +77,9 @@ Description: Linux Multimedia Studio - minimal installation Package: lmms Architecture: any -Depends: lmms-bin, ${misc:Depends} +Depends: + lmms-bin, + ${misc:Depends}, Description: Linux Multimedia Studio LMMS aims to be a free alternative to popular (but commercial and closed- source) programs like FruityLoops, Cubase and Logic giving you the ability of @@ -83,7 +95,10 @@ Description: Linux Multimedia Studio Package: lmms-common Architecture: all -Depends: zynaddsubfx-data, ${shlibs:Depends}, ${misc:Depends} +Depends: + ${shlibs:Depends}, + ${misc:Depends}, + zynaddsubfx-data, Pre-Depends: ${misc:Pre-Depends} Description: Linux Multimedia Studio - common files LMMS aims to be a free alternative to popular (but commercial and closed- @@ -101,7 +116,10 @@ Description: Linux Multimedia Studio - common files Package: lmms-vst-server Architecture: amd64 i386 -Depends: wine64 [amd64] | wine64-development [amd64] | wine32 [i386] | wine32-development [i386], ${shlibs:Depends}, ${misc:Depends} +Depends: + wine64 [amd64] | wine64-development [amd64] | wine32 [i386] | wine32-development [i386], + ${shlibs:Depends}, + ${misc:Depends}, Recommends: lmms-bin:any Description: Linux Multimedia Studio - VST server This package contains a helper application that loads VST plugins. diff --git a/debian/copyright b/debian/copyright index 3fbf0917e..01b30459e 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,4 +1,4 @@ -Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: LMMS Upstream-Contact: https://github.com/LMMS/lmms Source: https://github.com/LMMS/lmms/tags @@ -1367,7 +1367,7 @@ License: WOL documentation for any purpose is hereby granted without fee, provided that the above copyright notice and this license appear in all source copies. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF - ANY KIND. See http://www.dspguru.com/wol.htm for more information. + ANY KIND. See https://www.dspguru.com/wol.htm for more information. License: non-free This license does not comply with Debian Free Software Guidelines. diff --git a/debian/patches/contributors.patch b/debian/patches/contributors.patch deleted file mode 100644 index d2b55fd9a..000000000 --- a/debian/patches/contributors.patch +++ /dev/null @@ -1,89 +0,0 @@ -Description: Add contributors - The list of contributors is missing from the source tarball. This list is - generated from upstream repository, running: - git shortlog -sne v1.1.3 | cut -c8- - See https://github.com/LMMS/lmms/issues/2914 for more information. -Author: Javier Serrano Polo - -Index: lmms-1.1.3/doc/CONTRIBUTORS -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ lmms-1.1.3/doc/CONTRIBUTORS 2016-07-12 00:41:47.000000000 +0200 -@@ -0,0 +1,77 @@ -+Tobias Doerffel -+Vesa -+Javier Serrano Polo -+Paul Giblock -+Tres Finocchiaro -+Lukas W -+Raine M. Ekman -+Wong Cho Ching -+Hannu Haahti -+Danny McRae -+Dave French -+Daniel Winzen -+Andreas Brandmaier -+Andrew Kelley -+Oskar Wallgren -+Mike Choi -+Alexandre Almeida -+NoiseByNorthwest -+Johannes Lorenz -+Stian Jørgensrud -+falkTX -+Csaba Hruska -+StakeoutPunch -+ma2moto -+mikobuntu -+8tab <8tab@wp.pl> -+Matthew Krafczyk -+Spekular -+Umcaruje -+DeRobyJ -+Jonathan Aquilina -+ra -+wongcc966422 -+Gurjot Singh -+Janne Sinisalo -+Krzysztof Foltman -+Lou Herard -+Paul Wayper -+Rüdiger Ranft -+Yann Collette -+grindhold -+midi-pascal -+unfa -+Ian Sannar -+Jaroslav Petrnoušek -+LYF610400210 -+Rafael Ruggiero -+psyomn -+quadro -+sarahkeefe -+Achim Settelmeier -+André Hentschel -+Armin Kazmi -+Attila Herman -+Christopher A. Oliver -+Devin Venable -+Fastigium -+Frank Mather -+Frederik -+Hexasoft -+Jens Lang -+Jesse Dubay -+Joel Muzzerall -+Kristi -+Markus Elfring -+Nikos Chantziaras -+Paul Nasca -+Peter Nelson -+Ra -+Steffen Baranowsky -+Thorsten Müller -+TonyChyi -+devin -+dnl-music -+fundamental -+groboclown -+zm1990s diff --git a/debian/patches/series b/debian/patches/series index aba1af044..94ae11454 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,3 +1,2 @@ -contributors.patch clang.patch build-amd64-20181013.patch From 33b36ffc5ea7889066a04d7823fa6d027461ce0f Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Fri, 15 Nov 2019 17:09:57 -0500 Subject: [PATCH 092/160] Add some missing includes --- include/Pitch.h | 2 ++ include/panning.h | 1 + 2 files changed, 3 insertions(+) diff --git a/include/Pitch.h b/include/Pitch.h index 8a9b371b4..2f866a1c5 100644 --- a/include/Pitch.h +++ b/include/Pitch.h @@ -25,6 +25,8 @@ #ifndef PITCH_H #define PITCH_H +#include + typedef int16_t pitch_t; const pitch_t CentsPerSemitone = 100; diff --git a/include/panning.h b/include/panning.h index 8994df9ab..c043adf5c 100644 --- a/include/panning.h +++ b/include/panning.h @@ -29,6 +29,7 @@ #include "lmms_basics.h" #include "panning_constants.h" #include "Midi.h" +#include "volume.h" inline stereoVolumeVector panningToVolumeVector( panning_t _p, float _scale = 1.0f ) From 256ae6dad6d03cbad2c72a14939faf2aaea20549 Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Fri, 15 Nov 2019 19:46:09 -0600 Subject: [PATCH 093/160] Fix incorrect m_lastSoloed after moving/deleting an FX channel Original code by @gi0e5b06. --- src/core/FxMixer.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 032090bf1..f04435e05 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -336,6 +336,11 @@ void FxMixer::deleteChannel( int index ) deleteChannelSend( ch->m_receives.first() ); } + // if m_lastSoloed was our index, reset it + if (m_lastSoloed == index) { m_lastSoloed = -1; } + // if m_lastSoloed is > delete index, it will move left + else if (m_lastSoloed > index) { --m_lastSoloed; } + // actually delete the channel m_fxChannels.remove(index); delete ch; @@ -373,6 +378,10 @@ void FxMixer::moveChannelLeft( int index ) // channels to swap int a = index - 1, b = index; + // check if m_lastSoloed is one of our swaps + if (m_lastSoloed == a) { m_lastSoloed = b; } + else if (m_lastSoloed == b) { m_lastSoloed = a; } + // go through every instrument and adjust for the channel index change QVector songTrackList = Engine::getSong()->tracks(); QVector bbTrackList = Engine::getBBTrackContainer()->tracks(); From cf4bb7b85121d2794e3588886f05c53d8596d558 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 17 Nov 2019 16:09:48 +0000 Subject: [PATCH 094/160] Fix remote plugin crash reading parameters from Grooove plugin (#5300) --- plugins/vst_base/RemoteVstPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index d12ccd88a..15a56e869 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -1072,7 +1072,7 @@ void RemoteVstPlugin::getParameterDump() for( int i = 0; i < m_plugin->numParams; ++i ) { - char paramName[32]; + char paramName[256]; memset( paramName, 0, sizeof( paramName ) ); pluginDispatch( effGetParamName, i, 0, paramName ); paramName[sizeof(paramName)-1] = 0; From 229de18bc0608220e281249990296e5a908d91e7 Mon Sep 17 00:00:00 2001 From: Andres Date: Sun, 17 Nov 2019 23:05:16 -0300 Subject: [PATCH 095/160] Fixed triggering of stop animation when not playing --- include/SampleTrack.h | 6 ++++++ src/tracks/SampleTrack.cpp | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/SampleTrack.h b/include/SampleTrack.h index dcac2053d..cad43a15d 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -162,6 +162,11 @@ public: return "sampletrack"; } + bool wasPlaying() + { + return m_wasPlaying; + } + signals: void playing(); void notPlaying(); @@ -215,6 +220,7 @@ public: public slots: void showEffects(); + void stopPlaying(); protected: diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index afc6b89e7..83c8ba130 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -866,7 +866,7 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : connect(_t, SIGNAL(notPlaying()), m_activityIndicator, SLOT(notPlaying())); connect(Engine::getSong(), SIGNAL(stopped()), - m_activityIndicator, SLOT(notPlaying())); + this, SLOT(stopPlaying())); setModel( _t ); @@ -982,6 +982,16 @@ void SampleTrackView::dropEvent(QDropEvent *de) +void SampleTrackView::stopPlaying() +{ + if (dynamic_cast(getTrack())->wasPlaying()) + { + m_activityIndicator->notPlaying(); + } +} + + + SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : QWidget(), From 2f0010270eaf64efd716d775ddaffbb25818b3ea Mon Sep 17 00:00:00 2001 From: Kapandaria Date: Mon, 18 Nov 2019 22:29:08 +0200 Subject: [PATCH 096/160] Xpressive plugin updates (#5217) * Updated plugin artwork. * Update the formula in the presets to use integrate(f) instead of t*f, as integrate operation is more robust to frequency changes. * rename X-Pressive to Xpressive in help window title. * Xpressive.cpp, spaces to tabs and remove commented code. --- data/presets/X-Pressive/Ambition.xpf | 21 --- data/presets/X-Pressive/Baby Violin.xpf | 21 --- data/presets/X-Pressive/Bad Singer.xpf | 21 --- data/presets/X-Pressive/Cloud Bass.xpf | 21 --- data/presets/X-Pressive/Creature.xpf | 21 --- data/presets/X-Pressive/Electric Shock.xpf | 21 --- data/presets/X-Pressive/Faded Colors.xpf | 21 --- data/presets/X-Pressive/Fat Flute.xpf | 21 --- data/presets/X-Pressive/Horn.xpf | 21 --- data/presets/X-Pressive/Piano-Gong.xpf | 21 --- data/presets/X-Pressive/Rubber Bass.xpf | 21 --- data/presets/X-Pressive/Space Echoes.xpf | 21 --- data/presets/X-Pressive/Speaker Swapper.xpf | 21 --- data/presets/X-Pressive/Toss.xpf | 21 --- data/presets/X-Pressive/Untuned Bell.xpf | 21 --- data/presets/X-Pressive/Vibrato.xpf | 21 --- data/presets/X-Pressive/X-Distorted.xpf | 21 --- .../{X-Pressive => Xpressive}/Accordion.xpf | 0 data/presets/Xpressive/Ambition.xpf | 22 +++ data/presets/Xpressive/Baby Violin.xpf | 22 +++ data/presets/Xpressive/Bad Singer.xpf | 22 +++ data/presets/Xpressive/Cloud Bass.xpf | 22 +++ data/presets/Xpressive/Creature.xpf | 22 +++ .../{X-Pressive => Xpressive}/Dream.xpf | 0 data/presets/Xpressive/Electric Shock.xpf | 22 +++ .../Xpressive/Faded Colors - notes test.xpf | 22 +++ data/presets/Xpressive/Faded Colors.xpf | 22 +++ data/presets/Xpressive/Fat Flute.xpf | 22 +++ .../{X-Pressive => Xpressive}/Frog.xpf | 0 data/presets/Xpressive/Horn.xpf | 22 +++ .../{X-Pressive => Xpressive}/Low Battery.xpf | 23 ++-- data/presets/Xpressive/Piano-Gong.xpf | 22 +++ data/presets/Xpressive/Rubber Bass.xpf | 22 +++ data/presets/Xpressive/Space Echoes.xpf | 22 +++ data/presets/Xpressive/Speaker Swapper.xpf | 22 +++ data/presets/Xpressive/Toss.xpf | 22 +++ data/presets/Xpressive/Untuned Bell.xpf | 22 +++ data/presets/Xpressive/Vibrato.xpf | 22 +++ data/presets/Xpressive/X-Distorted.xpf | 22 +++ plugins/Xpressive/Xpressive.cpp | 90 ++++++------ plugins/Xpressive/Xpressive.svg | 130 ++++++++++++++++++ plugins/Xpressive/Xpressive_logo.svg | 106 ++++++++++++++ plugins/Xpressive/artwork.png | Bin 70956 -> 10950 bytes plugins/Xpressive/help_active.png | Bin 840 -> 546 bytes plugins/Xpressive/help_inactive.png | Bin 806 -> 549 bytes plugins/Xpressive/logo.png | Bin 4497 -> 1884 bytes plugins/Xpressive/o1_active.png | Bin 629 -> 548 bytes plugins/Xpressive/o1_inactive.png | Bin 508 -> 512 bytes plugins/Xpressive/o2_active.png | Bin 723 -> 653 bytes plugins/Xpressive/o2_inactive.png | Bin 602 -> 589 bytes plugins/Xpressive/w1_active.png | Bin 685 -> 590 bytes plugins/Xpressive/w1_inactive.png | Bin 549 -> 554 bytes plugins/Xpressive/w2_active.png | Bin 766 -> 715 bytes plugins/Xpressive/w2_inactive.png | Bin 628 -> 662 bytes plugins/Xpressive/w3_active.png | Bin 771 -> 705 bytes plugins/Xpressive/w3_inactive.png | Bin 626 -> 652 bytes plugins/Xpressive/wavegraph.png | Bin 9029 -> 9006 bytes 57 files changed, 694 insertions(+), 408 deletions(-) delete mode 100644 data/presets/X-Pressive/Ambition.xpf delete mode 100644 data/presets/X-Pressive/Baby Violin.xpf delete mode 100644 data/presets/X-Pressive/Bad Singer.xpf delete mode 100644 data/presets/X-Pressive/Cloud Bass.xpf delete mode 100644 data/presets/X-Pressive/Creature.xpf delete mode 100644 data/presets/X-Pressive/Electric Shock.xpf delete mode 100644 data/presets/X-Pressive/Faded Colors.xpf delete mode 100644 data/presets/X-Pressive/Fat Flute.xpf delete mode 100644 data/presets/X-Pressive/Horn.xpf delete mode 100644 data/presets/X-Pressive/Piano-Gong.xpf delete mode 100644 data/presets/X-Pressive/Rubber Bass.xpf delete mode 100644 data/presets/X-Pressive/Space Echoes.xpf delete mode 100644 data/presets/X-Pressive/Speaker Swapper.xpf delete mode 100644 data/presets/X-Pressive/Toss.xpf delete mode 100644 data/presets/X-Pressive/Untuned Bell.xpf delete mode 100644 data/presets/X-Pressive/Vibrato.xpf delete mode 100644 data/presets/X-Pressive/X-Distorted.xpf rename data/presets/{X-Pressive => Xpressive}/Accordion.xpf (100%) create mode 100644 data/presets/Xpressive/Ambition.xpf create mode 100644 data/presets/Xpressive/Baby Violin.xpf create mode 100644 data/presets/Xpressive/Bad Singer.xpf create mode 100644 data/presets/Xpressive/Cloud Bass.xpf create mode 100644 data/presets/Xpressive/Creature.xpf rename data/presets/{X-Pressive => Xpressive}/Dream.xpf (100%) create mode 100644 data/presets/Xpressive/Electric Shock.xpf create mode 100644 data/presets/Xpressive/Faded Colors - notes test.xpf create mode 100644 data/presets/Xpressive/Faded Colors.xpf create mode 100644 data/presets/Xpressive/Fat Flute.xpf rename data/presets/{X-Pressive => Xpressive}/Frog.xpf (100%) create mode 100644 data/presets/Xpressive/Horn.xpf rename data/presets/{X-Pressive => Xpressive}/Low Battery.xpf (64%) create mode 100644 data/presets/Xpressive/Piano-Gong.xpf create mode 100644 data/presets/Xpressive/Rubber Bass.xpf create mode 100644 data/presets/Xpressive/Space Echoes.xpf create mode 100644 data/presets/Xpressive/Speaker Swapper.xpf create mode 100644 data/presets/Xpressive/Toss.xpf create mode 100644 data/presets/Xpressive/Untuned Bell.xpf create mode 100644 data/presets/Xpressive/Vibrato.xpf create mode 100644 data/presets/Xpressive/X-Distorted.xpf create mode 100644 plugins/Xpressive/Xpressive.svg create mode 100644 plugins/Xpressive/Xpressive_logo.svg diff --git a/data/presets/X-Pressive/Ambition.xpf b/data/presets/X-Pressive/Ambition.xpf deleted file mode 100644 index 2d93f7c05..000000000 --- a/data/presets/X-Pressive/Ambition.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Baby Violin.xpf b/data/presets/X-Pressive/Baby Violin.xpf deleted file mode 100644 index 2e887d3d2..000000000 --- a/data/presets/X-Pressive/Baby Violin.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Bad Singer.xpf b/data/presets/X-Pressive/Bad Singer.xpf deleted file mode 100644 index ca9cfd5a3..000000000 --- a/data/presets/X-Pressive/Bad Singer.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Cloud Bass.xpf b/data/presets/X-Pressive/Cloud Bass.xpf deleted file mode 100644 index 4e444f22a..000000000 --- a/data/presets/X-Pressive/Cloud Bass.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Creature.xpf b/data/presets/X-Pressive/Creature.xpf deleted file mode 100644 index b667a9c7f..000000000 --- a/data/presets/X-Pressive/Creature.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Electric Shock.xpf b/data/presets/X-Pressive/Electric Shock.xpf deleted file mode 100644 index 7dea6fe4a..000000000 --- a/data/presets/X-Pressive/Electric Shock.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Faded Colors.xpf b/data/presets/X-Pressive/Faded Colors.xpf deleted file mode 100644 index 84a37826a..000000000 --- a/data/presets/X-Pressive/Faded Colors.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Fat Flute.xpf b/data/presets/X-Pressive/Fat Flute.xpf deleted file mode 100644 index 92242114e..000000000 --- a/data/presets/X-Pressive/Fat Flute.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Horn.xpf b/data/presets/X-Pressive/Horn.xpf deleted file mode 100644 index 099480569..000000000 --- a/data/presets/X-Pressive/Horn.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Piano-Gong.xpf b/data/presets/X-Pressive/Piano-Gong.xpf deleted file mode 100644 index 241f61a55..000000000 --- a/data/presets/X-Pressive/Piano-Gong.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Rubber Bass.xpf b/data/presets/X-Pressive/Rubber Bass.xpf deleted file mode 100644 index 73c3648ba..000000000 --- a/data/presets/X-Pressive/Rubber Bass.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Space Echoes.xpf b/data/presets/X-Pressive/Space Echoes.xpf deleted file mode 100644 index 1d4d2b543..000000000 --- a/data/presets/X-Pressive/Space Echoes.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Speaker Swapper.xpf b/data/presets/X-Pressive/Speaker Swapper.xpf deleted file mode 100644 index cf80b9304..000000000 --- a/data/presets/X-Pressive/Speaker Swapper.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Toss.xpf b/data/presets/X-Pressive/Toss.xpf deleted file mode 100644 index 27a0b3f96..000000000 --- a/data/presets/X-Pressive/Toss.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Untuned Bell.xpf b/data/presets/X-Pressive/Untuned Bell.xpf deleted file mode 100644 index 744927063..000000000 --- a/data/presets/X-Pressive/Untuned Bell.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Vibrato.xpf b/data/presets/X-Pressive/Vibrato.xpf deleted file mode 100644 index 34795de11..000000000 --- a/data/presets/X-Pressive/Vibrato.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/X-Distorted.xpf b/data/presets/X-Pressive/X-Distorted.xpf deleted file mode 100644 index cbe3742a5..000000000 --- a/data/presets/X-Pressive/X-Distorted.xpf +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/presets/X-Pressive/Accordion.xpf b/data/presets/Xpressive/Accordion.xpf similarity index 100% rename from data/presets/X-Pressive/Accordion.xpf rename to data/presets/Xpressive/Accordion.xpf diff --git a/data/presets/Xpressive/Ambition.xpf b/data/presets/Xpressive/Ambition.xpf new file mode 100644 index 000000000..dd6448977 --- /dev/null +++ b/data/presets/Xpressive/Ambition.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Baby Violin.xpf b/data/presets/Xpressive/Baby Violin.xpf new file mode 100644 index 000000000..45e407fc8 --- /dev/null +++ b/data/presets/Xpressive/Baby Violin.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Bad Singer.xpf b/data/presets/Xpressive/Bad Singer.xpf new file mode 100644 index 000000000..10fe3b308 --- /dev/null +++ b/data/presets/Xpressive/Bad Singer.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Cloud Bass.xpf b/data/presets/Xpressive/Cloud Bass.xpf new file mode 100644 index 000000000..15bf4188d --- /dev/null +++ b/data/presets/Xpressive/Cloud Bass.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Creature.xpf b/data/presets/Xpressive/Creature.xpf new file mode 100644 index 000000000..bee39f224 --- /dev/null +++ b/data/presets/Xpressive/Creature.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/X-Pressive/Dream.xpf b/data/presets/Xpressive/Dream.xpf similarity index 100% rename from data/presets/X-Pressive/Dream.xpf rename to data/presets/Xpressive/Dream.xpf diff --git a/data/presets/Xpressive/Electric Shock.xpf b/data/presets/Xpressive/Electric Shock.xpf new file mode 100644 index 000000000..3f9aef104 --- /dev/null +++ b/data/presets/Xpressive/Electric Shock.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Faded Colors - notes test.xpf b/data/presets/Xpressive/Faded Colors - notes test.xpf new file mode 100644 index 000000000..de4938f4d --- /dev/null +++ b/data/presets/Xpressive/Faded Colors - notes test.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Faded Colors.xpf b/data/presets/Xpressive/Faded Colors.xpf new file mode 100644 index 000000000..a514ee438 --- /dev/null +++ b/data/presets/Xpressive/Faded Colors.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Fat Flute.xpf b/data/presets/Xpressive/Fat Flute.xpf new file mode 100644 index 000000000..76d9e2f84 --- /dev/null +++ b/data/presets/Xpressive/Fat Flute.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/X-Pressive/Frog.xpf b/data/presets/Xpressive/Frog.xpf similarity index 100% rename from data/presets/X-Pressive/Frog.xpf rename to data/presets/Xpressive/Frog.xpf diff --git a/data/presets/Xpressive/Horn.xpf b/data/presets/Xpressive/Horn.xpf new file mode 100644 index 000000000..d44b332b2 --- /dev/null +++ b/data/presets/Xpressive/Horn.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/X-Pressive/Low Battery.xpf b/data/presets/Xpressive/Low Battery.xpf similarity index 64% rename from data/presets/X-Pressive/Low Battery.xpf rename to data/presets/Xpressive/Low Battery.xpf index c0e648ac9..78f1fc78f 100644 --- a/data/presets/X-Pressive/Low Battery.xpf +++ b/data/presets/Xpressive/Low Battery.xpf @@ -1,20 +1,21 @@ - + - - + + - + + + - - - - + + + + - - - + + diff --git a/data/presets/Xpressive/Piano-Gong.xpf b/data/presets/Xpressive/Piano-Gong.xpf new file mode 100644 index 000000000..a8244b799 --- /dev/null +++ b/data/presets/Xpressive/Piano-Gong.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Rubber Bass.xpf b/data/presets/Xpressive/Rubber Bass.xpf new file mode 100644 index 000000000..4b1409e22 --- /dev/null +++ b/data/presets/Xpressive/Rubber Bass.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Space Echoes.xpf b/data/presets/Xpressive/Space Echoes.xpf new file mode 100644 index 000000000..be6de3653 --- /dev/null +++ b/data/presets/Xpressive/Space Echoes.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Speaker Swapper.xpf b/data/presets/Xpressive/Speaker Swapper.xpf new file mode 100644 index 000000000..d4da5aa2f --- /dev/null +++ b/data/presets/Xpressive/Speaker Swapper.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Toss.xpf b/data/presets/Xpressive/Toss.xpf new file mode 100644 index 000000000..387e78fd9 --- /dev/null +++ b/data/presets/Xpressive/Toss.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Untuned Bell.xpf b/data/presets/Xpressive/Untuned Bell.xpf new file mode 100644 index 000000000..5dd61ec18 --- /dev/null +++ b/data/presets/Xpressive/Untuned Bell.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/Vibrato.xpf b/data/presets/Xpressive/Vibrato.xpf new file mode 100644 index 000000000..a7dda25e9 --- /dev/null +++ b/data/presets/Xpressive/Vibrato.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/presets/Xpressive/X-Distorted.xpf b/data/presets/Xpressive/X-Distorted.xpf new file mode 100644 index 000000000..b42495d75 --- /dev/null +++ b/data/presets/Xpressive/X-Distorted.xpf @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index 018319c82..a80a0ae41 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -53,9 +53,9 @@ extern "C" { Plugin::Descriptor PLUGIN_EXPORT xpressive_plugin_descriptor = { STRINGIFY( - PLUGIN_NAME), "X-Pressive", QT_TRANSLATE_NOOP("pluginBrowser", - "Mathematical expression parser"), "Orr Dvori", 0x0100, - Plugin::Instrument, new PluginPixmapLoader("logo"), NULL, NULL }; + PLUGIN_NAME), "Xpressive", QT_TRANSLATE_NOOP("pluginBrowser", + "Mathematical expression parser"), "Orr Dvori", 0x0100, + Plugin::Instrument, new PluginPixmapLoader("logo"), NULL, NULL }; } @@ -257,7 +257,6 @@ public: setCenterPointY(14.5); setInnerRadius(4); setOuterRadius(9); - setOuterColor(QColor(0x519fff)); setTotalAngle(300.0); setLineWidth(3); } @@ -277,14 +276,18 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : InstrumentViewFixedSize(_instrument, _parent) { - const int COL_KNOBS = 194; - const int ROW_KNOBSA1 = 26; - const int ROW_KNOBSA2 = 26 + 32; - const int ROW_KNOBSA3 = 26 + 64; - const int ROW_KNOBSP1 = 126; - const int ROW_KNOBSP2 = 126 + 32; - const int ROW_KNOBREL = 126 + 64; - const int ROW_WAVEBTN = 234; + const int COL_KNOBS = 191; + const int BASE_START = 2; + const int ROW_KNOBSA1 = BASE_START; + const int ROW_KNOBSA2 = BASE_START + 32; + const int ROW_KNOBSA3 = BASE_START + 64; + const int ROW_KNOBSP1 = BASE_START + 100; + const int ROW_KNOBSP2 = BASE_START + 100 + 32; + const int ROW_KNOBREL = BASE_START + 100 + 64; + const int ROW_BTN = BASE_START + 85; + const int ROW_WAVEBTN = BASE_START + 233 - 26; + const int EXPR_TEXT_Y = BASE_START + 102; + const int EXPR_TEXT_H = 90; setAutoFillBackground(true); QPalette pal; @@ -293,7 +296,7 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : setPalette(pal); m_graph = new Graph(this, Graph::LinearStyle, 180, 81); - m_graph->move(9, 27); + m_graph->move(3, BASE_START + 1); m_graph->setAutoFillBackground(true); m_graph->setGraphColor(QColor(255, 255, 255)); m_graph->setEnabled(false); @@ -313,37 +316,37 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : PixmapButton * m_helpBtn; m_w1Btn = new PixmapButton(this, NULL); - m_w1Btn->move(9, 111); + m_w1Btn->move(3, ROW_BTN); m_w1Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w1_active")); m_w1Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w1_inactive")); ToolTip::add(m_w1Btn, tr("Select oscillator W1")); m_w2Btn = new PixmapButton(this, NULL); - m_w2Btn->move(32, 111); + m_w2Btn->move(26, ROW_BTN); m_w2Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w2_active")); m_w2Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w2_inactive")); ToolTip::add(m_w2Btn, tr("Select oscillator W2")); m_w3Btn = new PixmapButton(this, NULL); - m_w3Btn->move(55, 111); + m_w3Btn->move(49, ROW_BTN); m_w3Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w3_active")); m_w3Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("w3_inactive")); ToolTip::add(m_w3Btn, tr("Select oscillator W3")); m_o1Btn = new PixmapButton(this, NULL); - m_o1Btn->move(85, 111); + m_o1Btn->move(79, ROW_BTN); m_o1Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("o1_active")); m_o1Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("o1_inactive")); ToolTip::add(m_o1Btn, tr("Select output O1")); m_o2Btn = new PixmapButton(this, NULL); - m_o2Btn->move(107, 111); + m_o2Btn->move(101, ROW_BTN); m_o2Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("o2_active")); m_o2Btn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("o2_inactive")); ToolTip::add(m_o2Btn, tr("Select output O2")); m_helpBtn = new PixmapButton(this, NULL); - m_helpBtn->move(139, 111); + m_helpBtn->move(133, ROW_BTN); m_helpBtn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("help_active")); m_helpBtn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("help_inactive")); ToolTip::add(m_helpBtn, tr("Open help window")); @@ -359,38 +362,38 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : m_selectedGraphGroup->setModel(&e->selectedGraph()); m_sinWaveBtn = new PixmapButton(this, tr("Sine wave")); - m_sinWaveBtn->move(10, ROW_WAVEBTN); + m_sinWaveBtn->move(4, ROW_WAVEBTN); m_sinWaveBtn->setActiveGraphic(embed::getIconPixmap("sin_wave_active")); m_sinWaveBtn->setInactiveGraphic(embed::getIconPixmap("sin_wave_inactive")); ToolTip::add(m_sinWaveBtn, tr("Sine wave")); m_moogWaveBtn = new PixmapButton(this, tr("Moog-saw wave")); - m_moogWaveBtn->move(10, ROW_WAVEBTN-14); + m_moogWaveBtn->move(4, ROW_WAVEBTN-14); m_moogWaveBtn->setActiveGraphic( embed::getIconPixmap( "moog_saw_wave_active" ) ); m_moogWaveBtn->setInactiveGraphic(embed::getIconPixmap("moog_saw_wave_inactive")); ToolTip::add(m_moogWaveBtn, tr("Moog-saw wave")); m_expWaveBtn = new PixmapButton(this, tr("Exponential wave")); - m_expWaveBtn->move(10 +14, ROW_WAVEBTN-14); + m_expWaveBtn->move(4 +14, ROW_WAVEBTN-14); m_expWaveBtn->setActiveGraphic(embed::getIconPixmap( "exp_wave_active" ) ); m_expWaveBtn->setInactiveGraphic(embed::getIconPixmap( "exp_wave_inactive" ) ); ToolTip::add(m_expWaveBtn, tr("Exponential wave")); m_sawWaveBtn = new PixmapButton(this, tr("Saw wave")); - m_sawWaveBtn->move(10 + 14 * 2, ROW_WAVEBTN-14); + m_sawWaveBtn->move(4 + 14 * 2, ROW_WAVEBTN-14); m_sawWaveBtn->setActiveGraphic(embed::getIconPixmap("saw_wave_active")); m_sawWaveBtn->setInactiveGraphic(embed::getIconPixmap("saw_wave_inactive")); ToolTip::add(m_sawWaveBtn, tr("Saw wave")); m_usrWaveBtn = new PixmapButton(this, tr("User-defined wave")); - m_usrWaveBtn->move(10 + 14 * 3, ROW_WAVEBTN-14); + m_usrWaveBtn->move(4 + 14 * 3, ROW_WAVEBTN-14); m_usrWaveBtn->setActiveGraphic(embed::getIconPixmap("usr_wave_active")); m_usrWaveBtn->setInactiveGraphic(embed::getIconPixmap("usr_wave_inactive")); ToolTip::add(m_usrWaveBtn, tr("User-defined wave")); m_triangleWaveBtn = new PixmapButton(this, tr("Triangle wave")); - m_triangleWaveBtn->move(10 + 14, ROW_WAVEBTN); + m_triangleWaveBtn->move(4 + 14, ROW_WAVEBTN); m_triangleWaveBtn->setActiveGraphic( embed::getIconPixmap("triangle_wave_active")); m_triangleWaveBtn->setInactiveGraphic( @@ -398,14 +401,14 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : ToolTip::add(m_triangleWaveBtn, tr("Triangle wave")); m_sqrWaveBtn = new PixmapButton(this, tr("Square wave")); - m_sqrWaveBtn->move(10 + 14 * 2, ROW_WAVEBTN); + m_sqrWaveBtn->move(4 + 14 * 2, ROW_WAVEBTN); m_sqrWaveBtn->setActiveGraphic(embed::getIconPixmap("square_wave_active")); m_sqrWaveBtn->setInactiveGraphic( embed::getIconPixmap("square_wave_inactive")); ToolTip::add(m_sqrWaveBtn, tr("Square wave")); m_whiteNoiseWaveBtn = new PixmapButton(this, tr("White noise")); - m_whiteNoiseWaveBtn->move(10 + 14 * 3, ROW_WAVEBTN); + m_whiteNoiseWaveBtn->move(4 + 14 * 3, ROW_WAVEBTN); m_whiteNoiseWaveBtn->setActiveGraphic( embed::getIconPixmap("white_noise_wave_active")); m_whiteNoiseWaveBtn->setInactiveGraphic( @@ -415,16 +418,16 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : m_waveInterpolate = new LedCheckBox("Interpolate", this, tr("WaveInterpolate"), LedCheckBox::Green); - m_waveInterpolate->move(120, 230); + m_waveInterpolate->move(2, 230); m_expressionValidToggle = new LedCheckBox("", this, tr("ExpressionValid"), LedCheckBox::Red); - m_expressionValidToggle->move(174, 216); + m_expressionValidToggle->move(168, EXPR_TEXT_Y+EXPR_TEXT_H-2); m_expressionValidToggle->setEnabled( false ); m_expressionEditor = new QPlainTextEdit(this); - m_expressionEditor->move(9, 128); - m_expressionEditor->resize(180, 90); + m_expressionEditor->move(3, EXPR_TEXT_Y); + m_expressionEditor->resize(180, EXPR_TEXT_H); m_generalPurposeKnob[0] = new XpressiveKnob(this,"A1"); m_generalPurposeKnob[0]->setHintText(tr("General purpose 1:"), ""); @@ -452,9 +455,16 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : - m_smoothKnob=new Knob(this,"Smoothness"); + m_smoothKnob=new Knob(knobStyled, this, "Smoothness"); + m_smoothKnob->setFixedSize(25, 25); + m_smoothKnob->setCenterPointX(12.5); + m_smoothKnob->setCenterPointY(12.5); + m_smoothKnob->setInnerRadius(4); + m_smoothKnob->setOuterRadius(9); + m_smoothKnob->setTotalAngle(280.0); + m_smoothKnob->setLineWidth(3); m_smoothKnob->setHintText(tr("Smoothness"), ""); - m_smoothKnob->move(80, 220); + m_smoothKnob->move(66, EXPR_TEXT_Y + EXPR_TEXT_H + 4); connect(m_generalPurposeKnob[0], SIGNAL(sliderMoved(float)), this, SLOT(expressionChanged())); @@ -748,7 +758,7 @@ void XpressiveView::updateLayout() { void XpressiveView::sinWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("sinew(t*f)"); + m_expressionEditor->appendPlainText("sinew(integrate(f))"); else m_expressionEditor->appendPlainText("sinew(t)"); Engine::getSong()->setModified(); @@ -756,7 +766,7 @@ void XpressiveView::sinWaveClicked() { void XpressiveView::triangleWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("trianglew(t*f)"); + m_expressionEditor->appendPlainText("trianglew(integrate(f))"); else m_expressionEditor->appendPlainText("trianglew(t)"); Engine::getSong()->setModified(); @@ -764,7 +774,7 @@ void XpressiveView::triangleWaveClicked() { void XpressiveView::sawWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("saww(t*f)"); + m_expressionEditor->appendPlainText("saww(integrate(f))"); else m_expressionEditor->appendPlainText("saww(t)"); Engine::getSong()->setModified(); @@ -772,7 +782,7 @@ void XpressiveView::sawWaveClicked() { void XpressiveView::sqrWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("squarew(t*f)"); + m_expressionEditor->appendPlainText("squarew(integrate(f))"); else m_expressionEditor->appendPlainText("squarew(t)"); Engine::getSong()->setModified(); @@ -786,7 +796,7 @@ void XpressiveView::noiseWaveClicked() { void XpressiveView::moogSawWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("moogsaww(t*f)"); + m_expressionEditor->appendPlainText("moogsaww(integrate(f))"); else m_expressionEditor->appendPlainText("moogsaww(t)"); Engine::getSong()->setModified(); @@ -794,7 +804,7 @@ void XpressiveView::moogSawWaveClicked() void XpressiveView::expWaveClicked() { if (m_output_expr) - m_expressionEditor->appendPlainText("expw(t*f)"); + m_expressionEditor->appendPlainText("expw(integrate(f))"); else m_expressionEditor->appendPlainText("expw(t)"); Engine::getSong()->setModified(); @@ -861,7 +871,7 @@ QString XpressiveHelpView::s_helpText= XpressiveHelpView::XpressiveHelpView():QTextEdit(s_helpText) { - setWindowTitle ( "X-Pressive Help" ); + setWindowTitle ( "Xpressive Help" ); setTextInteractionFlags ( Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse ); gui->mainWindow()->addWindowedWidget( this ); parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false ); diff --git a/plugins/Xpressive/Xpressive.svg b/plugins/Xpressive/Xpressive.svg new file mode 100644 index 000000000..ef3029c0d --- /dev/null +++ b/plugins/Xpressive/Xpressive.svg @@ -0,0 +1,130 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/Xpressive/Xpressive_logo.svg b/plugins/Xpressive/Xpressive_logo.svg new file mode 100644 index 000000000..fca1f0d98 --- /dev/null +++ b/plugins/Xpressive/Xpressive_logo.svg @@ -0,0 +1,106 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/plugins/Xpressive/artwork.png b/plugins/Xpressive/artwork.png index d5b39acee2eb1a99bec7c5581619f7436906b593..1f4e35ad51e06095d738315a49801ca0115195ae 100644 GIT binary patch literal 10950 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 literal 70956 zcmXt9bx<4K+kM;OR=ij#7K#_QB1HlOD*-}Dk>c)d1&Rc>;K74C4erIESRtiAaVri5 zirbgpAKzp)J9}q0lbzjrp7We@Zj_d$GBE)i!GHh#M+{O?)W+;@{pS{8hkxc~jf^dCr3PS@wRRJ(Gm z0~C>a>ELV@ks$7RAieDu|GEVx_j3DcUu0Sozu5qB&lpWw29?XllZ3^Y&pU*YmaE2-khFHR z#-v!SE^!T-IZuw2T3cU3nd&8IM`1xqD zW5h#HP6f1Zj@O&mQIi;5v|w|3cN(ksE8w<=Vvoc8r9+Z-;tU{@RM^ncGUpvK0iAeXp-D|p#hnMZi&{WHEdeEgoF+62A) zgSKr4V(m1!3Usga!$b0v4~}1*p3l?AN3c$_G3#JKQ}S&Hl;7fJ8)r|+%=M6QMI&9k z0#%#QYgzNyuU7okDU}@R!`57e+v|m7eC|?K0?q)J@10N|dGJJ^#M`2eJijtznbB+e zvR4j7`!_oa z`2@?hT)a`qn7Yg{ri~Kwzl9vU=1K%YtFzv{6ms&LM zf}X6QpF<5bPYQ#PJp4Qq1$c~$f#230zO#Is-f5z3#2tGr)XTHJKPnb*IX@&Guhy_+ zR!FglZ%R|h_4jI*f{$+~5@}}8PZQnS*lBK+%();e-z>to;y-qEPFq}Q(jW5vecoAL zLRSp4N0-EBQ1guG9R2DWy)r)lv?`55-i+mBBT}z4Gz7LU^1~z5;cIW6`fj6C7hT@z z=7`S6dy>*o2_U5@J$u$TOjwfx7{^Y9JWNB@%pE>IkxCfwP=XKcRP@?)t}iD?{9AD3 zOC#)+RK}$?$mIc8Rq|^Y;dfG9!ip-Td`xnWnXY|J^u?o+!a}dh- z+%Q(ukZp2Ga=j_pZuqDntUFjKq1g?bb%ckLX_U%kzR>FPkSVOI5;{IY(Nx2VIS4fzD%Cei%sz)=k$S^v?5pVTj7re$tAv!MXzct{3ne?oEkEVZ%vaGNniX#u@jqrM+Kv-7#&X- zUJRjr>>-|x%=<-Emt`P&*kfJjiGMHg$*ZC?zJ0S7OklMGQ_KRiLy+&Z(`|~2(%?-q z`Hu67&`K2$gIzVO zV4C0IR7yFOTsEQAeVgyliedrvgOaKCFT;yqwpcCooQ?h03?4Ws74oU+92Kg>l%U>g zGbJNE8{oLOA?AUoJKB|1C{ZdYW(9{@JqOSHEW3h^`Fp=g{0RUdv{_w=vux}B{9bz^ zulke2dcAgJnt{^CZJz|3TVN}xP0&!ae!WZo3p!S}U|otX_@|IIg~KLJ6Z=9)7@N8W zrxQoHS>-I`a@jxc!X#K1AV&kYlpqWOG8Q*1@Jc$xoQ;GPc77%r{pRVgEl+A?X~cXy$8|W?8K}QSF%+XCqGeYuB$PSLg%R`c(JVl z2}bhM9sO|h+}S7YlKBiISBgZjE#l~Eo6xhL`HZsso_3XPf<%W;6f7vKj*J`{aMWm` z?U|=Tt}O=r8qFG){yItg8CrSZD=E-%FkjBe1J6(?FBvt_=W)HME9&tPElGQ-Fq;`} zAI)Ko$~^nqqhL5%V)|P}qYvVsKFY=;RDohqQScilIaGS4TLZiQ2j@r-(8ysTo_F}X z&Q&_=aCWfF>g6XLMjE!j3p3+isTqk0VbkD1J>|sme(vKKO`91f7axi(QUM+w$)4ma{}~C@3CC$Q>Oms+8Ew;$d^2-hPhd8 z!V-hLSD#OfL`@@u1eg=CYb;rdza0pFCEnj-whZUt{@zw(E272wtab}rt-x1E>{tuR zdC~71ZU2_zUH5G1hG<4kf}fgwgZ`PrI{8+zyMg5ZR8{N04?azyTv)ADiLk-WWCI>CZpB=45eCUl#s{B4uIB

&K{~^TIix3)kfh;7+h1@bQ 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

rzdyv7L?w) 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 Date: Sat, 1 Feb 2020 06:38:59 -0600 Subject: [PATCH 116/160] fix spelling errors (#5385) --- data/presets/ZynAddSubFX/Companion/0074-Smooth Expanded.xiz | 2 +- plugins/lb302/README | 2 +- src/core/Song.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/presets/ZynAddSubFX/Companion/0074-Smooth Expanded.xiz b/data/presets/ZynAddSubFX/Companion/0074-Smooth Expanded.xiz index cc060f775..6ef773a33 100644 --- a/data/presets/ZynAddSubFX/Companion/0074-Smooth Expanded.xiz +++ b/data/presets/ZynAddSubFX/Companion/0074-Smooth Expanded.xiz @@ -19,7 +19,7 @@ Will Godfrey GPL V 2 or later Now has a slow long tail. -Only really noticable on lower notes. +Only really noticeable on lower notes. diff --git a/plugins/lb302/README b/plugins/lb302/README index 2a8058e56..b556d80d4 100644 --- a/plugins/lb302/README +++ b/plugins/lb302/README @@ -15,7 +15,7 @@ BUG: to be caused by 'unexhausted buffers' That is, the problem manifests itself to a greater degree when the user "buffer size" configuration is increased to over 1024 frames or so. The problem is much less - noticable when the buffer size is set to 64 frames. + noticeable when the buffer size is set to 64 frames. BUG: The synth does not make accomodations for sampling rates other than diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 78c9f422a..fc7b51a03 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -1437,7 +1437,7 @@ QString Song::errorSummary() QString errors = m_errors.join("\n") + '\n'; errors.prepend( "\n\n" ); - errors.prepend( tr( "The following errors occured while loading: " ) ); + errors.prepend( tr( "The following errors occurred while loading: " ) ); return errors; } From 89d8363218b1a019f85cff7bf765abfed40dffba Mon Sep 17 00:00:00 2001 From: Martin Pavelek Date: Fri, 7 Feb 2020 07:00:42 +0100 Subject: [PATCH 117/160] Add the vectorscope plugin (#5328) The credit for the `ColorChooser` class goes to CYBERDEViLNL. --- cmake/modules/PluginList.cmake | 1 + include/ColorChooser.h | 41 +++ plugins/SpectrumAnalyzer/SaControls.h | 14 +- plugins/Vectorscope/CMakeLists.txt | 3 + plugins/Vectorscope/README.md | 14 + plugins/Vectorscope/VecControls.cpp | 70 +++++ plugins/Vectorscope/VecControls.h | 66 +++++ plugins/Vectorscope/VecControlsDialog.cpp | 94 +++++++ plugins/Vectorscope/VecControlsDialog.h | 47 ++++ plugins/Vectorscope/VectorView.cpp | 328 ++++++++++++++++++++++ plugins/Vectorscope/VectorView.h | 80 ++++++ plugins/Vectorscope/Vectorscope.cpp | 80 ++++++ plugins/Vectorscope/Vectorscope.h | 52 ++++ plugins/Vectorscope/logo.png | Bin 0 -> 774 bytes 14 files changed, 883 insertions(+), 7 deletions(-) create mode 100644 include/ColorChooser.h create mode 100644 plugins/Vectorscope/CMakeLists.txt create mode 100644 plugins/Vectorscope/README.md create mode 100644 plugins/Vectorscope/VecControls.cpp create mode 100644 plugins/Vectorscope/VecControls.h create mode 100644 plugins/Vectorscope/VecControlsDialog.cpp create mode 100644 plugins/Vectorscope/VecControlsDialog.h create mode 100644 plugins/Vectorscope/VectorView.cpp create mode 100644 plugins/Vectorscope/VectorView.h create mode 100644 plugins/Vectorscope/Vectorscope.cpp create mode 100644 plugins/Vectorscope/Vectorscope.h create mode 100644 plugins/Vectorscope/logo.png diff --git a/cmake/modules/PluginList.cmake b/cmake/modules/PluginList.cmake index c82bba329..2d8530388 100644 --- a/cmake/modules/PluginList.cmake +++ b/cmake/modules/PluginList.cmake @@ -64,6 +64,7 @@ SET(LMMS_PLUGIN_LIST VstEffect watsyn waveshaper + Vectorscope vibed Xpressive zynaddsubfx diff --git a/include/ColorChooser.h b/include/ColorChooser.h new file mode 100644 index 000000000..fe5b7a22a --- /dev/null +++ b/include/ColorChooser.h @@ -0,0 +1,41 @@ +/* ColorChooser.h - declaration and definition of ColorChooser class. + * + * Copyright (c) 2019 CYBERDEViLNL + * + * 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 +#include +#include + +class ColorChooser: public QColorDialog +{ +public: + ColorChooser(const QColor &initial, QWidget *parent): QColorDialog(initial, parent) {}; + ColorChooser(QWidget *parent): QColorDialog(parent) {}; + +protected: + // Forward key events to the parent to prevent stuck notes when the dialog gets focus + void keyReleaseEvent(QKeyEvent *event) override + { + QKeyEvent ke(*event); + QApplication::sendEvent(parentWidget(), &ke); + } +}; diff --git a/plugins/SpectrumAnalyzer/SaControls.h b/plugins/SpectrumAnalyzer/SaControls.h index 4673416bc..ee8a9e001 100644 --- a/plugins/SpectrumAnalyzer/SaControls.h +++ b/plugins/SpectrumAnalyzer/SaControls.h @@ -81,13 +81,13 @@ private: FloatModel m_zeroPaddingModel; // colors (hard-coded, values must add up to specific numbers) - QColor m_colorL; //!< color of the left channel - QColor m_colorR; //!< color of the right channel - QColor m_colorMono; //!< mono color for spectrum display - QColor m_colorMonoW; //!< mono color for waterfall display - QColor m_colorBG; //!< spectrum display background color - QColor m_colorGrid; //!< color of grid lines - QColor m_colorLabels; //!< color of axis labels + QColor m_colorL; //!< color of the left channel + QColor m_colorR; //!< color of the right channel + QColor m_colorMono; //!< mono color for spectrum display + QColor m_colorMonoW; //!< mono color for waterfall display + QColor m_colorBG; //!< spectrum display background color + QColor m_colorGrid; //!< color of grid lines + QColor m_colorLabels; //!< color of axis labels friend class SaControlsDialog; friend class SaSpectrumView; diff --git a/plugins/Vectorscope/CMakeLists.txt b/plugins/Vectorscope/CMakeLists.txt new file mode 100644 index 000000000..b73ff76d5 --- /dev/null +++ b/plugins/Vectorscope/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) +BUILD_PLUGIN(vectorscope Vectorscope.cpp VecControls.cpp VecControlsDialog.cpp VectorView.cpp +MOCFILES VecControls.h VecControlsDialog.h VectorView.h EMBEDDED_RESOURCES logo.png) diff --git a/plugins/Vectorscope/README.md b/plugins/Vectorscope/README.md new file mode 100644 index 000000000..18b218f6d --- /dev/null +++ b/plugins/Vectorscope/README.md @@ -0,0 +1,14 @@ +# Vectorscope plugin + +## Overview + +Vectorscope is a simple stereo field visualizer. Samples are plotted into a graph, with left and right channels providing the coordinates. Previously drawn samples quickly fade away and are continuously replaced by new samples, creating a real-time plot of the most recently played samples. + +Similar to other effect plugins, the top-level widget is VecControlDialog. It displays configuration knobs and the main VectorView widget. The back-end configuration class is VecControls, which holds all models and configuration values. + +VectorView computes and shows the plot. It gets data for processing from the Vectorscope class, which handles the interface with LMMS. In order to avoid any stalling of the realtime-sensitive audio thread, data are exchanged through a lockless ring buffer. + +## Changelog + + 1.0.0 2019-11-21 + - initial release diff --git a/plugins/Vectorscope/VecControls.cpp b/plugins/Vectorscope/VecControls.cpp new file mode 100644 index 000000000..0e7a2d061 --- /dev/null +++ b/plugins/Vectorscope/VecControls.cpp @@ -0,0 +1,70 @@ +/* + * VecControls.cpp - definition of VecControls class. + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 "VecControls.h" + +#include + +#include "VecControlsDialog.h" +#include "Vectorscope.h" + + +VecControls::VecControls(Vectorscope *effect) : + EffectControls(effect), + m_effect(effect), + + // initialize models and set default values + m_persistenceModel(0.5f, 0.0f, 1.0f, 0.05f, this, tr("Display persistence amount")), + m_logarithmicModel(false, this, tr("Logarithmic scale")), + m_highQualityModel(false, this, tr("High quality")) +{ + // Colors (percentages include sRGB gamma correction) + m_colorFG = QColor(60, 255, 130, 255); // ~LMMS green + m_colorGrid = QColor(76, 80, 84, 128); // ~60 % gray (slightly cold / blue), 50 % transparent + m_colorLabels = QColor(76, 80, 84, 255); // ~60 % gray (slightly cold / blue) + m_colorOutline = QColor(30, 34, 38, 255); // ~40 % gray (slightly cold / blue) +} + + +// Create the VecControlDialog widget which handles display of GUI elements. +EffectControlDialog* VecControls::createView() +{ + return new VecControlsDialog(this); +} + + +void VecControls::loadSettings(const QDomElement &element) +{ + m_persistenceModel.loadSettings(element, "Persistence"); + m_logarithmicModel.loadSettings(element, "Logarithmic"); + m_highQualityModel.loadSettings(element, "HighQuality"); +} + + +void VecControls::saveSettings(QDomDocument &document, QDomElement &element) +{ + m_persistenceModel.saveSettings(document, element, "Persistence"); + m_logarithmicModel.saveSettings(document, element, "Logarithmic"); + m_highQualityModel.saveSettings(document, element, "HighQuality"); +} diff --git a/plugins/Vectorscope/VecControls.h b/plugins/Vectorscope/VecControls.h new file mode 100644 index 000000000..04b688e5a --- /dev/null +++ b/plugins/Vectorscope/VecControls.h @@ -0,0 +1,66 @@ +/* + * VecControls.h - declaration of VecControls class. + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 VECCONTROLS_H +#define VECCONTROLS_H + +#include + +#include "EffectControls.h" + + +class Vectorscope; + +// Holds all the configuration values +class VecControls : public EffectControls +{ + Q_OBJECT +public: + explicit VecControls(Vectorscope *effect); + virtual ~VecControls() {} + + EffectControlDialog *createView() override; + + void saveSettings (QDomDocument &document, QDomElement &element) override; + void loadSettings (const QDomElement &element) override; + + QString nodeName() const override {return "Vectorscope";} + int controlCount() override {return 3;} + +private: + Vectorscope *m_effect; + + FloatModel m_persistenceModel; + BoolModel m_logarithmicModel; + BoolModel m_highQualityModel; + + QColor m_colorFG; + QColor m_colorGrid; + QColor m_colorLabels; + QColor m_colorOutline; + + friend class VecControlsDialog; + friend class VectorView; +}; +#endif // VECCONTROLS_H diff --git a/plugins/Vectorscope/VecControlsDialog.cpp b/plugins/Vectorscope/VecControlsDialog.cpp new file mode 100644 index 000000000..9916d7756 --- /dev/null +++ b/plugins/Vectorscope/VecControlsDialog.cpp @@ -0,0 +1,94 @@ +/* + * VecControlsDialog.cpp - definition of VecControlsDialog class. + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 "VecControlsDialog.h" + +#include +#include +#include +#include +#include + +#include "embed.h" +#include "LedCheckbox.h" +#include "VecControls.h" +#include "Vectorscope.h" +#include "VectorView.h" + + +// The entire GUI layout is built here. +VecControlsDialog::VecControlsDialog(VecControls *controls) : + EffectControlDialog(controls), + m_controls(controls) +{ + QVBoxLayout *master_layout = new QVBoxLayout; + master_layout->setContentsMargins(0, 2, 0, 0); + setLayout(master_layout); + + // Visualizer widget + // The size of 768 pixels seems to offer a good balance of speed, accuracy and trace thickness. + VectorView *display = new VectorView(controls, m_controls->m_effect->getBuffer(), 768, this); + master_layout->addWidget(display); + + // Config area located inside visualizer + QVBoxLayout *internal_layout = new QVBoxLayout(display); + QHBoxLayout *config_layout = new QHBoxLayout(); + QVBoxLayout *switch_layout = new QVBoxLayout(); + internal_layout->addStretch(); + internal_layout->addLayout(config_layout); + config_layout->addLayout(switch_layout); + + // High-quality switch + LedCheckBox *highQualityButton = new LedCheckBox(tr("HQ"), this); + highQualityButton->setToolTip(tr("Double the resolution and simulate continuous analog-like trace.")); + highQualityButton->setCheckable(true); + highQualityButton->setMinimumSize(70, 12); + highQualityButton->setModel(&controls->m_highQualityModel); + switch_layout->addWidget(highQualityButton); + + // Log. scale switch + LedCheckBox *logarithmicButton = new LedCheckBox(tr("Log. scale"), this); + logarithmicButton->setToolTip(tr("Display amplitude on logarithmic scale to better see small values.")); + logarithmicButton->setCheckable(true); + logarithmicButton->setMinimumSize(70, 12); + logarithmicButton->setModel(&controls->m_logarithmicModel); + switch_layout->addWidget(logarithmicButton); + + config_layout->addStretch(); + + // Persistence knob + Knob *persistenceKnob = new Knob(knobSmall_17, this); + persistenceKnob->setModel(&controls->m_persistenceModel); + persistenceKnob->setLabel(tr("Persist.")); + persistenceKnob->setToolTip(tr("Trace persistence: higher amount means the trace will stay bright for longer time.")); + persistenceKnob->setHintText(tr("Trace persistence"), ""); + config_layout->addWidget(persistenceKnob); +} + + +// Suggest the best widget size. +QSize VecControlsDialog::sizeHint() const +{ + return QSize(275, 300); +} diff --git a/plugins/Vectorscope/VecControlsDialog.h b/plugins/Vectorscope/VecControlsDialog.h new file mode 100644 index 000000000..b76c06ad0 --- /dev/null +++ b/plugins/Vectorscope/VecControlsDialog.h @@ -0,0 +1,47 @@ +/* + * VecControlsDialog.h - declatation of VecControlsDialog class. + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 VECCONTROLSDIALOG_H +#define VECCONTROLSDIALOG_H + +#include "EffectControlDialog.h" + +class VecControls; + +//! Top-level widget holding the configuration GUI and vector display +class VecControlsDialog : public EffectControlDialog +{ + Q_OBJECT +public: + explicit VecControlsDialog(VecControls *controls); + virtual ~VecControlsDialog() {} + + bool isResizable() const override {return true;} + QSize sizeHint() const override; + +private: + VecControls *m_controls; +}; + +#endif // VECCONTROLSDIALOG_H diff --git a/plugins/Vectorscope/VectorView.cpp b/plugins/Vectorscope/VectorView.cpp new file mode 100644 index 000000000..9a3f855eb --- /dev/null +++ b/plugins/Vectorscope/VectorView.cpp @@ -0,0 +1,328 @@ +/* VectorView.cpp - implementation of VectorView class. + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 "VectorView.h" + +#include +#include +#include +#include +#include + +#include "ColorChooser.h" +#include "GuiApplication.h" +#include "MainWindow.h" + + +VectorView::VectorView(VecControls *controls, LocklessRingBuffer *inputBuffer, unsigned short displaySize, QWidget *parent) : + QWidget(parent), + m_controls(controls), + m_inputBuffer(inputBuffer), + m_bufferReader(*inputBuffer), + m_displaySize(displaySize), + m_zoom(1.f), + m_persistTimestamp(0), + m_zoomTimestamp(0), + m_oldHQ(m_controls->m_highQualityModel.value()), + m_oldX(m_displaySize / 2), + m_oldY(m_displaySize / 2) +{ + setMinimumSize(200, 200); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + connect(gui->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(periodicUpdate())); + + m_displayBuffer.resize(sizeof qRgb(0,0,0) * m_displaySize * m_displaySize, 0); + +#ifdef VEC_DEBUG + m_executionAvg = 0; +#endif +} + + +// Compose and draw all the content; called by Qt. +void VectorView::paintEvent(QPaintEvent *event) +{ +#ifdef VEC_DEBUG + unsigned int drawTime = std::chrono::high_resolution_clock::now().time_since_epoch().count(); +#endif + + // All drawing done in this method, local variables are sufficient for the boundary + const int displayTop = 2; + const int displayBottom = height() - 2; + const int displayLeft = 2; + const int displayRight = width() - 2; + const int displayWidth = displayRight - displayLeft; + const int displayHeight = displayBottom - displayTop; + + const float centerX = displayLeft + (displayWidth / 2.f); + const float centerY = displayTop + (displayWidth / 2.f); + + const int margin = 4; + const int gridCorner = 30; + + // Setup QPainter and font sizes + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing, true); + + QFont normalFont, boldFont; + boldFont.setPixelSize(26); + boldFont.setBold(true); + const int labelWidth = 26; + const int labelHeight = 26; + + bool hq = m_controls->m_highQualityModel.value(); + + // Clear display buffer if quality setting was changed + if (hq != m_oldHQ) + { + m_oldHQ = hq; + for (std::size_t i = 0; i < m_displayBuffer.size(); i++) + { + m_displayBuffer.data()[i] = 0; + } + } + + // Dim stored image based on persistence setting and elapsed time. + // Update period is limited to 50 ms (20 FPS) for non-HQ mode and 10 ms (100 FPS) for HQ mode. + const unsigned int currentTimestamp = std::chrono::duration_cast + ( + std::chrono::high_resolution_clock::now().time_since_epoch() + ).count(); + const unsigned int elapsed = currentTimestamp - m_persistTimestamp; + const unsigned int threshold = hq ? 10 : 50; + if (elapsed > threshold) + { + m_persistTimestamp = currentTimestamp; + // Non-HQ mode uses half the resolution → use limited buffer space. + const std::size_t useableBuffer = hq ? m_displayBuffer.size() : m_displayBuffer.size() / 4; + // The knob value is interpreted on log. scale, otherwise the effect would ramp up too slowly. + // Persistence value specifies fraction of light intensity that remains after 10 ms. + // → Compensate it based on elapsed time (exponential decay). + const float persist = log10(1 + 9 * m_controls->m_persistenceModel.value()); + const float persistPerFrame = pow(persist, elapsed / 10.f); + // Note that for simplicity and performance reasons, this implementation only dims all stored + // values by a given factor. A true simulation would also do the inverse of desaturation that + // occurs in high-intensity traces in HQ mode. + for (std::size_t i = 0; i < useableBuffer; i++) + { + m_displayBuffer.data()[i] *= persistPerFrame; + } + } + + // Get new samples from the lockless input FIFO buffer + auto inBuffer = m_bufferReader.read_max(m_inputBuffer->capacity()); + std::size_t frameCount = inBuffer.size(); + + // Draw new points on top + float left, right; + int x, y; + + const bool logScale = m_controls->m_logarithmicModel.value(); + const unsigned short activeSize = hq ? m_displaySize : m_displaySize / 2; + + // Helper lambda functions for better readability + // Make sure pixel stays within display bounds: + auto saturate = [=](short pixelPos) {return qBound((short)0, pixelPos, (short)(activeSize - 1));}; + // Take existing pixel and brigthen it. Very bright light should reduce saturation and become + // white. This effect is easily approximated by capping elementary colors to 255 individually. + auto updatePixel = [&](unsigned short x, unsigned short y, QColor addedColor) + { + QColor currentColor = ((QRgb*)m_displayBuffer.data())[x + y * activeSize]; + currentColor.setRed(std::min(currentColor.red() + addedColor.red(), 255)); + currentColor.setGreen(std::min(currentColor.green() + addedColor.green(), 255)); + currentColor.setBlue(std::min(currentColor.blue() + addedColor.blue(), 255)); + ((QRgb*)m_displayBuffer.data())[x + y * activeSize] = currentColor.rgb(); + }; + + if (hq) + { + // High quality mode: check distance between points and draw a line. + // The longer the line is, the dimmer, simulating real electron trace on luminescent screen. + for (std::size_t frame = 0; frame < frameCount; frame++) + { + float inLeft = inBuffer[frame][0] * m_zoom; + float inRight = inBuffer[frame][1] * m_zoom; + // Scale left and right channel from (-1.0, 1.0) to display range + if (logScale) + { + // To better preserve shapes, the log scale is applied to the distance from origin, + // not the individual channels. + const float distance = sqrt(inLeft * inLeft + inRight * inRight); + const float distanceLog = log10(1 + 9 * abs(distance)); + const float angleCos = inLeft / distance; + const float angleSin = inRight / distance; + left = distanceLog * angleCos * (activeSize - 1) / 4; + right = distanceLog * angleSin * (activeSize - 1) / 4; + } + else + { + left = inLeft * (activeSize - 1) / 4; + right = inRight * (activeSize - 1) / 4; + } + + // Rotate display coordinates 45 degrees, flip Y axis and make sure the result stays within bounds + x = saturate(right - left + activeSize / 2.f); + y = saturate(activeSize - (right + left + activeSize / 2.f)); + + // Estimate number of points needed to fill space between the old and new pixel. Cap at 100. + unsigned char points = std::min((int)sqrt((m_oldX - x) * (m_oldX - x) + (m_oldY - y) * (m_oldY - y)), 100); + + // Large distance = dim trace. The curve for darker() is choosen so that: + // - no movement (0 points) actually _increases_ brightness slightly, + // - one point between samples = returns exactly the specified color, + // - one to 99 points between samples = follows a sharp "1/x" decaying curve, + // - 100 points between samples = returns approximately 5 % brightness. + // Everything else is discarded (by the 100 point cap) because there is not much to see anyway. + QColor addedColor = m_controls->m_colorFG.darker(75 + 20 * points).rgb(); + + // Draw the new pixel: the beam sweeps across area that may have been excited before + // → add new value to existing pixel state. + updatePixel(x, y, addedColor); + + // Draw interpolated points between the old pixel and the new one + int newX = right - left + activeSize / 2.f; + int newY = activeSize - (right + left + activeSize / 2.f); + for (unsigned char i = 1; i < points; i++) + { + x = saturate(((points - i) * m_oldX + i * newX) / points); + y = saturate(((points - i) * m_oldY + i * newY) / points); + updatePixel(x, y, addedColor); + } + m_oldX = newX; + m_oldY = newY; + } + } + else + { + // To improve performance, non-HQ mode uses smaller display size and only + // one full-color pixel per sample. + for (std::size_t frame = 0; frame < frameCount; frame++) + { + float inLeft = inBuffer[frame][0] * m_zoom; + float inRight = inBuffer[frame][1] * m_zoom; + if (logScale) { + const float distance = sqrt(inLeft * inLeft + inRight * inRight); + const float distanceLog = log10(1 + 9 * abs(distance)); + const float angleCos = inLeft / distance; + const float angleSin = inRight / distance; + left = distanceLog * angleCos * (activeSize - 1) / 4; + right = distanceLog * angleSin * (activeSize - 1) / 4; + } else { + left = inLeft * (activeSize - 1) / 4; + right = inRight * (activeSize - 1) / 4; + } + x = saturate(right - left + activeSize / 2.f); + y = saturate(activeSize - (right + left + activeSize / 2.f)); + ((QRgb*)m_displayBuffer.data())[x + y * activeSize] = m_controls->m_colorFG.rgb(); + } + } + + // Draw background + painter.fillRect(displayLeft, displayTop, displayWidth, displayHeight, QColor(0,0,0)); + + // Draw the final image + QImage temp = QImage(m_displayBuffer.data(), + activeSize, + activeSize, + QImage::Format_RGB32); + temp.setDevicePixelRatio(devicePixelRatio()); + painter.drawImage(displayLeft, displayTop, + temp.scaledToWidth(displayWidth * devicePixelRatio(), + Qt::SmoothTransformation)); + + // Draw the grid and labels + painter.setPen(QPen(m_controls->m_colorGrid, 1.5, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); + painter.drawEllipse(QPointF(centerX, centerY), displayWidth / 2.f, displayWidth / 2.f); + painter.setPen(QPen(m_controls->m_colorGrid, 1.5, Qt::DotLine, Qt::RoundCap, Qt::BevelJoin)); + painter.drawLine(QPointF(centerX, centerY), QPointF(displayLeft + gridCorner, displayTop + gridCorner)); + painter.drawLine(QPointF(centerX, centerY), QPointF(displayRight - gridCorner, displayTop + gridCorner)); + + painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); + painter.setFont(boldFont); + painter.drawText(displayLeft + margin, displayTop, + labelWidth, labelHeight, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip, + QString("L")); + painter.drawText(displayRight - margin - labelWidth, displayTop, + labelWidth, labelHeight, Qt::AlignRight| Qt::AlignTop | Qt::TextDontClip, + QString("R")); + + // Draw the outline + painter.setPen(QPen(m_controls->m_colorOutline, 2, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); + painter.drawRoundedRect(1, 1, width() - 2, height() - 2, 2.f, 2.f); + + // Draw zoom info if changed within last second (re-using timestamp acquired for dimming) + if (currentTimestamp - m_zoomTimestamp < 1000) + { + painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); + painter.setFont(normalFont); + painter.drawText(displayWidth / 2 - 50, displayBottom - 20, 100, 16, Qt::AlignCenter, + QString("Zoom: ").append(std::to_string((int)round(m_zoom * 100)).c_str()).append(" %")); + } + + // Optionally measure drawing performance +#ifdef VEC_DEBUG + drawTime = std::chrono::high_resolution_clock::now().time_since_epoch().count() - drawTime; + m_executionAvg = 0.95f * m_executionAvg + 0.05f * drawTime / 1000000.f; + painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); + painter.setFont(normalFont); + painter.drawText(displayWidth / 2 - 50, displayBottom - 16, 100, 16, Qt::AlignLeft, + QString("Exec avg.: ").append(std::to_string(m_executionAvg).substr(0, 5).c_str()).append(" ms")); +#endif +} + + +// Periodically trigger repaint and check if the widget is visible +void VectorView::periodicUpdate() +{ + m_visible = isVisible(); + if (m_visible) {update();} +} + + +// Allow to change color on double-click. +// More of an Easter egg, to avoid cluttering the interface with non-essential functionality. +void VectorView::mouseDoubleClickEvent(QMouseEvent *event) +{ + ColorChooser *colorDialog = new ColorChooser(m_controls->m_colorFG, this); + if (colorDialog->exec()) + { + m_controls->m_colorFG = colorDialog->currentColor(); + } +} + + +// Change zoom level using the mouse wheel +void VectorView::wheelEvent(QWheelEvent *event) +{ + // Go through integers to avoid accumulating errors + const unsigned short old_zoom = round(100 * m_zoom); + // Min-max bounds are 20 and 1000 %, step for 15°-increment mouse wheel is 20 % + const unsigned short new_zoom = qBound(20, old_zoom + event->angleDelta().y() / 6, 1000); + m_zoom = new_zoom / 100.f; + event->accept(); + m_zoomTimestamp = std::chrono::duration_cast + ( + std::chrono::high_resolution_clock::now().time_since_epoch() + ).count(); + +} diff --git a/plugins/Vectorscope/VectorView.h b/plugins/Vectorscope/VectorView.h new file mode 100644 index 000000000..066e306a0 --- /dev/null +++ b/plugins/Vectorscope/VectorView.h @@ -0,0 +1,80 @@ +/* VectorView.h - declaration of VectorView class. + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 VECTORVIEW_H +#define VECTORVIEW_H + +#include +#include +#include + +#include "Knob.h" +#include "LedCheckbox.h" +#include "LocklessRingBuffer.h" +#include "VecControls.h" + +//#define VEC_DEBUG + + +// Widget that displays a vectorscope visualization of stereo signal. +class VectorView : public QWidget +{ + Q_OBJECT +public: + explicit VectorView(VecControls *controls, LocklessRingBuffer *inputBuffer, unsigned short displaySize, QWidget *parent = 0); + virtual ~VectorView() {} + + QSize sizeHint() const override {return QSize(300, 300);} + +protected: + void paintEvent(QPaintEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + void wheelEvent(QWheelEvent *event) override; + +private slots: + void periodicUpdate(); + +private: + VecControls *m_controls; + + LocklessRingBuffer *m_inputBuffer; + LocklessRingBufferReader m_bufferReader; + + std::vector m_displayBuffer; + const unsigned short m_displaySize; + + bool m_visible; + + float m_zoom; + + // State variables for comparison with previous repaint + unsigned int m_persistTimestamp; + unsigned int m_zoomTimestamp; + bool m_oldHQ; + int m_oldX; + int m_oldY; + +#ifdef VEC_DEBUG + float m_executionAvg = 0; +#endif +}; +#endif // VECTORVIEW_H diff --git a/plugins/Vectorscope/Vectorscope.cpp b/plugins/Vectorscope/Vectorscope.cpp new file mode 100644 index 000000000..f8bc30c40 --- /dev/null +++ b/plugins/Vectorscope/Vectorscope.cpp @@ -0,0 +1,80 @@ +/* + * Vectorscope.cpp - definition of Vectorscope class. + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 "Vectorscope.h" + +#include "embed.h" +#include "plugin_export.h" + + +extern "C" { + Plugin::Descriptor PLUGIN_EXPORT vectorscope_plugin_descriptor = + { + STRINGIFY(PLUGIN_NAME), + "Vectorscope", + QT_TRANSLATE_NOOP("pluginBrowser", "A stereo field visualizer."), + "Martin Pavelek ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader("logo"), + NULL, + NULL + }; +} + + +Vectorscope::Vectorscope(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : + Effect(&vectorscope_plugin_descriptor, parent, key), + m_controls(this), + // Buffer is sized to cover 4* the current maximum LMMS audio buffer size, + // so that it has some reserve space in case GUI thresd is busy. + m_inputBuffer(4 * m_maxBufferSize) +{ +} + + +// Take audio data and store them for processing and display in the GUI thread. +bool Vectorscope::processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) +{ + if (!isEnabled() || !isRunning ()) {return false;} + + // Skip processing if the controls dialog isn't visible, it would only waste CPU cycles. + if (m_controls.isViewVisible()) + { + // To avoid processing spikes on audio thread, data are stored in + // a lockless ringbuffer and processed in a separate thread. + m_inputBuffer.write(buffer, frame_count); + } + return isRunning(); +} + + +extern "C" { + // needed for getting plugin out of shared lib + PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *parent, void *data) + { + return new Vectorscope(parent, static_cast(data)); + } +} + diff --git a/plugins/Vectorscope/Vectorscope.h b/plugins/Vectorscope/Vectorscope.h new file mode 100644 index 000000000..b45ff6de4 --- /dev/null +++ b/plugins/Vectorscope/Vectorscope.h @@ -0,0 +1,52 @@ +/* Vectorscope.h - declaration of Vectorscope class. + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 VECTORSCOPE_H +#define VECTORSCOPE_H + +#include "Effect.h" +#include "LocklessRingBuffer.h" +#include "VecControls.h" + + +//! Top level class; handles LMMS interface and accumulates data for processing. +class Vectorscope : public Effect +{ +public: + Vectorscope(Model *parent, const Descriptor::SubPluginFeatures::Key *key); + virtual ~Vectorscope() {}; + + bool processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) override; + EffectControls *controls() override {return &m_controls;} + LocklessRingBuffer *getBuffer() {return &m_inputBuffer;} + +private: + VecControls m_controls; + + // Maximum LMMS buffer size (hard coded, the actual constant is hard to get) + const unsigned int m_maxBufferSize = 4096; + LocklessRingBuffer m_inputBuffer; +}; + +#endif // VECTORSCOPE_H + diff --git a/plugins/Vectorscope/logo.png b/plugins/Vectorscope/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9340da708dd79ed97111eb535f51b81a91d6a15b GIT binary patch literal 774 zcmV+h1Nr=kP)7WEc)VQ)zLm`B#lSD% z0Wg;#IH?7|3b0p&fj_`2;A{cm@zx+(Ge?s$@EN$6LtMjg>;+(boCbaXw;ja=b6mQ?gRp67Yf18(18niCN0v&eY^{Cr&#;#IcF{ks?!* z&o!_q>9xbSw`QytRI!M?zP_o#K-8ExJaV?vi znPt?~0KhTu23U+Gy8^Tw;^SzWSet9n Date: Thu, 2 Jan 2020 12:33:50 +0900 Subject: [PATCH 118/160] macOS CI: fix package conflicts due to Homebrew's Python 2 --- .circleci/config.yml | 5 ++++- .travis/osx..before_install.sh | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a3be541b5..f7f509d7e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -178,7 +178,10 @@ jobs: - *restore_cache - run: name: Install Homebrew dependencies - command: brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio fltk qt5 carla + command: | + # unlink Homebrew's python 2 to prevent an node-gyp error + brew unlink python@2 || true + brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio fltk qt5 carla - run: name: Install nodejs dependencies command: npm install -g appdmg diff --git a/.travis/osx..before_install.sh b/.travis/osx..before_install.sh index b59920a5e..61f25af66 100755 --- a/.travis/osx..before_install.sh +++ b/.travis/osx..before_install.sh @@ -3,3 +3,5 @@ set -e brew update +# Python 2 may cause conflicts on dependency installation +brew unlink python@2 || true From 3410db4d99bd24b32ea10c53c9703ae8c1a3bd75 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sun, 29 Dec 2019 15:18:36 +0900 Subject: [PATCH 119/160] Travis/macOS: rename qt5 to qt in the installation script Travis-CI started installs Qt 5 to their macOS images. To exclude qt properly, it should not be written as its alias 'qt5'. --- .travis/osx..install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/osx..install.sh b/.travis/osx..install.sh index e3dd670bf..93d478c40 100755 --- a/.travis/osx..install.sh +++ b/.travis/osx..install.sh @@ -2,7 +2,7 @@ set -e -PACKAGES="cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio node fltk qt5 carla" +PACKAGES="cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio node fltk qt carla" if "${TRAVIS}"; then PACKAGES="$PACKAGES ccache" From a77e592c19aacecf1ba2e305646760998470211b Mon Sep 17 00:00:00 2001 From: tresf Date: Sat, 8 Feb 2020 01:57:30 -0500 Subject: [PATCH 120/160] =?UTF-8?q?Fix=20handling=20of=20plugin=5Fexport.h?= =?UTF-8?q?=20Fixes=20error:=20definition=20is=20marked=20=E2=80=98dllimpo?= =?UTF-8?q?rt=E2=80=99=20Per=20#4813?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/carlabase/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/carlabase/CMakeLists.txt b/plugins/carlabase/CMakeLists.txt index 722f8463d..264780996 100644 --- a/plugins/carlabase/CMakeLists.txt +++ b/plugins/carlabase/CMakeLists.txt @@ -8,14 +8,15 @@ ENDIF() # If Carla was not provided by the system, make a dummy library instead if(LMMS_HAVE_WEAKCARLA) - # Mimic the Makefile header - FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/config.h "") + # Mimic the autoconf header + FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/autoconf) + FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/autoconf/config.h "") SET(CARLA_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/carla/source ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/includes ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/utils ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/backend - ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/autoconf ) INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS}) ADD_LIBRARY(carla_native-plugin MODULE DummyCarla.cpp) From eebdc0f4be2be800d86758e91cabb47d55d6ac75 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Date: Fri, 21 Feb 2020 19:26:29 +0100 Subject: [PATCH 121/160] Linked model groups (#4964) Add labeled controls for different types with a common base class Implement a container for multiple equal groups of linked models and suiting views. Such groups are suited for representing mono effects where each Model occurs twice. A group provides Models for one mono processor and is visually represented with a group box. This concept is common for LADSPA and Lv2, and useful for any mono effect. --- include/ControlLayout.h | 133 ++++++++++ include/Controls.h | 134 ++++++++++ include/LinkedModelGroupViews.h | 105 ++++++++ include/LinkedModelGroups.h | 175 ++++++++++++ include/stdshims.h | 7 + src/core/CMakeLists.txt | 1 + src/core/LinkedModelGroups.cpp | 185 +++++++++++++ src/gui/CMakeLists.txt | 3 + src/gui/widgets/ControlLayout.cpp | 308 ++++++++++++++++++++++ src/gui/widgets/Controls.cpp | 140 ++++++++++ src/gui/widgets/LinkedModelGroupViews.cpp | 160 +++++++++++ 11 files changed, 1351 insertions(+) create mode 100644 include/ControlLayout.h create mode 100644 include/Controls.h create mode 100644 include/LinkedModelGroupViews.h create mode 100644 include/LinkedModelGroups.h create mode 100644 src/core/LinkedModelGroups.cpp create mode 100644 src/gui/widgets/ControlLayout.cpp create mode 100644 src/gui/widgets/Controls.cpp create mode 100644 src/gui/widgets/LinkedModelGroupViews.cpp diff --git a/include/ControlLayout.h b/include/ControlLayout.h new file mode 100644 index 000000000..625b3f468 --- /dev/null +++ b/include/ControlLayout.h @@ -0,0 +1,133 @@ +/* + * ControlLayout.h - layout for controls + * + * Copyright (c) 2019-2019 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. + * + */ + +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CONTROLLAYOUT_H +#define CONTROLLAYOUT_H + +#include +#include +#include +class QLayoutItem; +class QRect; +class QString; + +/** + Layout for controls (models) + + Originally token from Qt's FlowLayout example. Modified. + + Features a search bar, as well as looking up widgets with string keys + Keys have to be provided in the widgets' objectNames +*/ +class ControlLayout : public QLayout +{ + Q_OBJECT + +public: + explicit ControlLayout(QWidget *parent, + int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~ControlLayout() override; + + void addItem(QLayoutItem *item) override; + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int) const override; + int count() const override; + QLayoutItem *itemAt(int index) const override; + QLayoutItem *itemByString(const QString& key) const; + QSize minimumSize() const override; + void setGeometry(const QRect &rect) override; + QSize sizeHint() const override; + QLayoutItem *takeAt(int index) override; + +private slots: + void onTextChanged(const QString&); + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + QMap::const_iterator pairAt(int index) const; + + QMultiMap m_itemMap; + int m_hSpace; + int m_vSpace; + // relevant dimension is width, as later, heightForWidth() will be called + // 400 looks good and is ~4 knobs in a row + constexpr const static int m_minWidth = 400; + class QLineEdit* m_searchBar; + //! name of search bar, must be ASCII sorted before any alpha numerics + static constexpr const char* s_searchBarName = "!!searchBar!!"; +}; + +#endif // CONTROLLAYOUT_H diff --git a/include/Controls.h b/include/Controls.h new file mode 100644 index 000000000..236abbc11 --- /dev/null +++ b/include/Controls.h @@ -0,0 +1,134 @@ +/* + * Controls.h - labeled control widgets + * + * Copyright (c) 2019-2019 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 CONTROLS_H +#define CONTROLS_H + + +#include "Model.h" + +// headers only required for covariance +#include "AutomatableModel.h" +#include "ComboBoxModel.h" + + +class QString; +class QWidget; +class AutomatableModel; + + +/** + These classes provide + - a control with a text label + - a type safe way to set a model + (justification: setting the wrong typed model to a widget will cause + hard-to-find runtime errors) +*/ +class Control +{ +public: + virtual QWidget* topWidget() = 0; + virtual void setText(const QString& text) = 0; + + virtual void setModel(AutomatableModel* model) = 0; + virtual AutomatableModel* model() = 0; + virtual class AutomatableModelView* modelView() = 0; + + virtual ~Control(); +}; + + +class KnobControl : public Control +{ + class Knob* m_knob; + +public: + void setText(const QString& text) override; + QWidget* topWidget() override; + + void setModel(AutomatableModel* model) override; + FloatModel* model() override; + class AutomatableModelView* modelView() override; + + KnobControl(QWidget* parent = nullptr); + ~KnobControl() override; +}; + + +class ComboControl : public Control +{ + QWidget* m_widget; + class ComboBox* m_combo; + class QLabel* m_label; + +public: + void setText(const QString& text) override; + QWidget* topWidget() override { return m_widget; } + + void setModel(AutomatableModel* model) override; + ComboBoxModel* model() override; + class AutomatableModelView* modelView() override; + + ComboControl(QWidget* parent = nullptr); + ~ComboControl() override; +}; + + +class LcdControl : public Control +{ + class LcdSpinBox* m_lcd; + +public: + void setText(const QString& text) override; + QWidget* topWidget() override; + + void setModel(AutomatableModel* model) override; + IntModel* model() override; + class AutomatableModelView* modelView() override; + + LcdControl(int numDigits, QWidget* parent = nullptr); + ~LcdControl() override; +}; + + +class CheckControl : public Control +{ + QWidget* m_widget; + class LedCheckBox* m_checkBox; + QLabel* m_label; + +public: + void setText(const QString& text) override; + QWidget* topWidget() override; + + void setModel(AutomatableModel* model) override; + BoolModel *model() override; + class AutomatableModelView* modelView() override; + + CheckControl(QWidget* parent = nullptr); + ~CheckControl() override; +}; + + +#endif // CONTROLS_H diff --git a/include/LinkedModelGroupViews.h b/include/LinkedModelGroupViews.h new file mode 100644 index 000000000..79fab76af --- /dev/null +++ b/include/LinkedModelGroupViews.h @@ -0,0 +1,105 @@ +/* + * LinkedModelGroupViews.h - view for groups of linkable models + * + * Copyright (c) 2019-2019 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 LINKEDMODELGROUPVIEWS_H +#define LINKEDMODELGROUPVIEWS_H + + +#include +#include +#include +#include + + +/** + @file LinkedModelGroupViews.h + See Lv2ViewBase.h for example usage +*/ + + +/** + View for a representative processor + + Features: + * Remove button for removable models + * Simple handling of adding, removing and model changing + + @note Neither this class, nor any inheriting classes, shall inherit + ModelView. The "view" in the name is just for consistency + with LinkedModelGroupsView. +*/ +class LinkedModelGroupView : public QWidget +{ +public: + /** + @param colNum numbers of columns for the controls + (link LEDs not counted) + */ + LinkedModelGroupView(QWidget *parent, class LinkedModelGroup* model, + std::size_t colNum); + ~LinkedModelGroupView(); + + //! Reconnect models if model changed + void modelChanged(class LinkedModelGroup *linkedModelGroup); + +protected: + //! Add a control to this widget + //! @warning This widget will own this control, do not free it + void addControl(class Control *ctrl, const std::string &id, + const std::string& display, bool removable); + + void removeControl(const QString &key); + +private: + class LinkedModelGroup* m_model; + + //! column number in surrounding grid in LinkedModelGroupsView + std::size_t m_colNum; + class ControlLayout* m_layout; + std::map> m_widgets; +}; + + +/** + Container class for one LinkedModelGroupView + + @note It's intended this class does not inherit from ModelView. + Inheriting classes need to do that, see e.g. Lv2Instrument.h +*/ +class LinkedModelGroupsView +{ +protected: + ~LinkedModelGroupsView() = default; + + //! Reconnect models if model changed; to be called by child virtuals + void modelChanged(class LinkedModelGroups* ctrlBase); + +private: + //! The base class must return the adressed group view, + //! which has the same value as "this" + virtual LinkedModelGroupView* getGroupView() = 0; +}; + + +#endif // LINKEDMODELGROUPVIEWS_H diff --git a/include/LinkedModelGroups.h b/include/LinkedModelGroups.h new file mode 100644 index 000000000..355290d9a --- /dev/null +++ b/include/LinkedModelGroups.h @@ -0,0 +1,175 @@ +/* + * LinkedModelGroups.h - base classes for groups of linked models + * + * Copyright (c) 2019-2019 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 LINKEDMODELGROUPS_H +#define LINKEDMODELGROUPS_H + + +#include +#include +#include + +#include "Model.h" + + +/** + @file LinkedModelGroups.h + See Lv2ControlBase.h and Lv2Proc.h for example usage +*/ + + +/** + Base class for a group of linked models + + See the LinkedModelGroup class for explenations + + Features: + * Models are stored by their QObject::objectName + * Models are linked automatically +*/ +class LinkedModelGroup : public Model +{ + Q_OBJECT +public: + /* + Initialization + */ + //! @param parent model of the LinkedModelGroups class + LinkedModelGroup(Model* parent) : Model(parent) {} + + /* + Linking (initially only) + */ + void linkControls(LinkedModelGroup *other); + + /* + Models + */ + struct ModelInfo + { + QString m_name; + class AutomatableModel* m_model; + ModelInfo() { /* hopefully no one will use this */ } // TODO: remove? + ModelInfo(const QString& name, AutomatableModel* model) + : m_name(name), m_model(model) {} + }; + + // TODO: refactor those 2 + template + void foreach_model(const Functor& ftor) + { + for (auto itr = m_models.begin(); itr != m_models.end(); ++itr) + { + ftor(itr->first, itr->second); + } + } + + template + void foreach_model(const Functor& ftor) const + { + for (auto itr = m_models.cbegin(); itr != m_models.cend(); ++itr) + { + ftor(itr->first, itr->second); + } + } + + std::size_t modelNum() const { return m_models.size(); } + bool containsModel(const QString& name) const; + void removeControl(AutomatableModel *); + + /* + Load/Save + */ + void saveValues(class QDomDocument& doc, class QDomElement& that); + void loadValues(const class QDomElement& that); + +signals: + // NOTE: when separating core from UI, this will need to be removed + // (who would kno if the client is Qt, i.e. it may not have slots at all) + // In this case you'd e.g. send the UI something like + // "/added " + void modelAdded(AutomatableModel* added); + void modelRemoved(AutomatableModel* removed); + +public: + AutomatableModel* getModel(const std::string& s) + { + auto itr = m_models.find(s); + return (itr == m_models.end()) ? nullptr : itr->second.m_model; + } + + //! Register a further model + void addModel(class AutomatableModel* model, const QString& name); + //! Unregister a model, return true if a model was erased + bool eraseModel(const QString& name); + + //! Remove all models + void clearModels(); + +private: + //! models for the controls + std::map m_models; +}; + + +/** + Container for a group of linked models + + Each group contains the same models and model types. The models are + numbered, and equal numbered models are associated and always linked. + + A typical application are two mono plugins making a stereo plugin. + + @note Though this class can contain multiple model groups, a corresponding + view ("LinkedModelGroupViews") will only display one group, as they all have + the same values + + @note Though called "container", this class does not contain, but only + know the single groups. The inheriting classes are responsible for storage. +*/ +class LinkedModelGroups +{ +public: + virtual ~LinkedModelGroups(); + + void linkAllModels(); + + /* + Load/Save + */ + void saveSettings(class QDomDocument& doc, class QDomElement& that); + void loadSettings(const class QDomElement& that); + + /* + General + */ + //! Derived classes must return the group with index @p idx, + //! or nullptr if @p is out of range + virtual LinkedModelGroup* getGroup(std::size_t idx) = 0; + //! @see getGroup + virtual const LinkedModelGroup* getGroup(std::size_t idx) const = 0; +}; + + +#endif // LINKEDMODELGROUPS_H diff --git a/include/stdshims.h b/include/stdshims.h index 85c4f457a..5eee6543c 100644 --- a/include/stdshims.h +++ b/include/stdshims.h @@ -21,6 +21,13 @@ std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } + +//! Overload for the case a deleter should be specified +template +std::unique_ptr make_unique(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} #endif #endif // include guard diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a50b32a0f..f1c183e3f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -31,6 +31,7 @@ set(LMMS_SRCS core/LadspaControl.cpp core/LadspaManager.cpp core/LfoController.cpp + core/LinkedModelGroups.cpp core/LocklessAllocator.cpp core/MemoryHelper.cpp core/MemoryManager.cpp diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp new file mode 100644 index 000000000..c9bbc475a --- /dev/null +++ b/src/core/LinkedModelGroups.cpp @@ -0,0 +1,185 @@ +/* + * LinkedModelGroups.cpp - base classes for groups of linked models + * + * Copyright (c) 2019-2019 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 "LinkedModelGroups.h" + +#include +#include + +#include "AutomatableModel.h" +#include "stdshims.h" + + + + +/* + LinkedModelGroup +*/ + + +void LinkedModelGroup::linkControls(LinkedModelGroup *other) +{ + foreach_model([&other](const std::string& id, ModelInfo& inf) + { + auto itr2 = other->m_models.find(id); + Q_ASSERT(itr2 != other->m_models.end()); + AutomatableModel::linkModels(inf.m_model, itr2->second.m_model); + }); +} + + + + +void LinkedModelGroup::saveValues(QDomDocument &doc, QDomElement &that) +{ + foreach_model([&doc, &that](const std::string& , ModelInfo& inf) + { + inf.m_model->saveSettings(doc, that, /*m_models[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ + }); +} + + + + +void LinkedModelGroup::loadValues(const QDomElement &that) +{ + foreach_model([&that](const std::string& , ModelInfo& inf) + { + // try to load, if it fails, this will load a sane initial value + inf.m_model->loadSettings(that, /*m_models()[idx].m_name*/ inf.m_name); /* TODO: m_name useful */ + }); +} + + + + +void LinkedModelGroup::addModel(AutomatableModel *model, const QString &name) +{ + model->setObjectName(name); + m_models.emplace(std::string(name.toUtf8().data()), ModelInfo(name, model)); + connect(model, &AutomatableModel::destroyed, + this, [this, model](jo_id_t){ + if(containsModel(model->objectName())) + { + emit modelRemoved(model); + eraseModel(model->objectName()); + } + }, + Qt::DirectConnection); + + // View needs to create another child view, e.g. a new knob: + emit modelAdded(model); + emit dataChanged(); +} + + + + +void LinkedModelGroup::removeControl(AutomatableModel* mdl) +{ + if(containsModel(mdl->objectName())) + { + emit modelRemoved(mdl); + eraseModel(mdl->objectName()); + } +} + + + + +bool LinkedModelGroup::eraseModel(const QString& name) +{ + return m_models.erase(name.toStdString()) > 0; +} + + + + +void LinkedModelGroup::clearModels() +{ + m_models.clear(); +} + + + + +bool LinkedModelGroup::containsModel(const QString &name) const +{ + return m_models.find(name.toStdString()) != m_models.end(); +} + + + + +/* + LinkedModelGroups +*/ + + + +LinkedModelGroups::~LinkedModelGroups() {} + + + + +void LinkedModelGroups::linkAllModels() +{ + LinkedModelGroup* first = getGroup(0); + LinkedModelGroup* cur; + + for (std::size_t i = 1; (cur = getGroup(i)); ++i) + { + first->linkControls(cur); + } +} + + + + +void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) +{ + LinkedModelGroup* grp0 = getGroup(0); + if (grp0) + { + QDomElement models = doc.createElement("models"); + that.appendChild(models); + grp0->saveValues(doc, models); + } + else { /* don't even add a "models" node */ } +} + + + + +void LinkedModelGroups::loadSettings(const QDomElement& that) +{ + QDomElement models = that.firstChildElement("models"); + LinkedModelGroup* grp0; + if (!models.isNull() && (grp0 = getGroup(0))) + { + // only load the first group, the others are linked to the first + grp0->loadValues(models); + } +} + diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index af316fddb..dbdcc5d84 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -52,6 +52,7 @@ SET(LMMS_SRCS gui/widgets/ComboBox.cpp gui/widgets/ControllerRackView.cpp gui/widgets/ControllerView.cpp + gui/widgets/Controls.cpp gui/widgets/CPULoadWidget.cpp gui/widgets/EffectRackView.cpp gui/widgets/EffectView.cpp @@ -71,6 +72,8 @@ SET(LMMS_SRCS gui/widgets/LcdSpinBox.cpp gui/widgets/LcdWidget.cpp gui/widgets/LedCheckbox.cpp + gui/widgets/ControlLayout.cpp + gui/widgets/LinkedModelGroupViews.cpp gui/widgets/MeterDialog.cpp gui/widgets/MidiPortMenu.cpp gui/widgets/NStateButton.cpp diff --git a/src/gui/widgets/ControlLayout.cpp b/src/gui/widgets/ControlLayout.cpp new file mode 100644 index 000000000..afd4e68e4 --- /dev/null +++ b/src/gui/widgets/ControlLayout.cpp @@ -0,0 +1,308 @@ +/* + * ControlLayout.cpp - implementation for ControlLayout.h + * + * Copyright (c) 2019-2019 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. + * + */ + +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "ControlLayout.h" + +#include +#include +#include +#include +#include + +constexpr const int ControlLayout::m_minWidth; + +ControlLayout::ControlLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing), + m_searchBar(new QLineEdit(parent)) +{ + setContentsMargins(margin, margin, margin, margin); + m_searchBar->setPlaceholderText("filter"); + m_searchBar->setObjectName(s_searchBarName); + connect(m_searchBar, SIGNAL(textChanged(const QString&)), + this, SLOT(onTextChanged(const QString& ))); + addWidget(m_searchBar); + m_searchBar->setHidden(true); // nothing to filter yet +} + +ControlLayout::~ControlLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) { delete item; } +} + +void ControlLayout::onTextChanged(const QString&) +{ + invalidate(); + update(); +} + +void ControlLayout::addItem(QLayoutItem *item) +{ + QWidget* widget = item->widget(); + const QString str = widget ? widget->objectName() : QString("unnamed"); + m_itemMap.insert(str, item); + invalidate(); +} + +int ControlLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { return m_hSpace; } + else + { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int ControlLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { return m_vSpace; } + else + { + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } +} + +int ControlLayout::count() const +{ + return m_itemMap.size() - 1; +} + +QMap::const_iterator +ControlLayout::pairAt(int index) const +{ + if (index < 0) { return m_itemMap.cend(); } + + auto skip = [&](QLayoutItem* item) -> bool + { + return item->widget()->objectName() == s_searchBarName; + }; + + QMap::const_iterator itr = m_itemMap.cbegin(); + for (; itr != m_itemMap.cend() && (index > 0 || skip(itr.value())); ++itr) + { + if(!skip(itr.value())) { index--; } + } + return itr; +} + +// linear time :-( +QLayoutItem *ControlLayout::itemAt(int index) const +{ + auto itr = pairAt(index); + return (itr == m_itemMap.end()) ? nullptr : itr.value(); +} + +QLayoutItem *ControlLayout::itemByString(const QString &key) const +{ + auto itr = m_itemMap.find(key); + return (itr == m_itemMap.end()) ? nullptr : *itr; +} + +// linear time :-( +QLayoutItem *ControlLayout::takeAt(int index) +{ + auto itr = pairAt(index); + return (itr == m_itemMap.end()) ? nullptr : m_itemMap.take(itr.key()); +} + +Qt::Orientations ControlLayout::expandingDirections() const +{ + return Qt::Orientations(); +} + +bool ControlLayout::hasHeightForWidth() const +{ + return true; +} + +int ControlLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} + +void ControlLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize ControlLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize ControlLayout::minimumSize() const +{ + // original formula from Qt's FlowLayout example: + // get maximum height and width for all children. + // as Qt will later call heightForWidth, only the width here really matters + QSize size; + for (const QLayoutItem *item : qAsConst(m_itemMap)) + { + size = size.expandedTo(item->minimumSize()); + } + const QMargins margins = contentsMargins(); + size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); + + // the original formula would leed to ~1 widget per row + // bash it at least to 400 so we have ~4 knobs per row + size.setWidth(qMax(size.width(), m_minWidth)); + return size; +} + +int ControlLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + + const QString filterText = m_searchBar->text(); + bool first = true; + + QMapIterator itr(m_itemMap); + while (itr.hasNext()) + { + itr.next(); + QLayoutItem* item = itr.value(); + QWidget *wid = item->widget(); + if (wid) + { + if ( first || // do not filter search bar + filterText.isEmpty() || // no filter - pass all + itr.key().contains(filterText, Qt::CaseInsensitive)) + { + if (first) + { + // for the search bar, only show it if there are at least + // two control widgets (i.e. at least 3 widgets) + if (m_itemMap.size() > 2) { wid->show(); } + else { wid->hide(); } + } + else { wid->show(); } + + int spaceX = horizontalSpacing(); + if (spaceX == -1) + { + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + } + int spaceY = verticalSpacing(); + if (spaceY == -1) + { + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); + } + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) + { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + + if (!testOnly) + { + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + } + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + first = false; + } + else + { + wid->hide(); + } + } + } + return y + lineHeight - rect.y() + bottom; +} + +int ControlLayout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject *parent = this->parent(); + if (!parent) { return -1; } + else if (parent->isWidgetType()) + { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, nullptr, pw); + } + else { return static_cast(parent)->spacing(); } +} + + diff --git a/src/gui/widgets/Controls.cpp b/src/gui/widgets/Controls.cpp new file mode 100644 index 000000000..15b4e0d28 --- /dev/null +++ b/src/gui/widgets/Controls.cpp @@ -0,0 +1,140 @@ +/* + * Controls.cpp - labeled control widgets + * + * Copyright (c) 2019-2019 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 "Controls.h" + +#include +#include +#include + +#include "ComboBox.h" +#include "LcdSpinBox.h" +#include "LedCheckbox.h" +#include "Knob.h" + + + + +Control::~Control() {} + + + + +void KnobControl::setText(const QString &text) { m_knob->setLabel(text); } + +QWidget *KnobControl::topWidget() { return m_knob; } + +void KnobControl::setModel(AutomatableModel *model) +{ + m_knob->setModel(model->dynamicCast(true)); +} + +FloatModel *KnobControl::model() { return m_knob->model(); } + +AutomatableModelView* KnobControl::modelView() { return m_knob; } + +KnobControl::KnobControl(QWidget *parent) : + m_knob(new Knob(parent)) {} + +KnobControl::~KnobControl() {} + + + + +void ComboControl::setText(const QString &text) { m_label->setText(text); } + +void ComboControl::setModel(AutomatableModel *model) +{ + m_combo->setModel(model->dynamicCast(true)); +} + +ComboBoxModel *ComboControl::model() { return m_combo->model(); } + +AutomatableModelView* ComboControl::modelView() { return m_combo; } + +ComboControl::ComboControl(QWidget *parent) : + m_widget(new QWidget(parent)), + m_combo(new ComboBox(nullptr)), + m_label(new QLabel(m_widget)) +{ + m_combo->setFixedSize(64, 22); + QVBoxLayout* vbox = new QVBoxLayout(m_widget); + vbox->addWidget(m_combo); + vbox->addWidget(m_label); + m_combo->repaint(); +} + +ComboControl::~ComboControl() {} + + + + +void CheckControl::setText(const QString &text) { m_label->setText(text); } + +QWidget *CheckControl::topWidget() { return m_widget; } + +void CheckControl::setModel(AutomatableModel *model) +{ + m_checkBox->setModel(model->dynamicCast(true)); +} + +BoolModel *CheckControl::model() { return m_checkBox->model(); } + +AutomatableModelView* CheckControl::modelView() { return m_checkBox; } + +CheckControl::CheckControl(QWidget *parent) : + m_widget(new QWidget(parent)), + m_checkBox(new LedCheckBox(nullptr, QString(), LedCheckBox::Green)), + m_label(new QLabel(m_widget)) +{ + QVBoxLayout* vbox = new QVBoxLayout(m_widget); + vbox->addWidget(m_checkBox); + vbox->addWidget(m_label); +} + +CheckControl::~CheckControl() {} + + + + +void LcdControl::setText(const QString &text) { m_lcd->setLabel(text); } + +QWidget *LcdControl::topWidget() { return m_lcd; } + +void LcdControl::setModel(AutomatableModel *model) +{ + m_lcd->setModel(model->dynamicCast(true)); +} + +IntModel *LcdControl::model() { return m_lcd->model(); } + +AutomatableModelView* LcdControl::modelView() { return m_lcd; } + +LcdControl::LcdControl(int numDigits, QWidget *parent) : + m_lcd(new LcdSpinBox(numDigits, parent)) +{ +} + +LcdControl::~LcdControl() {} + diff --git a/src/gui/widgets/LinkedModelGroupViews.cpp b/src/gui/widgets/LinkedModelGroupViews.cpp new file mode 100644 index 000000000..21d40efcc --- /dev/null +++ b/src/gui/widgets/LinkedModelGroupViews.cpp @@ -0,0 +1,160 @@ +/* + * LinkedModelGroupViews.h - view for groups of linkable models + * + * Copyright (c) 2019-2019 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 "LinkedModelGroupViews.h" + +#include +#include "Controls.h" +#include "ControlLayout.h" +#include "LinkedModelGroups.h" + + +/* + LinkedModelGroupViewBase +*/ + + +LinkedModelGroupView::LinkedModelGroupView(QWidget* parent, + LinkedModelGroup *model, std::size_t colNum) : + QWidget(parent), + m_model(model), + m_colNum(colNum), + m_layout(new ControlLayout(this)) +{ +} + + + + +LinkedModelGroupView::~LinkedModelGroupView() {} + + + + +void LinkedModelGroupView::modelChanged(LinkedModelGroup *group) +{ + // reconnect models + group->foreach_model([this](const std::string& str, + const LinkedModelGroup::ModelInfo& minf) + { + auto itr = m_widgets.find(str); + // in case there are new or deleted widgets, the subclass has already + // modified m_widgets, so this will go into the else case + if (itr == m_widgets.end()) + { + // no widget? this can happen when the whole view is being destroyed + // (for some strange reasons) + } + else + { + itr->second->setModel(minf.m_model); + } + }); + + m_model = group; +} + + + + +void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id, + const std::string &display, bool removable) +{ + int wdgNum = static_cast(m_widgets.size()); + if (ctrl) + { + QWidget* box = new QWidget(this); + QHBoxLayout* boxLayout = new QHBoxLayout(box); + boxLayout->addWidget(ctrl->topWidget()); + + if (removable) + { + QPushButton* removeBtn = new QPushButton; + removeBtn->setIcon( embed::getIconPixmap( "discard" ) ); + QObject::connect(removeBtn, &QPushButton::clicked, + this, [this,ctrl](bool){ + AutomatableModel* controlModel = ctrl->model(); + // remove control out of model group + // (will also remove it from the UI) + m_model->removeControl(controlModel); + // delete model (includes disconnecting all connections) + delete controlModel; + }, + Qt::DirectConnection); + boxLayout->addWidget(removeBtn); + } + + // required, so the Layout knows how to sort/filter widgets by string + box->setObjectName(QString::fromStdString(display)); + m_layout->addWidget(box); + + // take ownership of control and add it + m_widgets.emplace(id, std::unique_ptr(ctrl)); + ++wdgNum; + } + + if (isHidden()) { setHidden(false); } +} + + + + +void LinkedModelGroupView::removeControl(const QString& key) +{ + auto itr = m_widgets.find(key.toStdString()); + if (itr != m_widgets.end()) + { + QLayoutItem* item = m_layout->itemByString(key); + Q_ASSERT(!!item); + QWidget* wdg = item->widget(); + Q_ASSERT(!!wdg); + + // remove item from layout + m_layout->removeItem(item); + // the widget still exists and is visible - remove it now + delete wdg; + // erase widget pointer from dictionary + m_widgets.erase(itr); + // repaint immediately, so we don't have dangling model views + m_layout->update(); + } +} + + +/* + LinkedModelGroupsViewBase +*/ + + +void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups) +{ + LinkedModelGroupView* groupView = getGroupView(); + LinkedModelGroup* group0 = groups->getGroup(0); + if (group0 && groupView) + { + groupView->modelChanged(group0); + } +} + + From bbb3624399e2683864e34c51f7d5f00968f25685 Mon Sep 17 00:00:00 2001 From: Spekular Date: Sun, 23 Feb 2020 14:01:40 +0100 Subject: [PATCH 122/160] Bias dropped clip position backwards --- src/core/Track.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 5c61dc5ab..cb430da96 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1620,9 +1620,13 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) // TODO -- Need to draw the hovericon either way, or ghost the TCOs // onto their final position. + float snapSize = gui->songEditor()->m_editor->getSnapSize(); // All patterns should be offset the same amount as the grabbed pattern + MidiTime offset = MidiTime(tcoPos - grabbedTCOPos); + // Users expect clips to "fall" backwards, so bias the offset + offset = offset - MidiTime::ticksPerBar() * snapSize / 2; // The offset is quantized (rather than the positions) to preserve fine adjustments - int offset = MidiTime(tcoPos - grabbedTCOPos).quantize(gui->songEditor()->m_editor->getSnapSize()); + offset = offset.quantize(snapSize); for( int i = 0; i Date: Sat, 7 Dec 2019 22:31:27 +0100 Subject: [PATCH 123/160] Fix doxygen comment --- include/AutomatableModel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index cdfdffccf..73723abb4 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -325,7 +325,7 @@ private: DataType m_dataType; - ScaleType m_scaleType; //! scale type, linear by default + ScaleType m_scaleType; //!< scale type, linear by default float m_value; float m_initValue; float m_minValue; From a0f4e50805caa96db254f3aa89cc490248ef6a8a Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 7 Dec 2019 22:33:37 +0100 Subject: [PATCH 124/160] Fix knobs not updating vals on link (#4904) --- include/AutomatableModel.h | 1 + src/core/AutomatableModel.cpp | 15 ++++++ tests/CMakeLists.txt | 1 + tests/src/core/AutomatableModelTest.cpp | 67 +++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 tests/src/core/AutomatableModelTest.cpp diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 73723abb4..46a225189 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -220,6 +220,7 @@ public: m_centerValue = centerVal; } + //! link @p m1 and @p m2, let @p m1 take the values of @p m2 static void linkModels( AutomatableModel* m1, AutomatableModel* m2 ); static void unlinkModels( AutomatableModel* m1, AutomatableModel* m2 ); diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 5a97c3280..14bb661e9 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -443,8 +443,23 @@ void AutomatableModel::unlinkModel( AutomatableModel* model ) void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* model2 ) { + if (!model1->m_linkedModels.contains( model2 ) && model1 != model2) + { + // copy data + model1->m_value = model2->m_value; + if (model1->valueBuffer() && model2->valueBuffer()) + { + std::copy_n(model2->valueBuffer()->data(), + model1->valueBuffer()->length(), + model1->valueBuffer()->data()); + } + // send dataChanged() before linking (because linking will + // connect the two dataChanged() signals) + emit model1->dataChanged(); + // finally: link the models model1->linkModel( model2 ); model2->linkModel( model1 ); + } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8cbe23858..ce0e00f4c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,6 +18,7 @@ ADD_EXECUTABLE(tests QTestSuite $ + src/core/AutomatableModelTest.cpp src/core/ProjectVersionTest.cpp src/core/RelativePathsTest.cpp diff --git a/tests/src/core/AutomatableModelTest.cpp b/tests/src/core/AutomatableModelTest.cpp new file mode 100644 index 000000000..5b1363f6c --- /dev/null +++ b/tests/src/core/AutomatableModelTest.cpp @@ -0,0 +1,67 @@ +/* + * AutomatableModelTest.cpp + * + * Copyright (c) 2020 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 "AutomatableModel.h" + +class AutomatableModelTest : QTestSuite +{ + Q_OBJECT + + bool m1Changed, m2Changed; + void resetChanged() { m1Changed = m2Changed = false; } + +private slots: // helper slots + void onM1Changed(Model* ) { m1Changed = true; } + void onM2Changed(Model* ) { m2Changed = true; } + +private slots: // tests + void LinkTests() + { + BoolModel m1(false), m2(false); + + QObject::connect(&m1, SIGNAL(dataChanged(Model*)), + this, SLOT(onM1Changed(Model*))); + QObject::connect(&m2, SIGNAL(dataChanged(Model*)), + this, SLOT(onM2Changed(Model*))); + + resetChanged(); + AutomatableModel::linkModels(&m1, &m1); + QVERIFY(!m1Changed); // cannot link to itself + QVERIFY(!m2Changed); + + resetChanged(); + AutomatableModel::linkModels(&m1, &m2); + QVERIFY(m1Changed); // since m1 takes the value of m2 + QVERIFY(!m2Changed); // the second model is the source + + resetChanged(); + AutomatableModel::linkModels(&m1, &m2); + QVERIFY(!m1Changed); // it's already linked + QVERIFY(!m2Changed); + } +} AutomatableModelTests; + +#include "AutomatableModelTest.moc" From 97a6379c6db356bbab4f67af57ae69c5e89f8d1f Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 7 Dec 2019 22:35:42 +0100 Subject: [PATCH 125/160] Update UI after linking models (#4904) --- src/core/AutomatableModel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 14bb661e9..636a05ca9 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -460,6 +460,7 @@ void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* m model1->linkModel( model2 ); model2->linkModel( model1 ); } + emit model1->dataChanged(); } From 47786865ef4ae3874ba6c26d91bdbbaedc64c6c3 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 2 Jan 2020 19:49:02 +0100 Subject: [PATCH 126/160] Document strange bug --- tests/src/core/AutomatableModelTest.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/src/core/AutomatableModelTest.cpp b/tests/src/core/AutomatableModelTest.cpp index 5b1363f6c..553656f8d 100644 --- a/tests/src/core/AutomatableModelTest.cpp +++ b/tests/src/core/AutomatableModelTest.cpp @@ -61,6 +61,20 @@ private slots: // tests AutomatableModel::linkModels(&m1, &m2); QVERIFY(!m1Changed); // it's already linked QVERIFY(!m2Changed); + + resetChanged(); + BoolModel m3(false); + m1.setValue(1.f); + m2.setValue(1.f); + AutomatableModel::linkModels(&m1, &m2); + QVERIFY(m1.value()); + QVERIFY(m2.value()); + QVERIFY(!m3.value()); + AutomatableModel::linkModels(&m2, &m3); // drag m3, drop on m2 + // m2 should take m3's (0) value + // due to a bug(?), this does not happen + QVERIFY(m2.value()); + QVERIFY(!m3.value()); } } AutomatableModelTests; From b2c4f2939d986c6c6d0342e5be1d0b9f31816d76 Mon Sep 17 00:00:00 2001 From: Spekular Date: Sun, 23 Feb 2020 14:33:33 +0100 Subject: [PATCH 127/160] Elaborate on desired version information Closes #5368 --- .github/ISSUE_TEMPLATE/bug_report.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2c22146ae..fcc875601 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -19,7 +19,13 @@ assignees: '' #### Screenshot -#### LMMS version used +#### Affected LMMS versions + + #### Logs

From 94431ea9decdd8d6f9a1c377c1e7ee621cc19bb7 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sun, 1 Mar 2020 12:03:49 +0900 Subject: [PATCH 128/160] RemoteVstPlugin: fix issues with FXP/FXB files on Windows (#5411) --- plugins/vst_base/RemoteVstPlugin.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 15a56e869..be92126e3 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -1286,7 +1286,7 @@ void RemoteVstPlugin::savePreset( const std::string & _file ) if (!isPreset &&!chunky) uIntToFile = (unsigned int) m_plugin->numPrograms; pBank->numPrograms = endian_swap( uIntToFile ); - FILE * stream = F_OPEN_UTF8( _file, "w" ); + FILE * stream = F_OPEN_UTF8( _file, "wb" ); if (!stream) { fprintf( stderr, @@ -1344,7 +1344,7 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) unsigned int * pLen = new unsigned int[ 1 ]; unsigned int len = 0; sBank * pBank = (sBank*) new char[ sizeof( sBank ) ]; - FILE * stream = F_OPEN_UTF8( _file, "r" ); + FILE * stream = F_OPEN_UTF8( _file, "rb" ); if (!stream) { fprintf( stderr, From 4dc26d1aabcf203cd2137e5ad70ed09d6e13a452 Mon Sep 17 00:00:00 2001 From: Muhammed Furkan USLU Date: Sun, 8 Mar 2020 09:31:36 +0300 Subject: [PATCH 129/160] Fix issue #5409. (#5413) Fix multiple uninitialized reads. --- src/core/Song.cpp | 2 ++ src/core/StepRecorder.cpp | 3 ++- src/gui/editors/PianoRoll.cpp | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index fc7b51a03..60ab3bfbc 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -69,6 +69,7 @@ Song::Song() : m_oldTicksPerBar( DefaultTicksPerBar ), m_masterVolumeModel( 100, 0, 200, this, tr( "Master volume" ) ), m_masterPitchModel( 0, -12, 12, this, tr( "Master pitch" ) ), + m_nLoadingTrack( 0 ), m_fileName(), m_oldFileName(), m_modified( false ), @@ -79,6 +80,7 @@ Song::Song() : m_renderBetweenMarkers( false ), m_playing( false ), m_paused( false ), + m_savingProject( false ), m_loadingProject( false ), m_isCancelled( false ), m_playMode( Mode_None ), diff --git a/src/core/StepRecorder.cpp b/src/core/StepRecorder.cpp index 7a63e88e2..e107c5818 100644 --- a/src/core/StepRecorder.cpp +++ b/src/core/StepRecorder.cpp @@ -32,7 +32,8 @@ const int REMOVE_RELEASED_NOTE_TIME_THRESHOLD_MS = 70; StepRecorder::StepRecorder(PianoRoll& pianoRoll, StepRecorderWidget& stepRecorderWidget): m_pianoRoll(pianoRoll), - m_stepRecorderWidget(stepRecorderWidget) + m_stepRecorderWidget(stepRecorderWidget), + m_pattern(nullptr) { m_stepRecorderWidget.hide(); } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 3b42bd9b2..5918c9333 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -152,6 +152,8 @@ PianoRoll::PianoRoll() : m_zoomingModel(), m_quantizeModel(), m_noteLenModel(), + m_scaleModel(), + m_chordModel(), m_pattern( NULL ), m_currentPosition(), m_recording( false ), From ab8be73047ecb15026ecd074471b6ca4137bd7cd Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 8 Mar 2020 08:24:46 +0100 Subject: [PATCH 130/160] Cherry-pick from master commit 4dc26d1 (#5413) --- src/core/Song.cpp | 2 ++ src/gui/editors/PianoRoll.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 54f19e1d3..3285497a8 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -75,6 +75,7 @@ Song::Song() : m_oldTicksPerTact( DefaultTicksPerTact ), m_masterVolumeModel( 100, 0, 200, this, tr( "Master volume" ) ), m_masterPitchModel( 0, -12, 12, this, tr( "Master pitch" ) ), + m_nLoadingTrack( 0 ), m_fileName(), m_oldFileName(), m_modified( false ), @@ -85,6 +86,7 @@ Song::Song() : m_renderBetweenMarkers( false ), m_playing( false ), m_paused( false ), + m_savingProject( false ), m_loadingProject( false ), m_isCancelled( false ), m_playMode( Mode_None ), diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index f6bda682b..924463995 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -154,6 +154,8 @@ PianoRoll::PianoRoll() : m_zoomingModel(), m_quantizeModel(), m_noteLenModel(), + m_scaleModel(), + m_chordModel(), m_pattern( NULL ), m_currentPosition(), m_recording( false ), From d382d4e08b474a072285ce25478d1edf3150e1f3 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 8 Mar 2020 08:47:27 +0100 Subject: [PATCH 131/160] Fix previous commit --- src/core/Song.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 3285497a8..cffe23450 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -86,7 +86,6 @@ Song::Song() : m_renderBetweenMarkers( false ), m_playing( false ), m_paused( false ), - m_savingProject( false ), m_loadingProject( false ), m_isCancelled( false ), m_playMode( Mode_None ), From 0196f31509f52b1ccbf2eb8edd6f0a117f752bf6 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Wed, 11 Mar 2020 11:55:33 -0400 Subject: [PATCH 132/160] Fix SDL2 detection Per #5389 (Comment) Recommended by @PhysSong Adopted from upstream SDL1 patch https://github.com/Kitware/CMake/commit/c5c217c6b53b035303747a2971b1b80be5391566 --- cmake/modules/FindSDL2.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindSDL2.cmake b/cmake/modules/FindSDL2.cmake index 89aea7a8a..7b9e5b149 100644 --- a/cmake/modules/FindSDL2.cmake +++ b/cmake/modules/FindSDL2.cmake @@ -82,7 +82,7 @@ SET(SDL2_SEARCH_PATHS FIND_PATH(SDL2_INCLUDE_DIR SDL.h HINTS $ENV{SDL2DIR} - PATH_SUFFIXES include/SDL2 include + PATH_SUFFIXES SDL2 include/SDL2 include PATHS ${SDL2_SEARCH_PATHS} ) From 85e05741382c688de151da34302517eaa48e7907 Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Wed, 11 Mar 2020 15:07:02 -0400 Subject: [PATCH 133/160] Refactor FFT helpers (#5309) * Do not check if unsigned int is negative * Reduce scope of some local variables * Use right types for iterators * Check conditional returns first * Remove unused functions * Utilize a range-based for loop opportunity --- include/fft_helpers.h | 12 ---- src/core/fft_helpers.cpp | 127 ++++++--------------------------------- 2 files changed, 20 insertions(+), 119 deletions(-) diff --git a/include/fft_helpers.h b/include/fft_helpers.h index 876510f8b..88183fb19 100644 --- a/include/fft_helpers.h +++ b/include/fft_helpers.h @@ -103,16 +103,4 @@ int LMMS_EXPORT absspec(const fftwf_complex *complex_buffer, float *absspec_buff int LMMS_EXPORT compressbands(const float * _absspec_buffer, float * _compressedband, int _num_old, int _num_new, int _bottom, int _top); - -int LMMS_EXPORT calc13octaveband31(float * _absspec_buffer, float * _subbands, - int _num_spec, float _max_frequency); - - -/** Compute power of finite time sequence. - * Take care num_values is length of timesignal[]. - * - * @return power on success, else -1 - */ -float LMMS_EXPORT signalpower(const float *timesignal, int num_values); - #endif diff --git a/src/core/fft_helpers.cpp b/src/core/fft_helpers.cpp index 2cf54b7f2..0dcf77e3a 100644 --- a/src/core/fft_helpers.cpp +++ b/src/core/fft_helpers.cpp @@ -35,13 +35,12 @@ */ float maximum(const float *abs_spectrum, unsigned int spec_size) { - float maxi = 0; - unsigned int i; - if (abs_spectrum == NULL) {return -1;} - if (spec_size <= 0) {return -1;} + if (spec_size == 0) {return -1;} - for (i = 0; i < spec_size; i++) + float maxi = 0; + + for (unsigned int i = 0; i < spec_size; i++) { if (abs_spectrum[i] > maxi) {maxi = abs_spectrum[i];} } @@ -61,13 +60,11 @@ float maximum(const std::vector &abs_spectrum) */ int normalize(const float *abs_spectrum, float *norm_spectrum, unsigned int bin_count, unsigned int block_size) { - int i; - if (abs_spectrum == NULL || norm_spectrum == NULL) {return -1;} if (bin_count == 0 || block_size == 0) {return -1;} block_size /= 2; - for (i = 0; i < bin_count; i++) + for (unsigned int i = 0; i < bin_count; i++) { norm_spectrum[i] = abs_spectrum[i] / block_size; } @@ -89,9 +86,9 @@ int normalize(const std::vector &abs_spectrum, std::vector &norm_s */ int notEmpty(const std::vector &spectrum) { - for (int i = 0; i < spectrum.size(); i++) + for (float s : spectrum) { - if (spectrum[i] != 0) {return 1;} + if (s != 0) {return 1;} } return 0; } @@ -103,22 +100,21 @@ int notEmpty(const std::vector &spectrum) */ int precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool normalized) { - unsigned int i; + if (window == NULL) {return -1;} + float gain = 0; float a0; float a1; float a2; float a3; - if (window == NULL) {return -1;} - // constants taken from // https://en.wikipedia.org/wiki/Window_function#AList_of_window_functions switch (type) { default: case RECTANGULAR: - for (i = 0; i < length; i++) {window[i] = 1.0;} + for (unsigned int i = 0; i < length; i++) {window[i] = 1.0;} gain = 1; return 0; case BLACKMAN_HARRIS: @@ -142,7 +138,7 @@ int precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool } // common computation for cosine-sum based windows - for (i = 0; i < length; i++) + for (unsigned int i = 0; i < length; i++) { window[i] = (a0 - a1 * cos(2 * F_PI * i / ((float)length - 1.0)) + a2 * cos(4 * F_PI * i / ((float)length - 1.0)) @@ -152,7 +148,7 @@ int precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool // apply amplitude correction gain /= (float) length; - for (i = 0; i < length; i++) {window[i] /= gain;} + for (unsigned int i = 0; i < length; i++) {window[i] /= gain;} return 0; } @@ -166,12 +162,10 @@ int precomputeWindow(float *window, unsigned int length, FFT_WINDOWS type, bool */ int absspec(const fftwf_complex *complex_buffer, float *absspec_buffer, unsigned int compl_length) { - int i; - if (complex_buffer == NULL || absspec_buffer == NULL) {return -1;} - if (compl_length <= 0) {return -1;} + if (compl_length == 0) {return -1;} - for (i = 0; i < compl_length; i++) + for (unsigned int i = 0; i < compl_length; i++) { absspec_buffer[i] = (float)sqrt(complex_buffer[i][0] * complex_buffer[i][0] + complex_buffer[i][1] * complex_buffer[i][1]); @@ -189,33 +183,28 @@ int absspec(const fftwf_complex *complex_buffer, float *absspec_buffer, unsigned */ int compressbands(const float *absspec_buffer, float *compressedband, int num_old, int num_new, int bottom, int top) { - float ratio; - int i, usefromold; - float j; - float j_min, j_max; - if (absspec_buffer == NULL || compressedband == NULL) {return -1;} if (num_old < num_new) {return -1;} if (num_old <= 0 || num_new <= 0) {return -1;} if (bottom < 0) {bottom = 0;} if (top >= num_old) {top = num_old - 1;} - usefromold = num_old - (num_old - top) - bottom; + int usefromold = num_old - (num_old - top) - bottom; - ratio = (float)usefromold / (float)num_new; + float ratio = (float)usefromold / (float)num_new; // for each new subband - for (i = 0; i < num_new; i++) + for (int i = 0; i < num_new; i++) { compressedband[i] = 0; - j_min = (i * ratio) + bottom; + float j_min = (i * ratio) + bottom; if (j_min < 0) {j_min = bottom;} - j_max = j_min + ratio; + float j_max = j_min + ratio; - for (j = (int)j_min; j <= j_max; j++) + for (float j = (int)j_min; j <= j_max; j++) { compressedband[i] += absspec_buffer[(int)j]; } @@ -223,79 +212,3 @@ int compressbands(const float *absspec_buffer, float *compressedband, int num_ol return 0; } - - -int calc13octaveband31(float *absspec_buffer, float *subbands, int num_spec, float max_frequency) -{ - static const int onethirdoctavecenterfr[] = {20, 25, 31, 40, 50, 63, 80, 100, 125, 160, 200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8000, 10000, 12500, 16000, 20000}; - int i, j; - float f_min, f_max, frequency, bandwidth; - int j_min, j_max = 0; - float fpower; - - if (absspec_buffer == NULL || subbands == NULL) {return -1;} - if (num_spec < 31) {return -1;} - if (max_frequency <= 0) {return -1;} - - /*** energy ***/ - fpower = 0; - for (i = 0; i < num_spec; i++) - { - absspec_buffer[i] = (absspec_buffer[i] * absspec_buffer[i]) / FFT_BUFFER_SIZE; - fpower = fpower + (2 * absspec_buffer[i]); - } - fpower = fpower - (absspec_buffer[0]); //dc not mirrored - - /*** for each subband: sum up power ***/ - for (i = 0; i < 31; i++) - { - subbands[i] = 0; - - // calculate bandwidth for subband - frequency = onethirdoctavecenterfr[i]; - - bandwidth = (pow(2, 1.0/3.0)-1) * frequency; - - f_min = frequency - bandwidth / 2.0; - f_max = frequency + bandwidth / 2.0; - - j_min = (int)(f_min / max_frequency * (float)num_spec); - j_max = (int)(f_max / max_frequency * (float)num_spec); - - - if (j_min < 0 || j_max < 0) - { - fprintf(stderr, "Error: calc13octaveband31() in fft_helpers.cpp line %d failed.\n", __LINE__); - return -1; - } - - for (j = j_min; j <= j_max; j++) - { - if (j_max < num_spec) {subbands[i] += absspec_buffer[j];} - } - - } //for - - return 0; -} - -/* Compute power of finite time sequence. - * Take care num_values is length of timesignal[] - * - * return power on success, else -1 - */ -float signalpower(const float *timesignal, int num_values) -{ - if (num_values <= 0) {return -1;} - - if (timesignal == NULL) {return -1;} - - float power = 0; - for (int i = 0; i < num_values; i++) - { - power += timesignal[i] * timesignal[i]; - } - - return power; -} - From 2367a62a51ec833e08d6dd342b65b9b1829391d5 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 23 Mar 2020 15:11:13 +0900 Subject: [PATCH 134/160] Fix crashes and hangs on importing some Hydrogen drum kit songs (#5420) --- plugins/HydrogenImport/HydrogenImport.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugins/HydrogenImport/HydrogenImport.cpp b/plugins/HydrogenImport/HydrogenImport.cpp index f86cdffd7..3ca0f2da2 100644 --- a/plugins/HydrogenImport/HydrogenImport.cpp +++ b/plugins/HydrogenImport/HydrogenImport.cpp @@ -200,13 +200,19 @@ bool HydrogenImport::readSong() else { unsigned nLayer = 0; - QDomNode layerNode = instrumentNode.firstChildElement( "layer" ); + QDomNode instrumentComponentNode = instrumentNode.firstChildElement("instrumentComponent"); + if (instrumentComponentNode.isNull()) + { + instrumentComponentNode = instrumentNode; + } + + QDomNode layerNode = instrumentComponentNode.firstChildElement( "layer" ); while ( ! layerNode.isNull() ) { if ( nLayer >= MAX_LAYERS ) { - printf( "nLayer >= MAX_LAYERS" ); - continue; + printf("nLayer >= MAX_LAYERS\n"); + break; } QString sFilename = LocalFileMng::readXmlString( layerNode, "filename", "" ); QString sMode = LocalFileMng::readXmlString( layerNode, "smode", "forward" ); From b51079e921c31f2570a8d07d3100a67ad4584123 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 24 Mar 2020 10:36:50 +0900 Subject: [PATCH 135/160] Use proper synchronization methods on some instrument track operations --- src/tracks/InstrumentTrack.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 3f0c59b7d..7caf8e2fc 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -445,7 +445,7 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH ) } m_midiNotesMutex.unlock(); - lock(); + Engine::mixer()->requestChangeInModel(); // invalidate all NotePlayHandles and PresetPreviewHandles linked to this track m_processHandles.clear(); @@ -455,7 +455,7 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH ) flags |= PlayHandle::TypeInstrumentPlayHandle; } Engine::mixer()->removePlayHandlesOfTypes( this, flags ); - unlock(); + Engine::mixer()->doneChangeInModel(); } @@ -544,11 +544,13 @@ void InstrumentTrack::setName( const QString & _new_name ) void InstrumentTrack::updateBaseNote() { + Engine::mixer()->requestChangeInModel(); for( NotePlayHandleList::Iterator it = m_processHandles.begin(); it != m_processHandles.end(); ++it ) { ( *it )->setFrequencyUpdate(); } + Engine::mixer()->doneChangeInModel(); } From a8df120a58b3fbafe65225893c89f1078eeb1146 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 24 Mar 2020 10:55:46 +0900 Subject: [PATCH 136/160] STK Mallets: don't silence active notes when switching instruments This also fixes the underlying noise on instrument switches. --- plugins/stk/mallets/mallets.cpp | 16 ++++++++++------ plugins/stk/mallets/mallets.h | 11 +++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/plugins/stk/mallets/mallets.cpp b/plugins/stk/mallets/mallets.cpp index 29b6aeb20..22880484f 100644 --- a/plugins/stk/mallets/mallets.cpp +++ b/plugins/stk/mallets/mallets.cpp @@ -338,6 +338,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n, Engine::mixer()->processingSampleRate() ); } m.unlock(); + static_cast(_n->m_pluginData)->setPresetIndex(p); } const fpp_t frames = _n->framesLeftForCurrentPeriod(); @@ -345,6 +346,7 @@ void malletsInstrument::playNote( NotePlayHandle * _n, malletsSynth * ps = static_cast( _n->m_pluginData ); ps->setFrequency( freq ); + p = ps->presetIndex(); sample_t add_scale = 0.0f; if( p == 10 && m_isOldVersionModel.value() == true ) @@ -355,9 +357,9 @@ void malletsInstrument::playNote( NotePlayHandle * _n, for( fpp_t frame = offset; frame < frames + offset; ++frame ) { _working_buffer[frame][0] = ps->nextSampleLeft() * - ( m_scalers[m_presetsModel.value()] + add_scale ); + ( m_scalers[p] + add_scale ); _working_buffer[frame][1] = ps->nextSampleRight() * - ( m_scalers[m_presetsModel.value()] + add_scale ); + ( m_scalers[p] + add_scale ); } instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n ); @@ -579,7 +581,6 @@ void malletsInstrumentView::modelChanged() void malletsInstrumentView::changePreset() { malletsInstrument * inst = castModel(); - inst->instrumentTrack()->silenceAllNotes(); int _preset = inst->m_presetsModel.value(); if( _preset < 9 ) @@ -614,7 +615,8 @@ malletsSynth::malletsSynth( const StkFloat _pitch, const StkFloat _control11, const int _control16, const uint8_t _delay, - const sample_rate_t _sample_rate ) + const sample_rate_t _sample_rate ) : + m_presetIndex(0) { try { @@ -664,7 +666,8 @@ malletsSynth::malletsSynth( const StkFloat _pitch, const StkFloat _control11, const StkFloat _control128, const uint8_t _delay, - const sample_rate_t _sample_rate ) + const sample_rate_t _sample_rate ) : + m_presetIndex(0) { try { @@ -712,7 +715,8 @@ malletsSynth::malletsSynth( const StkFloat _pitch, const StkFloat _control64, const StkFloat _control128, const uint8_t _delay, - const sample_rate_t _sample_rate ) + const sample_rate_t _sample_rate ) : + m_presetIndex(0) { try { diff --git a/plugins/stk/mallets/mallets.h b/plugins/stk/mallets/mallets.h index 3928c531c..c8f5e7a47 100644 --- a/plugins/stk/mallets/mallets.h +++ b/plugins/stk/mallets/mallets.h @@ -120,8 +120,19 @@ public: } } + inline int presetIndex() + { + return m_presetIndex; + } + + inline void setPresetIndex(int presetIndex) + { + m_presetIndex = presetIndex; + } + protected: + int m_presetIndex; Instrmnt * m_voice; StkFloat * m_delay; From 656eede6bac47915c7ca974b40c81162d28a28ae Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Wed, 1 Apr 2020 21:22:05 +0200 Subject: [PATCH 137/160] Fix bug made in #5336 This removes a duplicate dataChanged() emit. Thanks to @PhysSong for the hint. --- src/core/AutomatableModel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 636a05ca9..14bb661e9 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -460,7 +460,6 @@ void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* m model1->linkModel( model2 ); model2->linkModel( model1 ); } - emit model1->dataChanged(); } From 8afa2d541276a3e7bfdb7a836b2567a73dd750d9 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sat, 4 Apr 2020 12:06:35 +0900 Subject: [PATCH 138/160] Fix the linking method for the dummy Carla library --- plugins/carlabase/CMakeLists.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/carlabase/CMakeLists.txt b/plugins/carlabase/CMakeLists.txt index 264780996..5e024da40 100644 --- a/plugins/carlabase/CMakeLists.txt +++ b/plugins/carlabase/CMakeLists.txt @@ -18,15 +18,15 @@ if(LMMS_HAVE_WEAKCARLA) ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/backend ${CMAKE_CURRENT_BINARY_DIR}/autoconf ) - INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS}) - ADD_LIBRARY(carla_native-plugin MODULE DummyCarla.cpp) - INSTALL(TARGETS carla_native-plugin LIBRARY DESTINATION "${PLUGIN_DIR}/optional") - SET(CARLA_LIBRARIES $) - SET(CARLA_LIBRARY_DIRS $) + ADD_LIBRARY(carla_native-plugin SHARED DummyCarla.cpp) + TARGET_INCLUDE_DIRECTORIES(carla_native-plugin PUBLIC ${CARLA_INCLUDE_DIRS}) + INSTALL(TARGETS carla_native-plugin + LIBRARY DESTINATION "${PLUGIN_DIR}/optional" + RUNTIME DESTINATION "${PLUGIN_DIR}/optional" + ) + SET(CARLA_LIBRARIES carla_native-plugin) # Set parent scope variables so carlarack and carlapatchbay can see them SET(CARLA_LIBRARIES ${CARLA_LIBRARIES} PARENT_SCOPE) - SET(CARLA_INCLUDE_DIRS ${CARLA_INCLUDE_DIRS} PARENT_SCOPE) - SET(CARLA_LIBRARY_DIRS ${CARLA_LIBRARY_DIRS} PARENT_SCOPE) endif() if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA) From 9c2ccdf3a826f99d4c4f9c3427e9074c64f15a98 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Wed, 25 Mar 2020 14:54:23 +0900 Subject: [PATCH 139/160] Fix some issues in shell scripts spotted by shellcheck --- .travis/script.sh | 2 +- cmake/linux/package_linux.sh.in | 2 +- cmake/msys/extract_debs.sh | 2 ++ cmake/msys/msys_helper.sh | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis/script.sh b/.travis/script.sh index b723f5dd0..a6d87b37f 100755 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -22,7 +22,7 @@ else "$TRAVIS_BUILD_DIR/.travis/$TRAVIS_OS_NAME.$TARGET_OS.script.sh" # Package and upload non-tagged builds - if [ ! -z "$TRAVIS_TAG" ]; then + if [ -n "$TRAVIS_TAG" ]; then # Skip, handled by travis deploy instead exit 0 elif [[ $TARGET_OS == win* ]]; then diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index dcbb6cb54..adcf9daa2 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -108,7 +108,7 @@ chmod +x "${APPDIR}usr/bin/lmms" unset LD_LIBRARY_PATH # Ensure linuxdeployqt can find shared objects -export LD_LIBRARY_PATH="${APPDIR}usr/lib/lmms/":"${APPDIR}usr/lib/lmms/optional":$LD_LIBRARY_PATH +export LD_LIBRARY_PATH="${APPDIR}"usr/lib/lmms/:"${APPDIR}"usr/lib/lmms/optional:"$LD_LIBRARY_PATH" # Handle wine linking if [ -d "@WINE_32_LIBRARY_DIR@" ]; then diff --git a/cmake/msys/extract_debs.sh b/cmake/msys/extract_debs.sh index cb2a868d3..939912bb2 100644 --- a/cmake/msys/extract_debs.sh +++ b/cmake/msys/extract_debs.sh @@ -1,4 +1,6 @@ #!/bin/bash +set -e + ppa_dir=./ppa/ pushd $ppa_dir diff --git a/cmake/msys/msys_helper.sh b/cmake/msys/msys_helper.sh index 294edd476..a6a7e6aae 100644 --- a/cmake/msys/msys_helper.sh +++ b/cmake/msys/msys_helper.sh @@ -68,7 +68,7 @@ stkver="4.5.1" mingw_root="/$(echo "$MSYSTEM"|tr '[:upper:]' '[:lower:]')" info "Downloading and building fltk $fltkver" -if ! which fluid; then +if ! command -v fluid; then wget http://fltk.org/pub/fltk/$fltkver/fltk-$fltkver-source.tar.gz -O "$HOME/fltk-source.tar.gz" tar zxf "$HOME/fltk-source.tar.gz" -C "$HOME/" pushd "$HOME/fltk-$fltkver" From d173f42fecf04ff43878d2f48e6252289c92ddfb Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 2 Jan 2020 12:32:39 +0900 Subject: [PATCH 140/160] Fix wine detection --- cmake/linux/package_linux.sh.in | 18 ++++++++++-------- cmake/modules/FindWine.cmake | 20 ++++++++++++++------ tests/src/core/AutomatableModelTest.cpp | 12 ++++++------ 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index adcf9daa2..a1f9ff865 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -110,14 +110,6 @@ unset LD_LIBRARY_PATH # Ensure linuxdeployqt can find shared objects export LD_LIBRARY_PATH="${APPDIR}"usr/lib/lmms/:"${APPDIR}"usr/lib/lmms/optional:"$LD_LIBRARY_PATH" -# Handle wine linking -if [ -d "@WINE_32_LIBRARY_DIR@" ]; then - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@WINE_32_LIBRARY_DIRS@ -fi -if [ -d "@WINE_64_LIBRARY_DIR@" ]; then - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@WINE_64_LIBRARY_DIRS@ -fi - # Move executables so linuxdeployqt can find them ZYNLIB="${APPDIR}usr/lib/lmms/RemoteZynAddSubFx" VSTLIB32="${APPDIR}usr/lib/lmms/32/RemoteVstPlugin32.exe.so" @@ -131,6 +123,16 @@ mv "$ZYNLIB" "$ZYNBIN" mv "$VSTLIB32" "$VSTBIN32" || true mv "$VSTLIB64" "$VSTBIN64" || true +# Handle wine linking +if [ -d "@WINE_32_LIBRARY_DIR@" ] && \ + ldd "$VSTBIN32" | grep "libwine\.so" | grep "not found"; then + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"@WINE_32_LIBRARY_DIRS@" +fi +if [ -d "@WINE_64_LIBRARY_DIR@" ] && \ + ldd "$VSTBIN64" | grep "libwine\.so" | grep "not found"; then + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"@WINE_64_LIBRARY_DIRS@" +fi + # Patch the desktop file sed -i 's/.*Exec=.*/Exec=lmms.real/' "$DESKTOPFILE" diff --git a/cmake/modules/FindWine.cmake b/cmake/modules/FindWine.cmake index 50bf54edb..f7f3b0aa6 100644 --- a/cmake/modules/FindWine.cmake +++ b/cmake/modules/FindWine.cmake @@ -11,11 +11,19 @@ MACRO(_findwine_find_flags output expression result) STRING(REPLACE " " ";" WINEBUILD_FLAGS "${output}") FOREACH(FLAG ${WINEBUILD_FLAGS}) IF("${FLAG}" MATCHES "${expression}") - SET(${result} "${FLAG}") + LIST(APPEND ${result} "${FLAG}") ENDIF() ENDFOREACH() ENDMACRO() +MACRO(_regex_replace_foreach EXPRESSION REPLACEMENT RESULT INPUT) + SET(${RESULT} "") + FOREACH(ITEM ${INPUT}) + STRING(REGEX REPLACE "${EXPRESSION}" "${REPLACEMENT}" ITEM "${ITEM}") + LIST(APPEND ${RESULT} "${ITEM}") + ENDFOREACH() +ENDMACRO() + LIST(APPEND CMAKE_PREFIX_PATH /opt/wine-stable /opt/wine-devel /opt/wine-staging /usr/lib/wine/) FIND_PROGRAM(WINE_CXX @@ -31,10 +39,10 @@ IF(WINE_CXX) _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "^-isystem" WINEGCC_INCLUDE_DIR) _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "libwinecrt0\\.a.*" WINECRT_32) _findwine_find_flags("${WINEBUILD_OUTPUT_64}" "libwinecrt0\\.a.*" WINECRT_64) - STRING(REGEX REPLACE "^-isystem" "" WINE_INCLUDE_HINT "${WINEGCC_INCLUDE_DIR}") - STRING(REGEX REPLACE "/wine/windows$" "" WINE_INCLUDE_HINT "${WINE_INCLUDE_HINT}") - STRING(REGEX REPLACE "libwinecrt0\\.a.*" "" WINE_32_LIBRARY_DIR "${WINECRT_32}") - STRING(REGEX REPLACE "libwinecrt0\\.a.*" "" WINE_64_LIBRARY_DIR "${WINECRT_64}") + _regex_replace_foreach("^-isystem" "" WINE_INCLUDE_HINT "${WINEGCC_INCLUDE_DIR}") + _regex_replace_foreach("/wine/windows$" "" WINE_INCLUDE_HINT "${WINE_INCLUDE_HINT}") + STRING(REGEX REPLACE "wine/libwinecrt0\\.a.*" "" WINE_32_LIBRARY_DIR "${WINECRT_32}") + STRING(REGEX REPLACE "wine/libwinecrt0\\.a.*" "" WINE_64_LIBRARY_DIR "${WINECRT_64}") IF(BUGGED_WINEGCC) MESSAGE(WARNING "Your winegcc is unusable due to https://bugs.winehq.org/show_bug.cgi?id=46293,\n @@ -76,7 +84,7 @@ IF(WINE_CXX) ENDIF() FIND_PATH(WINE_INCLUDE_DIR wine/exception.h - HINTS "${WINE_INCLUDE_HINT}" + HINTS ${WINE_INCLUDE_HINT} ) SET(_ARCHITECTURE ${CMAKE_LIBRARY_ARCHITECTURE}) diff --git a/tests/src/core/AutomatableModelTest.cpp b/tests/src/core/AutomatableModelTest.cpp index 6bf9090e1..d84d89db4 100644 --- a/tests/src/core/AutomatableModelTest.cpp +++ b/tests/src/core/AutomatableModelTest.cpp @@ -35,8 +35,8 @@ class AutomatableModelTest : QTestSuite void resetChanged() { m1Changed = m2Changed = false; } private slots: // helper slots - void onM1Changed(Model* ) { m1Changed = true; } - void onM2Changed(Model* ) { m2Changed = true; } + void onM1Changed() { m1Changed = true; } + void onM2Changed() { m2Changed = true; } private slots: // tests //! Test that upcast and exact casts work, @@ -62,10 +62,10 @@ private slots: // tests { BoolModel m1(false), m2(false); - QObject::connect(&m1, SIGNAL(dataChanged(Model*)), - this, SLOT(onM1Changed(Model*))); - QObject::connect(&m2, SIGNAL(dataChanged(Model*)), - this, SLOT(onM2Changed(Model*))); + QObject::connect(&m1, SIGNAL(dataChanged()), + this, SLOT(onM1Changed())); + QObject::connect(&m2, SIGNAL(dataChanged()), + this, SLOT(onM2Changed())); resetChanged(); AutomatableModel::linkModels(&m1, &m1); From 7628b253b0a94e0a22ef44e12de0ee72173cbad1 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Wed, 25 Mar 2020 14:46:15 +0900 Subject: [PATCH 141/160] Fix linking issues with the dummy Carla library(especially with MSVC) --- plugins/carlabase/CMakeLists.txt | 14 +++++++++----- plugins/carlabase/CarlaConfig/config.h | 9 +++++++++ plugins/carlabase/DummyCarla.cpp | 19 ++++++++++--------- plugins/carlabase/carla.h | 5 ++--- plugins/carlapatchbay/carlapatchbay.cpp | 1 + plugins/carlarack/carlarack.cpp | 1 + 6 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 plugins/carlabase/CarlaConfig/config.h diff --git a/plugins/carlabase/CMakeLists.txt b/plugins/carlabase/CMakeLists.txt index 7807aebd2..28a1bc88c 100644 --- a/plugins/carlabase/CMakeLists.txt +++ b/plugins/carlabase/CMakeLists.txt @@ -8,15 +8,11 @@ ENDIF() # If Carla was not provided by the system, make a dummy library instead if(LMMS_HAVE_WEAKCARLA) - # Mimic the autoconf header - FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/autoconf) - FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/autoconf/config.h "") SET(CARLA_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/carla/source ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/includes ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/utils ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/backend - ${CMAKE_CURRENT_BINARY_DIR}/autoconf ) ADD_LIBRARY(carla_native-plugin SHARED DummyCarla.cpp) TARGET_INCLUDE_DIRECTORIES(carla_native-plugin PUBLIC ${CARLA_INCLUDE_DIRS}) @@ -30,11 +26,19 @@ if(LMMS_HAVE_WEAKCARLA) endif() if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA) + # Mimic the missing "config.h" + SET(CARLA_INCLUDE_DIRS ${CARLA_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/CarlaConfig) + SET(CARLA_INCLUDE_DIRS ${CARLA_INCLUDE_DIRS} PARENT_SCOPE) + INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS}) LINK_DIRECTORIES(${CARLA_LIBRARY_DIRS}) LINK_LIBRARIES(${CARLA_LIBRARIES}) - BUILD_PLUGIN(carlabase carla.cpp carla.h MOCFILES carla.h EMBEDDED_RESOURCES artwork-patchbay.png artwork-rack.png LINK SHARED) + BUILD_PLUGIN(carlabase carla.cpp carla.h + MOCFILES carla.h + EMBEDDED_RESOURCES artwork-patchbay.png artwork-rack.png + EXPORT_BASE_NAME carlabase + LINK SHARED) SET_TARGET_PROPERTIES(carlabase PROPERTIES SKIP_BUILD_RPATH TRUE BUILD_WITH_INSTALL_RPATH TRUE diff --git a/plugins/carlabase/CarlaConfig/config.h b/plugins/carlabase/CarlaConfig/config.h new file mode 100644 index 000000000..46b3f3464 --- /dev/null +++ b/plugins/carlabase/CarlaConfig/config.h @@ -0,0 +1,9 @@ +// config.h for Carla +#ifndef FOR_CARLA_CONFIG_H +#define FOR_CARLA_CONFIG_H + +#ifdef _MSC_VER +#define HAVE_CPP11_SUPPORT 1 +#endif + +#endif diff --git a/plugins/carlabase/DummyCarla.cpp b/plugins/carlabase/DummyCarla.cpp index 560e866b7..8fd9ef6d7 100644 --- a/plugins/carlabase/DummyCarla.cpp +++ b/plugins/carlabase/DummyCarla.cpp @@ -1,12 +1,13 @@ // A dummy Carla interface +#define BUILDING_CARLA #include "CarlaNativePlugin.h" -const char* carla_get_library_filename() { return nullptr; } -const char* carla_get_library_folder() { return nullptr; } -const NativePluginDescriptor* carla_get_native_rack_plugin() { return nullptr; } -const NativePluginDescriptor* carla_get_native_patchbay_plugin() { return nullptr; } -const NativePluginDescriptor* carla_get_native_patchbay16_plugin() { return nullptr; } -const NativePluginDescriptor* carla_get_native_patchbay32_plugin() { return nullptr; } -const NativePluginDescriptor* carla_get_native_patchbay64_plugin() { return nullptr; } -const NativePluginDescriptor* carla_get_native_patchbay_cv_plugin() { return nullptr; } -CarlaBackend::CarlaEngine* carla_get_native_plugin_engine(const NativePluginDescriptor* desc, NativePluginHandle handle) { return nullptr; } +CARLA_EXPORT const char* carla_get_library_filename() { return nullptr; } +CARLA_EXPORT const char* carla_get_library_folder() { return nullptr; } +CARLA_EXPORT const NativePluginDescriptor* carla_get_native_rack_plugin() { return nullptr; } +CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay_plugin() { return nullptr; } +CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay16_plugin() { return nullptr; } +CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay32_plugin() { return nullptr; } +CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay64_plugin() { return nullptr; } +CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay_cv_plugin() { return nullptr; } +CARLA_EXPORT CarlaBackend::CarlaEngine* carla_get_native_plugin_engine(const NativePluginDescriptor* desc, NativePluginHandle handle) { return nullptr; } diff --git a/plugins/carlabase/carla.h b/plugins/carlabase/carla.h index 393912a50..2c683add9 100644 --- a/plugins/carlabase/carla.h +++ b/plugins/carlabase/carla.h @@ -26,10 +26,9 @@ #define CARLA_H #include -#include "plugin_export.h" +#include "carlabase_export.h" #include "CarlaNative.h" -#define REAL_BUILD // FIXME this shouldn't be needed #if CARLA_VERSION_HEX >= 0x010911 #include "CarlaNativePlugin.h" #else @@ -48,7 +47,7 @@ class QPushButton; -class PLUGIN_EXPORT CarlaInstrument : public Instrument +class CARLABASE_EXPORT CarlaInstrument : public Instrument { Q_OBJECT diff --git a/plugins/carlapatchbay/carlapatchbay.cpp b/plugins/carlapatchbay/carlapatchbay.cpp index ac00630d4..ad0c1f6ae 100644 --- a/plugins/carlapatchbay/carlapatchbay.cpp +++ b/plugins/carlapatchbay/carlapatchbay.cpp @@ -25,6 +25,7 @@ #include "carla.h" #include "embed.h" +#include "plugin_export.h" #include "InstrumentTrack.h" extern "C" diff --git a/plugins/carlarack/carlarack.cpp b/plugins/carlarack/carlarack.cpp index c0a39f9c2..ee2a78835 100644 --- a/plugins/carlarack/carlarack.cpp +++ b/plugins/carlarack/carlarack.cpp @@ -25,6 +25,7 @@ #include "carla.h" #include "embed.h" +#include "plugin_export.h" #include "InstrumentTrack.h" extern "C" From ebfa883e4d063a32dead4f17550661a777fc6f1a Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 26 Mar 2020 16:21:29 +0900 Subject: [PATCH 142/160] Carla: fix MSVC compatibility --- plugins/carlabase/carla.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index 669ac800a..a6faa4d27 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -333,8 +333,14 @@ void CarlaInstrument::play(sampleFrame* workingBuffer) fTimeInfo.bbt.ticksPerBeat = ticksPerBeat; fTimeInfo.bbt.beatsPerMinute = s->getTempo(); +#ifndef _MSC_VER float buf1[bufsize]; float buf2[bufsize]; +#else + float *buf1 = static_cast(_alloca(bufsize * sizeof(float))); + float *buf2 = static_cast(_alloca(bufsize * sizeof(float))); +#endif + float* rBuf[] = { buf1, buf2 }; std::memset(buf1, 0, sizeof(float)*bufsize); std::memset(buf2, 0, sizeof(float)*bufsize); From 924743dd1725a542afbeca017a3d22e2558551ed Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Wed, 25 Mar 2020 15:13:37 +0900 Subject: [PATCH 143/160] Fix submodule fetching from non-default branches with old Git --- cmake/modules/CheckSubmodules.cmake | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/cmake/modules/CheckSubmodules.cmake b/cmake/modules/CheckSubmodules.cmake index 32a2f9951..f45885cc6 100644 --- a/cmake/modules/CheckSubmodules.cmake +++ b/cmake/modules/CheckSubmodules.cmake @@ -100,11 +100,11 @@ ENDFOREACH() # Once called, status is stored in GIT_RESULT respectively. # Note: Git likes to write to stderr. Don't assume stderr is error; Check GIT_RESULT instead. -MACRO(GIT_SUBMODULE SUBMODULE_PATH FORCE_DEINIT FORCE_REMOTE FULL_CLONE) +MACRO(GIT_SUBMODULE SUBMODULE_PATH FORCE_DEINIT FORCE_REMOTE NO_DEPTH) FIND_PACKAGE(Git REQUIRED) # Handle missing commits SET(FORCE_REMOTE_FLAG "${FORCE_REMOTE}") - SET(FULL_CLONE_FLAG "${FULL_CLONE}") + SET(NO_DEPTH_FLAG "${NO_DEPTH}") IF(FORCE_REMOTE_FLAG) MESSAGE("-- Adding remote submodulefix to ${SUBMODULE_PATH}") EXECUTE_PROCESS( @@ -115,7 +115,7 @@ MACRO(GIT_SUBMODULE SUBMODULE_PATH FORCE_DEINIT FORCE_REMOTE FULL_CLONE) OUTPUT_QUIET ERROR_QUIET ) # Recurse - GIT_SUBMODULE(${SUBMODULE_PATH} false false ${FULL_CLONE_FLAG}) + GIT_SUBMODULE(${SUBMODULE_PATH} false false ${NO_DEPTH_FLAG}) ELSEIF(${FORCE_DEINIT}) MESSAGE("-- Resetting ${SUBMODULE_PATH}") EXECUTE_PROCESS( @@ -125,21 +125,15 @@ MACRO(GIT_SUBMODULE SUBMODULE_PATH FORCE_DEINIT FORCE_REMOTE FULL_CLONE) ) MESSAGE("-- Deleting ${CMAKE_SOURCE_DIR}/.git/${SUBMODULE_PATH}") FILE(REMOVE_RECURSE "${CMAKE_SOURCE_DIR}/.git/modules/${SUBMODULE_PATH}") - # Recurse + # Recurse without depth GIT_SUBMODULE(${SUBMODULE_PATH} false false true) ELSE() # Try to use the depth switch - IF(NO_SHALLOW_CLONE OR GIT_VERSION_STRING VERSION_LESS "1.8.4") + IF(NO_SHALLOW_CLONE OR GIT_VERSION_STRING VERSION_LESS "1.8.4" OR NO_DEPTH_FLAG) # Shallow submodules were introduced in 1.8.4 MESSAGE("-- Fetching ${SUBMODULE_PATH}") SET(DEPTH_CMD "") SET(DEPTH_VAL "") - ELSEIF(FULL_CLONE_FLAG) - # Depth doesn't revert easily... It should be "--no-recommend-shallow" - # but it's ignored by nested submodules, use the highest value instead. - MESSAGE("-- Fetching ${SUBMODULE_PATH}") - SET(DEPTH_CMD "--depth") - SET(DEPTH_VAL "2147483647") ELSE() MESSAGE("-- Fetching ${SUBMODULE_PATH} @ --depth ${DEPTH_VALUE}") SET(DEPTH_CMD "--depth") @@ -201,13 +195,7 @@ FOREACH(_submodule ${SUBMODULE_LIST}) BREAK() ELSEIF("${GIT_MESSAGE}" MATCHES "${_phrase}") MESSAGE("-- Retrying ${_submodule} using 'deinit' (attempt ${COUNTED} of ${MAX_ATTEMPTS})...") - IF(COUNTED LESS 2) - SET(FULL_CLONE false) - ELSE() - SET(FULL_CLONE true) - ENDIF() - - GIT_SUBMODULE("${_submodule}" true false ${FULL_CLONE}) + GIT_SUBMODULE("${_submodule}" true false false) BREAK() ENDIF() ENDFOREACH() From 8d908c681ee66636fe99427d5d22760baee0838f Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 26 Mar 2020 12:52:59 +0900 Subject: [PATCH 144/160] Ensure plugin dependencies are deployed correctly --- cmake/install/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/install/CMakeLists.txt b/cmake/install/CMakeLists.txt index a3a81beeb..e123ae384 100644 --- a/cmake/install/CMakeLists.txt +++ b/cmake/install/CMakeLists.txt @@ -31,7 +31,8 @@ IF(LMMS_BUILD_WIN32 OR LMMS_INSTALL_DEPENDENCIES) NAME "plugins" TARGETS ${PLUGINS_BUILT} DESTINATION ${PLUGIN_DEP_DESTINATION} - LIB_DIRS ${LIB_DIRS} "${PLUGIN_DIR}" + LIB_DIRS ${LIB_DIRS} "${PLUGIN_DIR}" "${PLUGIN_DIR}/optional" + SEARCH_PATH "${PLUGIN_DIR}" "${PLUGIN_DIR}/optional" ) ENDIF() From 174630087ef24899dfb52ef2e892c68a7fd7caf2 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 3 Mar 2020 12:05:04 +0900 Subject: [PATCH 145/160] Use shimmed std::as_const instead of qAsConst --- include/stdshims.h | 22 +++++++++++++++++++++- src/gui/widgets/ControlLayout.cpp | 4 +++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/stdshims.h b/include/stdshims.h index 5eee6543c..c12254e15 100644 --- a/include/stdshims.h +++ b/include/stdshims.h @@ -5,11 +5,12 @@ #define STDSHIMS_H #include +#include #include #if (__cplusplus >= 201402L || _MSC_VER) #ifndef _MSC_VER -#warning "This file should now be removed! The functions it provides are part of the C++14 standard." +#warning "This part of this file should now be removed! The functions it provides are part of the C++14 standard." #endif using std::make_unique; @@ -30,5 +31,24 @@ std::unique_ptr make_unique(Args&&... args) } #endif +#if (__cplusplus >= 201703L || _MSC_VER >= 1914) +#ifndef _MSC_VER +#warning "This part of this file should now be removed! The functions it provides are part of the C++17 standard." +#endif +using std::as_const; + +#else + +/// Shim for http://en.cppreference.com/w/cpp/utility/as_const +template +constexpr typename std::add_const::type& as_const(T& t) noexcept +{ + return t; +} + +template +void as_const(const T&&) = delete; +#endif + #endif // include guard diff --git a/src/gui/widgets/ControlLayout.cpp b/src/gui/widgets/ControlLayout.cpp index afd4e68e4..ce2e9e4ef 100644 --- a/src/gui/widgets/ControlLayout.cpp +++ b/src/gui/widgets/ControlLayout.cpp @@ -73,6 +73,8 @@ #include "ControlLayout.h" +#include "stdshims.h" + #include #include #include @@ -208,7 +210,7 @@ QSize ControlLayout::minimumSize() const // get maximum height and width for all children. // as Qt will later call heightForWidth, only the width here really matters QSize size; - for (const QLayoutItem *item : qAsConst(m_itemMap)) + for (const QLayoutItem *item : as_const(m_itemMap)) { size = size.expandedTo(item->minimumSize()); } From 2aea19fff13f0aa326fd6bee1eb570d4bec009ba Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Tue, 14 Apr 2020 14:45:08 +0200 Subject: [PATCH 146/160] Add "Open containing folder" (#5453) Add functionality to open the containing folder of a file that's selected in LMMS' file browser. Technical details ------------------ Add a new private method openContainingFolder to FileBrowser. Add a new action to the context menu of a selected file. This action in turn calls the added method. The current implementation of openContainingFolder delegates to QDesktopServices::openUrl with the directory of the selected file. Please note that this will only open the directory but not select the file as this is much more complicated due to different implementations that are needed for the different platforms (Linux/Windows/MacOS). Using QDesktopServices::openUrl seems to be the most simple cross platform way which uses functionality that's already available in Qt. --- include/FileBrowser.h | 1 + src/gui/FileBrowser.cpp | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/FileBrowser.h b/include/FileBrowser.h index b84ba5e54..0e8ce7bf3 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -125,6 +125,7 @@ private slots: void activateListItem( QTreeWidgetItem * item, int column ); void openInNewInstrumentTrackBBE( void ); void openInNewInstrumentTrackSE( void ); + void openContainingFolder(); void sendToActiveInstrumentTrack( void ); void updateDirectory( QTreeWidgetItem * item ); diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 809518683..52641a73d 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -24,6 +24,7 @@ */ +#include #include #include #include @@ -380,6 +381,11 @@ void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e ) "B+B Editor" ), this, SLOT( openInNewInstrumentTrackBBE() ) ); + contextMenu.addSeparator(); + contextMenu.addAction( QIcon(embed::getIconPixmap( "folder" )), + tr( "Open containing folder" ), + this, + SLOT( openContainingFolder() ) ); contextMenu.exec( e->globalPos() ); m_contextMenuItem = NULL; } @@ -671,6 +677,16 @@ void FileBrowserTreeWidget::openInNewInstrumentTrackSE( void ) +void FileBrowserTreeWidget::openContainingFolder() +{ + if (m_contextMenuItem) + { + QFileInfo fileInfo(m_contextMenuItem->fullName()); + QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.dir().path())); + } +} + + void FileBrowserTreeWidget::sendToActiveInstrumentTrack( void ) { From b85aef2f33794eb6bd7fa1153f7c411ef14c5b7c Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 19 Apr 2020 22:08:09 +0200 Subject: [PATCH 147/160] Code review changes Move openContainingFolder to the end of the method block. Adjust FileBrowserTreeWidget::contextMenuEvent to the coding conventions and also make the code more readable by splitting up some conditions. Add comments to clarify as to why the member m_contextMenuItem is set to nullptr at the end of the execution of contextMenuEvent. Please note that this implementation is not exception safe and should be changed in the future, e.g. by passing the FileItem as a parameter of the slot. --- include/FileBrowser.h | 2 +- src/gui/FileBrowser.cpp | 53 +++++++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/include/FileBrowser.h b/include/FileBrowser.h index 0e8ce7bf3..d36eacf07 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -125,9 +125,9 @@ private slots: void activateListItem( QTreeWidgetItem * item, int column ); void openInNewInstrumentTrackBBE( void ); void openInNewInstrumentTrackSE( void ); - void openContainingFolder(); void sendToActiveInstrumentTrack( void ); void updateDirectory( QTreeWidgetItem * item ); + void openContainingFolder(); } ; diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 52641a73d..29f25ec30 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -364,30 +364,41 @@ QList FileBrowserTreeWidget::expandedDirs( QTreeWidgetItem * item ) con void FileBrowserTreeWidget::contextMenuEvent(QContextMenuEvent * e ) { - FileItem * f = dynamic_cast( itemAt( e->pos() ) ); - if( f != NULL && ( f->handling() == FileItem::LoadAsPreset || - f->handling() == FileItem::LoadByPlugin ) ) + FileItem * f = dynamic_cast(itemAt(e->pos())); + if (f == nullptr) { + return; + } + + if (f->handling() == FileItem::LoadAsPreset || f->handling() == FileItem::LoadByPlugin) + { + // Set the member to the current FileItem so that it is available during the + // execution of the slots of the context menu we are about to create and execute. m_contextMenuItem = f; - QMenu contextMenu( this ); - contextMenu.addAction( tr( "Send to active instrument-track" ), - this, - SLOT( sendToActiveInstrumentTrack() ) ); - contextMenu.addAction( tr( "Open in new instrument-track/" - "Song Editor" ), - this, - SLOT( openInNewInstrumentTrackSE() ) ); - contextMenu.addAction( tr( "Open in new instrument-track/" - "B+B Editor" ), - this, - SLOT( openInNewInstrumentTrackBBE() ) ); - contextMenu.addSeparator(); - contextMenu.addAction( QIcon(embed::getIconPixmap( "folder" )), - tr( "Open containing folder" ), + + QMenu contextMenu(this); + + contextMenu.addAction(tr("Send to active instrument-track"), this, - SLOT( openContainingFolder() ) ); - contextMenu.exec( e->globalPos() ); - m_contextMenuItem = NULL; + SLOT(sendToActiveInstrumentTrack())); + contextMenu.addAction(tr("Open in new instrument-track/Song Editor"), + this, + SLOT(openInNewInstrumentTrackSE())); + contextMenu.addAction(tr("Open in new instrument-track/B+B Editor"), + this, + SLOT(openInNewInstrumentTrackBBE())); + + contextMenu.addSeparator(); + + contextMenu.addAction(QIcon(embed::getIconPixmap("folder")), + tr("Open containing folder"), + this, + SLOT(openContainingFolder())); + + contextMenu.exec(e->globalPos()); + + // The context menu has been executed so we can reset this member back to nullptr. + m_contextMenuItem = nullptr; } } From c37fdd005529f1fbc19d974c62d617fe89dfe988 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Tue, 21 Apr 2020 20:12:50 +0200 Subject: [PATCH 148/160] Code review changes (comment added) Add a comment which describes that only the folder is opened without selecting any file. Also explain why it is only done like this for now. --- src/gui/FileBrowser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 29f25ec30..c976c39f6 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -692,6 +692,12 @@ void FileBrowserTreeWidget::openContainingFolder() { if (m_contextMenuItem) { + // Delegate to QDesktopServices::openUrl with the directory of the selected file. Please note that + // this will only open the directory but not select the file as this is much more complicated due + // to different implementations that are needed for different platforms (Linux/Windows/MacOS). + + // Using QDesktopServices::openUrl seems to be the most simple cross platform way which uses + // functionality that's already available in Qt. QFileInfo fileInfo(m_contextMenuItem->fullName()); QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.dir().path())); } From abcfee13343051002aa0cb42161254013868e64e Mon Sep 17 00:00:00 2001 From: tecknixia <50790262+tecknixia@users.noreply.github.com> Date: Wed, 22 Apr 2020 13:47:59 -0500 Subject: [PATCH 149/160] disable drag after drawing line (#5315) --- include/AutomationEditor.h | 1 + src/gui/editors/AutomationEditor.cpp | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 60b894f1f..520416be8 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -240,6 +240,7 @@ private: EditModes m_editMode; + bool m_mouseDownLeft; bool m_mouseDownRight; //true if right click is being held down TimeLineWidget * m_timeLine; diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index bf56e3039..048123ef5 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -96,6 +96,7 @@ AutomationEditor::AutomationEditor() : m_y_delta( DEFAULT_Y_DELTA ), m_y_auto( true ), m_editMode( DRAW ), + m_mouseDownLeft(false), m_mouseDownRight( false ), m_scrollBack( false ), m_barLineColor( 0, 0, 0 ), @@ -539,6 +540,10 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) ++it; } + if (mouseEvent->button() == Qt::LeftButton) + { + m_mouseDownLeft = true; + } if( mouseEvent->button() == Qt::RightButton ) { m_mouseDownRight = true; @@ -555,6 +560,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) drawLine( m_drawLastTick, m_drawLastLevel, pos_ticks, level ); + m_mouseDownLeft = false; } m_drawLastTick = pos_ticks; m_drawLastLevel = level; @@ -657,6 +663,11 @@ void AutomationEditor::mouseReleaseEvent(QMouseEvent * mouseEvent ) { bool mustRepaint = false; + if (mouseEvent->button() == Qt::LeftButton) + { + m_mouseDownLeft = false; + mustRepaint = true; + } if ( mouseEvent->button() == Qt::RightButton ) { m_mouseDownRight = false; @@ -742,7 +753,8 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) int pos_ticks = x * MidiTime::ticksPerBar() / m_ppb + m_currentPosition; - if( mouseEvent->buttons() & Qt::LeftButton && m_editMode == DRAW ) + // m_mouseDownLeft used to prevent drag when drawing line + if (m_mouseDownLeft && m_editMode == DRAW) { if( m_action == MOVE_VALUE ) { From aec0dd3b3e34ca7e47067c04d2b2d9737bf5ba4e Mon Sep 17 00:00:00 2001 From: Veratil Date: Sun, 26 Apr 2020 10:14:29 -0500 Subject: [PATCH 150/160] If AutomationPattern has a single tick at 0, set it's length to 1 bar. --- src/core/AutomationPattern.cpp | 26 ++++++++++++++------------ src/core/Track.cpp | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index b38c704ef..9a8bdca15 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -178,9 +178,19 @@ const AutomationPattern::objectVector& AutomationPattern::objects() const MidiTime AutomationPattern::timeMapLength() const { - if( m_timeMap.isEmpty() ) return 0; + MidiTime one_bar = MidiTime(1, 0); + if (m_timeMap.isEmpty()) + { + return one_bar; + } timeMap::const_iterator it = m_timeMap.end(); - return MidiTime( MidiTime( (it-1).key() ).nextFullBar(), 0 ); + tick_t last_tick = static_cast((it-1).key()); + bar_t last_bar = qMax(0, MidiTime(last_tick).nextFullBar() - 1); + if (last_bar == 0 && last_tick == 0) + { + return one_bar; + } + return MidiTime(last_bar, last_tick); } @@ -223,12 +233,7 @@ MidiTime AutomationPattern::putValue( const MidiTime & time, } generateTangents( it, 3 ); - // we need to maximize our length in case we're part of a hidden - // automation track as the user can't resize this pattern - if( getTrack() && getTrack()->type() == Track::HiddenAutomationTrack ) - { - updateLength(); - } + updateLength(); emit dataChanged(); @@ -251,10 +256,7 @@ void AutomationPattern::removeValue( const MidiTime & time ) } generateTangents(it, 3); - if( getTrack() && getTrack()->type() == Track::HiddenAutomationTrack ) - { - updateLength(); - } + updateLength(); emit dataChanged(); } diff --git a/src/core/Track.cpp b/src/core/Track.cpp index cb430da96..0c046a504 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -159,7 +159,7 @@ void TrackContentObject::movePosition( const MidiTime & pos ) /*! \brief Change the length of this TrackContentObject * - * If the track content object's length has chaanged, update it. We + * If the track content object's length has changed, update it. We * also add a journal entry for undo and update the display. * * \param _length The new length of the track content object. From a4f677362d3c37e5436884c70c8869f0cc9174cd Mon Sep 17 00:00:00 2001 From: Veratil Date: Sun, 26 Apr 2020 14:44:05 -0500 Subject: [PATCH 151/160] Add comments and reduce unnecessary code --- src/core/AutomationPattern.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 9a8bdca15..2fd1cea12 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -179,18 +179,15 @@ const AutomationPattern::objectVector& AutomationPattern::objects() const MidiTime AutomationPattern::timeMapLength() const { MidiTime one_bar = MidiTime(1, 0); - if (m_timeMap.isEmpty()) - { - return one_bar; - } + if (m_timeMap.isEmpty()) { return one_bar; } + timeMap::const_iterator it = m_timeMap.end(); tick_t last_tick = static_cast((it-1).key()); - bar_t last_bar = qMax(0, MidiTime(last_tick).nextFullBar() - 1); - if (last_bar == 0 && last_tick == 0) - { - return one_bar; - } - return MidiTime(last_bar, last_tick); + // if last_tick is 0 (single item at tick 0) + // return length as a whole bar to prevent disappearing TCO + if (last_tick == 0) { return one_bar; } + + return MidiTime(last_tick); } @@ -198,7 +195,8 @@ MidiTime AutomationPattern::timeMapLength() const void AutomationPattern::updateLength() { - changeLength( timeMapLength() ); + // Do not resize down in case user manually extended up + changeLength(qMax(length(), timeMapLength())); } From e199f72686224011fe8da23b8db8befb5a0bd2b4 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 28 Apr 2020 14:39:39 +0900 Subject: [PATCH 152/160] Fix crash on drawing line on the end of a graph (#5471) This bug was introduced in dff76b2e83d4cfe140a8554f590cfd3780b61204. The function changes [sample_end, sample_begin), but emits the signal as if [sample_end, sample_begin] has been changed. That bug made Multitap Echo crash when tweaking the cutoff of the 32nd stage. This commit fixes the issue by emitting sampleChanged with sample_end - 1. --- src/gui/widgets/Graph.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/widgets/Graph.cpp b/src/gui/widgets/Graph.cpp index 4710089dd..773a6f51b 100644 --- a/src/gui/widgets/Graph.cpp +++ b/src/gui/widgets/Graph.cpp @@ -235,8 +235,9 @@ void Graph::drawLineAt( int _x, int _y, int _lastx ) model()->drawSampleAt( sample_begin + i , val_begin + ((i ) * ystep)); } - - model()->samplesChanged( sample_begin, sample_end ); + // We've changed [sample_end, sample_begin) + // However, samplesChanged expects two end points + model()->samplesChanged(sample_begin, sample_end - 1); } void Graph::changeSampleAt( int _x, int _y ) From b46ea0e9af5755c5305398adf763d7c2ae2688cc Mon Sep 17 00:00:00 2001 From: Spekular Date: Wed, 29 Apr 2020 20:44:09 +0200 Subject: [PATCH 153/160] refactor --- include/FadeButton.h | 2 +- include/SampleTrack.h | 16 ++++----- src/gui/widgets/FadeButton.cpp | 27 ++++++---------- src/tracks/SampleTrack.cpp | 59 +++++++++++----------------------- 4 files changed, 37 insertions(+), 67 deletions(-) diff --git a/include/FadeButton.h b/include/FadeButton.h index 4cd45893a..54703d194 100644 --- a/include/FadeButton.h +++ b/include/FadeButton.h @@ -46,8 +46,8 @@ public: public slots: void activate(); + void activateOnce(); void noteEnd(); - void notPlaying(); protected: diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 4624e2a39..47cc0df39 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -162,19 +162,19 @@ public: return "sampletrack"; } - bool wasPlaying() + bool isPlaying() { - return m_wasPlaying; + return m_isPlaying; } - void setWasPlaying(bool wasPlaying) + void setPlaying(bool playing) { - m_wasPlaying = wasPlaying; + if (m_isPlaying != playing) { emit playingChanged(); } + m_isPlaying = playing; } signals: - void playing(); - void notPlaying(); + void playingChanged(); public slots: void updateTcos(); @@ -186,7 +186,7 @@ private: FloatModel m_panningModel; IntModel m_effectChannelModel; AudioPort m_audioPort; - bool m_wasPlaying; + bool m_isPlaying; @@ -225,7 +225,7 @@ public: public slots: void showEffects(); - void stopPlaying(); + void updateIndicator(); protected: diff --git a/src/gui/widgets/FadeButton.cpp b/src/gui/widgets/FadeButton.cpp index 95b8bbeb6..43f806144 100644 --- a/src/gui/widgets/FadeButton.cpp +++ b/src/gui/widgets/FadeButton.cpp @@ -75,6 +75,14 @@ void FadeButton::activate() +void FadeButton::activateOnce() +{ + if (activeNotes == 0) { activate(); } +} + + + + void FadeButton::noteEnd() { if (activeNotes <= 0) @@ -92,29 +100,12 @@ void FadeButton::noteEnd() m_releaseTimer.restart(); } - signalUpdate(); -} - - - - -void FadeButton::notPlaying() -{ - activeNotes = 0; - m_releaseTimer.restart(); - signalUpdate(); -} - - - - -void FadeButton::customEvent(QEvent *) -{ update(); } + void FadeButton::paintEvent(QPaintEvent * _pe) { QColor col = m_normalColor; diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 7d3bdb581..f4dbedda2 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -593,7 +593,7 @@ SampleTrack::SampleTrack( TrackContainer* tc ) : this, tr( "Panning" ) ), m_effectChannelModel( 0, 0, 0, this, tr( "FX channel" ) ), m_audioPort( tr( "Sample track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ), - m_wasPlaying(false) + m_isPlaying(false) { setName( tr( "Sample track" ) ); m_panningModel.setCenterValue( DefaultPanning ); @@ -618,27 +618,26 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, { m_audioPort.effects()->startRunning(); bool played_a_note = false; // will be return variable - bool is_playing = false; + bool nowPlaying = false; tcoVector tcos; ::BBTrack * bb_track = NULL; if( _tco_num >= 0 ) { - if (m_wasPlaying && _start > getTCO(_tco_num)->length()) + if (_start > getTCO(_tco_num)->length()) { - m_wasPlaying = false; - emit notPlaying(); + nowPlaying = false; } if( _start != 0 ) { + nowPlaying = false; return false; } tcos.push_back( getTCO( _tco_num ) ); if (trackContainer() == (TrackContainer*)Engine::getBBTrackContainer()) { bb_track = BBTrack::findBBTrack( _tco_num ); - m_wasPlaying = true; - emit playing(); + nowPlaying = true; } } else @@ -650,10 +649,6 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, if( _start >= sTco->startPosition() && _start < sTco->endPosition() ) { - if (sTco->isPlaying()) - { - is_playing = true; - } if( sTco->isPlaying() == false && _start > sTco->startPosition() + sTco->startTimeOffset() ) { auto bufferFramesPerTick = Engine::framesPerTick (sTco->sampleBuffer ()->sampleRate ()); @@ -670,7 +665,7 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, sTco->setSamplePlayLength( samplePlayLength ); tcos.push_back( sTco ); sTco->setIsPlaying( true ); - is_playing = true; + nowPlaying = true; } } } @@ -678,20 +673,10 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, { sTco->setIsPlaying( false ); } + nowPlaying = nowPlaying || sTco->isPlaying(); } - - if (is_playing && !m_wasPlaying) - { - m_wasPlaying = true; - emit playing(); - } - else if (!is_playing && m_wasPlaying) - { - m_wasPlaying = false; - emit notPlaying(); - } - } + setPlaying(nowPlaying); for( tcoVector::Iterator it = tcos.begin(); it != tcos.end(); ++it ) { @@ -861,10 +846,7 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : getTrackSettingsWidget()); m_activityIndicator->setGeometry(settingsWidgetWidth-2*24-11, 2, 8, 28); m_activityIndicator->show(); - connect(_t, SIGNAL(playing()), - m_activityIndicator, SLOT(activate())); - connect(_t, SIGNAL(notPlaying()), - m_activityIndicator, SLOT(notPlaying())); + connect(_t, SIGNAL(playingChanged()), this, SLOT(updateIndicator())); connect(Engine::getSong(), SIGNAL(stopped()), this, SLOT(stopPlaying())); @@ -877,6 +859,15 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : +void SampleTrackView::updateIndicator() +{ + if (model()->isPlaying()) m_activityIndicator->activateOnce(); + else { m_activityIndicator->noteEnd(); } +} + + + + SampleTrackView::~SampleTrackView() { if(m_window != NULL) @@ -982,18 +973,6 @@ void SampleTrackView::dropEvent(QDropEvent *de) -void SampleTrackView::stopPlaying() -{ - SampleTrack * smpltrck = dynamic_cast(getTrack()); - if (smpltrck->wasPlaying()) - { - m_activityIndicator->notPlaying(); - smpltrck->setWasPlaying(false); - } -} - - - SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : QWidget(), From 5821466f18387c6dae624db315984e3e0a448309 Mon Sep 17 00:00:00 2001 From: Spekular Date: Wed, 29 Apr 2020 21:17:16 +0200 Subject: [PATCH 154/160] Fix indicator in BB editor --- src/tracks/SampleTrack.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index f4dbedda2..060018656 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -618,7 +618,6 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, { m_audioPort.effects()->startRunning(); bool played_a_note = false; // will be return variable - bool nowPlaying = false; tcoVector tcos; ::BBTrack * bb_track = NULL; @@ -626,22 +625,22 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, { if (_start > getTCO(_tco_num)->length()) { - nowPlaying = false; + setPlaying(false); } if( _start != 0 ) { - nowPlaying = false; return false; } tcos.push_back( getTCO( _tco_num ) ); if (trackContainer() == (TrackContainer*)Engine::getBBTrackContainer()) { bb_track = BBTrack::findBBTrack( _tco_num ); - nowPlaying = true; + setPlaying(true); } } else { + bool nowPlaying = false; for( int i = 0; i < numOfTCOs(); ++i ) { TrackContentObject * tco = getTCO( i ); @@ -675,8 +674,8 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, } nowPlaying = nowPlaying || sTco->isPlaying(); } + setPlaying(nowPlaying); } - setPlaying(nowPlaying); for( tcoVector::Iterator it = tcos.begin(); it != tcos.end(); ++it ) { From c755b56a52145a572247303a09d126e764238c11 Mon Sep 17 00:00:00 2001 From: akimaze <63190361+akimaze@users.noreply.github.com> Date: Thu, 30 Apr 2020 04:22:28 +0200 Subject: [PATCH 155/160] Piano roll vertical zoom (#5442) --- include/PianoRoll.h | 14 ++- src/gui/editors/PianoRoll.cpp | 226 +++++++++++++++++++++------------- 2 files changed, 156 insertions(+), 84 deletions(-) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 27a15149e..8421392c3 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -180,7 +180,7 @@ protected: void focusOutEvent( QFocusEvent * ) override; int getKey( int y ) const; - static void drawNoteRect( QPainter & p, int x, int y, + void drawNoteRect( QPainter & p, int x, int y, int width, const Note * n, const QColor & noteCol, const QColor & noteTextColor, const QColor & selCol, const int noteOpc, const bool borderless, bool drawNoteName ); void removeSelection(); @@ -192,6 +192,8 @@ protected: // for entering values with dblclick in the vol/pan bars void enterValue( NoteVector* nv ); + void updateYScroll(); + protected slots: void play(); void record(); @@ -217,6 +219,7 @@ protected slots: void updatePositionStepRecording(const MidiTime & t ); void zoomingChanged(); + void zoomingYChanged(); void quantizeChanged(); void noteLengthChanged(); void quantizeNotes(); @@ -330,12 +333,14 @@ private: static TextFloat * s_textFloat; ComboBoxModel m_zoomingModel; + ComboBoxModel m_zoomingYModel; ComboBoxModel m_quantizeModel; ComboBoxModel m_noteLenModel; ComboBoxModel m_scaleModel; ComboBoxModel m_chordModel; static const QVector m_zoomLevels; + static const QVector m_zoomYLevels; Pattern* m_pattern; NoteVector m_ghostNotes; @@ -385,6 +390,12 @@ private: int m_ppb; // pixels per bar int m_totalKeysToScroll; + int m_keyLineHeight; + int m_octaveHeight; + int m_whiteKeySmallHeight; + int m_whiteKeyBigHeight; + int m_blackKeyHeight; + // remember these values to use them // for the next note that is set MidiTime m_lenOfNewNotes; @@ -501,6 +512,7 @@ private: PianoRoll* m_editor; ComboBox * m_zoomingComboBox; + ComboBox * m_zoomingYComboBox; ComboBox * m_quantizeComboBox; ComboBox * m_noteLenComboBox; ComboBox * m_scaleComboBox; diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index d49c6f0b7..510fc800e 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -78,11 +78,11 @@ const int SCROLLBAR_SIZE = 12; const int PIANO_X = 0; const int WHITE_KEY_WIDTH = 64; -const int WHITE_KEY_SMALL_HEIGHT = 18; -const int WHITE_KEY_BIG_HEIGHT = 24; -const int BLACK_KEY_HEIGHT = 16; -const int KEY_LINE_HEIGHT = 12; -const int OCTAVE_HEIGHT = KEY_LINE_HEIGHT * KeysPerOctave; // = 12 * 12; +const int BLACK_KEY_WIDTH = 41; + +const int DEFAULT_KEY_LINE_HEIGHT = 12; +const int DEFAULT_CELL_WIDTH = 12; + const int NOTE_EDIT_RESIZE_BAR = 6; const int NOTE_EDIT_MIN_HEIGHT = 50; @@ -128,8 +128,6 @@ static QString getNoteString( int key ) return s_noteStrings[key % 12] + QString::number( static_cast( key / KeysPerOctave ) ); } - - // used for drawing of piano PianoRoll::PianoRollKeyTypes PianoRoll::prKeyOrder[] = { @@ -139,10 +137,13 @@ PianoRoll::PianoRollKeyTypes PianoRoll::prKeyOrder[] = } ; -const int DEFAULT_PR_PPB = KEY_LINE_HEIGHT * DefaultStepsPerBar; +const int DEFAULT_PR_PPB = DEFAULT_CELL_WIDTH * DefaultStepsPerBar; const QVector PianoRoll::m_zoomLevels = - { 0.125f, 0.25f, 0.5f, 1.0f, 2.0f, 4.0f, 8.0f }; + {0.125f, 0.25f, 0.5f, 1.0f, 1.5f, 2.0f, 4.0f, 8.0f}; + +const QVector PianoRoll::m_zoomYLevels = + {0.25f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 4.0f}; PianoRoll::PianoRoll() : @@ -150,6 +151,7 @@ PianoRoll::PianoRoll() : m_noteEditMenu( NULL ), m_semiToneMarkerMenu( NULL ), m_zoomingModel(), + m_zoomingYModel(), m_quantizeModel(), m_noteLenModel(), m_scaleModel(), @@ -171,6 +173,11 @@ PianoRoll::PianoRoll() : m_oldNotesEditHeight( 100 ), m_notesEditHeight( 100 ), m_ppb( DEFAULT_PR_PPB ), + m_keyLineHeight(DEFAULT_KEY_LINE_HEIGHT), + m_octaveHeight(m_keyLineHeight * KeysPerOctave), + m_whiteKeySmallHeight(round(m_keyLineHeight * 1.5)), + m_whiteKeyBigHeight(m_keyLineHeight * 2), + m_blackKeyHeight(round(m_keyLineHeight * 1.3333)), m_lenOfNewNotes( MidiTime( 0, DefaultTicksPerBar/4 ) ), m_lastNoteVolume( DefaultVolume ), m_lastNotePanning( DefaultPanning ), @@ -352,6 +359,15 @@ PianoRoll::PianoRoll() : connect( &m_zoomingModel, SIGNAL( dataChanged() ), this, SLOT( zoomingChanged() ) ); + // zoom y + for (float const & zoomLevel : m_zoomYLevels) + { + m_zoomingYModel.addItem(QString( "%1\%" ).arg(zoomLevel * 100)); + } + m_zoomingYModel.setValue(m_zoomingYModel.findText("100%")); + connect(&m_zoomingYModel, SIGNAL(dataChanged()), + this, SLOT(zoomingYChanged())); + // Set up quantization model m_quantizeModel.addItem( tr( "Note lock" ) ); for( int i = 0; i <= NUM_EVEN_LENGTHS; ++i ) @@ -951,7 +967,7 @@ void PianoRoll::drawNoteRect( QPainter & p, int x, int y, const int borderWidth = borders ? 1 : 0; - const int noteHeight = KEY_LINE_HEIGHT - 1 - borderWidth; + const int noteHeight = m_keyLineHeight - 1 - borderWidth; int noteWidth = width + 1 - borderWidth; // adjust note to make it a bit faded if it has a lower volume @@ -993,7 +1009,12 @@ void PianoRoll::drawNoteRect( QPainter & p, int x, int y, int const distanceToBorder = 2; int const xOffset = borderWidth + distanceToBorder; - int const yOffset = (noteHeight + noteTextHeight) / 2; + + // noteTextHeight, textSize are not suitable for determining vertical spacing, + // capHeight() can be used for this, but requires Qt 5.8. + // We use boundingRect() with QChar (the QString version returns wrong value). + QRect const boundingRect = fontMetrics.boundingRect(QChar::fromLatin1('H')); + int const yOffset = (noteHeight - boundingRect.top() - boundingRect.bottom()) / 2; if (textSize.width() < noteWidth - xOffset) { @@ -1022,7 +1043,7 @@ void PianoRoll::drawNoteRect( QPainter & p, int x, int y, void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x, int _y ) const { - int middle_y = _y + KEY_LINE_HEIGHT / 2; + int middle_y = _y + m_keyLineHeight / 2; _p.setPen( noteColor() ); _p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, width() - WHITE_KEY_WIDTH, @@ -1039,7 +1060,7 @@ void PianoRoll::drawDetuningInfo( QPainter & _p, const Note * _n, int _x, const float level = it.value(); - int pos_y = middle_y - level * KEY_LINE_HEIGHT; + int pos_y = middle_y - level * m_keyLineHeight; if( old_x != 0 && old_y != 0 ) { @@ -2596,7 +2617,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) int visible_keys = ( height() - PR_TOP_MARGIN - PR_BOTTOM_MARGIN - m_notesEditHeight ) / - KEY_LINE_HEIGHT + 2; + m_keyLineHeight + 2; const int s_key = m_startKey - 1; if( key_num <= s_key ) @@ -2846,6 +2867,11 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // set font-size to 8 p.setFont( pointSize<8>( p.font() ) ); + QFontMetrics fontMetrics(p.font()); + QRect const boundingRect = fontMetrics.boundingRect(QChar::fromLatin1('H')); + // This is two times of the y coordinate of the center of the bounding rectangle + // (-(top+bottom)=-2(center)) but labelHeight is more intuitive/describing name + int const labelHeight = - boundingRect.top() - boundingRect.bottom(); // y_offset is used to align the piano-keys on the key-lines int y_offset = 0; @@ -2853,20 +2879,20 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // calculate y_offset according to first key switch( prKeyOrder[m_startKey % KeysPerOctave] ) { - case PR_BLACK_KEY: y_offset = KEY_LINE_HEIGHT / 4; break; - case PR_WHITE_KEY_BIG: y_offset = KEY_LINE_HEIGHT / 2; break; + case PR_BLACK_KEY: y_offset = m_keyLineHeight / 4; break; + case PR_WHITE_KEY_BIG: y_offset = m_keyLineHeight / 2; break; case PR_WHITE_KEY_SMALL: if( prKeyOrder[( ( m_startKey + 1 ) % KeysPerOctave)] != PR_BLACK_KEY ) { - y_offset = KEY_LINE_HEIGHT / 2; + y_offset = m_keyLineHeight / 2; } break; } // start drawing at the bottom int key_line_y = keyAreaBottom() - 1; // used for aligning black-keys later - int first_white_key_height = WHITE_KEY_SMALL_HEIGHT; + int first_white_key_height = m_whiteKeySmallHeight; // key-counter - only needed for finding out whether the processed // key is the first one int keys_processed = 0; @@ -2875,7 +2901,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // draw all white keys... for( int y = key_line_y + 1 + y_offset; y > PR_TOP_MARGIN; - key_line_y -= KEY_LINE_HEIGHT, ++keys_processed ) + key_line_y -= m_keyLineHeight, ++keys_processed ) { // check for white key that is only half visible on the // bottom of piano-roll @@ -2884,16 +2910,16 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) PR_BLACK_KEY ) { // draw it! - p.drawPixmap( PIANO_X, y - WHITE_KEY_SMALL_HEIGHT, + p.drawPixmap( PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, *s_whiteKeySmallPm ); // update y-pos - y -= WHITE_KEY_SMALL_HEIGHT / 2; + y -= m_whiteKeySmallHeight / 2; // move first black key down (we didn't draw whole // white key so black key needs to be lifted down) // (default for first_white_key_height = - // WHITE_KEY_SMALL_HEIGHT, so WHITE_KEY_SMALL_HEIGHT/2 + // m_whiteKeySmallHeight, so m_whiteKeySmallHeight/2 // is smaller) - first_white_key_height = WHITE_KEY_SMALL_HEIGHT / 2; + first_white_key_height = m_whiteKeySmallHeight / 2; } // check whether to draw a big or a small white key if( prKeyOrder[key % KeysPerOctave] == PR_WHITE_KEY_SMALL ) @@ -2901,14 +2927,16 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // draw a small one while checking if it is pressed or not if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) { - p.drawPixmap( PIANO_X, y - WHITE_KEY_SMALL_HEIGHT, *s_whiteKeySmallPressedPm ); + p.drawPixmap(PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, + *s_whiteKeySmallPressedPm); } else { - p.drawPixmap( PIANO_X, y - WHITE_KEY_SMALL_HEIGHT, *s_whiteKeySmallPm ); + p.drawPixmap(PIANO_X, y - m_whiteKeySmallHeight, WHITE_KEY_WIDTH, m_whiteKeySmallHeight, + *s_whiteKeySmallPm); } // update y-pos - y -= WHITE_KEY_SMALL_HEIGHT; + y -= m_whiteKeySmallHeight; } else if( prKeyOrder[key % KeysPerOctave] == PR_WHITE_KEY_BIG ) @@ -2916,47 +2944,51 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // draw a big one while checking if it is pressed or not if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) { - p.drawPixmap( PIANO_X, y - WHITE_KEY_BIG_HEIGHT, *s_whiteKeyBigPressedPm ); + p.drawPixmap(PIANO_X, y - m_whiteKeyBigHeight, WHITE_KEY_WIDTH, m_whiteKeyBigHeight, + *s_whiteKeyBigPressedPm); } else { - p.drawPixmap( PIANO_X, y-WHITE_KEY_BIG_HEIGHT, *s_whiteKeyBigPm ); + p.drawPixmap(PIANO_X, y-m_whiteKeyBigHeight, WHITE_KEY_WIDTH, m_whiteKeyBigHeight, + *s_whiteKeyBigPm); } // if a big white key has been the first key, // black keys needs to be lifted up if( keys_processed == 0 ) { - first_white_key_height = WHITE_KEY_BIG_HEIGHT; + first_white_key_height = m_whiteKeyBigHeight; } // update y-pos - y -= WHITE_KEY_BIG_HEIGHT; + y -= m_whiteKeyBigHeight; } // Compute the corrections for the note names int yCorrectionForNoteLabels = 0; int keyCode = key % KeysPerOctave; - switch( keyCode ) + switch (keyCode) { - case 0: - case 5: - yCorrectionForNoteLabels = -4; + case 0: // C + case 5: // F + yCorrectionForNoteLabels = (m_whiteKeySmallHeight - labelHeight + 1) / -2; break; - case 2: - case 7: - case 9: - yCorrectionForNoteLabels = -2; + case 2: // D + case 7: // G + case 9: // A + yCorrectionForNoteLabels = (m_whiteKeyBigHeight / 2 - labelHeight + 1) / -2; break; - case 4: - case 11: - yCorrectionForNoteLabels = 2; + case 4: // E + case 11: // B + // calculate center point of key and move half of text + yCorrectionForNoteLabels = -(((m_whiteKeySmallHeight - (m_whiteKeySmallHeight * 2 + 3) / 6) / 4) + - labelHeight / 2); break; } if( Piano::isWhiteKey( key ) ) { // Draw note names if activated in the preferences, C notes are always drawn - if ( key % 12 == 0 || drawNoteNames ) + if ( (key % 12 == 0 || drawNoteNames) && m_keyLineHeight > 10 ) { QString noteString = getNoteString( key ); @@ -3000,14 +3032,14 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) PR_BLACK_KEY ) { // draw the black key! - p.drawPixmap( PIANO_X, y - BLACK_KEY_HEIGHT / 2, + p.drawPixmap( PIANO_X, y - m_blackKeyHeight / 2, BLACK_KEY_WIDTH, m_blackKeyHeight, *s_blackKeyPm ); // is the one after the start-note a black key?? if( prKeyOrder[( key + 1 ) % KeysPerOctave] != PR_BLACK_KEY ) { // no, then move it up! - y -= KEY_LINE_HEIGHT / 2; + y -= m_keyLineHeight / 2; } } // current key black? @@ -3019,19 +3051,19 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) if( hasValidPattern() && m_pattern->instrumentTrack()->pianoModel()->isKeyPressed( key ) ) { p.drawPixmap( PIANO_X, y - ( first_white_key_height - - WHITE_KEY_SMALL_HEIGHT ) - - WHITE_KEY_SMALL_HEIGHT/2 - 1 - - BLACK_KEY_HEIGHT, *s_blackKeyPressedPm ); + m_whiteKeySmallHeight ) - + m_whiteKeySmallHeight/2 - 1 - + m_blackKeyHeight, BLACK_KEY_WIDTH, m_blackKeyHeight, *s_blackKeyPressedPm ); } else { p.drawPixmap( PIANO_X, y - ( first_white_key_height - - WHITE_KEY_SMALL_HEIGHT ) - - WHITE_KEY_SMALL_HEIGHT/2 - 1 - - BLACK_KEY_HEIGHT, *s_blackKeyPm ); + m_whiteKeySmallHeight ) - + m_whiteKeySmallHeight/2 - 1 - + m_blackKeyHeight, BLACK_KEY_WIDTH, m_blackKeyHeight, *s_blackKeyPm ); } // update y-pos - y -= WHITE_KEY_BIG_HEIGHT; + y -= m_whiteKeyBigHeight; // reset white-counter white_cnt = 0; } @@ -3042,7 +3074,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) ++white_cnt; if( white_cnt > 1 ) { - y -= WHITE_KEY_BIG_HEIGHT/2; + y -= m_whiteKeyBigHeight/2; } } @@ -3103,7 +3135,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // Draw horizontal lines key = m_startKey; for( int y = keyAreaBottom() - 1; y > PR_TOP_MARGIN; - y -= KEY_LINE_HEIGHT ) + y -= m_keyLineHeight ) { if( static_cast( key % KeysPerOctave ) == Key_C ) { @@ -3162,14 +3194,14 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) { const int key_num = m_markedSemiTones.at( i ); const int y = keyAreaBottom() + 5 - - KEY_LINE_HEIGHT * ( key_num - m_startKey + 1 ); + - m_keyLineHeight * ( key_num - m_startKey + 1 ); if( y > keyAreaBottom() ) { break; } - p.fillRect( WHITE_KEY_WIDTH + 1, y - KEY_LINE_HEIGHT / 2, width() - 10, KEY_LINE_HEIGHT + 1, + p.fillRect( WHITE_KEY_WIDTH + 1, y - m_keyLineHeight / 2, width() - 10, m_keyLineHeight + 1, markedSemitoneColor() ); } } @@ -3200,7 +3232,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) height() - PR_TOP_MARGIN ); const int visible_keys = ( keyAreaBottom()-keyAreaTop() ) / - KEY_LINE_HEIGHT + 2; + m_keyLineHeight + 2; QPolygonF editHandles; @@ -3239,7 +3271,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // we've done and checked all, let's draw the // note drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * KEY_LINE_HEIGHT, + y_base - key * m_keyLineHeight, note_width, note, ghostNoteColor(), ghostNoteTextColor(), selectedNoteColor(), ghostNoteOpacity(), ghostNoteBorders(), drawNoteNames ); } @@ -3281,7 +3313,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // we've done and checked all, let's draw the // note drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * KEY_LINE_HEIGHT, + y_base - key * m_keyLineHeight, note_width, note, noteColor(), noteTextColor(), selectedNoteColor(), noteOpacity(), noteBorders(), drawNoteNames ); } @@ -3332,7 +3364,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) { drawDetuningInfo( p, note, x + WHITE_KEY_WIDTH, - y_base - key * KEY_LINE_HEIGHT ); + y_base - key * m_keyLineHeight ); p.setClipRect(WHITE_KEY_WIDTH, PR_TOP_MARGIN, width() - WHITE_KEY_WIDTH, height() - PR_TOP_MARGIN); @@ -3368,7 +3400,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) // we've done and checked all, let's draw the note drawNoteRect( p, x + WHITE_KEY_WIDTH, - y_base - key * KEY_LINE_HEIGHT, + y_base - key * m_keyLineHeight, note_width, note, m_stepRecorder.curStepNoteColor(), noteTextColor(), selectedNoteColor(), noteOpacity(), noteBorders(), drawNoteNames ); } @@ -3399,8 +3431,8 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) MidiTime::ticksPerBar(); int w = ( ( ( sel_pos_end - m_currentPosition ) * m_ppb ) / MidiTime::ticksPerBar() ) - x; - int y = (int) y_base - sel_key_start * KEY_LINE_HEIGHT; - int h = (int) y_base - sel_key_end * KEY_LINE_HEIGHT - y; + int y = (int) y_base - sel_key_start * m_keyLineHeight; + int h = (int) y_base - sel_key_end * m_keyLineHeight - y; p.setPen( selectedNoteColor() ); p.setBrush( Qt::NoBrush ); p.drawRect( x + WHITE_KEY_WIDTH, y, w, h ); @@ -3426,8 +3458,8 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) if( hasValidPattern() ) { int key_num = getKey( mapFromGlobal( QCursor::pos() ).y() ); - p.fillRect( 10, keyAreaBottom() + 3 - KEY_LINE_HEIGHT * - ( key_num - m_startKey + 1 ), width() - 10, KEY_LINE_HEIGHT - 7, currentKeyCol ); + p.fillRect( 10, keyAreaBottom() + 3 - m_keyLineHeight * + ( key_num - m_startKey + 1 ), width() - 10, m_keyLineHeight - 7, currentKeyCol ); } // bar to resize note edit area @@ -3475,23 +3507,7 @@ void PianoRoll::resizeEvent(QResizeEvent * re) SCROLLBAR_SIZE, width()-WHITE_KEY_WIDTH, SCROLLBAR_SIZE ); - m_topBottomScroll->setGeometry( width() - SCROLLBAR_SIZE, PR_TOP_MARGIN, - SCROLLBAR_SIZE, - height() - PR_TOP_MARGIN - - SCROLLBAR_SIZE ); - - int total_pixels = OCTAVE_HEIGHT * NumOctaves - ( height() - - PR_TOP_MARGIN - PR_BOTTOM_MARGIN - - m_notesEditHeight ); - m_totalKeysToScroll = total_pixels * KeysPerOctave / OCTAVE_HEIGHT; - - m_topBottomScroll->setRange( 0, m_totalKeysToScroll ); - - if( m_startKey > m_totalKeysToScroll ) - { - m_startKey = m_totalKeysToScroll; - } - m_topBottomScroll->setValue( m_totalKeysToScroll - m_startKey ); + updateYScroll(); Engine::getSong()->getPlayPos( Song::Mode_PlayPattern ).m_timeLine->setFixedWidth( width() ); @@ -3664,7 +3680,7 @@ int PianoRoll::getKey(int y ) const { int key_line_y = keyAreaBottom() - 1; // pressed key on piano - int key_num = ( key_line_y - y ) / KEY_LINE_HEIGHT; + int key_num = ( key_line_y - y ) / m_keyLineHeight; key_num += m_startKey; // some range-checking-stuff @@ -4039,6 +4055,28 @@ void PianoRoll::enterValue( NoteVector* nv ) } +void PianoRoll::updateYScroll() +{ + m_topBottomScroll->setGeometry(width() - SCROLLBAR_SIZE, PR_TOP_MARGIN, + SCROLLBAR_SIZE, + height() - PR_TOP_MARGIN - + SCROLLBAR_SIZE); + + int total_pixels = m_octaveHeight * NumOctaves - (height() - + PR_TOP_MARGIN - PR_BOTTOM_MARGIN - + m_notesEditHeight); + m_totalKeysToScroll = total_pixels * KeysPerOctave / m_octaveHeight; + + m_topBottomScroll->setRange(0, m_totalKeysToScroll); + + if(m_startKey > m_totalKeysToScroll) + { + m_startKey = m_totalKeysToScroll; + } + m_topBottomScroll->setValue(m_totalKeysToScroll - m_startKey); +} + + void PianoRoll::copyToClipboard( const NoteVector & notes ) const { DataFile dataFile( DataFile::ClipboardData ); @@ -4279,6 +4317,17 @@ void PianoRoll::zoomingChanged() } +void PianoRoll::zoomingYChanged() +{ + m_keyLineHeight = m_zoomYLevels[m_zoomingYModel.value()] * DEFAULT_KEY_LINE_HEIGHT; + m_octaveHeight = m_keyLineHeight * KeysPerOctave; + m_whiteKeySmallHeight = round(m_keyLineHeight * 1.5); + m_whiteKeyBigHeight = m_keyLineHeight * 2; + m_blackKeyHeight = round(m_keyLineHeight * 1.3333); + + updateYScroll(); + update(); +} void PianoRoll::quantizeChanged() @@ -4501,13 +4550,21 @@ PianoRollWindow::PianoRollWindow() : DropToolBar *zoomAndNotesToolBar = addDropToolBarToTop( tr( "Zoom and note controls" ) ); QLabel * zoom_lbl = new QLabel( m_toolBar ); - zoom_lbl->setPixmap( embed::getIconPixmap( "zoom" ) ); + zoom_lbl->setPixmap( embed::getIconPixmap( "zoom_x" ) ); m_zoomingComboBox = new ComboBox( m_toolBar ); m_zoomingComboBox->setModel( &m_editor->m_zoomingModel ); m_zoomingComboBox->setFixedSize( 64, 22 ); m_zoomingComboBox->setToolTip( tr( "Horizontal zooming") ); + QLabel * zoom_y_lbl = new QLabel(m_toolBar); + zoom_y_lbl->setPixmap(embed::getIconPixmap("zoom_y")); + + m_zoomingYComboBox = new ComboBox(m_toolBar); + m_zoomingYComboBox->setModel(&m_editor->m_zoomingYModel); + m_zoomingYComboBox->setFixedSize(64, 22); + m_zoomingYComboBox->setToolTip(tr("Vertical zooming")); + // setup quantize-stuff QLabel * quantize_lbl = new QLabel( m_toolBar ); quantize_lbl->setPixmap( embed::getIconPixmap( "quantize" ) ); @@ -4555,6 +4612,9 @@ PianoRollWindow::PianoRollWindow() : zoomAndNotesToolBar->addWidget( zoom_lbl ); zoomAndNotesToolBar->addWidget( m_zoomingComboBox ); + zoomAndNotesToolBar->addWidget(zoom_y_lbl); + zoomAndNotesToolBar->addWidget(m_zoomingYComboBox); + zoomAndNotesToolBar->addSeparator(); zoomAndNotesToolBar->addWidget( quantize_lbl ); zoomAndNotesToolBar->addWidget( m_quantizeComboBox ); From ae2af96c9ad46268996559f5da91d010053caddc Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 30 Apr 2020 18:06:06 +0200 Subject: [PATCH 156/160] Use nullptr instead of NULL Requested by Veratil --- include/Track.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Track.h b/include/Track.h index c08cfd4e3..29ed1f699 100644 --- a/include/Track.h +++ b/include/Track.h @@ -742,7 +742,7 @@ private: virtual FadeButton * getActivityIndicator() { - return NULL; + return nullptr; } friend class TrackLabelButton; From aaf94ef69dab60b016044671f4036d603b836e28 Mon Sep 17 00:00:00 2001 From: Spekular Date: Thu, 30 Apr 2020 22:07:00 +0200 Subject: [PATCH 157/160] Formatting chananges --- src/core/Track.cpp | 11 ++++++----- src/tracks/SampleTrack.cpp | 37 +++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/core/Track.cpp b/src/core/Track.cpp index de01457bb..2711f9944 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -3066,13 +3066,14 @@ void TrackView::muteChanged() if (m_track->m_mutedModel.value()) { actind->setActiveColor(QApplication::palette().color( - QPalette::Active, - QPalette::Highlight)); - } else + QPalette::Active, QPalette::Highlight + )); + } + else { actind->setActiveColor(QApplication::palette().color( - QPalette::Active, - QPalette::BrightText)); + QPalette::Active, QPalette::BrightText + )); } } } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 060018656..8d2ca8aba 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -585,21 +585,19 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) -SampleTrack::SampleTrack( TrackContainer* tc ) : - Track( Track::SampleTrack, tc ), - m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 0.1f, this, - tr( "Volume" ) ), - m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, - this, tr( "Panning" ) ), - m_effectChannelModel( 0, 0, 0, this, tr( "FX channel" ) ), - m_audioPort( tr( "Sample track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ), +SampleTrack::SampleTrack(TrackContainer* tc) : + Track(Track::SampleTrack, tc), + m_volumeModel(DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr("Volume")), + m_panningModel(DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr("Panning")), + m_effectChannelModel(0, 0, 0, this, tr("FX channel")), + m_audioPort(tr("Sample track"), true, &m_volumeModel, &m_panningModel, &m_mutedModel), m_isPlaying(false) { - setName( tr( "Sample track" ) ); - m_panningModel.setCenterValue( DefaultPanning ); - m_effectChannelModel.setRange( 0, Engine::fxMixer()->numChannels()-1, 1); + setName(tr("Sample track")); + m_panningModel.setCenterValue(DefaultPanning); + m_effectChannelModel.setRange(0, Engine::fxMixer()->numChannels()-1, 1); - connect( &m_effectChannelModel, SIGNAL( dataChanged() ), this, SLOT( updateEffectChannel() ) ); + connect(&m_effectChannelModel, SIGNAL(dataChanged()), this, SLOT(updateEffectChannel())); } @@ -836,13 +834,12 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_panningKnob->setLabel( tr( "PAN" ) ); m_panningKnob->show(); - m_activityIndicator = new FadeButton(QApplication::palette().color(QPalette::Active, - QPalette::Background), - QApplication::palette().color(QPalette::Active, - QPalette::BrightText), - QApplication::palette().color(QPalette::Active, - QPalette::BrightText).darker(), - getTrackSettingsWidget()); + m_activityIndicator = new FadeButton( + QApplication::palette().color(QPalette::Active, QPalette::Background), + QApplication::palette().color(QPalette::Active, QPalette::BrightText), + QApplication::palette().color(QPalette::Active, QPalette::BrightText).darker(), + getTrackSettingsWidget() + ); m_activityIndicator->setGeometry(settingsWidgetWidth-2*24-11, 2, 8, 28); m_activityIndicator->show(); connect(_t, SIGNAL(playingChanged()), this, SLOT(updateIndicator())); @@ -860,7 +857,7 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : void SampleTrackView::updateIndicator() { - if (model()->isPlaying()) m_activityIndicator->activateOnce(); + if (model()->isPlaying()) { m_activityIndicator->activateOnce(); } else { m_activityIndicator->noteEnd(); } } From 0c180b8dc52327eddd1b3f4a55e22ba39d696db0 Mon Sep 17 00:00:00 2001 From: Spekular Date: Fri, 1 May 2020 12:27:41 +0200 Subject: [PATCH 158/160] Nicer spacing in activity indicator's setGeometry call Co-authored-by: Kevin Zander --- src/tracks/SampleTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 8d2ca8aba..f377e35cf 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -840,7 +840,7 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : QApplication::palette().color(QPalette::Active, QPalette::BrightText).darker(), getTrackSettingsWidget() ); - m_activityIndicator->setGeometry(settingsWidgetWidth-2*24-11, 2, 8, 28); + m_activityIndicator->setGeometry(settingsWidgetWidth - 2 * 24 - 11, 2, 8, 28); m_activityIndicator->show(); connect(_t, SIGNAL(playingChanged()), this, SLOT(updateIndicator())); connect(Engine::getSong(), SIGNAL(stopped()), From 9ed5f80fe003e3ea21e3710c3dafd83d1fb1e701 Mon Sep 17 00:00:00 2001 From: Spekular Date: Fri, 1 May 2020 20:03:27 +0200 Subject: [PATCH 159/160] Refactor palette update on un/mute --- include/Track.h | 4 +++- src/core/Track.cpp | 27 +++++++++++---------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/include/Track.h b/include/Track.h index 29ed1f699..07cee8ffd 100644 --- a/include/Track.h +++ b/include/Track.h @@ -745,12 +745,14 @@ private: return nullptr; } + void setIndicatorMute(FadeButton* indicator, bool muted); + friend class TrackLabelButton; private slots: void createTCOView( TrackContentObject * tco ); - void muteChanged (); + void muteChanged(); } ; diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 2711f9944..f6b109280 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -3060,20 +3060,15 @@ void TrackView::createTCOView( TrackContentObject * tco ) void TrackView::muteChanged() { - FadeButton * actind = getActivityIndicator(); - if (actind) - { - if (m_track->m_mutedModel.value()) - { - actind->setActiveColor(QApplication::palette().color( - QPalette::Active, QPalette::Highlight - )); - } - else - { - actind->setActiveColor(QApplication::palette().color( - QPalette::Active, QPalette::BrightText - )); - } - } + FadeButton * indicator = getActivityIndicator(); + if (indicator) { setIndicatorMute(indicator, m_track->m_mutedModel.value()); } +} + + + + +void TrackView::setIndicatorMute(FadeButton* indicator, bool muted) +{ + QPalette::ColorRole role = muted ? QPalette::Highlight : QPalette::BrightText; + indicator->setActiveColor(QApplication::palette().color(QPalette::Active, role)); } From c18edd4cefff7517fbb4d011e19508ec51315ca9 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sun, 3 May 2020 12:44:30 +0900 Subject: [PATCH 160/160] Use local cursor instead of global one in PianoRoll (#5200) Fixes stuck vertical cursor as well. --- src/gui/editors/PianoRoll.cpp | 52 +++++++++++------------------------ 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 510fc800e..0eb5df163 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1412,7 +1412,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) { m_ctrlMode = m_editMode; m_editMode = ModeSelect; - QApplication::changeOverrideCursor( Qt::ArrowCursor ); + setCursor( Qt::ArrowCursor ); ke->accept(); } break; @@ -1466,11 +1466,6 @@ void PianoRoll::keyReleaseEvent(QKeyEvent* ke ) void PianoRoll::leaveEvent(QEvent * e ) { - while( QApplication::overrideCursor() != NULL ) - { - QApplication::restoreOverrideCursor(); - } - QWidget::leaveEvent( e ); s_textFloat->hide(); update(); // cleaning inner mouse-related graphics @@ -1560,7 +1555,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) { m_ctrlMode = m_editMode; m_editMode = ModeSelect; - QApplication::changeOverrideCursor( QCursor( Qt::ArrowCursor ) ); + setCursor( Qt::ArrowCursor ); update(); } @@ -1778,8 +1773,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) m_action = ActionResizeNote; // set resize-cursor - QCursor c( Qt::SizeHorCursor ); - QApplication::setOverrideCursor( c ); + setCursor( Qt::SizeHorCursor ); } else { @@ -1792,8 +1786,7 @@ void PianoRoll::mousePressEvent(QMouseEvent * me ) m_action = ActionMoveNote; // set move-cursor - QCursor c( Qt::SizeAllCursor ); - QApplication::setOverrideCursor( c ); + setCursor( Qt::SizeAllCursor ); // if they're holding shift, copy all selected notes if( ! is_new_note && me->modifiers() & Qt::ShiftModifier ) @@ -2225,7 +2218,7 @@ void PianoRoll::mouseReleaseEvent( QMouseEvent * me ) if( m_editMode == ModeDraw ) { - QApplication::restoreOverrideCursor(); + setCursor( Qt::ArrowCursor ); } if( mustRepaint ) @@ -2249,8 +2242,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) { if( me->y() > keyAreaBottom() && me->y() < noteEditTop() ) { - QApplication::setOverrideCursor( - QCursor( Qt::SizeVerCursor ) ); + setCursor( Qt::SizeVerCursor ); return; } } @@ -2463,37 +2455,19 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) bool atTail = note->length() > 0 && x > noteRightX - RESIZE_AREA_WIDTH; Qt::CursorShape cursorShape = atTail ? Qt::SizeHorCursor : - Qt::SizeAllCursor; - if( QApplication::overrideCursor() ) - { - if( QApplication::overrideCursor()->shape() != cursorShape ) - { - while( QApplication::overrideCursor() != NULL ) - { - QApplication::restoreOverrideCursor(); - } - QApplication::setOverrideCursor(QCursor(cursorShape)); - } - } - else - { - QApplication::setOverrideCursor(QCursor(cursorShape)); - } + Qt::SizeAllCursor; + setCursor( cursorShape ); } else { // the cursor is over no note, so restore cursor - while( QApplication::overrideCursor() != NULL ) - { - QApplication::restoreOverrideCursor(); - } + setCursor( Qt::ArrowCursor ); } } else if( me->buttons() & Qt::LeftButton && m_editMode == ModeSelect && m_action == ActionSelectNotes ) { - // change size of selection // get tick in which the cursor is posated @@ -2565,6 +2539,12 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } } } + else if (me->buttons() == Qt::NoButton && m_editMode != ModeDraw) + { + // Is needed to restore cursor when it previously was set to + // Qt::SizeVerCursor (between keyAreaBottom and noteEditTop) + setCursor( Qt::ArrowCursor ); + } } else { @@ -2643,7 +2623,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) --m_selectedKeys; } } - QApplication::restoreOverrideCursor(); + setCursor( Qt::ArrowCursor ); } m_lastMouseX = me->x();

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�F>nWc;KI~6AD!}HbH{Wr z<^A>p|MlB{q*H(nP7dc-x`kRwELMG)cRD#`;tdg9%Yi#3r~($@n$zPG;xNR-B#k0e zSq51q%471h;KTMkqUkFj+p_4omZm5;J~?DG9HHw5s-m)4ZMi(TV(1OYvaH`glb3X& zr^#H`CddP_EN8J=P<0iCp<_B0WnD6|M!Z|SC2tE9S>t|rho)-Ob;B}Ta4?vWMll~A z?~vpU$8phBohB26`;fY+8Q30{VYApTn2+ZS>>*`V^XAnToX*dASl<(65tS%Os{~cm zcw9V^l^L?6pjr~6gAqkrunl|9TalLx2Saq(;{ED9+h9Xlq~uM>e0)rlguH(Fn*Z^S z|4LdUJiEL>X=UoX=Kbm&mxmWDgC*9$rc?WB!`3XCM&M`t1|T6d47>qK)3FFwh^|AE zHI~r^NtSxwp%9!859vPkz7F(3PXrbPn>1iF7?6AfwRF)^mN{jXlT{_6>rj4o^X8Ag zY#+FO_KeANiXZtL9UarOO&=mDa$dc7N!2#I-#l=BbkaZQw6v9=QzeFu$A0fqwKWui zqHAb7iQ#yNND7L&(D!eG|yNk`~QSvPUdr>I>YS@R7GKz z_&j^|2~p8!rA6B#xOtM{rxBlj{yB;yePJvM?nnGDePvUq;xvO_f)qWr|@K)J=lk5M6`*x|Jd{rqQNgCblpIcdi$*HcszUZjBFLN2$ua4M#Df90wEgOrbCq#qAXWb!tpEDDCsn4Q1FHXU4d zz%LKK^by&j#Ifvt8KlvnX*yM25~nd$Q=uyws;Z%(6GZ_sGNxfslqFf75fu@YsJNJ) zGn*a}tU{``BCBI8%jVb5zvbuqpZVm)3*sa~myJG~jzL}2-+%KhMU`V47Ng0K`Qg0BzVj4Y zbyx>0il(6H1jpk;cH2E=nX?afNQ#VR8_dQNcH2E!nKE<7gh|N3c*J78!qH8#G$qVa uPLGawxVuGGB&xDTQB@>KMnhr~2mEjNPV$FS65jd%0000w$$WEky|T&0ftFLK~zY`)s{bN!cY{&e|bKe;9#p? zK*&}olrDm}gbq^7(vRdfs*4VtI=DInDeWdA5?qP~ihuIwbx5jG(pJ&IoavE!b92wT z=e!pUVAbi|0xjSkD3TqtftGIDj+T&k0j|g=F|Sr(#DVBPD1=WTL!5sXGeudlz>_*owy3PV^d2*+d8a0sOm7qwa(^L^BOPB<9A zVv*MBy*!mC(i%PFU4VSGO8)v9f4M}tF6(X=r4*u4A%AlNrb(D=&<_;Fx#&X+0-}$P zczt=f4HyPF(~KvR#8cI?&qwq|!vntqOyz@1uY^X zlbVK5nw%1){CO>gdA*-|r-9n!S-pGCcfNDaJ?EbLNW85ls3k&x3QPT7ZmmN_X{JlkGjq6f<1yZToWiQ04@V;PC@b>vTT51)!m<%UY<8>8 z<5F;NSJ;Fx+IbvC$@)(uQ@B6+8Uw?B&(If-006Gt8pU`rg_g!GxH<9?19zXIx3iU> z2xY4H5;-KuiqlkH>X1O0s;^hLRWxm{$;rWsVfEKE4G&+8BQZIx|6bKPM(I zGy4N;%L;XTPv-#uz^4!IG5viG6{UVI7X<)^tUw|Xmb4`sz|`MFLhQ^{WPMDSuD$vx zG1XqC0DxorcW}8f6;Z^er80`*iYi-67=2VmQM_hT z&U=lC2&xG`|z1xSVV1A2-V?w@jF6gX6rfQCaBEpmz^m(erE0` zo{v2;VXAAwaJs!*d(dBi%BpR;zse|rp^d^34gl-Q0|@v$w!P~2766#jllwF+c0veb z)vd3;ew#)2#d{{q?emrJ2Fkeh=GqN78*a)O!z#lMULjQCM@I)AN8)Wq4O+DQ(C-I_ zNYIuRO+oT`z)X4r$G_wiVXhBe5shD2FkScqfiH5Ny8L+mgdxl2r|}n~M*xs^XF{_8 O0000|W` z)05%g!Dx&Z2t5&=z(?YZs70gdFK2{d%mv{NyOC*YK1VgS@c+=--;AkMv^#N)IQ7nKS^2%P6qz} hFgIzP0BX8v+CPa&s3c`Q-x2@-002ovPDHLkV1gvJ)k6RP delta 698 zcmV;r0!96$1f~X%Q3~M!3KS*_3^Mvtky|T&0*XmQK~zY`-B!Apis&M%`7Yyw<$74j`lce@M`e*Z$5v;mv28Te5G2&(2;ITCF59H%VDFSTeWLOQzNQO zZ5NT(i@5mYRZE=Z12${6YBb|{n{{_$5L6K0_xmAm4xe(hhGe7z zC*x83dqj|A5JF%W26v7HNp6XMiL%SWe=n7h{_v>*b1D{r;JXLUioSK1EMt4MZ(f4S^xk507*qoM6N<$f?7XDOaK4? diff --git a/plugins/Xpressive/logo.png b/plugins/Xpressive/logo.png index 68b613176d684bd586ceb10ad2053fcf8309e7d6..555c3f13e682a44b0ed0ebecbb233f5a47470bd0 100644 GIT binary patch literal 1884 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 diff --git a/plugins/Xpressive/o1_inactive.png b/plugins/Xpressive/o1_inactive.png index 0fd4f8b2989d027df7529e31e3c28a34d7690230..7a6b603e3e79540dc6c86a90980343dcc63c57dc 100644 GIT binary patch delta 471 zcmV;|0Vw|b1AqjO85#xv007MxCy4+600v@9M??Vs0RI60puMM)ks%&`;|dB37y$Q2 zOz{8!03B&mSad^gZEa<4bN~PV002XBWnpw>WFU8GbZ8()Nlj2>E@cM*00Cr4L_t(I z%cYY&io#G7g}+3>pNIrR5V26u`e7w#BXI+>2f+>K00T>R4oKB~|>9RV2<2Wd#Fbo5K%d$wP(^RWf>h(H7 z5U^Y>Bh9J delta 493 zcmVeSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00DMM zL_t(2&z({&%K||Vo?S%^R+GpbDkeejFNjS9llg)MIjhJue}BVS#Bg9T8T1br44W(p zqQzhmlNW^9)!R3$tRL@-;5%WNdEl96cIF|LWx>B93;>Nr;|(Q*NGU(ihQnbP0E7_F zsFacrq7&VMQmS(GKA6YjaWELHRx2SyE|+UIo7rskxufg4%jMGZyvMYhbKmz{t=8po zsZ=VlSZp?%*?+d}x~^duQp){)&lsCdr^#fJbN2x|CkMDLnL?ndt`+WeY*XvSBA;il+RUw4X z+oF^PL2$iZ-|`8Iw4Y1##^_%;|)00000NkvXXu0mjfSBBe- diff --git a/plugins/Xpressive/o2_active.png b/plugins/Xpressive/o2_active.png index 51897412d42493bbe5e5ee71851fe6bee62c6136..4e3b5f214d72036360dec9be83298432cda3d78c 100644 GIT binary patch delta 583 zcmV-N0=WIt1&sxeQGeqK3JW6@rYJ`u0000TX;fHrLvL+uWo~o;00000Lvm$dbY)~9 zcWHEJAV*0}P-HG;2LJ#8@kvBMR5;6}lTS#KVI0Rl&%2D})>sr7jag}EK{~Lo3~|GH zAx4lbg1VTopi4xT==xI8!RSzV5_Yl(>7e0t2Va6h55|Tarhnoe-X`T@y<3}(YI{5E z-QZSY#Aj6W^aeZajkF&3A%_Opcfdm?o#N^Jn*azQaJu&3JL5;bGK9@$1LzkK zRWks_ojv4-&uOHJDfCm4gru_|581d0FtXq05lzJ zt|$ZGX>J7|xw2$|tuB1#Lv#uu1RbaQtguQ9AuL6PVSm*8bUO8w*CUTHN<}UY-KFJN zrxoToe6*~bT%%YlRxj7J<+9Y*oKE01(Z*Etz z|HiWi;XoQdTgNE|?k^R9o%0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJ zAV*0}P*;Ht7XSbPJ4r-AR5;76lUqnsVHAbGf6n>O@rX!K69y@0I^MFtK#WR^$|$sy z$bw1{B+7>nB7=G>ib%+^z*4$s5|oyLP-f5+g4uBQ!W~)BD+tciKxtSTs55`Gkk91i&*n!Tpv>+Zentzm}bT+5#VN2Zi)V6zFwX!BrfQi4+|;5ol8jQi+X90KnyLLpLP4A?X|JWPDw;q;rv+gomH)e6RfM+isQ&C&W;K%^FZqW3!4nWFU8GbZ8()Nlj2>E@cM*00FT{L_t(I z%cYaQisEn-g}>;^_>VylMZ`kElp&}^L>n=MHp13EfY0G0h!3Hp2s=+8i=9^f&@QA9 zL<@_U0SSV^ZPXo+Ep|MA<-OeRo&)zv073{A=mSmQo&VDZ&{tJeNLSErAi?)_02+a- zqgOy&=legd(Kw&aOs7*;s}+aCfp9oXKA%U|b&|;>uIn-w4DQqMcpODh&~=?)FbL3; zgb){i(P)HaS=4GZ>h(I??H0o@2!%rQdOZ?}zvey9!!%87+osijYEi9L0SEwWHXAI< zA{L9$Y&MBTqZErpN~IE>=P{ej9x_ED5z^^20Q>#^UJn3lw_5;GsnpFf0GUjNul<|W z>oxQF8~{a89%28IBuP&-$8j(W1K;=QbUNho`A1kbo4s51egEFv?RI?apH3&n<1wD+ z(QdaXm&*@}0sy&xTn<^5IUbKpCKEi*!?tY}iv^KL1WnVpTrQZV$$r15QmLS6+H(>i zgbTdiPGh-T;<_$xZ*LR|h5KnZj>B*`d;-X_Ot;$w_>_bYzkr{-u8-RX99{$+fPO$# mRRJ`v5BvEYM?gbWRq+R9i>I-$HFU)Q0000eSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00GoV zL_t(2&z+Lb%P~0rpl%eq>>#tR)sx zGbO80GB(OW%7#ey-ZAqo?i+7!Z%;k%S=>(N-p{@Fd(M~1WHRtyVFZ9&F85cJVHjQ4 zzri}4ju8L|A%9kNU1u2PoxHDLS(fH>{gCJLxz%dPvP=kZyWRPG-syA#!2N!2Hk+%} zO4Bs2*IO!;tbbN30I&dnF}_?b#bQwu#c((rjYbtkDVNLp{T^f7Znx+2c`z7EBocxk zG#U+r(1#q4$Jgt%P$;C+>0~lluh&&o?e%&X(@GTw5 aPvaL|eiog_CX>@2HM@dakWG-a~00059 zNkl+ORA2PcP^nuYnsqFVdGx2+8>E-%nu>qyb)D~yiI`mglHmgJpXwAyCSrEs23IbOnQuR_k^lez07*qoM6N<$0fGfI?ScRR delta 650 zcmV;50(Je)1g!-jiBL{Q4GJ0x0000DNk~Le0000K0000E2nGNE058Nm@R1=ff8Yxh z6C@*tXsTlX000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0006NNkl3!Y+9-q&xUNT52myfWx*MMVjOtndzy>uVV;49xd<<#8IErhd=Qw@kD4)N7 zz&MJ>6E`?@`3TEvxp5Su6F0ed=Oi|$F^>8TsEwjCoMN@ILg`}xr6u#le>4I?{{1Z4 zDDtIQLfcws4(()dX`bhk51DxN6d}BRZK?sSB)RVZ0Qqv7r7tDE{rE~E+DooDO+Z7T zlqC`AL0iey2j_V*eis){s zu3n^+WD99x;Z7PhHPF@?A)R|iDsj+fvVPw~1Y@V!Z24YJ` zL;(K){{a7>y{D6rAs&C@3JMDl3g1gF=Kufz9cffpbVF}#ZDnqB0000007G(RVRU6= zAa`kWXdp*PO;BVmWd{HN0gg#TK~y-)t(3j0;$RSlpCre^1a%dHn#9h2iC3WBfTy*w z*4i8JA_OnN&dnXTy^vsMBNpZp(nL|QkYKhkVb2eoKi_VKnP-3A8TdX4!1KJ{zzApq zdA=_P7}>VHl$cuX9}^m;vX zU1z;s<2VlWdYw+E!*n`jx7($(!C-)@s!7j^E?}lG<*!%WybE z)3o=8^$P?MIRUR8^%^Dsj172*VKH_o-H^SeAtlf+&gz zf`CjWgRbkJ2mim~cDp4A0xZj-R;zJ7pINO|+-^52l?sZYd<~qe=(;XZ6k(cXI&866 zeCZ4TPp=(^VThtAl*?rlMWI+M5{BW6n$LodK)z5Ykjq@3zGCTN;QzuyNqN}lKa0iJoRoaE&l9tb@EBiXj?CD6QoAjh9N1)8>P aFaH63d6FP-C7pf%0000#7ev>NpPhZM_jf zrqd}RerZrj|GxZOtJUg_5F(}g(m+H?X)NMj7-L%NlMzAyU_PG@hr`$F1pu?zY%myv zVF&=O>-PKoFbtJaN-5WMCzA;PD5WA;O4;djlv12?A%ySyl+yivuXQe$OC%DU^YwbY z*=!I|N-2bhJWDBU+a`o?&P~&NKA+ug*YmtyuNMTtXfy(V#bWVzJoLh`%?M5=lc`k7 z^E}fuv)OF3+1%}R=kr-9)oQh*l*{Fk5HcQ*5fK3X@T`=wZToV$oKB}orBW`JgCO9X z8-`J@*YyMKQ%?qf2o^#JAuP+f-|xrcu~aH$G8shNZnup_BYvQLB4Vtg$pBER)fi*9 z+by5ZBVwUYxL&W8Wx=P?aI8OI##pslH4G!2P6I%(SR{n#o&N_@$8p;2cAScDq7r9A n$L4T2{5xSk$hPnMzk}ZaIqgR7(&?4!00000NkvXXu0mjfg1_kQ diff --git a/plugins/Xpressive/w2_active.png b/plugins/Xpressive/w2_active.png index cd4729949e171992a25d089f90a5180649a20d74..17d7b53d935a12a59d14d7b253e0a3ace613f751 100644 GIT binary patch delta 680 zcmV;Z0$2V115123j0013nR9JLFZ*6U5Zgc_CX>@2HM@dakWG-a~0006n zNkl|{}T@(U$L?8{m^2Me+bA#@@_f!KoGC3WM1pzghrcA?dc zP+m%2MBQeaW++|c2VN<`D34(ce?i@_scUVdve~hVoekFRYX6%T{yhBnKhN{NuK+L` z4_*TNKnEc6?+O5ZceN)Z7|I;>2ynVwrlHPEGWC;4EXqn)!)0@!I#jeZjdUhW zc;g$#%T8c3+Y7*2SYvYTe=Pz6t(RL1!2qfwoBGpgh}HG4C=M0noJu^g%iPC#4u2ow zv^sM$1Ym3DJCC0~Lf3Wf-+DmP`KDs9;>gZT z43PSjqSx!Av!%136X|boG#25QREGVm1IZ-OP-i9@i)0sP)abg-e$a~{Hn!HecJ(@4 zZCyooP6CpETwha9O=T^+i5;9aCz46Zr;NrTh@!{^n-hTTAKNS~F99&UFvax36adZ2 z75eY>10;pnc<>EylhP`XdwGcxLKA>rbXR*qK)dmQB>y)l(C)7Gg#G~h+`ll)ZtaQy O0000_CX>@2HM@dakSAh-}0007JNkl1wU|D69<%TcqGgiH}<9H&tkHMImGqAQgx$~J+Liy#oeMbRRnb}bAf zh!kuQghC)j#B{-w!b`=7XkNzge=?3TIDtjjjhy*gj7}N`E&NvJ@O|gwd0$?RQeRPL zu!$uAl6RJD%CdmJFT7A{0!2|Uj4-UCC;%9SvEljeTH6xKTYe+Qh03d3Wf6$&)NHDa_ z!lyZeRGIaAP*CuFoJ48@-@+ux+fwNrdcnJqZVGH=9NmAGL3bYyo;D&iv1XyJlAp5| z0AIkv;?e@&e|{w|yBM$EO_YSWg&FcJMJ#>^kYGxp@>mTg4pb5wy_H$N2dS&;CGXyq z!S=)yy#5JHaVAoe)5xJ1uJF%JbF~!Fb3Ik8x zOA2y}QB{?hISJF#10$xOH4@s3fLw-2ca+McxF>}toHVZ~~;)8l$YcfS)&m+L(Y zlg4AWIv5@sAlqz3N{P*q%lPC6N{hV!Z24YJ` zL;(K){{a7>y{D6rAs&C@3JMDn92u$jGynhq9cffpbVF}#ZDnqB0000007G(RVRU6= zAa`kWXdp*PO;BVmWd{HN0s2WqK~y-)t(479;!qfdpHfVe&^j0zq^%|vNo}7 z+)lgzZ-N`(BHVcq(->Vldtw_|vB3?ei!Q9B5+zN;nZ-~t47-0B-sQ>nJ?DGh^P>QS zVR#PofDWMZ@A843<2WzM0{Rp9#%44DE=!9^Hw9(E>Xm`c)6)|{5a4+puIrLz8Mn8$ zNGZ9wxxp|DMxznF?{j>7jOTgW-Q58orKDP|a(a5o#l;1xsseP%fNoh9K@cE>z;#`s zC_+jJfDi)1Fo=Jm2ms5n&~=?|w+p~*Hsj&pf#>IEG)<%3ZUg8g0Nb_!md#NVv9q(Y z0u(~@w=HTFf>2%6)IAni5pR>QeUu5|E_$L$Y@9&XP za&>jZ;o;#rxUd)m0ePNdS=Ms1e!ssKLzZRuzR%a!7oO+QYPD9Jl>eSMNfK05rO{}h zsw%ZwZJAwBBc+5TPR0+fI2k@QE$}e8tWP{KN&?`BP^8#EhK9KZ( fCj~AY$9ee$YuLwPkeYGq00000NkvXXu0mjf8fzlE literal 628 zcmV-)0*n2LP)C$482~`UdcB^@<*wH& zB0fJqvn+c&9ucwKZfCPuK@ghFCdYC4e7@0WoK7c2QND_0SxzRCilT_3c)Q(}%OxRX zHk+y1ZnwX^y$uF~OeRw(6h@;_tJS*QZja)4JPrV&DDLr^?L2~dOXjwEL$uVpPrr`o)Hld{eC~gFo(mz z;c!q&old7DN#Sr95&Qi<&+~4#+wFEwCX?ZC_~@)EhGBd@-)6J1SS*O>a=CW9ojUV; zJ_i6vlDgfl`r)Zm3IIr@Qi;W4cPjpY%AE}ro5f=Bcf$UI?d5X$v*KUhryjt9k`j*q O0000_CX>@2HM@dakWG-a~0006d zNkl<%?`f4B)gTr%k%Jie!mZ2zQ5;rGysX} z;$nVca0MB1O10eG;!$fp~(4!Fd9^WN88)g6deh2Os0KIINe*wrB^8ge+ zec-Tl*p=(7F1s6mWID-%I}eFHjo~=wWaV9gcw(so)(Zf#vt67k=2S@kiGFNW`w_70 zbOA7admO8IkQOa88VvxaO=na;M1WH#^$eP=lq)4ll`@559+%C9*=S)cyGEngp!j8r zW4hzmtai0|Y;ugfdKG}7e@j;w9vW89gwDq8Xsdp|kzwQWCbGkge8Ek%w#!O-m4n|0 zNarO)tyXQGnVKOsHH*jXA+`RNx%k{su+u1w{k7O_#oyp^r9_{o!+zF*s1;R<09;B+Ww3G=xDMzo zCJQFR0K2tsNHz&kE2=8Xl@fv=aM30KAlW1e+XW_HJYlz9Wx!x!^!hDA*G4_CX>@2HM@dakSAh-}0007ONkl4TBaws{=3R;d##c<4Dz|l{y;uh8GC^ z2%rd=p`aHD3IEqVgsR{V^%1{0fw74oEgcOM<{skUo-%p{-{XE#hfr4=YBG+D-2nJU zy?mbzG52$Z>~t4{!5*Rn1VTQtEqNGCMv4ncDcN66QQiRngb)Z#URjD}6ztrQ%$7}C z84UIk8xu=Hd?MLaCoenOe;FMQu(0?Whs}i}!->P@P$T>8lSX)^$ zsN~r4>GyjW81BKGWW|!2$-rFefX`5A<&F#l_od(HJsPLE?Wn3$QJBExJ))x_F2 zTuK^8mNkbDpSws;vm%7RX0hYx>!u)YFPc%Xudo>Z$N-Pp+|15RlWWhX^3=shLaDm6 zQZG4vA_Ar)Nrc1UHwd)L!2b{6O?T>-5-!JGk-Y8&DGKy3|C~iC-@jeEVV}bvfda_n TSmoI300000NkvXXu0mjfXQo6i diff --git a/plugins/Xpressive/w3_inactive.png b/plugins/Xpressive/w3_inactive.png index e5b0bc7d9e1c94ab1cbaaf9733c12fffc4a46489..d45d8b497ab51e777c96856de43f5b2c8f5930c8 100644 GIT binary patch delta 635 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 From df3e3883935fe71976f34ef97ce92f835985ccbf Mon Sep 17 00:00:00 2001 From: Andres Date: Tue, 19 Nov 2019 19:07:10 -0300 Subject: [PATCH 097/160] Fix triggering of stop animation when playing no samples after stopping song playing samples --- include/SampleTrack.h | 5 +++++ src/tracks/SampleTrack.cpp | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/SampleTrack.h b/include/SampleTrack.h index cad43a15d..4624e2a39 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -167,6 +167,11 @@ public: return m_wasPlaying; } + void setWasPlaying(bool wasPlaying) + { + m_wasPlaying = wasPlaying; + } + signals: void playing(); void notPlaying(); diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 83c8ba130..7d3bdb581 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -984,9 +984,11 @@ void SampleTrackView::dropEvent(QDropEvent *de) void SampleTrackView::stopPlaying() { - if (dynamic_cast(getTrack())->wasPlaying()) + SampleTrack * smpltrck = dynamic_cast(getTrack()); + if (smpltrck->wasPlaying()) { m_activityIndicator->notPlaying(); + smpltrck->setWasPlaying(false); } } From da73ddd242dd699bf32471c42662016b358c6e1e Mon Sep 17 00:00:00 2001 From: Martin Pavelek Date: Thu, 21 Nov 2019 14:44:18 +0100 Subject: [PATCH 098/160] Spectrum analyzer update (#5160) * advanced config: expose hidden constants to user screen * advanced config: add support for FFT window overlapping * waterfall: display at native resolution on high-DPI screens * waterfall: add cursor and improve label density * FFT: fix normalization so that 0 dBFS matches full-scale sinewave * FFT: decouple data acquisition from processing and display * FFT: separate lock for reallocation (to avoid some needless waiting) * moved ranges and other constants to a separate file * debug: better performance measurements * minor fixes * build the ringbuffer library as part of LMMS core --- .gitmodules | 3 + include/LocklessRingBuffer.h | 132 +++++ include/RingBuffer.h | 2 + include/lmms_constants.h | 43 ++ plugins/SpectrumAnalyzer/Analyzer.cpp | 51 +- plugins/SpectrumAnalyzer/Analyzer.h | 24 +- plugins/SpectrumAnalyzer/CMakeLists.txt | 4 +- plugins/SpectrumAnalyzer/DataprocLauncher.h | 52 ++ plugins/SpectrumAnalyzer/README.md | 34 +- plugins/SpectrumAnalyzer/SaControls.cpp | 46 +- plugins/SpectrumAnalyzer/SaControls.h | 71 +-- plugins/SpectrumAnalyzer/SaControlsDialog.cpp | 133 ++++- plugins/SpectrumAnalyzer/SaProcessor.cpp | 459 +++++++++++------- plugins/SpectrumAnalyzer/SaProcessor.h | 70 ++- plugins/SpectrumAnalyzer/SaSpectrumView.cpp | 127 ++--- plugins/SpectrumAnalyzer/SaSpectrumView.h | 17 +- plugins/SpectrumAnalyzer/SaWaterfallView.cpp | 207 ++++++-- plugins/SpectrumAnalyzer/SaWaterfallView.h | 26 +- plugins/SpectrumAnalyzer/advanced_off.svg | 243 ++++++++++ plugins/SpectrumAnalyzer/advanced_on.svg | 224 +++++++++ plugins/SpectrumAnalyzer/advanced_src.svg | 238 +++++++++ src/3rdparty/CMakeLists.txt | 19 + src/3rdparty/ringbuffer | 1 + src/CMakeLists.txt | 3 + src/core/CMakeLists.txt | 1 + src/core/fft_helpers.cpp | 1 + 26 files changed, 1867 insertions(+), 364 deletions(-) create mode 100644 include/LocklessRingBuffer.h create mode 100644 plugins/SpectrumAnalyzer/DataprocLauncher.h create mode 100644 plugins/SpectrumAnalyzer/advanced_off.svg create mode 100644 plugins/SpectrumAnalyzer/advanced_on.svg create mode 100644 plugins/SpectrumAnalyzer/advanced_src.svg create mode 160000 src/3rdparty/ringbuffer diff --git a/.gitmodules b/.gitmodules index 28d6c5d46..56b7f3eab 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,3 +34,6 @@ [submodule "doc/wiki"] path = doc/wiki url = https://github.com/lmms/lmms.wiki.git +[submodule "src/3rdparty/ringbuffer"] + path = src/3rdparty/ringbuffer + url = https://github.com/JohannesLorenz/ringbuffer.git diff --git a/include/LocklessRingBuffer.h b/include/LocklessRingBuffer.h new file mode 100644 index 000000000..3b18dd475 --- /dev/null +++ b/include/LocklessRingBuffer.h @@ -0,0 +1,132 @@ +/* + * LocklessRingBuffer.h - LMMS wrapper for a lockless ringbuffer library + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 LOCKLESSRINGBUFFER_H +#define LOCKLESSRINGBUFFER_H + +#include +#include + +#include "lmms_basics.h" +#include "lmms_export.h" +#include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h" + + +//! A convenience layer for a realtime-safe and thread-safe multi-reader ring buffer library. +template +class LocklessRingBufferBase +{ + template + friend class LocklessRingBufferReader; +public: + LocklessRingBufferBase(std::size_t sz) : m_buffer(sz) + { + m_buffer.touch(); // reserve storage space before realtime operation starts + } + ~LocklessRingBufferBase() {}; + + 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();} + +protected: + ringbuffer_t m_buffer; + QWaitCondition m_notifier; +}; + + +// The SampleFrameCopier is required because sampleFrame is just a two-element +// array and therefore does not have a copy constructor needed by std::copy. +class SampleFrameCopier +{ + const sampleFrame* m_src; +public: + SampleFrameCopier(const sampleFrame* src) : m_src(src) {} + void operator()(std::size_t src_offset, std::size_t count, sampleFrame* dest) + { + for (std::size_t i = src_offset; i < src_offset + count; i++, dest++) + { + (*dest)[0] = m_src[i][0]; + (*dest)[1] = m_src[i][1]; + } + } +}; + + +//! Standard ring buffer template for data types with copy constructor. +template +class LocklessRingBuffer : public LocklessRingBufferBase +{ +public: + LocklessRingBuffer(std::size_t sz) : LocklessRingBufferBase(sz) {}; + + std::size_t write(const sampleFrame *src, std::size_t cnt, bool notify = false) + { + std::size_t written = LocklessRingBufferBase::m_buffer.write(src, cnt); + // Let all waiting readers know new data are available. + if (notify) {LocklessRingBufferBase::m_notifier.wakeAll();} + return written; + } +}; + + +//! Specialized ring buffer template with write function modified to support sampleFrame. +template <> +class LocklessRingBuffer : public LocklessRingBufferBase +{ +public: + LocklessRingBuffer(std::size_t sz) : LocklessRingBufferBase(sz) {}; + + std::size_t write(const sampleFrame *src, std::size_t cnt, bool notify = false) + { + SampleFrameCopier copier(src); + std::size_t written = LocklessRingBufferBase::m_buffer.write_func(copier, cnt); + // Let all waiting readers know new data are available. + if (notify) {LocklessRingBufferBase::m_notifier.wakeAll();} + return written; + } +}; + + +//! Wrapper for lockless ringbuffer reader +template +class LocklessRingBufferReader : public ringbuffer_reader_t +{ +public: + LocklessRingBufferReader(LocklessRingBuffer &rb) : + ringbuffer_reader_t(rb.m_buffer), + m_notifier(&rb.m_notifier) {}; + + bool empty() const {return !this->read_space();} + void waitForData() + { + QMutex useless_lock; + m_notifier->wait(&useless_lock); + useless_lock.unlock(); + } +private: + QWaitCondition *m_notifier; +}; + +#endif //LOCKLESSRINGBUFFER_H diff --git a/include/RingBuffer.h b/include/RingBuffer.h index c761616bd..c7e91bd33 100644 --- a/include/RingBuffer.h +++ b/include/RingBuffer.h @@ -32,6 +32,8 @@ #include "lmms_math.h" #include "MemoryManager.h" +/** \brief A basic LMMS ring buffer for single-thread use. For thread and realtime safe alternative see LocklessRingBuffer. +*/ class LMMS_EXPORT RingBuffer : public QObject { Q_OBJECT diff --git a/include/lmms_constants.h b/include/lmms_constants.h index befa789dd..ae6d3d277 100644 --- a/include/lmms_constants.h +++ b/include/lmms_constants.h @@ -49,4 +49,47 @@ const float F_PI_SQR = (float) LD_PI_SQR; const float F_E = (float) LD_E; const float F_E_R = (float) LD_E_R; +// Frequency ranges (in Hz). +// Arbitrary low limit for logarithmic frequency scale; >1 Hz. +const int LOWEST_LOG_FREQ = 10; + +// Full range is defined by LOWEST_LOG_FREQ and current sample rate. +enum FREQUENCY_RANGES +{ + FRANGE_FULL = 0, + FRANGE_AUDIBLE, + FRANGE_BASS, + FRANGE_MIDS, + FRANGE_HIGH +}; + +const int FRANGE_AUDIBLE_START = 20; +const int FRANGE_AUDIBLE_END = 20000; +const int FRANGE_BASS_START = 20; +const int FRANGE_BASS_END = 300; +const int FRANGE_MIDS_START = 200; +const int FRANGE_MIDS_END = 5000; +const int FRANGE_HIGH_START = 4000; +const int FRANGE_HIGH_END = 20000; + +// Amplitude ranges (in dBFS). +// Reference: full scale sine wave (-1.0 to 1.0) is 0 dB. +// Doubling or halving the amplitude produces 3 dB difference. +enum AMPLITUDE_RANGES +{ + ARANGE_EXTENDED = 0, + ARANGE_AUDIBLE, + ARANGE_LOUD, + ARANGE_SILENT +}; + +const int ARANGE_EXTENDED_START = -80; +const int ARANGE_EXTENDED_END = 20; +const int ARANGE_AUDIBLE_START = -50; +const int ARANGE_AUDIBLE_END = 0; +const int ARANGE_LOUD_START = -30; +const int ARANGE_LOUD_END = 0; +const int ARANGE_SILENT_START = -60; +const int ARANGE_SILENT_END = -10; + #endif diff --git a/plugins/SpectrumAnalyzer/Analyzer.cpp b/plugins/SpectrumAnalyzer/Analyzer.cpp index 9c3fe0814..656d18bd4 100644 --- a/plugins/SpectrumAnalyzer/Analyzer.cpp +++ b/plugins/SpectrumAnalyzer/Analyzer.cpp @@ -27,7 +27,13 @@ #include "Analyzer.h" +#ifdef SA_DEBUG + #include + #include +#endif + #include "embed.h" +#include "lmms_basics.h" #include "plugin_export.h" @@ -38,7 +44,7 @@ extern "C" { "Spectrum Analyzer", QT_TRANSLATE_NOOP("pluginBrowser", "A graphical spectrum analyzer."), "Martin Pavelek ", - 0x0100, + 0x0112, Plugin::Effect, new PluginPixmapLoader("logo"), NULL, @@ -50,17 +56,54 @@ extern "C" { Analyzer::Analyzer(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : Effect(&analyzer_plugin_descriptor, parent, key), m_processor(&m_controls), - m_controls(this) + m_controls(this), + m_processorThread(m_processor, m_inputBuffer), + // Buffer is sized to cover 4* the current maximum LMMS audio buffer size, + // so that it has some reserve space in case data processor is busy. + m_inputBuffer(4 * m_maxBufferSize) { + m_processorThread.start(); } +Analyzer::~Analyzer() +{ + m_processor.terminate(); + m_inputBuffer.wakeAll(); + m_processorThread.wait(); +} + // Take audio data and pass them to the spectrum processor. -// Skip processing if the controls dialog isn't visible, it would only waste CPU cycles. bool Analyzer::processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) { + // Measure time spent in audio thread; both average and peak should be well under 1 ms. + #ifdef SA_DEBUG + unsigned int audio_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + if (audio_time - m_last_dump_time > 5000000000) // print every 5 seconds + { + std::cout << "Analyzer audio thread: " << m_sum_execution / m_dump_count << " ms avg / " + << m_max_execution << " ms peak." << std::endl; + m_last_dump_time = audio_time; + m_sum_execution = m_max_execution = m_dump_count = 0; + } + #endif + if (!isEnabled() || !isRunning ()) {return false;} - if (m_controls.isViewVisible()) {m_processor.analyse(buffer, frame_count);} + + // Skip processing if the controls dialog isn't visible, it would only waste CPU cycles. + if (m_controls.isViewVisible()) + { + // To avoid processing spikes on audio thread, data are stored in + // a lockless ringbuffer and processed in a separate thread. + m_inputBuffer.write(buffer, frame_count, true); + } + #ifdef SA_DEBUG + audio_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - audio_time; + m_dump_count++; + m_sum_execution += audio_time / 1000000.0; + if (audio_time / 1000000.0 > m_max_execution) {m_max_execution = audio_time / 1000000.0;} + #endif + return isRunning(); } diff --git a/plugins/SpectrumAnalyzer/Analyzer.h b/plugins/SpectrumAnalyzer/Analyzer.h index 157cc1eae..304777c9a 100644 --- a/plugins/SpectrumAnalyzer/Analyzer.h +++ b/plugins/SpectrumAnalyzer/Analyzer.h @@ -27,7 +27,11 @@ #ifndef ANALYZER_H #define ANALYZER_H +#include + +#include "DataprocLauncher.h" #include "Effect.h" +#include "LocklessRingBuffer.h" #include "SaControls.h" #include "SaProcessor.h" @@ -37,7 +41,7 @@ class Analyzer : public Effect { public: Analyzer(Model *parent, const Descriptor::SubPluginFeatures::Key *key); - virtual ~Analyzer() {}; + virtual ~Analyzer(); bool processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) override; EffectControls *controls() override {return &m_controls;} @@ -47,6 +51,24 @@ public: private: SaProcessor m_processor; SaControls m_controls; + + // Maximum LMMS buffer size (hard coded, the actual constant is hard to get) + const unsigned int m_maxBufferSize = 4096; + + // QThread::create() workaround + // Replace DataprocLauncher by QThread and replace initializer in constructor + // with the following commented line when LMMS CI starts using Qt > 5.9 + //m_processorThread = QThread::create([=]{m_processor.analyze(m_inputBuffer);}); + DataprocLauncher m_processorThread; + + LocklessRingBuffer m_inputBuffer; + + #ifdef SA_DEBUG + int m_last_dump_time; + int m_dump_count; + float m_sum_execution; + float m_max_execution; + #endif }; #endif // ANALYZER_H diff --git a/plugins/SpectrumAnalyzer/CMakeLists.txt b/plugins/SpectrumAnalyzer/CMakeLists.txt index 630fbf1be..488495a9e 100644 --- a/plugins/SpectrumAnalyzer/CMakeLists.txt +++ b/plugins/SpectrumAnalyzer/CMakeLists.txt @@ -1,5 +1,7 @@ INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) + LINK_LIBRARIES(${FFTW3F_LIBRARIES}) + BUILD_PLUGIN(analyzer Analyzer.cpp SaProcessor.cpp SaControls.cpp SaControlsDialog.cpp SaSpectrumView.cpp SaWaterfallView.cpp -MOCFILES SaProcessor.h SaControls.h SaControlsDialog.h SaSpectrumView.h SaWaterfallView.h EMBEDDED_RESOURCES *.svg logo.png) +MOCFILES SaProcessor.h SaControls.h SaControlsDialog.h SaSpectrumView.h SaWaterfallView.h DataprocLauncher.h EMBEDDED_RESOURCES *.svg logo.png) diff --git a/plugins/SpectrumAnalyzer/DataprocLauncher.h b/plugins/SpectrumAnalyzer/DataprocLauncher.h new file mode 100644 index 000000000..d91e0bedf --- /dev/null +++ b/plugins/SpectrumAnalyzer/DataprocLauncher.h @@ -0,0 +1,52 @@ +/* + * DataprocLauncher.h - QThread::create workaround for older Qt version + * + * Copyright (c) 2019 Martin Pavelek + * + * 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 DATAPROCLAUNCHER_H +#define DATAPROCLAUNCHER_H + +#include + +#include "SaProcessor.h" +#include "LocklessRingBuffer.h" + +class DataprocLauncher : public QThread +{ +public: + explicit DataprocLauncher(SaProcessor &proc, LocklessRingBuffer &buffer) + : m_processor(&proc), + m_inputBuffer(&buffer) + { + } + +private: + void run() override + { + m_processor->analyze(*m_inputBuffer); + } + + SaProcessor *m_processor; + LocklessRingBuffer *m_inputBuffer; +}; + +#endif // DATAPROCLAUNCHER_H diff --git a/plugins/SpectrumAnalyzer/README.md b/plugins/SpectrumAnalyzer/README.md index 3d3506d65..473083da8 100644 --- a/plugins/SpectrumAnalyzer/README.md +++ b/plugins/SpectrumAnalyzer/README.md @@ -4,13 +4,41 @@ This plugin consists of three widgets and back-end code to provide them with required data. -The top-level widget is SaControlDialog. It populates a configuration widget (created dynamically) and instantiates spectrum display widgets. Its main back-end class is SaControls, which holds all configuration values and globally valid constants (e.g. range definitions). +The top-level widget is `SaControlDialog`. It populates configuration widgets (created dynamically) and instantiates spectrum display widgets. Its main back-end class is `SaControls`, which holds all configuration values. -SaSpectrumDisplay and SaWaterfallDisplay show the result of spectrum analysis. Their main back-end class is SaProcessor, which performs FFT analysis on data received from the Analyzer class, which in turn handles the interface with LMMS. +`SaSpectrumView` and `SaWaterfallView` widgets show the result of spectrum analysis. Their main back-end class is `SaProcessor`, which performs FFT analysis on data received from the `Analyzer` class, which in turn handles the interface with LMMS. + +## Threads + +The Spectrum Analyzer is involved in three different threads: + - **Effect mixer thread**: periodically calls `Analyzer::processAudioBuffer()` to provide the plugin with more data. This thread is real-time sensitive -- any latency spikes can potentially cause interruptions in the audio stream. For this reason, `Analyzer::processAudioBuffer()` must finish as fast as possible and must not call any functions that could cause it to be delayed for unpredictable amount of time. A lock-less ring buffer is used to safely feed data to the FFT analysis thread without risking any latency spikes due to a shared mutex being unavailable at the time of writing. + - **FFT analysis thread**: a standalone thread formed by the `SaProcessor::analyze()` function. Takes in data from the ring buffer, performs FFT analysis and prepares results for display. This thread is not real-time sensitive but excessive locking is discouraged to maintain good performance. + - **GUI thread**: periodically triggers `paintEvent()` of all Qt widgets, including `SaSpectrumView` and `SaWaterfallView`. While it is not as sensitive to latency spikes as the effect mixer thread, the `paintEvent()`s appear to be called sequentially and the execution time of each widget therefore adds to the total time needed to complete one full refresh cycle. This means the maximum frame rate of the Qt GUI will be limited to `1 / total_execution_time`. Good performance of the `paintEvent()` functions should be therefore kept in mind. ## Changelog - + 1.1.2 2019-11-18 + - waterfall is no longer cut short when width limit is reached + - various small tweaks based on final review + 1.1.1 2019-10-13 + - improved interface for accessing SaProcessor private data + - readme file update + - other small improvements based on reviews + 1.1.0 2019-08-29 + - advanced config: expose hidden constants to user + - advanced config: add support for FFT window overlapping + - waterfall: display at native resolution on high-DPI screens + - waterfall: add cursor and improve label density + - FFT: fix normalization so that 0 dBFS matches full-scale sinewave + - FFT: decouple data acquisition from processing and display + - FFT: separate lock for reallocation (to avoid some needless waiting) + - moved ranges and other constants to a separate file + - debug: better performance measurements + - various performance optimizations + 1.0.3 2019-07-25 + - rename and tweak amplitude ranges based on feedback + 1.0.2 2019-07-12 + - variety of small changes based on code review 1.0.1 2019-06-02 - code style changes - added tool-tips diff --git a/plugins/SpectrumAnalyzer/SaControls.cpp b/plugins/SpectrumAnalyzer/SaControls.cpp index 5691c0ae4..6be298e27 100644 --- a/plugins/SpectrumAnalyzer/SaControls.cpp +++ b/plugins/SpectrumAnalyzer/SaControls.cpp @@ -50,7 +50,17 @@ SaControls::SaControls(Analyzer *effect) : m_freqRangeModel(this, tr("Frequency range")), m_ampRangeModel(this, tr("Amplitude range")), m_blockSizeModel(this, tr("FFT block size")), - m_windowModel(this, tr("FFT window type")) + m_windowModel(this, tr("FFT window type")), + + // Advanced settings knobs + m_envelopeResolutionModel(0.25f, 0.1f, 3.0f, 0.05f, this, tr("Peak envelope resolution")), + m_spectrumResolutionModel(1.5f, 0.1f, 3.0f, 0.05f, this, tr("Spectrum display resolution")), + m_peakDecayFactorModel(0.992f, 0.95f, 0.999f, 0.001f, this, tr("Peak decay multiplier")), + m_averagingWeightModel(0.15f, 0.01f, 0.5f, 0.01f, this, tr("Averaging weight")), + m_waterfallHeightModel(300.0f, 50.0f, 1000.0f, 50.0f, this, tr("Waterfall history size")), + m_waterfallGammaModel(0.30f, 0.10f, 1.00f, 0.05f, this, tr("Waterfall gamma correction")), + m_windowOverlapModel(2.0f, 1.0f, 4.0f, 1.0f, this, tr("FFT window overlap")), + m_zeroPaddingModel(2.0f, 0.0f, 4.0f, 1.0f, this, tr("FFT zero padding")) { // Frequency and amplitude ranges; order must match // FREQUENCY_RANGES and AMPLITUDE_RANGES defined in SaControls.h @@ -62,10 +72,10 @@ SaControls::SaControls(Analyzer *effect) : m_freqRangeModel.setValue(m_freqRangeModel.findText(tr("Full (auto)"))); m_ampRangeModel.addItem(tr("Extended")); - m_ampRangeModel.addItem(tr("Default")); m_ampRangeModel.addItem(tr("Audible")); - m_ampRangeModel.addItem(tr("Noise")); - m_ampRangeModel.setValue(m_ampRangeModel.findText(tr("Default"))); + m_ampRangeModel.addItem(tr("Loud")); + m_ampRangeModel.addItem(tr("Silent")); + m_ampRangeModel.setValue(m_ampRangeModel.findText(tr("Audible"))); // FFT block size labels are generated automatically, based on // FFT_BLOCK_SIZES vector defined in fft_helpers.h @@ -95,12 +105,15 @@ SaControls::SaControls(Analyzer *effect) : // Colors // Background color is defined by Qt / theme. - // Make sure the sum of colors for L and R channel stays lower or equal - // to 255. Otherwise the Waterfall pixels may overflow back to 0 even when - // the input signal isn't clipping (over 1.0). + // Make sure the sum of colors for L and R channel results into a neutral + // color that has at least one component equal to 255 (i.e. ideally white). + // This means the color overflows to zero exactly when signal reaches + // clipping threshold, indicating the problematic frequency to user. + // Mono waterfall color should have similarly at least one component at 255. m_colorL = QColor(51, 148, 204, 135); m_colorR = QColor(204, 107, 51, 135); m_colorMono = QColor(51, 148, 204, 204); + m_colorMonoW = QColor(64, 185, 255, 255); m_colorBG = QColor(7, 7, 7, 255); // ~20 % gray (after gamma correction) m_colorGrid = QColor(30, 34, 38, 255); // ~40 % gray (slightly cold / blue) m_colorLabels = QColor(192, 202, 212, 255); // ~90 % gray (slightly cold / blue) @@ -126,6 +139,15 @@ void SaControls::loadSettings(const QDomElement &_this) m_ampRangeModel.loadSettings(_this, "RangeY"); m_blockSizeModel.loadSettings(_this, "BlockSize"); m_windowModel.loadSettings(_this, "WindowType"); + + m_envelopeResolutionModel.loadSettings(_this, "EnvelopeRes"); + m_spectrumResolutionModel.loadSettings(_this, "SpectrumRes"); + m_peakDecayFactorModel.loadSettings(_this, "PeakDecayFactor"); + m_averagingWeightModel.loadSettings(_this, "AverageWeight"); + m_waterfallHeightModel.loadSettings(_this, "WaterfallHeight"); + m_waterfallGammaModel.loadSettings(_this, "WaterfallGamma"); + m_windowOverlapModel.loadSettings(_this, "WindowOverlap"); + m_zeroPaddingModel.loadSettings(_this, "ZeroPadding"); } @@ -141,4 +163,14 @@ void SaControls::saveSettings(QDomDocument &doc, QDomElement &parent) m_ampRangeModel.saveSettings(doc, parent, "RangeY"); m_blockSizeModel.saveSettings(doc, parent, "BlockSize"); m_windowModel.saveSettings(doc, parent, "WindowType"); + + m_envelopeResolutionModel.saveSettings(doc, parent, "EnvelopeRes"); + m_spectrumResolutionModel.saveSettings(doc, parent, "SpectrumRes"); + m_peakDecayFactorModel.saveSettings(doc, parent, "PeakDecayFactor"); + m_averagingWeightModel.saveSettings(doc, parent, "AverageWeight"); + m_waterfallHeightModel.saveSettings(doc, parent, "WaterfallHeight"); + m_waterfallGammaModel.saveSettings(doc, parent, "WaterfallGamma"); + m_windowOverlapModel.saveSettings(doc, parent, "WindowOverlap"); + m_zeroPaddingModel.saveSettings(doc, parent, "ZeroPadding"); + } diff --git a/plugins/SpectrumAnalyzer/SaControls.h b/plugins/SpectrumAnalyzer/SaControls.h index e0b54e6a2..4673416bc 100644 --- a/plugins/SpectrumAnalyzer/SaControls.h +++ b/plugins/SpectrumAnalyzer/SaControls.h @@ -27,52 +27,10 @@ #include "ComboBoxModel.h" #include "EffectControls.h" +#include "lmms_constants.h" //#define SA_DEBUG 1 // define SA_DEBUG to enable performance measurements -// Frequency ranges (in Hz). -// Full range is defined by LOWEST_LOG_FREQ and current sample rate. -const int LOWEST_LOG_FREQ = 10; // arbitrary low limit for log. scale, >1 - -enum FREQUENCY_RANGES -{ - FRANGE_FULL = 0, - FRANGE_AUDIBLE, - FRANGE_BASS, - FRANGE_MIDS, - FRANGE_HIGH -}; - -const int FRANGE_AUDIBLE_START = 20; -const int FRANGE_AUDIBLE_END = 20000; -const int FRANGE_BASS_START = 20; -const int FRANGE_BASS_END = 300; -const int FRANGE_MIDS_START = 200; -const int FRANGE_MIDS_END = 5000; -const int FRANGE_HIGH_START = 4000; -const int FRANGE_HIGH_END = 20000; - -// Amplitude ranges. -// Reference: sine wave from -1.0 to 1.0 = 0 dB. -// I.e. if master volume is 100 %, positive values signify clipping. -// Doubling or halving the amplitude produces 3 dB difference. -enum AMPLITUDE_RANGES -{ - ARANGE_EXTENDED = 0, - ARANGE_DEFAULT, - ARANGE_AUDIBLE, - ARANGE_NOISE -}; - -const int ARANGE_EXTENDED_START = -80; -const int ARANGE_EXTENDED_END = 20; -const int ARANGE_DEFAULT_START = -30; -const int ARANGE_DEFAULT_END = 0; -const int ARANGE_AUDIBLE_START = -50; -const int ARANGE_AUDIBLE_END = 10; -const int ARANGE_NOISE_START = -60; -const int ARANGE_NOISE_END = -20; - class Analyzer; @@ -90,11 +48,12 @@ public: void loadSettings (const QDomElement &_this) override; QString nodeName() const override {return "Analyzer";} - int controlCount() override {return 12;} + int controlCount() override {return 20;} private: Analyzer *m_effect; + // basic settings BoolModel m_pauseModel; BoolModel m_refFreezeModel; @@ -111,12 +70,24 @@ private: ComboBoxModel m_blockSizeModel; ComboBoxModel m_windowModel; - QColor m_colorL; - QColor m_colorR; - QColor m_colorMono; - QColor m_colorBG; - QColor m_colorGrid; - QColor m_colorLabels; + // advanced settings + FloatModel m_envelopeResolutionModel; + FloatModel m_spectrumResolutionModel; + FloatModel m_peakDecayFactorModel; + FloatModel m_averagingWeightModel; + FloatModel m_waterfallHeightModel; + FloatModel m_waterfallGammaModel; + FloatModel m_windowOverlapModel; + FloatModel m_zeroPaddingModel; + + // colors (hard-coded, values must add up to specific numbers) + QColor m_colorL; //!< color of the left channel + QColor m_colorR; //!< color of the right channel + QColor m_colorMono; //!< mono color for spectrum display + QColor m_colorMonoW; //!< mono color for waterfall display + QColor m_colorBG; //!< spectrum display background color + QColor m_colorGrid; //!< color of grid lines + QColor m_colorLabels; //!< color of axis labels friend class SaControlsDialog; friend class SaSpectrumView; diff --git a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp index 4ba307a4d..d89cc1093 100644 --- a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp +++ b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp @@ -34,6 +34,7 @@ #include "ComboBoxModel.h" #include "embed.h" #include "Engine.h" +#include "Knob.h" #include "LedCheckbox.h" #include "PixmapButton.h" #include "SaControls.h" @@ -53,13 +54,24 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) master_layout->setContentsMargins(2, 6, 2, 8); setLayout(master_layout); - // QSplitter top: configuration section + // Display splitter top: controls section + QWidget *controls_widget = new QWidget; + QHBoxLayout *controls_layout = new QHBoxLayout; + controls_layout->setContentsMargins(0, 0, 0, 0); + controls_widget->setLayout(controls_layout); + controls_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); + controls_widget->setMaximumHeight(m_configHeight); + display_splitter->addWidget(controls_widget); + + + // Basic configuration QWidget *config_widget = new QWidget; QGridLayout *config_layout = new QGridLayout; config_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); config_widget->setMaximumHeight(m_configHeight); config_widget->setLayout(config_layout); - display_splitter->addWidget(config_widget); + controls_layout->addWidget(config_widget); + controls_layout->setStretchFactor(config_widget, 10); // Pre-compute target pixmap size based on monitor DPI. // Using setDevicePixelRatio() on pixmap allows the SVG image to be razor @@ -67,6 +79,8 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) // enlarged. No idea how to make Qt do it in a more reasonable way. QSize iconSize = QSize(22.0 * devicePixelRatio(), 22.0 * devicePixelRatio()); QSize buttonSize = 1.2 * iconSize; + QSize advButtonSize = QSize((m_configHeight * devicePixelRatio()) / 3, m_configHeight * devicePixelRatio()); + // pause and freeze buttons PixmapButton *pauseButton = new PixmapButton(this, tr("Pause")); @@ -79,7 +93,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) pauseButton->setInactiveGraphic(*pauseOffPixmap); pauseButton->setCheckable(true); pauseButton->setModel(&controls->m_pauseModel); - config_layout->addWidget(pauseButton, 0, 0, 2, 1); + config_layout->addWidget(pauseButton, 0, 0, 2, 1, Qt::AlignHCenter); PixmapButton *refFreezeButton = new PixmapButton(this, tr("Reference freeze")); refFreezeButton->setToolTip(tr("Freeze current input as a reference / disable falloff in peak-hold mode.")); @@ -91,7 +105,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) refFreezeButton->setInactiveGraphic(*freezeOffPixmap); refFreezeButton->setCheckable(true); refFreezeButton->setModel(&controls->m_refFreezeModel); - config_layout->addWidget(refFreezeButton, 2, 0, 2, 1); + config_layout->addWidget(refFreezeButton, 2, 0, 2, 1, Qt::AlignHCenter); // misc configuration switches LedCheckBox *waterfallButton = new LedCheckBox(tr("Waterfall"), this); @@ -194,6 +208,117 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) processor->rebuildWindow(); connect(&controls->m_windowModel, &ComboBoxModel::dataChanged, [=] {processor->rebuildWindow();}); + // set stretch factors so that combo boxes expand first + config_layout->setColumnStretch(3, 2); + config_layout->setColumnStretch(5, 3); + + + // Advanced configuration + QWidget *advanced_widget = new QWidget; + QGridLayout *advanced_layout = new QGridLayout; + advanced_widget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + advanced_widget->setMaximumHeight(m_configHeight); + advanced_widget->setLayout(advanced_layout); + advanced_widget->hide(); + controls_layout->addWidget(advanced_widget); + controls_layout->setStretchFactor(advanced_widget, 10); + + // Peak envelope resolution + Knob *envelopeResolutionKnob = new Knob(knobSmall_17, this); + envelopeResolutionKnob->setModel(&controls->m_envelopeResolutionModel); + envelopeResolutionKnob->setLabel(tr("Envelope res.")); + envelopeResolutionKnob->setToolTip(tr("Increase envelope resolution for better details, decrease for better GUI performance.")); + envelopeResolutionKnob->setHintText(tr("Draw at most"), tr(" envelope points per pixel")); + advanced_layout->addWidget(envelopeResolutionKnob, 0, 0, 1, 1, Qt::AlignCenter); + + // Spectrum graph resolution + Knob *spectrumResolutionKnob = new Knob(knobSmall_17, this); + spectrumResolutionKnob->setModel(&controls->m_spectrumResolutionModel); + spectrumResolutionKnob->setLabel(tr("Spectrum res.")); + spectrumResolutionKnob->setToolTip(tr("Increase spectrum resolution for better details, decrease for better GUI performance.")); + spectrumResolutionKnob->setHintText(tr("Draw at most"), tr(" spectrum points per pixel")); + advanced_layout->addWidget(spectrumResolutionKnob, 1, 0, 1, 1, Qt::AlignCenter); + + // Peak falloff speed + Knob *peakDecayFactorKnob = new Knob(knobSmall_17, this); + peakDecayFactorKnob->setModel(&controls->m_peakDecayFactorModel); + peakDecayFactorKnob->setLabel(tr("Falloff factor")); + peakDecayFactorKnob->setToolTip(tr("Decrease to make peaks fall faster.")); + peakDecayFactorKnob->setHintText(tr("Multiply buffered value by"), ""); + advanced_layout->addWidget(peakDecayFactorKnob, 0, 1, 1, 1, Qt::AlignCenter); + + // Averaging weight + Knob *averagingWeightKnob = new Knob(knobSmall_17, this); + averagingWeightKnob->setModel(&controls->m_averagingWeightModel); + averagingWeightKnob->setLabel(tr("Averaging weight")); + averagingWeightKnob->setToolTip(tr("Decrease to make averaging slower and smoother.")); + averagingWeightKnob->setHintText(tr("New sample contributes"), ""); + advanced_layout->addWidget(averagingWeightKnob, 1, 1, 1, 1, Qt::AlignCenter); + + // Waterfall history size + Knob *waterfallHeightKnob = new Knob(knobSmall_17, this); + waterfallHeightKnob->setModel(&controls->m_waterfallHeightModel); + waterfallHeightKnob->setLabel(tr("Waterfall height")); + waterfallHeightKnob->setToolTip(tr("Increase to get slower scrolling, decrease to see fast transitions better. Warning: medium CPU usage.")); + waterfallHeightKnob->setHintText(tr("Keep"), tr(" lines")); + advanced_layout->addWidget(waterfallHeightKnob, 0, 2, 1, 1, Qt::AlignCenter); + processor->reallocateBuffers(); + connect(&controls->m_waterfallHeightModel, &FloatModel::dataChanged, [=] {processor->reallocateBuffers();}); + + // Waterfall gamma correction + Knob *waterfallGammaKnob = new Knob(knobSmall_17, this); + waterfallGammaKnob->setModel(&controls->m_waterfallGammaModel); + waterfallGammaKnob->setLabel(tr("Waterfall gamma")); + waterfallGammaKnob->setToolTip(tr("Decrease to see very weak signals, increase to get better contrast.")); + waterfallGammaKnob->setHintText(tr("Gamma value:"), ""); + advanced_layout->addWidget(waterfallGammaKnob, 1, 2, 1, 1, Qt::AlignCenter); + + // FFT window overlap + Knob *windowOverlapKnob = new Knob(knobSmall_17, this); + windowOverlapKnob->setModel(&controls->m_windowOverlapModel); + windowOverlapKnob->setLabel(tr("Window overlap")); + windowOverlapKnob->setToolTip(tr("Increase to prevent missing fast transitions arriving near FFT window edges. Warning: high CPU usage.")); + windowOverlapKnob->setHintText(tr("Each sample processed"), tr(" times")); + advanced_layout->addWidget(windowOverlapKnob, 0, 3, 1, 1, Qt::AlignCenter); + + // FFT zero padding + Knob *zeroPaddingKnob = new Knob(knobSmall_17, this); + zeroPaddingKnob->setModel(&controls->m_zeroPaddingModel); + zeroPaddingKnob->setLabel(tr("Zero padding")); + zeroPaddingKnob->setToolTip(tr("Increase to get smoother-looking spectrum. Warning: high CPU usage.")); + zeroPaddingKnob->setHintText(tr("Processing buffer is"), tr(" steps larger than input block")); + advanced_layout->addWidget(zeroPaddingKnob, 1, 3, 1, 1, Qt::AlignCenter); + processor->reallocateBuffers(); + connect(&controls->m_zeroPaddingModel, &FloatModel::dataChanged, [=] {processor->reallocateBuffers();}); + + + // Advanced settings button + PixmapButton *advancedButton = new PixmapButton(this, tr("Advanced settings")); + advancedButton->setToolTip(tr("Access advanced settings")); + QPixmap *advancedOnPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("advanced_on").scaled(advButtonSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + QPixmap *advancedOffPixmap = new QPixmap(PLUGIN_NAME::getIconPixmap("advanced_off").scaled(advButtonSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + advancedOnPixmap->setDevicePixelRatio(devicePixelRatio()); + advancedOffPixmap->setDevicePixelRatio(devicePixelRatio()); + advancedButton->setActiveGraphic(*advancedOnPixmap); + advancedButton->setInactiveGraphic(*advancedOffPixmap); + advancedButton->setCheckable(true); + controls_layout->addStretch(0); + controls_layout->addWidget(advancedButton); + + connect(advancedButton, &PixmapButton::toggled, [=](bool checked) + { + if (checked) + { + config_widget->hide(); + advanced_widget->show(); + } + else + { + config_widget->show(); + advanced_widget->hide(); + } + } + ); // QSplitter middle and bottom: spectrum display widgets m_spectrum = new SaSpectrumView(controls, processor, this); diff --git a/plugins/SpectrumAnalyzer/SaProcessor.cpp b/plugins/SpectrumAnalyzer/SaProcessor.cpp index 9261658aa..9d83f2916 100644 --- a/plugins/SpectrumAnalyzer/SaProcessor.cpp +++ b/plugins/SpectrumAnalyzer/SaProcessor.cpp @@ -26,15 +26,23 @@ #include "SaProcessor.h" #include +#ifdef SA_DEBUG + #include +#endif #include -#include +#ifdef SA_DEBUG + #include + #include +#endif #include #include "lmms_math.h" +#include "LocklessRingBuffer.h" -SaProcessor::SaProcessor(SaControls *controls) : +SaProcessor::SaProcessor(const SaControls *controls) : m_controls(controls), + m_terminate(false), m_inBlockSize(FFT_BLOCK_SIZES[0]), m_fftBlockSize(FFT_BLOCK_SIZES[0]), m_sampleRate(Engine::mixer()->processingSampleRate()), @@ -47,21 +55,23 @@ SaProcessor::SaProcessor(SaControls *controls) : m_fftWindow.resize(m_inBlockSize, 1.0); precomputeWindow(m_fftWindow.data(), m_inBlockSize, BLACKMAN_HARRIS); - m_bufferL.resize(m_fftBlockSize, 0); - m_bufferR.resize(m_fftBlockSize, 0); + m_bufferL.resize(m_inBlockSize, 0); + m_bufferR.resize(m_inBlockSize, 0); + m_filteredBufferL.resize(m_fftBlockSize, 0); + m_filteredBufferR.resize(m_fftBlockSize, 0); m_spectrumL = (fftwf_complex *) fftwf_malloc(binCount() * sizeof (fftwf_complex)); m_spectrumR = (fftwf_complex *) fftwf_malloc(binCount() * sizeof (fftwf_complex)); - m_fftPlanL = fftwf_plan_dft_r2c_1d(m_fftBlockSize, m_bufferL.data(), m_spectrumL, FFTW_MEASURE); - m_fftPlanR = fftwf_plan_dft_r2c_1d(m_fftBlockSize, m_bufferR.data(), m_spectrumR, FFTW_MEASURE); + m_fftPlanL = fftwf_plan_dft_r2c_1d(m_fftBlockSize, m_filteredBufferL.data(), m_spectrumL, FFTW_MEASURE); + m_fftPlanR = fftwf_plan_dft_r2c_1d(m_fftBlockSize, m_filteredBufferR.data(), m_spectrumR, FFTW_MEASURE); m_absSpectrumL.resize(binCount(), 0); m_absSpectrumR.resize(binCount(), 0); m_normSpectrumL.resize(binCount(), 0); m_normSpectrumR.resize(binCount(), 0); - m_history.resize(binCount() * m_waterfallHeight * sizeof qRgb(0,0,0), 0); - - clear(); + m_waterfallHeight = 100; // a small safe value + m_history_work.resize(waterfallWidth() * m_waterfallHeight * sizeof qRgb(0,0,0), 0); + m_history.resize(waterfallWidth() * m_waterfallHeight * sizeof qRgb(0,0,0), 0); } @@ -79,169 +89,229 @@ SaProcessor::~SaProcessor() } -// Load a batch of data from LMMS; run FFT analysis if buffer is full enough. -void SaProcessor::analyse(sampleFrame *in_buffer, const fpp_t frame_count) +// Load data from audio thread ringbuffer and run FFT analysis if buffer is full enough. +void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) { - #ifdef SA_DEBUG - int start_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - #endif - // only take in data if any view is visible and not paused - if ((m_spectrumActive || m_waterfallActive) && !m_controls->m_pauseModel.value()) + LocklessRingBufferReader reader(ring_buffer); + + // Processing thread loop + while (!m_terminate) { - const bool stereo = m_controls->m_stereoModel.value(); - fpp_t in_frame = 0; - while (in_frame < frame_count) + // If there is nothing to read, wait for notification from the writing side. + if (reader.empty()) {reader.waitForData();} + + // skip waterfall render if processing can't keep up with input + bool overload = ring_buffer.free() < ring_buffer.capacity() / 2; + + auto in_buffer = reader.read_max(ring_buffer.capacity() / 4); + std::size_t frame_count = in_buffer.size(); + + // Process received data only if any view is visible and not paused. + // Also, to prevent a momentary GUI freeze under high load (due to lock + // starvation), skip analysis when buffer reallocation is requested. + if ((m_spectrumActive || m_waterfallActive) && !m_controls->m_pauseModel.value() && !m_reallocating) { - // fill sample buffers and check for zero input - bool block_empty = true; - for (; in_frame < frame_count && m_framesFilledUp < m_inBlockSize; in_frame++, m_framesFilledUp++) + const bool stereo = m_controls->m_stereoModel.value(); + fpp_t in_frame = 0; + while (in_frame < frame_count) { + // Lock data access to prevent reallocation from changing + // buffers and control variables. + QMutexLocker data_lock(&m_dataAccess); + + // Fill sample buffers and check for zero input. + bool block_empty = true; + for (; in_frame < frame_count && m_framesFilledUp < m_inBlockSize; in_frame++, m_framesFilledUp++) + { + if (stereo) + { + m_bufferL[m_framesFilledUp] = in_buffer[in_frame][0]; + m_bufferR[m_framesFilledUp] = in_buffer[in_frame][1]; + } + else + { + m_bufferL[m_framesFilledUp] = + m_bufferR[m_framesFilledUp] = (in_buffer[in_frame][0] + in_buffer[in_frame][1]) * 0.5f; + } + if (in_buffer[in_frame][0] != 0.f || in_buffer[in_frame][1] != 0.f) + { + block_empty = false; + } + } + + // Run analysis only if buffers contain enough data. + if (m_framesFilledUp < m_inBlockSize) {break;} + + // Print performance analysis once per 2 seconds if debug is enabled + #ifdef SA_DEBUG + unsigned int total_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + if (total_time - m_last_dump_time > 2000000000) + { + std::cout << "FFT analysis: " << std::fixed << std::setprecision(2) + << m_sum_execution / m_dump_count << " ms avg / " + << m_max_execution << " ms peak, executing " + << m_dump_count << " times per second (" + << m_sum_execution / 20.0 << " % CPU usage)." << std::endl; + m_last_dump_time = total_time; + m_sum_execution = m_max_execution = m_dump_count = 0; + } + #endif + + // update sample rate + m_sampleRate = Engine::mixer()->processingSampleRate(); + + // apply FFT window + for (unsigned int i = 0; i < m_inBlockSize; i++) + { + m_filteredBufferL[i] = m_bufferL[i] * m_fftWindow[i]; + m_filteredBufferR[i] = m_bufferR[i] * m_fftWindow[i]; + } + + // Run FFT on left channel, convert the result to absolute magnitude + // spectrum and normalize it. + fftwf_execute(m_fftPlanL); + absspec(m_spectrumL, m_absSpectrumL.data(), binCount()); + normalize(m_absSpectrumL, m_normSpectrumL, m_inBlockSize); + + // repeat analysis for right channel if stereo processing is enabled if (stereo) { - m_bufferL[m_framesFilledUp] = in_buffer[in_frame][0]; - m_bufferR[m_framesFilledUp] = in_buffer[in_frame][1]; + fftwf_execute(m_fftPlanR); + absspec(m_spectrumR, m_absSpectrumR.data(), binCount()); + normalize(m_absSpectrumR, m_normSpectrumR, m_inBlockSize); } - else + + // count empty lines so that empty history does not have to update + if (block_empty && m_waterfallNotEmpty) { - m_bufferL[m_framesFilledUp] = - m_bufferR[m_framesFilledUp] = (in_buffer[in_frame][0] + in_buffer[in_frame][1]) * 0.5f; + m_waterfallNotEmpty -= 1; } - if (in_buffer[in_frame][0] != 0.f || in_buffer[in_frame][1] != 0.f) + else if (!block_empty) { - block_empty = false; + m_waterfallNotEmpty = m_waterfallHeight + 2; } - } - - // Run analysis only if buffers contain enough data. - // Also, to prevent audio interruption and a momentary GUI freeze, - // skip analysis if buffers are being reallocated. - if (m_framesFilledUp < m_inBlockSize || m_reallocating) {return;} - // update sample rate - m_sampleRate = Engine::mixer()->processingSampleRate(); - - // apply FFT window - for (unsigned int i = 0; i < m_inBlockSize; i++) - { - m_bufferL[i] = m_bufferL[i] * m_fftWindow[i]; - m_bufferR[i] = m_bufferR[i] * m_fftWindow[i]; - } - - // lock data shared with SaSpectrumView and SaWaterfallView - QMutexLocker lock(&m_dataAccess); - - // Run FFT on left channel, convert the result to absolute magnitude - // spectrum and normalize it. - fftwf_execute(m_fftPlanL); - absspec(m_spectrumL, m_absSpectrumL.data(), binCount()); - normalize(m_absSpectrumL, m_normSpectrumL, m_inBlockSize); - - // repeat analysis for right channel if stereo processing is enabled - if (stereo) - { - fftwf_execute(m_fftPlanR); - absspec(m_spectrumR, m_absSpectrumR.data(), binCount()); - normalize(m_absSpectrumR, m_normSpectrumR, m_inBlockSize); - } - - // count empty lines so that empty history does not have to update - if (block_empty && m_waterfallNotEmpty) - { - m_waterfallNotEmpty -= 1; - } - else if (!block_empty) - { - m_waterfallNotEmpty = m_waterfallHeight + 2; - } - - if (m_waterfallActive && m_waterfallNotEmpty) - { - // move waterfall history one line down and clear the top line - QRgb *pixel = (QRgb *)m_history.data(); - std::copy(pixel, - pixel + binCount() * m_waterfallHeight - binCount(), - pixel + binCount()); - memset(pixel, 0, binCount() * sizeof (QRgb)); - - // add newest result on top - int target; // pixel being constructed - float accL = 0; // accumulators for merging multiple bins - float accR = 0; - - for (unsigned int i = 0; i < binCount(); i++) + if (m_waterfallActive && m_waterfallNotEmpty) { - // Every frequency bin spans a frequency range that must be - // partially or fully mapped to a pixel. Any inconsistency - // may be seen in the spectrogram as dark or white lines -- - // play white noise to confirm your change did not break it. - float band_start = freqToXPixel(binToFreq(i) - binBandwidth() / 2.0, binCount()); - float band_end = freqToXPixel(binToFreq(i + 1) - binBandwidth() / 2.0, binCount()); - if (m_controls->m_logXModel.value()) + // move waterfall history one line down and clear the top line + QRgb *pixel = (QRgb *)m_history_work.data(); + std::copy(pixel, + pixel + waterfallWidth() * m_waterfallHeight - waterfallWidth(), + pixel + waterfallWidth()); + memset(pixel, 0, waterfallWidth() * sizeof (QRgb)); + + // add newest result on top + int target; // pixel being constructed + float accL = 0; // accumulators for merging multiple bins + float accR = 0; + for (unsigned int i = 0; i < binCount(); i++) { - // Logarithmic scale - if (band_end - band_start > 1.0) + // fill line with red color to indicate lost data if CPU cannot keep up + if (overload && i < waterfallWidth()) { - // band spans multiple pixels: draw all pixels it covers - for (target = (int)band_start; target < (int)band_end; target++) + pixel[i] = qRgb(42, 0, 0); + continue; + } + + // Every frequency bin spans a frequency range that must be + // partially or fully mapped to a pixel. Any inconsistency + // may be seen in the spectrogram as dark or white lines -- + // play white noise to confirm your change did not break it. + float band_start = freqToXPixel(binToFreq(i) - binBandwidth() / 2.0, waterfallWidth()); + float band_end = freqToXPixel(binToFreq(i + 1) - binBandwidth() / 2.0, waterfallWidth()); + if (m_controls->m_logXModel.value()) + { + // Logarithmic scale + if (band_end - band_start > 1.0) { - if (target >= 0 && target < binCount()) + // band spans multiple pixels: draw all pixels it covers + for (target = (int)band_start; target < (int)band_end; target++) + { + if (target >= 0 && target < waterfallWidth()) + { + pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); + } + } + // save remaining portion of the band for the following band / pixel + // (in case the next band uses sub-pixel drawing) + accL = (band_end - (int)band_end) * m_normSpectrumL[i]; + accR = (band_end - (int)band_end) * m_normSpectrumR[i]; + } + else + { + // sub-pixel drawing; add contribution of current band + target = (int)band_start; + if ((int)band_start == (int)band_end) + { + // band ends within current target pixel, accumulate + accL += (band_end - band_start) * m_normSpectrumL[i]; + accR += (band_end - band_start) * m_normSpectrumR[i]; + } + else + { + // Band ends in the next pixel -- finalize the current pixel. + // Make sure contribution is split correctly on pixel boundary. + accL += ((int)band_end - band_start) * m_normSpectrumL[i]; + accR += ((int)band_end - band_start) * m_normSpectrumR[i]; + + if (target >= 0 && target < waterfallWidth()) {pixel[target] = makePixel(accL, accR);} + + // save remaining portion of the band for the following band / pixel + accL = (band_end - (int)band_end) * m_normSpectrumL[i]; + accR = (band_end - (int)band_end) * m_normSpectrumR[i]; + } + } + } + else + { + // Linear: always draws one or more pixels per band + for (target = (int)band_start; target < band_end; target++) + { + if (target >= 0 && target < waterfallWidth()) { pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); } } - // save remaining portion of the band for the following band / pixel - // (in case the next band uses sub-pixel drawing) - accL = (band_end - (int)band_end) * m_normSpectrumL[i]; - accR = (band_end - (int)band_end) * m_normSpectrumR[i]; - } - else - { - // sub-pixel drawing; add contribution of current band - target = (int)band_start; - if ((int)band_start == (int)band_end) - { - // band ends within current target pixel, accumulate - accL += (band_end - band_start) * m_normSpectrumL[i]; - accR += (band_end - band_start) * m_normSpectrumR[i]; - } - else - { - // Band ends in the next pixel -- finalize the current pixel. - // Make sure contribution is split correctly on pixel boundary. - accL += ((int)band_end - band_start) * m_normSpectrumL[i]; - accR += ((int)band_end - band_start) * m_normSpectrumR[i]; - - if (target >= 0 && target < binCount()) {pixel[target] = makePixel(accL, accR);} - - // save remaining portion of the band for the following band / pixel - accL = (band_end - (int)band_end) * m_normSpectrumL[i]; - accR = (band_end - (int)band_end) * m_normSpectrumR[i]; - } } } - else + + // Copy work buffer to result buffer. Done only if requested, so + // that time isn't wasted on updating faster than display FPS. + // (The copy is about as expensive as the movement.) + if (m_flipRequest) { - // Linear: always draws one or more pixels per band - for (target = (int)band_start; target < band_end; target++) - { - if (target >= 0 && target < binCount()) - { - pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); - } - } + m_history = m_history_work; + m_flipRequest = false; } } - } - #ifdef SA_DEBUG - // report FFT processing speed - start_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - start_time; - std::cout << "Processed " << m_framesFilledUp << " samples in " << start_time / 1000000.0 << " ms" << std::endl; - #endif + // clean up before checking for more data from input buffer + const unsigned int overlaps = m_controls->m_windowOverlapModel.value(); + if (overlaps == 1) // Discard buffer, each sample used only once + { + m_framesFilledUp = 0; + } + else + { + // Drop only a part of the buffer from the beginning, so that new + // data can be added to the end. This means the older samples will + // be analyzed again, but in a different position in the window, + // making short transient signals show up better in the waterfall. + const unsigned int drop = m_inBlockSize / overlaps; + std::move(m_bufferL.begin() + drop, m_bufferL.end(), m_bufferL.begin()); + std::move(m_bufferR.begin() + drop, m_bufferR.end(), m_bufferR.begin()); + m_framesFilledUp -= drop; + } - // clean up before checking for more data from input buffer - m_framesFilledUp = 0; - } - } + #ifdef SA_DEBUG + // measure overall FFT processing speed + total_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - total_time; + m_dump_count++; + m_sum_execution += total_time / 1000000.0; + if (total_time / 1000000.0 > m_max_execution) {m_max_execution = total_time / 1000000.0;} + #endif + } // frame filler and processing + } // process if active + } // thread loop end } @@ -251,8 +321,9 @@ void SaProcessor::analyse(sampleFrame *in_buffer, const fpp_t frame_count) // Gamma correction is applied to make small values more visible and to make // a linear gradient actually appear roughly linear. The correction should be // around 0.42 to 0.45 for sRGB displays (or lower for bigger visibility boost). -QRgb SaProcessor::makePixel(float left, float right, float gamma_correction) const +QRgb SaProcessor::makePixel(float left, float right) const { + const float gamma_correction = m_controls->m_waterfallGammaModel.value(); if (m_controls->m_stereoModel.value()) { float ampL = pow(left, gamma_correction); @@ -265,9 +336,9 @@ QRgb SaProcessor::makePixel(float left, float right, float gamma_correction) con { float ampL = pow(left, gamma_correction); // make mono color brighter to compensate for the fact it is not summed - return qRgb(m_controls->m_colorMono.lighter().red() * ampL, - m_controls->m_colorMono.lighter().green() * ampL, - m_controls->m_colorMono.lighter().blue() * ampL); + return qRgb(m_controls->m_colorMonoW.red() * ampL, + m_controls->m_colorMonoW.green() * ampL, + m_controls->m_colorMonoW.blue() * ampL); } } @@ -301,6 +372,7 @@ void SaProcessor::reallocateBuffers() { new_in_size = FFT_BLOCK_SIZES.back(); } + m_zeroPadFactor = m_controls->m_zeroPaddingModel.value(); if (new_size_index + m_zeroPadFactor < FFT_BLOCK_SIZES.size()) { new_fft_size = FFT_BLOCK_SIZES[new_size_index + m_zeroPadFactor]; @@ -312,12 +384,16 @@ void SaProcessor::reallocateBuffers() new_bins = new_fft_size / 2 +1; - // Lock data shared with SaSpectrumView and SaWaterfallView. - // The m_reallocating is here to tell analyse() to avoid asking for the - // lock, since fftw3 can take a while to find the fastest FFT algorithm - // for given machine, which would produce interruption in the audio stream. + // Use m_reallocating to tell analyze() to avoid asking for the lock. This + // is needed because under heavy load the FFT thread requests data lock so + // often that this routine could end up waiting even for several seconds. m_reallocating = true; - QMutexLocker lock(&m_dataAccess); + + // Lock data shared with SaSpectrumView and SaWaterfallView. + // Reallocation lock must be acquired first to avoid deadlock (a view class + // may already have it and request the "stronger" data lock on top of that). + QMutexLocker reloc_lock(&m_reallocationAccess); + QMutexLocker data_lock(&m_dataAccess); // destroy old FFT plan and free the result buffer if (m_fftPlanL != NULL) {fftwf_destroy_plan(m_fftPlanL);} @@ -328,30 +404,42 @@ void SaProcessor::reallocateBuffers() // allocate new space, create new plan and resize containers m_fftWindow.resize(new_in_size, 1.0); precomputeWindow(m_fftWindow.data(), new_in_size, (FFT_WINDOWS) m_controls->m_windowModel.value()); - m_bufferL.resize(new_fft_size, 0); - m_bufferR.resize(new_fft_size, 0); + m_bufferL.resize(new_in_size, 0); + m_bufferR.resize(new_in_size, 0); + m_filteredBufferL.resize(new_fft_size, 0); + m_filteredBufferR.resize(new_fft_size, 0); m_spectrumL = (fftwf_complex *) fftwf_malloc(new_bins * sizeof (fftwf_complex)); m_spectrumR = (fftwf_complex *) fftwf_malloc(new_bins * sizeof (fftwf_complex)); - m_fftPlanL = fftwf_plan_dft_r2c_1d(new_fft_size, m_bufferL.data(), m_spectrumL, FFTW_MEASURE); - m_fftPlanR = fftwf_plan_dft_r2c_1d(new_fft_size, m_bufferR.data(), m_spectrumR, FFTW_MEASURE); + m_fftPlanL = fftwf_plan_dft_r2c_1d(new_fft_size, m_filteredBufferL.data(), m_spectrumL, FFTW_MEASURE); + m_fftPlanR = fftwf_plan_dft_r2c_1d(new_fft_size, m_filteredBufferR.data(), m_spectrumR, FFTW_MEASURE); if (m_fftPlanL == NULL || m_fftPlanR == NULL) { - std::cerr << "Failed to create new FFT plan!" << std::endl; + #ifdef SA_DEBUG + std::cerr << "Analyzer: failed to create new FFT plan!" << std::endl; + #endif } m_absSpectrumL.resize(new_bins, 0); m_absSpectrumR.resize(new_bins, 0); m_normSpectrumL.resize(new_bins, 0); m_normSpectrumR.resize(new_bins, 0); - m_history.resize(new_bins * m_waterfallHeight * sizeof qRgb(0,0,0), 0); + m_waterfallHeight = m_controls->m_waterfallHeightModel.value(); + m_history_work.resize((new_bins < m_waterfallMaxWidth ? new_bins : m_waterfallMaxWidth) + * m_waterfallHeight + * sizeof qRgb(0,0,0), 0); + m_history.resize((new_bins < m_waterfallMaxWidth ? new_bins : m_waterfallMaxWidth) + * m_waterfallHeight + * sizeof qRgb(0,0,0), 0); // done; publish new sizes and clean up m_inBlockSize = new_in_size; m_fftBlockSize = new_fft_size; - lock.unlock(); + data_lock.unlock(); + reloc_lock.unlock(); m_reallocating = false; + clear(); } @@ -369,17 +457,39 @@ void SaProcessor::rebuildWindow() // Note: may take a few milliseconds, do not call in a loop! void SaProcessor::clear() { + const unsigned int overlaps = m_controls->m_windowOverlapModel.value(); QMutexLocker lock(&m_dataAccess); - m_framesFilledUp = 0; + // If there is any window overlap, leave space only for the new samples + // and treat the rest at initialized with zeros. Prevents missing + // transients at the start of the very first block. + m_framesFilledUp = m_inBlockSize - m_inBlockSize / overlaps; std::fill(m_bufferL.begin(), m_bufferL.end(), 0); std::fill(m_bufferR.begin(), m_bufferR.end(), 0); + std::fill(m_filteredBufferL.begin(), m_filteredBufferL.end(), 0); + std::fill(m_filteredBufferR.begin(), m_filteredBufferR.end(), 0); std::fill(m_absSpectrumL.begin(), m_absSpectrumL.end(), 0); std::fill(m_absSpectrumR.begin(), m_absSpectrumR.end(), 0); std::fill(m_normSpectrumL.begin(), m_normSpectrumL.end(), 0); std::fill(m_normSpectrumR.begin(), m_normSpectrumR.end(), 0); + std::fill(m_history_work.begin(), m_history_work.end(), 0); std::fill(m_history.begin(), m_history.end(), 0); } +// Clear only history work buffer. Used to flush old data when waterfall +// is shown after a period of inactivity. +void SaProcessor::clearHistory() +{ + QMutexLocker lock(&m_dataAccess); + std::fill(m_history_work.begin(), m_history_work.end(), 0); +} + +// Check if result buffers contain any non-zero values +bool SaProcessor::spectrumNotEmpty() +{ + QMutexLocker lock(&m_reallocationAccess); + return notEmpty(m_normSpectrumL) || notEmpty(m_normSpectrumR); +} + // -------------------------------------- // Frequency conversion helpers @@ -407,6 +517,17 @@ unsigned int SaProcessor::binCount() const } +// Return the final width of waterfall display buffer. +// Normally the waterfall width equals the number of frequency bins, but the +// FFT transform can easily produce more bins than can be reasonably useful for +// currently used display resolutions. This function limits width of the final +// image to a given size, which is then used during waterfall render and display. +unsigned int SaProcessor::waterfallWidth() const +{ + return binCount() < m_waterfallMaxWidth ? binCount() : m_waterfallMaxWidth; +} + + // Return the center frequency of given frequency bin. float SaProcessor::binToFreq(unsigned int bin_index) const { @@ -499,10 +620,10 @@ float SaProcessor::getAmpRangeMin(bool linear) const switch (m_controls->m_ampRangeModel.value()) { case ARANGE_EXTENDED: return ARANGE_EXTENDED_START; - case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_START; - case ARANGE_NOISE: return ARANGE_NOISE_START; + case ARANGE_SILENT: return ARANGE_SILENT_START; + case ARANGE_LOUD: return ARANGE_LOUD_START; default: - case ARANGE_DEFAULT: return ARANGE_DEFAULT_START; + case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_START; } } @@ -512,10 +633,10 @@ float SaProcessor::getAmpRangeMax() const switch (m_controls->m_ampRangeModel.value()) { case ARANGE_EXTENDED: return ARANGE_EXTENDED_END; - case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_END; - case ARANGE_NOISE: return ARANGE_NOISE_END; + case ARANGE_SILENT: return ARANGE_SILENT_END; + case ARANGE_LOUD: return ARANGE_LOUD_END; default: - case ARANGE_DEFAULT: return ARANGE_DEFAULT_END; + case ARANGE_AUDIBLE: return ARANGE_AUDIBLE_END; } } diff --git a/plugins/SpectrumAnalyzer/SaProcessor.h b/plugins/SpectrumAnalyzer/SaProcessor.h index ae2df16f8..0c396b3c0 100644 --- a/plugins/SpectrumAnalyzer/SaProcessor.h +++ b/plugins/SpectrumAnalyzer/SaProcessor.h @@ -27,6 +27,7 @@ #ifndef SAPROCESSOR_H #define SAPROCESSOR_H +#include #include #include #include @@ -34,27 +35,45 @@ #include "fft_helpers.h" #include "SaControls.h" +template +class LocklessRingBuffer; //! Receives audio data, runs FFT analysis and stores the result. class SaProcessor { public: - explicit SaProcessor(SaControls *controls); + explicit SaProcessor(const SaControls *controls); virtual ~SaProcessor(); - void analyse(sampleFrame *in_buffer, const fpp_t frame_count); + // analysis thread and a method to terminate it + void analyze(LocklessRingBuffer &ring_buffer); + void terminate() {m_terminate = true;} // inform processor if any processing is actually required void setSpectrumActive(bool active); void setWaterfallActive(bool active); + void flipRequest() {m_flipRequest = true;} // request refresh of history buffer // configuration is taken from models in SaControls; some changes require // an exlicit update request (reallocation and window rebuild) void reallocateBuffers(); void rebuildWindow(); void clear(); + void clearHistory(); + + const float *getSpectrumL() const {return m_normSpectrumL.data();} + const float *getSpectrumR() const {return m_normSpectrumR.data();} + const uchar *getHistory() const {return m_history.data();} // information about results and unit conversion helpers + unsigned int inBlockSize() const {return m_inBlockSize;} + unsigned int binCount() const; //!< size of output (frequency domain) data block + bool spectrumNotEmpty(); //!< check if result buffers contain any non-zero values + + unsigned int waterfallWidth() const; //!< binCount value capped at 3840 (for display) + unsigned int waterfallHeight() const {return m_waterfallHeight;} + bool waterfallNotEmpty() const {return m_waterfallNotEmpty;} + float binToFreq(unsigned int bin_index) const; float binBandwidth() const; @@ -72,26 +91,38 @@ public: float getAmpRangeMin(bool linear = false) const; float getAmpRangeMax() const; - // data access lock must be acquired by any friendly class that touches - // the results, mainly to prevent unexpected mid-way reallocation + // Reallocation lock prevents the processor from changing size of its buffers. + // It is used to keep consistent bin-to-frequency mapping while drawing the + // spectrum and to make sure reading side does not find itself out of bounds. + // The processor is meanwhile free to work on another block. + QMutex m_reallocationAccess; + // Data access lock prevents the processor from changing both size and content + // of its buffers. It is used when writing to a result buffer, or when a friendly + // class reads them and needs guaranteed data consistency. + // It causes FFT analysis to be paused, so this lock should be used sparingly. + // If using both locks at the same time, reallocation lock MUST be acquired first. QMutex m_dataAccess; + private: - SaControls *m_controls; + const SaControls *m_controls; + + // thread communication and control + bool m_terminate; // currently valid configuration - const unsigned int m_zeroPadFactor = 2; //!< use n-steps bigger FFT for given block size - unsigned int m_inBlockSize; //!< size of input (time domain) data block + unsigned int m_zeroPadFactor = 2; //!< use n-steps bigger FFT for given block size + std::atomic m_inBlockSize;//!< size of input (time domain) data block unsigned int m_fftBlockSize; //!< size of padded block for FFT processing unsigned int m_sampleRate; - unsigned int binCount() const; //!< size of output (frequency domain) data block - // data buffers (roughly in the order of processing, from input to output) unsigned int m_framesFilledUp; std::vector m_bufferL; //!< time domain samples (left) std::vector m_bufferR; //!< time domain samples (right) std::vector m_fftWindow; //!< precomputed window function coefficients + std::vector m_filteredBufferL; //!< time domain samples with window function applied (left) + std::vector m_filteredBufferR; //!< time domain samples with window function applied (right) fftwf_plan m_fftPlanL; fftwf_plan m_fftPlanR; fftwf_complex *m_spectrumL; //!< frequency domain samples (complex) (left) @@ -102,21 +133,28 @@ private: std::vector m_normSpectrumR; //!< frequency domain samples (normalized) (right) // spectrum history for waterfall: new normSpectrum lines are added on top - std::vector m_history; - const unsigned int m_waterfallHeight = 200; // Number of stored lines. - // Note: high values may make it harder to see transients. + std::vector m_history_work; //!< local history buffer for render + std::vector m_history; //!< public buffer for reading + bool m_flipRequest; //!< update public buffer only when requested + std::atomic m_waterfallHeight; //!< number of stored lines in history buffer + // Note: high values may make it harder to see transients. + const unsigned int m_waterfallMaxWidth = 3840; // book keeping bool m_spectrumActive; bool m_waterfallActive; - unsigned int m_waterfallNotEmpty; + std::atomic m_waterfallNotEmpty; //!< number of lines remaining visible on display bool m_reallocating; // merge L and R channels and apply gamma correction to make a spectrogram pixel - QRgb makePixel(float left, float right, float gamma_correction = 0.30) const; + QRgb makePixel(float left, float right) const; - friend class SaSpectrumView; - friend class SaWaterfallView; + #ifdef SA_DEBUG + unsigned int m_last_dump_time; + unsigned int m_dump_count; + float m_sum_execution; + float m_max_execution; + #endif }; #endif // SAPROCESSOR_H diff --git a/plugins/SpectrumAnalyzer/SaSpectrumView.cpp b/plugins/SpectrumAnalyzer/SaSpectrumView.cpp index 746d52cfd..13aaeb724 100644 --- a/plugins/SpectrumAnalyzer/SaSpectrumView.cpp +++ b/plugins/SpectrumAnalyzer/SaSpectrumView.cpp @@ -39,7 +39,6 @@ #ifdef SA_DEBUG #include - #include #endif @@ -68,7 +67,11 @@ SaSpectrumView::SaSpectrumView(SaControls *controls, SaProcessor *processor, QWi m_logAmpTics = makeLogAmpTics(m_processor->getAmpRangeMin(), m_processor->getAmpRangeMax()); m_linearAmpTics = makeLinearAmpTics(m_processor->getAmpRangeMin(), m_processor->getAmpRangeMax()); - m_cursor = QPoint(0, 0); + m_cursor = QPointF(0, 0); + + #ifdef SA_DEBUG + m_execution_avg = m_path_avg = m_draw_avg = 0; + #endif } @@ -134,12 +137,20 @@ void SaSpectrumView::paintEvent(QPaintEvent *event) 2.0, 2.0); #ifdef SA_DEBUG - // display what FPS would be achieved if spectrum display ran in a loop + // display performance measurements if enabled total_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - total_time; + m_execution_avg = 0.95 * m_execution_avg + 0.05 * total_time / 1000000.0; painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); - painter.drawText(m_displayRight -100, 70, 100, 16, Qt::AlignLeft, - QString(std::string("Max FPS: " + std::to_string(1000000000.0 / total_time)).c_str())); + painter.drawText(m_displayRight -150, 10, 130, 16, Qt::AlignLeft, + QString("Exec avg.: ").append(std::to_string(m_execution_avg).substr(0, 5).c_str()).append(" ms")); + painter.drawText(m_displayRight -150, 30, 130, 16, Qt::AlignLeft, + QString("Buff. upd. avg: ").append(std::to_string(m_refresh_avg).substr(0, 5).c_str()).append(" ms")); + painter.drawText(m_displayRight -150, 50, 130, 16, Qt::AlignLeft, + QString("Path build avg: ").append(std::to_string(m_path_avg).substr(0, 5).c_str()).append(" ms")); + painter.drawText(m_displayRight -150, 70, 130, 16, Qt::AlignLeft, + QString("Path draw avg: ").append(std::to_string(m_draw_avg).substr(0, 5).c_str()).append(" ms")); + #endif } @@ -148,22 +159,14 @@ void SaSpectrumView::paintEvent(QPaintEvent *event) void SaSpectrumView::drawSpectrum(QPainter &painter) { #ifdef SA_DEBUG - int path_time = 0, draw_time = 0; + int draw_time = 0; #endif // draw the graph only if there is any input, averaging residue or peaks - QMutexLocker lock(&m_processor->m_dataAccess); - if (m_decaySum > 0 || notEmpty(m_processor->m_normSpectrumL) || notEmpty(m_processor->m_normSpectrumR)) + if (m_decaySum > 0 || m_processor->spectrumNotEmpty()) { - lock.unlock(); - #ifdef SA_DEBUG - path_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - #endif // update data buffers and reconstruct paths refreshPaths(); - #ifdef SA_DEBUG - path_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - path_time; - #endif // draw stored paths #ifdef SA_DEBUG @@ -199,17 +202,10 @@ void SaSpectrumView::drawSpectrum(QPainter &painter) draw_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - draw_time; #endif } - else - { - lock.unlock(); - } #ifdef SA_DEBUG - // display measurement results - painter.drawText(m_displayRight -100, 90, 100, 16, Qt::AlignLeft, - QString(std::string("Path ms: " + std::to_string(path_time / 1000000.0)).c_str())); - painter.drawText(m_displayRight -100, 110, 100, 16, Qt::AlignLeft, - QString(std::string("Draw ms: " + std::to_string(draw_time / 1000000.0)).c_str())); + // save performance measurement result + m_draw_avg = 0.95 * m_draw_avg + 0.05 * draw_time / 1000000.0; #endif } @@ -218,9 +214,9 @@ void SaSpectrumView::drawSpectrum(QPainter &painter) // and build QPainter paths. void SaSpectrumView::refreshPaths() { - // Lock is required for the entire function, mainly to prevent block size - // changes from causing reallocation of data structures mid-way. - QMutexLocker lock(&m_processor->m_dataAccess); + // Reallocation lock is required for the entire function, to keep display + // buffer size consistent with block size. + QMutexLocker reloc_lock(&m_processor->m_reallocationAccess); // check if bin count changed and reallocate display buffers accordingly if (m_processor->binCount() != m_displayBufferL.size()) @@ -240,8 +236,8 @@ void SaSpectrumView::refreshPaths() int refresh_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); #endif m_decaySum = 0; - updateBuffers(m_processor->m_normSpectrumL.data(), m_displayBufferL.data(), m_peakBufferL.data()); - updateBuffers(m_processor->m_normSpectrumR.data(), m_displayBufferR.data(), m_peakBufferR.data()); + updateBuffers(m_processor->getSpectrumL(), m_displayBufferL.data(), m_peakBufferL.data()); + updateBuffers(m_processor->getSpectrumR(), m_displayBufferR.data(), m_peakBufferR.data()); #ifdef SA_DEBUG refresh_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - refresh_time; #endif @@ -254,41 +250,43 @@ void SaSpectrumView::refreshPaths() } #ifdef SA_DEBUG - int make_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + int path_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); #endif // Use updated display buffers to prepare new paths for QPainter. // This is the second slowest action (first is the subsequent drawing); use // the resolution parameter to balance display quality and performance. - m_pathL = makePath(m_displayBufferL, 1.5); + m_pathL = makePath(m_displayBufferL, m_controls->m_spectrumResolutionModel.value()); if (m_controls->m_stereoModel.value()) { - m_pathR = makePath(m_displayBufferR, 1.5); + m_pathR = makePath(m_displayBufferR, m_controls->m_spectrumResolutionModel.value()); } if (m_controls->m_peakHoldModel.value() || m_controls->m_refFreezeModel.value()) { - m_pathPeakL = makePath(m_peakBufferL, 0.25); + m_pathPeakL = makePath(m_peakBufferL, m_controls->m_envelopeResolutionModel.value()); if (m_controls->m_stereoModel.value()) { - m_pathPeakR = makePath(m_peakBufferR, 0.25); + m_pathPeakR = makePath(m_peakBufferR, m_controls->m_envelopeResolutionModel.value()); } } #ifdef SA_DEBUG - make_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - make_time; + path_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - path_time; #endif #ifdef SA_DEBUG - // print measurement results - std::cout << "Buffer update ms: " << std::to_string(refresh_time / 1000000.0) << ", "; - std::cout << "Path-make ms: " << std::to_string(make_time / 1000000.0) << std::endl; + // save performance measurement results + m_refresh_avg = 0.95 * m_refresh_avg + 0.05 * refresh_time / 1000000.0; + m_path_avg = .95f * m_path_avg + .05f * path_time / 1000000.f; #endif } // Update display buffers: add new data, update average and peaks / reference. // Output the sum of all displayed values -- draw only if it is non-zero. -// NOTE: The calling function is responsible for acquiring SaProcessor data -// access lock! -void SaSpectrumView::updateBuffers(float *spectrum, float *displayBuffer, float *peakBuffer) +// NOTE: The calling function is responsible for acquiring SaProcessor +// reallocation access lock! Data access lock is not needed: the final result +// buffer is updated very quickly and the worst case is that one frame will be +// part new, part old. At reasonable frame rate, such difference is invisible.. +void SaSpectrumView::updateBuffers(const float *spectrum, float *displayBuffer, float *peakBuffer) { for (int n = 0; n < m_processor->binCount(); n++) { @@ -297,7 +295,8 @@ void SaSpectrumView::updateBuffers(float *spectrum, float *displayBuffer, float { if (m_controls->m_smoothModel.value()) { - displayBuffer[n] = spectrum[n] * m_smoothFactor + displayBuffer[n] * (1 - m_smoothFactor); + const float smoothFactor = m_controls->m_averagingWeightModel.value(); + displayBuffer[n] = spectrum[n] * smoothFactor + displayBuffer[n] * (1 - smoothFactor); } else { @@ -319,7 +318,7 @@ void SaSpectrumView::updateBuffers(float *spectrum, float *displayBuffer, float } else if (!m_controls->m_refFreezeModel.value()) { - peakBuffer[n] = peakBuffer[n] * m_peakDecayFactor; + peakBuffer[n] = peakBuffer[n] * m_controls->m_peakDecayFactorModel.value(); } } else if (!m_controls->m_refFreezeModel.value() && !m_controls->m_peakHoldModel.value()) @@ -539,38 +538,52 @@ void SaSpectrumView::drawGrid(QPainter &painter) // Draw cursor and its coordinates if it is within display bounds. void SaSpectrumView::drawCursor(QPainter &painter) { - if( m_cursor.x() >= m_displayLeft + if ( m_cursor.x() >= m_displayLeft && m_cursor.x() <= m_displayRight && m_cursor.y() >= m_displayTop && m_cursor.y() <= m_displayBottom) { // cursor lines painter.setPen(QPen(m_controls->m_colorGrid.lighter(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); - painter.drawLine(m_cursor.x(), m_displayTop, m_cursor.x(), m_displayBottom); - painter.drawLine(m_displayLeft, m_cursor.y(), m_displayRight, m_cursor.y()); + painter.drawLine(QPointF(m_cursor.x(), m_displayTop), QPointF(m_cursor.x(), m_displayBottom)); + painter.drawLine(QPointF(m_displayLeft, m_cursor.y()), QPointF(m_displayRight, m_cursor.y())); - // coordinates + // coordinates: background box + QFontMetrics fontMetrics = painter.fontMetrics(); + unsigned int const box_left = 5; + unsigned int const box_top = 5; + unsigned int const box_margin = 3; + unsigned int const box_height = 2*(fontMetrics.size(Qt::TextSingleLine, "0 HzdBFS").height() + box_margin); + unsigned int const box_width = fontMetrics.size(Qt::TextSingleLine, "-99.9 dBFS").width() + 2*box_margin; painter.setPen(QPen(m_controls->m_colorLabels.darker(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); - painter.drawText(m_displayRight -60, 5, 100, 16, Qt::AlignLeft, "Cursor"); + painter.fillRect(m_displayLeft + box_left, m_displayTop + box_top, + box_width, box_height, QColor(0, 0, 0, 64)); + // coordinates: text + painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); QString tmps; + // frequency int xFreq = (int)m_processor->xPixelToFreq(m_cursor.x() - m_displayLeft, m_displayWidth); - tmps = QString(std::string(std::to_string(xFreq) + " Hz").c_str()); - painter.drawText(m_displayRight -60, 18, 100, 16, Qt::AlignLeft, tmps); + tmps = QString("%1 Hz").arg(xFreq); + painter.drawText(m_displayLeft + box_left + box_margin, + m_displayTop + box_top + box_margin, + box_width, box_height / 2, Qt::AlignLeft, tmps); // amplitude float yAmp = m_processor->yPixelToAmp(m_cursor.y(), m_displayBottom); if (m_controls->m_logYModel.value()) { - tmps = QString(std::string(std::to_string(yAmp).substr(0, 5) + " dB").c_str()); + tmps = QString(std::to_string(yAmp).substr(0, 5).c_str()).append(" dBFS"); } else { // add 0.0005 to get proper rounding to 3 decimal places - tmps = QString(std::string(std::to_string(0.0005f + yAmp)).substr(0, 5).c_str()); + tmps = QString(std::to_string(0.0005f + yAmp).substr(0, 5).c_str()); } - painter.drawText(m_displayRight -60, 30, 100, 16, Qt::AlignLeft, tmps); + painter.drawText(m_displayLeft + box_left + box_margin, + m_displayTop + box_top + box_height / 2, + box_width, box_height / 2, Qt::AlignLeft, tmps); } } @@ -774,14 +787,18 @@ void SaSpectrumView::periodicUpdate() // Handle mouse input: set new cursor position. +// For some reason (a bug?), localPos() only returns integers. As a workaround +// the fractional part is taken from windowPos() (which works correctly). void SaSpectrumView::mouseMoveEvent(QMouseEvent *event) { - m_cursor = event->pos(); + m_cursor = QPointF( event->localPos().x() - (event->windowPos().x() - (long)event->windowPos().x()), + event->localPos().y() - (event->windowPos().y() - (long)event->windowPos().y())); } void SaSpectrumView::mousePressEvent(QMouseEvent *event) { - m_cursor = event->pos(); + m_cursor = QPointF( event->localPos().x() - (event->windowPos().x() - (long)event->windowPos().x()), + event->localPos().y() - (event->windowPos().y() - (long)event->windowPos().y())); } diff --git a/plugins/SpectrumAnalyzer/SaSpectrumView.h b/plugins/SpectrumAnalyzer/SaSpectrumView.h index 0db5852e1..b59264d9c 100644 --- a/plugins/SpectrumAnalyzer/SaSpectrumView.h +++ b/plugins/SpectrumAnalyzer/SaSpectrumView.h @@ -27,6 +27,8 @@ #ifndef SASPECTRUMVIEW_H #define SASPECTRUMVIEW_H +#include "SaControls.h" + #include #include #include @@ -34,7 +36,6 @@ class QMouseEvent; class QPainter; -class SaControls; class SaProcessor; //! Widget that displays a spectrum curve and frequency / amplitude grid @@ -84,7 +85,7 @@ private: std::vector m_displayBufferR; std::vector m_peakBufferL; std::vector m_peakBufferR; - void updateBuffers(float *spectrum, float *displayBuffer, float *peakBuffer); + void updateBuffers(const float *spectrum, float *displayBuffer, float *peakBuffer); // final paths to be drawn by QPainter and methods to build them QPainterPath m_pathL; @@ -99,14 +100,11 @@ private: bool m_freezeRequest; // new reference should be acquired bool m_frozen; // a reference is currently stored in the peakBuffer - const float m_smoothFactor = 0.15; // alpha for exponential smoothing - const float m_peakDecayFactor = 0.992; // multiplier for gradual peak decay - // top level: refresh buffers, make paths and draw the spectrum void drawSpectrum(QPainter &painter); // current cursor location and a method to draw it - QPoint m_cursor; + QPointF m_cursor; void drawCursor(QPainter &painter); // wrappers for most used SaProcessor conversion helpers @@ -121,6 +119,13 @@ private: unsigned int m_displayLeft; unsigned int m_displayRight; unsigned int m_displayWidth; + + #ifdef SA_DEBUG + float m_execution_avg; + float m_refresh_avg; + float m_path_avg; + float m_draw_avg; + #endif }; #endif // SASPECTRUMVIEW_H diff --git a/plugins/SpectrumAnalyzer/SaWaterfallView.cpp b/plugins/SpectrumAnalyzer/SaWaterfallView.cpp index 617e80b2c..e015d31ef 100644 --- a/plugins/SpectrumAnalyzer/SaWaterfallView.cpp +++ b/plugins/SpectrumAnalyzer/SaWaterfallView.cpp @@ -23,8 +23,12 @@ #include "SaWaterfallView.h" #include +#ifdef SA_DEBUG + #include +#endif #include #include +#include #include #include #include @@ -47,8 +51,22 @@ SaWaterfallView::SaWaterfallView(SaControls *controls, SaProcessor *processor, Q connect(gui->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(periodicUpdate())); + m_displayTop = 1; + m_displayBottom = height() -2; + m_displayLeft = 26; + m_displayRight = width() -26; + m_displayWidth = m_displayRight - m_displayLeft; + m_displayHeight = m_displayBottom - m_displayTop; + m_timeTics = makeTimeTics(); - m_oldTimePerLine = (float)m_processor->m_inBlockSize / m_processor->getSampleRate(); + m_oldSecondsPerLine = 0; + m_oldHeight = 0; + + m_cursor = QPointF(0, 0); + + #ifdef SA_DEBUG + m_execution_avg = 0; + #endif } @@ -58,15 +76,14 @@ SaWaterfallView::SaWaterfallView(SaControls *controls, SaProcessor *processor, Q void SaWaterfallView::paintEvent(QPaintEvent *event) { #ifdef SA_DEBUG - int start_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + unsigned int draw_time = std::chrono::high_resolution_clock::now().time_since_epoch().count(); #endif - // all drawing done here, local variables are sufficient for the boundary - const int displayTop = 1; - const int displayBottom = height() -2; - const int displayLeft = 26; - const int displayRight = width() -26; - const int displayWidth = displayRight - displayLeft; + // update boundary + m_displayBottom = height() -2; + m_displayRight = width() -26; + m_displayWidth = m_displayRight - m_displayLeft; + m_displayHeight = m_displayBottom - m_displayTop; float label_width = 20; float label_height = 16; float margin = 2; @@ -75,10 +92,11 @@ void SaWaterfallView::paintEvent(QPaintEvent *event) painter.setRenderHint(QPainter::Antialiasing, true); // check if time labels need to be rebuilt - if ((float)m_processor->m_inBlockSize / m_processor->getSampleRate() != m_oldTimePerLine) + if (secondsPerLine() != m_oldSecondsPerLine || m_processor->waterfallHeight() != m_oldHeight) { m_timeTics = makeTimeTics(); - m_oldTimePerLine = (float)m_processor->m_inBlockSize / m_processor->getSampleRate(); + m_oldSecondsPerLine = secondsPerLine(); + m_oldHeight = m_processor->waterfallHeight(); } // print time labels @@ -86,78 +104,104 @@ void SaWaterfallView::paintEvent(QPaintEvent *event) painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); for (auto & line: m_timeTics) { - pos = timeToYPixel(line.first, displayBottom); + pos = timeToYPixel(line.first, m_displayHeight); // align first and last label to the edge if needed, otherwise center them if (line == m_timeTics.front() && pos < label_height / 2) { - painter.drawText(displayLeft - label_width - margin, displayTop - 1, + painter.drawText(m_displayLeft - label_width - margin, m_displayTop - 1, label_width, label_height, Qt::AlignRight | Qt::AlignTop | Qt::TextDontClip, QString(line.second.c_str())); - painter.drawText(displayRight + margin, displayTop - 1, + painter.drawText(m_displayRight + margin, m_displayTop - 1, label_width, label_height, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip, QString(line.second.c_str())); } - else if (line == m_timeTics.back() && pos > displayBottom - label_height + 2) + else if (line == m_timeTics.back() && pos > m_displayBottom - label_height + 2) { - painter.drawText(displayLeft - label_width - margin, displayBottom - label_height, + painter.drawText(m_displayLeft - label_width - margin, m_displayBottom - label_height, label_width, label_height, Qt::AlignRight | Qt::AlignBottom | Qt::TextDontClip, QString(line.second.c_str())); - painter.drawText(displayRight + margin, displayBottom - label_height + 2, + painter.drawText(m_displayRight + margin, m_displayBottom - label_height + 2, label_width, label_height, Qt::AlignLeft | Qt::AlignBottom | Qt::TextDontClip, QString(line.second.c_str())); } else { - painter.drawText(displayLeft - label_width - margin, pos - label_height / 2, + painter.drawText(m_displayLeft - label_width - margin, pos - label_height / 2, label_width, label_height, Qt::AlignRight | Qt::AlignVCenter | Qt::TextDontClip, QString(line.second.c_str())); - painter.drawText(displayRight + margin, pos - label_height / 2, + painter.drawText(m_displayRight + margin, pos - label_height / 2, label_width, label_height, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip, QString(line.second.c_str())); } } // draw the spectrogram precomputed in SaProcessor - if (m_processor->m_waterfallNotEmpty) + if (m_processor->waterfallNotEmpty()) { - QMutexLocker lock(&m_processor->m_dataAccess); - painter.drawImage(displayLeft, displayTop, // top left corner coordinates - QImage(m_processor->m_history.data(), // raw pixel data to display - m_processor->binCount(), // width = number of frequency bins - m_processor->m_waterfallHeight, // height = number of history lines - QImage::Format_RGB32 - ).scaled(displayWidth, // scale to fit view.. - displayBottom, - Qt::IgnoreAspectRatio, - Qt::SmoothTransformation)); + QMutexLocker lock(&m_processor->m_reallocationAccess); + QImage temp = QImage(m_processor->getHistory(), // raw pixel data to display + m_processor->waterfallWidth(), // width = number of frequency bins + m_processor->waterfallHeight(), // height = number of history lines + QImage::Format_RGB32); lock.unlock(); + temp.setDevicePixelRatio(devicePixelRatio()); // display at native resolution + painter.drawImage(m_displayLeft, m_displayTop, + temp.scaled(m_displayWidth * devicePixelRatio(), + m_displayHeight * devicePixelRatio(), + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation)); + m_processor->flipRequest(); } else { - painter.fillRect(displayLeft, displayTop, displayWidth, displayBottom, QColor(0,0,0)); + painter.fillRect(m_displayLeft, m_displayTop, m_displayWidth, m_displayHeight, QColor(0,0,0)); } + // draw cursor (if it is within bounds) + drawCursor(painter); + // always draw the outline painter.setPen(QPen(m_controls->m_colorGrid, 2, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); - painter.drawRoundedRect(displayLeft, displayTop, displayWidth, displayBottom, 2.0, 2.0); + painter.drawRoundedRect(m_displayLeft, m_displayTop, m_displayWidth, m_displayHeight, 2.0, 2.0); #ifdef SA_DEBUG - // display what FPS would be achieved if waterfall ran in a loop - start_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - start_time; + draw_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - draw_time; + m_execution_avg = 0.95 * m_execution_avg + 0.05 * draw_time / 1000000.0; painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); - painter.drawText(displayRight -100, 10, 100, 16, Qt::AlignLeft, - QString(std::string("Max FPS: " + std::to_string(1000000000.0 / start_time)).c_str())); + painter.drawText(m_displayRight -150, 10, 100, 16, Qt::AlignLeft, + QString("Exec avg.: ").append(std::to_string(m_execution_avg).substr(0, 5).c_str()).append(" ms")); #endif } +// Helper functions for time conversion +float SaWaterfallView::samplesPerLine() +{ + return (float)m_processor->inBlockSize() / m_controls->m_windowOverlapModel.value(); +} + +float SaWaterfallView::secondsPerLine() +{ + return samplesPerLine() / m_processor->getSampleRate(); +} + + // Convert time value to Y coordinate for display of given height. float SaWaterfallView::timeToYPixel(float time, int height) { - float pixels_per_line = (float)height / m_processor->m_waterfallHeight; - float seconds_per_line = ((float)m_processor->m_inBlockSize / m_processor->getSampleRate()); + float pixels_per_line = (float)height / m_processor->waterfallHeight(); - return pixels_per_line * time / seconds_per_line; + return pixels_per_line * time / secondsPerLine(); +} + + +// Convert Y coordinate on display of given height back to time value. +float SaWaterfallView::yPixelToTime(float position, int height) +{ + if (height == 0) {height = 1;} + float pixels_per_line = (float)height / m_processor->waterfallHeight(); + + return (position / pixels_per_line) * secondsPerLine(); } @@ -167,16 +211,21 @@ std::vector> SaWaterfallView::makeTimeTics() std::vector> result; float i; - // upper limit defined by number of lines * time per line - float limit = m_processor->m_waterfallHeight * ((float)m_processor->m_inBlockSize / m_processor->getSampleRate()); + // get time value of the last line + float limit = yPixelToTime(m_displayBottom, m_displayHeight); - // set increment so that about 8 tics are generated - float increment = std::round(10 * limit / 7) / 10; + // set increment to about 30 pixels (but min. 0.1 s) + float increment = std::round(10 * limit / (m_displayHeight / 30)) / 10; + if (increment < 0.1) {increment = 0.1;} // NOTE: labels positions are rounded to match the (rounded) label value for (i = 0; i <= limit; i += increment) { - if (i < 10) + if (i > 99) + { + result.emplace_back(std::round(i), std::to_string(std::round(i)).substr(0, 3)); + } + else if (i < 10) { result.emplace_back(std::round(i * 10) / 10, std::to_string(std::round(i * 10) / 10).substr(0, 3)); } @@ -208,10 +257,7 @@ void SaWaterfallView::updateVisibility() if (m_controls->m_waterfallModel.value()) { // clear old data before showing the waterfall - QMutexLocker lock(&m_processor->m_dataAccess); - std::fill(m_processor->m_history.begin(), m_processor->m_history.end(), 0); - lock.unlock(); - + m_processor->clearHistory(); setVisible(true); // increase window size if it is too small @@ -228,3 +274,70 @@ void SaWaterfallView::updateVisibility() } } + +// Draw cursor and its coordinates if it is within display bounds. +void SaWaterfallView::drawCursor(QPainter &painter) +{ + if ( m_cursor.x() >= m_displayLeft + && m_cursor.x() <= m_displayRight + && m_cursor.y() >= m_displayTop + && m_cursor.y() <= m_displayBottom) + { + // cursor lines + painter.setPen(QPen(m_controls->m_colorGrid.lighter(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); + painter.drawLine(QPointF(m_cursor.x(), m_displayTop), QPointF(m_cursor.x(), m_displayBottom)); + painter.drawLine(QPointF(m_displayLeft, m_cursor.y()), QPointF(m_displayRight, m_cursor.y())); + + // coordinates: background box + QFontMetrics fontMetrics = painter.fontMetrics(); + unsigned int const box_left = 5; + unsigned int const box_top = 5; + unsigned int const box_margin = 3; + unsigned int const box_height = 2*(fontMetrics.size(Qt::TextSingleLine, "0 Hz").height() + box_margin); + unsigned int const box_width = fontMetrics.size(Qt::TextSingleLine, "20000 Hz ").width() + 2*box_margin; + painter.setPen(QPen(m_controls->m_colorLabels.darker(), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); + painter.fillRect(m_displayLeft + box_left, m_displayTop + box_top, + box_width, box_height, QColor(0, 0, 0, 64)); + + // coordinates: text + painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin)); + QString tmps; + + // frequency + int freq = (int)m_processor->xPixelToFreq(m_cursor.x() - m_displayLeft, m_displayWidth); + tmps = QString("%1 Hz").arg(freq); + painter.drawText(m_displayLeft + box_left + box_margin, + m_displayTop + box_top + box_margin, + box_width, box_height / 2, Qt::AlignLeft, tmps); + + // time + float time = yPixelToTime(m_cursor.y(), m_displayBottom); + tmps = QString(std::to_string(time).substr(0, 5).c_str()).append(" s"); + painter.drawText(m_displayLeft + box_left + box_margin, + m_displayTop + box_top + box_height / 2, + box_width, box_height / 2, Qt::AlignLeft, tmps); + } +} + + +// Handle mouse input: set new cursor position. +// For some reason (a bug?), localPos() only returns integers. As a workaround +// the fractional part is taken from windowPos() (which works correctly). +void SaWaterfallView::mouseMoveEvent(QMouseEvent *event) +{ + m_cursor = QPointF( event->localPos().x() - (event->windowPos().x() - (long)event->windowPos().x()), + event->localPos().y() - (event->windowPos().y() - (long)event->windowPos().y())); +} + +void SaWaterfallView::mousePressEvent(QMouseEvent *event) +{ + m_cursor = QPointF( event->localPos().x() - (event->windowPos().x() - (long)event->windowPos().x()), + event->localPos().y() - (event->windowPos().y() - (long)event->windowPos().y())); +} + + +// Handle resize event: rebuild time labels +void SaWaterfallView::resizeEvent(QResizeEvent *event) +{ + m_timeTics = makeTimeTics(); +} diff --git a/plugins/SpectrumAnalyzer/SaWaterfallView.h b/plugins/SpectrumAnalyzer/SaWaterfallView.h index 0e104c0a1..bd91d6d16 100644 --- a/plugins/SpectrumAnalyzer/SaWaterfallView.h +++ b/plugins/SpectrumAnalyzer/SaWaterfallView.h @@ -32,6 +32,7 @@ #include "SaControls.h" #include "SaProcessor.h" +class QMouseEvent; // Widget that displays a spectrum waterfall (spectrogram) and time labels. class SaWaterfallView : public QWidget @@ -48,6 +49,9 @@ public: protected: void paintEvent(QPaintEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void resizeEvent(QResizeEvent *event) override; private slots: void periodicUpdate(); @@ -58,9 +62,29 @@ private: const EffectControlDialog *m_controlDialog; // Methods and data used to make time labels - float m_oldTimePerLine; + float m_oldSecondsPerLine; + float m_oldHeight; + float samplesPerLine(); + float secondsPerLine(); float timeToYPixel(float time, int height); + float yPixelToTime(float position, int height); std::vector> makeTimeTics(); std::vector> m_timeTics; // 0..n (s) + + // current cursor location and a method to draw it + QPointF m_cursor; + void drawCursor(QPainter &painter); + + // current boundaries for drawing + unsigned int m_displayTop; + unsigned int m_displayBottom; + unsigned int m_displayLeft; + unsigned int m_displayRight; + unsigned int m_displayWidth; + unsigned int m_displayHeight; + + #ifdef SA_DEBUG + float m_execution_avg; + #endif }; #endif // SAWATERFALLVIEW_H diff --git a/plugins/SpectrumAnalyzer/advanced_off.svg b/plugins/SpectrumAnalyzer/advanced_off.svg new file mode 100644 index 000000000..6d3ed82b1 --- /dev/null +++ b/plugins/SpectrumAnalyzer/advanced_off.svg @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff --git a/plugins/SpectrumAnalyzer/advanced_on.svg b/plugins/SpectrumAnalyzer/advanced_on.svg new file mode 100644 index 000000000..9e6b1ca3f --- /dev/null +++ b/plugins/SpectrumAnalyzer/advanced_on.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/plugins/SpectrumAnalyzer/advanced_src.svg b/plugins/SpectrumAnalyzer/advanced_src.svg new file mode 100644 index 000000000..ae201aad0 --- /dev/null +++ b/plugins/SpectrumAnalyzer/advanced_src.svg @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + ADV. + + + diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index 473e7702f..bdc4a4d86 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -10,3 +10,22 @@ ENDIF() ADD_SUBDIRECTORY(rpmalloc) ADD_SUBDIRECTORY(weakjack) + +# The lockless ring buffer library is compiled as part of the core +SET(RINGBUFFER_DIR "${CMAKE_SOURCE_DIR}/src/3rdparty/ringbuffer/") +SET(RINGBUFFER_DIR ${RINGBUFFER_DIR} PARENT_SCOPE) +# Create a dummy ringbuffer_export.h, since ringbuffer is not compiled as a library +FILE(WRITE ${CMAKE_BINARY_DIR}/src/ringbuffer_export.h + "#include \"${CMAKE_BINARY_DIR}/src/lmms_export.h\"\n + #define RINGBUFFER_EXPORT LMMS_EXPORT") +# Enable MLOCK support for ringbuffer if available +INCLUDE(CheckIncludeFiles) +CHECK_INCLUDE_FILES(sys/mman.h HAVE_SYS_MMAN) +IF(HAVE_SYS_MMAN) + SET(USE_MLOCK ON) +ELSE() + SET(USE_MLOCK OFF) +ENDIF() +# Generate ringbuffer configuration headers +CONFIGURE_FILE(${RINGBUFFER_DIR}/src/ringbuffer-config.h.in ${CMAKE_BINARY_DIR}/src/ringbuffer-config.h) +CONFIGURE_FILE(${RINGBUFFER_DIR}/src/ringbuffer-version.h.in ${CMAKE_BINARY_DIR}/src/ringbuffer-version.h) diff --git a/src/3rdparty/ringbuffer b/src/3rdparty/ringbuffer new file mode 160000 index 000000000..82ed7cfb9 --- /dev/null +++ b/src/3rdparty/ringbuffer @@ -0,0 +1 @@ +Subproject commit 82ed7cfb9ad40467421d8b14ca1af0350e92613c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ed0deefd1..59710926d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,7 @@ INCLUDE_DIRECTORIES( "${CMAKE_BINARY_DIR}/include" "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/include" + "${RINGBUFFER_DIR}/include" ) IF(WIN32 AND MSVC) @@ -89,6 +90,8 @@ IF(NOT ("${LAME_INCLUDE_DIRS}" STREQUAL "")) INCLUDE_DIRECTORIES("${LAME_INCLUDE_DIRS}") ENDIF() +LIST(APPEND LMMS_SRCS "${RINGBUFFER_DIR}/src/lib/ringbuffer.cpp") + # Use libraries in non-standard directories (e.g., another version of Qt) IF(LMMS_BUILD_LINUX) LINK_LIBRARIES(-Wl,--enable-new-dtags) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ba41e089c..a50b32a0f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,5 +1,6 @@ set(LMMS_SRCS ${LMMS_SRCS} + core/AutomatableModel.cpp core/AutomationPattern.cpp core/BandLimitedWave.cpp diff --git a/src/core/fft_helpers.cpp b/src/core/fft_helpers.cpp index bc7d289e3..2cf54b7f2 100644 --- a/src/core/fft_helpers.cpp +++ b/src/core/fft_helpers.cpp @@ -66,6 +66,7 @@ int normalize(const float *abs_spectrum, float *norm_spectrum, unsigned int bin_ if (abs_spectrum == NULL || norm_spectrum == NULL) {return -1;} if (bin_count == 0 || block_size == 0) {return -1;} + block_size /= 2; for (i = 0; i < bin_count; i++) { norm_spectrum[i] = abs_spectrum[i] / block_size; From a2e328e3dd8cc33fdafde7ed2dfe99aec4b77f1c Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Fri, 22 Nov 2019 21:26:47 +0900 Subject: [PATCH 099/160] Fix crash on deleting instrument with controller connections on knobs (#5306) Knob::friendlyUpdate() can be called after the model is deleted due to signal-slot connections. Adding a check for the model fixes a crash due to null pointer dereference. --- src/gui/widgets/Knob.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 7b6038449..3932795b7 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -803,9 +803,9 @@ void Knob::enterValue() void Knob::friendlyUpdate() { - if( model()->controllerConnection() == NULL || + if (model() && (model()->controllerConnection() == NULL || model()->controllerConnection()->getController()->frequentUpdates() == false || - Controller::runningFrames() % (256*4) == 0 ) + Controller::runningFrames() % (256*4) == 0)) { update(); } From 578a9475ecf7b8c529bb7edb86885987aa77642d Mon Sep 17 00:00:00 2001 From: Cyp <48363+Cyp@users.noreply.github.com> Date: Fri, 22 Nov 2019 14:26:00 +0100 Subject: [PATCH 100/160] Fix invalid read in RemotePlugin::RemotePlugin() on opening the ZynAddSubFx GUI. (#5299) Calling .toUtf8().constData() returns a pointer which is invalid at the end of the statement. --- src/core/RemotePlugin.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index d82bc61af..f816b7529 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -101,14 +101,14 @@ RemotePlugin::RemotePlugin() : m_socketFile = QDir::tempPath() + QDir::separator() + QUuid::createUuid().toString(); - const char * path = m_socketFile.toUtf8().constData(); - size_t length = strlen( path ); + auto path = m_socketFile.toUtf8(); + size_t length = path.length(); if ( length >= sizeof sa.sun_path ) { length = sizeof sa.sun_path - 1; qWarning( "Socket path too long." ); } - memcpy( sa.sun_path, path, length ); + memcpy(sa.sun_path, path.constData(), length ); sa.sun_path[length] = '\0'; m_server = socket( PF_LOCAL, SOCK_STREAM, 0 ); @@ -116,7 +116,7 @@ RemotePlugin::RemotePlugin() : { qWarning( "Unable to start the server." ); } - remove( path ); + remove(path.constData()); int ret = bind( m_server, (struct sockaddr *) &sa, sizeof sa ); if ( ret == -1 || listen( m_server, 1 ) == -1 ) { From b11e8eb33b7000d98009f0ce3ca38ac4423e2d2f Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 23 Nov 2019 20:24:13 -0700 Subject: [PATCH 101/160] SpectrumAnalyzer: Fix typo --- plugins/SpectrumAnalyzer/SaControlsDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp index d89cc1093..f1aad2a01 100644 --- a/plugins/SpectrumAnalyzer/SaControlsDialog.cpp +++ b/plugins/SpectrumAnalyzer/SaControlsDialog.cpp @@ -183,7 +183,7 @@ SaControlsDialog::SaControlsDialog(SaControls *controls, SaProcessor *processor) blockSizeLabel->setPixmap(blockSizeIcon->scaled(iconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); config_layout->addWidget(blockSizeLabel, 0, 4, 2, 1, Qt::AlignRight); - ComboBox *blockSizeCombo = new ComboBox(this, tr("FFT block bize")); + ComboBox *blockSizeCombo = new ComboBox(this, tr("FFT block size")); blockSizeCombo->setToolTip(tr("FFT block size")); blockSizeCombo->setMinimumSize(100, 22); blockSizeCombo->setMaximumSize(200, 22); From a9e3e70ae3d28e7aa846904edae71863a9734a71 Mon Sep 17 00:00:00 2001 From: liushuyu Date: Sat, 23 Nov 2019 23:05:25 -0700 Subject: [PATCH 102/160] filebrowser: making the string more flexible for i18n --- src/gui/FileBrowser.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 7eeb87ed2..5abbdf82f 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -444,8 +444,7 @@ void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) if( !dataFile.validate( f->extension() ) ) { QMessageBox::warning( 0, tr ( "Error" ), - f->fullName() + " " + tr( "does not appear to be a valid" ) + " " + f->extension() + - " " + tr( "file" ), + tr( "%1 does not appear to be a valid %2 file" ).arg( f->fullName(), f->extension() ), QMessageBox::Ok, QMessageBox::NoButton ); m_pphMutex.unlock(); return; From abf3530d33e9b278e5dc86461f7f292e8de6c4ca Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Sun, 15 Dec 2019 00:50:43 -0500 Subject: [PATCH 103/160] Return EXIT_SUCCESS instead of 0 in main --- src/core/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/main.cpp b/src/core/main.cpp index 531a0a4da..26d12713c 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -894,7 +894,7 @@ int main( int argc, char * * argv ) } else // Exit { - return 0; + return EXIT_SUCCESS; } } From 4bfcc30a71429f06cf5be1baa223c0475d4702e6 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 23 Dec 2019 17:33:46 +0900 Subject: [PATCH 104/160] MIDI import: fix putting notes before the beginning of a pattern (#5343) --- plugins/MidiImport/MidiImport.cpp | 49 +++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index e83ef8c27..f0c4888da 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -215,8 +215,7 @@ public: p( NULL ), it_inst( NULL ), isSF2( false ), - hasNotes( false ), - lastEnd( 0 ) + hasNotes( false ) { } InstrumentTrack * it; @@ -224,7 +223,6 @@ public: Instrument * it_inst; bool isSF2; bool hasNotes; - MidiTime lastEnd; QString trackName; smfMidiChannel * create( TrackContainer* tc, QString tn ) @@ -255,9 +253,11 @@ public: if( trackName != "") { it->setName( tn ); } - lastEnd = 0; // General MIDI default it->pitchRangeModel()->setInitValue( 2 ); + + // Create a default pattern + p = dynamic_cast(it->createTCO(0)); } return this; } @@ -265,16 +265,37 @@ public: void addNote( Note & n ) { - if( !p || n.pos() > lastEnd + DefaultTicksPerTact ) + if (!p) { - MidiTime pPos = MidiTime( n.pos().getTact(), 0 ); - p = dynamic_cast( it->createTCO( 0 ) ); - p->movePosition( pPos ); + p = dynamic_cast(it->createTCO(0)); } + p->addNote(n, false); hasNotes = true; - lastEnd = n.pos() + n.length(); - n.setPos( n.pos( p->startPosition() ) ); - p->addNote( n, false ); + } + + void splitPatterns() + { + Pattern * newPattern = nullptr; + MidiTime lastEnd(0); + + p->rearrangeAllNotes(); + for (auto n : p->notes()) + { + if (!newPattern || n->pos() > lastEnd + DefaultTicksPerTact) + { + MidiTime pPos = MidiTime(n->pos().getTact(), 0); + newPattern = dynamic_cast(it->createTCO(0)); + newPattern->movePosition(pPos); + } + lastEnd = n->pos() + n->length(); + + Note newNote(*n); + newNote.setPos(n->pos(newPattern->startPosition())); + newPattern->addNote(newNote, false); + } + + delete p; + p = nullptr; } }; @@ -534,7 +555,11 @@ bool MidiImport::readSMF( TrackContainer* tc ) for( int c=0; c < 256; ++c ) { - if( !chs[c].hasNotes && chs[c].it ) + if (chs[c].hasNotes) + { + chs[c].splitPatterns(); + } + else if (chs[c].it) { printf(" Should remove empty track\n"); // must delete trackView first - but where is it? From d849cc179c8e31c9fdbc3a236cd6e2e4ad6383a9 Mon Sep 17 00:00:00 2001 From: Cyp Date: Wed, 23 Oct 2019 16:01:10 +0200 Subject: [PATCH 105/160] Only filter out <>:"/\|?* while exporting tracks. --- include/Track.h | 2 ++ src/core/RenderManager.cpp | 2 +- src/tracks/InstrumentTrack.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/Track.h b/include/Track.h index f3b6c5e4c..bc9f161a9 100644 --- a/include/Track.h +++ b/include/Track.h @@ -72,6 +72,8 @@ const int DEFAULT_TRACK_HEIGHT = 32; const int TCO_BORDER_WIDTH = 2; +char const *const FILENAME_FILTER = "[\\0000-\x1f\"*/:<>?\\\\|\x7f]"; + class TrackContentObject : public Model, public JournallingObject { diff --git a/src/core/RenderManager.cpp b/src/core/RenderManager.cpp index 478aa46e7..7b9c489e1 100644 --- a/src/core/RenderManager.cpp +++ b/src/core/RenderManager.cpp @@ -203,7 +203,7 @@ QString RenderManager::pathForTrack(const Track *track, int num) { QString extension = ProjectRenderer::getFileExtensionFromFormat( m_format ); QString name = track->name(); - name = name.remove(QRegExp("[^a-zA-Z]")); + name = name.remove(QRegExp(FILENAME_FILTER)); name = QString( "%1_%2%3" ).arg( num ).arg( name ).arg( extension ); return QDir(m_outputPath).filePath(name); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 98a691a9a..3f0c59b7d 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1615,7 +1615,7 @@ void InstrumentTrackWindow::saveSettingsBtnClicked() sfd.setDirectory( presetRoot + m_track->instrumentName() ); sfd.setFileMode( FileDialog::AnyFile ); QString fname = m_track->name(); - sfd.selectFile( fname.remove(QRegExp("[^a-zA-Z0-9_\\-\\d\\s]")) ); + sfd.selectFile(fname.remove(QRegExp(FILENAME_FILTER))); sfd.setDefaultSuffix( "xpf"); if( sfd.exec() == QDialog::Accepted && From 42f7e262e9d9353671a3920215f8e41658590891 Mon Sep 17 00:00:00 2001 From: Cyp Date: Tue, 29 Oct 2019 16:53:17 +0100 Subject: [PATCH 106/160] Fix scrolling direction in SongEditor due to stuck Ctrl/Shift. --- CMakeLists.txt | 2 +- include/MainWindow.h | 21 ++++++--------------- src/core/Track.cpp | 2 +- src/gui/MainWindow.cpp | 1 + src/gui/editors/SongEditor.cpp | 11 +++++------ src/gui/widgets/Knob.cpp | 2 +- src/gui/widgets/LcdSpinBox.cpp | 2 +- 7 files changed, 16 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d82ace4ae..e3c62cb44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,7 +480,7 @@ ENDIF() # Due to a regression in gcc-4.8.X, we need to disable array-bounds check IF (CMAKE_COMPILER_IS_GNUCXX AND ((CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "4.8.0") OR (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.8.0") OR LMMS_BUILD_WIN32)) - SET(WERROR_FLAGS "${WERROR_FLAGS} -Wno-array-bounds") + SET(WERROR_FLAGS "${WERROR_FLAGS} -Wno-array-bounds -Wno-attributes") ENDIF() IF(NOT CMAKE_BUILD_TYPE) diff --git a/include/MainWindow.h b/include/MainWindow.h index 7ba2ac630..3de847f63 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -126,21 +126,12 @@ public: void clearKeyModifiers(); - bool isCtrlPressed() - { - return m_keyMods.m_ctrl; - } - + [[deprecated]] // TODO Remove this function, since m_shift can get stuck down. bool isShiftPressed() { return m_keyMods.m_shift; } - bool isAltPressed() - { - return m_keyMods.m_alt; - } - static void saveWidgetState( QWidget * _w, QDomElement & _de ); static void restoreWidgetState( QWidget * _w, const QDomElement & _de ); @@ -176,11 +167,11 @@ public slots: void autoSave(); protected: - virtual void closeEvent( QCloseEvent * _ce ); - virtual void focusOutEvent( QFocusEvent * _fe ); - virtual void keyPressEvent( QKeyEvent * _ke ); - virtual void keyReleaseEvent( QKeyEvent * _ke ); - virtual void timerEvent( QTimerEvent * _ev ); + void closeEvent( QCloseEvent * _ce ) override; + void focusOutEvent( QFocusEvent * _fe ) override; + void keyPressEvent( QKeyEvent * _ke ) override; + void keyReleaseEvent( QKeyEvent * _ke ) override; + void timerEvent( QTimerEvent * _ev ) override; private: diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 7a04ded95..6a6b0deb1 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -694,7 +694,7 @@ void TrackContentObjectView::mousePressEvent( QMouseEvent * me ) dataFile.toString(), thumbnail, this ); } else if( me->button() == Qt::LeftButton && - /* engine::mainWindow()->isShiftPressed() == false &&*/ + /* (me->modifiers() & Qt::ShiftModifier) &&*/ fixedTCOs() == false ) { m_tco->addJournalCheckPoint(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 6be277095..e6971f96d 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1423,6 +1423,7 @@ void MainWindow::sessionCleanup() void MainWindow::focusOutEvent( QFocusEvent * _fe ) { + // TODO Remove this function, since it is apparently never actually called! // when loosing focus we do not receive key-(release!)-events anymore, // so we might miss release-events of one the modifiers we're watching! clearKeyModifiers(); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 31cb142c0..de661f29e 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -415,14 +415,13 @@ void SongEditor::setEditModeSelect() void SongEditor::keyPressEvent( QKeyEvent * ke ) { - if( /*_ke->modifiers() & Qt::ShiftModifier*/ - gui->mainWindow()->isShiftPressed() == true && + bool isShiftPressed = ke->modifiers() & Qt::ShiftModifier; + if( isShiftPressed && ( ke->key() == Qt::Key_Insert || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) ) { m_song->insertBar(); } - else if(/* _ke->modifiers() & Qt::ShiftModifier &&*/ - gui->mainWindow()->isShiftPressed() == true && + else if( isShiftPressed && ( ke->key() == Qt::Key_Delete || ke->key() == Qt::Key_Backspace ) ) { m_song->removeBar(); @@ -458,7 +457,7 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) void SongEditor::wheelEvent( QWheelEvent * we ) { - if( gui->mainWindow()->isCtrlPressed() == true ) + if( we->modifiers() & Qt::ControlModifier ) { int z = m_zoomingModel->value(); @@ -480,7 +479,7 @@ void SongEditor::wheelEvent( QWheelEvent * we ) // and make sure, all TCO's are resized and relocated realignTracks(); } - else if( gui->mainWindow()->isShiftPressed() == true || we->orientation() == Qt::Horizontal ) + else if( (we->modifiers() & Qt::ShiftModifier) || we->orientation() == Qt::Horizontal ) { m_leftRightScroll->setValue( m_leftRightScroll->value() - we->delta() / 30 ); diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 3932795b7..e559d120c 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -601,7 +601,7 @@ void Knob::mousePressEvent( QMouseEvent * _me ) m_buttonPressed = true; } else if( _me->button() == Qt::LeftButton && - gui->mainWindow()->isShiftPressed() == true ) + (_me->modifiers() & Qt::ShiftModifier) ) { new StringPairDrag( "float_value", QString::number( model()->value() ), diff --git a/src/gui/widgets/LcdSpinBox.cpp b/src/gui/widgets/LcdSpinBox.cpp index 7dc21194a..7102f5f6b 100644 --- a/src/gui/widgets/LcdSpinBox.cpp +++ b/src/gui/widgets/LcdSpinBox.cpp @@ -122,7 +122,7 @@ void LcdSpinBox::mouseMoveEvent( QMouseEvent* event ) if( m_mouseMoving ) { int dy = event->globalY() - m_origMousePos.y(); - if( gui->mainWindow()->isShiftPressed() ) + if( event->modifiers() & Qt::ShiftModifier ) dy = qBound( -4, dy/4, 4 ); if( dy > 1 || dy < -1 ) { From 11e5de3a4e03c7739403c8772f11ae85efa6baa5 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 24 Dec 2019 12:03:17 +0900 Subject: [PATCH 107/160] Debian: add libx11-xcb-dev as an explicit build dependency --- debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control b/debian/control index 0997676c9..910722c03 100644 --- a/debian/control +++ b/debian/control @@ -27,6 +27,7 @@ Build-Depends: libsoundio-dev, libstk0-dev, libvorbis-dev, + libx11-xcb-dev, libxcb-keysyms1-dev, libxcb-util0-dev, portaudio19-dev, From a9640c88980b54e17df81ca887a1a0eaf87d8236 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 26 Dec 2019 18:23:52 +0100 Subject: [PATCH 108/160] Comment-out deprecated attribute It may be valid, but fails our CI --- include/MainWindow.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/MainWindow.h b/include/MainWindow.h index 3de847f63..d1fe3eb4e 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -126,7 +126,8 @@ public: void clearKeyModifiers(); - [[deprecated]] // TODO Remove this function, since m_shift can get stuck down. + // TODO Remove this function, since m_shift can get stuck down. + // [[deprecated]] bool isShiftPressed() { return m_keyMods.m_shift; From ef99c533576a040eea3509f4a8deca27d2751bc4 Mon Sep 17 00:00:00 2001 From: Martin Pavelek Date: Tue, 3 Dec 2019 20:03:58 +0100 Subject: [PATCH 109/160] fix locking of the useless_lock in LocklessRingBuffer.h --- include/LocklessRingBuffer.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/LocklessRingBuffer.h b/include/LocklessRingBuffer.h index 3b18dd475..d313fd722 100644 --- a/include/LocklessRingBuffer.h +++ b/include/LocklessRingBuffer.h @@ -122,6 +122,7 @@ public: void waitForData() { QMutex useless_lock; + useless_lock.lock(); m_notifier->wait(&useless_lock); useless_lock.unlock(); } From c52682dfb15870745e7a60a9182d1b8c1044d05a Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 5 Jan 2020 20:53:54 +0000 Subject: [PATCH 110/160] Fix stuck notes with Helm VSTi --- src/core/NotePlayHandle.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index d62857350..d1a5843ed 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -540,6 +540,15 @@ void NotePlayHandle::processMidiTime( const MidiTime& time ) void NotePlayHandle::resize( const bpm_t _new_tempo ) { + if (origin() == OriginMidiInput || + (origin() == OriginNoteStacking && m_parent->origin() == OriginMidiInput)) + { + // Don't resize notes from MIDI input - they should continue to play + // until the key is released, and their large duration can cause + // overflows in this method. + return; + } + double completed = m_totalFramesPlayed / (double) m_frames; double new_frames = m_origFrames * m_origTempo / (double) _new_tempo; m_frames = (f_cnt_t)new_frames; From 63d11f763c6f9712e4ee527b28b04bedccdd21fd Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Sun, 12 Jan 2020 20:42:54 -0500 Subject: [PATCH 111/160] Simplify FadeButton update (#5311) --- include/FadeButton.h | 2 -- include/custom_events.h | 46 ---------------------------------- include/update_event.h | 44 -------------------------------- src/gui/widgets/FadeButton.cpp | 18 +------------ 4 files changed, 1 insertion(+), 109 deletions(-) delete mode 100644 include/custom_events.h delete mode 100644 include/update_event.h diff --git a/include/FadeButton.h b/include/FadeButton.h index 09a4c6457..57d8ba1e6 100644 --- a/include/FadeButton.h +++ b/include/FadeButton.h @@ -50,7 +50,6 @@ public slots: protected: - void customEvent( QEvent * ) override; void paintEvent( QPaintEvent * _pe ) override; @@ -66,7 +65,6 @@ private: QColor m_holdColor; int activeNotes; - void signalUpdate(); QColor fadeToColor(QColor, QColor, QTime, float); } ; diff --git a/include/custom_events.h b/include/custom_events.h deleted file mode 100644 index 955521490..000000000 --- a/include/custom_events.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * custom_events.h - custom event types list - * - * Copyright (c) 2007 Javier Serrano Polo - * - * 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 CUSTOM_EVENTS_H -#define CUSTOM_EVENTS_H - - -#include - - -namespace customEvents -{ - - enum Type - { - GUI_UPDATE = QEvent::User - } ; - -} - - - - -#endif diff --git a/include/update_event.h b/include/update_event.h deleted file mode 100644 index c37732630..000000000 --- a/include/update_event.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * update_event.h - signal GUI updates - * - * Copyright (c) 2007 Javier Serrano Polo - * - * 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 UPDATE_EVENT_H -#define UPDATE_EVENT_H - -#include "custom_events.h" - - - -class updateEvent : public QEvent -{ -public: - updateEvent() : - QEvent( (QEvent::Type)customEvents::GUI_UPDATE ) - { - } - -} ; - - -#endif diff --git a/src/gui/widgets/FadeButton.cpp b/src/gui/widgets/FadeButton.cpp index b633286b1..8f75ea33b 100644 --- a/src/gui/widgets/FadeButton.cpp +++ b/src/gui/widgets/FadeButton.cpp @@ -24,12 +24,10 @@ #include -#include #include #include "embed.h" #include "FadeButton.h" -#include "update_event.h" const float FadeDuration = 300; @@ -71,7 +69,7 @@ void FadeButton::activate() { m_stateTimer.restart(); activeNotes++; - signalUpdate(); + update(); } @@ -94,14 +92,6 @@ void FadeButton::noteEnd() m_releaseTimer.restart(); } - signalUpdate(); -} - - - - -void FadeButton::customEvent(QEvent *) -{ update(); } @@ -165,9 +155,3 @@ QColor FadeButton::fadeToColor(QColor startCol, QColor endCol, QTime timer, floa return col; } - - -void FadeButton::signalUpdate() -{ - QApplication::postEvent(this, new updateEvent()); -} From fd77c79cdaab5bbaf05dc88f589eca627c3b0147 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Fri, 17 Jan 2020 16:55:07 +0100 Subject: [PATCH 112/160] Switch to Xenial build environment (#4813) * Switch to Xenial build environment * Add Carla submodule/weak linking support, related #3963 * Fix Carla detection in AppImage, closes #5369 --- .gitmodules | 3 +++ .travis.yml | 6 +++++- .travis/linux..before_install.sh | 6 ------ .travis/linux..install.sh | 10 ++------- CMakeLists.txt | 3 ++- cmake/linux/launch_lmms.sh | 7 +++---- cmake/linux/package_linux.sh.in | 7 ++++--- cmake/modules/BuildPlugin.cmake | 6 +++++- plugins/carlabase/CMakeLists.txt | 29 +++++++++++++++++++++++++-- plugins/carlabase/DummyCarla.cpp | 12 +++++++++++ plugins/carlabase/carla | 1 + plugins/carlabase/carla.cpp | 3 ++- plugins/carlabase/logo.png | Bin 0 -> 3313 bytes plugins/carlapatchbay/CMakeLists.txt | 4 ++-- plugins/carlarack/CMakeLists.txt | 4 ++-- 15 files changed, 70 insertions(+), 31 deletions(-) create mode 100644 plugins/carlabase/DummyCarla.cpp create mode 160000 plugins/carlabase/carla create mode 100644 plugins/carlabase/logo.png diff --git a/.gitmodules b/.gitmodules index 4abd7ae3e..3c0663dd6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "src/3rdparty/rpmalloc/rpmalloc"] path = src/3rdparty/rpmalloc/rpmalloc url = https://github.com/rampantpixels/rpmalloc.git +[submodule "plugins/carlabase/carla"] + path = plugins/carlabase/carla + url = https://github.com/falktx/carla diff --git a/.travis.yml b/.travis.yml index 5f8002cb1..396217af8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: cpp compiler: gcc -dist: trusty +dist: xenial sudo: required cache: directories: @@ -10,13 +10,17 @@ cache: matrix: include: - env: TARGET_OS=win32 + dist: trusty - env: TARGET_OS=win64 + dist: trusty - os: osx osx_image: xcode8.2 - env: QT5= - env: QT5=True - env: QT5=True TARGET_OS=win32 TARGET_DEPLOY=True + dist: trusty - env: QT5=True TARGET_OS=win64 TARGET_DEPLOY=True + dist: trusty - os: osx osx_image: xcode8.2 env: QT5=True diff --git a/.travis/linux..before_install.sh b/.travis/linux..before_install.sh index 91f86eef2..8e6d7bb00 100644 --- a/.travis/linux..before_install.sh +++ b/.travis/linux..before_install.sh @@ -1,9 +1,3 @@ #!/usr/bin/env bash - -sudo add-apt-repository ppa:beineri/opt-qt592-trusty -y -sudo add-apt-repository ppa:andrewrk/libgroove -y -sudo sed -e "s/trusty/precise/" -i \ - /etc/apt/sources.list.d/andrewrk-libgroove-trusty.list - sudo dpkg --add-architecture i386 sudo apt-get update -qq || true diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index 3e86eecfe..72481fc23 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -2,7 +2,7 @@ PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev libasound2-dev libjack-jackd2-dev libsdl-dev libsamplerate0-dev libstk0-dev stk - libfluidsynth-dev portaudio19-dev g++-multilib libfltk1.3-dev + libfluidsynth-dev portaudio19-dev g++-multilib libfltk1.3-dev fluid libgig-dev libsoundio-dev" VST_PACKAGES="wine-dev libqt5x11extras5-dev qtbase5-private-dev libxcb-util0-dev libxcb-keysyms1-dev" @@ -11,15 +11,9 @@ VST_PACKAGES="wine-dev libqt5x11extras5-dev qtbase5-private-dev libxcb-util0-dev PACKAGES="$PACKAGES $VST_PACKAGES libjack-jackd2-0" if [ $QT5 ]; then - PACKAGES="$PACKAGES qt59base qt59translations qt59tools" + PACKAGES="$PACKAGES qttools5-dev-tools" else PACKAGES="$PACKAGES libqt4-dev" fi sudo apt-get install -y $PACKAGES - -# kxstudio repo offers Carla; avoid package conflicts (wine, etc) by running last -sudo add-apt-repository -y ppa:kxstudio-debian/libs -sudo add-apt-repository -y ppa:kxstudio-debian/apps -sudo apt-get update -sudo apt-get install -y carla diff --git a/CMakeLists.txt b/CMakeLists.txt index e3c62cb44..a84018eac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -241,7 +241,8 @@ IF(WANT_CARLA) SET(LMMS_HAVE_CARLA TRUE) SET(STATUS_CARLA "OK") ELSE(CARLA_FOUND) - SET(STATUS_CARLA "not found, please install the latest carla") + SET(LMMS_HAVE_WEAKCARLA TRUE) + SET(STATUS_CARLA "OK (weak linking enabled)") ENDIF(CARLA_FOUND) ENDIF(WANT_CARLA) diff --git a/cmake/linux/launch_lmms.sh b/cmake/linux/launch_lmms.sh index e6432aa3a..198b5711a 100644 --- a/cmake/linux/launch_lmms.sh +++ b/cmake/linux/launch_lmms.sh @@ -1,9 +1,8 @@ #!/usr/bin/env bash -alias standard_which="command -v" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export PATH="$PATH:/sbin" -if standard_which carla > /dev/null 2>&1; then - CARLAPATH="$(standard_which carla)" +if command -v carla > /dev/null 2>&1; then + CARLAPATH="$(command -v carla)" CARLAPREFIX="${CARLAPATH%/bin*}" echo "Carla appears to be installed on this system at $CARLAPREFIX/lib[64]/carla so we'll use it." export LD_LIBRARY_PATH=$CARLAPREFIX/lib/carla:$CARLAPREFIX/lib64/carla:$LD_LIBRARY_PATH @@ -22,4 +21,4 @@ else echo "Jack does not appear to be installed. That's OK, we'll use a dummy version instead." export LD_LIBRARY_PATH=$DIR/usr/lib/lmms/optional:$LD_LIBRARY_PATH fi -QT_X11_NO_NATIVE_MENUBAR=1 "$DIR"/usr/bin/lmms.real "$@" \ No newline at end of file +QT_X11_NO_NATIVE_MENUBAR=1 "$DIR"/usr/bin/lmms.real "$@" diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index cb3764dcb..8e48c2438 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -105,7 +105,7 @@ chmod +x "${APPDIR}usr/bin/lmms" unset LD_LIBRARY_PATH # Ensure linuxdeployqt can find shared objects -export LD_LIBRARY_PATH="${APPDIR}usr/lib/lmms/":$LD_LIBRARY_PATH +export LD_LIBRARY_PATH="${APPDIR}usr/lib/lmms/":"${APPDIR}usr/lib/lmms/optional":$LD_LIBRARY_PATH # Handle wine linking if [ -d "@WINE_LIBRARY_FIX@" ]; then @@ -120,7 +120,7 @@ ZYNBIN="${APPDIR}usr/bin/RemoteZynAddSubFx" VSTBIN="${APPDIR}usr/bin/RemoteVstPlugin.exe.so" mv "$ZYNLIB" "$ZYNBIN" -mv "$VSTLIB" "$VSTBIN" +mv "$VSTLIB" "$VSTBIN" || true # Patch the desktop file sed -i 's/.*Exec=.*/Exec=lmms.real/' "$DESKTOPFILE" @@ -146,13 +146,14 @@ success "Bundled and relinked dependencies" # Link to original location so lmms can find them ln -sr "$ZYNBIN" "$ZYNLIB" -ln -sr "$VSTBIN" "$VSTLIB" +ln -sr "$VSTBIN" "$VSTLIB" || true # Remove wine library conflict rm -f "${APPDIR}/usr/lib/libwine.so.1" # Use system-provided carla rm -f "${APPDIR}usr/lib/"libcarla*.so +rm -f "${APPDIR}usr/lib/lmms/optional/"libcarla*.so # Remove bundled jack in LD_LIBRARY_PATH if exists if [ -e "${APPDIR}/usr/lib/libjack.so.0" ]; then diff --git a/cmake/modules/BuildPlugin.cmake b/cmake/modules/BuildPlugin.cmake index 25cca92af..f69fafe86 100644 --- a/cmake/modules/BuildPlugin.cmake +++ b/cmake/modules/BuildPlugin.cmake @@ -70,7 +70,11 @@ MACRO(BUILD_PLUGIN PLUGIN_NAME) TARGET_LINK_LIBRARIES(${PLUGIN_NAME} lmms) ENDIF(LMMS_BUILD_WIN32) - INSTALL(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION "${PLUGIN_DIR}") + IF(LMMS_BUILD_WIN32 AND "${PLUGIN_LINK}" STREQUAL "SHARED") + INSTALL(TARGETS ${PLUGIN_NAME} RUNTIME DESTINATION "${PLUGIN_DIR}") + ELSE() + INSTALL(TARGETS ${PLUGIN_NAME} LIBRARY DESTINATION "${PLUGIN_DIR}") + ENDIF() IF(LMMS_BUILD_APPLE) IF ("${PLUGIN_LINK}" STREQUAL "SHARED") diff --git a/plugins/carlabase/CMakeLists.txt b/plugins/carlabase/CMakeLists.txt index 4fa81a491..722f8463d 100644 --- a/plugins/carlabase/CMakeLists.txt +++ b/plugins/carlabase/CMakeLists.txt @@ -6,7 +6,29 @@ IF(NOT CMAKE_VERSION VERSION_LESS 3.9) CMAKE_POLICY(SET CMP0068 OLD) ENDIF() -if(LMMS_HAVE_CARLA) +# If Carla was not provided by the system, make a dummy library instead +if(LMMS_HAVE_WEAKCARLA) + # Mimic the Makefile header + FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/config.h "") + SET(CARLA_INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/carla/source + ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/includes + ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/utils + ${CMAKE_CURRENT_SOURCE_DIR}/carla/source/backend + ${CMAKE_CURRENT_BINARY_DIR} + ) + INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS}) + ADD_LIBRARY(carla_native-plugin MODULE DummyCarla.cpp) + INSTALL(TARGETS carla_native-plugin LIBRARY DESTINATION "${PLUGIN_DIR}/optional") + SET(CARLA_LIBRARIES $) + SET(CARLA_LIBRARY_DIRS $) + # Set parent scope variables so carlarack and carlapatchbay can see them + SET(CARLA_LIBRARIES ${CARLA_LIBRARIES} PARENT_SCOPE) + SET(CARLA_INCLUDE_DIRS ${CARLA_INCLUDE_DIRS} PARENT_SCOPE) + SET(CARLA_LIBRARY_DIRS ${CARLA_LIBRARY_DIRS} PARENT_SCOPE) +endif() + +if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA) INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS}) LINK_DIRECTORIES(${CARLA_LIBRARY_DIRS}) @@ -17,4 +39,7 @@ if(LMMS_HAVE_CARLA) BUILD_WITH_INSTALL_RPATH TRUE INSTALL_RPATH_USE_LINK_PATH TRUE INSTALL_RPATH "${CARLA_RPATH}") -endif(LMMS_HAVE_CARLA) + IF(LMMS_HAVE_WEAKCARLA) + ADD_DEPENDENCIES(carlabase carla_native-plugin) + ENDIF() +endif() diff --git a/plugins/carlabase/DummyCarla.cpp b/plugins/carlabase/DummyCarla.cpp new file mode 100644 index 000000000..560e866b7 --- /dev/null +++ b/plugins/carlabase/DummyCarla.cpp @@ -0,0 +1,12 @@ +// A dummy Carla interface +#include "CarlaNativePlugin.h" + +const char* carla_get_library_filename() { return nullptr; } +const char* carla_get_library_folder() { return nullptr; } +const NativePluginDescriptor* carla_get_native_rack_plugin() { return nullptr; } +const NativePluginDescriptor* carla_get_native_patchbay_plugin() { return nullptr; } +const NativePluginDescriptor* carla_get_native_patchbay16_plugin() { return nullptr; } +const NativePluginDescriptor* carla_get_native_patchbay32_plugin() { return nullptr; } +const NativePluginDescriptor* carla_get_native_patchbay64_plugin() { return nullptr; } +const NativePluginDescriptor* carla_get_native_patchbay_cv_plugin() { return nullptr; } +CarlaBackend::CarlaEngine* carla_get_native_plugin_engine(const NativePluginDescriptor* desc, NativePluginHandle handle) { return nullptr; } diff --git a/plugins/carlabase/carla b/plugins/carlabase/carla new file mode 160000 index 000000000..4ac8ff2ef --- /dev/null +++ b/plugins/carlabase/carla @@ -0,0 +1 @@ +Subproject commit 4ac8ff2ef412d4ab190d2e285e318b1f339af4ae diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index b25677f5c..0029a6b42 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -438,7 +438,8 @@ PluginView* CarlaInstrument::instantiateView(QWidget* parent) // Disable plugin focus per https://bugreports.qt.io/browse/QTBUG-30181 #ifndef CARLA_OS_MAC if (QWidget* const window = parent->window()) - fHost.uiParentId = window->winId(); + // TODO: Remove cast; Only needed for Qt4 + fHost.uiParentId = (uintptr_t)window->winId(); else #endif fHost.uiParentId = 0; diff --git a/plugins/carlabase/logo.png b/plugins/carlabase/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..353d5d718d6fef054ff4fed2932f4f45df7f792c GIT binary patch literal 3313 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 literal 0 HcmV?d00001 diff --git a/plugins/carlapatchbay/CMakeLists.txt b/plugins/carlapatchbay/CMakeLists.txt index d9aa6b321..8e3588916 100644 --- a/plugins/carlapatchbay/CMakeLists.txt +++ b/plugins/carlapatchbay/CMakeLists.txt @@ -1,4 +1,4 @@ -if(LMMS_HAVE_CARLA) +if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA) ADD_DEFINITIONS(-DCARLA_PLUGIN_PATCHBAY -DCARLA_PLUGIN_SYNTH) INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../carlabase") @@ -6,4 +6,4 @@ if(LMMS_HAVE_CARLA) ${CARLA_LIBRARY_DIRS}) LINK_LIBRARIES(carlabase) BUILD_PLUGIN(carlapatchbay carlapatchbay.cpp EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") -endif(LMMS_HAVE_CARLA) +endif() diff --git a/plugins/carlarack/CMakeLists.txt b/plugins/carlarack/CMakeLists.txt index 1834b2371..5de833dd5 100644 --- a/plugins/carlarack/CMakeLists.txt +++ b/plugins/carlarack/CMakeLists.txt @@ -1,4 +1,4 @@ -if(LMMS_HAVE_CARLA) +if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA) ADD_DEFINITIONS(-DCARLA_PLUGIN_RACK -DCARLA_PLUGIN_SYNTH) INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../carlabase") @@ -6,4 +6,4 @@ if(LMMS_HAVE_CARLA) ${CARLA_LIBRARY_DIRS}) LINK_LIBRARIES(carlabase) BUILD_PLUGIN(carlarack carlarack.cpp EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") -endif(LMMS_HAVE_CARLA) +endif() From 427d77966865df2c7dbb495ad8ba528a8cc1be74 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Jan 2020 20:33:58 +0100 Subject: [PATCH 113/160] FileBrowser: Add helpful comments --- include/FileBrowser.h | 19 +++++++++++++++++-- src/gui/FileBrowser.cpp | 20 ++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/include/FileBrowser.h b/include/FileBrowser.h index 03a1070bb..742c1a680 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -48,6 +48,14 @@ class FileBrowser : public SideBarWidget { Q_OBJECT public: + /** + Create a file browser side bar widget + @param directories '*'-separated list of directories to search for. + If a directory of factory files should be in the list it + must be the last one (for the factory files delimiter to work) + @param filter Filter as used in QDir::match + @param recurse *to be documented* + */ FileBrowser( const QString & directories, const QString & filter, const QString & title, const QPixmap & pm, QWidget * parent, bool dirs_as_items = false, bool recurse = false ); @@ -69,8 +77,8 @@ private: QLineEdit * m_filterEdit; - QString m_directories; - QString m_filter; + QString m_directories; //!< Directories to search, split with '*' + QString m_filter; //!< Filter as used in QDir::match() bool m_dirsAsItems; bool m_recurse; @@ -159,7 +167,14 @@ private: static QPixmap * s_folderOpenedPixmap; static QPixmap * s_folderLockedPixmap; + //! Directories that lead here + //! Initially, this is just set to the current path of a directory + //! If, however, you have e.g. 'TripleOscillator/xyz' in two of the + //! file browser's search directories 'a' and 'b', this will have two + //! entries 'a/TripleOscillator' and 'b/TripleOscillator' + //! and 'xyz' in the tree widget QStringList m_directories; + //! Filter as used in QDir::match() QString m_filter; int m_dirCount; diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index b661a9fb2..3cb55fd45 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -232,6 +232,7 @@ void FileBrowser::addItems(const QString & path ) return; } + // try to add all directories from file system alphabetically into the tree QDir cdir( path ); QStringList files = cdir.entryList( QDir::Dirs, QDir::Name ); for( QStringList::const_iterator it = files.constBegin(); @@ -247,6 +248,7 @@ void FileBrowser::addItems(const QString & path ) m_l->topLevelItem( i ) ); if( d == NULL || cur_file < d->text( 0 ) ) { + // insert before item, we're done Directory *dd = new Directory( cur_file, path, m_filter ); m_l->insertTopLevelItem( i,dd ); @@ -256,6 +258,11 @@ void FileBrowser::addItems(const QString & path ) } else if( cur_file == d->text( 0 ) ) { + // imagine we have subdirs named "TripleOscillator/xyz" in + // two directories from m_directories + // then only add one tree widget for both + // so we don't add a new Directory - we just + // add the path to the current directory d->addDirectory( path ); d->update(); orphan = false; @@ -264,6 +271,8 @@ void FileBrowser::addItems(const QString & path ) } if( orphan ) { + // it has not yet been added yet, so it's (lexically) + // larger than all other dirs => append it at the bottom Directory *d = new Directory( cur_file, path, m_filter ); d->update(); @@ -761,6 +770,7 @@ void Directory::update( void ) if( !childCount() ) { m_dirCount = 0; + // for all paths leading here, add their items for( QStringList::iterator it = m_directories.begin(); it != m_directories.end(); ++it ) { @@ -796,6 +806,7 @@ bool Directory::addItems(const QString & path ) bool added_something = false; + // try to add all directories from file system alphabetically into the tree QStringList files = thisDir.entryList( QDir::Dirs, QDir::Name ); for( QStringList::const_iterator it = files.constBegin(); it != files.constEnd(); ++it ) @@ -810,6 +821,7 @@ bool Directory::addItems(const QString & path ) child( i ) ); if( d == NULL || cur_file < d->text( 0 ) ) { + // insert before item, we're done insertChild( i, new Directory( cur_file, path, m_filter ) ); orphan = false; @@ -818,6 +830,12 @@ bool Directory::addItems(const QString & path ) } else if( cur_file == d->text( 0 ) ) { + // imagine we have top-level subdirs named "TripleOscillator" in + // two directories from FileBrowser::m_directories + // and imagine both have a sub folder named "xyz" + // then only add one tree widget for both + // so we don't add a new Directory - we just + // add the path to the current directory d->addDirectory( path ); orphan = false; break; @@ -825,6 +843,8 @@ bool Directory::addItems(const QString & path ) } if( orphan ) { + // it has not yet been added yet, so it's (lexically) + // larger than all other dirs => append it at the bottom addChild( new Directory( cur_file, path, m_filter ) ); m_dirCount++; From d280b8628d2ac4642bf704d528e5a7ac935015c5 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sun, 19 Jan 2020 20:34:48 +0100 Subject: [PATCH 114/160] Fixes #3183: Fix file factory delimeter position --- src/gui/FileBrowser.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 3cb55fd45..4a3b8e85b 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -774,18 +774,25 @@ void Directory::update( void ) for( QStringList::iterator it = m_directories.begin(); it != m_directories.end(); ++it ) { - int top_index = childCount(); + int filesBeforeAdd = childCount() - m_dirCount; if( addItems( fullName( *it ) ) && ( *it ).contains( ConfigManager::inst()->dataDir() ) ) { - QTreeWidgetItem * sep = new QTreeWidgetItem; - sep->setText( 0, - FileBrowserTreeWidget::tr( - "--- Factory files ---" ) ); - sep->setIcon( 0, embed::getIconPixmap( - "factory_files" ) ); - insertChild( m_dirCount + top_index, sep ); + // factory file directory is added + // note: those are always added last + int filesNow = childCount() - m_dirCount; + if(filesNow > filesBeforeAdd) // any file appended? + { + QTreeWidgetItem * sep = new QTreeWidgetItem; + sep->setText( 0, + FileBrowserTreeWidget::tr( + "--- Factory files ---" ) ); + sep->setIcon( 0, embed::getIconPixmap( + "factory_files" ) ); + // add delimeter after last file before appending our files + insertChild( filesBeforeAdd + m_dirCount, sep ); + } } } } From aeac24c06d1a839b242d7738e1412f068508cb4a Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sun, 26 Jan 2020 16:05:47 +0900 Subject: [PATCH 115/160] Fix a muted demo project "Greippi - Krem Kaakkuja (Second Flight Remix)" --- ...- Krem Kaakkuja (Second Flight Remix).mmpz | Bin 46086 -> 46081 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/data/projects/demos/Greippi - Krem Kaakkuja (Second Flight Remix).mmpz b/data/projects/demos/Greippi - Krem Kaakkuja (Second Flight Remix).mmpz index 6c9f3436920e5a7a7b847b00a2b5a77e881da457..9ea29b34d3f1aaaf6fe86afe1e0bf10a162abe9a 100644 GIT binary patch delta 16596 zcmX~8b6^DN?5dX!&_*0qjB0m++0YmSxKTT$8A)yQ{ zB@Z#)fF#+V^albf>uP`Y3hsyK{A9e$l)J(9BEEg44%k+K7{~dS+{3@oU|gt0$CUj=95^pEJ{qe znS?;BP*&# z_3|TIybIrVl@FE9^H_fz3T29`^>_Bh2GbwsoVMb5QM1sR<3#f3qdF;fD8>l7scY6C z_|yXLuv_{E(_6+22A?V0z*@-{a|qgCGJ2|YUk=xvMwCu{z%uFkp#0!q2nGu7Lb;{i3Oj%q;9`yrZP4F>wB%>&$H-EFlEH_x87nG9 zQ(?uK8{)P$8J`xKn^~t(e{FS9RC86LM62UVE?~j>no#kfURT8O1m%(Cxl(v-R>%k| z4+EHRbcqFSs_^HsqLy;c?A=gpge)sfgf1Ksy+QF|Rub{?!(Y(|EP!f)C2pAZz5_o% zDQhicIxHfK1e7rE38K#SY!>q&=~g!GKjFDa*A~x=JaSB1ySJ!Iu~nd+Imtc|K(C(- znCOJSy|5M$-n5e%FuB9OJy~gCG%fO+ug)7L5sz3X

aCA@j!VGXO8EgAlu;)c1g)$M1Vb*@D&h zL1UKCha*1PE65KN-a6W~F^rsj57obrmKNZOjP+kG)&}Nw^;!BPh{?#qj;`z(2d6Jh zm#@>@;zlqI@1&Ep#Aos6V8RAea4Gg^-dlV7x!TZIxIyp_e10)7VPs~zUhwaSmvv~m z3}G*#xlzqu9CO)uHPQAfr7$*V{C&WXf3@87yI#6ni7Psqh+e-FI@r74#ru*H;TH6J z#BVeCX6@^o>r4(X?CIdw(2Hja z+WhzA-Q*Jp+&*eJe%IAUUqLedQhnyzb&GrkZ5Qob<`!A4Sv(h;^s8Jv|a~)z`C_3Oa;Z~_kW$g=x@8- zucyARyL?al&SLv--kkki9F9W7Pi)SZ`^nx>`pfq0H!Kd@)6CF-&0rK~W;4%;9N9Hwtc`NI8Y3W+GH(-13hEd5sm|IVx59rkTJH2)BtDiCy zR*HqH^*hD>M{Xu?lnw{yPmJJ=)GH{b;sY)4#_|OR>H{O~73LYJ^n~C=cntqedr#OS z-=@CI?{V%m6z%yyf>#sDh|ax!Q&VyVp0hOphyOS5xs7tp{+sZ0rS*@jWFN{qXa9&- z`%%U*=UoV4lIurqm>_pE+HFqRl05|3o#bDRUAiKfVAC-Gqg)BuQ+G!$lFf*IyaFXv zS`PNU&dFaT9)ccJTmeBwFPcoB_wftbEwBL*!u6S8tKe<8;wMU#tu^wGrVC)j)D47#_n-ZwD z*HLSu6)eL z9>IKm=Lp=v8`cLFd7YBv9vy3`W9qRw+Ymjo&c=Uo*k2yf*zX+DNabONX7O7|jD!AfH+`JM&&6zI=L^)^h(L5W zWM8|6h$)zaMFndnUnUtosa)Rsh?l|ignE8!V^R-@dx9R7@0`G#F|!{0_!XYj)#}~^ zge(vHGFb6?LX9u1kiK({VS4`y`GQ!unhx#>>$o`AnT3{`8B^wteIDUI4QPF5`}zHM zvyC8svk7)NlwxoZir6X(KktCN4ne&mRdncE*rvBRXp8*8el#z#L(|^c zLy*h*h4oBWB=|+M>Wl@nacBs#t?lIjf&nCxHN}i=p-*Ny=IgfaBV(y}BoN#xc<%^z z54yU14vw2}5#5=W=dh$pOXJTQj}TqsU}Q`V3RThT>ziQt5YAmySq`C);z(u>t~N{_ z(V3_ru}{Y%z4(IS&(Vjr2!DOE6HJ7d&j}?M9vJ<8WIQr0v3&tB zj9q&>uaiXoC%;)&=lD0}etzeo+Sc_6Y1HMma4-u>uC|1NN=?8yd2 zF4>HDi(KsUXT7(xmefAaFuPlT4(1d3P*Xs{teTy@erBGRCau3(ZDv`$O-P-5TJf%o{Z5y_W z^d>IV%{addO^6Es#<8`=oNz8qds3cWPIJLL3)>GGk27FvcqY(9>g3w&Q_)Ra?k^_! zvr)}l9(-S(8IA;6w&V&Ab+%(;c4%Rp7?VqR+`LK_pFZQR^7uZ6BkOyv{0b(RjL!EK zhoAj~R|+%hSGi+f0X5%97LhGo`@a9TR(NBWQ9ZnhuFC9-^mrN8f6kOe|AFifK8&^NuL1Ga5 zuGJ#$hcFm(%maFSj*Pm=Wl_oRMsh+Ok7@5DS4ZxeHXc75t>GTEL;~H=Y z{8>|B6k>)0ibs}SE2uoC6o+bkx-7d5vJ_Eh0cUmtxIl~j^Rbz&YFwt!gYqLboLpv>sN-&*1DjgHFtzw<>Z6d5WpFKC$a9+x3m7$asP`h>hT@=@t+k@1K~WR~GoZnsPRdYB*v$bB#+Ju01}e#~&x@s>^{o*)u$?HoD&hGnb=87ET*~va^WxSDgjG z?;FQFp^h(9`Dgc!!(o~ii&}6!bp$URYI!Ewe6)wm9L2fV`0XfRgEDmmcgE(Vo{?tk zXznsD%DlN(()Yf17EbO}{#bsR;EV&Rab|hvSPlq~da9>3Kimd}n)XXPUmd-SzJaTq zFB01O96fO?DWlwGJ>P_Okyw2psaH)Gzaev0JHmRgpR;*(IX-+%OLS^15?tL#`Ha?2RWPEec1BtlvuVH6U>+K3LxYxPSpKXx!jneZYLZK-fDFZ#d zBgs!qvmo26VsE~Sl}z1xjCg+I?ee;+jz7SN)$OKqY4-7Mb&{z2!X5y^vPS*TQj^D? z-#)%#tT%`>CIWsLAeiYXD3c&K0`KyVBU%(gh&=s@6+-MLal3r(tDb-YFe^3xa3GLi)(orG6_U_LjaU%NttXwlDY*i;A z#?fm3t$lvVZp9S?rGNv)P;`qmzjY`xo6r10drgK^mAZjjIRXOIp-tD1i!&Hol}QK+ z7&zd76QZTK#$VvfmEQahD?${?a?3Zz{67@v*O{S};Ei3{!f5z?sBYyRlS}AqJ->~t z3O^GMWCbQ{+6?JcUeB?3rXA&>I*(Pr%q9h$+%kji2|FHd^|sACaAcFX+7&wuYbfRgV)8 z>|edLEc|Yt!vb<4mC)XrnaK3~9sf5ufJtv<2iWtyv^g2~tphP*`X7*e8Jp5fx)lm~ zy2f-id%C8I6@I&kj{Dn_q|upRUG(r$5H{gqbK3oq1)QcWPfPu*G}^Q%rLhpwmjt7WX%{EpeKfpy+tTXQv&xNYas ze5W}dG6F8>F$d!1o6sk(|9fvO$e(VJwqW&I6Y3UF&~I)AUcUqcFf?AaufEg=?!G_` zYORJ{yDlNN($5$Sus~jsv1#i8c9o3LFK7eE*2=NmDnFMV-SDG8-hIO7W?(!=$<1n> zD(edwMAQ)C3o^gUVAmu^T|^Lqui2CcG>i!16;Zw4p~%%R=kuz|;p)(C43toCzisY| z$~RZ?9nkDkF3#?qIGv~c^JN8}Hxl~uIyvUgSHhvSeFmU_<*D&$SE~Y8)%Vt@n}yG{ zDzP0czH0Mg&^*GuD=pr3F8}>y{;f^RwPxpYIzTMJz9G%DEJ|aZ3DJt%h0ge5#Z9G7 z8>v16MQWWM6j#Gn>pVG#U8YbAhPfJrY`cz?U_;Rv%;-Kg+uR7hl5I=xo5LXTjS-`+ zntPH=F&GwR@WhBOL3IWYOLUKkM){CGDAY6BSJv$G!>&g@XxEE}DBq%MvtbrWUUtaP zbjn;URnqGxjHN~aIaxwkDFmn?Yekac1|-c*qPR>VxQ8&Ls5u)GOpp!^Nq|`Y==S`L zn>iy_)hc-_d4M(yH$G-ipPNycR%0oh8oMBInX~qA$*j@n_YM|dwt>E=#WzhCpI}Wf z`P7t#vC?v&a0sd1NVLGrh6WXL!~vBaqY-OXHrd6E%B0vs`9!rZNTnfs^*3tdb$4Bw z54o}j6{pB3;kP~#ZgL0U#x|?PZ zEtVvXzMiWnV?X$T36cJsn=nIUiO1jO@YWstc+@61q;UlXkvKtRw;Q&~44BEksqD$z za3{bSn7(sR#ab|58}ze&>(B!S*N8Tx3h_$AfgglH=!1b`_)%$!jTsf|Vn5OZa$^KL z(tpdOGgxR^yDUx8)~SIjS!a%dILdB>SiV@CWB^^Cr-W4kBq~<8O{7@CKNjr1AOhwO zpswNe9qdGn;uErT&d*ZO)e8I4sXryKNyB9oCSmGTBY$Vnz83oMIR37fu_*gS@A0u) zCsMPqi8#@uqFYkdnAx1Tb3K1m)z3w- z^(V2EIXVOe;|(i9pc1s9q;itDVx1Fy0`b^@q{%}8?5AJaHyqWn3OV);(|Pz_`Op%f zADV@R9HVptDHRa>W#h#n>GP3~1saTxT6t@63o{i8fN0SQoT{U$dHF`vm=+U<3#!ZH z8JHlZ3@8ghGEYLaMW{OQjXX0(QBs_#jNx$wdIlZTr!Q8odr(fCu%eC;7LRdUDRQyE zQDg_X5;M9s=04_Z;l_(m&eM4VL*VFN*qsQ3skI`V2E0~*{*Imq+Xfv~_Sh(fcyNxg zdjnzS=%K3!+ z%t(B|DisktFpXIecMNikE@|1mL;Ze1+0>per2M%~lfx-B9sN2nZFMh40A;Q=I>qvr z)++HL1_6taq#09%T6yTObt^(ztzUyjS!IDTP`z26W}z)LIM|;J4H?G4hOX?X_eV}= zkgCiP{T8!t#|>%-Et0%fgN7Zl3MQ$XGN6zmR1wih`fk;Mj{uIfoB~6Q3bRpn=IWSF zubiE&kEkK*FmNa~OkgtT*Sa}OgnAMP5iGg>1vF}=p&zk>4;5l+V z5F_T!M7e8398W)Jk8duO{{cy@AWbuSu5Mh8g8E~Q2uXWa#JS9Q$yG*|4sWS-dA5jL zwpgXn09SEpUK~`GBLc&MTrnb#O6p`8!UT*|q!d=PF*yJZ+s_O(zCWUgYQQB{4rTJb zbP^;hXuyew8cXd*bBEKLAqc!9(^>~NuxTFVrJ#)4;SxaeP{dPFwAan7NG8iyNyv^$ ziBasga#B;kfkBI@UH!DJlF*=BZ(?7&iSM{!SxP0^X#yn&M=wbTyI|&9Y-L+wilY^q z2A+LDGmLOEkf&VhpG%pBv`ME7w~+ZGVvU~RqoNhNXy8@xUW>H>J1}VdQ3MndIK@{9 zxh@hal}(|etbZJ~|E^1|K?`%(huv(Yfmr=4SOh?6H%^NrF(0GB;jM7Xhz*{0uvPJsd&W5QHQhe~OVdAw0ewVb3l zWW8X7ts*)2I?nYy@VjwVc?r`#fO20G!q{%o%~;H2t?=AiBlo&6Os(eLhLB)o22u-- zeI`bML2E6MLj0?7HtBFi(fpBg=A69}l@oL2hcG)U&hQ(3x&k9ynapjN7)0`V!jdYBnf*f2kp#vj^bL;2Ky*(L>6&Hu4pkc`HesPSoxrUcedOdkTvY$Y4MdKNPzO zXHHtuTeHKohJd2##)i%a1JzMe>s1tFD1jbO(v2G!0-?KHF7b+{!7j>i?%xQKmK(T` zM#Ie`REsRE?-D^o&*XAJ3YO9-j0?6BA``g?8N3XYAK~^4r_EK$(Og%KpGuPWQ<8$L zI~Y~);iGy*^yO&A<4!2yLG}90$HMsI=`GofgXl`6^YV}5Z0gu)0D2LB_x#fPVtiG+ z<*=FJH$Ev2th`vcL1TuLg0X-1duwIKl2m6^@Qw$+?=JSn;%i&Dn}l~Bmw{id^K}$%CC!8AzcCvlV1qiScUrbJt{aLd4Xh zp@?1Ocq%Z)?w3xyH89aiq88s*V^aMt@;&4`GqXrVvHYkL>23iR`XR;=>*8OIJ(s`%juYMieJjg82kU@5?@rAZ)d@&cqPTCKpl%yZ`B=$D3) z?<`SI$rb#e4K=PqaFQw?r~R#QiZF^FTKq7we&nfBjrF@1y`h4DrVIe-dbrQNdBo#lXZTtQ*&%$lUk{)KVgul!?IPIarMr~PrHSl{J*;NsbY|F#j4Qx{XT1@ zmS7FzrnKSP5P>d3<}*X?eL*dQP8kTQy_I{Z)8?bkG&n%)fK;QN@|Zw2MYvKO?lwBL z=+696{`VP;R{}NK#fk`<(7JzvDtT}bAD+EaOGC!BNN&7*xDi?Z?=Y4z>8a_n?^7U2 zpo!2dg`Dq+%#t}gV#-x|l2Y>^Rm80|15o;e`4L2|;9$t$yn3jn#R}9AOGF6by$FTh ztNHMMrl9~&>J_Vupc^wmZ}sC46?AE}fzi~=WaVI_ke^266O@h>n;G)0y4BMfbrl4; z%K5`$^3*DC_8S3K?>W=D=9W;_$Zbv%h`rl4{rP7kY1P*#awFkG6!b^El#iWqxPM*96SC;X$>E9Huqnkn%Rv1HL z5>nKH@t5}$-UX7i@>I4gx1kIbJ^E*@5^W-2UZFu)@?ep_nvECHM*5pV)K8s`HUSH@ z^JkhSs9IVyO?ZlP*o?W3Nl{1H>i%hDi}Dq^3?Jo_$`A&GK!y`jO4`_mA^A#TkcMQO zI2{T>_p=uxqOl&;YASd-8nq-XeEWgIX`DC%aHm$rx0&aMT}A`r!0y z{mcb>BE`_Q7U4S&8`W`9Duz0r!6R(1*dLpu7Yo%I3OO6Sa%y;DHmP7e`qBI%j@^b? zOL^|pqw4LFnPPu63qO0qKx+h5p3_obz-7vWy)CN_(U4GTVMc@OLmu2Dn3i0Agp0<^ zUM($>IX)~2k>Iq6c5#&Cu+Wr$Ha2HL^|iyNGy?MiBO{7W>)6p)^H#!`Woacv%}##R zzmuymxTl~>1q(4G7A%fRwy5qv40j(HLJ*H3Brc+==sra!_R)GZ4H_MW$JhqoiahZ9 zTVQI;s$6NGSw@2Pi_2n?rz~{Ni0X+nS$t>$H=wvo`&{^WJ&@asrQC5&PbNN-1)kPB zm-dX{TlI>wzSi!6$&3wp0Y)IV?z|8!eJ_owA29|ks<^Y+pmk&3NfEf$Gx^D`v`iU> zF-GmNn$=}O`i(O4jwO%pTSOGf5(r~7`~1mn4yBCM~{V}xL|y!OnMFU}^) zV_D^iY{udJdoYp>*N-E5PI`+2<)7&7nx(~+uX)E)gDTYJSkIV%j7CJ%_uS;9GLZ&2 zuyRDhe8UdC$=_8h`rEi7Y$>bc{m|(3`zI`x4KylsTG=tI1(#YuoKaIbMQX`57cGs_&C@YaBo>Yt~kI&`}@UpDN zwr(=%KD*dpA~TxqKxJy`p#!6L`oEw=1epU zeUMBU=5@#6a&-}kW276X8wTjC2N-e>a3~xK6I3QC6(!CCApTzC*yx0ap2`2dG#&&Fk$E@_Ii3624q zzdKXV6l|wzL?vWlpxb5ni$7E#z14i)9ENU~NHT*)T5NdqLIlopxawB++a5(>b%ek` zm6J5Hsr0_%4=9YH!Cwj6@Ee8qfs6QBtlm-<)R^)> zM-Ym@qw1)DiT(S@zXw*i61=kOlk% z#^1oXBXk(zD}~~rXY-6Kg%b(~8ZwVNe)toQ<_zsNC|7$%G#iLVYS3^5SXc29LlF&6 zSoR^R6#l#nHZJ5VX-+D)n2$Q+OO|hF$i=Muu2)J4JXlmOTA~lB)~=)uRKg4tCYow4 zN}ji%*I;RAn@XeBmBf=@@CaDDS{;zujW(G_`YV^sqlt>YCO#4qQEcW8@^_Re?fcR= z@%*09eSC&nVo9viRmp3Z1s5pZM3AuDg;ZRv0O?Y8P6s1g8r_(wiQf?=f%;4hsT5}( zRFj(r79ug~r*Xm6%-|}kw5MaS13puW-KHzLnWXZl?R7c6*9zRh+nNp5I*dWTY7IE8 z_i9$h!OB^2*)>B?08T7~I268ye~ymGr`C6U1|;ZNwm>giPn$we{P zs+X*i`6mt_0_M?jmIiIA8BxATCxXB;)f`sjNm7U>`i_R(CW!harJ2&_5WyQY^5u}C z@XJ&#faL9velz~fQcq)UGQu6SC>6#w+jt$SSf0n1?98uIMQx;>vP4hlHZy0cNpf!j zl#6kc&L|4e+1bkZAv0H#AdJAq4yX$Gw&E%%N;lHLSMa6Ju`c4ye1mlk?pvH+a@wZ9 z1-qv7Qpzj9iA^x!G7deBfwccw!bo4MddXnSPIK#!qE^{-CUJ1fmYKHsR|5XeutHv9 zsKV^9p(QOY?lIJKxm-n>1IkYe7KjlC;MQQEK{JG4@gqs`K{(`Gys}}vbhIdjy|{cN zDaMKu$vaQ653I1M3tdHv4xfs%<~#V?eJNSdDpzRPe; zT0P2I5;CXb!X6FUr_{9cgEUfn&dU~NyDoDDG>dAw-3(Dz<4*McJm$nexwOZ3^Rr7&r8wu1wqD%vpOH@q1BJ-$%V6j!plxT|x8yxgZ z?``Gq$rj42potNJ(L&SYyt)uUKNwoWkpyuK=2gOu`Y5LE(X}&T1O#MOdm$hh(>Jf7WLBv!pg7G2T1B;Tf#oN)zV;iY1Xwzo0Xa3eYn&Y(zg|2*4 zF~QL4BtpeC=9jdP6_DS=Ach;}k%?M3L7iSSA!b6HhX#%K$6_5;i8ghiJWenQd-9HD zv%I`f2ub^=@=kZasC;Iokd1^!T!y-Fg+(1Hjy)Xdc3576aX^7e!+Jj4Jof2d>U>!@ zAN1{hk)v(qZxgz%N|#_681lP=)FGOqlAq3TeQX^@z>hGeCICz%pTsc+$i zw*?>*Y7nazai}R?KzwGLGTFWGI;Z`Dli|j3FG>^o zy!H3tF~j84(92!io-z*;e>fAO1f;e z^_mZpv%WY8e{o~LXyI@OC8-2WibJ$k4$U+IEbaATAhQT-LNwf16Z1${IigxHZz$GKHq&4l=0@ zLmlS%f(b&Tu+%|es-h3|R;68BniAK?V%(IP)R1z-@oY34%yE`oqDmhP#fHhN>3lNl zSJ^g*pDHz;v|5IbiT!Af(WBhSRU^AfK_jJ9Z%mc8Q1{1+QNg%EUt^} z3Me$Zf92t05G`psHtrevWJ1pjt*-;Q>>44hl5)^0t#D`5sp3Gb^w_@7X)D@cXk)D9 zMkx!msK80)xSdFvfTj(bQgk2d5UkKA2p6~!VI}R(NT$yevQ?tff^HNF{%C}Hp#mJv zrnL&3DJ#vKjyzw$HmUX-4iJJCic?Eb7#CMae>UhF3XT~QLTdKe!Gfr<9PVI%stov&bGJE;&Sbjm2Q2hWCsrLt>g&VN+FLSgAN3W?63%b_(%u zfwWV%3hZ%KBA~8k_J&?GVeR=;norF3ASiS~N#e_W3^Q~a#T0H{?$6PsK947@sb`gK ze@YD1%8=;t{|9pEEcL}4>BnxYgL#zk90kf zAC9^c1lK8Jkn~se%=RXPD~;>gq=jl#f4mLporM_mjB(<4XiH8Sgq->HDZy~HD$w;> zS6YoF6feyqF(?cqh{cp?KT%4>+=rtQC0cS`1(RkyR8hMLLb|RR^<5&K@q;KpM-!H@GDOiq4e-&5K zteEE*+!mE2(!h6$^Nu!Ql!h2Oys22>pg&HEEkSZ%igPSDs6-1_((I^Pt$0;8gp@)H zk=WIo60mN!X4mA9ui(ZAX7RQP(PP^%5(~uq7}SZ`T2(8_Q^}Rv+eigN znW3GQl(^X#r)dXf2a_}|4LZQ@i8Nv{7BF?GTK1Bsjc6GM0gv`TfTpmmYQ|KRc2ZL* zh%R)o87Uhw(Xwcw?6;fnIAyfDJ(rfP32xUjGvEKWJ5lB&2IR_dHCxRKe=Z0EMq{O& zIWRj+(k>q_nxM+&}ptL6zgTB z*rR2ABBh)G^mGCEev#TO2kin=C5=hHF>G@6PCdcYgbF4Rh_~8ul?3r}sU)Xt0UAyC zM5Nq>Z1h%wqL&Bi)Wpa2e_2K%3n!%AIS$diKILK-oAhXC)#`S;sy^uywQ-Z~7Fu=^ zClF&}ORv~px=krjLyMIgq3DYUl6GvBQ97tahs|ENtVs4i}_VU}UtFf4O?vm`-L&jg82x z)m|Ynux%@nZxM+*s8#B=9}G+7DXZ4#G6vP*27(oXQcM>nMw9O*Hd#bwlqjemibG~Y zQ>v20tQ_K2S;g5pQk7%6UsVe&ax@-E;#egV61HR4Ef+f!7dot$Zx;t*Q^h(G-*?Kh z4pMD+UWk{iPFYYQe-ds>3s`6rwJg))ELtj^Az5fSG-VChFu+>ktTviu35iUCjIfj* zG(seit!GLScjkV86dH6P;z!MqnUuT|*KQYkQxMy_4N0gVUX&zb%%I!7N~>0w6VP0d z7^VqnG|BOJVUQAIMISnBuL!$hM0Xd3@^ApcnB_t*;40;ge@0^vuLLU668uap&zT?! zFk&@WQE0U-&ZJtW+77Bdn+8h~r^ghZFx8|zlNf@f18go;>&tl5V+MYos?FAUm_>u@ zqY*?_60%>12S_KPP)c@IMH0y(beW}-vesHm#zYqg!59WC;~2EIQfp1!jyiUx2J;pr z*`wBK;NgBle^cwjdIeo+iXA%Q64>j|l6!^vm`~fNQA~NbvCtZlg*gKou@>&YUov>h zUX6h_+H2QGQ@1e)H3V*E*0?_^F%_kYHHXYpFy|{8?-!PZn53!(F@k8X$~Uwo;)OJT zjExB#pBjC}t&IYuTM8>&)tpxNxJOosQj?ys>BuRYf3Q#pfD2Z|yG;%0b`2n>41^Ff z0}}fka!ALLCcfjw@cV zSFV$7+?W?y>}o8?kthdK(b8jF^s3{r>=ukrnDE%3>juMxGL^k~#H=!*MF`$d&6CUR zhTE-^f33RWGxdanh@^r{ahnyC=a2!scY2qm@wve!JMQKVhkE?@G63SS< z*W3laC3FP=ZkJ`tO>xQ03u$cuA-q|SU8Is>atr}^^31U9xt`V@2;96>w@bxtfVeeg zG}5xRQvNpAce{xiz9i!eLrf9>6(gI%}nCPr(F6ZH7 zlz2_u6-3-9nGl4LoEV!DOTuCxjRyuQaEq~6fsz^o*|grpteyya8L=|qfL6wFan)e# zA-ZZV@D(Oev%aTCUc}X!JzrQ#Jb=~7ph?+SK{{M2lV)G@Ry-v*jRNE)q+w5*1Ht9+ ze{y>+m8vUZ8bK_(YygsBD;-W}CQ`QNzSJ^?3>wk} zi{!-xN>Ve~)FULKre#rS2TP*dU>3a~ilxc8ICKRCALP2nu9n(bd&*4Hk^{MeS=1X4 zIM-BNLzi`=qYRX;kkrK_8CtVur~{pce<>u0k*F>#v}DHPPCg0m6GjZAe z|2aC3>{g*53NDBNNu(i&oJEEiIp@skKmIcx**<-lwv2*|82dY$S+mRq9n#`)U;Cb4;BoFE{Yk^t=x5~8V~CVhSH$@1#FRn=Z{E||NN zPuY3RHTp^PVYXtmYh5NEVt?idv5zXrbUr=Hb1Z!&Q-?Mgv;hw_(jKpaXzmY3aHyHO zyz{-?y1xrB&@{}1+j#v?e{9pCO18f_oADIU^b3A!{Z}euHu!}>|0T5c+v-Iy#s2El zw;_P3AwnMnxsEqXi>cn9UtB4f2|X~{^f78fXdn^bHshv@#7>tOJO`65WMIk4hz48V&f8z+0|cLGS@=5NR;5 zN_L^P7<8<~k8B3(xOodiWHS%xqXDb>@C`rNiP{A4XA&$+e_SY~>1-wnbhfLVt1ef< zlFgbOha)?Wn&S?eC&r7InGS+`?n{mv&x$VAdD5$@c^9d=AvhvN{l@@-%G&G9P~RSP zs`GQwgLpddT^ZasRwu_RnyH=(q5YF|Pgi;A?> znltx}e{&X5lfTc&XxRsj71}%g8nc(Ld*3m^?;eD%cu4Yyr4p;$kvS45P?+4e31d?X zKV*80fGiysTT!gQX^*a2Ya7oW`#k4w$?9S=u}e00$WGbNG@x%TjZ?=fl8YCk*t{4o zd%qX+KEe~0qEz(Zd!WUEVS8T~0necHeWxY~e_-@ktg+L5N9Gj0cw+?Ptv8PUQ1z1) z#V$pCwmBL>nufTloY0gOfIh(s2Xf#Bh#nxz5Nakkq2Q%Y3vsc(sE_6n5svaWBwC}Z z^$Q7aR*XY~o#zQ48CdPlF}2>|{s&EdaV;`s*LmJ?*3F-jl8YM}4}r2=iw zf5C~65TQtr_R;s31Z9a?0dCki2{F&gK`gAkPD|JfTkD}t4;SD!HQv~_*;cglEE^Ts znFTg|1%B}ey&H(16jql91!%)u1Mc0QnxXC5>qTN#^7kaf8fKy(&wh7H73%Ee8rz+m z#vJ4!nk!FPDn;_vZHFqg_i^7jA_T9?f7;$-lw%$YGzu6Vz$dvK`cfi2LF1CpBu#Jf zHlhPZ@qlD;+QFgZU)@USR)1NEL%SY1bvu@imU71%$M3XpL$9)hhoejgw5Yx3C605s zYyNUSEuErBNj*~~^He2>Rwu#39Gx*UMuZ%{oWe;$fB(f9 z`@sdV7NP-9h|k!&A?Xt2tZNlHKy?Vq9tW3JHTkXrb0#)AWv(22OM%#nH>#C}xETC? zAKGWo1PCUk_MvU|3mF{8Fk5*s+^g%8PNul7_)QT`toWVz6S`EISYDDM$;s?)ehT)b zbZdAXmB4J^HGqnpPt|zf1D4O`f9LvE(+VoBbqxYD&bOS`YoJBrBH}>OKRMZsoXXdA zTJl0nEMij&@K&GDH=DVP+bxnkM_#B4b44Q+1tL6%6Cm#<|IPhM?ypn{8k2Mokhook z8Cc&QrWR9dcz*#PUu6Py>{H*`Kw7o$H$x2D<%j|$qBNT1fk4U&Ofwx|e_0HJe^LU9 zOy@`FCl|@p6n*1oTM`!DgoMwvOha%4?Oc=qyrSWk^saZ)htH3DOVPEOSpP294%s9c zdOZgVhkpWt+t(ZZ3z;xEgH=i`p;{qii{qsFSuqEdvM(%52noOZ)L~IHrqtRwGGOLw zByLs5K766r*HoP`y)ovSe`~^p_alGz(Q}L+g-$D{lGQ;DH&M2BtB0OZOPc{YOI1HW zIN^^4SJ>7te7#vFrP-=$BMEkR9XC0O^tDF!fY^!@j3PDMiZ&{v9mA;jqo$Bg9?5-Ge5Zi0?$s7eg=A_NNomcs60`%oC8}gQzpHc@`_&Dw(FSQiXf+j4z26HlnJY?;g69xey-`V zo+Vf3e(59UJ+{LTekcU!z5FA&g0SyW)&ZSQbxHsnfkOC0{Q(; z=rButFL?$pm4O{bukhmBn!C=_r*GYpLgQCgtuXo4&#L;M>(t zjS5+{pHdhO$m@R^dkFq}B(UxLo)WvWEfTS>S9S=7`$m0pf2Mn*%FlY-Mr!2W_Em)s zo0tBhz~!Co+JX;`okpG_>hVwB4X5oGg|3H*UOqo+t6|<(!}@KS(r*Oi$k*ZE}>N^~?`Rl=tQPV4-A-;Nbb!m4JAYd=4g;b^v`V?Lv`q+yH zUEzY;o_=q*?5CS1u*#wT^sDZF9t{ykhseDsMSfH0?=Q;T_10FSiM+7w9;Y;6)%aI> zrz!x3)|1{PK6u>pF(ew5oYugq6cwYk>|y$Q(yA?J-wIWw^obz(_) zTQq095{P)MnbDhLu5lgQ5sy5Ea=MK6)ZCOWZfv#c>N~|Df_tc8r4J0CON2K zl&;;me}lg47n2gceIAOx(7@IFtz~+|k7sY3ERKru38#LCZ~Dv)sqwoA0olIc0~3Ak z+zyf-Y-aJ1O2w==%0}0CT-bT0NM|4RaUM(#gT%kOf)C|DSGKqH)}q79i4o2%CTxHH ze2+*w8>o@{)!*vpD-_2Q*OFgWM1tJ{Qqnygf1{t>-Ynp0@-B-HL!hz`~B=JUNboL ze^uJ9YN(;PW!{Qwv!h_4cAe&=7eZ_G}e!Kl!iaZH^jpz+Wo9 zi|i3PJb!*3*`uz_cd-no2yqYCRqkjbf3_d+bCoIzf%977@ob_2*k2z57S61WMuDI{ zYY+NeJ=K2m)v_zmFd#(WP&w9zm3n(H)T{O1Z!eVQUsdH`rxA9EcgC|gTJ&-}!zQei zshgi?F|k=&yA?e1>u|nTA9~-3O(YR45JqL)DgaI6?Xcy$zniO^?Mi}QpXmx1e>8AH zu~}sszGPr$1fZ>T_`=DqsmY-P2TM30tIco+I{B?@pOA*?z0DwypKL3NT)+NK6Q5(Y z1P3IORh#{$KV;ca?d|{<6+@)0fkye`7jP%3TiWAwAMg%koji~QHo;iXln+R(>ox@6 z^-Fjl)JE+<7|%p`F_rn)NEQyDf1JV6)4Y@Q4!Y03_1%J2Pm?jr6m*DKVUGZK=+WKt z;R%HLbfq#p;6s|L>A3G0s><i_PZdL8rNsjTlu$zmTT-((*Aa@LHc5_nz%h8f0ISqKAhY7 zn2{me{wx7Eq|6(inf2^1t(qY|{XX2?oon}wdQ(UZCpCie{vvm;w*8FbUh~m%FLT;R ztoDUqDIhRBTK=}23JPO)LkruL_|oqv0=m?5$Jo+dTCBq00gXQ}_q1|8b(s}gwejj; z?plwa)r3!v&jZN@$-SuWaR;FNHT&gGLW;7$ucwDyYt-`4<|Z^;%{MtT>Q zo$tYNaDU6vl5ju_;*BkcX5gjOEpC!PBP`p3I%h4=9V$BY&@=|Vf5EexVh2I^m)uOO zu)t5Rh|v18!VeQWijj2*-3;#3Q%hG%T@VlN>UF`nElLc)E#}@TNgNrer4@1J%U8a4 zy&s}^NeViaUXC5Q`Q|o-4(frcy=_N3r5@rM-|aDzn0ik{-=-uf-G`Uu+j;kXLLmKA zgV?={o4#;2i7G)%e+g6GQGTw1@6`25uMtE^U?Yypa#3;)wlzKLlIRQ5xZMAqTvL7B zm!(|AN&2)u3&Hrlo^rkneh&pXZU&l^0Viz-_FO2aX#yaX9m+Ne7JKx3tw`cCq{%Yn zojS0Fn9aIyB>J0HZ3HY@%CkC<;}0@q`ONG!3w`ME_(+mRfA&7(h!4EVpq3KK^6p{f zuy+Kt@7X-_kGIsoO}QZD{x&>&o5Zv5cu;%BFuOxty44ZWqrqCUr=CKApi%pYzl$zl z2)FI~POiY{&?9J;*=yd`BZ6K^1OL<}!xRPC@jKO}a=+RhWfC?e)`G0!=@)F#yAh?F z!YJLF7ac##e*~L5f4*)l0|6qR6v>xP1lp;UQ0HJviThseMOhV0@w~FtyYUp;EMmwu z&2sd7?Z)SDPE;y=dI5WAZfj#-;AVu@&yuPOPgfpjF!$MMRGZC} ztbOLXsL0fP@>^nWYk~Zjd%VtTysDCK6h>jaM3R)n|A3Dl{4QV3O_cKVw^%>0lZAlZ zfM%}~f4Ef<1VH_^HAxsrl|wyU;1CSvIg+eMidf}t$8T5iTEan6KSCe!0Zx31lCjq9 zd2m4ZLq=bzzuRh7T6hUXhNGrd;Ow$r5;h79rc4+~e4*^&a zo{qEZNz#s{H(Q_HuXaR<^m&bih)<@kjJtimf0ugtT>Hb}zx%z$PU#Ncmr>XBdEef8 zD*52-y?f`k^wWU;JVw-hU3F3fNWWsb$>1~R%LYo%A`O1_tKXn^m-aS+fFmtVvpIhs ztCUN!ZTyF9dm_I1X}WFnJv*l(;~u!p1aFT#1nrZv^%uvkOuJypFu8ZO7L%F5!yb%B zf1HD>+;LDslD$pRAKDA;n1d0T2$SZ1Kmtg ze{R3N zP4m}FlpauE{zH;jK0hBAda%!HAEd~+N-N+O@e>=Fq}1Z`2$rxTPY)VF!Dijh?FmD| zx|`fSmIS?(4mJ_~p2fq0*Sd{gp%(qI(l(j@Xn}D>lk#2D1eQ&mO>4YS8SB@Di|$`> zq09~r4D`%Lm#xHz>5j|wQo=ivf2TV1=Q+c8d0C&GbyV;bF)9wI7U}bDiy6*)2k_d{ zNnEff2vj0QXlO1Re*bq3UL@?+i|58?|Aes7>1WIhhW?xNd=m22nyrCdSaG68QI*Xx z;Q3cW^9`H7)%U74u!`lt z`V36^j>^u&;FO)Ia5JDE^Z6;^eKroC z?1lcx1BNcBH)xhVScl<|M5q7exous6w7Y(v*=69MCwSS;6O+hU#)(47h!6?>7yAjG z)^o{sVINa$C~wYbjm6Aje;ZNTaf)hqCLR`=5WD|A@%jDT0H)a8pz&2cC{s%eSLjMJ!Jt0yoI-TZ5?`8eAw8fC!Qzgn^`9*73`N|Qz?0; zM{e+Hi7GN}!&8l@7B-|Z2QtlsRG8pj{iPX*e`C0JOvQltWIIA; zjtkST;AejqO?&~7e*1U-MVyz)Yy1gU>Ad6`OW!cHvadcK3xg6Em+fe@(Rzc7Kk5QU zPHbgt7;!}}eajT0&U*)Fj|}gWB5{VMez+LM5Z0Wh0^Gm^*k^gazN95RYXk0$R!qqp zg90l?2}_k#lZ;XpX<(mBVCYd%(I-TOv|!bjd9!oC&m-u!YreFAn_ z^N{=bg|zk~SKxd4OR!lp#>a=Xc{Bc{%MEc=386p(L^pkpe?f37__R+#%ldJhRtPPx z*53~|y-S>o81}#ABT$r>EfwV0QT6Qxl=~_nVeR%rS`Kd)Fl{G z(D2*fEXq;MQNj$?a7A=oHqO?Zbt`*Dghpl}iMm737bme^pCre6vLF+1u6%Np7dhP1 zL?|Z~iipq3f9FfZyBw#m-5;uJ7>_b*AKh^TUr%;JuIe_Cex1DO-^F-lwaVJ6o@C+r zmI$J{<-3CybrkYbFCC;6dBqAkHDKc_uhOr2j_=ucsrtLsV!F0Lj#@IkL!Ok0Tj|}P zB(1#Ru0pUCKZ`9#1VxR;~-#+ z!;5J7e;y6Nza7K?;-;_eRwGSu<5^)-gpZC%m7y8qWJD%W=@~wH=sC-6#)-36Eg|Pt z7x^*yqf6GXR8g>7X`XC%n6G2fD zmX6jLm2vzO)_Qa{-?;8rrA?PAlV{IHv&eJ!F`WsbVr*3$;2ii6%sKM}f6!oM#iTOb zfA>{ZbUjj?z?jdOf-7@K>C>wAOsKf}eybmzXqgM3aztv!c->k?r!Mp;kD878aei$o zQFK>Q@^~}Z=?>4vM|7lpVULeanvC6y8H)Qf(kbjl_}l?~h8hRDu8ZAB2W9-zXD z=PHDMuJ2fJWTlpj7_?pO?&qm@Cro{Ne~AyDI-CI5i{k)6EF+#J2vX)nCkiKnl_Ci7 zmLPQ9af%>aFbfy?L2m&y7uYj^PYEQ?^OzvvQx2lq@)&`H;VlI9nCKg!+F>3UY~Wah z7r+OlT0ORj`+z?(y4pie`9o< zhrM6+HFsq6(ho_}-O?-`uD@tB_&W^aX}HFuX$;A;p&&$116Y0k?p4v6&@9s5v*I^t zz=!nN`21lR?2V`mc^D!GhFw?#k^*sl(hK0X?0aBHBv5QzzNNDeZ=K*|q@*SKqkIZy zTY@8fIiy%ab5aBRm3&i|9^AyMe^S*YdRgkODU4U z8oE2r-Nfe%W)u&IQTcj>VU5O>f4d5cp`yW&pxMfGG7e3}P=F;L3Rk83f8j$8ObL7Y zVMXvP0sGiLcyxd(5gi0TfMWA|>e^d(U#76UQXLCB`#o8zW}Uk-bnjmfTDI3sPt0p%j=AQf8#HBer9i}sH6KG zQbT-U^kOJ{_FP+MHX;->`4awa#G@*VU>ZvVFJ>RzmR@Z4VJen-WJW=TJe^TtpW#HX z>~~wh?!c{35!rpkfNmAr$Clccr%OT?etKWDB)TsiH8rR&!{@xYs%bAmUg1X0J?7bB!U~R=`epb zv6rJZ)+WBX*gvP5 z{(7#{_q0CDe?T5DoVxYCx%Gi^O=vk)r>*0I=q-STbMqhqj&@@Rlyv4l2yC^Lz z?eCuWOTP2yFe$)Oyl=9@@%cA9%hdQNSn*IJdW4+-f3?5`n+sva_;(SjkOny+rBR(0 zUhX^C7c!rSBA&q;*)O2h5?}pg#x`W$V@miE^Z`hwp(@)RtrVzY_c28^epS(LBlU7y z^)lQ&VG3Ag;QQ!MOBcw%*(h{B#dvwONx_-xV0{cR<41*nrt$R1sX=1Bj7gb!Q$(PE zE~rn{e;haXrdS&j94TF!Si5XJfg@(V^IN|s&)8Z!}mKU~C>ENx%4-j^Xl$uXF z))k!dJN6d=)F%?Csh7>5ve)@{rHO#{l@4>>@T3 zpR8v*c&#Za18i@zrMu7SkBT7?`?)%^|IK*xe+vZN!ri;4Y2i<;pRx)6iiH~90e4(V z=YE;gkJ;yGSXh=}UhOn#>g?t%cu~RvT4ZV#Ifj1mW?q?+%Cd?P`~+Ru&~9-NBD!=v zyCI=Ich1*h1dI%L!W6eP<8xS8{)nhK&=$q8X`4)QUEjF~#H9cs?!)>#t+@Bc@xIGA zf7)X2Oh}C&3#@53)F)m~?EOIIiUi_K6dA%a;U~KC)_0?JC_u+)i+z5v!nc>AX7rNR z>(a~Ty)!85zLdtFC;6qXkk5*cMf|xKzMMJI=Xz&`d&I9_p3+CXT0U>y%F;pf3y3RV zn3^g^xPvdSZw|O?|9l#s;<|Jny>dGUe{aFjgBIqcD4e5kl@ufKPIDIm$WQ0kM}3)A z=G%@@7C2tWt#TNmjr$Y&N=!}s5QnA&e)eh4Ti{|qq&cQ(+Sv^xCzUEEUvq42DXO{CkN0C1{Qw@3Eyfxoh^+9Qprs}?a_0&TS z#@JOg#_(+7M!`k{LCR)_s3F%m$7rxng<5$sU|hcO%69D>{?^W}_W+`Rm&u=(I|4XN zJn}lLMaZVKQmc2S*4b|^vz(@Xf9*S~6EoY|Z|8~2{RTWYV(qS=FXFoi8m%+PEtH$0Md1n3wGD3{TQ#>e@@bDVH^}ie`~G@04LHg z7iJcOI|s0JU{h!XcK)J&4jdgJ!?GK zFj%HOweTi=0|sSY#`|8?1ikC1y+Y-ulmAFU$|Q^A-pHq5&+~GC4-JN(v4c(1fx}8z zVIlW6Hi z(jtiRd?ul*CVXn1ZiPofA5u{ffIx5EcA3AmbZw;56xi-uVuS`*UQjH#Ig$rQ3`eszaGgk$o{BRM08BBYvz7dWXauWeVMW;eK;c5{0c|8Fwf> zHAGFuxPD`2boqhzmz9y^w$#{hxBsqP(<;iVBu`OJ(Yr7pf0Z}O7|9%wO%B{kfKrRT7CU>pD%< z9jQCt?S%jiYr4jLWGvd+zvm^+l2o9dJY(MKM9xWKUEJOsf-zlu^nFXbwkCT`bYLQs zjWTaj96HeuMEPmm6j#*ew`l~m@bI+l;ouC;qyQ?+fAy3z>M-DmCU{l>xYRt=p)YRl z#6&ejSER)+=6V>R{B<#&^!()!F(S~|9daAFz22+sQLuADM=J^y1j_d^*4-yzq~BD_3@Z)S-39*5w5*zz69EbK@pn!P!mvqwRYQ zBTMZYf1Q?7%i3v^dOW|1YJl=bolh>+zqsS5w?E^_a9*6&TvMm+Se^FLmKlz7>w@}8 zEsrG9Ed&!|y~;SRjwrk~_og1`#xgDaKArf5G|NtGkPkmilsT|8_TN%xV9by1z5EO> z?-*i(Y0pu}G?6Tyn%uW?JSy2$XhrbBkFHF4f6#OujxHs_io8#u<0tW?xpZs35{0^{xp;+{9y095SteMilcCr~pGbKaTY%zaB}>X> zT(TgZj;YgVM3uIka zuHA1gm_3PW_87s+#fXu~fppH*sNJ^h$?E6YKjA+k|6DCkftHqMzVzkm7AMTBJ0E%q zqx{iC@wP-j5SE1y0#hPSY>4deO)t$Me|}O9OgI|#tIU>%dRFcGGS;Vl)=xrI%e8Jh zM5l#)CF!$!%1YyfLj7_+-jMvIg?`=#dwuc-thk%|iyDl8VU$3JVp)7L_7`{lVqXwr zRGv^SULCVyhC04;BbFb8=9g9LR@q~v2y~a>n6cKk-+YKV<|<~1cG+?7UoWbBf7iK_ zr>Hq>04S`!(;^YS?%4f#5n}sdRhBa!DHH$2>Uo51#ruU^6DLWInTjXad^vJRNr_kF zuU1c5IbJ7k&PeBWTQ@Nf7`73Ws{FVoF++EL17R}e${*d#Z@uW{Sqo7dc0`w z5zit~89N5~(P3co1A=oKEtc{uQ#& zTi`^7wq4aLjG}qXU0lVH&z!9$dNZ|cc)o{K7SZ;S=-IIXqSMw4`XgIZf1EX0<#r!c z+ZLtMUrDi0K{QdT%gBjbUJG}Y1eYNJ#j3yTRk4uU_wFqW20vGbXw$JEG*a?LaTKOu zkqB1VaH6vAr`DEkrB`06^dZ6?q~NI#M6Fot>kcWGeHq$wFX>^pb2zIJ*^9UHTLzb` zQT-apGBY#4=Y zf0@5*Q-dgdmFYC?GyCbEPcHwxX%*u7$4TQMdK7J>z&!*$==?Oj`~9qGgZl3X2&wb@ zoUdfmq8$iD3*45iWYLDE%j<@WNeoo_4kRY>dr%V%bM=wQo?y)$EYWPKJ7-Fwi)7`k z(A12hyq12#_IGQve=l(^1`?=|=fJFiKx7t3f;HDQ)36rny0q~GcM4fPyO!h+&?(V< z3nOXs8if>}Ucd60j3KM@b*uZEUGEGh!;Poo-r}N*9QOHnpoxT2n!awKaP4OEbg~bwuJ6}ls z39$D(3g6P7CgF<4IlXpFJ=*Jx;-3(@<+!vyd)D>dnl@w)b&q{SHJx7~q{mLH)@O#j zCZ@npjeZH0W@)^0YlF2}Dgr=Rs4v9?MC#K(3mN<7HAkxQY=*BxBz`Hg(P<_6?c z2x}BSDO4Cbf5LE56Y?gZ0$v7#TvWx8SD64l+cqT~y&5^pQGl$~Y49R2bL%#QbcQGJ zn0HtO%bibG+U4j0egKF$rZRF}+-N2?+bK*6szC=e@Y|26S-Tn`8)rf{6s!G%G>y<}=8q3M?Qq$*6!L>EYw$pz%)-YULvqNU0*Uwn ze@~2dEMRt>?PhqTc`zle>PQ07-t-*a8?r`s}s% z7L0Gt9`Mfwv5zj`&5ArIrK zNK(W`hrInNKO~H0rs)N~dxP!d!;`Bth>)w8S6Zc9sab(%Wq6+OHRZ292*y#eC-Sq~ zc{^w3|Gti16v1H_^5@0^SZ}^1H+5j2!>S-PE`p*!5Tq%Pl=R>z1{}XhZq|bZdHEId ze-s&3CNI(o@oT>>FwEIyEZ!@T;hD=UdkOA+qf~oxrFCyo35|S#U`DA_`9w^hd5@By z*H=j}{sQ|e(zJAR`dAyr$y1!>8=EeB|cd0HG zZsaST(q2#Ss+Z$WyW=Kt*s`XxjUzYOe;+D4d_L^kE4<{80={R+3IAJo$$xSB-Q9?% zshNoCi8c#Yu_nhQPYPTrw}!iv5ZK`H_ap1dq4%wmL}I1>Y@hnGk3i7&2T1c_L5UXJ zQ|Vn`_YDmDK!QgThJwjnTc|HJzAM+I&8ua{4eeXYU}pzt*~i;^TI7Pjmo+|Lf3_=% zeSApFo~d-IZb3!1#y%5wgTRWg;$o@_i)8d!R0#MQ;c~o@;JPX@n}PwF&C2Z3m98|X zr#xdY)aei@0Hk0%&x_|820pyZX~vcc!zKZ}=g4DamcItwQ_KA*z! zENTE1nZqv(hqokgvkH;*7iD&9f0YkaDRqalF5w~Z0oN&={-EBo&(pvfpo_K zUr>zyTjSZ!-sK_0CBn21(!MxgVWGhG9ED9d45m209_?n+b7B2)Xqju>e}vkF_nnDm z!9|b?x;Wk)g)|gZm02KV?`guvYecjuar_`S>}7aYboRnuzMrm-@hXQO3z^b!sZsxW z4Ps<$@63j0c-pBypy|5Q&lK?xGeb0pb@qqtwdYT#GRv9rFeG{FXVXyhC;ch#D$$u` ztG6}r=em~tCq=bd$&lA|f1YH-;)H%%ERa%Z14Rf|`h#HT6qRhQ?35iX97BvXd@xNe zY4^kb34$AK`!Vbs3f-T(|)Kn;I2EBc4 zLW6x@Zx$$ESZA3d&&)_A^?R-qmVEI&D0!U&a!h9n&_a+X@!4zgf98nbX{m+DDK5Vx z3!EalEBLpv#L-O zJYcaQb-eVh4QX~EH!VprS%V3bT}pV@`)H5$&O0?C11`Qkm}LZCdgLF$KwTw4@O`mM zh$|9lTm@u}bh&@MfATTW&vnD%xv){fw@s7zyWybOeW31NuB2JI55n!k>I8RYvYsi{ zDlCf0a?o7ZkiGjYXIkA=KRr`s^l7fR`Bb{)LF&i#__m#6sI-BNA0J(yxV8K-NdyG;T7Y-c~1D>5& zzvCN%Tj0rRUr1tHnf1yLbyThfK14Eg zssBw%eQI46D0r*5d8Xj1nexsaf?|l$RkLkid-S6bDrEl(S$lYCEG-x~E5w?^tQXNA zZkcyW*A~mF*Ma5>I`erZk6BtPo>TN>pur6{xdkaPQIrJhr9*ZsHDz&>GH%S(+U z{d6I*{ylW!RFm7bfaza%SF-CYlBM_c6jy&QYJswtr>xABn89EJ#tcTP1@kVn6!C%t1;(g7?GF_U1>OJ!AzKb0g1>JSpu@A};(4xr`l{fv;xY%;jCv)5 z>N;0oSSTo1O(upU&DNLW&yuUkyHv7?m}{AWE3@I7Ll+lLTZd1HtBC#z-D1)%xXeP( zyx->pB^~tXID|9>msv;Kq))&&g?C+zR%MlHrMoS|4H=-OUm`6kFXq7@N(}2;>G}Yd zRlWcpDLu1X^_R~OtAO>--lAuyj;Xf|_wenP(jA-SDbzAldbl!>`u;(kNo8^jXyKk1 zm9jr!&uhihxYE!Mq=D82(i%T*l4<;SI1lQ6T%GO;9ami-Vi7#vPq2Qe5|quqx)1F* z4$PL{wN5Wh#g6xN$eW4~>5nC()&UrqFJ1^1;e8fs>ysT#X{Yoz=f~wqix=IpF6F(4 z_3!7#RaN9*z1!?wPVZln52yi$wJWi|g5_YgXaNSGr9oKRKWTDy_hi@OSNFXiZ1-4T zESaI1uD`%^wolS3ZKQGAk89|^YB0@SyzPPWEP5Y;$J!sv`>)sLU19G@M**td>~J_q zFlmx>OOcAV0cw07-Ok-4SW9TbvsY)bk?elk#$oD zU^Znldr(?cG+d~MQC%&z7J(9#o)B44>jhKsxXjwV)##SVDWbpqk4 zBYAY#W`p@pxPF5fpv_HUVDOs$i#_XmdMkfwfC4DK=|>|6eV1L8tC;%->y3G{b8#+^U2@hzq3d)0#NK4%mTbVJO5si z&;B<{&GEl+gn+Bqqq|1-N6Jid>1W(LDCgCyER>8yrCH-%@W*HA#T>c}e4DsEQR)Z# zJKhf1ds(ZAJr!)@3@F(cFukriR$w7H1Cpda0CzZ6b|yDmn8J!ygWoWd5lT!etcL-oCIbK37$*(OkuX9 zQ@FHHxGmsQvh==4+n^N;$u4JphYdr{vuE2YJ8s8L00A5NXuJqu9%aT&86U)cXJfn2 zU8kPgW8gLVFot!TRVqTmGKJO9S3J-Gd`s04V7BdoJFdvgd#&uhG9f&U-t|Jd>8Ilf zc2nA|(K%H}4Lp4J!wDg;xJ^fyQ**CG8wE{Y)l6TZ&WWT2Dg=n8D#*?;E&@-5Dk~vh zTp8TwPk>-!ch-umd>R)!TCb^J&=@=~v&4f_G&Sa_nQ`E@0btCf4Z$OQ?EAPsH*hbyMiMOKH^-0-QL!T&?E}*mL->L8W}Hzeg)Yj)O0C?k~O9?%=u;lRiH%pOY2G=pWpfUf(^ zuektGBiIVhh?`4rNot3~MMcO%gXV4Ey|QNIX~id!3&8&64UU2`No!!Co72ClU5m?Z z{j1DEO-C|!$1wYgRNLz*_4&oWXPf%`XYi@Q%6jO`1x>twYQk&lSyZw(%a8}P9kiQS zrUk;87{6UWm&7g?XWBuu4a`a)lmDl$B5+QgUFqh|GQ4S>otX_L1{Iz}tC`b%z~rd9 z4*hpfgH>B2v0(+-bXW;f1v$!!#2Qs-fJ`|gyv;nIqtb6yc71M+!3rid z$P4@i!idDc?|QZVIdlvO%x#lNC(T@hv@XX0^gXjd^Vd5JF0%QkrlURt2A2y`K zzUgqzwP{|BXBoP2TYKv%>-Nkh+tB}AjJr|J$YIL<~vs`C~#}b&G>g&8Z z77aWnzWiK0++#SvZWM~rNsmrlTX(uB8QD^pdhNT0!rgsmIX_%Drqt>jtZ!*SIG_D1$1{FyJop~+dqDlZTXtn!zg@4)vSG+#rGJ$Us9`fKX?x{o(kivp_%_So?y@r+kGvc_ z;H$CKa&W8>&2kQ$4Cck(_i2V!7>vb^u2w-`4flK1p(Srdd)tTe0y1~HRr+pBtaZwa zt4)j1ZBTg-tA?OgD=BZN-b3r`T_&1D@<5|m~q7RHQSJB*9!!+ocVoXeAE z)~;CCg;n-nXA>lvj+2W_o}HTB7g1^NBf)CusDtAf4C5W%L$->Y>%|YjJKVgVeKQ^N zJ{Dh2EDF?zhJzoxD%$DU1nn-hK*Tqa4jvSz~pZR51gFLidlnE zHicLzH{FO-JYWRcPFAA7Kuuz9Mz!muB#$}CnCm)sv1^|vpdy^_*2t`P(alxHWl0?5 zabMRRrcHPZUg@Eof*ryPKu37O?S}XW)PAk3vZSldVvs{%1ZqV$0?om7NW3AZAn$g4 z3^Q*hPx^aqbDSNUZd)w;Q9RwRdx}b6YwX2prnoXj`$47NMUZA%1G4`hx-@MBjO|vw{`OKXb#JZh# z&9d5w6Oq47ZQg2RA?Gv}53e;yoSwdN4;EH9^)xand15jdB;*2Sgb4|f-?Cl2Xw!nQ z73R7ExHXY z;aJv~i=a%N2h9WRtW``BjnPNDMQ3|KLKkQZlUipsuZp3OD_{M6#WhCzuWcNI!8h-J z-+icn<4f~&pJbYGe!Dt*?0lmMtW<%c4vUOTFX+>pN4=;z!A*{~Xj+5RCjv1Gc@otu zuxo-8GYv1w%N4Uj;w(PD2Y%$bh@h2_zf9jpE9+{qT6L@D$2ga7b)B5!$G)^6!g}G? zmwt1fzBO~_>hOVf8XIb5?8D?X-{P6{Z+RmHf{SK25Ctg>QeK{-U5c(#L*Ih#qN#Z!{hnE(?1mg2_tv=6IOst;H zV8UDGvo6(E1;B-x1Yu0(pJ6&&z#vGS2om{6o&Fek$J#i=xqA~PwqNCMNeg-nHr1d4 z4lO~d+Wd{4tICIW`n96xGotr;T6Rfw^v z9~UteFkjvYt5;7;mKrA3h!(DY(z`b`w630*KGtSIir!Qr6J!S6I3{T$=A(Xp#2*-J zv_ag1)VZhAXJ5Z>ILtfsX@I}9l1{1u9=?^#(!dEeD%<<}NCufb44ilP7d6ys3UK5i zV@=A{qmG&7Sv}n5+9N=n_j)eK3Bpw=H@vc{QZccClXV0j|1>CvRGegTdg_uBn_^Y) zHh}N~WKb?>vca^5-JjL#MzW%6FB^P49Pi5)UHv$izg3R+c|dL_ZHXPIb{hKv-SI*= zRl}GradECH0!JU^n|m($UZo#j^X`;)e)U>0DQ4Ntw%kk|)nfjkSFU@w3GkQoj9T3= zttWv^4Q~1A1paf#$D^6ybw33ZXMOS^mtFQ~fw=oT6M7gU)Iv*S@kx5N5R^gjYpwWx zgRpz-QqF=tr3rHdIkaqw&eF{hFg$Pn{Rv;5DRD$y?M9ZL-L;}%cuuD%=-==U4t9S) zEil5~VQTWCod45d_Wp_FH2*{$p?9bSUkDp__;RwN2Es>7?Kb>91#4K(jhL&GNdN@eebm;`3b|t z0s@a78Z4dlL5~Lo77F{qss+BGw8A_SB_iM0)sL?ZP_Le|?MhQcpMy-wh&ej*llEms zSwrr_@*@FfGlJaPF89F8pIG`RujA%=u5WEkZ-)+iHRTq2tCO`GjpY`UHyoK4L}$w_w4C*`oR-H>eV8zON!?T?7X!}jgGWb~ zKw<|}iJ#@x8^QZ^D5!jYXk1+1MM)2u?l-VLS}TCI^U_Hsf|||uxiZMX2SY^2vyf9u zdCLpkQXobbHK6L@_C)A*C6mvI{Duto)EJutveu_y&uwNcV5!Q?Yx&^9#lTV55=?*j z;+l7O%feqb?WYIC)WM{T>5e0ATrz)eEW;>!)1kuFZrd_7shAtwqje+;O=msD;pF#e zM>OCy@<|{4P!IYx&Ar(-d56QQI@qa0(<5L*16)Dz0iti(fI~qvgC*f7dm|IxZ6fk@ zs!5j9jDYi)iol{pw8u#B%h-1X=&k<<@P}I6aJh+lX8LFAA^y7q3_Ti@D3xGVy@o z?{{bAvvrd)1>%?N1d~hR?>;wE2H%D~b?cVw_44R76m0eQ-VomanGjIyjDXD(V#&F0 zZ=iv@Tyw;D`|RqS$$`)Nkf8a7M)cc1xc?tqM0x)QuctnxSuY1Cc*3{+GhID1eb36P zNS+5LUPj`8e~|D$xRU!1G7-NRSXsPtgiKid0FW~OGfh4JGyQ}AOjFPQOh}o)e=g%A zT|=vOD<`%H+P36g*VlRVTiMFlRky>LOt&hFC!9a+Ez2_=O)Mvjg5*{?{Lp5bm~Y|^9>~<# z)zwN=vFx%4|J08de09H7*a7^rJlt-y*)~AF00#2QM4#h9$osKf3oHI+klCbef}k7` zNs?ie?=iIUqYsXo>$;`59<;R?WN>BQ!}Rn| ze)}hX{F6TipZ3##;LEqs7m*`h^5ebVTkVsizgsZ@$D7Qrv$b3QMT{ebw0EO23gZL# zo!gENBcBCo%XWvG(v^t4nX6>F;2GZtYReV}N;PD1Ed_VLGeDMlhc9|?EH_C9j;mm)>e;9_0$AG_bk&@7S6w(_RiHu) zeD77qSL#Q_g+4r&ye*oOT8Y^;xx&v&nGzl~K!U`v*ePE%BYUSOW#Z0 zeg_}5>jvAS(D_j8pm~F`(#K`m_0d5C;IuXYHhxFyhTo3my9RUDCOzENX8GOyDwPiK z>ejRMdTzPZYfg8H!ZbN)o~=3yyc>}RR|%fUk-WL!Ypkk{muKAv5(MjG(S_`BEj#%; zE^ao?FBJH+VCmy1ae6HOP+rj5b_X>8H^vD2}$=GcZ$7`gp z6Vk2Ff1a!h0&+MFIvSW}hhmPV=7z_fvby}4y;+Bnk_227pRq*x8)83*;QBDv^77M>HtmtNdD&R=Z+KH*{Gybr)iZ0^j zJaZ9*!30kJNTN@2)rrUcG^?n?Kj*+}p1jpCY3FGT8FwnUq-?!%;54omVlE$v6HJ)y z|4gS2{^y)cx=s}}#~A!_R!FUam}sg0T5Q2w#4FjJ!pmYIE>Fe{D`g~aX9cL>G-tn# zjA`Js7zynm06gg1AN4|tk}pYW|D;HmH=kVnC1Q1L{&)~Nu0+z=!oH`TAd5~OgWGB6vz5@! zCb2laUB^%pp|gY2M7h9lErGc@2So8<92(75Ra{Fvz5V@ORZ%l2 zo0(}9)m1fKGIrt(V!tu}l&@d;xf%wu)B^L=0^{Z7{CE5#DZ{cJgh`EmZr>Fq{(9OaB^YFrGI45W(gL&)Wrt{hpSnPR&zqrS&4&hxGlr6kbHZs25h z;qi?w4}I89FyA5UP$!rewq2Z1k@Q2Q>3WG@nP^Bq3g?%)q4C(ykODQ*U9Z<0-6 zL&JodT1_%_2JXv$cyjNUat5KBq70*Pp}0hEe6eBbp$DJGb)U<^Wb$3%cRDxoO4e=SB`E#h;fPU@g_W6pfog1FtJ-UxF|yYQ>3@tJZyxWnQ3 z0I;4I+u^wKehfpWHkvi?hH9|l#d(02lewWuX|if7BkD_OGU+J8KI5WS=+M{ivw1^RpSHyzQm0oqA%ux1##xT{Ms>FdNS%k(IYEIQ3N^caVyY|0gO>N@LSRPJ?if5&?DUmXJ46;IBfj%27Ei2jhN0)+`z zqfJ{-sYZ(>;E~1-DPP^@zD4{^U`5b2eS#`!-Rpn5l=mM59koVc(70RjwXXYAo^#pBH&ssb1#qdfz= zAD}8GZQVL~h)2A%GtoQn{5aK|y(T{W=YsCZbQ&~$6p{A8f60D%FvCL}ii-@kb-CJ) z7;aAQ@M|a5%eyT2E@ZEMs_Ey_@qmYtUL{XFY-hW>Jn?;vLVCj+BXsQ?phRIt`JlS) z?e*^E%uRZvU`U2lc6f>#kh)v99H|h-hw#WRi68|L)4un}%j_+tLawHAmu5W@CYR4^ z4ermH93Xr|PTe7Q|CqX!CugMp_CdP*b1&$WuSV_$!v)jpZ~D-5692Zu9f8B4MxPHF zl)2%JWm<3R?IMh?L!_`JutVg1L2#R^g!PqX=gk4oZa$RJMD(IE>|NSt2oKG;)fFVE6)FJMnr*O{IoQ3K{ zGs1;`rGaTlF|(g5~_}sQ26-C-2&EzR9L<48fmog-R9Ee zr!Xr`lpT=}1TNVV@EzHEyQTkFY&F~}$Po9&rT27#fhMD`I@1}E+~mO>#pKe(LD-lk z_#942$mR|fg;Ab*sF#2@S|lYwYhy7>XUk7`n&E|OK=CqmB{P~qvg_N}3FD&YwBNK8 zh~f)McU35Vat+GvQ?L^rb!FnHA%h#&U4}U~6o~ji+5}PzFtX;9y#Mf+gtzm{o`-oV zpk97*JprRaXAT@y7?v-F+~IK*QbwwVs7Cykum2^xIg(=uo~UjS3LKW0Vn&P0JW6hr zM29>HSc6>dQ9-@i|8|X6dh;D&dh-ooI;B$N4<`Kv%oD%=jVxUxNHVk6Ssux5cI_qWP}*e@ugeAOS0qFZZhjm9ojPU7`GV$E!~ZFy-6h-!fEzb+X*l8NKSm%S zA0G|On6*6}e>|>)4p!m4hZ{=JYU)<9l_8Wk@vz#+_&YnZv|>z|2>YU?;odJ*05E{f zi&=t>#(n;sRpEb=7xZa)GT;aQB&7s{)l2NSr~QiI(C2~Z9DExS{ES+XGhPuKW(MQG zSA3C$@nWF@T#jV=?JE?1#8ZZ=%uVVUK{*2~%_)Ws8^ghkclWH+;mj zeR(^qOHBUD<1BPhTejwgAJ7eP`1LX=PRdsSfJDQg7zyQ9i-HJ599EZ+5_x z)Z-3x;Ya1fXV*`{$lG?aEu9#xE?w}(ylz9to_Vngu5{?6%}774MU({}0rro$SNtIg zPfEp)T>_Bz<2n3mcwf7B>o4d6X$yz8t~aLyYFnAcPqLs9Q4L80Wqb&PAQQ*^va(4hSi{BiB) zlSjIRBYj3;-wK)@S}Gxc_Nu)u7ZC6`63w)!mKzn&8ug|)+nNn9Eoy%o{$jxdvi-4g z{Dpe<79^2%+T|i*^%t96M%$Y}hojZLFjbT~ z$ZF9pcpnA|{q~8uCLN^Nq2;6|-o5M%fwg4ycujXr!E@y9{tYCaZ;!x$HxMlgTAJdy zO%o}a*JrHKoR{QI@sncZSmIj)N{{76GOw-<9~5Cdf&NJX$Q+e?!AhR zxf{f0%CQXU27C`&M=52|DeDRIwW+O;EHh5IyJ5#@C()9uhMOvnB@b}<8#G^w$kMhx_RrHUBgw>&xBJ<_+!8ZVPw_rEz6y+Ff- zV{-X8Rz%us{KD%6tINTuT3JV6O5hS#!l_&V6<^)F0~HG1lP_-!NC)Hla?7rD)EWS6 z24@qx%W&-jLYMI-@5~*qxSs+?^H&IO0Wj~Iw?k?$?-;PFOQ9J9cM!IRX{GC zH@}GbJ5Rlfm^UE??#20iYdd@PJpC26mI``X{d7&=<;`7%8|XXdB%CiTk495@RdSyfvM;=7PZoOd!tjIxTbVK}F*G;wM>hhf6f=}^J9EJ%AG*oQr|!AZ zOKZuwW~^(sqKut%xBH#5U!B*MB%W72y^g-_OGKOc^?XE-;1qrF=3XI)Ye;O@ORQOq zAor1q{;(%ipv4!STE8jDrk7UAp@vW45nRg#zKQ{`_^&K^iIl{&J9RVE%qMZ}jg*km z77{ChKX;pIB`;qmFr|CoS{Q_16$~E4jSvd9GiN&||Fm(1lV45TvQvk?&hA|-^5r&4 zolnn9!Rl;DcDiLR&iO(R6uH2mxC~MAyZlz1qj0Q1;RYHIX$pD&@Ef6+& zjGeR*xiM_fnH7bN!)oG6Pu}MZ<9gKwP%pne%(305<}IqUZ2k2`UY{C5p<|v($#2+A z&@^rtZ-;cbV>B9H`ZGXgO$>j5x2J(?t&=!@ovvO? zU>$8`)M**s=-V4JY?vmKWrKQ^Uy8JVMSGhkTc;q%Uf0I}!=?Uv%uc?Hpi8p%LYB$? z!jWURMqpg?7lLtTShd z0$Q;%a^vuR(I(Eid2%*~B?lqk+zJNo)Y!kjybr2Eg{Wv7qb z4b$_-!`p@1>^)VNVt9|j9*uW^dmo{-l@`K7JOs$@hN@A@EK;vJ#fuDBTeYE7a*`s4JY z%la!NqzPBuc)kek!S!9{SzUvyJiW`yx1GiL)Git<%YB9&wn)sFv6{AYkUcvl1PxpK zQfd8WRR(+j8fE9L^q2;a$SMpMLZ-mk9z6yJ;qU0_WdcLtXt{=c z6v0FdS|A%?%nT!kpf1M!6kcq$T)%VKGs?M-pU`p+ND^C_C)@^r$`n*@SZ3zSzuBJI z>dvDZ6pteej)a(ok-YzuKuzdox%~0~f1xM76WWxrSna)$)n8kFIm!0*OKU+bErjki za-1Laj={|QL>t^aWo*wIqVNaQ^||8AQgxpVd}F+)O91u1nduP zg0KKV{`s9zlGazEq3zc>8D0;XPUn0d4CDhMo|U1)DuxudU*Rtd-sPLy{YbW=um9KN zztEFx=HK3PFVp%kJAf1BSH7br(%w{kkMiYv#R7WXe;R!_7Cx-wvcL9lsEiu`rUqa7 zX5K3E$6yMR`2K&fq36G112gZ!I0ARNKRlP?J!DQqra5gjrq$IY`L6rbCC@8VDCZEL zN8w)cvSu3v=_>lrH*Pyt2q#l0Q)rT9A&Khq-F5}Mh!tha*66}CKSPBvNI~A1WI*Zn zI1ObdE9_=wK$PQ}BH{&a)Th9E%xCYH+xNFmj~9~uFRLUCEuqrc_I&lhB6%YMZ}*4+ zKO%#3j6qK~kRNcOXp3*B+e{Ql9Uw9qsrW%CFPC@txl0kf=y$WA#^7GXhsYERfzn)r zzDbpbeIUD*+T!EY)F!vSaF*NB1PIbb2+|**T1hL-`4s4&ByI1bu7ioifC7ts1QCC` z-ToAy48gMou^h~N_+ELRYwXBu0K^}l&qH|a`?pKXJC>BQ(umM`jnCIASfa>lnPl5p z?YDP9*t9C?=sUsB+Z$If^;3JO>z?~V(}sE=f%$EP6!L~M=c`WbN5c3p;1&DL8N##y z;%cI{WTE#hEfmBzoctAxYFC3J7{tDci1P`aehAt4hTXccS)Ke-VDu;4kmV)4%xrH5 z(;GHNnLW!>?LJ+dK_O%i)zgC@>N19%fO*tIq4u4giqDahe6^s?)445pZa@kDux}%D z?BfI_$bk`zta^IlTF*8Ga4Hl6gC$NSNgY=J*WUPi{ZzX;1Hd%*j%IqRLbDDh*kEH3 z!QNrV{w$}`$(wybR(p@n zu75d21Y!7Q%}Kz+NuXbl-TUgPQT?mlE~=EWPXg zrf)p()8+vx5cBI+l|ICWN=nVM(gT0yrQtH@S)^e20Vb0!JzMp@{X471rnXd-DML4j z82f2N6hbRq?Wh_o;spwlrVyS^MRABRq6fB0O^BTs#zIDP2y~edYs7bSiF1f9e`ry} z^`0DKI#ozAb1Gv1PS&lk;5b}dD3asOSnYRD!HE{4i1agf)+J>G%cRqA{vh zgpAkGbLR7se0%jma?MW88KZ4?X5FqobNZOLNxuO4^@6a)Zlf>~D#? zWYTI_0e;oA>R}}hgxJr@6}gfE{6%lzY-guXqKx-oJtD?HiSvjhTlE2Xe94##Scl-VMZa%`DPOF6u#hiMfQs_i>QW!`l zp}W3o7xfCDha2&NfURI<)U7qhsXn@K_1v@Jw|s#5=r-+&)32Cmi}0|quBC)DMaohs zd{UmMWEUIedKk#ELG|?6T-`)c@@VIS-$unOn?DEpy?w!9$vK0lx5o~>MNQu!Qulmt z#C7=>O0k*!=Luo>{S}mbCXPBQPklH2i}$g51JGxHF{ULLA$=Os5=BC&h+@UbLjrhH zwL?UrutWC0;@qD3#``CIXIdX@RT^G%waH8^_rGl(${mOAtv9;PAWpvgwKn0fMLI1} zw7lL$F(8sDATL7t{sSW=IuS!Wk8p=C`4M5=)OuI5LQUg`wnN*qn7gt(T%!(?Mna2h z5hBNLz=@EwSYDY!XC9gy+1wCm^>3@^2uh+p@1T1+vesbwdc8Up6~9EjIoTo;bK3H0 zh^B^dLl(b!WU$?zGIXSAG2!FZedC5%B1qht+{st=z62iCy0tvJ#Gm_;82B%~!%-br zqDFJ#xbca7WLQkL1XM%D0;-w}m4QG`t7J$4z+$81+C=R3mZL^0ji#h(nS!jMvM@NB z^(DzXK|f*c?tsf++#Nq)Ha2m)UPd%z8V<`O)rRgosW*+8!wJo8z=q$K63g3^g|41P zLCu6yVyD7;COyR|p3xK1i+0y^pxlrA+UjX6P^ki06llJqDmw1QkaS2))0RlW z<(>~iw`Oy2@9U}f9qhqI^jC5{v&}4yM1JV!5R-F_nL;07-2^ejhV@>+<}tWq&C zcy3Kf-MzOzks7au0&V`+LkLhf6TX-pkkIUiVn7$hepBW{hUf7+Hz=g!fkJM9ls|wC zrkCzdxSuRFB8d1{GA~cliQKI;vo?-x|5tuYM{0JSrJfjB!S2A)lF?l|izu190B&TV z1Dm=O`R_iFj=}wIH&;*LTzPU;(YyRfPm?= zXID~+pa7Dkblx*k2Me6&Ub1uTUXz{@6dBt}6#{(lH^Zj7 zV+>(#&f6}KBp2gd*6!xNDj2Vblqy}*$mldqM{+VEy>%q+N!{kHH6_k|q&$h2^~az5 zph>lDSn1!AbZin(H++L(*qi&^C&4fs+A;dlCf ztq`t67Y}ng!*_e5ZL33PNn;Nqt?O=E;#}- z_=ZM6C*lI0l-E2I+WV4|45*B`{5XyKBDMQnJb2cMa35jL6v{bDe7Rf-o_yYl1-`T& zynV~g{-<=x=@0_aG8F&NuCx30Z3H}$j)?%LWxPIj{jJ#s9mMv^iKs2f08g&47|ZHO z6nd?$b$Fe_RGGU-_tD%q87Ib)rnnl#g-jib`B&B?F~phQxCFiN>%jS5rZr~;^G@O= zzT3>mpc8x*l9rh*$5(BxL#c4*^5K7fUO|@42})YDlcTp-7^Kk{qjia_HZI@yS1?1S zd1u{1Lsn?MQhN^4)WS`Z!iW1~6$qt^t`^WXe=kmB1+AgOc63G#_zxu|nPMx^N|yd&kJXqfOe#5B>1uAW92 z*PrZ-RI%X2-PLhHNldohn$HwGZFq4%AjZjw|J0z}!sqs-;U^Gkt@$ox)TQ3m5pUOH z6njeN7^cO%{%a%f5PgN2V$tx{S}dE~xfIKWrq%G&Hdy*b5l}B1U5A%Ydr2yHKW0>j zzi;_>eRBHu?clI6*pn~ibiCCU)qaRtM}MwAhBRX4%DUPC7Ca@JHF{o@mYRRdl!yO7 z%hbjR%i@jW5sm@osujsY(Hapi33LV*kJAIX*m6PkJ#i~=d2rH)( zp5O*DEj-I9jgaN!(89gio-OetVIt+Qni6<8$C_PIMpNm32sNrO){RSes_uDOBh|>V z_nOV)MjsVH;_7tJ`SnOim<>k=6)OkU``Y)Uy+C<6c}_a*0VRGG9MA z2+w`dk5IeCW;G)S*n6pGl=df+3nkS1_HDqMX;J#CNA4FxE-^f@mOPvJfHKH&KWU$^ z8S4#Y;Brv$4>_&GC>3QXqzYQ5jz|+!0E$l?*>+ZSQ8hn zk~Px_qgH_qSZyA>dr=9vq!u$dCW(@WbduNaBd^k!!yEW; zR)U3&#nm7yuikaND(Psa_|JOq7T_`mO_D3+fk@(w3Z~Ov{lfmscxpx7{Qj_WIhv0l z&=qaEY^UtGh)`O{jNo+i!}raS_5QS=xtD%<7FuZM%7}z@X%f^M<8mG)_odTNXKO=b zpOUInZQ@k?^`A9Jvg4`TrWQg{f1GI;z9F~GOK;8npv^|Mh>T?`Jx;_qq>Q27MG)LU z2CCSK84=Ax>q7XQl}S}l;EW^cY!bX3L+}?MN}nL}<>&jL()LIt0>=va6DgK|CL%}R zGuoK_l;-%OD4F);(2OgSC1^fPz6g(I%uKTPhec;jDz2fUI!l|PUHQBgay9@u%cZxL zGz>0X6vIVRerp?yE=3sBIhNWxkP;J70+2JAc`P7^X$BvAEMmAu`j0w8iq$|V_hBii z6+tzHu4k$BAl30<)6-NGL-p^f=4&88IVDYMU6@9@AT2gx7to%g z(Du7}mdE(v&17G|VH+tXNIQNV!daR0SF3D>=1^3QlA>mV>{%jK`D7?-#b zEwQ~;kXhzFO*Oqxwgwe5q?xot{Y<2Cgwf~cjHaas6IK3PqT-ou z5lZ*82YV5U<-+e7=D1eA!;r=#6oBqhT_m|6?cSi}$K^6rIodlsoMW0~9DPU1=`d_6&IzRl<-v62zLbojTC8@2W zF~32w-B^v^(H{rLqbLb884ECPQX*nthpSVBiA-b)#+}bPnca^sJmsj~-ffN=MXrgD z!RpH=h)UHfmq1+hles5Lu0mHV5cu-%BscIwVMa4OPq*+7%8sS3dJ;3Fda=G^y}u>_ z10i^m(xE&-kk+**~pIsQO`0vqNY#V%aDr$wxXh<#lfx3`Nccu+q-VT z7zUKukA1!=-&>x<(|cfeAW}#hQ^V;C!a5yRiaLhmwP7Vz(l>`dYZ+8p4#ZnJgUi#A z>LpynPD1%FkQo3cO1uBj-ewfrTJ>sTmr%yYCYTmJmLmru+dw#|dniZaHv zQqQ*e9nVP&>6wymc*x5f)5JV(yAt7@X_ z8^NYq>eHk&Hf>G$L&F}GZ&N}yXci;#yK5g6k5-cYnnhQWEaUF%#`v0NZQK!?%v#oraLXrL>&gXvo}i#wkgn;vQGkA{h1>?!$A^(VZ-{)2 z!y-uED_(R299kF1`5Aq2q6fVzfm;CW8dJht!-(_Jn{lZ5vpON>f|;H{ zOBiP(q-=C=3X)W3Y2>Rh_1hPA1Rd34k1_@>tYAjMYIadr5?b2X$Ai&I&RcO;X`K_j za6Rk9a)o_hYWFwRh}_*Z^T1)6sFibr0CUkwfbuv(Dx!a6L3h@}uJ#hH0u088(;Hh+ zs89T{!V969a%s0!g;K8yq{RHIC)P@_3@W`#)X=z-9fC6+t;4-KK~IsQ_7?Ovbtg$@ zmYyFlwRk0(lmwl0_x&Ejito&FH&Uz%JT(ZyiSjW(kW`7bE1vrVl1*Gn)%{GWIV4Ft z)BGs=pvInh4MF6dF=$2e8?_N@>5VRndO}(}C(f@Dd=2VM>AWP8Kf#_h`0OHBILW0} zX`;Eiib4j4a$&SVL+#S>^(J1`^Oab)?POX3+&73}x>2t1$4P1LwrRg2@LlU`rQ>|) z7OlGhY>Vk_8QtF42z7mw1ZCKz3~hc~x|&l_sScHd<(t72taRM-kEk~?z4Z1^Q7 zTqE0MAtE^g7UzZq3-+&zmb~1^VScCCt+j}9&PbSprG#o!qFALWOo#P_g!Jruq*d6m zy>c0@GFDYTQOw89L0L^U;{MZv<|R{ME%ID|>t7zD)aHVRQ8JR;qor`%<=P;jNNAUQ z%n2u@rq=OpzMX>xgpgFihWb}DO4%X?n+s7*_(0{W&y>Z{?>h?<3MXQz zQ(vNBw2DFkiH%!Xogr+0Ye5@HR^p`T!gCdmPU99})v*nR&*Jf$jgv9ND|#_o%JQi} zE|70RJ_J$!PUNE6`3qI_O^LX=%O0T~2&QEVKNrgn^3X?!uU&R&uhz_<#Ox(O(&rUQn&!aBS^_zAcFTj+$1Z0}_tq-MCKTqSqFXAjHDu0PiMx3ca zA!(QFw1E5DLj0PpU4d?a1VjwyzL(5$K2n=MX#C}U>q8}$G@J-GS z!dPa>2|hv!n}J4!;CM$WuR#B@Fyk#kie|`?jI=7RwKG&Np zm6JXm<-2Aj^0&O^g3|D5Rxzj6A0y+&bENd^IS^&*!RHTi!c2q+MvS4uT?;G+vjWVU z^k%+?;nXz50RjJ)r{eM7;)4;fWGB+}*#;wM>pJ)@ zEpSZKTx!EA`@U6EB~19gDs?1Aq$9>vcu<%|Q(j5aV#Ys}b3Dhjz`opGzr z{ma4lxOSn2kHI|ryOCHm?$|=iDX5AViE5A;R^by+VGP6mG-jC$A&4m<0lTc>nz>1` z^Y*6oW0vRP%pxOZgE#&JD1+qeL|*!s5I1@b2MoSTgT-OE<1nsS$3yD&WRWAn>m+>{ zDik%9`V)t)Arn##mEm`!Lz*n!a$)ky)y1y3c<0YLs&M~N( zOPeO%q7Bq8{btx-M~4}MGn>&ct1Hn|tQiT5Gb!aj{eG-m@txac#v#Ew&VjTi0?|m9 zzOe5bV-H4kI-;))UK1Cb2sXhR>`fBkERHIv)(X+05*c54R9Y8IEuhMJHgT#}XOMyY zidMK~6-4VxZJbh_L~zp|Zb7-uZPNn@{8*A6ijo86M$62DF)}&o979>43VMw=0WcaM<|2#cYmcHt zLY&tV-OHr=lLH2V61l5$;`Pi16=yFPBqu7E4=DBHBbmX80SBq1baWAA1no0LZfmH9 zjG(wdKEXRj<4VUXICzU_R;I@86e{^Wari5(a7nq^?UUcP$$mMpVVi$syKBuMRB7R} zbhVp804j3RS{`$AZNYa^e^b-y#&# zhm%Jb&2YiW0m)D+KjjkYaSt3sX+tFY3b@S1N^+~9WoxvAwZ9kio3tdNNMMA<^jU+K z<n|7J*X z$9L^GnjNElb?$TfQJ>=v()?vHke|6SLXDKDfH$742EjmAP}jX4-W zF*2;>r`W^L&&hnsrq$BL1Mc+$8_S3|aea5q4B3%DxFu%}m^b76IhaPlX`>?MxzIap zhME=mDWJOuv!+6=1%hufV`oa!lM$l*HnoxcLFT;kpQLgTSk3%bQ{hn(=GCyA-EI z*;muV-kxeSvdHW~lLA8z)`(QrU;i&?J(t3W_XCR56UxzlriQC}I)wSmlxp{uSQl38 zezz@<(V$vWAe;^uh^lx4S*8Z9I%_vT+tDb^K@t?B&{C-(ny9yEzAi3Mwm(saJ*Ve% zQghiDpfL)U``xnPJE~{-o+IZ+z}hNFOn)}3Mq#^emPh>t!RSnBWE-=2BB;HT?$;$- zDEZFNg)L%#JnW{Wf`{{7$1Wthp(tnfkSw^G3bZ6O4n_7m*a@gV>KFo$8t0t5Os7`pV_1~B*~B~^^$Qy z7Dc}f4Wc#~$_qg+@RNz#ZqH`a)JLL9h`A`Rua!RHfayg=RV_40*`>@rl!F^XRVh+v zGHM~?@*vP)p3fP;MG6Wn6sAhrKx>qm`Gp~JT`a;4u}%#rTNq7;gZ>O>*##K={VJ(7x?R5#l0cJ!{O4?JB=`y)=8EbS;({nj* zI7Hn*1_o!{T>AEe6{mJfn$2O0RJt`AFhK*wsfExR<(Ej>@9Hv+>0?5yciH}&sIV?C z4=koadsdDjg8@Fm8*D*Chn&)Hv})7P8X*ojKzWtLV7-EO^fE(Ys#{_crN^*hel$pb z)6O_(<)XnHX(mn?=;O3NKyBCP4BT+cTC<5b8yn5OpKAr8$QQd9rfU|8$=s~iouLbD z7L6Md*DP9;5GbVq(dNBT&7GwzhuV=>&L?gO;U`ruY!*T$a_d}!Xiv2qO`vp+PnJv0 zs0}+4QcSi zhO3l;tXJFOawMX7VHOI0t}j9?CQrJtT*zlO92Lvqf^$olIPIXa(vA_*ag?xs>k!eD z?}t7*9E$}Z$fXRC@cji}FLVM8XNq;PXN@An5@U@EiOhD%jmH=OO=(ow#g(MrlS_Uo z`il{%;Bu1YvlxTZpyF8Udv<=-Qpb#369StzWHab@M{&N<6K$B{Y!miN;oK2bD{Plb zZrKSSIoCi$b~&SZSi4=ZDpJ6ImvDUuvv^a1=#i!Cu?c*B3~EJerK}dDiRegGM&)Yb zUTcEzK#7JWlc^Pgvgw;DRx5<{k;QiBF{hOJd{r9pB1#0EQb8%E?NT>@nNnd?(jBx) z_EL_wkP?P611l-WQN1=wk`~PN$4OM^w}9Ofs>FQM!<2<$S__`m!$stO`#jnK4w}q1 z$|+Npn{h>yRTw<#ajA zIlv7Vj^t))!|WhV+I%#x8=|>TEV$VpbjNuvQ73*z+ar<7S0`#+68uV)8R}{(JC#aK zqq$-)UoFb{4lQY8G2wK7Ag6P{_6wDE(QoFMGO3TdwLzV$wyH6v#FRe{fxp$1$|UfY z3k4}*bI@?i#{%WdCB3uk$y%|mObmQfou(u*w*%Ul;Sk;FQVwRaafgPMjdr`OXyaC1 z9o6Y}u3^Pd3^5kAaP!@TQx{_;Fj=V<2%dl-Ny}0gxrLf^Q11kPi;7qSK8ys$amKbn z&}yxrw}5DYoE}0EWt`SweL+P~4y>11uZx9TSh%< zCgZ7GVM8))G?z#OblZ~T8$|5%E2XOC`GZ1n!YUQIh(T4jhG2QW5Yf4@Ugz7fMdpzy zCG-^F#UayyA(lyhVpeS!9?xq|zkEbmm94-Q7C>JIrqsz*M|{#m^?bs^wYgdoP0a3F zh&gxq-h#m!)^Y@_(N42EoH(_9pdxTRHAmfHfhoyjtlxJ0!Can5?kr@M zX|F-_+<}q>mzy=GT_qb;*<-3P2N7`z2?r#)mtRG8sVO`CRU+?jXEIj$=#+w-|NZ3itNHDMx5>>0{Yxn_jA{fMG3sbpv&5>k_Yq98Z@1yQUq^Nt@z;&_xFI6WEfXR^mC7n*8w z!c3Ba4LSX3*y$5ES63WelQg6y_vLmkt_pEHFsJq4|L5pDw%i1QDEdJxNFpsk^>sKB2bq}=5pN=Hc`g(GeA%O`k&>zDv8>%@reGS8dZT5?LQ<+N8V=dO` z!C3!W{m@q{FC;?u-}#(0mIn6_hiO@W5(u8Z6aWQbM+rQG_QmT_Lg0TiEzyi@alBiL zNfNyRMh`_}^>+6=a?!l)c)*K)htiF4w1pb?U!M5%PgX-UDtPKbJ%qarCPt=ML-rg1dCduzneW1%IOd(Ua}cF-F04Z)$jO^QB`i{`h$SnbufWT0 z;135IX?IS?F+k9DyX~SYZU}+{;(wOYnKX-&%4D1QQ~-*&BE`PYg?>wa>RU+?UuBtP zW2GOab4i;{`M#g%v-qf$sL31|0f)UY{3}~VK}L-Ioz1LS=7J7s@i;Jnt4G65bzz}p z4rS--HSd;tui{vD3l>cyXiRii6NnOotImxYS(Dg2GENW>O-X=u2no?tP?NsC_hfl> z-l}RZITy@b%BSqS<{JHfB>FI0vD&pRlMk^!^Mu$(m1H`fp5-}~zLKd!n+)23hZ<>* z*FiM*ha))DOkLjj-frFB4H#$|X2NZ}{wKESP$k>noXvQOX!->|wf-xWF&q5Cp#KtD z`)&0im|}l*>e~>&)DWQ$f?UTNro~k6&o8c&%!D2oZTc9svC86q2-7^G2e+uw(!UyK z)os2TB!C`8PCY8nE!j_CkdDf##-dYm>WMuT1cubkC{qkZM93!1;^U%_lg?gw1DkOK z%JQQ{1z`CAsPCZTbqm?5zfP7)s*02lW$Qf_jd2AoG*K4GDUKA z+x>mF(7$^8Y`XcoaZ#1uTtv>&5V^W>Ha7mq{+9qVd3;!3!9O5Yv=M92NV2|j=_f1w z5lFKn3Bj?Sh$(WitDIr$09D0iN6m4E%@gBA%uENtJ@+NYjb}v{>pbaI)x3*T-4GlRqyA%n zKxOT9W~gs}k2=-)Iq5+>9r&&cZXBzV*++;vlm_duYwKFz6KCK2i z`Xhhe08Br|6`hqf^ycoEw=lzhmmAQ8Cx+1K-P4noSl&XfMBbT#FAE8oDK=M@<8CUq zj@dGp&MjkdYjRGe?)i(p{ueB^>qdfB`jF)tMj+`1T782t$tW8clKGYOA1-{i?b*~1 zXidm};o@O_%UcOhph+C`p?_0;Qc$sWRwUKpJ`}>=jRiE({oRAXH#!JNMVV)jFF=Lg z-wd4*JLkH;F_GrXedC-()a36v87=$3u|j*tUt{+2b?-YS_}zoh6%R=su~cG}J2FQC z1qze@!B` zLs$(IUZ7NEQzWrp6ol zHrtApo@JvVJF~#1ufQ)Jp?3rElfvqM@}K~1m}|hj`%^QtU3AgjmB&6y(|O zZmB|@y1_q@b$E_cme?x&?w6e+1^s$`z31kvgwc$lLzX2u9{r-tfG{zHFB6GsM`8&=iW z*N#&-N$9^gV?Vec)4h!ig2XGk-#tN)yXV zQY1N<-OW$IzLaha&!ZBU4ZH?WvGb`K4}8G#+5BAJYFa_1wXQ*6#`%`>dJVK_Ttpm5 z`X?vbkyH7)PD@^hiA8K`0p98p`erkial1vb=g13nVXkPTqCkWPaRTIj-Q>TyU&;NI zDnVnC4gwOl>o5cB+r!jiiVg2C0OYGoppJd&TN_BL_WfpvVY?hrphT2LlROYed4XxB z11yVS@J~uWk?H&h{p2FKnxb#~Y)itzn~?CimT3r%pq+~nfLAp9lHT=h`tbR2Zz;M~ z6YJmQ+98`nL$Bvx;qXs?U~v0-!+#+YCTFlpsU=h^glut~R6i@`pi=gQg$W_ym!CQ; zipG>$J4Xi0e2v7d>ez=b6#JU0Gp0Ajd~;2>@P6d)K6;MvqtI#PRI)nA;U>z~ZuQVJ zYH2e-XQ}E32q*lp;0oIshOalvq%>P~Z6v`Cuj3|1k-paG9uQl9k%CdAhFj4_Www0_ z4cyhS+)GR>`_;FEge!$kV1Vt)KMH&b5GYuk1m(_0bbw9uh--Ip?9 zwKe=P635RqUDmVY>fA4VG`nA^4eyII8t_E>A78&*i;Cz&8@vfm7Uexs%s%VMp!yC6ZT@=jW7PCYXo#;~U0vFp1PItmY9W=W zgFb~L07oowx{14F8k@G39NGHKmDrvpGQM~#L*#gFG`W$6#DxQ{yU{#8WQCs#f{XJ>b7Eg_Q(9T|>!0OLk zXGe)^2oG_9ce%hS@@t#eVli3@m-?0HxAAxdGYI|QL|`_5@G9qg(8g^|fIw+NcBn*p z;GCI%Sc*EaB)lz}GhPWqyw=R<%`w-w4(^CY9z!`@MtkbbbVQx>vr6l%Qw6NQw0 zTzQS^A!d^t)G$id?%Y9N_KQgg-#!n;UufWJ{?;-*;>WW$P8LT+`Gixy!#92AhSd07 zgn(?{@PUcGcWwvC4>q&-NTp&{9A%?xJTB~iJX55z5BoR|CWk@dUtPh6a-b{Q+j?u! z;pM~#=N1#TKYzYQq@4}a$o=YX_45^qH;Yzjf`s_$eX#lWqH3W`+4z)EO{JP&)e3b>#A z*YY+;4LRU172ien2pyh3KacED*XFxehEs&N2ka_$v=Q46__<0Ig}`~O@OU=S0PL@i z0SjkVN25ScpS1`5uAXYY`D)pfXc!P8aHt&X!%Dq980yvf?~fNs^RKFMu+s>CyTm)= zSsX2TIi6t?*2>h)&$F1=tgYP&p80h+->VP3@5CmO2o?yVvThZCrtx;z^4;IfRnB%L z!LQGB1q>QEq1ddl4PP>_GXl`oI(*?|*VN=tf`cU-kkw|m1D*WVwNFSx_1$WOKv zMXq0er-{!oTY>|U$*RqM(;u>b?5K8kfQyPD($+wu{P7F8lhiHk@wyLqhq6u{$O4;S zENIFHB-V8sg75kzJP>N5b|8#rqP&>Od~75O2T;yn>1p1{dI#O--}-JrtEb7BWePe( ztguG_JoM=9`S1ineY#Q^9`GT})pXo<3{~a$u*dX670EnU!8*;fG$`w~!QJkI=+EeMmWm)@OkV}p0v+1q;+d|7Vb}DIqyVoFnF<4F9 z9+AnSZ6D5Ueay%ZZhw}58&c+t&&+!EmsZUXpMD?i?#{J)N4+VehLak>d4G|+SKEHZ zaj*GkxtBR@Bv$)EuoMt~7#=NuTTTUqvAdy#?Mi&bYZVX)i5SVeo*)ADDYu zIiI@Bimlpsbuf3WN6>1*r^n}kWPzwkIYr5b#e(+(c|V-&E;1sm52_axD$x4dyzvL@ zhltxY6;z*gePSWt$>P!QzS@n|1$^bMz~o-k_qYSl{+j)ACm}_DS>V^x&5QJHt>Eu# z0L`~#iz_3&i_6aUU^%$IWobz`AO`Wq7DO}f(&`pBNuUvyZ9$#07U&KYoqA{*1K;4; zO|gR@{7Y^oR#@PtS43$2S>cC?9mUAHgl-0R>Zzrxr7nnvclEm9+!iGU;1+Xll_ZXg z)Y6JL^W`hwyWS6f(Yz!D9ZN6A4&8inn?eWmz}4Qiqn%O@agFcxm`O~%C!%jtl9cYl z%ku5Kdp{wNeyTz2UdBydxSK?kAf|*V?E8al{8+ zWl&2AWqJ3oa@ae9+V^ap`Nvyo;HF%Va(^41y-ng-cs!^*W0>8cF5T*g>Cs@V*;7xU zK+vfD#NS1K7chj|_I)Q;V07paG|TKYZ|f03FQtKhYLj7#g6#O6>QcF1ZI3bun-Xh5 zR`K);w&>l6Qchu%?#+vipJjs0oj+ffLyXZ5A}FK{z|LhENq)rF@k4>Xwj>@=#)=EcAQ zbz+2Xzf-n(yngKL(#I`quf61GIrl&IqyWd=vX)%x3CuKQef7*F){H$?MiI3i+kfX9 zHb|g3`quqg96^~#E+}V?P%`fwa3}jb+Ki@z!6F4F75h7Frv;O7%YR&t2N>E>$O+%L zst*8v^y`V8Y(UmNb6r$q>OT1`vA4BAe#|{y=QUnc$u|n4uwEib%Hn^(#}9s&ujVF7 zdHP$dAK1x4KyN^^*9qLJ2m+vf+nOYdq{^Y5E^r72^BhT5Bt@)px8t`fc`e}}sUM*a z`2Z(AMafv}_B=Qs{2`;S)L-zp*55Vm<-3)Ce=aVGM=^CS(Qb;Mvtgu_-(RL_36$_m z1}wX@0M3U1tO!rXS@tApN7I|FPw!VdqD1<<#zMp=(^tmbzTZndeXjlC@Za-ZW2bb7 z@5`ub`n+#%J(YZL_TIhoTl#50e;y-hzpgqd0;FFt-DL2Y^JN32XORX!`_*sIyGwh2 zn?S&k7N^;qzsoA+l589QA={paZ+@C?8-35t>BzVTZZpB#BM(9Q7oB0vLxBGC0@78Ilf z%0`3DC#K2Xv})+u8Nb`hfFXsc6HldPLcu@}nqMYHI-xH22Bv4quQHlfqC1-KX;+guiF;u;8_B<5#Fff2_1k=093sT+yU_*EE4;Q)kl} zZ&b$mb>X7>S6nEwg98IS^U-B1F=D#oa=n!B&g7{M{dvwXUS8H`XB`!MMU09Aszv&| z+hT_E-T}P!bP^XV3Idgg5gM8chu{C*gBJ8=Ls8Lj9a}0R?)zEyy=5O`Astv3nM~Y-s*s8A;TFE!dVJmRw>Y47VlwAPf-W>zA z(VLIyhetXzlIa`0W$9n4_{YF*s#sD%=d{ z$9#TDc%O~KCwrlP@_?a#3+fG;r4QC&I3&^Ozja2U;=ur5`8${T* z!rhx+j;Bw+E^8igKfjRHe&h;#Pk#wEYsUEaur_bTzjV1Ft|}oEXn^RZ?=c8&1)ugw zXjwn5(+Z*G)%yG4rgw>x5ySqsd<2RTv!#L@JF33jfHI%mvXsU@Z&ZpRl>TLuJCoCj zw$y%>g}MZPV+tC68=OTssyRxS!5XfJuFJ;RnzL?Y&xp{-Oe9fv2>RkA*6WkxSWgyY z0?w6BuJR&>dzuL4#6l7AS^0dac$ec8w);bM4dYQ}?V~%6;Ooh5$W`4Y(yx;@{ks^? ztX5fD)srk--x5Jow|sZ-qK-m->ZOCUBCl9Mrv_|)eC1X8RnPG~8!uIVw^~fsHpo#+ zrgzAbGI1-t8l;RT^oiCpV7=(O;eH77m26&nrn%;rFmGXg-d$JS{ z@&p$it>M9=0j&LjKM(PjTQ~KNQ!SZ#Hb>rx?NuRGqSerEaT-rLCXcwbXD2rdik4Re zYP;@#oHcB5*`J}!f%ja0)>zkvj98(rg6^nY-SX7Kz=6r)WF9~=o<~C!5K#7*=P7xR z7a9LR5po;^jB$7oE#IRd__u=?K-~1z-D;#MZagb&ity1fsWLQUoQ%jMDm}wT4?SnO z%{X!PswL#y3Zt&n7U^rk$@a+M3=5k_mxkDXI_7ihkCDzq_Wl|fN`Bgkr zt@a^d-X1vrp3U)a^y?F=|1x{(*_g8*U2n9xNmsPN&S4}6_7GB?qis>0)mF;G?cec# zLSG+*<7px&O2X37I-@dg_=t{wv~T=S`&uX(_@SgS;+=ZYkCaZ?WT>)X+uIP? z`KPT2Le>LRc=23?@Xz%fD~_zxk`aTptKI!P_3nhJZ!huTQ->1(dvP2fh-JjH1VPHY z=tSXUuu=pe-V%haJ5CX#3ufUWKj<^p>L@F{`hc^(rae9A#oTOK2jFua9-pdJ%_ zBUC%gBZCbbtMCH&pj4~JHgO;DM@CnB2#$zxQsDvh@Cdl(heLkhPL?;$HTLi)jw9M( zP?RoXM01Re^RV~JzUGdMUiu+Px?7sX!}S-927iZPJPp^FG>sv7HWY*iY5=S6-?J)O z6PiW(dsh4=4fv2g8=pTcgS`=dwIL5fe7Roc-7?POG;-_VrWF$(G9;|HlS8D@s;Pb ziDybEo{UHYHXNh+1hJ^oXcWDo!w`oD%Yqbl`O8Kk+k>@QvR4VZP!_{~>{3ppvQFSw z&TCDl19&M#GFU@*=ee8soWYFZ0Wm6HuQ069xbkmTVKG!RI1)5lxlYERsTc~d2agVLC8C1>2vBT(PhETK?#mRGcgix^Gp8`w!~Fa?(Lvn- z0R$X#fM{?Mq_Q86I5CHR5#ns-WMzm*Ou`^3%|W)`!;aFO&8>vyibICuO`28v!#*$v zyz60h9RDJqr*uh?*ZZri+2vpWVx+f*#!Xr)zK2la7ruHRWpLm^1xru0cvJVG z$n?TP7MjMBH3r;VNeZ~1O!+E77@FJ%I=pzO7Z1?f8gPVb`@XY(mB!*$>@%DQmi=yj3)mgF6)GaTuNcs+V*A)q`|@;2=)zC$i+Qu;9`XIhuj5!4HC!L%Z8}&^0=J>P+t6MV0eNge7MWdu=R|h z?b0BAF&*ZA&nEVA)TX>i*LB-yGCM0Mv)oFisNV#N=1?0+?@pn>FHAfCY_|NXnfa=xy5WNM^aBdz%z|n3Dfs)Sr z2Z61&5}5B7SWoS9tw^f_B$7u}MLE@nK8>*-;T~6iuwcm@7g_NzZHfYL7e-4;)zNx; z%L)hK+sb@ChO+%V6MxBf9vvnHc#8K;b~rx&W@nii9|bEOYDAB)6QCBjU~?hN82>I} z71AIlq%^A2!pnUJ`$FauQN%NNBl`u^TH>p}%-Dv^drS#mf<6GrG*o5Vqm=?x>^`Qb z#;+=W`fa3MZmV8~yC+Nm%M5%U9ct+U88{n-?xz?puQn+-a~-UYA!huj5YRN99yv8g ztd}t>D(`q`Z4=F4GYUM%&VOyO`YAm1usfiK#NSxBFE4#-pnggQdw3pf}fy& zD;wG^EEtrdi$0J@V)YxrdW;Un@<;-%P=P2?gXR0C3o44jWt3D`A(llMtT*v)9 zr?~)lr=EJq!5F)$#u%PW+$h+8Xdp=0><~5NI_DS-7OGGyPX>(3H(uGUox|VS+4UYk z6!0?n^KwT3hlxjCXSE2~lvZl>&eS^l&1II;^sjwqbz){)`|Uh&x!-`tD_B7mo(s9n zSt^xRI%<-X%9k9#VFb>eEW?N*kntB=0b|PqDAxI=ag!CxN=<%KnA^*L0B#79G!Jnq zdzKgYGlSNi2(VZy$FzaHdQWX$642avax(G8+#4FurV)X)m*=Pc4sD`O zqnVHvuV;-X8wShNrxxCSq;J5W%*%M+tD2y99ko}e{B-gkNl2Mwk=z^k6zq9k4)CGD z5HxnMX*zIN32XeD>5C`RhTiNyrR0B3N}(XYf++=j>5MD@Gy^au?NE3qz%&O}40WL9 z@hibW4rnc5Kz_0PyF{}31((Qc5uEU|Z`*pj#Q!=&k837Z5{X@Zr&1xR+J-}g3=qZj z=ep7!Rqex+X6Q3og#?6F30nP6ZK*;FNhI>16pH3WY@*MQZSdM4UgMPT11-Sh9|&IL zB#u$&V-g_ZP+A00p3fw7)r3#Y)2+}+{Wo?DB{#L<%c7(t8BDl5?@*D9@#AwgLcFp* z^pkI#a(;MU-2b$HTOV@un=!y%TeWM5(UNQk%Ly%0W}Cg3CTD~WBYR5IOu8VzO1ZH; zI&p9*02HkS$_NK-LsdSrBLv<*#DY7pHgbF%;g?Ge{)j{u&n4?UgyD!{t zPD-M%RV?EUrKg6d$r#sf?2Ily@cyzglH8UWJMQ-1y=z)Ud6ncT$|-sm2Bh+486%k^ zvdMva2~cWtV7H$TXi;3X`=hI+T(83OY%tB_>1-3$e-eIQU#0nE_oXcHH)6wq`R(++ z$>gpzn3)}a6Jl)yq$?O#ZUIj5;-X{T7Z%!2FM)cEoU*T_{?U@FrAI3V+Y@4pNM`RG zDh!PS;ct!J>7{d4IA6Rw<3E+LM7b;(PZcDr*{05g}I(`MjZw`(FD&b z0GFDlI`qZuotUVG=!&%X#as^~l)o;8$RYvxV*MJitr@x zG=V68SeS6WjdniqW_>$(=IbNV(2rgmd!A1RxP%w}5pU(n?vFaO?%2A#zyI{te(Y=?S;pH7eY%uLP3YjL7?!_lQgxO{ywmWnZV z-rp9i_@kzb4s(?-@fGbUN0Ij_bo?ZqG?#A8SE5iiH5aci(?f>+ILibpbutv&=o2Y_ z4`T~ZysTtNxr|E|#M9BXBEsg*&*}yw3g&=|DY#>K52-zK9K-55_@yun+FW3C-FKB> z+z1jV9@Eq;eM;H2@$SHp9i;JPz(im5%XPoW*V>`Yof-?I!)fGW;VlLiSCqG`IkzYS zF&qiAe}Sy)%C-B=1+yn{%^o9Ixfn5jGC7dWxf-?Gwmn(>T>B^dXXKx&AhuOxkTPg!ZaP^e$d#~YHrw9wD{V6RW!fE9Ohe^G-GFpLs^=uj++ zPsaY@&R^^cVvNcY%EhZ=R?JYxcW%V;gV6l4YTYV(tQ3LnG8{A3`u3X-QO8`xEYU7I z?)~dUmG3%t@)R|P4FHAJcUmOk*B!e*FG6fztjcocBW2>hSUr!ht$4qXYvLryF;npb zn=eNWDJk)a{MG77E63~P&6#O`a_iOq=}gI!drPtJhu^JkVb3JL!VhKcJql|RAOhxl zCGnK({U@?3b-|s>7Ay%8!Wb5!L+|Mo#*l2(X>FTTzijd`h|?tLDkuzM$*-C(ptve# zx?e&?M2{EkJ>pp;irj%=*R|Fi+_?7~BdDXm##T6*rRt_zt^pQ`B)+qg$@sb ztBli0zSF-#7J3Vu$k4W{dWBImuepn>IP#gZ)kJTmwhhnsu*xFZUJ^Y!RzP&xnn8bL zi;A-*tK9CRYTKf8`YS0GDu^a(bs0I4%WL7zlHf8Vpjh>ny($)R``*2U!Qkf#5p6ma zghoo26@sW0i+$Z8<+3kBd+sGY40jG^ zH6nZQc7Ds?k~OMdBU$F=&2OzUOOl}B>FIvmro6EuaORwkDTP>NPpUaUBZz{N`s)Q8 zXMr*Ob$Ozr-d_*pgA;KN*0cuVe!oQC1XF7XX`)7%#_MlMNox&%*s<=*fP?2(`m}fd z#FoF9{U+8sKQizRf725Z*WVAO2J$&It^1aLFS(KTM0zu{)swexjZI)V1QxM+v zxb+gVjSZu)?Jx6}ZE6suuQHvceP%!X^U3AEFRemc|2SzpM31746u5`L2c4g$cfX%C zZBYLm0U>pspYxS}j9RnXV7c?@O1m6Azz+a1$5ckHiyO^<#Af>?BRN0iW@~Xm0BZ5usTfw0 z8Mot?4qYB|BTI^9Jx!ud+%BqT`6(-~Gx31fZN&kWTX$m;j7YHa76aaARwtd)kK$`a zi+3=oTmQZP>dtfL8gz!I(B|BlH55Feo~GL=_T~hU~ zJ{fy|y$3+kmly-Adr=9*-;_k(xlJ|bpay>XF*R#fL*wLD2g0$yy6z{`_gB+$1kKl~ zR>5g2wuKe{Zo!MPlosVeC3vg<`{GRKhGMmUkfsrO&HV9UryVZ)mO_5eWepw(lv((g zV@MA9Qy>vv;O~vmjs?uFv)v4@G!LfaIhFr^@zlau-$gCrNd}`$8nkOfH(y;lpdTQ~ zj~ZJ5Lr9;!7T<#L?b!qV*&z1O1-w~Nh}yzYWdm@u9SX)|GD8U%XIh&)RL2#+Wi{)e z95<=+W3V4}0u>jVaZ^=y^*ype+NR5l2C=di`3M3^xIEn$s zZ<3q!U_oAf#XLoZmC1{=Lj2mV3k-918H@KyWO(K>%U*(e-ze3dTxs2#R6-+PAed1q zRX!0DXx^hF==D_+j6Z^e8UU*KL_9%%#oIOE(de|OHOOxgD2n>krmnAFb5S1erkn2X zor!sIe!yL-i-jBcil?;K6TIr>_|xvVNgTGU>1^Z3jrNDi4xbPE_6jdKq=4@ka>D-> zUh-d@es?$GX=)~-dZNw3RjkQz$&&(?%B|rpB?LBj{Qbzfa_D{QB#~IDKij8&{_GsOO5Z!b!qcz*>OYr)-u@H0b2I)_MR5G zAn;|4&zJ3rVjmw8vu7%us#{Qzt+CI<-5{_cthkt}!Xg=c78L@%Mz|brB)G1M%%)&~ zX0tN8bfqf|>M73{40Sq03IHj87|-+KxrTucFLRo)rNZ#a_9Bezl$XFoXZp|Lr5wXM z`?=1-369UFusn+zKt<;83&Y_pN!+YLWc@{%-CE^CRZ89Atc!RskV0It0QJGNmgIPy zR^wjZQN=l*8}P1;Kz|rke{vx+G!a6B^htG|*QLL?wRpXW=G0Y_WINw~FE!ixt4?|k z&#C9Y^=}~EaljW8+yusug%6AptZ&aX$i+4Njke;iuo zS~sC~;eBVKS#S}gf-a7CM`P*?XGs@fs0rN*q534tp8i6`j5Cm+zJMnTF7-1-JjBcp4Pu@BVSDZQ)2YmIraTNu-ul@z z6#Yqm3cN~mX4&d(P5im8W&cT0tyVJRb)6>}u{fdM77L_Q+CUM)mHr?YIz=U$D?4RJ z3&#*+4IfOCOWOU7Pr=8*Ca-W^QoaD)%Yu5h##ch8IKKK8JiCs6`lf^OyNs~I@vmS@ zw+1y8%9=rMUz^Zi-`AT33K-T|=EyTMQc3-uD}^Ord=E-q=YSm3*#fi>Buaetn!Gt8 zcv@;ebXA8Bz&FEHG5kz*#1@csLgF=NRNL>gBCStDKUU$1;j^mE;?crI*|@NLs%{%$yEb|0wwmn&(O?t^gq zusXq=nXG4uwF--3vK%xQHe~O9%b8Yp)z8o04X}ikBu{65?gZ(q56%#0sDo7$J_4(-K<#p~nF{zPzfJ=1s|4 z5Skex`P0c1{vEQp{(E<9Zsn9cD*%{3{ueKAc@sdcU@BNJRs9~L z6vl-^hs1zqCl>kcFaY^|h)t)&z5hbe?D@-)=~bWLF%5nfFJHt4I=+us;X_nik}LVy zXM zTmwjA6G{Ln{I*xssqIEY$bMzWlAG85YqKU&lVd~1nx%UT6o+VFdJN?k*ouMh_}AT3 zmFzl;Wa)i9#ns=7TA(cEDJwH2W-!=*F@uq6!8{LUhNthiS*fh9-n7(BMftr}KnHx9cba-V+2~0Mh8)(qCHu diff --git a/data/projects/shorties/DirtyLove.mmpz b/data/projects/shorties/DirtyLove.mmpz index 177c05ce5162094d4fa92b51ee6cd1be0755baee..37b766f4d29fd4c1a110ff642c0b3b5014c0de79 100644 GIT binary patch literal 8595 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 z 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 Z#p(RV*qTLUG8W(K_SY*y(nBWnzW~JK7#IKm literal 8660 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 diff --git a/data/projects/shorties/Root84-TrancyLoop.mmpz b/data/projects/shorties/Root84-TrancyLoop.mmpz index 22bd5f2fad7d02e5c3d1ab85514b923889a26b5d..f5eb032acbe4c5b31c57709bb4983443300ea619 100644 GIT binary patch literal 4386 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 C1&MP2 diff --git a/data/projects/shorties/Skiessi-222.mmpz b/data/projects/shorties/Skiessi-222.mmpz index a5683364580a8351c71e27bfb67090dab78786e7..a7076949cf62b5e3a8869ce74a888a735da7f76f 100644 GIT binary patch literal 4747 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 Z2(@aNJ;ZHVHw?EHHuL3={{pKE-FCwDViW)X literal 4825 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 diff --git a/data/projects/shorties/Surrender-Main.mmpz b/data/projects/shorties/Surrender-Main.mmpz index a94df2497ef56987ee7b0a6e93a42bc43edc1a32..5565811708f410837b3146c81294bb11fc6b7dcc 100644 GIT binary patch delta 2325 zcmV+w3F`Lf7^WD1003i)c%1EB-EQN!6~3RR(7G3UQ(L4Y%TC70Vs>VMT{P1!X3{N+ z#UfBF*;b>!k?8!i=%W>Vq`pGWAt_lhnZ&d`ne4Cpk{eRuk5^3A)yygmQ>&p%j6npXXy%>NNh8RWNrlNP%xqLW^gXY*dS25VYI z0n5vi-r4*2ADd*Wj02@kUs={<5e1>e;xww_`9&CIc^YRz)azN7MGA#Iw`Zk6#iFu^ z*>rKzBX#;ZPvAa*bX`NXOrx@vw|NVwA6~#ED}(9PG|yNNXHjWg$5nipfC;o`-NYe- zs>j6XS&Jxto-Y{G;0}8oBX3W7kaNd%OM}}ujsF>iLZ3KWr9^9Wd$I;Bv}Ce)7Nn4M z2DbOCDo^sZ6v)L{#Y(UwW1@zY-Q42wxM$67F$WYcf{fp}3@Y#*gKCGPp0%u^j?1th zUX?*M=LWA@>$E2|V`W?~sBtdU>igUxd_pKR!I5Z(AJ)f&cPkJtN8qb95KKOK+ z2W1rzt}5o2aOv>EjwlRvwC;AKXp@hH9bQ)CiTlnJju~9tO!g7uCpw(@wa&l8P0JFk(w-SCgdOBuLUDpLbb_I?dt) z_3dVV(De)lGr)U9>AvLwo`gH%+P6k(JI_th8mtwo-Mvi|BWn}VA}yKRfmAQ((SpjuY= zkV=`PX`)&b@}da4(*>28tr-BaZee!d0wvcD2$c?_sjOBuW-Dfb3tp3n0%{A}DCD`< zQIbz%cE^+c0(TWx5!f;u${5baM=e&za4zn6YnDAIlZpedDI^w6Yza&ne!=b_m&fRa z?}Mt^hHl_03cjJG;Pc=c`gE*g9EhH9AP{PJAO|AOt}m+KW|NBrH-EER zN(O|itR47nZ7|*5V1m!5G?==h4Y><{L`P3fUkp5V4?X;Z3Hb@q6w?ML<4#|$`GeEN4BY(W(ZMM|hd(QCE<`A#zw)fQ9Shg?M(Rhia_OP$! z@TE-)&yKb_%u8LehG%`chG)Ge-(REZ{u&+l4O$%&;_e--4hTd}b5P^Bdp!h-U`&D< ztogI;vUx{G+aNfySBSm>a9kbHAiv)Ke%tTKU^Cq6>=Cz)hMT}ve+{;=O zJiyi`gRMN!KM`d0R94bNe5m3eGbFY%l=CzvZH=JZ7r}yE6I5pWvxD~47&`#=y#RKq z0`@<}(ZC)7UuOjl6Wo?Zte|d0({2JIjA{)Iws4+;h z0zreUpL8Zk`OS~_4fn#TD}T*OMT2D+=ZG92989CCQr09=P%Q^R0o&&7mJz;2Dm{?o zc~L)1vy!)hxvR@aUScdR0Y!wMQDt9&%Lo+|agY|$2Nhf5z|w$6G0gNRT;ARBtXosd zOE~e?82b{=y*0*u{iomR2Sr8V^3Fa^!qt<+@8>+6)c9qbij&3~f`9aSyu@QTIVivyQ3y3Z&Vj7G+m|yK0op{q3ru$|k?Q9u1-u*_!^@45%EC ziZN1g4oKxo2k!$?`3j9aAe94Bc|J(x09Bp}s>rN{n`x6v3JZk1&@{(6tYUYGo>N-y zIs5>GH3gJ|51})5Jbya!$Y`aq;U4oDV27<<+#EF@it_h@m$pDkpA{or!u(v>iyVx& zK;g|fox?kehj$ha?<~qk;qcDlOTj!}>?=t;f^EEe`Z%yL+?IOceJxD|ntd&Q{nY7B zUz*u<@3GZIn{7TGI%yA`)IoxJT-rg0zUBntlI3Z@;vD)JOH-;y5inR?gqN^2H|n8+ zvrPj+TdkZ&X_2q`nMGM8PVlHpl?l$VP08yZSw-C=FX#t~m&}hq51luwgU2{Qp>lZ6Q) z8iekaE=Bm-jMN$QHLky|0{oiuMFL|7`u>8G!3iOMR?ko~e(xL6R5zl@VC&5HofMdRT; z8vE)=sOO@xuUC0QRVM7qsEipr#>(W|W~uc7!tgI~bfY^X#*c+H!50&^_~k{y!!PyS z2g9#_RAy4dL6&1%kDo#tixw!a-yJ?2-s}2T2m@BTxMJVvuv^m%bvJ89{&oPO=xXC8 zMnw9GJd9K(-~9Ua-JA0_zpj42;K%d#XV%Yu{r>Eyw^pxj+kbQ4+V;EicjAUOAh!L( zpL#2HgZRPx)^boKDh+ z1#)0Ov|UQia%OOZ2il+SwNWUV@QsU0ar%- zn=&X~TmODFvAN{w#1@TR=HZ;TCl?KUcw5e$7O@k65a-ZeJF)40e*Xhfg!4T4w$St@35B4`!01eG(Z?XGy5^8|b38PM6Ya-;hzS0JDnDM8@wJ8L zHABEi79Vj}%nG1GrdJt+mY}p2|NHMqf64)P7g`7B7VsZki+;wg{p~l*Yz@_VDF!$_ vfu)(16r^D@3M#+fh^&87R7{Xt&5&R5F%{2(R{2j-01WH=xW@khUi9qwpQC;T delta 2392 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 literal 3906 zcmV-I554dJ0Mcl9ob4Uia@$7o-Cu$5Ve_y{;^ZNjQp%BB6<4fH%GPEpl}folVn||* zE5J~PxAxz+2gl$Tkbr=ZDbn*KVve5fo}TWW?gljf;lXvJI}}E??_CVkkuvz<^40vi z>tC*JfB*HZ@OzejjgZDQ#AL(OZj0U4F zUrEw@gG@`pwu>Tr{mDX}@7i86)fh;Nz=grn@j!CT2%|8t@p5x9pwD-{1N{mtm_hKi zE(){xa=Ze;kNkk2I5d}^m%fKh+e4vrXGiwJ0TR@KbZ=W2Ku?U>K-wUCy}{5t100G( zK3oj6f%F(paLotXwZ96;Ufi=VBajIUzzIFu2$ckZ3?#oo(_6=^sIFOBHO120TDHHk9Ry;$L{a32 zHPc`sJ5I7avUa>~h=??&j6qeuqgBJxWyM*bSa z%XEWOoFlx&uoRgq*U%*AMAh^n$1<@gt?m(y7hlm0TZJfsA0s$GtL2tRxS=TQPNh&_ zS0pD22wfvUe2E?8-I;Cz)Cnx)#J}i|tZU;t$sCZe=+RrczLhKw+ZW!OcZe|cVgN8q zoXm)}5fM!?Nh8hK8qp?5e*_kz9OA~(d6yo5dGUlWMae=-0@RoScF9^b?2L&8cJv~4 z+=13HM_LKeDYfH9VnGbP31a7Rau#ZOmK#5`QoAI@4^)XHkMVNxuOaeGP_@>sMa(cj z>$M$2g5Q=MCQJ*1wMjHre4^or*eHhB5+7kuaWV3oW0gTDa|T+A)3d*1WU_J{#;$GI zfgfT}Xu%frr|Eg=POt0-WW}v_$nlppevF&_EhYoOf`q=~B#J3(%x0_U?4-Fg9oTG{ zv9(f4gA&a?R5MthY1g1A6QdT|n9d<)Acv@GaGn#?OBmIWN-PRUpafj=S7@~Yza>gk z@}5mCDpZJU3zA=Z(Vi%c zd#V`?q}RY;x1tUTcfyGXAC_=Nl#hse}Zq7-^Mk2zpPwsZL0w-P3PBK7pEZ1leAURcH zMSw&ENDdz$q2{r7jAYg)x?zl|m)!%(G=rilGd>ZUX&-b93TD;^ooNO}SE|m_a|T+< zlF={L!pMf&n)->hOEMZ^)cK!`VAMf*4hHs%*$4*q4BH3>_6+|B2KI~q2?q8J0tpWG zjRTzq2Rp@qu7*F^Kbg3D9B52yWb|(&4z#g1CjPW^e1M~v&qFG6tV8(3DT1Ot0A1Gt~bWoa&J#e7MZ39!XRA@L4lB$?}8`pZi4-pI)!k z2uJ6`XMNI+&wTDvWleYZ5;87nbwz zQspOP^8{t{@HLb|Hic}~%jSucxeD1lLD@W!B4Qz%Cn%ezQlc${^Av^iL<-=AaK238 z?37dfU|XnrLRnX`gmQuULPFU%frPR$n&_pRatJIt(Ok09;hPH_6OMVhaUwFwMPfxJ zS!9w$Ci!b+k`tSF&@}RRr^j}HEHXvrB_xr@^~N`M3`(Yf^0Vxu7saVwwKhp^}17 z@=Y2Gp%g+{?C2(-$&(l{ipQ z*2bLFaj#Mdor3B0-A`T>7nD0~Km6HPanZ}@<>+v#tLj$(LiLW$=_?;?3rU)6c}v5` zCm8m8A2VqGS+_jBT&29rr{Ngk>VOC%SX~fd#Hu|IBSVA{sV;~x0@VSLr+@jcO?a@2 zjI{dlUt@TX@t~f-6AFa!pE{w!cu!B@N*29keWy;SFrHI;C>o95GJezZecbZ-J}Dry zm(X59d$mACXs=R7SM9~O?owzkp}mCmYJrN-UZsw%+KX=~q|jbMdkO8;0u`aXN*$f! zUVMuah58ceOQ^3Fs0j5{>gcMz_*VQ2?IpCA&|WQ25!$QN(b@UpTY@jtmr!3qeYHSE zsIOvkhc`9if2`O2O^qpiQ=`DclYUbpA`JVqv4f2toaiU~j-{So5^rjpD6!&A4e_SN z;osCqZDZ!4$M(3(n;M)dGkN`|GG71Dt8L*b$NvDFaiUj=Czp>;xBFN#f71KahaENP z{fbMDn)H5|>7ypSXIks1Y3~;kKWfr@2GkBud(CY;Z6dx8cs3}^`{wduQ5immmZeWT z^awvXRo*2GtMN^wCEVnRhpEjpL!BDRw21K&facnkE7-Co;|8{TQn-PwC?9@s%hJ6M zZbgoo;yFK>QB)VsX$s_!U}X}>nbrn!rcVsyupt~A+JS@oXlXkRxdm0u^t{>+_YJt7 zD2YQ3II6_s93-u3a>?_}FhUCPJGG=EBo9VHla`DJT9owq0{z8_YGEQ7op{B?5n4Wx z_9s8crJ3vjK`(!Txmx~|PTBaYRfMoodQwYI$w;^kn$Fr+iw{~1KYh?+@DUC+7Q)+< zEc&RHA2sHJuMqJoHmKDGjWhu3l$dpLS$EZkuF63xl?JV*S8>Zy^aJu#>!dBg7- zB2NY1RcC$IYKVu4%oL_;HJ=P)UiUwn?4CbkV@%8|-{tu=PV>{4^LNIdB)?9EKTYWC zY)*L}0b>Thn0dFq0-*N})!rjcW`h{^l>GGin_kIxpRm6Bn=STT6Wcy;n2coo__;}k z#3SUIj%<^N4y0Fv!Ya4O4g8$R7gY5MN6Gt5c{UPpt zB;TKeF4ugpUHeNaTi_n#PZWvRs1bWh%v$9pnd}2`er9T!4~ayULG84VTERkAE{Dk& z$xr;Y?CL=H^4HeJWv>Q0632(`iDOz(U?wmMvkIP!TJ?m|F>JZPJOP&4fACvv?{jjs z1-B-S0kJg^q)Soqp~MfY@r1LHYU`0&b)>p$q~3a@(R$>#HJ%e@aD9o7564p(1uz-hUehz3$1g-z^gNjbfR7FU%-CC{4M?kA z%__9bas9bu~`b$rKDXH9gU|gbNsEU1e z{SGdNDqNUDN1wL77Y)_o#Yla=i;+I9cu;bMTXUJ^YRY>;!Y<>Z))^mad49GtPpGBc z*?lvMkY`Dz>u)`r-pT2ORel1#+FpF)EQWORvQXCdqD=qE^b=54kI}lPCvLXWrl)2? zsjI)qta&nKSX-Hyn(=JNWK{FH?#zNW8tz7W-7sL%G+>$z&=^v9n>1_&pW^DvMK{Bi z&+lN`>eN4{b7ky3SNVU&ekpojycb`g1)i)0wAt<>p6dZk=z-JL1CwT#;2F=AGSVi^ zM{8tgJWiL!R-Sh}V8*n?0W&6}x}uB90aK3SfT_ATVBHSbbngMns~%3-Znch0F%hSo zg2_ajNbb|nrsLLur=5qB2|NvV4rL-wLv=jb>0UF@CpiAQ4<19`Og%eOcWDb6jM^C~ z(%LbK#-w%;O=D6!*+PR-`?x^^Q@fdK#9v;B@k$3q>ZcA16hU8OZ=7DW`K3S`7g^5vE!I&j0=rMe? z74$skfGnGo7BnuBk})PJJ(45STapp_|ADXT`TqWX zbgz&6a4p|{kUzY=8g8)b7>XQ$TrTm*!WQV%IcdC{Z;)wS&RvAfR9gRnF&m5?Fewo8 zFiIs$zMRJue`#cp43L*#kS<>Z{{a0X{a3o**cc7T5ku!v5TfCIXa=vP|Gk>a@yyG) zoGiBRtw$*>ACy+G#&Bi2w)1%Y{ck9=OwauObybYi`luYjt1X^;C z8G5$2mafCCEB(!Mfw#mZ7y)}>dEjnPWB|cDFhg?+v#*V9mS?? zHM&)zbLpA$?+3i-X6kAsn`Bo{F)0uP2nYlSfmwWi=ef!aj$$Y9-;B^i8-0KBYVpn4 z$J5K-F5WAy=f&eN3jV@N0{!`>LheKSW)uhhW>mzWEF)}_AbKLWeB3Z$@eRCUD`o2JHlczw>aE zj91wVls@_cev-&uUM~Zm*p81Q<;IDfD;HQmBjwgv5kNgRjz-EBJDV+eGcph~@=V^n z8R;YCo(=HqJI8bW!7EOW<7X{CTQZ-(K!J8dfcv%wT_1q=krD@PkRJu(96u%zaN-k= zA>*5IJe!Y{^&Ra2IzroLPp<43R*wK$3tGDv7nY0y(qED7Zy3YbY=!v*Wh8P!7YFgu zab3Ea1#re#@wu{w1$DKALMohx( z3X`2+Ds7@-lj8(oMU3GPabp+eQ`|VD+B?za(-|y2aj}16dxWvzhAZsy2^RRf^>G_;-3lVB*W1N7#H zCx0mUQQ*3mvj$** zBc_5C&l5yQAmZHxOt8u;n{BaL0f=u@W0c?mk75(q9=FVqL((yHSIaOWeKrzY6o@PG zOSeW)Hw6|gSmX5?T$PwvoOGFG1IM5g=9>To!b;jZ68yb$NKt_BfXg`D$6b3Bhc?HN z80yrPbO%{Oqq{&Q&rjG@mol)+N1r$5vxSbZNBUZPnwxFLfd zA*5M)^$@dU2J^_AR*pcZk!+;5p%xi!i9}`-Z40$14(a(dzAr%z)+lnWz~2)pLTw`K z+P1rc#?lVs$;x$0$=V+oy1R|zr(-X+@J2*GmJupZ0_KnDvMjh9Lz>Mj>oy$Wd znl#oX^SXCHKNm-KK^#dbYoA zokq_)ESpUEQTt^plepGp-DII5y@SG)3^zKgTnY8&{?c`mI+}LSIazSXs$aZ?Dqj8m z#=xLTGMIzG91P}QFbCWS4CY`k2aS{u=HP2E2h*7~n1jI_G*dp9gO_6t7HZ)jp(%O$ z&W__llDAZudGa>5y3Zqdt7~Q1+cE9#m%klT+f|#vO#lu|;Zgz)Na1Sv6t4CnQ@FES z3Kyk0-1-D=>tr8FlYQM&eGi)GGafH*`yf2h>H2f2r(9C6W60YMdD|gxOEJI9ylv}L zmVVSGl~n_h=CO>Imc=SzYRF=Vh3XKRa$mG<7RxY`?+B~n(vFUL`!Jzov=o=tOrBD1 z>Q!-RE7EnVDln}kJ)QfYxHOx4nvgEVrEM5Lns<*&)4^6UwG@_i6u1Ph zM#8xqmbRik8!yGBorK;cEX`(5m%b@1t(rd3STwIyy1s{8IZM*&%zLmUgDn|s$zV$w zY)Pwhx}o*4C3-JgB9HE5OSE3LMDJxwv|hGE?`KQ2KDI>bWlKr~-o=)b=}Q+|qV=;S zdOuqt3%!#q(R{8LTe0L>}G6mZX#)W=l{XTcXQJpf0uq^|B@D zSaqxgx<-PpnkR_H~pcuq+{mp;;^bqYK(Z| z&d8{H9*MiLFv$CvJKEhnb4MA?Qg_sL)u!$e0GhfRBMu@Z@AN|QPA??ysH*m@zcgNC z@=i;VcRJ04h}@kuf2j1GHlY?YN&D#Y1M)t`L*{*^S>ne^Gd~kUD&%>BR33U9K2s*; z(xM@eHze{pCGuWm_U&YO1V%|uPJRq zS__i1vdkJv)56Rq0@K>eQo0^1`FG=7QJv_M{Szn-PXE0yeYZO3yJk*|8kLk(er%%2 zv`&d-wMUbhqF}8ZA*mua&7UcA>9miRpAdm# zX?m>OAz9F6xFyH*hIAf551_hyMBNEN$#>N3=&LmXOa3Uf5kaTe8p*Ns87N6P zr#MQR>zFL6JIQrSXg*J_V-ncXvAL_6*riP^E@vv?r{t{QU(WKnj2|(&;mE*DFKRL$ zH*V)2u1 zIuXPGE1)P=uVKA3Gzm5v8m@#w_hPc6N+mgsiHem+&EjbxcGl$BSlNUvfrp1$oh^Z4J@p2+-LGjlRUkg^tqiih~WtVQ7q+5qu^QRDTgLh@g`Q_JTdp*$^U~x^{SYh?JE>PQQ{6X$k8!gh)>xH4Gur7s%`o zA`Kx@=Q6_NEqO-i89`F}l0q?{SXfwbM`j+jw!AP8m|d3H2wGcem5)v+ zlSt9}N+QLrv$e^eOE&;KL&W&>LDUd2eu2ym5#tasK7CRx-5onR!!xlt>hn?gY4(2Q zO*M)RH%&@NN)DnMYu?%*X)Jwfu_BF^PqtYSsSNHAZW^oM+8~j?A#2T!k>F^ojccVP zjki;_VW+uJt`(NXdbt*>(tKfMt7VaD=MG}1v4pM_7O9Y~49f$>Hw6pUC9EB6SmTNe zHf*qAtyX2QVGn1+x*SjqHmq?)1{*fmuvV)w*szDQVO@@z2OHM7B7+SZY*?#R8Ejbp zjM2xw86$Qx3CkGG1nFb_t4JBaP=81nJ%=pHkTUuLnH^F_L(1ssQ$`;TOBuCsuN%K8 zY2%O{f<*S<4#FZiJdgKT&&NZLy$3lwq8$1aWesw8C^_`2%o^lS=$0?gP>Z)l+dO3@-gy4(%1o~Vz(ah9xxAhfBDkr88<5}{IZ+9f{ zY~lo49 zO?&`J=Pc-?8UpS>AJVkPB8`Q?Jt%8KS5br3cOE!#r5C}>Ei=0a#-JxOcTAtyJvq}> z7rtCE3UY!f|Tz0 zs)UrTwX1|QTSHD;Lt6Wg#-!A87j7e~TA%Dby($2`381FHh6B_T)^LEDf*KBB)o@y0 z8dFvhQ(p?Rw8OZ(2&Sp{CLAHo(svdT9Q9LJ;}6#d0C=)njUri1diA*4!+IYFD-SyOd7+a?iX!P_ed_I7e>OoC{mUjC&1^!{|bC7x?)Y7ATHl1x@)1zNYD zohOSe>{%xZ4-;Fc=-&XdCHRie$j=9-pX-)-vS4AOh~y}Mx`gfH8h_?)6urRMe4j5+ z6hQV?i5)M5g}#hze~AID9g$tAP={%vDE|Sc9)z&P%IU?=$~M?QOrd!9%CA6Ca2o>; z%PmCT0Qqi56vuUUAa$GSDIEjBKaCUMCnX_@Vg3kg5yWm{(kpNYz}3a^cZ#h*01;i^ zL%e5$nE_M70GEO1!IY65hg6D@<8R^#h!~atBpl|2KvyrcBYOjiupLvyIfgi#+`s;e z(&;OX#Z^RgzsT6j)vkB<#~{X?zoJ$v@c&^7@ZtRY(;&eYBEh%24ZPBdu4(g}%oZxO zs?@|%tNzCyv@^F!C1-Jthw0m`xS^IF^o58YlhU(3Hw1ITa*OF+m!-mD)&-LW{sz`k Rbv-X0i&;)y{U1;NJZl}9VPOCO literal 3924 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 From 9eb6e3fa33346402520576ae815f5cbce5bd53a0 Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Fri, 13 Sep 2019 14:03:26 -0400 Subject: [PATCH 023/160] Update .mailmap --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 71b6697c8..ce10c7d24 100644 --- a/.mailmap +++ b/.mailmap @@ -29,3 +29,4 @@ grejppi Johannes Lorenz Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com> Noah Brecht +Hussam al-Homsi Hussam Eddin Alhomsi From b07c007a27d660fe576cb5c852e01db7560c9e73 Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Sat, 14 Sep 2019 11:57:44 -0500 Subject: [PATCH 024/160] Add save/load of PianoRoll marked semitones (#5146) --- include/PianoRoll.h | 1 + src/gui/editors/PianoRoll.cpp | 42 ++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 4451a07c5..f56d791f4 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -95,6 +95,7 @@ public: void setCurrentPattern( Pattern* newPattern ); void setGhostPattern( Pattern* newPattern ); void loadGhostNotes( const QDomElement & de ); + void loadMarkedSemiTones(const QDomElement & de); inline void stopRecording() { diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 5edc5d47c..a1fcdc6d8 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -24,6 +24,8 @@ * */ +#include "PianoRoll.h" + #include #include #include @@ -46,7 +48,6 @@ #include "AutomationEditor.h" #include "ActionGroup.h" #include "ConfigManager.h" -#include "PianoRoll.h" #include "BBTrackContainer.h" #include "Clipboard.h" #include "ComboBox.h" @@ -655,6 +656,32 @@ void PianoRoll::clearGhostPattern() } +void PianoRoll::loadMarkedSemiTones(const QDomElement & de) +{ + // clear marked semitones to prevent leftover marks + m_markedSemiTones.clear(); + if (de.isElement()) + { + QDomNode node = de.firstChild(); + while (!node.isNull()) + { + bool ok; + int key = node.toElement().attribute( + QString("key"), QString("-1")).toInt(&ok, 10); + if (ok && key >= 0) + { + m_markedSemiTones.append(key); + } + node = node.nextSibling(); + } + } + // from markSemiTone, required otherwise marks will not show + std::sort(m_markedSemiTones.begin(), m_markedSemiTones.end(), std::greater()); + QList::iterator new_end = std::unique(m_markedSemiTones.begin(), m_markedSemiTones.end()); + m_markedSemiTones.erase(new_end, m_markedSemiTones.end()); +} + + void PianoRoll::setCurrentPattern( Pattern* newPattern ) { if( hasValidPattern() ) @@ -4697,6 +4724,18 @@ void PianoRollWindow::saveSettings( QDomDocument & doc, QDomElement & de ) de.appendChild( ghostNotesRoot ); } + if (m_editor->m_markedSemiTones.length() > 0) + { + QDomElement markedSemiTonesRoot = doc.createElement("markedSemiTones"); + for (int ix = 0; ix < m_editor->m_markedSemiTones.size(); ++ix) + { + QDomElement semiToneNode = doc.createElement("semiTone"); + semiToneNode.setAttribute("key", m_editor->m_markedSemiTones.at(ix)); + markedSemiTonesRoot.appendChild(semiToneNode); + } + de.appendChild(markedSemiTonesRoot); + } + MainWindow::saveWidgetState( this, de ); } @@ -4706,6 +4745,7 @@ void PianoRollWindow::saveSettings( QDomDocument & doc, QDomElement & de ) void PianoRollWindow::loadSettings( const QDomElement & de ) { m_editor->loadGhostNotes( de.firstChildElement("ghostnotes") ); + m_editor->loadMarkedSemiTones(de.firstChildElement("markedSemiTones")); MainWindow::restoreWidgetState( this, de ); } From 0059c6e71f6431b4ed13d8e444f133642bbe933d Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Sat, 14 Sep 2019 20:35:54 +0300 Subject: [PATCH 025/160] Fix #4188 - Check for failed qFind (#5184) --- src/gui/editors/PianoRoll.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index b65a6b56b..7ba01f983 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -537,7 +537,10 @@ void PianoRoll::markSemiTone( int i ) for (int ix = 0; ix < aok.size(); ++ix) { i = qFind(m_markedSemiTones.begin(), m_markedSemiTones.end(), aok.at(ix)); - m_markedSemiTones.erase(i); + if (i != m_markedSemiTones.end()) + { + m_markedSemiTones.erase(i); + } } } else From 8fdf6c4d120430194ee9c4303b6d8a7bcad99c32 Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Sat, 14 Sep 2019 12:36:06 -0500 Subject: [PATCH 026/160] Fix #4188 - Check for failed std::find (#5137) --- src/gui/editors/PianoRoll.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index a1fcdc6d8..36505e982 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -550,7 +550,10 @@ void PianoRoll::markSemiTone( int i ) for (int ix = 0; ix < aok.size(); ++ix) { i = std::find(m_markedSemiTones.begin(), m_markedSemiTones.end(), aok.at(ix)); - m_markedSemiTones.erase(i); + if (i != m_markedSemiTones.end()) + { + m_markedSemiTones.erase(i); + } } } else From ffa03df72bae81aa886f444e90204246e854cbb1 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sun, 15 Sep 2019 10:18:47 -0400 Subject: [PATCH 027/160] Fix $PATH value in AppImage Closes #5187 --- cmake/linux/package_linux.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index 0dec715f4..b838237b7 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -103,7 +103,7 @@ mv "${APPDIR}usr/bin/lmms" "${APPDIR}usr/bin/lmms.real" cat >"${APPDIR}usr/bin/lmms" < /dev/null 2>&1; then CARLAPATH="\$(which carla)" CARLAPREFIX="\${CARLAPATH%/bin*}" From 419321dd0146d9893f7934ef852cb54994ffd26f Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Mon, 16 Sep 2019 07:09:42 +0200 Subject: [PATCH 028/160] Only MacOS and Windows are case insensitive (#4768) --- src/core/Song.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 943524e6d..6bd94a484 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -1414,7 +1414,12 @@ void Song::exportProject( bool multiExport ) // Get first extension from selected dropdown. // i.e. ".wav" from "WAV-File (*.wav), Dummy-File (*.dum)" suffix = efd.selectedNameFilter().mid( stx + 2, etx - stx - 2 ).split( " " )[0].trimmed(); - exportFileName.remove( "." + suffix, Qt::CaseInsensitive ); + + Qt::CaseSensitivity cs = Qt::CaseSensitive; +#if defined(LMMS_BUILD_APPLE) || defined(LMMS_BUILD_WIN32) + cs = Qt::CaseInsensitive; +#endif + exportFileName.remove( "." + suffix, cs ); if ( efd.selectedFiles()[0].endsWith( suffix ) ) { if( VersionedSaveDialog::fileExistsQuery( exportFileName + suffix, From 90dec52db00b20ec986748da571a72df5145a200 Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Thu, 19 Sep 2019 12:02:11 -0400 Subject: [PATCH 029/160] Add missing icons of File menu actions (#5183) --- src/gui/MainWindow.cpp | 7 ++++--- src/gui/menus/TemplatesMenu.cpp | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index bdb3a7472..08bc90852 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -291,7 +291,7 @@ void MainWindow::finalize() tr( "&Save" ), this, SLOT( saveProject() ), QKeySequence::Save ); - project_menu->addAction( embed::getIconPixmap( "project_saveas" ), + project_menu->addAction( embed::getIconPixmap( "project_save" ), tr( "Save &As..." ), this, SLOT( saveProjectAs() ), Qt::CTRL + Qt::SHIFT + Qt::Key_S ); @@ -300,8 +300,9 @@ void MainWindow::finalize() this, SLOT( saveProjectAsNewVersion() ), Qt::CTRL + Qt::ALT + Qt::Key_S ); - project_menu->addAction( tr( "Save as default template" ), - this, SLOT( saveProjectAsDefaultTemplate() ) ); + project_menu->addAction( embed::getIconPixmap( "project_save" ), + tr( "Save as default template" ), + this, SLOT( saveProjectAsDefaultTemplate() ) ); project_menu->addSeparator(); project_menu->addAction( embed::getIconPixmap( "project_import" ), diff --git a/src/gui/menus/TemplatesMenu.cpp b/src/gui/menus/TemplatesMenu.cpp index 5fb740203..b944b7cf1 100644 --- a/src/gui/menus/TemplatesMenu.cpp +++ b/src/gui/menus/TemplatesMenu.cpp @@ -11,6 +11,8 @@ TemplatesMenu::TemplatesMenu(QWidget *parent) : QMenu(tr("New from template"), parent) { + setIcon(embed::getIconPixmap("project_new")); + connect( this, SIGNAL( aboutToShow() ), SLOT( fillTemplatesMenu() ) ); connect( this, SIGNAL( triggered( QAction * ) ), SLOT( createNewProjectFromTemplate( QAction * ) ) ); From 1324cf48c16a91898a8cdec8af43f0f0ce41bc82 Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Mon, 12 Aug 2019 12:35:12 +0300 Subject: [PATCH 030/160] Enable automatic dll installation for RemoteVstPlugin32 Co-Authored-By: Hyunjin Song --- plugins/vst_base/CMakeLists.txt | 30 +++++++++----- .../vst_base/RemoteVstPlugin/CMakeLists.txt | 15 +++++++ plugins/vst_base/RemoteVstPlugin32.cmake | 40 +++++++++---------- plugins/vst_base/RemoteVstPlugin64.cmake | 1 - 4 files changed, 54 insertions(+), 32 deletions(-) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 44ed0dcb3..873ed5f8e 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -11,23 +11,35 @@ SET(REMOTE_VST_PLUGIN_FILEPATH_64 "RemoteVstPlugin64" CACHE STRING "Relative fil ADD_SUBDIRECTORY(vstbase) -SET(LMMS_BINARY_DIR ${CMAKE_BINARY_DIR}) -SET(LMMS_SOURCE_DIR ${CMAKE_SOURCE_DIR}) - SET(EXTERNALPROJECT_ARGS SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin" - INSTALL_COMMAND "" + #INSTALL_COMMAND "" BUILD_ALWAYS ON + # Skip the install step. + INSTALL_COMMAND "" ) +set(export_variables + "LMMS_SOURCE_DIR" + "LMMS_BINARY_DIR" + "CMAKE_MODULE_PATH" + "CMAKE_RUNTIME_OUTPUT_DIRECTORY" + "CMAKE_BUILD_TYPE" + "LMMS_BUILD_LINUX" + "LMMS_BUILD_WIN32" + "PLUGIN_DIR") + SET(EXTERNALPROJECT_CMAKE_ARGS - "-DLMMS_SOURCE_DIR=${CMAKE_SOURCE_DIR}" - "-DLMMS_BINARY_DIR=${CMAKE_BINARY_DIR}" - "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" - "-DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}" - "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" + "-DBUILD_WITH_EXTERNALPROJECT=ON" ) +macro(_export_var_to_external var_name) + list(APPEND EXTERNALPROJECT_CMAKE_ARGS "-D${var_name}=${${var_name}}") +endmacro() +foreach(var ${export_variables}) + _export_var_to_external(${var}) +endforeach() + # build 32 bit version of RemoteVstPlugin IF(WANT_VST_32) INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin32.cmake") diff --git a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt index 59dd19a0a..f4023fd42 100644 --- a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt +++ b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt @@ -76,3 +76,18 @@ endif() IF(STRIP) ADD_CUSTOM_COMMAND(TARGET ${EXE_NAME} POST_BUILD COMMAND "${STRIP}" "$") ENDIF() + +if(BITNESS EQUAL 32) + INSTALL(TARGETS ${EXE_NAME} RUNTIME DESTINATION "${PLUGIN_DIR}/32") +else() + INSTALL(TARGETS ${EXE_NAME} RUNTIME DESTINATION "${PLUGIN_DIR}") +endif() + +if(BUILD_WITH_EXTERNALPROJECT) + include(InstallTargetDependencies) + INSTALL_TARGET_DEPENDENCIES(TARGETS ${EXE_NAME} + DESTINATION "${PLUGIN_DIR}/32") +else() + # Needed to deploy dependencies of RemoteVstPlugin + SET_PROPERTY(GLOBAL APPEND PROPERTY PLUGINS_BUILT "${EXE_NAME}") +endif() diff --git a/plugins/vst_base/RemoteVstPlugin32.cmake b/plugins/vst_base/RemoteVstPlugin32.cmake index 9a8f04529..efbd00ee9 100644 --- a/plugins/vst_base/RemoteVstPlugin32.cmake +++ b/plugins/vst_base/RemoteVstPlugin32.cmake @@ -1,14 +1,21 @@ +# INSTALL_EXTERNAL_PROJECT: install a project created with ExternalProject_Add in the +# parent project's install time. +# +# Description: +# In a regular scenario, cmake will install external projects +# BEFORE actually building the parent project. Since the building +# process may use installed components from the project. +# We want to give the external project the ability to install +# files directly to the parent's install. Therefore, we have to +# manually trigger the install stage with the parent's INSTALL_PREFIX. +MACRO(INSTALL_EXTERNAL_PROJECT name) + ExternalProject_Get_Property(${name} BINARY_DIR) + + install(CODE "include(\"${BINARY_DIR}/cmake_install.cmake\")") +ENDMACRO() + IF(LMMS_BUILD_WIN32 AND NOT LMMS_BUILD_WIN64) ADD_SUBDIRECTORY(RemoteVstPlugin) - IF(MSVC) - SET(VCPKG_ROOT "${CMAKE_FIND_ROOT_PATH}") - INSTALL(FILES "${VCPKG_ROOT}/bin/Qt5Core.dll" DESTINATION "${PLUGIN_DIR}/32") - INSTALL(FILES "${VCPKG_ROOT}/bin/zlib1.dll" DESTINATION "${PLUGIN_DIR}/32") - ELSE(MSVC) - INSTALL(FILES "${MINGW_PREFIX}/bin/Qt5Core.dll" DESTINATION "${PLUGIN_DIR}/32") - INSTALL(FILES "${MINGW_PREFIX}/bin/zlib1.dll" DESTINATION "${PLUGIN_DIR}/32") - ENDIF(MSVC) - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") ELSEIF(LMMS_BUILD_WIN64 AND MSVC) SET(MSVC_VER ${CMAKE_CXX_COMPILER_VERSION}) @@ -46,16 +53,7 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) "-DCMAKE_PREFIX_PATH=${QT_32_PREFIX}" ) - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") - - #TODO: find a solution when not using vcpkg for qt - SET(VCPKG_ROOT_32 "${CMAKE_FIND_ROOT_PATH}/../x86-windows") - - INSTALL(FILES "${VCPKG_ROOT_32}/bin/zlib1.dll" DESTINATION "${PLUGIN_DIR}/32") - INSTALL(FILES "${VCPKG_ROOT_32}/bin/pcre2-16.dll" DESTINATION "${PLUGIN_DIR}/32") - INSTALL(FILES "${VCPKG_ROOT_32}/bin/double-conversion.dll" DESTINATION "${PLUGIN_DIR}/32") - INSTALL(FILES "${VCPKG_ROOT_32}/bin/qt5core.dll" DESTINATION "${PLUGIN_DIR}/32") - + INSTALL_EXTERNAL_PROJECT(RemoteVstPlugin32) ELSEIF(LMMS_BUILD_LINUX) # Use winegcc INCLUDE(CheckWineGcc) @@ -82,9 +80,7 @@ ELSEIF(CMAKE_TOOLCHAIN_FILE_32) "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH_32}" "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE_32}" ) - INSTALL(FILES "${CMAKE_PREFIX_PATH_32}/bin/Qt5Core.dll" DESTINATION "${PLUGIN_DIR}/32") - INSTALL(FILES "${CMAKE_PREFIX_PATH_32}/bin/zlib1.dll" DESTINATION "${PLUGIN_DIR}/32") - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") + INSTALL_EXTERNAL_PROJECT(RemoteVstPlugin32) ELSE() MESSAGE(WARNING "Can't build RemoteVstPlugin32, unknown environment. Please supply CMAKE_TOOLCHAIN_FILE_32 and optionally CMAKE_PREFIX_PATH_32") RETURN() diff --git a/plugins/vst_base/RemoteVstPlugin64.cmake b/plugins/vst_base/RemoteVstPlugin64.cmake index 4b02bf8ab..65b33a162 100644 --- a/plugins/vst_base/RemoteVstPlugin64.cmake +++ b/plugins/vst_base/RemoteVstPlugin64.cmake @@ -1,6 +1,5 @@ IF(LMMS_BUILD_WIN64) ADD_SUBDIRECTORY(RemoteVstPlugin) - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin64.exe" DESTINATION "${PLUGIN_DIR}") ELSEIF(LMMS_BUILD_LINUX) INCLUDE(CheckWineGcc) CheckWineGcc(64 "${WINEGCC}" WINEGCC_WORKING) From ae9d619abd0410e642c50f48a2095c4fcb82ee7a Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Wed, 21 Aug 2019 21:32:39 +0300 Subject: [PATCH 031/160] BuildPlugin: Always remove 'lib' suffix from the DLL name --- cmake/modules/BuildPlugin.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/modules/BuildPlugin.cmake b/cmake/modules/BuildPlugin.cmake index e285e05bb..675433e63 100644 --- a/cmake/modules/BuildPlugin.cmake +++ b/cmake/modules/BuildPlugin.cmake @@ -75,9 +75,11 @@ MACRO(BUILD_PLUGIN PLUGIN_NAME) ENDIF() ADD_DEPENDENCIES(${PLUGIN_NAME} lmms) ENDIF(LMMS_BUILD_APPLE) - IF(LMMS_BUILD_WIN32 AND STRIP) + IF(LMMS_BUILD_WIN32) + IF(STRIP) + ADD_CUSTOM_COMMAND(TARGET ${PLUGIN_NAME} POST_BUILD COMMAND ${STRIP} "$") + ENDIF() SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES PREFIX "") - ADD_CUSTOM_COMMAND(TARGET ${PLUGIN_NAME} POST_BUILD COMMAND ${STRIP} "$") ENDIF() SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${RCC_OUT} ${plugin_MOC_out}") From 2f64c461e8dcf4d5ac185a763a500cbdd0aec4bf Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sat, 24 Aug 2019 11:13:49 +0900 Subject: [PATCH 032/160] Fix missing system libraries in MSVC installations * Install UCRT libraries * Install debug libraries in debug builds --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36e35bf4c..a08a9c8f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -709,4 +709,9 @@ MESSAGE( "\n\n") SET(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION "${BIN_DIR}") +if(MSVC) + # We can't set this on the install time according to the configuration + SET(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE) + SET(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) +endif() INCLUDE(InstallRequiredSystemLibraries) From 94354e305342d894332b9ac919dd9252b39c9422 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sat, 31 Aug 2019 09:14:36 +0900 Subject: [PATCH 033/160] Support deploying dependencies in MSVC builds with CMake < 3.14 --- cmake/modules/CreateTempFile.cmake | 7 +++++-- cmake/modules/DefineInstallVar.cmake | 13 +++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cmake/modules/CreateTempFile.cmake b/cmake/modules/CreateTempFile.cmake index ceefa4928..5210342ac 100644 --- a/cmake/modules/CreateTempFile.cmake +++ b/cmake/modules/CreateTempFile.cmake @@ -1,5 +1,5 @@ function(CreateTempFilePath) - set(options) + set(options CONFIG_SUFFIX) set(oneValueArgs OUTPUT_VAR TAG) set(multiValueArgs CONTENT) cmake_parse_arguments(TEMP "${options}" "${oneValueArgs}" @@ -10,9 +10,12 @@ function(CreateTempFilePath) string(SHA1 hashed_content "${TEMP_CONTENT}") set(file_name "${CMAKE_BINARY_DIR}/${TEMP_TAG}_${hashed_content}") + set(${TEMP_OUTPUT_VAR} "${file_name}" PARENT_SCOPE) + if(CONFIG_SUFFIX) + set(file_name "${file_name}_$") + endif() file(GENERATE OUTPUT "${file_name}" CONTENT "${TEMP_CONTENT}") - set(${TEMP_OUTPUT_VAR} "${file_name}" PARENT_SCOPE) endfunction() diff --git a/cmake/modules/DefineInstallVar.cmake b/cmake/modules/DefineInstallVar.cmake index 36f221410..b13cb1d52 100644 --- a/cmake/modules/DefineInstallVar.cmake +++ b/cmake/modules/DefineInstallVar.cmake @@ -14,13 +14,14 @@ function(DEFINE_INSTALL_VAR) # install(CODE) does not support generator expression in ver<3.14 if(VAR_GENERATOR_EXPRESSION AND ${CMAKE_VERSION} VERSION_LESS "3.14.0") - if(MSVC) - message(FATAL_ERROR "Installing is not supported with msvc and cmake<3.14") - endif() - include(CreateTempFile) - CreateTempFilePath(OUTPUT_VAR file_path TAG "${VAR_NAME}" CONTENT "${VAR_CONTENT}") - install(CODE "file(READ \"${file_path}\" \"${VAR_NAME}\")") + if(CMAKE_CONFIGURATION_TYPES) # in case of multi-config generators like MSVC generators + CreateTempFilePath(OUTPUT_VAR file_path TAG "${VAR_NAME}" CONTENT "${VAR_CONTENT}" CONFIG_SUFFIX) + install(CODE "file(READ \"${file_path}_\${CMAKE_INSTALL_CONFIG_NAME}\" \"${VAR_NAME}\")") + else() + CreateTempFilePath(OUTPUT_VAR file_path TAG "${VAR_NAME}" CONTENT "${VAR_CONTENT}") + install(CODE "file(READ \"${file_path}\" \"${VAR_NAME}\")") + endif() else() if(VAR_GENERATOR_EXPRESSION) cmake_policy(SET CMP0087 NEW) From 867639929ac882c867e87aa6e06101912638861b Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Mon, 30 Sep 2019 21:36:45 -0500 Subject: [PATCH 034/160] Add close button to SideBarWidget (#5133) --- include/SideBarWidget.h | 5 +++++ src/gui/widgets/SideBar.cpp | 3 +++ src/gui/widgets/SideBarWidget.cpp | 13 +++++++++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/SideBarWidget.h b/include/SideBarWidget.h index 229f07df0..37acc9770 100644 --- a/include/SideBarWidget.h +++ b/include/SideBarWidget.h @@ -28,6 +28,7 @@ #include #include #include +#include class SideBarWidget : public QWidget @@ -47,6 +48,8 @@ public: return m_title; } +signals: + void closeButtonClicked(); protected: virtual void paintEvent( QPaintEvent * _pe ); @@ -75,6 +78,8 @@ private: QVBoxLayout * m_layout; QString m_title; QPixmap m_icon; + QPushButton * m_closeBtn; + const QSize m_buttonSize; } ; diff --git a/src/gui/widgets/SideBar.cpp b/src/gui/widgets/SideBar.cpp index 01ea58919..cc83abae4 100644 --- a/src/gui/widgets/SideBar.cpp +++ b/src/gui/widgets/SideBar.cpp @@ -121,6 +121,9 @@ void SideBar::appendTab( SideBarWidget *widget ) widget->setMinimumWidth( 200 ); ToolTip::add( button, widget->title() ); + + connect(widget, &SideBarWidget::closeButtonClicked, + [=]() { button->click(); }); } diff --git a/src/gui/widgets/SideBarWidget.cpp b/src/gui/widgets/SideBarWidget.cpp index c1b46cfa6..41647a4fd 100644 --- a/src/gui/widgets/SideBarWidget.cpp +++ b/src/gui/widgets/SideBarWidget.cpp @@ -22,23 +22,31 @@ * */ +#include "SideBarWidget.h" + #include #include #include -#include "SideBarWidget.h" +#include "embed.h" SideBarWidget::SideBarWidget( const QString & _title, const QPixmap & _icon, QWidget * _parent ) : QWidget( _parent ), m_title( _title ), - m_icon( _icon ) + m_icon(_icon), + m_buttonSize(17, 17) { m_contents = new QWidget( this ); m_layout = new QVBoxLayout( m_contents ); m_layout->setSpacing( 5 ); m_layout->setMargin( 0 ); + m_closeBtn = new QPushButton(embed::getIconPixmap("close"), QString(), this); + m_closeBtn->resize(m_buttonSize); + m_closeBtn->setToolTip(tr("Close")); + connect(m_closeBtn, &QPushButton::clicked, + [=]() { this->closeButtonClicked(); }); } @@ -80,6 +88,7 @@ void SideBarWidget::resizeEvent( QResizeEvent * ) const int MARGIN = 6; m_contents->setGeometry( MARGIN, 40 + MARGIN, width() - MARGIN * 2, height() - MARGIN * 2 - 40 ); + m_closeBtn->move(m_contents->geometry().width() - MARGIN - 5, 5); } From 5132d91bfc7fa05d2fa559c7a96399efa0ae89b6 Mon Sep 17 00:00:00 2001 From: "https://gitlab.com/users/CYBERDEViLNL" <1148379+CYBERDEViLNL@users.noreply.github.com> Date: Wed, 2 Oct 2019 01:00:11 +0200 Subject: [PATCH 035/160] Give focus to AutomationEditor when the window gets the foucs (#5170) This will make shortcuts work on opening the editor. --- include/AutomationEditor.h | 3 +++ src/gui/editors/AutomationEditor.cpp | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 9705c5efa..968bb34be 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -297,6 +297,9 @@ public slots: signals: void currentPatternChanged(); +protected: + virtual void focusInEvent(QFocusEvent * event); + protected slots: void play(); void stop(); diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 94e9d5cc8..7583501c7 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -198,6 +198,8 @@ AutomationEditor::AutomationEditor() : setCurrentPattern( NULL ); setMouseTracking( true ); + setFocusPolicy( Qt::StrongFocus ); + setFocus(); } @@ -2516,6 +2518,11 @@ void AutomationEditorWindow::clearCurrentPattern() setCurrentPattern(nullptr); } +void AutomationEditorWindow::focusInEvent(QFocusEvent * event) +{ + m_editor->setFocus( event->reason() ); +} + void AutomationEditorWindow::play() { m_editor->play(); From ca8871455e97bee0274e1c6c4081a0fe82dfeb72 Mon Sep 17 00:00:00 2001 From: "https://gitlab.com/users/CYBERDEViLNL" <1148379+CYBERDEViLNL@users.noreply.github.com> Date: Wed, 2 Oct 2019 01:00:59 +0200 Subject: [PATCH 036/160] Make undo for PianoRoll cut action possible (#5214) --- src/gui/editors/PianoRoll.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 36505e982..065234aa4 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -4095,6 +4095,8 @@ void PianoRoll::cutSelectedNotes() if( ! selected_notes.empty() ) { + m_pattern->addJournalCheckPoint(); + copyToClipboard( selected_notes ); Engine::getSong()->setModified(); From 9f0baab93ae18f88c07dff76a0563f73200b649f Mon Sep 17 00:00:00 2001 From: Hussam al-Homsi Date: Tue, 1 Oct 2019 21:14:55 -0400 Subject: [PATCH 037/160] Fix regression of some default settings The regression was introduced in #3820. --- src/gui/SetupDialog.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index f23b5b271..885f43dec 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -125,9 +125,9 @@ SetupDialog::SetupDialog(ConfigTabs tab_to_open) : m_vstAlwaysOnTop(ConfigManager::inst()->value( "ui", "vstalwaysontop").toInt()), m_syncVSTPlugins(ConfigManager::inst()->value( - "ui", "syncvstplugins").toInt()), + "ui", "syncvstplugins", "1").toInt()), m_disableAutoQuit(ConfigManager::inst()->value( - "ui", "disableautoquit").toInt()), + "ui", "disableautoquit", "1").toInt()), m_NaNHandler(ConfigManager::inst()->value( "app", "nanhandler", "1").toInt()), m_hqAudioDev(ConfigManager::inst()->value( From 15fe551b1da9863db6b127665dd390473615513d Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Thu, 3 Oct 2019 11:47:49 -0400 Subject: [PATCH 038/160] Submodules: Only fetch required (#5182, closes #5105) + Non-shallow fallback (hotfix) * Retry updating submodules non-shallow if shallow clone fails (master hotfix) * Add `PLUGIN_LIST` support to CheckSubmodules (#5105) * Remove `SKIP_SUBMODULES` switch (it's redundant to specifying `PLUGIN_LIST`) * Add `NO_SHALLOW_CLONE` switch --- CMakeLists.txt | 1 + cmake/modules/CheckSubmodules.cmake | 129 ++++++++++++++++++---------- cmake/modules/PluginList.cmake | 104 ++++++++++++++++++++++ plugins/CMakeLists.txt | 84 +----------------- 4 files changed, 188 insertions(+), 130 deletions(-) create mode 100644 cmake/modules/PluginList.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a08a9c8f7..b63900dda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0057 NEW) ENDIF(COMMAND CMAKE_POLICY) +INCLUDE(PluginList) INCLUDE(CheckSubmodules) INCLUDE(AddFileDependencies) INCLUDE(CheckIncludeFiles) diff --git a/cmake/modules/CheckSubmodules.cmake b/cmake/modules/CheckSubmodules.cmake index 65e5be08b..d2475826b 100644 --- a/cmake/modules/CheckSubmodules.cmake +++ b/cmake/modules/CheckSubmodules.cmake @@ -7,12 +7,12 @@ # INCLUDE(CheckSubmodules) # # Options: -# SET(SKIP_SUBMODULES "foo;bar") +# SET(PLUGIN_LIST "zynaddsubfx;...") # skips submodules for plugins not explicitely listed # # Or via command line: -# cmake -DSKIP_SUBMODULES=foo;bar +# cmake -PLUGIN_LIST=foo;bar # -# Copyright (c) 2017, Tres Finocchiaro, +# Copyright (c) 2019, Tres Finocchiaro, # # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. @@ -20,13 +20,15 @@ # Files which confirm a successful clone SET(VALID_CRUMBS "CMakeLists.txt;Makefile;Makefile.in;Makefile.am;configure.ac;configure.py;autogen.sh;.gitignore;LICENSE;Home.md") +OPTION(NO_SHALLOW_CLONE "Disable shallow cloning of submodules" OFF) + # Try and use the specified shallow clone on submodules, if supported SET(DEPTH_VALUE 100) # Number of times git commands will retry before failing SET(MAX_ATTEMPTS 2) -MESSAGE("\nValidating submodules...") +MESSAGE("\nChecking submodules...") IF(NOT EXISTS "${CMAKE_SOURCE_DIR}/.gitmodules") MESSAGE("Skipping the check because .gitmodules not detected." "Please make sure you have all submodules in the source tree!" @@ -41,74 +43,110 @@ SET(LANG_BACKUP "$ENV{LANG}") SET(ENV{LC_ALL} "C") SET(ENV{LANG} "en_US") -# Assume alpha-numeric paths -STRING(REGEX MATCHALL "path = [-0-9A-Za-z/]+" SUBMODULE_LIST ${SUBMODULE_DATA}) -STRING(REGEX MATCHALL "url = [.:%-0-9A-Za-z/]+" SUBMODULE_URL_LIST ${SUBMODULE_DATA}) +# Submodule list pairs, unparsed (WARNING: Assumes alpha-numeric paths) +STRING(REGEX MATCHALL "path = [-0-9A-Za-z/]+" SUBMODULE_LIST_RAW ${SUBMODULE_DATA}) +STRING(REGEX MATCHALL "url = [.:%-0-9A-Za-z/]+" SUBMODULE_URL_RAW ${SUBMODULE_DATA}) -FOREACH(_part ${SUBMODULE_LIST}) - STRING(REPLACE "path = " "" SUBMODULE_PATH ${_part}) +# Submodule list pairs, parsed +SET(SUBMODULE_LIST "") +SET(SUBMODULE_URL "") - LIST(FIND SUBMODULE_LIST ${_part} SUBMODULE_INDEX) - LIST(GET SUBMODULE_URL_LIST ${SUBMODULE_INDEX} _url) - STRING(REPLACE "url = " "" SUBMODULE_URL ${_url}) +FOREACH(_path ${SUBMODULE_LIST_RAW}) + # Parse SUBMODULE_PATH + STRING(REPLACE "path = " "" SUBMODULE_PATH "${_path}") + + # Grab index for matching SUBMODULE_URL + LIST(FIND SUBMODULE_LIST_RAW "${_path}" SUBMODULE_INDEX) + LIST(GET SUBMODULE_URL_RAW ${SUBMODULE_INDEX} _url) + + # Parse SUBMODULE_URL + STRING(REPLACE "url = " "" SUBMODULE_URL "${_url}") - # Remove submodules from validation as specified in -DSKIP_SUBMODULES=foo;bar SET(SKIP false) + + # Loop over skipped plugins, add to SKIP_SUBMODULES (e.g. -DPLUGIN_LIST=foo;bar) + IF(${SUBMODULE_PATH} MATCHES "^plugins/") + SET(REMOVE_PLUGIN true) + FOREACH(_plugin ${PLUGIN_LIST}) + IF(_plugin STREQUAL "") + CONTINUE() + ENDIF() + IF(${SUBMODULE_PATH} MATCHES "${_plugin}") + SET(REMOVE_PLUGIN false) + ENDIF() + ENDFOREACH() + + IF(REMOVE_PLUGIN) + LIST(APPEND SKIP_SUBMODULES "${SUBMODULE_PATH}") + ENDIF() + ENDIF() + + # Finally, loop and mark "SKIP" on match IF(SKIP_SUBMODULES) FOREACH(_skip ${SKIP_SUBMODULES}) - IF(${SUBMODULE_PATH} MATCHES ${_skip}) - MESSAGE("-- Skipping ${SUBMODULE_PATH} matches \"${_skip}\"") + IF("${SUBMODULE_PATH}" MATCHES "${_skip}") + MESSAGE("-- Skipping ${SUBMODULE_PATH} matches \"${_skip}\" (absent in PLUGIN_LIST)") SET(SKIP true) + BREAK() ENDIF() ENDFOREACH() ENDIF() - IF(NOT SKIP) - LIST(INSERT SUBMODULE_LIST ${SUBMODULE_INDEX} ${SUBMODULE_PATH}) - LIST(INSERT SUBMODULE_URL_LIST ${SUBMODULE_INDEX} ${SUBMODULE_URL}) - ENDIF() - LIST(REMOVE_ITEM SUBMODULE_LIST ${_part}) - LIST(REMOVE_ITEM SUBMODULE_URL_LIST ${_url}) -ENDFOREACH() + IF(NOT SKIP) + LIST(APPEND SUBMODULE_LIST "${SUBMODULE_PATH}") + LIST(APPEND SUBMODULE_URL "${SUBMODULE_URL}") + ENDIF() +ENDFOREACH() # Once called, status is stored in GIT_RESULT respectively. # Note: Git likes to write to stderr. Don't assume stderr is error; Check GIT_RESULT instead. -MACRO(GIT_SUBMODULE SUBMODULE_PATH FORCE_DEINIT FORCE_REMOTE) +MACRO(GIT_SUBMODULE SUBMODULE_PATH FORCE_DEINIT FORCE_REMOTE FULL_CLONE) FIND_PACKAGE(Git REQUIRED) # Handle missing commits SET(FORCE_REMOTE_FLAG "${FORCE_REMOTE}") + SET(FULL_CLONE_FLAG "${FULL_CLONE}") IF(FORCE_REMOTE_FLAG) MESSAGE("-- Adding remote submodulefix to ${SUBMODULE_PATH}") EXECUTE_PROCESS( - COMMAND ${GIT_EXECUTABLE} remote rm submodulefix - COMMAND ${GIT_EXECUTABLE} remote add submodulefix ${FORCE_REMOTE} - COMMAND ${GIT_EXECUTABLE} fetch submodulefix - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/${SUBMODULE_PATH} + COMMAND "${GIT_EXECUTABLE}" remote rm submodulefix + COMMAND "${GIT_EXECUTABLE}" remote add submodulefix ${FORCE_REMOTE} + COMMAND "${GIT_EXECUTABLE}" fetch submodulefix + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/${SUBMODULE_PATH}" OUTPUT_QUIET ERROR_QUIET ) # Recurse - GIT_SUBMODULE(${SUBMODULE_PATH} false false) + GIT_SUBMODULE(${SUBMODULE_PATH} false false ${FULL_CLONE_FLAG}) ELSEIF(${FORCE_DEINIT}) MESSAGE("-- Resetting ${SUBMODULE_PATH}") EXECUTE_PROCESS( - COMMAND ${GIT_EXECUTABLE} submodule deinit -f ${CMAKE_SOURCE_DIR}/${SUBMODULE_PATH} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND "${GIT_EXECUTABLE}" submodule deinit -f "${CMAKE_SOURCE_DIR}/${SUBMODULE_PATH}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_QUIET ) # Recurse - GIT_SUBMODULE(${SUBMODULE_PATH} false false) + GIT_SUBMODULE(${SUBMODULE_PATH} false false ${FULL_CLONE_FLAG}) ELSE() # Try to use the depth switch - SET(DEPTH_CMD "") + IF(NO_SHALLOW_CLONE OR GIT_VERSION_STRING VERSION_LESS "1.8.4") + # Shallow submodules were introduced in 1.8.4 MESSAGE("-- Fetching ${SUBMODULE_PATH}") - IF(DEPTH_VALUE) - SET(DEPTH_CMD "--depth" ) + SET(DEPTH_CMD "") + SET(DEPTH_VAL "") + ELSEIF(FULL_CLONE_FLAG) + # Depth doesn't revert easily... It should be "--no-recommend-shallow" + # but it's ignored by nested submodules, use the highest value instead. + MESSAGE("-- Fetching ${SUBMODULE_PATH}") + SET(DEPTH_CMD "--depth") + SET(DEPTH_VAL "2147483647") + ELSE() MESSAGE("-- Fetching ${SUBMODULE_PATH} @ --depth ${DEPTH_VALUE}") + SET(DEPTH_CMD "--depth") + SET(DEPTH_VAL "${DEPTH_VALUE}") ENDIF() EXECUTE_PROCESS( - COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive ${DEPTH_CMD} ${DEPTH_VALUE} ${CMAKE_SOURCE_DIR}/${SUBMODULE_PATH} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND "${GIT_EXECUTABLE}" submodule update --init --recursive ${DEPTH_CMD} ${DEPTH_VAL} "${CMAKE_SOURCE_DIR}/${SUBMODULE_PATH}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE GIT_RESULT OUTPUT_VARIABLE GIT_STDOUT ERROR_VARIABLE GIT_STDERR @@ -124,7 +162,7 @@ SET(RETRY_PHRASES "Failed to recurse;cannot create directory;already exists;${MI # Attempt to do lazy clone FOREACH(_submodule ${SUBMODULE_LIST}) - STRING(REPLACE "/" ";" PATH_PARTS ${_submodule}) + STRING(REPLACE "/" ";" PATH_PARTS "${_submodule}") LIST(REVERSE PATH_PARTS) LIST(GET PATH_PARTS 0 SUBMODULE_NAME) @@ -138,7 +176,7 @@ FOREACH(_submodule ${SUBMODULE_LIST}) ENDIF() ENDFOREACH() IF(NOT CRUMB_FOUND) - GIT_SUBMODULE(${_submodule} false false) + GIT_SUBMODULE("${_submodule}" false false false) SET(COUNTED 0) SET(COUNTING "") @@ -154,25 +192,22 @@ FOREACH(_submodule ${SUBMODULE_LIST}) ENDIF() ENDFOREACH() FOREACH(_phrase ${RETRY_PHRASES}) - IF(${MISSING_COMMIT}) + IF(${MISSING_COMMIT} AND COUNTED LESS 2) LIST(FIND SUBMODULE_LIST ${_submodule} SUBMODULE_INDEX) LIST(GET SUBMODULE_URL_LIST ${SUBMODULE_INDEX} SUBMODULE_URL) MESSAGE("-- Retrying ${_submodule} using 'remote add submodulefix' (attempt ${COUNTED} of ${MAX_ATTEMPTS})...") - GIT_SUBMODULE(${_submodule} false "${SUBMODULE_URL}") + GIT_SUBMODULE("${_submodule}" false "${SUBMODULE_URL}" false) BREAK() ELSEIF("${GIT_MESSAGE}" MATCHES "${_phrase}") MESSAGE("-- Retrying ${_submodule} using 'deinit' (attempt ${COUNTED} of ${MAX_ATTEMPTS})...") - - # Shallow submodules were introduced in 1.8.4 - # Shallow commits can fail to clone from non-default branches, only try once - IF(GIT_VERSION_STRING VERSION_GREATER "1.8.3" AND COUNTED LESS 2) - # Try a shallow submodule clone + IF(COUNTED LESS 2) + SET(FULL_CLONE false) ELSE() - UNSET(DEPTH_VALUE) + SET(FULL_CLONE true) ENDIF() - GIT_SUBMODULE(${_submodule} true false) + GIT_SUBMODULE("${_submodule}" true false ${FULL_CLONE}) BREAK() ENDIF() ENDFOREACH() diff --git a/cmake/modules/PluginList.cmake b/cmake/modules/PluginList.cmake new file mode 100644 index 000000000..c82bba329 --- /dev/null +++ b/cmake/modules/PluginList.cmake @@ -0,0 +1,104 @@ +# Provides a fast mechanism for filtering the plugins used at build-time +SET(PLUGIN_LIST "" CACHE STRING "List of plug-ins to build") +STRING(REPLACE " " ";" PLUGIN_LIST "${PLUGIN_LIST}") +OPTION(LMMS_MINIMAL "Build a minimal list of plug-ins" OFF) +OPTION(LIST_PLUGINS "Lists the available plugins for building" OFF) + +SET(MINIMAL_LIST + audio_file_processor + kicker + triple_oscillator +) + +IF(LMMS_MINIMAL) + IF("${PLUGIN_LIST}" STREQUAL "") + STRING(REPLACE ";" " " MINIMAL_LIST_STRING "${MINIMAL_LIST}") + MESSAGE( +"-- Using minimal plug-ins: ${MINIMAL_LIST_STRING}\n" +" Note: You can specify specific plug-ins using -DPLUGIN_LIST=\"foo bar\"" + ) + ENDIF() + SET(PLUGIN_LIST ${MINIMAL_LIST} ${PLUGIN_LIST}) +ENDIF() + +SET(LMMS_PLUGIN_LIST + ${MINIMAL_LIST} + Amplifier + BassBooster + bit_invader + Bitcrush + carlabase + carlapatchbay + carlarack + CrossoverEQ + Delay + DualFilter + dynamics_processor + Eq + Flanger + HydrogenImport + ladspa_browser + LadspaEffect + lb302 + MidiImport + MidiExport + MultitapEcho + monstro + nes + OpulenZ + organic + FreeBoy + patman + peak_controller_effect + GigPlayer + ReverbSC + sf2_player + sfxr + sid + SpectrumAnalyzer + stereo_enhancer + stereo_matrix + stk + vst_base + vestige + VstEffect + watsyn + waveshaper + vibed + Xpressive + zynaddsubfx +) + +IF("${PLUGIN_LIST}" STREQUAL "") + SET(PLUGIN_LIST ${LMMS_PLUGIN_LIST}) +ENDIF() + +MACRO(LIST_ALL_PLUGINS) + MESSAGE("\n\nAll possible -DPLUGIN_LIST values") + MESSAGE("\n KEYWORD:") + MESSAGE(" -DLMMS_MINIMAL=True") + FOREACH(item IN LISTS MINIMAL_LIST) + MESSAGE(" ${item}") + ENDFOREACH() + MESSAGE("\n NAME:") + FOREACH(item IN LISTS LMMS_PLUGIN_LIST) + MESSAGE(" ${item}") + ENDFOREACH() + MESSAGE("\nNote: This value also impacts the fetching of git submodules.\n") + MESSAGE(FATAL_ERROR "Information was requested, aborting build!") +ENDMACRO() + +IF(LIST_PLUGINS) + UNSET(LIST_PLUGINS CACHE) + LIST_ALL_PLUGINS() +ENDIF() + +IF(MSVC) + SET(MSVC_INCOMPATIBLE_PLUGINS + LadspaEffect + zynaddsubfx + ) + message(WARNING "Compiling with MSVC. The following plugins are not available: ${MSVC_INCOMPATIBLE_PLUGINS}") + LIST(REMOVE_ITEM PLUGIN_LIST ${MSVC_INCOMPATIBLE_PLUGINS}) +ENDIF() + diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4f139f8b3..a4e56921f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -14,89 +14,7 @@ INCLUDE_DIRECTORIES( "${CMAKE_BINARY_DIR}/src" ) -SET(PLUGIN_LIST "" CACHE STRING "List of plug-ins to build") - -STRING(REPLACE " " ";" PLUGIN_LIST "${PLUGIN_LIST}") - -OPTION(LMMS_MINIMAL "Build a minimal list of plug-ins" OFF) - -SET(MINIMAL_LIST - audio_file_processor - kicker - triple_oscillator -) - -IF(LMMS_MINIMAL) - IF("${PLUGIN_LIST}" STREQUAL "") - STRING(REPLACE ";" " " MINIMAL_LIST_STRING "${MINIMAL_LIST}") - MESSAGE( -"-- Using minimal plug-ins: ${MINIMAL_LIST_STRING}\n" -" Note: You can specify specific plug-ins using -DPLUGIN_LIST=\"foo bar\"" - ) - ENDIF() - SET(PLUGIN_LIST ${MINIMAL_LIST} ${PLUGIN_LIST}) -ENDIF() - -IF("${PLUGIN_LIST}" STREQUAL "") - SET(PLUGIN_LIST - ${MINIMAL_LIST} - Amplifier - BassBooster - bit_invader - Bitcrush - carlabase - carlapatchbay - carlarack - CrossoverEQ - Delay - DualFilter - dynamics_processor - Eq - Flanger - HydrogenImport - ladspa_browser - LadspaEffect - lb302 - MidiImport - MidiExport - MultitapEcho - monstro - nes - OpulenZ - organic - FreeBoy - patman - peak_controller_effect - GigPlayer - ReverbSC - sf2_player - sfxr - sid - SpectrumAnalyzer - stereo_enhancer - stereo_matrix - stk - vst_base - vestige - VstEffect - watsyn - waveshaper - vibed - Xpressive - zynaddsubfx - ) - -ENDIF("${PLUGIN_LIST}" STREQUAL "") - -IF(MSVC) - SET(MSVC_INCOMPATIBLE_PLUGINS - LadspaEffect - zynaddsubfx - ) - message(WARNING "Compiling with MSVC. The following plugins are not available: ${MSVC_INCOMPATIBLE_PLUGINS}") - LIST(REMOVE_ITEM PLUGIN_LIST ${MSVC_INCOMPATIBLE_PLUGINS}) -ENDIF() - +# See cmake/modules/PluginList.cmake FOREACH(PLUGIN ${PLUGIN_LIST}) ADD_SUBDIRECTORY(${PLUGIN}) ENDFOREACH() From da09b2a941a0067d6b6a59dfd0be043134efa780 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sun, 6 Oct 2019 01:05:53 -0400 Subject: [PATCH 039/160] CheckSubmodules: Workaround submodules failing (#5222) Workaround git's inability to recover from a shallow clone --- cmake/modules/CheckSubmodules.cmake | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmake/modules/CheckSubmodules.cmake b/cmake/modules/CheckSubmodules.cmake index d2475826b..32a2f9951 100644 --- a/cmake/modules/CheckSubmodules.cmake +++ b/cmake/modules/CheckSubmodules.cmake @@ -123,8 +123,10 @@ MACRO(GIT_SUBMODULE SUBMODULE_PATH FORCE_DEINIT FORCE_REMOTE FULL_CLONE) WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_QUIET ) + MESSAGE("-- Deleting ${CMAKE_SOURCE_DIR}/.git/${SUBMODULE_PATH}") + FILE(REMOVE_RECURSE "${CMAKE_SOURCE_DIR}/.git/modules/${SUBMODULE_PATH}") # Recurse - GIT_SUBMODULE(${SUBMODULE_PATH} false false ${FULL_CLONE_FLAG}) + GIT_SUBMODULE(${SUBMODULE_PATH} false false true) ELSE() # Try to use the depth switch IF(NO_SHALLOW_CLONE OR GIT_VERSION_STRING VERSION_LESS "1.8.4") @@ -179,11 +181,9 @@ FOREACH(_submodule ${SUBMODULE_LIST}) GIT_SUBMODULE("${_submodule}" false false false) SET(COUNTED 0) - SET(COUNTING "") # Handle edge-cases where submodule didn't clone properly or re-uses a non-empty directory WHILE(NOT GIT_RESULT EQUAL 0 AND COUNTED LESS MAX_ATTEMPTS) - LIST(APPEND COUNTING "x") - LIST(LENGTH COUNTING COUNTED) + MATH(EXPR COUNTED "${COUNTED}+1") SET(MISSING_COMMIT false) FOREACH(_phrase ${MISSING_COMMIT_PHRASES}) IF("${GIT_MESSAGE}" MATCHES "${_phrase}") From 16390f61a51c2134d05e4029ca809cf528ce769f Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 7 Oct 2019 08:01:48 +0900 Subject: [PATCH 040/160] Work around a winegcc bug of Wine >= 4.14 (#5210) See https://bugs.winehq.org/show_bug.cgi?id=47710 for details --- plugins/vst_base/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 84920d9be..441b961f4 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -54,6 +54,8 @@ SET(WINE_CXX_ARGS -I${WINE_INCLUDE_BASE_DIR} -I${WINE_INCLUDE_DIR}/windows -L${WINE_LIBRARY_DIR} + # Work around https://bugs.winehq.org/show_bug.cgi?id=47710 + -D__WIDL_objidl_generated_name_0000000C="" ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp -std=c++0x -mwindows -lpthread -lole32 ${EXTRA_FLAGS} -fno-omit-frame-pointer From 2df4fff1c0f83f96e64cd8b4c6af30de2aba9016 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 7 Oct 2019 08:02:03 +0900 Subject: [PATCH 041/160] Work around a winegcc bug of Wine >= 4.14 (#5211) See https://bugs.winehq.org/show_bug.cgi?id=47710 for details --- cmake/modules/winegcc_wrapper.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/modules/winegcc_wrapper.in b/cmake/modules/winegcc_wrapper.in index d32aec664..7677e4c37 100755 --- a/cmake/modules/winegcc_wrapper.in +++ b/cmake/modules/winegcc_wrapper.in @@ -58,6 +58,9 @@ if [ "$win64" = true ] && [ "$no_link" != true ]; then extra_args="$extra_args @WINE_64_FLAGS@" fi +# Work around https://bugs.winehq.org/show_bug.cgi?id=47710 +extra_args="$extra_args -D__WIDL_objidl_generated_name_0000000C=" + # Run winegcc export WINEBUILD=@WINE_BUILD@ @WINE_CXX@ $extra_args $args From 770e8cc777406d42aab2fafe079b9104852e369b Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Tue, 1 Oct 2019 12:12:25 +0300 Subject: [PATCH 042/160] CircleCI: Use xenial for linux.gcc builds AppImage will fail otherwise. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 99b987f86..981034ffb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -101,7 +101,7 @@ jobs: - *save_cache linux.gcc: docker: - - image: lmmsci/linux.gcc:18.04 + - image: lmmsci/linux.gcc:xenial environment: <<: *common_environment steps: From 45cb7f21c5246cd2ca9bf895b86653fc1eefaff5 Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Tue, 1 Oct 2019 22:27:36 +0300 Subject: [PATCH 043/160] linuxdeployqt: drop -unsupported-allow-new-glibc --- cmake/linux/package_linux.sh.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index b838237b7..9f233d401 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -175,10 +175,9 @@ executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/pitch_scale # Bundle both qt and non-qt dependencies into appimage format echo -e "\nBundling and relinking system dependencies..." echo -e ">>>>> linuxdeployqt" > "$LOGFILE" -# FIXME: -unsupported-allow-new-glibc may result in an AppImage which is unusable on old systems. # shellcheck disable=SC2086 -"$LINUXDEPLOYQT" "$DESKTOPFILE" $executables -unsupported-allow-new-glibc -bundle-non-qt-libs -verbose=$VERBOSITY $STRIP >> "$LOGFILE" 2>&1 +"$LINUXDEPLOYQT" "$DESKTOPFILE" $executables -bundle-non-qt-libs -verbose=$VERBOSITY $STRIP >> "$LOGFILE" 2>&1 success "Bundled and relinked dependencies" # Link to original location so lmms can find them From 5aa87886c9c098b1e37c1eeb7e9383a1c8e1a069 Mon Sep 17 00:00:00 2001 From: Steffen Baranowsky Date: Mon, 7 Oct 2019 18:13:44 +0200 Subject: [PATCH 044/160] Rubberband fix for selecting large area in Songeditor (#5003) --- include/SongEditor.h | 17 +++ include/Track.h | 12 ++- include/TrackContainerView.h | 14 +-- src/gui/TrackContainerView.cpp | 59 ++-------- src/gui/editors/SongEditor.cpp | 189 +++++++++++++++++++++++++++++++-- src/gui/widgets/Rubberband.cpp | 12 --- 6 files changed, 218 insertions(+), 85 deletions(-) diff --git a/include/SongEditor.h b/include/SongEditor.h index e03c63280..de149e9a4 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -75,6 +75,9 @@ public: public slots: void scrolled( int new_pos ); + void selectRegionFromPixels(int xStart, int xEnd); + void stopSelectRegion(); + void updateRubberband(); void setEditMode( EditMode mode ); void setEditModeDraw(); @@ -85,6 +88,9 @@ public slots: protected: virtual void closeEvent( QCloseEvent * ce ); + virtual void mousePressEvent(QMouseEvent * me); + virtual void mouseMoveEvent(QMouseEvent * me); + virtual void mouseReleaseEvent(QMouseEvent * me); private slots: void setHighQuality( bool ); @@ -109,6 +115,9 @@ private: virtual bool allowRubberband() const; + int trackIndexFromSelectionPoint(int yPos); + int indexOfTrackView(const TrackView* tv); + Song * m_song; @@ -135,6 +144,14 @@ private: bool m_smoothScroll; EditMode m_mode; + QPoint m_origin; + QPoint m_scrollPos; + QPoint m_mousePos; + int m_rubberBandStartTrackview; + MidiTime m_rubberbandStartMidipos; + int m_currentZoomingValue; + int m_trackHeadWidth; + bool m_selectRegion; friend class SongEditorWindow; diff --git a/include/Track.h b/include/Track.h index f548168b7..f3b6c5e4c 100644 --- a/include/Track.h +++ b/include/Track.h @@ -210,6 +210,12 @@ public: { return m_tco; } + + inline TrackView * getTrackView() + { + return m_trackView; + } + // qproperty access func QColor mutedColor() const; QColor mutedBackgroundColor() const; @@ -229,7 +235,7 @@ public: // access needsUpdate member variable bool needsUpdate(); void setNeedsUpdate( bool b ); - + public slots: virtual bool close(); void cut(); @@ -256,10 +262,6 @@ protected: float pixelsPerTact(); - inline TrackView * getTrackView() - { - return m_trackView; - } DataFile createTCODataFiles(const QVector & tcos) const; diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index bf6ada409..e0c9d11f7 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -133,27 +133,21 @@ public slots: virtual void dropEvent( QDropEvent * _de ); virtual void dragEnterEvent( QDragEnterEvent * _dee ); - /// - /// \brief selectRegionFromPixels - /// \param x - /// \param y - /// Use the rubber band to select TCO from all tracks using x, y pixels - void selectRegionFromPixels(int xStart, int xEnd); /// /// \brief stopRubberBand /// Removes the rubber band from display when finished with. void stopRubberBand(); + protected: static const int DEFAULT_PIXELS_PER_TACT = 16; - virtual void mousePressEvent( QMouseEvent * _me ); - virtual void mouseMoveEvent( QMouseEvent * _me ); - virtual void mouseReleaseEvent( QMouseEvent * _me ); + virtual void resizeEvent( QResizeEvent * ); MidiTime m_currentPosition; + RubberBand *rubberBand() const; private: @@ -187,7 +181,7 @@ private: float m_ppt; RubberBand * m_rubberBand; - QPoint m_origin; + signals: diff --git a/src/gui/TrackContainerView.cpp b/src/gui/TrackContainerView.cpp index 4c7498ab0..255fb1a02 100644 --- a/src/gui/TrackContainerView.cpp +++ b/src/gui/TrackContainerView.cpp @@ -55,8 +55,7 @@ TrackContainerView::TrackContainerView( TrackContainer * _tc ) : m_trackViews(), m_scrollArea( new scrollArea( this ) ), m_ppt( DEFAULT_PIXELS_PER_TACT ), - m_rubberBand( new RubberBand( m_scrollArea ) ), - m_origin() + m_rubberBand( new RubberBand( m_scrollArea ) ) { m_tc->setHook( this ); //keeps the direction of the widget, undepended on the locale @@ -346,12 +345,8 @@ void TrackContainerView::dragEnterEvent( QDragEnterEvent * _dee ) arg( Track::SampleTrack ) ); } -void TrackContainerView::selectRegionFromPixels(int xStart, int xEnd) -{ - m_rubberBand->setEnabled( true ); - m_rubberBand->show(); - m_rubberBand->setGeometry( min( xStart, xEnd ), 0, max( xStart, xEnd ) - min( xStart, xEnd ), std::numeric_limits::max() ); -} + + void TrackContainerView::stopRubberBand() { @@ -427,46 +422,6 @@ void TrackContainerView::dropEvent( QDropEvent * _de ) -void TrackContainerView::mousePressEvent( QMouseEvent * _me ) -{ - if( allowRubberband() == true ) - { - m_origin = m_scrollArea->mapFromParent( _me->pos() ); - m_rubberBand->setEnabled( true ); - m_rubberBand->setGeometry( QRect( m_origin, QSize() ) ); - m_rubberBand->show(); - } - QWidget::mousePressEvent( _me ); -} - - - - -void TrackContainerView::mouseMoveEvent( QMouseEvent * _me ) -{ - if( rubberBandActive() == true ) - { - m_rubberBand->setGeometry( QRect( m_origin, - m_scrollArea->mapFromParent( _me->pos() ) ). - normalized() ); - } - QWidget::mouseMoveEvent( _me ); -} - - - - -void TrackContainerView::mouseReleaseEvent( QMouseEvent * _me ) -{ - m_rubberBand->hide(); - m_rubberBand->setEnabled( false ); - QWidget::mouseReleaseEvent( _me ); -} - - - - - void TrackContainerView::resizeEvent( QResizeEvent * _re ) { realignTracks(); @@ -476,6 +431,14 @@ void TrackContainerView::resizeEvent( QResizeEvent * _re ) +RubberBand *TrackContainerView::rubberBand() const +{ + return m_rubberBand; +} + + + + TrackContainerView::scrollArea::scrollArea( TrackContainerView * _parent ) : QScrollArea( _parent ), m_trackContainerView( _parent ) diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 286b01f15..31cb142c0 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -51,8 +51,7 @@ #include "TimeDisplayWidget.h" #include "AudioDevice.h" #include "PianoRoll.h" - - +#include "Track.h" positionLine::positionLine( QWidget * parent ) : QWidget( parent ) @@ -80,15 +79,21 @@ SongEditor::SongEditor( Song * song ) : m_zoomingModel(new ComboBoxModel()), m_scrollBack( false ), m_smoothScroll( ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt() ), - m_mode(DrawMode) + m_mode(DrawMode), + m_origin(), + m_scrollPos(), + m_mousePos(), + m_rubberBandStartTrackview(0), + m_rubberbandStartMidipos(0), + m_currentZoomingValue(m_zoomingModel->value()), + m_trackHeadWidth(ConfigManager::inst()->value("ui", "compacttrackbuttons").toInt()==1 + ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT + : DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH), + m_selectRegion(false) { m_zoomingModel->setParent(this); // create time-line - int widgetTotal = ConfigManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt()==1 ? - DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT : - DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH; - m_timeLine = new TimeLineWidget( widgetTotal, 32, + m_timeLine = new TimeLineWidget( m_trackHeadWidth, 32, pixelsPerTact(), m_song->m_playPos[Song::Mode_PlaySong], m_currentPosition, this ); @@ -239,6 +244,10 @@ SongEditor::SongEditor( Song * song ) : this, SLOT( scrolled( int ) ) ); connect( m_song, SIGNAL( lengthChanged( int ) ), this, SLOT( updateScrollBar( int ) ) ); + connect(m_leftRightScroll, SIGNAL(valueChanged(int)),this, SLOT(updateRubberband())); + connect(contentWidget()->verticalScrollBar(), SIGNAL(valueChanged(int)),this, SLOT(updateRubberband())); + connect(m_timeLine, SIGNAL(selectionFinished()), this, SLOT(stopSelectRegion())); + // Set up zooming model for( float const & zoomLevel : m_zoomLevels ) @@ -293,6 +302,99 @@ void SongEditor::scrolled( int new_pos ) +void SongEditor::selectRegionFromPixels(int xStart, int xEnd) +{ + if (!m_selectRegion) + { + m_selectRegion = true; + + //deselect all tcos + for (auto &it : findChildren()) { it->setSelected(false); } + + rubberBand()->setEnabled(true); + rubberBand()->show(); + + //we save the position of scrollbars, mouse position and zooming level + m_origin = QPoint(xStart, 0); + m_scrollPos = QPoint(m_leftRightScroll->value(), contentWidget()->verticalScrollBar()->value()); + m_currentZoomingValue = zoomingModel()->value(); + + //calculate the song position where the mouse was clicked + m_rubberbandStartMidipos = MidiTime((xStart - m_trackHeadWidth) + / pixelsPerTact() * MidiTime::ticksPerTact()) + + m_currentPosition; + m_rubberBandStartTrackview = 0; + } + //the current mouse position within the borders of song editor + m_mousePos = QPoint(qMax(m_trackHeadWidth, qMin(xEnd, width())) + , std::numeric_limits::max()); + updateRubberband(); +} + + + + +void SongEditor::stopSelectRegion() +{ + m_selectRegion = false; +} + + + + +void SongEditor::updateRubberband() +{ + if (rubberBandActive()) + { + int originX = m_origin.x(); + + //take care of the zooming + if (m_currentZoomingValue != m_zoomingModel->value()) + { + originX = m_trackHeadWidth + (originX - m_trackHeadWidth) + * m_zoomLevels[m_zoomingModel->value()] / m_zoomLevels[m_currentZoomingValue]; + } + + //take care of the scrollbar position + int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerTact(); + int vs = contentWidget()->verticalScrollBar()->value() - m_scrollPos.y(); + + //the adjusted origin point + QPoint origin = QPoint(qMax(originX - hs, m_trackHeadWidth), m_origin.y() - vs); + + //paint the rubber band rect + rubberBand()->setGeometry(QRect(origin, + contentWidget()->mapFromParent(QPoint(m_mousePos.x(), m_mousePos.y())) + ).normalized()); + + //the index of the TrackView the mouse is hover + int rubberBandTrackview = trackIndexFromSelectionPoint(m_mousePos.y()); + + //the miditime the mouse is hover + MidiTime rubberbandMidipos = MidiTime((qMin(m_mousePos.x(), width()) - m_trackHeadWidth) + / pixelsPerTact() * MidiTime::ticksPerTact()) + + m_currentPosition; + + //are tcos in the rect of selection? + for (auto &it : findChildren()) + { + TrackContentObjectView * tco = dynamic_cast(it); + if (tco) + { + auto indexOfTrackView = trackViews().indexOf(tco->getTrackView()); + bool isBeetweenRubberbandViews = indexOfTrackView >= qMin(m_rubberBandStartTrackview, rubberBandTrackview) + && indexOfTrackView <= qMax(m_rubberBandStartTrackview, rubberBandTrackview); + bool isBeetweenRubberbandMidiPos = tco->getTrackContentObject()->endPosition() >= qMin(m_rubberbandStartMidipos, rubberbandMidipos) + && tco->getTrackContentObject()->startPosition() <= qMax(m_rubberbandStartMidipos, rubberbandMidipos); + it->setSelected(isBeetweenRubberbandViews && isBeetweenRubberbandMidiPos); + } + } + } +} + + + + void SongEditor::setEditMode( EditMode mode ) { m_mode = mode; @@ -394,7 +496,7 @@ void SongEditor::wheelEvent( QWheelEvent * we ) void SongEditor::closeEvent( QCloseEvent * ce ) - { +{ if( parentWidget() ) { parentWidget()->hide(); @@ -404,7 +506,53 @@ void SongEditor::closeEvent( QCloseEvent * ce ) hide(); } ce->ignore(); - } +} + + + + +void SongEditor::mousePressEvent(QMouseEvent *me) +{ + if (allowRubberband()) + { + //we save the position of scrollbars, mouse position and zooming level + m_scrollPos = QPoint(m_leftRightScroll->value(), contentWidget()->verticalScrollBar()->value()); + m_origin = contentWidget()->mapFromParent(QPoint(me->pos().x(), me->pos().y())); + m_currentZoomingValue = zoomingModel()->value(); + + //paint the rubberband + rubberBand()->setEnabled(true); + rubberBand()->setGeometry(QRect(m_origin, QSize())); + rubberBand()->show(); + + //the trackView(index) and the miditime where the mouse was clicked + m_rubberBandStartTrackview = trackIndexFromSelectionPoint(me->y()); + m_rubberbandStartMidipos = MidiTime((me->x() - m_trackHeadWidth) + / pixelsPerTact() * MidiTime::ticksPerTact()) + + m_currentPosition; + } + QWidget::mousePressEvent(me); +} + + + + +void SongEditor::mouseMoveEvent(QMouseEvent *me) +{ + m_mousePos = me->pos(); + updateRubberband(); + QWidget::mouseMoveEvent(me); +} + + + + +void SongEditor::mouseReleaseEvent(QMouseEvent *me) +{ + rubberBand()->hide(); + rubberBand()->setEnabled(false); + QWidget::mouseReleaseEvent(me); +} @@ -602,6 +750,7 @@ void SongEditor::zoomingChanged() m_song->m_playPos[Song::Mode_PlaySong].m_timeLine-> setPixelsPerTact( pixelsPerTact() ); realignTracks(); + updateRubberband(); } @@ -615,6 +764,26 @@ bool SongEditor::allowRubberband() const +int SongEditor::trackIndexFromSelectionPoint(int yPos) +{ + const TrackView * tv = trackViewAt(yPos - m_timeLine->height()); + return tv ? indexOfTrackView(tv) + : yPos < m_timeLine->height() ? 0 + : trackViews().count(); +} + + + + +int SongEditor::indexOfTrackView(const TrackView *tv) +{ + return static_cast(std::distance(trackViews().begin(), + std::find(trackViews().begin(), trackViews().end(), tv))); +} + + + + ComboBoxModel *SongEditor::zoomingModel() const { return m_zoomingModel; diff --git a/src/gui/widgets/Rubberband.cpp b/src/gui/widgets/Rubberband.cpp index 6bf702edc..0a4e891b5 100644 --- a/src/gui/widgets/Rubberband.cpp +++ b/src/gui/widgets/Rubberband.cpp @@ -66,18 +66,6 @@ QVector RubberBand::selectedObjects() const void RubberBand::resizeEvent( QResizeEvent * _re ) { QRubberBand::resizeEvent( _re ); - if( isEnabled() ) - { - QVector so = selectableObjects(); - for( QVector::iterator it = so.begin(); - it != so.end(); ++it ) - { - ( *it )->setSelected( QRect( pos(), size() ).intersects( - QRect( ( *it )->mapTo( parentWidget(), - QPoint() ), - ( *it )->size() ) ) ); - } - } } From e1d311730b5de201c7b02360d5caa7e22b204af0 Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Mon, 7 Oct 2019 14:33:11 -0500 Subject: [PATCH 045/160] Fix regression introduced in #5170 (#5224) --- src/gui/editors/AutomationEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 7583501c7..bb00a1bd1 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -1484,7 +1484,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) m_leftRightScroll->setPageStep( l ); } - if( validPattern() && GuiApplication::instance()->automationEditor()->hasFocus() ) + if(validPattern() && GuiApplication::instance()->automationEditor()->m_editor->hasFocus()) { drawCross( p ); From 91a38a922d349e6f0b9beabfe25f1f5b2d0fc0a1 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 10 Oct 2019 10:06:14 +0900 Subject: [PATCH 046/160] Add back '-Wl,-E' (#5233) --- src/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 10ce72ae6..2fa72a401 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -198,8 +198,12 @@ IF(LMMS_BUILD_WIN32) ) ENDIF() ELSE() + IF(NOT LMMS_BUILD_APPLE) + SET_TARGET_PROPERTIES(lmms PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,-E") + ENDIF(NOT LMMS_BUILD_APPLE) + INSTALL(FILES "${CMAKE_BINARY_DIR}/lmms.1.gz" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man1/" PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) ENDIF() -INSTALL(TARGETS lmms RUNTIME DESTINATION "${BIN_DIR}") \ No newline at end of file +INSTALL(TARGETS lmms RUNTIME DESTINATION "${BIN_DIR}") From d7a78fe1212af26bdbfa38802bc3038eded0814f Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 10 Oct 2019 14:22:42 +0900 Subject: [PATCH 047/160] Specify timeout for transfer.sh uploads Prevents "No output has been received in the last 10m0s" errors on Travis CI. --- .travis/script.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis/script.sh b/.travis/script.sh index 70391a762..b723f5dd0 100755 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -42,5 +42,7 @@ else fi echo "Uploading $PACKAGE to transfer.sh..." - curl --upload-file "$PACKAGE" "https://transfer.sh/$PACKAGE" || true + # Limit the connection time to 3 minutes and total upload time to 5 minutes + # Otherwise the build may hang + curl --connect-timeout 180 --max-time 300 --upload-file "$PACKAGE" "https://transfer.sh/$PACKAGE" || true fi From e321dff733061597869821de6c37c1683d6f06c4 Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Thu, 10 Oct 2019 19:01:17 +0200 Subject: [PATCH 048/160] Translation Fixes (Continuation of #4482) (#5185) Fix some formatting issues with some translations. * Russian * Polish * Swedish * Ukrainian --- data/locale/pl.ts | 2 +- data/locale/ru.ts | 4 ++-- data/locale/sv.ts | 4 ++-- data/locale/uk.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/locale/pl.ts b/data/locale/pl.ts index 2e8f989ce..4cbc95441 100644 --- a/data/locale/pl.ts +++ b/data/locale/pl.ts @@ -3002,7 +3002,7 @@ You can remove and move FX channels in the context menu, which is accessed by ri VELOCITY - PRĘDKOŚĆ UDERZENIA + PRĘDKOŚĆ ENABLE MIDI OUTPUT diff --git a/data/locale/ru.ts b/data/locale/ru.ts index 9946e5cfd..1d1c2d73e 100644 --- a/data/locale/ru.ts +++ b/data/locale/ru.ts @@ -1664,7 +1664,7 @@ Oe Ai <oeai/at/symbiants/dot/com> W/D - НАСЫЩ + @@ -1679,7 +1679,7 @@ Oe Ai <oeai/at/symbiants/dot/com> DECAY - ЗАТУХАНИЕ + diff --git a/data/locale/sv.ts b/data/locale/sv.ts index 7b2557cfd..6e3e4f9af 100644 --- a/data/locale/sv.ts +++ b/data/locale/sv.ts @@ -1658,7 +1658,7 @@ If you're interested in translating LMMS in another language or want to imp W/D - B/T + @@ -1673,7 +1673,7 @@ If you're interested in translating LMMS in another language or want to imp DECAY - FÖRFALL + diff --git a/data/locale/uk.ts b/data/locale/uk.ts index 39403714f..fa0eede63 100644 --- a/data/locale/uk.ts +++ b/data/locale/uk.ts @@ -1678,7 +1678,7 @@ If you're interested in translating LMMS in another language or want to imp DECAY - ЗГАСАННЯ + From 6dee6a4418b7e541b548170fb32d4f15aab8ef9e Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Sat, 12 Oct 2019 17:41:30 +0200 Subject: [PATCH 049/160] Show icon on 'Turn off all notes' button (#5237) Backporting fix from 2815da28057788ba420d8e128958698a8c3d2d6d by @karmux --- plugins/vestige/vestige.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index fc07e46d5..143c9dc37 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -607,7 +607,7 @@ VestigeInstrumentView::VestigeInstrumentView( Instrument * _instrument, QPushButton * note_off_all_btn = new QPushButton( tr( "Turn off all " "notes" ), this ); note_off_all_btn->setGeometry( 20, 160, 200, 24 ); - note_off_all_btn->setIcon( embed::getIconPixmap( "state_stop" ) ); + note_off_all_btn->setIcon( embed::getIconPixmap( "stop" ) ); note_off_all_btn->setFont( pointSize<8>( note_off_all_btn->font() ) ); connect( note_off_all_btn, SIGNAL( clicked() ), this, SLOT( noteOffAll() ) ); From 95c46a805d4673b6ebbaf7a549b278fab478c249 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 15 Oct 2019 11:18:46 +0900 Subject: [PATCH 050/160] RemoteVstPlugin: fix crashes when failed to open a file (#5235) --- plugins/vst_base/RemoteVstPlugin.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 4a147f7c9..d12ccd88a 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -718,6 +718,7 @@ void RemoteVstPlugin::init( const std::string & _plugin_file ) static void close_check( FILE* fp ) { + if (!fp) {return;} if( fclose( fp ) ) { perror( "close" ); @@ -1115,6 +1116,12 @@ void RemoteVstPlugin::saveChunkToFile( const std::string & _file ) if( len > 0 ) { FILE* fp = F_OPEN_UTF8( _file, "wb" ); + if (!fp) + { + fprintf( stderr, + "Error opening file for saving chunk.\n" ); + return; + } if ( fwrite( chunk, 1, len, fp ) != len ) { fprintf( stderr, @@ -1280,6 +1287,12 @@ void RemoteVstPlugin::savePreset( const std::string & _file ) pBank->numPrograms = endian_swap( uIntToFile ); FILE * stream = F_OPEN_UTF8( _file, "w" ); + if (!stream) + { + fprintf( stderr, + "Error opening file for saving preset.\n" ); + return; + } fwrite ( pBank, 1, 28, stream ); fwrite ( progName, 1, isPreset ? 28 : 128, stream ); if ( chunky ) { @@ -1332,6 +1345,12 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) unsigned int len = 0; sBank * pBank = (sBank*) new char[ sizeof( sBank ) ]; FILE * stream = F_OPEN_UTF8( _file, "r" ); + if (!stream) + { + fprintf( stderr, + "Error opening file for loading preset.\n" ); + return; + } if ( fread ( pBank, 1, 56, stream ) != 56 ) { fprintf( stderr, "Error loading preset file.\n" ); @@ -1433,6 +1452,12 @@ void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len ) char * chunk = new char[_len]; FILE* fp = F_OPEN_UTF8( _file, "rb" ); + if (!fp) + { + fprintf( stderr, + "Error opening file for loading chunk.\n" ); + return; + } if ( fread( chunk, 1, _len, fp ) != _len ) { fprintf( stderr, "Error loading chunk from file.\n" ); From 170d28ffbd39b1f0406e437eaeac39a07389f525 Mon Sep 17 00:00:00 2001 From: Kevin Zander Date: Mon, 14 Oct 2019 21:22:41 -0500 Subject: [PATCH 051/160] Remove global automation from MIDI import (#5223) --- plugins/MidiImport/MidiImport.cpp | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index 1f2ab1f57..b8653fe18 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -309,10 +309,20 @@ bool MidiImport::readSMF( TrackContainer* tc ) smfMidiChannel chs[256]; MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel(); - AutomationPattern * timeSigNumeratorPat = - AutomationPattern::globalAutomationPattern( &timeSigMM.numeratorModel() ); - AutomationPattern * timeSigDenominatorPat = - AutomationPattern::globalAutomationPattern( &timeSigMM.denominatorModel() ); + AutomationTrack * nt = dynamic_cast( + Track::create(Track::AutomationTrack, Engine::getSong())); + nt->setName(tr("MIDI Time Signature Numerator")); + AutomationTrack * dt = dynamic_cast( + Track::create(Track::AutomationTrack, Engine::getSong())); + dt->setName(tr("MIDI Time Signature Denominator")); + AutomationPattern * timeSigNumeratorPat = + new AutomationPattern(nt); + timeSigNumeratorPat->setDisplayName(tr("Numerator")); + timeSigNumeratorPat->addObject(&timeSigMM.numeratorModel()); + AutomationPattern * timeSigDenominatorPat = + new AutomationPattern(dt); + timeSigDenominatorPat->setDisplayName(tr("Denominator")); + timeSigDenominatorPat->addObject(&timeSigMM.denominatorModel()); // TODO: adjust these to Time.Sig changes double beatsPerTact = 4; @@ -323,19 +333,12 @@ bool MidiImport::readSMF( TrackContainer* tc ) for( int s = 0; s < timeSigs->length(); ++s ) { Alg_time_sig timeSig = (*timeSigs)[s]; - // Initial timeSig, set song-default value - if(/* timeSig.beat == 0*/ true ) - { - // TODO set song-global default value - printf("Another timesig at %f\n", timeSig.beat); - timeSigNumeratorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.num ); - timeSigDenominatorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.den ); - } - else - { - } - + timeSigNumeratorPat->putValue(timeSig.beat * ticksPerBeat, timeSig.num); + timeSigDenominatorPat->putValue(timeSig.beat * ticksPerBeat, timeSig.den); } + // manually call otherwise the pattern shows being 1 bar + timeSigNumeratorPat->updateLength(); + timeSigDenominatorPat->updateLength(); pd.setValue( 2 ); From f1831ffdc614b44f051de6e345af134bdaf8241f Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 17 Oct 2019 08:50:12 +0900 Subject: [PATCH 052/160] Export TrackContentObject class Fix a build error introduced in #5223. --- include/Track.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Track.h b/include/Track.h index b00c50248..6c98de10e 100644 --- a/include/Track.h +++ b/include/Track.h @@ -73,7 +73,7 @@ const int DEFAULT_TRACK_HEIGHT = 32; const int TCO_BORDER_WIDTH = 2; -class TrackContentObject : public Model, public JournallingObject +class LMMS_EXPORT TrackContentObject : public Model, public JournallingObject { Q_OBJECT MM_OPERATORS From d005eec1aab1d3252c20b426ac43561ec0e75149 Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Thu, 17 Oct 2019 03:03:26 +0300 Subject: [PATCH 053/160] MSVC: Move MSVC year detection to DetectMachine.cmake. --- cmake/modules/DetectMachine.cmake | 19 ++++++++++++++++ plugins/vst_base/RemoteVstPlugin32.cmake | 28 +++++++----------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/cmake/modules/DetectMachine.cmake b/cmake/modules/DetectMachine.cmake index f3458165b..08e8745cd 100644 --- a/cmake/modules/DetectMachine.cmake +++ b/cmake/modules/DetectMachine.cmake @@ -30,6 +30,25 @@ IF(WIN32) ELSE(WIN64) SET(IS_X86 TRUE) ENDIF(WIN64) + + if(MSVC) + SET(MSVC_VER ${CMAKE_CXX_COMPILER_VERSION}) + + IF(MSVC_VER VERSION_GREATER 19.20 OR MSVC_VER VERSION_EQUAL 19.20) + SET(LMMS_MSVC_GENERATOR "Visual Studio 16 2019") + SET(LMMS_MSVC_YEAR 2019) # Qt only provides binaries for MSVC 2017, but 2019 is binary compatible + ELSEIF(MSVC_VER VERSION_GREATER 19.10 OR MSVC_VER VERSION_EQUAL 19.10) + SET(LMMS_MSVC_GENERATOR "Visual Studio 15 2017") + SET(LMMS_MSVC_YEAR 2017) + ELSEIF(MSVC_VER VERSION_GREATER 19.0 OR MSVC_VER VERSION_EQUAL 19.0) + SET(LMMS_MSVC_GENERATOR "Visual Studio 14 2015") + SET(LMMS_MSVC_YEAR 2015) + ELSE() + MESSAGE(SEND_WARNING "Can't detect MSVC version: ${MSVC_VER}") + ENDIF() + + unset(MSVC_VER) + endif() ELSE(WIN32) EXEC_PROGRAM( ${CMAKE_C_COMPILER} ARGS "-dumpmachine ${CMAKE_C_FLAGS}" OUTPUT_VARIABLE Machine ) MESSAGE("Machine: ${Machine}") diff --git a/plugins/vst_base/RemoteVstPlugin32.cmake b/plugins/vst_base/RemoteVstPlugin32.cmake index 1cac65e15..cba9a26c8 100644 --- a/plugins/vst_base/RemoteVstPlugin32.cmake +++ b/plugins/vst_base/RemoteVstPlugin32.cmake @@ -17,27 +17,15 @@ ENDMACRO() IF(LMMS_BUILD_WIN32 AND NOT LMMS_BUILD_WIN64) ADD_SUBDIRECTORY(RemoteVstPlugin) ELSEIF(LMMS_BUILD_WIN64 AND MSVC) - SET(MSVC_VER ${CMAKE_CXX_COMPILER_VERSION}) - - IF(NOT CMAKE_GENERATOR_32) - IF(MSVC_VER VERSION_GREATER 19.20 OR MSVC_VER VERSION_EQUAL 19.20) - SET(CMAKE_GENERATOR_32 "Visual Studio 16 2019") - SET(MSVC_YEAR 2017) # Qt only provides binaries for MSVC 2017, but 2019 is binary compatible - ELSEIF(MSVC_VER VERSION_GREATER 19.10 OR MSVC_VER VERSION_EQUAL 19.10) - SET(CMAKE_GENERATOR_32 "Visual Studio 15 2017") - SET(MSVC_YEAR 2017) - ELSEIF(MSVC_VER VERSION_GREATER 19.0 OR MSVC_VER VERSION_EQUAL 19.0) - SET(CMAKE_GENERATOR_32 "Visual Studio 14 2015") - SET(MSVC_YEAR 2015) - ELSE() - MESSAGE(SEND_WARNING "Can't build RemoteVstPlugin32, unknown MSVC version ${MSVC_VER} and no CMAKE_GENERATOR_32 set") - RETURN() - ENDIF() - ENDIF() - IF(NOT QT_32_PREFIX) + SET(LMMS_MSVC_YEAR_FOR_QT ${LMMS_MSVC_YEAR}) + + if(LMMS_MSVC_YEAR_FOR_QT EQUAL 2019) + SET(LMMS_MSVC_YEAR_FOR_QT 2017) # Qt only provides binaries for MSVC 2017, but 2019 is binary compatible + endif() + GET_FILENAME_COMPONENT(QT_BIN_DIR ${QT_QMAKE_EXECUTABLE} DIRECTORY) - SET(QT_32_PREFIX "${QT_BIN_DIR}/../../msvc${MSVC_YEAR}") + SET(QT_32_PREFIX "${QT_BIN_DIR}/../../msvc${LMMS_MSVC_YEAR_FOR_QT}") ENDIF() #TODO: qt5 installed using vcpkg: I don't know how to detect if the user built the x86 version of qt5 from here. At least not cleanly. @@ -48,7 +36,7 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) ExternalProject_Add(RemoteVstPlugin32 "${EXTERNALPROJECT_ARGS}" - CMAKE_GENERATOR "${CMAKE_GENERATOR_32}" + CMAKE_GENERATOR "${LMMS_MSVC_GENERATOR}" CMAKE_GENERATOR_PLATFORM Win32 #CMAKE_GENERATOR_TOOLSET "${CMAKE_GENERATOR_TOOLSET}" CMAKE_ARGS From a22b6d75750800123dd68119a66737fe52078075 Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Thu, 17 Oct 2019 03:04:18 +0300 Subject: [PATCH 054/160] NSIS: Add compiler to installer name --- cmake/nsis/CMakeLists.txt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmake/nsis/CMakeLists.txt b/cmake/nsis/CMakeLists.txt index ac628d549..3fcb4b2f3 100644 --- a/cmake/nsis/CMakeLists.txt +++ b/cmake/nsis/CMakeLists.txt @@ -1,3 +1,8 @@ +SET(WIN_PLATFORM mingw) +if(LMMS_MSVC_YEAR) + SET(WIN_PLATFORM "msvc${LMMS_MSVC_YEAR}") +endif() + SET(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/cmake/nsis/nsis_branding.bmp") IF(MSVC) STRING(REPLACE "/" "\\\\" CPACK_PACKAGE_ICON ${CPACK_PACKAGE_ICON}) @@ -15,7 +20,7 @@ SET(CPACK_NSIS_DEFINES " !include FileAssociation.nsh !include LogicLib.nsh !include WinVer.nsh") -SET(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${VERSION}-win32") +SET(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${VERSION}-${WIN_PLATFORM}-win32") SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " \\\${registerExtension} \\\"$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe\\\" \\\".mmp\\\" \\\"${PROJECT_NAME_UCASE} Project\\\" \\\${registerExtension} \\\"$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe\\\" \\\".mmpz\\\" \\\"${PROJECT_NAME_UCASE} Project (compressed)\\\" @@ -31,7 +36,7 @@ SET(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " " PARENT_SCOPE) IF(WIN64) - SET(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${VERSION}-win64") + SET(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${VERSION}-${WIN_PLATFORM}-win64") SET(CPACK_INSTALL_FIX "$PROGRAMFILES64\\\\${CPACK_PACKAGE_INSTALL_DIRECTORY}\\\\") SET(CPACK_NSIS_DEFINES " ${CPACK_NSIS_DEFINES} From 2d17bf07f3e575a1a5a5da00df57a01d0956cf3d Mon Sep 17 00:00:00 2001 From: Lost Robot <34612565+DouglasDGI@users.noreply.github.com> Date: Thu, 17 Oct 2019 01:44:55 -0600 Subject: [PATCH 055/160] Allow mid-note automation of BitInvader sample length (#4705) --- plugins/bit_invader/bit_invader.cpp | 20 ++++++++------------ plugins/bit_invader/bit_invader.h | 5 ++--- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index f8a8423ad..caa272fa7 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -64,17 +64,16 @@ Plugin::Descriptor PLUGIN_EXPORT bitinvader_plugin_descriptor = } -bSynth::bSynth( float * _shape, int _length, NotePlayHandle * _nph, bool _interpolation, +bSynth::bSynth( float * _shape, NotePlayHandle * _nph, bool _interpolation, float _factor, const sample_rate_t _sample_rate ) : sample_index( 0 ), sample_realindex( 0 ), nph( _nph ), - sample_length( _length ), sample_rate( _sample_rate ), interpolation( _interpolation) { - sample_shape = new float[sample_length]; - for (int i=0; i < _length; ++i) + sample_shape = new float[200]; + for (int i=0; i < 200; ++i) { sample_shape[i] = _shape[i] * _factor; } @@ -87,7 +86,7 @@ bSynth::~bSynth() } -sample_t bSynth::nextStringSample() +sample_t bSynth::nextStringSample( float sample_length ) { float sample_step = static_cast( sample_length / ( sample_rate / nph->frequency() ) ); @@ -140,10 +139,12 @@ sample_t bSynth::nextStringSample() bitInvader::bitInvader( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &bitinvader_plugin_descriptor ), m_sampleLength( 128, 4, 200, 1, this, tr( "Sample length" ) ), - m_graph( -1.0f, 1.0f, 128, this ), + m_graph( -1.0f, 1.0f, 200, this ), m_interpolation( false, this ), m_normalize( false, this ) { + + lengthChanged(); m_graph.setWaveToSine(); @@ -278,7 +279,6 @@ void bitInvader::playNote( NotePlayHandle * _n, _n->m_pluginData = new bSynth( const_cast( m_graph.samples() ), - m_graph.length(), _n, m_interpolation.value(), factor, Engine::mixer()->processingSampleRate() ); @@ -290,7 +290,7 @@ void bitInvader::playNote( NotePlayHandle * _n, bSynth * ps = static_cast( _n->m_pluginData ); for( fpp_t frame = offset; frame < frames + offset; ++frame ) { - const sample_t cur = ps->nextStringSample(); + const sample_t cur = ps->nextStringSample( m_graph.length() ); for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) { _working_buffer[frame][chnl] = cur; @@ -572,7 +572,3 @@ PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) } - - - - diff --git a/plugins/bit_invader/bit_invader.h b/plugins/bit_invader/bit_invader.h index 793831e4a..ae9c92cb2 100644 --- a/plugins/bit_invader/bit_invader.h +++ b/plugins/bit_invader/bit_invader.h @@ -42,12 +42,12 @@ class bSynth { MM_OPERATORS public: - bSynth( float * sample, int length, NotePlayHandle * _nph, + bSynth( float * sample, NotePlayHandle * _nph, bool _interpolation, float factor, const sample_rate_t _sample_rate ); virtual ~bSynth(); - sample_t nextStringSample(); + sample_t nextStringSample( float sample_length ); private: @@ -55,7 +55,6 @@ private: float sample_realindex; float* sample_shape; NotePlayHandle* nph; - const int sample_length; const sample_rate_t sample_rate; bool interpolation; From 732448c3921d9ef2e4297d6670ecfcade42e8572 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 2 Jun 2019 08:18:36 +0100 Subject: [PATCH 056/160] FreeBSD build version --- CMakeLists.txt | 4 ++-- cmake/modules/DetectMachine.cmake | 2 ++ include/versioninfo.h | 4 ++++ plugins/zynaddsubfx/CMakeLists.txt | 2 +- src/core/Mixer.cpp | 2 +- src/core/ProjectRenderer.cpp | 2 +- src/core/main.cpp | 2 +- src/gui/PianoView.cpp | 2 +- src/lmmsconfig.h.in | 1 + 9 files changed, 14 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 057cfa281..5dab2ab13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -431,9 +431,9 @@ If(WANT_GIG) ENDIF(WANT_GIG) # check for pthreads -IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD) +IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD) FIND_PACKAGE(Threads) -ENDIF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD) +ENDIF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD) IF(WANT_SNDIO) FIND_PACKAGE(Sndio) diff --git a/cmake/modules/DetectMachine.cmake b/cmake/modules/DetectMachine.cmake index 60c4a0953..c764b5507 100644 --- a/cmake/modules/DetectMachine.cmake +++ b/cmake/modules/DetectMachine.cmake @@ -4,6 +4,8 @@ ELSEIF(APPLE) SET(LMMS_BUILD_APPLE 1) ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") SET(LMMS_BUILD_OPENBSD 1) +ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + SET(LMMS_BUILD_FREEBSD 1) ELSEIF(HAIKU) SET(LMMS_BUILD_HAIKU 1) ELSE() diff --git a/include/versioninfo.h b/include/versioninfo.h index 8477a61c0..664fc80e6 100644 --- a/include/versioninfo.h +++ b/include/versioninfo.h @@ -28,6 +28,10 @@ #define PLATFORM "OpenBSD" #endif +#ifdef LMMS_BUILD_FREEBSD +#define PLATFORM "FreeBSD" +#endif + #ifdef LMMS_BUILD_WIN32 #define PLATFORM "win32" #endif diff --git a/plugins/zynaddsubfx/CMakeLists.txt b/plugins/zynaddsubfx/CMakeLists.txt index 9d90c5e38..4c170a987 100644 --- a/plugins/zynaddsubfx/CMakeLists.txt +++ b/plugins/zynaddsubfx/CMakeLists.txt @@ -2,7 +2,7 @@ INCLUDE(BuildPlugin) # definitions for ZynAddSubFX -IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD) +IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD) FIND_PACKAGE(X11) INCLUDE_DIRECTORIES(${X11_INCLUDE_DIR}) ADD_DEFINITIONS(-DOS_LINUX) diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index ac1aa2564..38ee5c46d 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -1242,7 +1242,7 @@ void Mixer::fifoWriter::run() disable_denormals(); #if 0 -#ifdef LMMS_BUILD_LINUX +#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_FREEBSD) #ifdef LMMS_HAVE_SCHED_H cpu_set_t mask; CPU_ZERO( &mask ); diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 95ed1bd8d..edf7a88a3 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -162,7 +162,7 @@ void ProjectRenderer::run() { MemoryManager::ThreadGuard mmThreadGuard; Q_UNUSED(mmThreadGuard); #if 0 -#ifdef LMMS_BUILD_LINUX +#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_FREEBSD) #ifdef LMMS_HAVE_SCHED_H cpu_set_t mask; CPU_ZERO( &mask ); diff --git a/src/core/main.cpp b/src/core/main.cpp index cc9cb4f2a..1f400e91a 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -666,7 +666,7 @@ int main( int argc, char * * argv ) // try to set realtime priority -#ifdef LMMS_BUILD_LINUX +#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_FREEBSD) #ifdef LMMS_HAVE_SCHED_H #ifndef __OpenBSD__ struct sched_param sparam; diff --git a/src/gui/PianoView.cpp b/src/gui/PianoView.cpp index 2a64e72dd..596a2c212 100644 --- a/src/gui/PianoView.cpp +++ b/src/gui/PianoView.cpp @@ -200,7 +200,7 @@ int PianoView::getKeyFromKeyEvent( QKeyEvent * _ke ) case 27: return 31; // ] } #endif -#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_OPENBSD) +#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_OPENBSD) || defined(LMMS_BUILD_FREEBSD) switch( k ) { case 52: return 0; // Z = C diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index aa4505dbb..7e37be8bd 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -3,6 +3,7 @@ #cmakedefine LMMS_BUILD_WIN64 #cmakedefine LMMS_BUILD_APPLE #cmakedefine LMMS_BUILD_OPENBSD +#cmakedefine LMMS_BUILD_FREEBSD #cmakedefine LMMS_BUILD_HAIKU #cmakedefine LMMS_HOST_X86 From a9262b9613cc786d797f437184e630c5e2563bab Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Sat, 19 Oct 2019 11:40:06 +0200 Subject: [PATCH 057/160] Fix Organic offset glitch (#5252) --- plugins/organic/organic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/organic/organic.cpp b/plugins/organic/organic.cpp index b062e5b11..d61072aaf 100644 --- a/plugins/organic/organic.cpp +++ b/plugins/organic/organic.cpp @@ -307,7 +307,7 @@ void organicInstrument::playNote( NotePlayHandle * _n, // fxKnob is [0;1] float t = m_fx1Model.value(); - for (int i=0 ; i < frames ; i++) + for (int i=0 ; i < frames + offset ; i++) { _working_buffer[i][0] = waveshape( _working_buffer[i][0], t ) * m_volModel.value() / 100.0f; From 4f11cf1b23c6ba8490c23a863b5b94804b017dc9 Mon Sep 17 00:00:00 2001 From: "Raine M. Ekman" Date: Sat, 19 Oct 2019 22:41:51 +0300 Subject: [PATCH 058/160] Make SampleBuffer adjust its members when resampling Fixes #5218. --- src/core/SampleBuffer.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 7f0980fe9..872b8cfac 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -382,11 +382,14 @@ void SampleBuffer::directFloatWrite ( sample_t * & _fbuf, f_cnt_t _frames, int _ void SampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr, bool _keep_settings ) { + const sample_rate_t old_rate = m_sampleRate; // do samplerate-conversion to our default-samplerate if( _src_sr != mixerSampleRate() ) { SampleBuffer * resampled = resample( _src_sr, mixerSampleRate() ); + + m_sampleRate = mixerSampleRate(); MM_FREE( m_data ); m_frames = resampled->frames(); m_data = MM_ALLOC( sampleFrame, m_frames ); @@ -401,6 +404,16 @@ void SampleBuffer::normalizeSampleRate( const sample_rate_t _src_sr, m_loopStartFrame = m_startFrame = 0; m_loopEndFrame = m_endFrame = m_frames; } + else if( old_rate != mixerSampleRate() ) + { + auto old_rate_to_new_rate_ratio = static_cast(mixerSampleRate()) / old_rate; + + m_startFrame = qBound(0, f_cnt_t(m_startFrame*old_rate_to_new_rate_ratio), m_frames); + m_endFrame = qBound(m_startFrame, f_cnt_t(m_endFrame*old_rate_to_new_rate_ratio), m_frames); + m_loopStartFrame = qBound(0, f_cnt_t(m_loopStartFrame*old_rate_to_new_rate_ratio), m_frames); + m_loopEndFrame = qBound(m_loopStartFrame, f_cnt_t(m_loopEndFrame*old_rate_to_new_rate_ratio), m_frames); + m_sampleRate = mixerSampleRate(); + } } From dbf5f47149fbb7584bf48ce9e767b9e90ee2df7e Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 21 Oct 2019 09:27:12 +0900 Subject: [PATCH 059/160] Bump version to 1.2.1 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dab2ab13..d82ace4ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ SET(PROJECT_DESCRIPTION "${PROJECT_NAME_UCASE} - Free music production software" SET(PROJECT_COPYRIGHT "2008-${PROJECT_YEAR} ${PROJECT_AUTHOR}") SET(VERSION_MAJOR "1") SET(VERSION_MINOR "2") -SET(VERSION_RELEASE "0") +SET(VERSION_RELEASE "1") SET(VERSION_STAGE "") SET(VERSION_BUILD "0") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}") From 13c4f4a99541a75c8ef4e9cc1d1b63064382a11e Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 22 Oct 2019 11:30:36 +0900 Subject: [PATCH 060/160] Let Travis Mac builds use the Xcode 9.4 image --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b3cb8aa7c..ab2a8ae85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,7 @@ matrix: git: depth: false - os: osx - osx_image: xcode8.3 + osx_image: xcode9.4 before_install: # appdmg doesn't work with old Node.js - if [ "$TRAVIS_OS_NAME" = osx ]; then nvm install 10; fi From 3745bfbd7fb7b90f682eecd7cd9c7c264122f95f Mon Sep 17 00:00:00 2001 From: Mark-Agent003 Date: Fri, 25 Oct 2019 02:20:02 -0500 Subject: [PATCH 061/160] Increase File Browser Spacing (#4252) Increase File Browser Spacing --- data/themes/default/style.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/data/themes/default/style.css b/data/themes/default/style.css index c5a449128..f95469201 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -9,16 +9,23 @@ QLabel, QTreeWidget, QListWidget, QGroupBox, QMenuBar { QTreeView { outline: none; + font-size: 12px; +} + +QTreeWidget::item { + padding: 1px; } QTreeWidget::item:hover, QTreeWidget::branch:hover { background-color: #3C444E; + padding-left: 0px; } QTreeWidget::item:selected, QTreeWidget::branch:selected { background-color: #17793b; + padding-left: 0px; } QTreeView::branch:has-children:open { From 5e4e536bf0dad4dfa28e8b51b305638fefaee468 Mon Sep 17 00:00:00 2001 From: knittl Date: Sun, 27 Oct 2019 21:17:04 +0100 Subject: [PATCH 062/160] Replace initializer list macros with delegating constructors (#5279) Closes #5278 --- include/LcdWidget.h | 2 +- src/gui/widgets/Knob.cpp | 29 +++++++++++------------------ src/gui/widgets/LcdWidget.cpp | 22 ++++++---------------- src/gui/widgets/LedCheckbox.cpp | 11 ++--------- 4 files changed, 20 insertions(+), 44 deletions(-) diff --git a/include/LcdWidget.h b/include/LcdWidget.h index 89bafd1d1..cf8a7f946 100644 --- a/include/LcdWidget.h +++ b/include/LcdWidget.h @@ -100,7 +100,7 @@ private: int m_numDigits; int m_marginWidth; - void initUi( const QString& name, const QString &style = QString("19green") ); //!< to be called by ctors + void initUi( const QString& name, const QString &style ); //!< to be called by ctors } ; diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 72c6c7f8b..7b6038449 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -53,35 +53,28 @@ TextFloat * Knob::s_textFloat = NULL; -//! @todo: in C++11, we can use delegating ctors -#define DEFAULT_KNOB_INITIALIZER_LIST \ - QWidget( _parent ), \ - FloatModelView( new FloatModel( 0, 0, 0, 1, NULL, _name, true ), this ), \ - m_label( "" ), \ - m_knobPixmap( NULL ), \ - m_volumeKnob( false ), \ - m_volumeRatio( 100.0, 0.0, 1000000.0 ), \ - m_buttonPressed( false ), \ - m_angle( -10 ), \ - m_lineWidth( 0 ), \ - m_textColor( 255, 255, 255 ) Knob::Knob( knobTypes _knob_num, QWidget * _parent, const QString & _name ) : - DEFAULT_KNOB_INITIALIZER_LIST, + QWidget( _parent ), + FloatModelView( new FloatModel( 0, 0, 0, 1, NULL, _name, true ), this ), + m_label( "" ), + m_knobPixmap( NULL ), + m_volumeKnob( false ), + m_volumeRatio( 100.0, 0.0, 1000000.0 ), + m_buttonPressed( false ), + m_angle( -10 ), + m_lineWidth( 0 ), + m_textColor( 255, 255, 255 ), m_knobNum( _knob_num ) { initUi( _name ); } Knob::Knob( QWidget * _parent, const QString & _name ) : - DEFAULT_KNOB_INITIALIZER_LIST, - m_knobNum( knobBright_26 ) + Knob( knobBright_26, _parent, _name ) { - initUi( _name ); } -#undef DEFAULT_KNOB_INITIALIZER_LIST - diff --git a/src/gui/widgets/LcdWidget.cpp b/src/gui/widgets/LcdWidget.cpp index 2b85a64ed..e34e1eb47 100644 --- a/src/gui/widgets/LcdWidget.cpp +++ b/src/gui/widgets/LcdWidget.cpp @@ -39,42 +39,32 @@ -//! @todo: in C++11, we can use delegating ctors -#define DEFAULT_LCDWIDGET_INITIALIZER_LIST \ - QWidget( parent ), \ - m_label(), \ - m_textColor( 255, 255, 255 ), \ - m_textShadowColor( 64, 64, 64 ) - LcdWidget::LcdWidget( QWidget* parent, const QString& name ) : - DEFAULT_LCDWIDGET_INITIALIZER_LIST, - m_numDigits( 1 ) + LcdWidget( 1, parent, name ) { - initUi( name ); } LcdWidget::LcdWidget( int numDigits, QWidget* parent, const QString& name ) : - DEFAULT_LCDWIDGET_INITIALIZER_LIST, - m_numDigits( numDigits ) + LcdWidget( numDigits, QString("19green"), parent, name ) { - initUi( name ); } LcdWidget::LcdWidget( int numDigits, const QString& style, QWidget* parent, const QString& name ) : - DEFAULT_LCDWIDGET_INITIALIZER_LIST, + QWidget( parent ), + m_label(), + m_textColor( 255, 255, 255 ), + m_textShadowColor( 64, 64, 64 ), m_numDigits( numDigits ) { initUi( name, style ); } -#undef DEFAULT_LCDWIDGET_INITIALIZER_LIST - diff --git a/src/gui/widgets/LedCheckbox.cpp b/src/gui/widgets/LedCheckbox.cpp index f69ec6719..bdb537744 100644 --- a/src/gui/widgets/LedCheckbox.cpp +++ b/src/gui/widgets/LedCheckbox.cpp @@ -39,13 +39,9 @@ static const QString names[LedCheckBox::NumColors] = -//! @todo: in C++11, we can use delegating ctors -#define DEFAULT_LEDCHECKBOX_INITIALIZER_LIST \ - AutomatableButton( _parent, _name ) - LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent, const QString & _name, LedColors _color ) : - DEFAULT_LEDCHECKBOX_INITIALIZER_LIST, + AutomatableButton( _parent, _name ), m_text( _text ) { initUi( _color ); @@ -56,13 +52,10 @@ LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent, LedCheckBox::LedCheckBox( QWidget * _parent, const QString & _name, LedColors _color ) : - DEFAULT_LEDCHECKBOX_INITIALIZER_LIST + LedCheckBox( QString(), _parent, _name, _color ) { - initUi( _color ); } -#undef DEFAULT_LEDCHECKBOX_INITIALIZER_LIST - LedCheckBox::~LedCheckBox() From eeaaf6d1da6b8b74b5bf28cf633245c47a365ec6 Mon Sep 17 00:00:00 2001 From: nia <29542929+niacat@users.noreply.github.com> Date: Mon, 28 Oct 2019 00:56:34 +0000 Subject: [PATCH 063/160] Support CMAKE_INSTALL_MANDIR (#5276) --- src/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2fa72a401..ed0deefd1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -202,7 +202,13 @@ ELSE() SET_TARGET_PROPERTIES(lmms PROPERTIES LINK_FLAGS "${LINK_FLAGS} -Wl,-E") ENDIF(NOT LMMS_BUILD_APPLE) - INSTALL(FILES "${CMAKE_BINARY_DIR}/lmms.1.gz" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/man/man1/" + if(CMAKE_INSTALL_MANDIR) + SET(INSTALL_MANDIR ${CMAKE_INSTALL_MANDIR}) + ELSE(CMAKE_INSTALL_MANDIR) + SET(INSTALL_MANDIR ${CMAKE_INSTALL_PREFIX}/share/man) + ENDIF(CMAKE_INSTALL_MANDIR) + INSTALL(FILES "${CMAKE_BINARY_DIR}/lmms.1.gz" + DESTINATION "${INSTALL_MANDIR}/man1/" PERMISSIONS OWNER_READ GROUP_READ WORLD_READ) ENDIF() From 1b45ce5a9856f16afc286b70508fb611b47e080c Mon Sep 17 00:00:00 2001 From: Shmuel H Date: Thu, 17 Oct 2019 15:28:49 +0300 Subject: [PATCH 064/160] CircleCI: Add macOS support. --- .circleci/config.yml | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 981034ffb..c1a8ea7ff 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,6 +12,18 @@ shared: key: ccache-{{ arch }}-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}-{{ .BuildNum }} paths: - ~/.ccache + restore_homebrew_cache: &restore_homebrew_cache + restore_cache: + keys: + - homebrew-{{ arch }}-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }} + - homebrew-{{ arch }}-{{ .Environment.CIRCLE_JOB }} + - homebrew-{{ arch }} + save_homebrew_cache: &save_homebrew_cache + save_cache: + key: homebrew-{{ arch }}-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}-{{ .BuildNum }} + paths: + - ~/Library/Caches/Homebrew + - /usr/local/Homebrew ccache_stats: &ccache_stats run: @@ -143,10 +155,53 @@ jobs: - run: name: Shellcheck command: shellcheck $(find "./cmake/" -type f -name '*.sh' -o -name "*.sh.in") + macos: + environment: + <<: *common_environment + macos: + xcode: "9.3.1" + steps: + - checkout + - *init + - *restore_homebrew_cache + - *restore_cache + - run: + name: Install Homebrew dependencies + command: brew update && brew install ccache fftw cmake pkg-config libogg libvorbis lame libsndfile libsamplerate jack sdl libgig libsoundio stk fluid-synth portaudio fltk qt5 carla + - run: + name: Install nodejs dependencies + command: npm install -g appdmg + - run: + name: Building + command: | + mkdir build && cd build + cmake .. -DCMAKE_INSTALL_PREFIX="../target" -DCMAKE_PREFIX_PATH="$(brew --prefix qt5)" $CMAKE_OPTS -DUSE_WERROR=OFF + make + - run: + name: Build tests + command: cd build && make tests + - run: + name: Run tests + command: build/tests/tests + - run: + name: Build DMG + command: | + cd build + make install + make dmg + cp ./lmms-*.dmg /tmp/artifacts/ + - store_artifacts: + path: /tmp/artifacts/ + destination: / + - *save_cache + - *save_homebrew_cache + + workflows: version: 2 build-and-test: jobs: + - macos - mingw32 - mingw64 - linux.gcc From 6c865c072da6ba096fe7bf7a9c1e4d1ceedc016f Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Tue, 29 Oct 2019 14:01:05 +0100 Subject: [PATCH 065/160] Piano Roll - Fix retrigger with vol/pan sliders (#5271) --- src/gui/editors/PianoRoll.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 7ba01f983..6c6acf1b0 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -2175,7 +2175,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) m_pattern->instrumentTrack()->processInEvent( evt ); } } - else if( n->isPlaying() ) + else if( n->isPlaying() && !isSelection() ) { // mouse not over this note, stop playing it. m_pattern->instrumentTrack()->pianoModel()->handleKeyRelease( n->key() ); From 4a9e9da78c7fcc5e77546ebebcdcc0736a0c359a Mon Sep 17 00:00:00 2001 From: Lukas W Date: Tue, 29 Oct 2019 22:42:20 +0100 Subject: [PATCH 066/160] Issue templates improvements * Ask users to search the tracker first * Ask for the LMMS version in bug reports --- .github/ISSUE_TEMPLATE/bug_report.md | 6 +++++- .github/ISSUE_TEMPLATE/feature_request.md | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3ca71ce76..2c22146ae 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,8 @@ assignees: '' --- +# Please search the issue tracker for existing bug reports before submitting your own. Delete this line to confirm no similar report has been posted yet. + ### Bug Summary #### Steps to reproduce @@ -17,10 +19,12 @@ assignees: '' #### Screenshot +#### LMMS version used + #### Logs