diff --git a/CMakeLists.txt b/CMakeLists.txt index dd1960450..76f5f2897 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 OLD) + 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 275baba35..16a6297eb 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -41,6 +41,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') @@ -106,6 +109,12 @@ 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 QT_AUTO_SCREEN_SCALE_FACTOR=1 \$DIR/usr/bin/lmms.real "\$@" EOL @@ -161,6 +170,14 @@ ln -sr "$VSTBIN" "$VSTLIB" # Remove wine library conflict rm -f "${APPDIR}/usr/lib/libwine.so.1" +# Remove problematic jack library, replace with weakjack +if [ -e "${APPDIR}/usr/lib/libjack.so.0" ]; then + rm -f "${APPDIR}/usr/lib/libjack.so.0" + mkdir -p "${APPDIR}usr/lib/lmms/optional/" + cp "@CMAKE_BINARY_DIR@/optional/weakjack.so" "${APPDIR}usr/lib/lmms/optional/weakjack.so" + ln -sr "${APPDIR}usr/lib/lmms/optional/weakjack.so" "${APPDIR}usr/lib/lmms/optional/libjack.so.0" +fi + # Create AppImage echo -e "\nFinishing the AppImage..." echo -e "\n\n>>>>> appimagetool" >> "$LOGFILE" 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/ConfigManager.h b/include/ConfigManager.h index 74cb29c17..997d7f24c 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -44,7 +44,7 @@ const QString TEMPLATE_PATH = "templates/"; const QString PRESETS_PATH = "presets/"; const QString SAMPLES_PATH = "samples/"; const QString GIG_PATH = "samples/gig/"; -const QString SF2_PATH = "samples/sf2/"; +const QString SF2_PATH = "samples/soundfonts/"; const QString LADSPA_PATH ="plugins/ladspa/"; const QString DEFAULT_THEME_PATH = "themes/default/"; const QString TRACK_ICON_PATH = "track_icons/"; 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 b8879ad9b..0ba8a3503 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" @@ -229,6 +230,17 @@ public: return m_loadingProject; } + void loadingCancelled() + { + m_isCancelled = true; + Engine::mixer()->clearNewPlayHandles(); + } + + bool isCancelled() + { + return m_isCancelled; + } + bool isModified() const { return m_modified; @@ -358,6 +370,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 b1b01be27..ccf5fbf62 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -229,11 +229,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/calf/src/audio_fx.cpp b/plugins/LadspaEffect/calf/src/audio_fx.cpp index f42ccc0a8..721a32ad5 100644 --- a/plugins/LadspaEffect/calf/src/audio_fx.cpp +++ b/plugins/LadspaEffect/calf/src/audio_fx.cpp @@ -731,6 +731,7 @@ void lookahead_limiter::process(float &left, float &right, float * multi_buffer) _peak = fabs(buffer[nextpos[j]]) > fabs(buffer[nextpos[j] + 1]) ? fabs(buffer[nextpos[j]]) : fabs(buffer[nextpos[j] + 1]); // calc a delta to use to reach our incoming peak from the // stored position + _peak = std::max( _peak, 0.000001f ); _delta = (_limit / peak - (limit * _multi_coeff * weight) / _peak) / (((buffer_size - nextpos[j] + pos) % buffer_size) / channels); if(_delta < nextdelta[j]) { // if the buffered delta is more important than the delta 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/plugins/LadspaEffect/swh/dyson_compress_1403.c b/plugins/LadspaEffect/swh/dyson_compress_1403.c index 25e0b7d64..27062f051 100644 --- a/plugins/LadspaEffect/swh/dyson_compress_1403.c +++ b/plugins/LadspaEffect/swh/dyson_compress_1403.c @@ -836,7 +836,7 @@ static void __attribute__((constructor)) swh_init() { D_("Release time (s)"); port_range_hints[DYSONCOMPRESS_RELEASE_TIME].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_LOW; - port_range_hints[DYSONCOMPRESS_RELEASE_TIME].LowerBound = 0; + port_range_hints[DYSONCOMPRESS_RELEASE_TIME].LowerBound = 0.0000001; port_range_hints[DYSONCOMPRESS_RELEASE_TIME].UpperBound = 1; /* Parameters for Fast compression ratio */ diff --git a/plugins/LadspaEffect/swh/shaper_1187.c b/plugins/LadspaEffect/swh/shaper_1187.c index c1a52906b..7adfb3abd 100644 --- a/plugins/LadspaEffect/swh/shaper_1187.c +++ b/plugins/LadspaEffect/swh/shaper_1187.c @@ -115,8 +115,8 @@ static void runShaper(LADSPA_Handle instance, unsigned long sample_count) { if (shapep < 1.0f && shapep > -1.0f) { shape = 1.0f; - } else if (shape < 0) { - shape = -1.0f / shape; + } else if (shapep < 0) { + shape = -1.0f / shapep; } else { shape = shapep; } @@ -160,8 +160,8 @@ static void runAddingShaper(LADSPA_Handle instance, unsigned long sample_count) if (shapep < 1.0f && shapep > -1.0f) { shape = 1.0f; - } else if (shape < 0) { - shape = -1.0f / shape; + } else if (shapep < 0) { + shape = -1.0f / shapep; } else { shape = shapep; } diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 81cd46a47..ec21e8e8b 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -339,7 +339,6 @@ lb302Synth::lb302Synth( InstrumentTrack * _instrumentTrack ) : // Experimenting with a0 between original (0.5) and 1.0 vca_a0 = 0.5; - vca_a = 9; vca_mode = 3; vcfs[0] = new lb302FilterIIR2(&fs); diff --git a/src/3rdparty/rpmalloc/CMakeLists.txt b/src/3rdparty/rpmalloc/CMakeLists.txt index b71af279b..23d1551c2 100644 --- a/src/3rdparty/rpmalloc/CMakeLists.txt +++ b/src/3rdparty/rpmalloc/CMakeLists.txt @@ -16,8 +16,15 @@ if (NOT LMMS_BUILD_WIN32) endif() if (CMAKE_BUILD_TYPE STREQUAL "Debug") + # rpmalloc uses GCC builtin "__builtin_umull_overflow" with ENABLE_VALIDATE_ARGS, + # which is only available starting with GCC 5 + if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5) + set(ENABLE_VALIDATE_ARGS OFF) + else () + set(ENABLE_VALIDATE_ARGS ON) + endif() target_compile_definitions(rpmalloc - PRIVATE -DENABLE_ASSERTS=1 -DENABLE_VALIDATE_ARGS=1 + PRIVATE -DENABLE_ASSERTS=1 -DENABLE_VALIDATE_ARGS=${ENABLE_VALIDATE_ARGS} ) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93df6e6b4..e906eda3f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -160,9 +160,11 @@ TARGET_LINK_LIBRARIES(lmms ) FOREACH(LIB ${LMMS_REQUIRED_LIBS}) - GET_TARGET_PROPERTY(INCLUDE_DIRS ${LIB} INTERFACE_INCLUDE_DIRECTORIES) - if (INCLUDE_DIRS) - TARGET_INCLUDE_DIRECTORIES(lmmsobjs PRIVATE ${INCLUDE_DIRS}) + IF(TARGET ${LIB}) + GET_TARGET_PROPERTY(INCLUDE_DIRS ${LIB} INTERFACE_INCLUDE_DIRECTORIES) + if (INCLUDE_DIRS) + TARGET_INCLUDE_DIRECTORIES(lmmsobjs PRIVATE ${INCLUDE_DIRS}) + ENDIF() ENDIF() ENDFOREACH() 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/CMakeLists.txt b/src/core/CMakeLists.txt index c8840997d..30566e5ae 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,5 +1,14 @@ IF(LMMS_HAVE_WEAKJACK) set(WEAKJACK core/audio/AudioWeakJack.c) + + # Build libjack.so.0 stub as weakjack.so for AppImages + IF(LMMS_BUILD_LINUX) + ADD_LIBRARY(weakjack MODULE ../../src/core/audio/AudioWeakJack.c) + INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include") + # We can't predict an AppImage build, so stash the build artifact for later + INSTALL(TARGETS weakjack LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/optional") + SET_TARGET_PROPERTIES(weakjack PROPERTIES PREFIX "" SUFFIX ".so") + ENDIF() ENDIF() set(LMMS_SRCS 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/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index dd57e9f9c..b85d02474 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -22,6 +22,7 @@ * */ +#include #include #include "PresetPreviewPlayHandle.h" @@ -66,12 +67,25 @@ public: NotePlayHandle* previewNote() { + #if QT_VERSION >= 0x050000 + return m_previewNote.loadAcquire(); + #else return m_previewNote; + #endif } void setPreviewNote( NotePlayHandle * _note ) { + #if QT_VERSION >= 0x050000 + m_previewNote.storeRelease( _note ); + #else m_previewNote = _note; + #endif + } + + bool testAndSetPreviewNote( NotePlayHandle * expectedVal, NotePlayHandle * newVal ) + { + return m_previewNote.testAndSetOrdered( expectedVal, newVal ); } void lockData() @@ -97,7 +111,7 @@ public: private: InstrumentTrack* m_previewInstrumentTrack; - NotePlayHandle* m_previewNote; + QAtomicPointer m_previewNote; QMutex m_dataMutex; friend class PresetPreviewPlayHandle; @@ -113,15 +127,14 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, PlayHandle( TypePresetPreviewHandle ), m_previewNote( NULL ) { - s_previewTC->lockData(); - setUsesBuffer( false ); - if( s_previewTC->previewNote() != NULL ) - { - s_previewTC->previewNote()->mute(); - } + s_previewTC->lockData(); + Engine::mixer()->requestChangeInModel(); + s_previewTC->setPreviewNote( nullptr ); + s_previewTC->previewInstrumentTrack()->silenceAllNotes(); + Engine::mixer()->doneChangeInModel(); const bool j = Engine::projectJournal()->isJournalling(); Engine::projectJournal()->setJournalling( false ); @@ -174,6 +187,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, s_previewTC->previewInstrumentTrack()-> midiPort()->setMode( MidiPort::Disabled ); + Engine::mixer()->requestChangeInModel(); // create note-play-handle for it m_previewNote = NotePlayHandleManager::acquire( s_previewTC->previewInstrumentTrack(), 0, @@ -186,6 +200,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, Engine::mixer()->addPlayHandle( m_previewNote ); + Engine::mixer()->doneChangeInModel(); s_previewTC->unlockData(); Engine::projectJournal()->setJournalling( j ); } @@ -195,15 +210,13 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, PresetPreviewPlayHandle::~PresetPreviewPlayHandle() { - s_previewTC->lockData(); + Engine::mixer()->requestChangeInModel(); // not muted by other preset-preview-handle? - if( !m_previewNote->isMuted() ) + if (s_previewTC->testAndSetPreviewNote(m_previewNote, nullptr)) { - // then set according state - s_previewTC->setPreviewNote( NULL ); + m_previewNote->noteOff(); } - m_previewNote->noteOff(); - s_previewTC->unlockData(); + Engine::mixer()->doneChangeInModel(); } @@ -228,7 +241,7 @@ bool PresetPreviewPlayHandle::isFinished() const bool PresetPreviewPlayHandle::isFromTrack( const Track * _track ) const { - return s_previewTC->previewInstrumentTrack() == _track; + return s_previewTC && s_previewTC->previewInstrumentTrack() == _track; } @@ -258,13 +271,11 @@ ConstNotePlayHandleList PresetPreviewPlayHandle::nphsOfInstrumentTrack( const InstrumentTrack * _it ) { ConstNotePlayHandleList cnphv; - s_previewTC->lockData(); if( s_previewTC->previewNote() != NULL && s_previewTC->previewNote()->instrumentTrack() == _it ) { cnphv.push_back( s_previewTC->previewNote() ); } - s_previewTC->unlockData(); return cnphv; } diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 01071668c..5f8e75320 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 ), @@ -1074,7 +1075,7 @@ void Song::loadProject( const QString & fileName ) } } - while( !node.isNull() ) + while( !node.isNull() && !isCancelled() ) { if( node.isElement() ) { @@ -1133,6 +1134,13 @@ void Song::loadProject( const QString & fileName ) emit projectLoaded(); + if( isCancelled() ) + { + m_isCancelled = false; + createNewProject(); + return; + } + if ( hasErrors()) { if ( gui ) @@ -1278,7 +1286,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 68bdb4bf2..448c233cb 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -319,6 +319,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 ); @@ -332,7 +342,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 ca65b5275..733cf2814 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -517,16 +517,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() || mouseEvent->button() == Qt::RightButton ) ) { break; } @@ -1378,13 +1375,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 ); @@ -2133,6 +2130,8 @@ void AutomationEditor::setQuantization() } quantization = DefaultTicksPerTact / quantization; AutomationPattern::setQuantization( quantization ); + + update(); } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 9b65c6847..ae5c33c89 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -963,6 +963,9 @@ void PianoRoll::shiftSemiTone( int amount ) // shift notes by amount semitones } } + m_pattern->rearrangeAllNotes(); + m_pattern->dataChanged(); + // we modified the song update(); gui->songEditor()->update(); diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 6aaf3eb75..90dbf11a6 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -442,12 +442,15 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH ) m_midiNotesMutex.unlock(); lock(); - // invalidate all NotePlayHandles linked to this track + // invalidate all NotePlayHandles and PresetPreviewHandles linked to this track m_processHandles.clear(); - Engine::mixer()->removePlayHandlesOfTypes( this, removeIPH - ? PlayHandle::TypeNotePlayHandle - | PlayHandle::TypeInstrumentPlayHandle - : PlayHandle::TypeNotePlayHandle ); + + quint8 flags = PlayHandle::TypeNotePlayHandle | PlayHandle::TypePresetPreviewHandle; + if( removeIPH ) + { + flags |= PlayHandle::TypeInstrumentPlayHandle; + } + Engine::mixer()->removePlayHandlesOfTypes( this, flags ); unlock(); }
Put down your project notes here.
Enter project notes here