diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ead1212b..1fc5e1d52 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0003 NEW) IF (CMAKE_MAJOR_VERSION GREATER 2) CMAKE_POLICY(SET CMP0026 NEW) + CMAKE_POLICY(SET CMP0045 NEW) CMAKE_POLICY(SET CMP0050 OLD) ENDIF() ENDIF(COMMAND CMAKE_POLICY) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index ddb460089..729169ec4 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -42,6 +42,9 @@ function skipped { echo -e " ${PLAIN}[${YELLOW}skipped${PLAIN}] ${1}" } +# Blindly assume system arch is appimage arch +ARCH=$(arch) +export ARCH # Check for problematic install locations INSTALL=$(echo "@CMAKE_INSTALL_PREFIX@" | sed 's/\/*$//g') diff --git a/data/projects/templates/Empty.mpt b/data/projects/templates/Empty.mpt index cf5bfa150..4974213b7 100644 --- a/data/projects/templates/Empty.mpt +++ b/data/projects/templates/Empty.mpt @@ -30,11 +30,7 @@ - - -

Put down your project notes here.

]]>
+ diff --git a/data/projects/templates/default.mpt b/data/projects/templates/default.mpt index 18fcdd925..bebd41fe3 100644 --- a/data/projects/templates/default.mpt +++ b/data/projects/templates/default.mpt @@ -78,11 +78,7 @@ - - -

Enter project notes here

]]>
+ diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 32b4bc286..0af7ea4fa 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -80,7 +80,7 @@ public: MidiTime putValue( const MidiTime & time, const float value, const bool quantPos = true, - const bool ignoreSurroundingPoints = false ); + const bool ignoreSurroundingPoints = true ); void removeValue( const MidiTime & time ); diff --git a/include/MainApplication.h b/include/MainApplication.h index 8d5df9f86..d3acb95ca 100644 --- a/include/MainApplication.h +++ b/include/MainApplication.h @@ -31,15 +31,26 @@ #ifdef LMMS_BUILD_WIN32 #include +#if QT_VERSION >= 0x050000 +#include +#endif #endif +#if defined(LMMS_BUILD_WIN32) && QT_VERSION >= 0x050000 +class MainApplication : public QApplication, public QAbstractNativeEventFilter +#else class MainApplication : public QApplication +#endif { public: MainApplication(int& argc, char** argv); bool event(QEvent* event); #ifdef LMMS_BUILD_WIN32 bool winEventFilter(MSG* msg, long* result); +#if QT_VERSION >= 0x050000 + bool nativeEventFilter(const QByteArray& eventType, void* message, + long* result); +#endif #endif inline QString& queuedFile() { diff --git a/include/Mixer.h b/include/Mixer.h index 8368e8434..1c3780b1b 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -155,6 +155,7 @@ public: void initDevices(); void clear(); + void clearNewPlayHandles(); // audio-device-stuff diff --git a/include/Song.h b/include/Song.h index 0feac4958..5c08426ea 100644 --- a/include/Song.h +++ b/include/Song.h @@ -33,6 +33,7 @@ #include "TrackContainer.h" #include "Controller.h" #include "MeterModel.h" +#include "Mixer.h" #include "VstSyncController.h" @@ -256,6 +257,17 @@ public: return m_loadingProject; } + void loadingCancelled() + { + m_isCancelled = true; + Engine::mixer()->clearNewPlayHandles(); + } + + bool isCancelled() + { + return m_isCancelled; + } + bool isModified() const { return m_modified; @@ -385,6 +397,7 @@ private: volatile bool m_paused; bool m_loadingProject; + bool m_isCancelled; QStringList m_errors; diff --git a/include/lmms_math.h b/include/lmms_math.h index 422cbd69d..7c68c6bfb 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -212,11 +212,12 @@ static inline float logToLinearScale( float min, float max, float value ) static inline float linearToLogScale( float min, float max, float value ) { static const float EXP = 1.0f / F_E; - const float val = ( value - min ) / ( max - min ); + const float valueLimited = qBound( min, value, max); + const float val = ( valueLimited - min ) / ( max - min ); if( min < 0 ) { const float mmax = qMax( qAbs( min ), qAbs( max ) ); - float result = signedPowf( value / mmax, EXP ) * mmax; + float result = signedPowf( valueLimited / mmax, EXP ) * mmax; return isnan( result ) ? 0 : result; } float result = powf( val, EXP ) * ( max - min ) + min; diff --git a/plugins/LadspaEffect/caps/SweepVF.cc b/plugins/LadspaEffect/caps/SweepVF.cc index f97c220cf..3dab97d7e 100644 --- a/plugins/LadspaEffect/caps/SweepVF.cc +++ b/plugins/LadspaEffect/caps/SweepVF.cc @@ -28,10 +28,10 @@ 02111-1307, USA or point your web browser to http://www.gnu.org. */ -#include - #include "basics.h" +#include + #include "SweepVF.h" #include "Descriptor.h" diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 0391061b6..e2a088e04 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -42,8 +42,6 @@ AutomatableModel::AutomatableModel( DataType type, Model( parent, displayName, defaultConstructed ), m_dataType( type ), m_scaleType( Linear ), - m_value( val ), - m_initValue( val ), m_minValue( min ), m_maxValue( max ), m_step( step ), @@ -59,6 +57,7 @@ AutomatableModel::AutomatableModel( DataType type, m_hasSampleExactData( false ) { + m_value = fittedValue( val ); setInitValue( val ); } @@ -523,14 +522,8 @@ float AutomatableModel::controllerValue( int frameOffset ) const ValueBuffer * AutomatableModel::valueBuffer() { - // if we've already calculated the valuebuffer this period, return the cached buffer - if( m_lastUpdatedPeriod == s_periodCounter ) - { - return m_hasSampleExactData - ? &m_valueBuffer - : NULL; - } QMutexLocker m( &m_valueBufferMutex ); + // if we've already calculated the valuebuffer this period, return the cached buffer if( m_lastUpdatedPeriod == s_periodCounter ) { return m_hasSampleExactData @@ -626,6 +619,7 @@ void AutomatableModel::setInitValue( const float value ) m_initValue = fittedValue( value ); bool journalling = testAndSetJournalling( false ); setValue( value ); + m_oldValue = m_value; setJournalling( journalling ); emit initValueChanged( value ); } diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index c99d3ab8c..ed8d04a6c 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -501,6 +501,20 @@ void Mixer::clear() +void Mixer::clearNewPlayHandles() +{ + requestChangeInModel(); + for( LocklessListElement * e = m_newPlayHandles.popList(); e; ) + { + LocklessListElement * next = e->next; + m_newPlayHandles.free( e ); + e = next; + } + doneChangeInModel(); +} + + + // removes all play-handles. this is necessary, when the song is stopped -> // all remaining notes etc. would be played until their end void Mixer::clearInternal() diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 0639af667..4d64d6ed9 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -86,6 +86,7 @@ Song::Song() : m_playing( false ), m_paused( false ), m_loadingProject( false ), + m_isCancelled( false ), m_playMode( Mode_None ), m_length( 0 ), m_patternToPlay( NULL ), @@ -1068,7 +1069,7 @@ void Song::loadProject( const QString & fileName ) } } - while( !node.isNull() ) + while( !node.isNull() && !isCancelled() ) { if( node.isElement() ) { @@ -1127,6 +1128,13 @@ void Song::loadProject( const QString & fileName ) emit projectLoaded(); + if( isCancelled() ) + { + m_isCancelled = false; + createNewProject(); + return; + } + if ( hasErrors()) { if ( gui ) @@ -1272,7 +1280,7 @@ void Song::saveControllerStates( QDomDocument & doc, QDomElement & element ) void Song::restoreControllerStates( const QDomElement & element ) { QDomNode node = element.firstChild(); - while( !node.isNull() ) + while( !node.isNull() && !isCancelled() ) { Controller * c = Controller::create( node.toElement(), this ); Q_ASSERT( c != NULL ); diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index 6e406b0a9..6877e6b1f 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -33,12 +33,14 @@ #include "AutomationTrack.h" #include "BBTrack.h" #include "BBTrackContainer.h" +#include "embed.h" #include "TrackContainer.h" #include "InstrumentTrack.h" #include "Song.h" #include "GuiApplication.h" #include "MainWindow.h" +#include "TextFloat.h" TrackContainer::TrackContainer() : Model( NULL ), @@ -110,6 +112,14 @@ void TrackContainer::loadSettings( const QDomElement & _this ) QEventLoop::AllEvents, 100 ); if( pd->wasCanceled() ) { + if ( gui ) + { + TextFloat::displayMessage( tr( "Loading cancelled" ), + tr( "Project loading was cancelled." ), + embed::getIconPixmap( "project_file", 24, 24 ), + 2000 ); + } + Engine::getSong()->loadingCancelled(); break; } } diff --git a/src/core/midi/MidiApple.cpp b/src/core/midi/MidiApple.cpp index 45f58e4bb..f4bc0d4dd 100644 --- a/src/core/midi/MidiApple.cpp +++ b/src/core/midi/MidiApple.cpp @@ -615,7 +615,7 @@ char * MidiApple::getFullName(MIDIEndpointRef &endpoint_ref) char * deviceName = getName(device); char * endPointName = getName(endpoint_ref); qDebug("device name='%s' endpoint name='%s'",deviceName,endPointName); - char * fullName = (char *)malloc(strlen(deviceName) + strlen(endPointName)+1); + char * fullName = (char *)malloc(strlen(deviceName) + strlen(":") + strlen(endPointName)+1); sprintf(fullName, "%s:%s", deviceName,endPointName); return fullName; } diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index bbcf8274e..92e7b7377 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -327,6 +327,16 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) float *values = m_pat->valuesAfter( it.key() ); + float nextValue; + if( m_pat->progressionType() == AutomationPattern::DiscreteProgression ) + { + nextValue = it.value(); + } + else + { + nextValue = ( it + 1 ).value(); + } + QPainterPath path; QPointF origin = QPointF( x_base + it.key() * ppTick, 0.0f ); path.moveTo( origin ); @@ -340,7 +350,7 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) path.lineTo( QPointF( x, value ) ); } - path.lineTo( x_base + ( ( it + 1 ).key() ) * ppTick, values[ ( it + 1 ).key() - 1 - it.key() ] ); + path.lineTo( x_base + ( ( it + 1 ).key() ) * ppTick, nextValue ); path.lineTo( x_base + ( ( it + 1 ).key() ) * ppTick, 0.0f ); path.lineTo( origin ); diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 0e828c329..8da1cb4e0 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -382,6 +382,10 @@ void FxMixerView::deleteChannel(int index) // remember selected line int selLine = m_currentFxLine->channelIndex(); + // in case the deleted channel is soloed or the remaining + // channels will be left in a muted state + Engine::fxMixer()->clearChannel(index); + // delete the real channel Engine::fxMixer()->deleteChannel(index); diff --git a/src/gui/MainApplication.cpp b/src/gui/MainApplication.cpp index 767eaa8fe..5210dd76c 100644 --- a/src/gui/MainApplication.cpp +++ b/src/gui/MainApplication.cpp @@ -33,7 +33,12 @@ MainApplication::MainApplication(int& argc, char** argv) : QApplication(argc, argv), - m_queuedFile() {} + m_queuedFile() +{ +#if defined(LMMS_BUILD_WIN32) && QT_VERSION >= 0x050000 + installNativeEventFilter(this); +#endif +} bool MainApplication::event(QEvent* event) { @@ -64,6 +69,7 @@ bool MainApplication::event(QEvent* event) } #ifdef LMMS_BUILD_WIN32 +// This can be moved into nativeEventFilter once Qt4 support has been dropped bool MainApplication::winEventFilter(MSG* msg, long* result) { switch(msg->message) @@ -85,4 +91,16 @@ bool MainApplication::winEventFilter(MSG* msg, long* result) return false; } } + +#if QT_VERSION >= 0x050000 +bool MainApplication::nativeEventFilter(const QByteArray& eventType, + void* message, long* result) +{ + if(eventType == "windows_generic_MSG") + { + return winEventFilter(static_cast(message), result); + } + return false; +} +#endif #endif diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index b91fe3ef2..32d16546b 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,21 @@ #include "lmmsversion.h" +#if !defined(LMMS_BUILD_WIN32) && !defined(LMMS_BULID_APPLE) && !defined(LMMS_BUILD_HAIKU) +//Work around an issue on KDE5 as per https://bugs.kde.org/show_bug.cgi?id=337491#c21 +void disableAutoKeyAccelerators(QWidget* mainWindow) +{ + using DisablerFunc = void(*)(QWidget*); + QLibrary kf5WidgetsAddon("KF5WidgetsAddons", 5); + DisablerFunc setNoAccelerators = + reinterpret_cast(kf5WidgetsAddon.resolve("_ZN19KAcceleratorManager10setNoAccelEP7QWidget")); + if(setNoAccelerators) + { + setNoAccelerators(mainWindow); + } + kf5WidgetsAddon.unload(); +} +#endif MainWindow::MainWindow() : @@ -76,6 +92,9 @@ MainWindow::MainWindow() : m_metronomeToggle( 0 ), m_session( Normal ) { +#if !defined(LMMS_BUILD_WIN32) && !defined(LMMS_BULID_APPLE) && !defined(LMMS_BUILD_HAIKU) + disableAutoKeyAccelerators(this); +#endif setAttribute( Qt::WA_DeleteOnClose ); QWidget * main_widget = new QWidget( this ); @@ -836,8 +855,8 @@ void MainWindow::createNewProjectFromTemplate( QAction * _idx ) ConfigManager::inst()->factoryTemplatesDir() : ConfigManager::inst()->userTemplateDir(); - Engine::getSong()->createNewProjectFromTemplate( - dirBase + _idx->text() + ".mpt" ); + const QString f = dirBase + _idx->text().replace("&&", "&") + ".mpt"; + Engine::getSong()->createNewProjectFromTemplate(f); } } @@ -888,7 +907,7 @@ void MainWindow::updateRecentlyOpenedProjectsMenu() } m_recentlyOpenedProjectsMenu->addAction( - embed::getIconPixmap( "project_file" ), *it ); + 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); @@ -904,12 +923,11 @@ void MainWindow::updateRecentlyOpenedProjectsMenu() - void MainWindow::openRecentlyOpenedProject( QAction * _action ) { if ( mayChangeProject(true) ) { - const QString & f = _action->text(); + const QString f = _action->text().replace("&&", "&"); setCursor( Qt::WaitCursor ); Engine::getSong()->loadProject( f ); setCursor( Qt::ArrowCursor ); @@ -1500,7 +1518,7 @@ void MainWindow::fillTemplatesMenu() { m_templatesMenu->addAction( embed::getIconPixmap( "project_file" ), - ( *it ).left( ( *it ).length() - 4 ) ); + ( *it ).left( ( *it ).length() - 4 ).replace("&", "&&") ); #ifdef LMMS_BUILD_APPLE m_templatesMenu->actions().last()->setIconVisibleInMenu(false); // QTBUG-44565 workaround m_templatesMenu->actions().last()->setIconVisibleInMenu(true); diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index f98c073d9..6d1ee2583 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -518,16 +518,13 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) // loop through whole time-map... while( it != time_map.end() ) { - MidiTime len = 4; - // and check whether the user clicked on an // existing value if( pos_ticks >= it.key() && - len > 0 && ( it+1==time_map.end() || pos_ticks <= (it+1).key() ) && ( pos_ticks<= it.key() + MidiTime::ticksPerTact() *4 / m_ppt ) && - level <= it.value() ) + level == it.value() ) { break; } @@ -1379,13 +1376,13 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) float *values = m_pattern->valuesAfter( it.key() ); float nextValue; - if ( m_pattern->valuesAfter( ( it + 1 ).key() ) != NULL ) + if( m_pattern->progressionType() == AutomationPattern::DiscreteProgression ) { - nextValue = *( m_pattern->valuesAfter( ( it + 1 ).key() ) ); + nextValue = it.value(); } else { - nextValue = values[ ( it + 1 ).key() - it.key() -1 ]; + nextValue = ( it + 1 ).value(); } p.setRenderHints( QPainter::Antialiasing, true );