diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..10785ef9a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/3rdparty/rpmalloc/rpmalloc"] + path = src/3rdparty/rpmalloc/rpmalloc + url = https://github.com/rampantpixels/rpmalloc.git diff --git a/.travis.yml b/.travis.yml index 9e73bf834..3ab8dbf14 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ matrix: - env: QT5=True TARGET_OS=win32 - env: QT5=True TARGET_OS=win64 - os: osx + osx_image: xcode8.2 env: QT5=True install: ${TRAVIS_BUILD_DIR}/.travis/install.sh script: ${TRAVIS_BUILD_DIR}/.travis/script.sh diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index dd9bd7680..cdfdffccf 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -25,6 +25,7 @@ #ifndef AUTOMATABLE_MODEL_H #define AUTOMATABLE_MODEL_H +#include #include #include "JournallingObject.h" diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 5fc3ca03a..9705c5efa 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -132,6 +132,7 @@ protected: void getSelectedValues(timeMap & selected_values ); void drawLine( int x0, float y0, int x1, float y1 ); + void removePoints( int x0, int x1 ); protected slots: void play(); diff --git a/include/MemoryManager.h b/include/MemoryManager.h index ef6c0abbf..23561e4c0 100644 --- a/include/MemoryManager.h +++ b/include/MemoryManager.h @@ -1,6 +1,7 @@ /* - * MemoryManager.h - A lightweight, generic memory manager for LMMS + * MemoryManager.h * + * Copyright (c) 2017 Lukas W * Copyright (c) 2014 Vesa Kivimäki * Copyright (c) 2007-2014 Tobias Doerffel * @@ -26,81 +27,22 @@ #ifndef MEMORY_MANAGER_H #define MEMORY_MANAGER_H -#include -#include -#include -#include "MemoryHelper.h" +#include +#include + #include "export.h" -class QReadWriteLock; - -const int MM_CHUNK_SIZE = 64; // granularity of managed memory -const int MM_INITIAL_CHUNKS = 1024 * 1024; // how many chunks to allocate at startup - TODO: make configurable -const int MM_INCREMENT_CHUNKS = 16 * 1024; // min. amount of chunks to increment at a time - -struct MemoryPool -{ - void * m_pool; - char * m_free; - size_t m_chunks; - QMutex m_mutex; - - MemoryPool() : - m_pool( NULL ), - m_free( NULL ), - m_chunks( 0 ) - {} - - MemoryPool( size_t chunks ) : - m_chunks( chunks ) - { - m_free = reinterpret_cast( MemoryHelper::alignedMalloc( chunks ) ); - memset( m_free, 1, chunks ); - } - - MemoryPool( const MemoryPool & mp ) : - m_pool( mp.m_pool ), - m_free( mp.m_free ), - m_chunks( mp.m_chunks ), - m_mutex() - {} - - MemoryPool & operator = ( const MemoryPool & mp ) - { - m_pool = mp.m_pool; - m_free = mp.m_free; - m_chunks = mp.m_chunks; - return *this; - } - - void * getChunks( int chunksNeeded ); - void releaseChunks( void * ptr, int chunks ); -}; - -struct PtrInfo -{ - int chunks; - MemoryPool * memPool; -}; - -typedef QVector MemoryPoolVector; -typedef QHash PointerInfoMap; - class EXPORT MemoryManager { public: - static bool init(); + struct ThreadGuard + { + ThreadGuard(); + ~ThreadGuard(); + }; + static void * alloc( size_t size ); static void free( void * ptr ); - static int extend( int chunks ); // returns index of created pool (for use by alloc) - static void cleanup(); - -private: - static MemoryPoolVector s_memoryPools; - static QReadWriteLock s_poolMutex; - - static PointerInfoMap s_pointerInfo; - static QMutex s_pointerMutex; }; template @@ -147,32 +89,4 @@ static void operator delete[] ( void * ptr ) \ // and just for symmetry... #define MM_FREE( ptr ) MemoryManager::free( ptr ) - - -// for debugging purposes - -#define MM_OPERATORS_DEBUG \ -public: \ -static void * operator new ( size_t size ) \ -{ \ - qDebug( "MM_OPERATORS_DEBUG: new called for %d bytes", size ); \ - return MemoryManager::alloc( size ); \ -} \ -static void * operator new[] ( size_t size ) \ -{ \ - qDebug( "MM_OPERATORS_DEBUG: new[] called for %d bytes", size ); \ - return MemoryManager::alloc( size ); \ -} \ -static void operator delete ( void * ptr ) \ -{ \ - qDebug( "MM_OPERATORS_DEBUG: delete called for %p", ptr ); \ - MemoryManager::free( ptr ); \ -} \ -static void operator delete[] ( void * ptr ) \ -{ \ - qDebug( "MM_OPERATORS_DEBUG: delete[] called for %p", ptr ); \ - MemoryManager::free( ptr ); \ -} - - #endif diff --git a/include/Note.h b/include/Note.h index 2d448c6d4..c14f9d931 100644 --- a/include/Note.h +++ b/include/Note.h @@ -113,11 +113,19 @@ public: void quantizeLength( const int qGrid ); void quantizePos( const int qGrid ); - static inline bool lessThan( Note * &lhs, Note * &rhs ) + static inline bool lessThan( const Note * lhs, const Note * rhs ) { // function to compare two notes - must be called explictly when // using qSort - return (bool) ((int) ( *lhs ).pos() < (int) ( *rhs ).pos()); + if( (int)( *lhs ).pos() < (int)( *rhs ).pos() ) + { + return true; + } + else if( (int)( *lhs ).pos() > (int)( *rhs ).pos() ) + { + return false; + } + return ( (int)( *lhs ).key() > (int)( *rhs ).key() ); } inline bool selected() const diff --git a/include/PluginFactory.h b/include/PluginFactory.h index b2d0131ad..fb3e8ea94 100644 --- a/include/PluginFactory.h +++ b/include/PluginFactory.h @@ -28,6 +28,7 @@ #include #include +#include #include #include "export.h" diff --git a/plugins/carlabase/carla.h b/plugins/carlabase/carla.h index 59a34a764..6431e5300 100644 --- a/plugins/carlabase/carla.h +++ b/plugins/carlabase/carla.h @@ -25,6 +25,8 @@ #ifndef CARLA_H #define CARLA_H +#include + #include "CarlaNative.h" #include "Instrument.h" diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index 0ac0079f8..24da1aedc 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -1692,6 +1692,8 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) makeknob( m_osc3SpoKnob, KNOBCOL4, O3ROW, tr( "Stereo phase offset" ), tr( " deg" ), "osc3Knob" ) makeknob( m_osc3SubKnob, KNOBCOL5, O3ROW, tr( "Sub-osc mix" ), "", "osc3Knob" ) + m_osc3VolKnob -> setVolumeKnob( true ); + m_osc3Wave1Box = new ComboBox( view ); m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, 22 ); m_osc3Wave1Box->setFont( pointSize<8>( m_osc3Wave1Box->font() ) ); diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt new file mode 100644 index 000000000..4067413e3 --- /dev/null +++ b/src/3rdparty/CMakeLists.txt @@ -0,0 +1,4 @@ +set(CMAKE_C_FLAGS "") +set(CMAKE_CXX_FLAGS "") + +ADD_SUBDIRECTORY(rpmalloc) diff --git a/src/3rdparty/rpmalloc/CMakeLists.txt b/src/3rdparty/rpmalloc/CMakeLists.txt new file mode 100644 index 000000000..b71af279b --- /dev/null +++ b/src/3rdparty/rpmalloc/CMakeLists.txt @@ -0,0 +1,30 @@ +set(CMAKE_C_FLAGS "-std=c11") + +add_library(rpmalloc STATIC + rpmalloc/rpmalloc/rpmalloc.c + rpmalloc/rpmalloc/rpmalloc.h +) + +target_include_directories(rpmalloc PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/rpmalloc/rpmalloc +) + +if (NOT LMMS_BUILD_WIN32) + target_compile_definitions(rpmalloc + PRIVATE -D_GNU_SOURCE + ) +endif() + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_definitions(rpmalloc + PRIVATE -DENABLE_ASSERTS=1 -DENABLE_VALIDATE_ARGS=1 + ) +endif() + +option(LMMS_ENABLE_MALLOC_STATS "Enables statistics for rpmalloc" OFF) + +if (LMMS_ENABLE_MALLOC_STATS) + target_compile_definitions(rpmalloc + PRIVATE -DENABLE_STATISTICS=1 + ) +endif() diff --git a/src/3rdparty/rpmalloc/rpmalloc b/src/3rdparty/rpmalloc/rpmalloc new file mode 160000 index 000000000..2e0479192 --- /dev/null +++ b/src/3rdparty/rpmalloc/rpmalloc @@ -0,0 +1 @@ +Subproject commit 2e0479192b8dfb15e0084969fdf06208cffbfd09 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c2a635eda..8e67c1ec6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,5 @@ +ADD_SUBDIRECTORY(3rdparty) + CONFIGURE_FILE("lmmsconfig.h.in" "${CMAKE_BINARY_DIR}/lmmsconfig.h") CONFIGURE_FILE("lmmsversion.h.in" "${CMAKE_BINARY_DIR}/lmmsversion.h") @@ -147,7 +149,9 @@ SET(LMMS_REQUIRED_LIBS ${SAMPLERATE_LIBRARIES} ${SNDFILE_LIBRARIES} ${EXTRA_LIBRARIES} + rpmalloc ) + # Expose required libs for tests binary SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} PARENT_SCOPE) @@ -155,6 +159,14 @@ TARGET_LINK_LIBRARIES(lmms ${LMMS_REQUIRED_LIBS} ) +FOREACH(LIB ${LMMS_REQUIRED_LIBS}) + GET_TARGET_PROPERTY(INCLUDE_DIRS ${LIB} INTERFACE_INCLUDE_DIRECTORIES) + if (INCLUDE_DIRS) + TARGET_INCLUDE_DIRECTORIES(lmmsobjs PRIVATE ${INCLUDE_DIRS}) + ENDIF() +ENDFOREACH() + + # Required libs for debug msys builds IF(LMMS_BUILD_MSYS AND CMAKE_BUILD_TYPE STREQUAL "Debug") TARGET_LINK_LIBRARIES(lmms QtCore4 QtGui4 QtXml4) diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 22327ae8e..56011de04 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -299,13 +299,22 @@ f_cnt_t InstrumentSoundShaping::envFrames( const bool _only_vol ) const f_cnt_t InstrumentSoundShaping::releaseFrames() const { + if( !m_instrumentTrack->instrument() ) + { + return 0; + } + + f_cnt_t ret_val = m_instrumentTrack->instrument()->desiredReleaseFrames(); + + if( m_instrumentTrack->instrument()->flags().testFlag( Instrument::IsSingleStreamed ) ) + { + return ret_val; + } + if( m_envLfoParameters[Volume]->isUsed() ) { return m_envLfoParameters[Volume]->releaseFrames(); } - f_cnt_t ret_val = m_instrumentTrack->instrument() - ? m_instrumentTrack->instrument()->desiredReleaseFrames() - : 0; for( int i = Volume+1; i < NumTargets; ++i ) { diff --git a/src/core/MemoryManager.cpp b/src/core/MemoryManager.cpp index 4196d1aac..d0932e4f1 100644 --- a/src/core/MemoryManager.cpp +++ b/src/core/MemoryManager.cpp @@ -1,8 +1,7 @@ /* - * MemoryManager.cpp - A lightweight, generic memory manager for LMMS + * MemoryManager.cpp * - * Copyright (c) 2014 Vesa Kivimäki - * Copyright (c) 2007-2014 Tobias Doerffel + * Copyright (c) 2017 Lukas W * * This file is part of LMMS - https://lmms.io * @@ -25,197 +24,54 @@ #include "MemoryManager.h" -#include + +#include +#include "rpmalloc.h" + +/// Global static object handling rpmalloc intializing and finalizing +struct MemoryManagerGlobalGuard { + MemoryManagerGlobalGuard() { + rpmalloc_initialize(); + } + ~MemoryManagerGlobalGuard() { + rpmalloc_finalize(); + } +} static mm_global_guard; -MemoryPoolVector MemoryManager::s_memoryPools; -QReadWriteLock MemoryManager::s_poolMutex; -PointerInfoMap MemoryManager::s_pointerInfo; -QMutex MemoryManager::s_pointerMutex; - - -bool MemoryManager::init() -{ - s_memoryPools.reserve( 64 ); - s_pointerInfo.reserve( 4096 ); - // construct first MemoryPool and allocate memory - MemoryPool m ( MM_INITIAL_CHUNKS ); - m.m_pool = MemoryHelper::alignedMalloc( MM_INITIAL_CHUNKS * MM_CHUNK_SIZE ); - s_memoryPools.append( m ); - return true; +namespace { +static thread_local size_t thread_guard_depth; } - -void * MemoryManager::alloc( size_t size ) +MemoryManager::ThreadGuard::ThreadGuard() { - if( !size ) - { - return NULL; - } - - int requiredChunks = size / MM_CHUNK_SIZE + ( size % MM_CHUNK_SIZE > 0 ? 1 : 0 ); - - MemoryPool * mp = NULL; - void * ptr = NULL; - - MemoryPoolVector::iterator it = s_memoryPools.begin(); - - s_poolMutex.lockForRead(); - while( it != s_memoryPools.end() && !ptr ) - { - ptr = ( *it ).getChunks( requiredChunks ); - if( ptr ) - { - mp = &( *it ); - } - ++it; - } - s_poolMutex.unlock(); - - if( ptr ) - { - s_pointerMutex.lock(); - PtrInfo p; - p.chunks = requiredChunks; - p.memPool = mp; - s_pointerInfo[ptr] = p; - s_pointerMutex.unlock(); - return ptr; - } - - // can't find enough chunks in existing pools, so - // create a new pool that is guaranteed to have enough chunks - int moreChunks = qMax( requiredChunks, MM_INCREMENT_CHUNKS ); - int i = MemoryManager::extend( moreChunks ); - - mp = &s_memoryPools[i]; - ptr = s_memoryPools[i].getChunks( requiredChunks ); - if( ptr ) - { - s_pointerMutex.lock(); - PtrInfo p; - p.chunks = requiredChunks; - p.memPool = mp; - s_pointerInfo[ptr] = p; - s_pointerMutex.unlock(); - return ptr; - } - // still no luck? something is horribly wrong - qFatal( "MemoryManager.cpp: Couldn't allocate memory: %d chunks asked", requiredChunks ); - return NULL; -} - - -void MemoryManager::free( void * ptr ) -{ - if( !ptr ) - { - return; // Null pointer deallocations are OK but do not need to be handled - } - - // fetch info on the ptr and remove - s_pointerMutex.lock(); - if( ! s_pointerInfo.contains( ptr ) ) // if we have no info on ptr, fail loudly - { - qFatal( "MemoryManager: Couldn't find pointer info for pointer: %p", ptr ); - } - PtrInfo p = s_pointerInfo[ptr]; - s_pointerInfo.remove( ptr ); - s_pointerMutex.unlock(); - - p.memPool->releaseChunks( ptr, p.chunks ); -} - - -int MemoryManager::extend( int chunks ) -{ - MemoryPool m ( chunks ); - m.m_pool = MemoryHelper::alignedMalloc( chunks * MM_CHUNK_SIZE ); - - s_poolMutex.lockForWrite(); - s_memoryPools.append( m ); - int i = s_memoryPools.size() - 1; - s_poolMutex.unlock(); - - return i; -} - - -void MemoryManager::cleanup() -{ - for( MemoryPoolVector::iterator it = s_memoryPools.begin(); it != s_memoryPools.end(); ++it ) - { - MemoryHelper::alignedFree( ( *it ).m_pool ); - MemoryHelper::alignedFree( ( *it ).m_free ); + if (thread_guard_depth++ == 0) { + rpmalloc_thread_initialize(); } } - -void * MemoryPool::getChunks( int chunksNeeded ) +MemoryManager::ThreadGuard::~ThreadGuard() { - if( chunksNeeded > m_chunks ) // not enough chunks in this pool? - { - return NULL; + if (--thread_guard_depth == 0) { + rpmalloc_thread_finalize(); } +} - m_mutex.lock(); +static thread_local MemoryManager::ThreadGuard local_mm_thread_guard{}; - // now find out if we have a long enough sequence of chunks in this pool - char last = 0; - intptr_t n = 0; - intptr_t index = -1; - bool found = false; - - for( int i = 0; i < m_chunks; ++i ) - { - if( m_free[i] ) - { - if( !last ) - { - index = i; - } - - ++n; - if( n >= chunksNeeded ) - { - found = true; - break; - } - } - else - { - n = 0; - } - - last = m_free[i]; - } - - if( found ) // if enough chunks found, return pointer to chunks - { - // set chunk flags to false so we know the chunks are in use - for( intptr_t i = 0; i < chunksNeeded; ++i ) - { - m_free[ index + i ] = 0; - } - m_mutex.unlock(); - return (char*)m_pool + ( index * MM_CHUNK_SIZE ); - } - m_mutex.unlock(); - return NULL; // out of stock, come again tomorrow! +void* MemoryManager::alloc(size_t size) +{ + // Reference local thread guard to ensure it is initialized. + // Compilers may optimize the instance away otherwise. + Q_UNUSED(&local_mm_thread_guard); + Q_ASSERT_X(rpmalloc_is_thread_initialized(), "MemoryManager::alloc", "Thread not initialized"); + return rpmalloc(size); } -void MemoryPool::releaseChunks( void * ptr, int chunks ) +void MemoryManager::free(void * ptr) { - m_mutex.lock(); - - intptr_t start = ( (intptr_t)ptr - (intptr_t)m_pool ) / MM_CHUNK_SIZE; - if( start < 0 ) - { - qFatal( "MemoryManager: error at releaseChunks() - corrupt pointer info?" ); - } - - memset( &m_free[ start ], 1, chunks ); - - m_mutex.unlock(); + Q_UNUSED(&local_mm_thread_guard); + Q_ASSERT_X(rpmalloc_is_thread_initialized(), "MemoryManager::free", "Thread not initialized"); + return rpfree(ptr); } diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 53cacbe63..c99d3ab8c 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -36,6 +36,7 @@ #include "NotePlayHandle.h" #include "ConfigManager.h" #include "SamplePlayHandle.h" +#include "MemoryHelper.h" // platform-specific audio-interface-classes #include "AudioAlsa.h" diff --git a/src/core/MixerWorkerThread.cpp b/src/core/MixerWorkerThread.cpp index dfcc1ff6a..e8c6bd61c 100644 --- a/src/core/MixerWorkerThread.cpp +++ b/src/core/MixerWorkerThread.cpp @@ -153,6 +153,7 @@ void MixerWorkerThread::startAndWaitForJobs() void MixerWorkerThread::run() { + MemoryManager::ThreadGuard mmThreadGuard; Q_UNUSED(mmThreadGuard); disable_denormals(); QMutex m; 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/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 52c4a16a4..62a073525 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -169,6 +169,7 @@ void ProjectRenderer::startProcessing() void ProjectRenderer::run() { + MemoryManager::ThreadGuard mmThreadGuard; Q_UNUSED(mmThreadGuard); #if 0 #ifdef LMMS_BUILD_LINUX #ifdef LMMS_HAVE_SCHED_H diff --git a/src/core/main.cpp b/src/core/main.cpp index dab9d445a..bb1dcf289 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -57,7 +57,6 @@ #include #include "MainApplication.h" -#include "MemoryManager.h" #include "ConfigManager.h" #include "NotePlayHandle.h" #include "embed.h" @@ -242,7 +241,6 @@ int main( int argc, char * * argv ) #endif // initialize memory managers - MemoryManager::init(); NotePlayHandleManager::init(); // intialize RNG @@ -973,9 +971,6 @@ int main( int argc, char * * argv ) Engine::destroy(); } - // cleanup memory managers - MemoryManager::cleanup(); - // ProjectRenderer::updateConsoleProgress() doesn't return line after render if( coreOnly ) { diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 2419a719c..39b913a80 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -589,6 +589,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) m_editMode == DRAW ) || m_editMode == ERASE ) { + m_drawLastTick = pos_ticks; m_pattern->addJournalCheckPoint(); // erase single value if( it != time_map.end() ) @@ -680,6 +681,39 @@ void AutomationEditor::mouseReleaseEvent(QMouseEvent * mouseEvent ) + +void AutomationEditor::removePoints( int x0, int x1 ) +{ + int deltax = qAbs( x1 - x0 ); + int x = x0; + int xstep; + + if( deltax < 1 ) + { + return; + } + + if( x0 < x1 ) + { + xstep = 1; + } + else + { + xstep = -1; + } + + int i = 0; + while( i <= deltax ) + { + m_pattern->removeValue( MidiTime( x ) ); + x += xstep; + i += 1; + } +} + + + + void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) { QMutexLocker m( &m_patternMutex ); @@ -735,14 +769,13 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) ( mouseEvent->buttons() & Qt::LeftButton && m_editMode == ERASE ) ) { - // int resolution needed to improve the sensitivity of - // the erase manoeuvre with zoom levels < 100% - int zoom = m_zoomingXModel.value(); - int resolution = 1 + zoom * zoom; - for( int i = -resolution; i < resolution; ++i ) + // removing automation point + if( pos_ticks < 0 ) { - m_pattern->removeValue( MidiTime( pos_ticks + i ) ); + pos_ticks = 0; } + removePoints( m_drawLastTick, pos_ticks ); + Engine::getSong()->setModified(); } else if( mouseEvent->buttons() & Qt::NoButton && m_editMode == DRAW ) { @@ -1067,7 +1100,7 @@ inline void AutomationEditor::drawAutomationPoint( QPainter & p, timeMap::iterat { int x = xCoordOfTick( it.key() ); int y = yCoordOfLevel( it.value() ); - const int outerRadius = qBound( 2, ( m_ppt * AutomationPattern::quantization() ) / 576, 5 ); // man, getting this calculation right took forever + const int outerRadius = qBound( 3, ( m_ppt * 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 ); 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(); } diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index a79b21107..372c0bb1b 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -215,28 +215,7 @@ Note * Pattern::addNote( const Note & _new_note, const bool _quant_pos ) } instrumentTrack()->lock(); - if( m_notes.size() == 0 || m_notes.back()->pos() <= new_note->pos() ) - { - m_notes.push_back( new_note ); - } - else - { - // simple algorithm for inserting the note between two - // notes with smaller and greater position - // maybe it could be optimized by starting in the middle and - // going forward or backward but note-inserting isn't that - // time-critical since it is usually not done while playing... - long new_note_abs_time = new_note->pos(); - NoteVector::Iterator it = m_notes.begin(); - - while( it != m_notes.end() && - ( *it )->pos() < new_note_abs_time ) - { - ++it; - } - - m_notes.insert( it, new_note ); - } + m_notes.insert(std::upper_bound(m_notes.begin(), m_notes.end(), new_note, Note::lessThan), new_note); instrumentTrack()->unlock(); checkType(); @@ -294,7 +273,7 @@ Note * Pattern::noteAtStep( int _step ) void Pattern::rearrangeAllNotes() { // sort notes by start time - qSort(m_notes.begin(), m_notes.end(), Note::lessThan ); + std::sort(m_notes.begin(), m_notes.end(), Note::lessThan); }