From dd429c5cafc8cf2f8173ec4aa000c3dc6b51ed00 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Mon, 25 Sep 2017 22:09:38 +0100 Subject: [PATCH 01/27] Don't let plugins make the main window transparent (#3809) * Don't let plugins make the main window transparent * Don't clear WS_EX_LAYERED if it was already set --- include/MainApplication.h | 9 +++++++++ src/gui/MainApplication.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/include/MainApplication.h b/include/MainApplication.h index 2f65ba538..8d5df9f86 100644 --- a/include/MainApplication.h +++ b/include/MainApplication.h @@ -25,13 +25,22 @@ #ifndef MAINAPPLICATION_H #define MAINAPPLICATION_H +#include "lmmsconfig.h" + #include +#ifdef LMMS_BUILD_WIN32 +#include +#endif + class MainApplication : public QApplication { public: MainApplication(int& argc, char** argv); bool event(QEvent* event); +#ifdef LMMS_BUILD_WIN32 + bool winEventFilter(MSG* msg, long* result); +#endif inline QString& queuedFile() { return m_queuedFile; diff --git a/src/gui/MainApplication.cpp b/src/gui/MainApplication.cpp index d31cf3058..767eaa8fe 100644 --- a/src/gui/MainApplication.cpp +++ b/src/gui/MainApplication.cpp @@ -62,3 +62,27 @@ bool MainApplication::event(QEvent* event) return QApplication::event(event); } } + +#ifdef LMMS_BUILD_WIN32 +bool MainApplication::winEventFilter(MSG* msg, long* result) +{ + switch(msg->message) + { + case WM_STYLECHANGING: + if(msg->wParam == GWL_EXSTYLE) + { + // Prevent plugins making the main window transparent + STYLESTRUCT * style = reinterpret_cast(msg->lParam); + if(!(style->styleOld & WS_EX_LAYERED)) + { + style->styleNew &= ~WS_EX_LAYERED; + } + *result = 0; + return true; + } + return false; + default: + return false; + } +} +#endif From f23cf4e0bf67160f43b8468b6bcf4288df5c4f30 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Tue, 26 Sep 2017 20:33:09 +0200 Subject: [PATCH 02/27] Fix "out of buffers" crash (#3783) Remove BufferManager implementation. Use MemoryManager allocation instead and re-use buffers where they are allocated (AudioPort.cpp & PlayHandle.cpp) --- include/BufferManager.h | 14 ------- include/MemoryHelper.h | 2 +- include/MemoryManager.h | 27 ++++++++++-- include/PlayHandle.h | 11 +++-- src/core/BufferManager.cpp | 80 ++++-------------------------------- src/core/MemoryHelper.cpp | 2 +- src/core/Mixer.cpp | 3 -- src/core/PlayHandle.cpp | 29 ++++++++----- src/core/audio/AudioPort.cpp | 8 ++-- 9 files changed, 60 insertions(+), 116 deletions(-) diff --git a/include/BufferManager.h b/include/BufferManager.h index db1895fd7..845f5fad4 100644 --- a/include/BufferManager.h +++ b/include/BufferManager.h @@ -30,9 +30,6 @@ #include "export.h" #include "lmms_basics.h" -const int BM_INITIAL_BUFFERS = 512; -//const int BM_INCREMENT = 64; - class EXPORT BufferManager { public: @@ -46,17 +43,6 @@ public: const f_cnt_t offset = 0 ); #endif static void release( sampleFrame * buf ); - static void refresh(); -// static void extend( int c ); - -private: - static sampleFrame ** s_available; - static AtomicInt s_availableIndex; - - static sampleFrame ** s_released; - static AtomicInt s_releasedIndex; -// static QReadWriteLock s_mutex; - static int s_size; }; #endif diff --git a/include/MemoryHelper.h b/include/MemoryHelper.h index f3a2e20a2..7bd31bf2b 100644 --- a/include/MemoryHelper.h +++ b/include/MemoryHelper.h @@ -31,7 +31,7 @@ class MemoryHelper { public: - static void* alignedMalloc( int ); + static void* alignedMalloc( size_t ); static void alignedFree( void* ); diff --git a/include/MemoryManager.h b/include/MemoryManager.h index aa8ed5cb4..ef6c0abbf 100644 --- a/include/MemoryManager.h +++ b/include/MemoryManager.h @@ -42,7 +42,7 @@ struct MemoryPool { void * m_pool; char * m_free; - int m_chunks; + size_t m_chunks; QMutex m_mutex; MemoryPool() : @@ -51,10 +51,10 @@ struct MemoryPool m_chunks( 0 ) {} - MemoryPool( int chunks ) : + MemoryPool( size_t chunks ) : m_chunks( chunks ) { - m_free = (char*) MemoryHelper::alignedMalloc( chunks ); + m_free = reinterpret_cast( MemoryHelper::alignedMalloc( chunks ) ); memset( m_free, 1, chunks ); } @@ -103,6 +103,25 @@ private: static QMutex s_pointerMutex; }; +template +struct MmAllocator +{ + typedef T value_type; + template struct rebind { typedef MmAllocator other; }; + + T* allocate( std::size_t n ) + { + return reinterpret_cast( MemoryManager::alloc( sizeof(T) * n ) ); + } + + void deallocate( T* p, std::size_t ) + { + MemoryManager::free( p ); + } + + typedef std::vector > vector; +}; + #define MM_OPERATORS \ public: \ @@ -124,7 +143,7 @@ static void operator delete[] ( void * ptr ) \ } // for use in cases where overriding new/delete isn't a possibility -#define MM_ALLOC( type, count ) (type*) MemoryManager::alloc( sizeof( type ) * count ) +#define MM_ALLOC( type, count ) reinterpret_cast( MemoryManager::alloc( sizeof( type ) * count ) ) // and just for symmetry... #define MM_FREE( ptr ) MemoryManager::free( ptr ) diff --git a/include/PlayHandle.h b/include/PlayHandle.h index 1093607fb..329a8f766 100644 --- a/include/PlayHandle.h +++ b/include/PlayHandle.h @@ -28,6 +28,8 @@ #include #include +#include "MemoryManager.h" + #include "ThreadableJob.h" #include "lmms_basics.h" @@ -142,20 +144,17 @@ public: void releaseBuffer(); - sampleFrame * buffer() - { - return m_playHandleBuffer; - } + sampleFrame * buffer(); private: Type m_type; f_cnt_t m_offset; QThread* m_affinity; QMutex m_processingLock; - sampleFrame * m_playHandleBuffer; + sampleFrame* m_playHandleBuffer; + bool m_bufferReleased; bool m_usesBuffer; AudioPort * m_audioPort; - } ; diff --git a/src/core/BufferManager.cpp b/src/core/BufferManager.cpp index 572bff7d9..2df7bcaa9 100644 --- a/src/core/BufferManager.cpp +++ b/src/core/BufferManager.cpp @@ -1,6 +1,7 @@ /* * BufferManager.cpp - A buffer caching/memory management system * + * Copyright (c) 2017 Lukas W * Copyright (c) 2014 Vesa Kivimäki * Copyright (c) 2006-2014 Tobias Doerffel * @@ -25,56 +26,28 @@ #include "BufferManager.h" +#include "Engine.h" +#include "Mixer.h" #include "MemoryManager.h" -sampleFrame ** BufferManager::s_available; -AtomicInt BufferManager::s_availableIndex = 0; -sampleFrame ** BufferManager::s_released; -AtomicInt BufferManager::s_releasedIndex = 0; -//QReadWriteLock BufferManager::s_mutex; -int BufferManager::s_size; - +static fpp_t framesPerPeriod; void BufferManager::init( fpp_t framesPerPeriod ) { - s_available = MM_ALLOC( sampleFrame*, BM_INITIAL_BUFFERS ); - s_released = MM_ALLOC( sampleFrame*, BM_INITIAL_BUFFERS ); - - int c = framesPerPeriod * BM_INITIAL_BUFFERS; - sampleFrame * b = MM_ALLOC( sampleFrame, c ); - - for( int i = 0; i < BM_INITIAL_BUFFERS; ++i ) - { - s_available[ i ] = b; - b += framesPerPeriod; - } - s_availableIndex = BM_INITIAL_BUFFERS - 1; - s_size = BM_INITIAL_BUFFERS; + ::framesPerPeriod = framesPerPeriod; } sampleFrame * BufferManager::acquire() { - if( s_availableIndex < 0 ) - { - qFatal( "BufferManager: out of buffers" ); - } - - int i = s_availableIndex.fetchAndAddOrdered( -1 ); - sampleFrame * b = s_available[ i ]; - - //qDebug( "acquired buffer: %p - index %d", b, i ); - return b; + return MM_ALLOC( sampleFrame, ::framesPerPeriod ); } - -void BufferManager::clear( sampleFrame * ab, const f_cnt_t frames, - const f_cnt_t offset ) +void BufferManager::clear( sampleFrame *ab, const f_cnt_t frames, const f_cnt_t offset ) { memset( ab + offset, 0, sizeof( *ab ) * frames ); } - #ifndef LMMS_DISABLE_SURROUND void BufferManager::clear( surroundSampleFrame * ab, const f_cnt_t frames, const f_cnt_t offset ) @@ -86,43 +59,6 @@ void BufferManager::clear( surroundSampleFrame * ab, const f_cnt_t frames, void BufferManager::release( sampleFrame * buf ) { - if (buf == nullptr) return; - int i = s_releasedIndex.fetchAndAddOrdered( 1 ); - s_released[ i ] = buf; - //qDebug( "released buffer: %p - index %d", buf, i ); + MM_FREE( buf ); } - -void BufferManager::refresh() // non-threadsafe, hence it's called periodically from mixer at a time when no other threads can interfere -{ - if( s_releasedIndex == 0 ) return; - //qDebug( "refresh: %d buffers", int( s_releasedIndex ) ); - - int j = s_availableIndex; - for( int i = 0; i < s_releasedIndex; ++i ) - { - ++j; - s_available[ j ] = s_released[ i ]; - } - s_availableIndex = j; - s_releasedIndex = 0; -} - - -/* // non-extensible for now -void BufferManager::extend( int c ) -{ - s_size += c; - sampleFrame ** tmp = MM_ALLOC( sampleFrame*, s_size ); - MM_FREE( s_available ); - s_available = tmp; - - int cc = c * Engine::mixer()->framesPerPeriod(); - sampleFrame * b = MM_ALLOC( sampleFrame, cc ); - - for( int i = 0; i < c; ++i ) - { - s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = b; - b += Engine::mixer()->framesPerPeriod(); - } -}*/ diff --git a/src/core/MemoryHelper.cpp b/src/core/MemoryHelper.cpp index 023572bb9..eb5a24d44 100644 --- a/src/core/MemoryHelper.cpp +++ b/src/core/MemoryHelper.cpp @@ -30,7 +30,7 @@ * Allocate a number of bytes and return them. * @param byteNum is the number of bytes */ -void* MemoryHelper::alignedMalloc( int byteNum ) +void* MemoryHelper::alignedMalloc( size_t byteNum ) { char *ptr, *ptr2, *aligned_ptr; int align_mask = ALIGN_SIZE - 1; diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 4ff732959..53cacbe63 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -482,9 +482,6 @@ const surroundSampleFrame * Mixer::renderNextBuffer() Controller::triggerFrameCounter(); AutomatableModel::incrementPeriodCounter(); - // refresh buffer pool - BufferManager::refresh(); - s_renderingThread = false; m_profiler.finishPeriod( processingSampleRate(), m_framesPerPeriod ); diff --git a/src/core/PlayHandle.cpp b/src/core/PlayHandle.cpp index f4b1f0aa2..5481ea3e2 100644 --- a/src/core/PlayHandle.cpp +++ b/src/core/PlayHandle.cpp @@ -24,16 +24,21 @@ #include "PlayHandle.h" #include "BufferManager.h" +#include "Engine.h" +#include "Mixer.h" #include +#include +#include -PlayHandle::PlayHandle( const Type type, f_cnt_t offset ) : - m_type( type ), - m_offset( offset ), - m_affinity( QThread::currentThread() ), - m_playHandleBuffer( NULL ), - m_usesBuffer( true ) +PlayHandle::PlayHandle(const Type type, f_cnt_t offset) : + m_type(type), + m_offset(offset), + m_affinity(QThread::currentThread()), + m_playHandleBuffer(BufferManager::acquire()), + m_bufferReleased(true), + m_usesBuffer(true) { } @@ -48,8 +53,8 @@ void PlayHandle::doProcessing() { if( m_usesBuffer ) { - if( ! m_playHandleBuffer ) m_playHandleBuffer = BufferManager::acquire(); - play( m_playHandleBuffer ); + m_bufferReleased = false; + play( buffer() ); } else { @@ -60,6 +65,10 @@ void PlayHandle::doProcessing() void PlayHandle::releaseBuffer() { - BufferManager::release( m_playHandleBuffer ); - m_playHandleBuffer = NULL; + m_bufferReleased = true; } + +sampleFrame* PlayHandle::buffer() +{ + return m_bufferReleased ? nullptr : reinterpret_cast(m_playHandleBuffer); +}; diff --git a/src/core/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index 4f779bb45..868f9f64f 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -36,7 +36,7 @@ AudioPort::AudioPort( const QString & _name, bool _has_effect_chain, FloatModel * volumeModel, FloatModel * panningModel, BoolModel * mutedModel ) : m_bufferUsage( false ), - m_portBuffer( NULL ), + m_portBuffer( BufferManager::acquire() ), m_extOutputEnabled( false ), m_nextFxChannel( 0 ), m_name( "unnamed port" ), @@ -57,6 +57,7 @@ AudioPort::~AudioPort() setExtOutputEnabled( false ); Engine::mixer()->removeAudioPort( this ); delete m_effects; + BufferManager::release( m_portBuffer ); } @@ -110,8 +111,7 @@ void AudioPort::doProcessing() const fpp_t fpp = Engine::mixer()->framesPerPeriod(); - // get a buffer for processing and clear it - m_portBuffer = BufferManager::acquire(); + // clear the buffer BufferManager::clear( m_portBuffer, fpp ); //qDebug( "Playhandles: %d", m_playHandles.size() ); @@ -225,8 +225,6 @@ void AudioPort::doProcessing() // TODO: improve the flow here - convert to pull model m_bufferUsage = false; } - - BufferManager::release( m_portBuffer ); // release buffer, we don't need it anymore } From e5db27542b3b05fe0b0cbdfe277f1ad187cf48d0 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 28 Sep 2017 09:55:16 +0900 Subject: [PATCH 03/27] Fix sound while playing from piano roll, when BB tracks are muted (#3804) * Don't set BB track for tracks not in the BB * Don't check for BB track mute state if we're playing a single pattern --- src/tracks/InstrumentTrack.cpp | 8 ++++++-- src/tracks/SampleTrack.cpp | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 3c53e4d4c..6aaf3eb75 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -157,7 +157,8 @@ InstrumentTrack::~InstrumentTrack() void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, NotePlayHandle* n ) { // we must not play the sound if this InstrumentTrack is muted... - if( isMuted() || ( n && n->isBbTrackMuted() ) || ! m_instrument ) + if( isMuted() || ( Engine::getSong()->playMode() != Song::Mode_PlayPattern && + n && n->isBbTrackMuted() ) || ! m_instrument ) { return; } @@ -613,7 +614,10 @@ bool InstrumentTrack::play( const MidiTime & _start, const fpp_t _frames, { TrackContentObject * tco = getTCO( _tco_num ); tcos.push_back( tco ); - bb_track = BBTrack::findBBTrack( _tco_num ); + if (trackContainer() == (TrackContainer*)Engine::getBBTrackContainer()) + { + bb_track = BBTrack::findBBTrack( _tco_num ); + } } else { diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 0dd491c64..3c16025a5 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -587,7 +587,10 @@ bool SampleTrack::play( const MidiTime & _start, const fpp_t _frames, return false; } tcos.push_back( getTCO( _tco_num ) ); - bb_track = BBTrack::findBBTrack( _tco_num ); + if (trackContainer() == (TrackContainer*)Engine::getBBTrackContainer()) + { + bb_track = BBTrack::findBBTrack( _tco_num ); + } } else { From 9407e84ffa8d01a27ab85c1395d24556843e2763 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 1 Oct 2017 21:01:38 +0100 Subject: [PATCH 04/27] Stop some autosave crashes (#3841) --- include/MainWindow.h | 7 ------- include/RemotePlugin.h | 33 +++++++++++++++++++++++++++++++++ src/gui/MainWindow.cpp | 21 +++++++-------------- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/include/MainWindow.h b/include/MainWindow.h index 1a58f868c..7ba2ac630 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -249,11 +249,4 @@ signals: } ; -class AutoSaveThread : public QThread -{ - Q_OBJECT -public: - void run(); -} ; - #endif diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index b3ac4b676..2d8114875 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -621,6 +621,11 @@ public: fetchAndProcessNextMessage(); } } + + static bool isMainThreadWaiting() + { + return waitDepthCounter() > 0; + } #endif virtual bool processMessage( const message & _m ) = 0; @@ -657,6 +662,14 @@ protected: private: +#ifndef BUILD_REMOTE_PLUGIN_CLIENT + static int & waitDepthCounter() + { + static int waitDepth = 0; + return waitDepth; + } +#endif + #ifdef SYNC_WITH_SHM_FIFO shmFifo * m_in; shmFifo * m_out; @@ -1089,6 +1102,26 @@ RemotePluginBase::message RemotePluginBase::waitForMessage( _busy_waiting = QThread::currentThread() == QCoreApplication::instance()->thread(); } + + struct WaitDepthCounter + { + WaitDepthCounter( int & depth, bool busy ) : + m_depth( depth ), + m_busy( busy ) + { + if( m_busy ) { ++m_depth; } + } + + ~WaitDepthCounter() + { + if( m_busy ) { --m_depth; } + } + + int & m_depth; + bool m_busy; + }; + + WaitDepthCounter wdc( waitDepthCounter(), _busy_waiting ); #endif while( !isInvalid() ) { diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 9bb4bfea6..b91fe3ef2 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -54,6 +54,7 @@ #include "PluginView.h" #include "ProjectJournal.h" #include "ProjectNotes.h" +#include "RemotePlugin.h" #include "SetupDialog.h" #include "SideBar.h" #include "SongEditor.h" @@ -1535,14 +1536,14 @@ void MainWindow::browseHelp() void MainWindow::autoSave() { if( !Engine::getSong()->isExporting() && + !Engine::getSong()->isLoadingProject() && + !RemotePluginBase::isMainThreadWaiting() && !QApplication::mouseButtons() && - ( ConfigManager::inst()->value( "ui", - "enablerunningautosave" ).toInt() || - ! Engine::getSong()->isPlaying() ) ) + ( ConfigManager::inst()->value( "ui", + "enablerunningautosave" ).toInt() || + ! Engine::getSong()->isPlaying() ) ) { - AutoSaveThread * ast = new AutoSaveThread(); - connect( ast, SIGNAL( finished() ), ast, SLOT( deleteLater() ) ); - ast->start(); + Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile()); autoSaveTimerReset(); // Reset timer } else @@ -1554,11 +1555,3 @@ void MainWindow::autoSave() } } } - - - - -void AutoSaveThread::run() -{ - Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile()); -} From fb5a515f77ebae9b9bd861478a8d624ad3d59c80 Mon Sep 17 00:00:00 2001 From: Umcaruje Date: Sun, 1 Oct 2017 22:10:45 +0200 Subject: [PATCH 05/27] Bump to rc4 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0306c5c2..6e1861eba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ SET(PROJECT_COPYRIGHT "2008-${PROJECT_YEAR} ${PROJECT_AUTHOR}") SET(VERSION_MAJOR "1") SET(VERSION_MINOR "2") SET(VERSION_RELEASE "0") -SET(VERSION_STAGE "rc3") +SET(VERSION_STAGE "rc4") SET(VERSION_BUILD "0") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}") IF(VERSION_STAGE) From e7e24935b35a15066c549f4634b35ffb81910226 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Mon, 2 Oct 2017 23:47:45 +0100 Subject: [PATCH 06/27] catching-up for openbsd sndio support + applying abs call fix (#3839) --- include/AudioSndio.h | 2 ++ src/core/audio/AudioSndio.cpp | 26 ++++++++++++++++---------- src/core/midi/MidiSndio.cpp | 12 ++++++------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/include/AudioSndio.h b/include/AudioSndio.h index 11e1f91e4..22a15abc5 100644 --- a/include/AudioSndio.h +++ b/include/AudioSndio.h @@ -72,6 +72,8 @@ private: struct sio_hdl *m_hdl; struct sio_par m_par; + + bool m_convertEndian; } ; diff --git a/src/core/audio/AudioSndio.cpp b/src/core/audio/AudioSndio.cpp index ca889d88c..3145eb218 100644 --- a/src/core/audio/AudioSndio.cpp +++ b/src/core/audio/AudioSndio.cpp @@ -27,9 +27,9 @@ #ifdef LMMS_HAVE_SNDIO -#include -#include -#include +#include +#include +#include #include "endian_handling.h" #include "LcdSpinBox.h" @@ -52,9 +52,10 @@ AudioSndio::AudioSndio(bool & _success_ful, Mixer * _mixer) : AudioDevice( tLimit( ConfigManager::inst()->value( "audiosndio", "channels" ).toInt(), - DEFAULT_CHANNELS, SURROUND_CHANNELS ), _mixer ) + DEFAULT_CHANNELS, SURROUND_CHANNELS ), _mixer ), + m_convertEndian ( false ) { - _success_ful = FALSE; + _success_ful = false; QString dev = ConfigManager::inst()->value( "audiosndio", "device" ); @@ -64,7 +65,7 @@ AudioSndio::AudioSndio(bool & _success_ful, Mixer * _mixer) : } else { - m_hdl = sio_open( dev.toAscii().data(), SIO_PLAY, 0 ); + m_hdl = sio_open( dev.toLatin1().constData(), SIO_PLAY, 0 ); } if( m_hdl == NULL ) @@ -82,6 +83,11 @@ AudioSndio::AudioSndio(bool & _success_ful, Mixer * _mixer) : m_par.round = mixer()->framesPerPeriod(); m_par.appbufsz = m_par.round * 2; + if ( (isLittleEndian() && (m_par.le == 0)) || + (!isLittleEndian() && (m_par.le == 1))) { + m_convertEndian = true; + } + struct sio_par reqpar = m_par; if (!sio_setpar(m_hdl, &m_par)) @@ -98,7 +104,7 @@ AudioSndio::AudioSndio(bool & _success_ful, Mixer * _mixer) : if (reqpar.pchan != m_par.pchan || reqpar.bits != m_par.bits || reqpar.le != m_par.le || - (abs(reqpar.rate - m_par.rate) * 100)/reqpar.rate > 2) + (::abs(static_cast(reqpar.rate) - static_cast(m_par.rate)) * 100)/reqpar.rate > 2) { printf( "sndio: returned params not as requested\n" ); return; @@ -110,7 +116,7 @@ AudioSndio::AudioSndio(bool & _success_ful, Mixer * _mixer) : return; } - _success_ful = TRUE; + _success_ful = true; } @@ -160,7 +166,7 @@ void AudioSndio::run( void ) int_sample_t * outbuf = new int_sample_t[mixer()->framesPerPeriod() * channels()]; - while( TRUE ) + while( true ) { const fpp_t frames = getNextBuffer( temp ); if( !frames ) @@ -169,7 +175,7 @@ void AudioSndio::run( void ) } uint bytes = convertToS16( temp, frames, - mixer()->masterGain(), outbuf, FALSE ); + mixer()->masterGain(), outbuf, m_convertEndian ); if( sio_write( m_hdl, outbuf, bytes ) != bytes ) { break; diff --git a/src/core/midi/MidiSndio.cpp b/src/core/midi/MidiSndio.cpp index 6827fbf81..4a1ed0614 100644 --- a/src/core/midi/MidiSndio.cpp +++ b/src/core/midi/MidiSndio.cpp @@ -27,8 +27,8 @@ #ifdef LMMS_HAVE_SNDIO -#include -#include +#include +#include #ifdef LMMS_HAVE_STDLIB_H #include @@ -42,7 +42,7 @@ MidiSndio::MidiSndio( void ) : MidiClientRaw(), - m_quit( FALSE ) + m_quit( false ) { QString dev = probeDevice(); @@ -52,7 +52,7 @@ MidiSndio::MidiSndio( void ) : } else { - m_hdl = mio_open( dev.toAscii().data(), MIO_IN | MIO_OUT, 0 ); + m_hdl = mio_open( dev.toLatin1().constData(), MIO_IN | MIO_OUT, 0 ); } if( m_hdl == NULL ) @@ -69,7 +69,7 @@ MidiSndio::~MidiSndio() { if( isRunning() ) { - m_quit = TRUE; + m_quit = true; wait( 1000 ); terminate(); } @@ -97,7 +97,7 @@ void MidiSndio::run( void ) char buf[0x100], *p; size_t n; int ret; - while( m_quit == FALSE && m_hdl ) + while( m_quit == false && m_hdl ) { nfds = mio_pollfd( m_hdl, &pfd, POLLIN ); ret = poll( &pfd, nfds, 100 ); From 6da87379b4b5e69561a424ea13e604c7f077526c Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Mon, 9 Oct 2017 06:29:23 +0200 Subject: [PATCH 07/27] Arpeggiator fixes (#3858) Set 'master note' for silent notes on sort mode. Prevent silent notes to play as ordinary notes in the background when they are waiting for their turn to arpeggiate. Remove unnecessary test (it will always return TRUE). Slower default speed (200ms). --- src/core/InstrumentFunctions.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 4a542b081..b1c4be22a 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -305,7 +305,7 @@ InstrumentFunctionArpeggio::InstrumentFunctionArpeggio( Model * _parent ) : m_arpCycleModel( 0.0f, 0.0f, 6.0f, 1.0f, this, tr( "Cycle steps" ) ), m_arpSkipModel( 0.0f, 0.0f, 100.0f, 1.0f, this, tr( "Skip rate" ) ), m_arpMissModel( 0.0f, 0.0f, 100.0f, 1.0f, this, tr( "Miss rate" ) ), - m_arpTimeModel( 100.0f, 25.0f, 2000.0f, 1.0f, 2000, this, tr( "Arpeggio time" ) ), + m_arpTimeModel( 200.0f, 25.0f, 2000.0f, 1.0f, 2000, this, tr( "Arpeggio time" ) ), m_arpGateModel( 100.0f, 1.0f, 200.0f, 1.0f, this, tr( "Arpeggio gate" ) ), m_arpDirectionModel( this, tr( "Arpeggio direction" ) ), m_arpModeModel( this, tr( "Arpeggio mode" ) ) @@ -396,14 +396,13 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) frames_processed += remaining_frames_for_cur_arp; - // init with zero - int cur_arp_idx = 0; - // in sorted mode: is it our turn or do we have to be quiet for // now? if( m_arpModeModel.value() == SortMode && ( ( cur_frame / arp_frames ) % total_range ) / range != (f_cnt_t) _n->index() ) { + // Set master note if not playing arp note or it will play as an ordinary note + _n->setMasterNote(); // update counters frames_processed += arp_frames; cur_frame += arp_frames; @@ -416,10 +415,9 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) if( 100 * ( (float) rand() / (float)( RAND_MAX + 1.0f ) ) < m_arpSkipModel.value() ) { - if( cur_arp_idx == 0 ) - { - _n->setMasterNote(); - } + // Set master note to prevent the note to extend over skipped notes + // This may only be needed for lb302 + _n->setMasterNote(); // update counters frames_processed += arp_frames; cur_frame += arp_frames; @@ -440,6 +438,7 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) } } + int cur_arp_idx = 0; // process according to arpeggio-direction... if( dir == ArpDirUp ) { From 2d583db990385506bff7020f4b4109976af5835f Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Tue, 10 Oct 2017 06:05:11 +0200 Subject: [PATCH 08/27] Fix arpeggio sort mode (#3867) When in sort mode and playing over multiple base notes, in the beginning of the notes there is a chance that the notes will play together as an ordinary chord instead of arpeggiate. This is a regression from 6650dd3. Fixes #3342 --- src/core/InstrumentFunctions.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index b1c4be22a..2860045b8 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -524,6 +524,13 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) frames_processed += arp_frames; cur_frame += arp_frames; } + + // make sure note is handled as arp-base-note, even + // if we didn't add a sub-note so far + if( m_arpModeModel.value() != FreeMode ) + { + _n->setMasterNote(); + } } From d0cd42ee5e60123b5ad3fe1154cfe27736b71a4c Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Tue, 10 Oct 2017 18:43:03 +0200 Subject: [PATCH 09/27] Update project year --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e1861eba..02dd0131a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ INCLUDE(FindPkgConfig) STRING(TOUPPER "${CMAKE_PROJECT_NAME}" PROJECT_NAME_UCASE) # Updated by maintenance tasks -SET(PROJECT_YEAR 2015) +SET(PROJECT_YEAR 2017) SET(PROJECT_AUTHOR "LMMS Developers") SET(PROJECT_URL "https://lmms.io") From f24f8c7b00fc9b4e0cd4541f89f977772154450f Mon Sep 17 00:00:00 2001 From: Rebecca LaVie Date: Tue, 10 Oct 2017 10:05:37 -0700 Subject: [PATCH 10/27] LCD Updates (#3864) --- data/themes/default/lcd_19green.png | Bin 643 -> 808 bytes data/themes/default/lcd_19red.png | Bin 637 -> 799 bytes data/themes/default/lcd_21pink.png | Bin 641 -> 803 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/themes/default/lcd_19green.png b/data/themes/default/lcd_19green.png index 437ac8baf587588d812fe0f8196202df21392021..708987c34144cfe33233b73022561d7443021b84 100644 GIT binary patch delta 726 zcmZo>UBNcNnw#$k0|Va?#;MwT(i7d(>lxHaTq8Qu7po^Gl18 zQx#lNi!;;n6kKysb5rw5iY4zBcQG(9h=FuCmX>7X7cm5-CZ#4PCjwQvWv1q&7{C9m z4O9^UQsJCmP+63jo>9W!>};SAP@0sJnXKTFpPQJO2UMPtnVeXXnO~o$kWo@nV5P60 zT$BnFC`(Pw&&|!xE7r>|O4ly{Yf1s@(XUD^%GWp4GtfWwc<)aJ2FACZE{-7;bKcIf z^^XjKn9(k~#(zF_%+Cp;bVCI=L-aQQj)TQIA#+OjWj?wH>c5ZM*}`pucoR?oDvDB!s-)U^T`RB*Pz|Hmb^D>oR zYu)PaZaj)z;l$@=;j&W%5RlhPX2WHu%mZl{noobl@|3rS#$MX zR`QGDwd?Lr-N%|R_ndH{{_GMf({pRKYOgx$tbIN5vC7^Hl@4u>7$v*dQ#Q>^pDbLb zWZNv7zKyppKWF<@E&i^ug0&aJn(B|AsgUGxLD(k(bmf^dW%=jWTH~+xr=^)$-_p)1 zTjyt-n-c5#yT10fNhMEk*7dorNk4x{UaMO8xxwd~dg7`VpMPyy zI2ZMHty!Dh^;eBRb&=0IcTD%Snk$ul?Uv}(XA7skUQ;+J?u7$VxNJVjX+Lq^!7k(5 vY{$*F-OifI?rP04mp!F|S2P9apU>P&qI6Deys2*wOkoV3u6{1-oD!MQAv_q)#2pXA3 z&@{u6?Iki?ih3mGLEe24ttTIZT88Lbw>@7VTI9@-y&>At8oE+zghh59W>^*Dlb8od zFHu;cj2txzF=(>bsj;J{7LAJ-X!-VL|p2fp-bCd7tta$I9+?oJ&h49e^W1V zzNL4O+Z&=CLY+s@$UK5(kspIOk>OI*BQX#1?u%$W`5@FXMBlpY`2x`*XNK$z(U#WG zm0BY#vhy&*su-WdJV<((GU=2UqIN1OJ%?JUvm{AI(ZDQdWFA2y^9Y(`=9zNBcl;g9 yB(rTud}F7rTIaxMhfwDcH2Mrx8qlco2%0}ofh^Ji;k`zq{2DBpt2}4J)?xd+1WrLpfo8bGg-kUKQ}Qm52!pPGdZy&GrvAhA)}d42(}aT^vIy=DfYN z(d&qVz_E`zKL>E{blb3D%cs&K>KndQuBm1ESh_|>mu2>0Csy0lt+yxaJySE=SLEM* zzaJd4&!%ir-fpnDel26u(>vS;{=0s3*k)v09TU&Tw%*J?Ev;;yjZWG-KfO&kPj-2I z`}Vi;o9UUEcfadQnm&Jh!L{yl%r~>X_lI2E?UmoO+$%W!#p$y3(kI=nP55#*`ep8y zl&N3czHR>&Ipv-1Bw45DjXjS*NXPoK%f8xqIkRe%Y%U+War50zrTR&|Zz5M$ua0%T zf8~|q^G2Q5HWOc_p7LCtW|VC!di9<2>Xn<*CVg4-*}?522u+(?xyn3NoC8_E2PHaCv*>|#kOZqJd>)W>RE}JIWW|!t~&3-XE$`+_D zb*{S6ULVVsSA5deE?;!3Hp8^^n$M)T61fu^U}V0@`1}<0KPt7&Q%}Cp_o+$tk6!!D o@g(2E&ko1V2PiK7?C_1d^}B{vDjN?gFkvxxy85}Sb4q9e0B$5uWB>pF delta 554 zcmV+_0@eMW2K@w(I|~ih000fw0YWI7c#%dYe*yYQL_t(|ob8&iPQySDL^r_#3X}$k zf|82gL3{x<-$Tt8Aby96k^+e)q5vca)k-{y!DMH-#MdlGSjhWf==6X+ChmsqMQ9=k4zY)<#k1 ze-Sjz{ttKbKDj3$;!^JnUE21#h!&~A>DpWFX^d!@dXe)jy^Gx55bY4^Jc35%5j4%P zMG%}B%k$D77V`g@DdNC+`{CYPtv(4%#tGPWnPqfIojP~jKWA+;6 z)b`zlv)%o|+9>Kgf+qQs&>g)`?n#Ka)H_3$w!JQ*MQU)m_Lh4ZBU+|jMG|9{}<%I9}JD5pkhmiQj sPFuCkfzb}3&Le2_8LFUB=Mgmj6y3-uGG?~1VE_OC07*qoM6N<$g84TN!~g&Q diff --git a/data/themes/default/lcd_21pink.png b/data/themes/default/lcd_21pink.png index 64ec75e33d9e9ba812a24319f7a164372b6a1985..c2009eedac64d7101c283db71982d39a50d493a6 100644 GIT binary patch delta 721 zcmZolxHaTq8Qu7po^Gl18 zQx#lNi!;;n6kKysb5rw5iY4zBcQG(9h=FuCmX>7X7cm5-CZ#4PCjwQvWv1q&7{C9m z4O9^UQsJCmP+63jo>9W!>};SAP@0sJnXKTFpPQJO2UMPtnVeXXnO~o$kWo@nV5P60 zT$BnFC`(Pw&&|!xE7r>|O4ly{Yf1s@(XUD^%GWp4GtfWwc<)aJ2F4ejE{-7;bKc%E z^g7}oa_r-FA;%+^4&@#x%I1H-xTpR4h7VHr4l`FUyw*LMrmN%1!n#)OP0N|Id*(BH z5Bd!?vmxTTl@aZnKwT&W}aQ6mp3Wr z$u6(5zcu#1rJkL6S0(oDak22ZX^TEPT+99cP;{^EceN=YE6dv!=KS9^F(-vL@5Q~M z{AACt$o!Q0?D?+m?kU|gERj2*0Y;OI{h!%&SRURt;#~Y@$dB4F& zU)$WRx7by!+s-lHJoLj*!Ub*@&_*lc&7U{FzWCtu>*kp=Wy{oWp851e>!jPd)hXur z{hQY()}MQ`F8$%YefJOF3b3E+J5JD#H7$` z(l_tlN}srUyGI$%Ty~=_5Zd(QjPTEIQ&Va`b!yBsTXyc-d2wJEoR7L2{Z^`N*)<`M zq348W#(lGVdBrDf?eaypYBNkrulY<``od;Hpp7*UPf$&tS3B>+-1_FJC*SD%)CBuS rul?qDvTxyMhhygh6c>MX_{P1_R6{6J(fSE6aWQzh`njxgN@xNAQn*Uh delta 558 zcmV+}0@3}W27v{TI|~ih000fw0YWI7c#%dYe*ykUL_t(|ob8%1N(4a=MQddQ5kmtv z5yOo=fCum%Uci?de*@6GEg-=u6{imk>GAlFt zGa(3$<}N@i7nw)UG-hVkoBKiG%l5&{%uY|%SOHq%+JjlB*qV?p1P|FZ~>$c|$M2nmmvNuFqT0>WAjj+hh!wjopd=m2@ z>1E2KQ(}nPsi^cEYNgJSBpF2mv!IcAe*}%pBWRMDXUYlR@pmwj%nl*(jh(h?odcsC zLY+s@tV|z+2@-W4K_l}B8ktAX$UK6kF*CcoxE&O}y+4_m*>Q80)vV8N5-qYWquqTx z&tAiv+P=GRetth;Z4`ALL6iJR=#Jhe_asDI>YbrW+g=yZA~iT&d&@nI5iL_Me{#O1 zcahs0q8&n=N6^STf@YB)gE^7mQq&_c5AyDdXg&EL)G|chy6yP_(IRJt>FvF@CpTs;!dYLlmlo+CRDk?pPTB)-nNk-AYENEmNK_l}Bnq=mga>94~9n2)N wLr8pMr>$D&z-Wh1=Mgme3{}vm81o34f9@(M8f&?IzyJUM07*qoM6N<$f*_s<0{{R3 From 54f3eccad77103ff7441470cbfc5739180f3c1ba Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Wed, 11 Oct 2017 02:35:02 +0100 Subject: [PATCH 11/27] linkage issue fixes due to inlined functions (#3815) --- plugins/LadspaEffect/swh/hermes_filter_1200.c | 6 +++--- plugins/LadspaEffect/swh/sifter_1210.c | 4 ++-- plugins/LadspaEffect/swh/util/waveguide_nl.h | 14 +++++++------- plugins/LadspaEffect/swh/vocoder_1337.c | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/LadspaEffect/swh/hermes_filter_1200.c b/plugins/LadspaEffect/swh/hermes_filter_1200.c index 5cac646ba..2f4386488 100644 --- a/plugins/LadspaEffect/swh/hermes_filter_1200.c +++ b/plugins/LadspaEffect/swh/hermes_filter_1200.c @@ -73,7 +73,7 @@ typedef struct { float *op; // pointer to output value } sv_filter; -inline float soft_clip(float sc_in) { +float soft_clip(float sc_in) { if ((sc_in < CLIP) && (sc_in > -CLIP)) { return sc_in; } else if (sc_in > 0.0f) { @@ -86,7 +86,7 @@ inline float soft_clip(float sc_in) { /* Store data in SVF struct, takes the sampling frequency, cutoff frequency and Q, and fills in the structure passed */ -inline void setup_svf(sv_filter *sv, float fs, float fc, float q, int t) { +void setup_svf(sv_filter *sv, float fs, float fc, float q, int t) { sv->f = 2.0f * sinf(M_PI * fc / (float)(fs * F_R)); sv->q = 2.0f * cosf(powf(q, 0.1f) * M_PI * 0.5f); sv->qnrm = sqrtf(sv->q*0.5f + 0.01f); @@ -111,7 +111,7 @@ inline void setup_svf(sv_filter *sv, float fs, float fc, float q, int t) { /* Change the frequency of a running SVF */ -inline void setup_f_svf(sv_filter *sv, const float fs, const float fc) { +void setup_f_svf(sv_filter *sv, const float fs, const float fc) { sv->f = 2.0f * sin(M_PI * fc / ((float)(fs * F_R))); } diff --git a/plugins/LadspaEffect/swh/sifter_1210.c b/plugins/LadspaEffect/swh/sifter_1210.c index 0e401e887..f7d8b557b 100644 --- a/plugins/LadspaEffect/swh/sifter_1210.c +++ b/plugins/LadspaEffect/swh/sifter_1210.c @@ -31,7 +31,7 @@ static void __attribute__((constructor)) swh_init(); // forward declaration #define MAX_BSIZE 1000 -inline int partition(LADSPA_Data array[], int left, int right); +int partition(LADSPA_Data array[], int left, int right); void q_sort(LADSPA_Data array[], int left, int right) { float pivot = partition(array, left, right); @@ -44,7 +44,7 @@ void q_sort(LADSPA_Data array[], int left, int right) { } } -inline int partition(LADSPA_Data array[], int left, int right) { +int partition(LADSPA_Data array[], int left, int right) { float pivot = array[left]; while (left < right) { diff --git a/plugins/LadspaEffect/swh/util/waveguide_nl.h b/plugins/LadspaEffect/swh/util/waveguide_nl.h index 398518eb0..e882e7771 100644 --- a/plugins/LadspaEffect/swh/util/waveguide_nl.h +++ b/plugins/LadspaEffect/swh/util/waveguide_nl.h @@ -35,7 +35,7 @@ waveguide_nl *waveguide_nl_new(int size, float fc, float da, float db) return wg; } -inline void waveguide_nl_reset(waveguide_nl *wg) +void waveguide_nl_reset(waveguide_nl *wg) { memset(wg->buffer[0], 0, wg->size * sizeof(float)); memset(wg->buffer[1], 0, wg->size * sizeof(float)); @@ -45,7 +45,7 @@ inline void waveguide_nl_reset(waveguide_nl *wg) wg->zm1[1] = 0.0f; } -inline void waveguide_nl_free(waveguide_nl *wg) +void waveguide_nl_free(waveguide_nl *wg) { if (!wg) { return; @@ -55,7 +55,7 @@ inline void waveguide_nl_free(waveguide_nl *wg) free(wg); } -inline void waveguide_nl_set_delay(waveguide_nl *wg, int delay) +void waveguide_nl_set_delay(waveguide_nl *wg, int delay) { if (delay > wg->size) { wg->delay = wg->size; @@ -66,18 +66,18 @@ inline void waveguide_nl_set_delay(waveguide_nl *wg, int delay) } } -inline void waveguide_nl_set_fc(waveguide_nl *wg, float fc) +void waveguide_nl_set_fc(waveguide_nl *wg, float fc) { wg->fc = fc; } -inline void waveguide_nl_set_ap(waveguide_nl *wg, float da, float db) +void waveguide_nl_set_ap(waveguide_nl *wg, float da, float db) { wg->a1a = (1.0f - da) / (1.0f + da); wg->a1b = (1.0f - db) / (1.0f + db); } -inline void waveguide_nl_process_lin(waveguide_nl *wg, float in0, float in1, float *out0, float *out1) +void waveguide_nl_process_lin(waveguide_nl *wg, float in0, float in1, float *out0, float *out1) { float tmp; @@ -103,7 +103,7 @@ inline void waveguide_nl_process_lin(waveguide_nl *wg, float in0, float in1, flo } } -inline void waveguide_nl_process(waveguide_nl *wg, float in0, float in1, float *out0, float *out1) +void waveguide_nl_process(waveguide_nl *wg, float in0, float in1, float *out0, float *out1) { float tmp; float a1; diff --git a/plugins/LadspaEffect/swh/vocoder_1337.c b/plugins/LadspaEffect/swh/vocoder_1337.c index d28975fe7..957a2f381 100644 --- a/plugins/LadspaEffect/swh/vocoder_1337.c +++ b/plugins/LadspaEffect/swh/vocoder_1337.c @@ -47,7 +47,7 @@ struct bandpasses LADSPA_Data y[MAX_BANDS]; }; -void inline doBandpasses(struct bandpasses *bands, LADSPA_Data sample, int num_bands); +void doBandpasses(struct bandpasses *bands, LADSPA_Data sample, int num_bands); struct bands_out{ LADSPA_Data decay[MAX_BANDS]; @@ -65,7 +65,7 @@ const LADSPA_Data decay_table[] = 1/250.0, 1/250.0, 1/250.0 }; -void inline doBandpasses(struct bandpasses *bands, LADSPA_Data sample, int num_bands) +void doBandpasses(struct bandpasses *bands, LADSPA_Data sample, int num_bands) { int i; for (i=0; i < num_bands; i++) From 2d07efd1dd48a1e33399f036e4b0b952c890d1ff Mon Sep 17 00:00:00 2001 From: Hyunin Song Date: Sun, 15 Oct 2017 22:38:37 +0900 Subject: [PATCH 12/27] Replace NotePlayHandle::done() to the actual destructor --- include/NotePlayHandle.h | 3 +-- src/core/NotePlayHandle.cpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index 344980eb6..768a74aa2 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -66,8 +66,7 @@ public: NotePlayHandle* parent = NULL, int midiEventChannel = -1, Origin origin = OriginPattern ); - virtual ~NotePlayHandle() {} - void done(); + virtual ~NotePlayHandle(); void * operator new ( size_t size, void * p ) { diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 84d888fee..11f71d8f1 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -128,7 +128,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, } -void NotePlayHandle::done() +NotePlayHandle::~NotePlayHandle() { lock(); noteOff( 0 ); @@ -599,7 +599,7 @@ NotePlayHandle * NotePlayHandleManager::acquire( InstrumentTrack* instrumentTrac void NotePlayHandleManager::release( NotePlayHandle * nph ) { - nph->done(); + nph->NotePlayHandle::~NotePlayHandle(); s_mutex.lockForRead(); s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = nph; s_mutex.unlock(); From eaa7b0dd7cc958096659a281c588041edeccb4a2 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 17 Oct 2017 00:14:47 +0900 Subject: [PATCH 13/27] Fix sfxr buffer noise (#3883) --- plugins/sfxr/sfxr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index b0222a0b1..303105761 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -464,6 +464,7 @@ void sfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe } else if( static_cast(_n->m_pluginData)->isPlaying() == false ) { + memset(_working_buffer + offset, 0, sizeof(sampleFrame) * frameNum); _n->noteOff(); return; } From 221d0cb355f55d751b7aa7ef5f6fc2f229440e1f Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Mon, 16 Oct 2017 13:48:38 -0400 Subject: [PATCH 14/27] Switch Travis-CI to Qt5.8 (#3885) --- .travis/linux..before_install.sh | 1 + .travis/linux..install.sh | 2 +- .travis/linux..script.sh | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis/linux..before_install.sh b/.travis/linux..before_install.sh index f5e3b435c..fc43bafae 100644 --- a/.travis/linux..before_install.sh +++ b/.travis/linux..before_install.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash +sudo add-apt-repository ppa:beineri/opt-qt58-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 diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index f5988a83d..9c7d3c4b1 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -9,7 +9,7 @@ PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev PACKAGES="$PACKAGES libjack0" if [ $QT5 ]; then - PACKAGES="$PACKAGES qtbase5-dev qttools5-dev-tools qttools5-dev" + PACKAGES="$PACKAGES qt58base qt58translations qt58tools" else PACKAGES="$PACKAGES libqt4-dev" fi diff --git a/.travis/linux..script.sh b/.travis/linux..script.sh index 895f5875a..8fdf66027 100644 --- a/.travis/linux..script.sh +++ b/.travis/linux..script.sh @@ -1,3 +1,7 @@ #!/usr/bin/env bash +if [ $QT5 ]; then + unset QTDIR QT_PLUGIN_PATH LD_LIBRARY_PATH + source /opt/qt58/bin/qt58-env.sh +fi cmake -DUSE_WERROR=ON $CMAKE_FLAGS .. From c051e9bf342d66caf183d73aca3dbe55dbea9457 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Mon, 16 Oct 2017 14:46:25 -0400 Subject: [PATCH 15/27] Use weak loading of libjack (#3887) --- CMakeLists.txt | 11 +- include/AudioJack.h | 4 + include/AudioWeakJack.def | 159 +++++++++++++++++++ include/AudioWeakJack.h | 237 ++++++++++++++++++++++++++++ include/MidiJack.h | 4 + src/CMakeLists.txt | 2 +- src/core/CMakeLists.txt | 5 + src/core/audio/AudioWeakJack.c | 273 +++++++++++++++++++++++++++++++++ src/lmmsconfig.h.in | 1 + 9 files changed, 694 insertions(+), 2 deletions(-) create mode 100644 include/AudioWeakJack.def create mode 100644 include/AudioWeakJack.h create mode 100644 src/core/audio/AudioWeakJack.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 02dd0131a..609107b42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,7 @@ OPTION(WANT_CAPS "Include C* Audio Plugin Suite (LADSPA plugins)" ON) OPTION(WANT_CARLA "Include Carla plugin" ON) OPTION(WANT_CMT "Include Computer Music Toolkit LADSPA plugins" ON) OPTION(WANT_JACK "Include JACK (Jack Audio Connection Kit) support" ON) +OPTION(WANT_WEAKJACK "Loosely link JACK libraries" ON) OPTION(WANT_MP3LAME "Include MP3/Lame support" ON) OPTION(WANT_OGGVORBIS "Include OGG/Vorbis support" ON) OPTION(WANT_PULSEAUDIO "Include PulseAudio support" ON) @@ -364,8 +365,16 @@ ENDIF(NOT LMMS_HAVE_ALSA) IF(WANT_JACK) PKG_CHECK_MODULES(JACK jack>=0.77) IF(JACK_FOUND) + IF(WANT_WEAKJACK) + SET(LMMS_HAVE_WEAKJACK TRUE) + SET(STATUS_JACK "OK (weak linking enabled)") + SET(JACK_INCLUDE_DIRS "") + # use dlsym instead + SET(JACK_LIBRARIES "dl") + ELSE() + SET(STATUS_JACK "OK") + ENDIF() SET(LMMS_HAVE_JACK TRUE) - SET(STATUS_JACK "OK") ELSE(JACK_FOUND) SET(STATUS_JACK "not found, please install libjack0.100.0-dev (or similar) " "if you require JACK support") diff --git a/include/AudioJack.h b/include/AudioJack.h index 7069a0691..9bbb3bd48 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -28,7 +28,11 @@ #include "lmmsconfig.h" #ifdef LMMS_HAVE_JACK +#ifndef LMMS_HAVE_WEAKJACK #include +#else +#include "AudioWeakJack.h" +#endif #include #include diff --git a/include/AudioWeakJack.def b/include/AudioWeakJack.def new file mode 100644 index 000000000..1684a56b0 --- /dev/null +++ b/include/AudioWeakJack.def @@ -0,0 +1,159 @@ +/* macro-absraction of the JACK API + * + * see weak_libjack.c for details, in general arguments are: + * + * [required], [return type], [name], [arguments], [code or return value] + * + * This file is included multiple times with different macro definitions + * do not add header guards. + * see https://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros + */ + +#ifdef USE_WEAK_JACK + +/* */ +JCFUN(1, int, client_close, 0) +JCFUN(1, char*, get_client_name, NULL) +JVFUN(0, on_shutdown, (jack_client_t *c, JackShutdownCallback s, void *a), (c,s,a),) +JVFUN(0, on_info_shutdown, (jack_client_t *c, JackInfoShutdownCallback s, void *a), (c,s,a),) + +JPFUN(1, int, set_process_callback, (jack_client_t *c, JackProcessCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_freewheel_callback, (jack_client_t *c, JackFreewheelCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_buffer_size_callback, (jack_client_t *c, JackBufferSizeCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_sample_rate_callback, (jack_client_t *c, JackSampleRateCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_port_registration_callback, (jack_client_t *c, JackPortRegistrationCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_port_connect_callback, (jack_client_t *c, JackPortConnectCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_graph_order_callback, (jack_client_t *c, JackGraphOrderCallback g, void *a), (c,g,a), -1) +JPFUN(1, int, set_xrun_callback, (jack_client_t *c, JackXRunCallback g, void *a), (c,g,a), -1) +JPFUN(1, int, set_latency_callback, (jack_client_t *c, JackLatencyCallback g, void *a), (c,g,a), -1) +JVFUN(1, set_error_function, (void (*f)(const char *)), (f),) +JVFUN(1, set_info_function, (void (*f)(const char *)), (f),) + +JCFUN(1, int, activate, -1) +JCFUN(1, int, deactivate, -1) + +JPFUN(1, int, client_name_size, (), (), 32) + +JCFUN(1, jack_nframes_t, get_sample_rate, 0) +JCFUN(1, jack_nframes_t, get_buffer_size, 0) +JPFUN(1, jack_nframes_t, frames_since_cycle_start, (const jack_client_t *c), (c), 0) +JPFUN(1, jack_nframes_t, frame_time, (const jack_client_t *c), (c), 0) +JPFUN(1, jack_nframes_t, last_frame_time, (const jack_client_t *c), (c), 0) +JPFUN(1, jack_time_t, get_time, (void), (), 0) +JCFUN(1, float, cpu_load, 0) +JCFUN(1, int, is_realtime, 0) + +JPFUN(1, int, set_freewheel, (jack_client_t *c, int o), (c,o), 0) +JPFUN(1, int, set_buffer_size, (jack_client_t *c, jack_nframes_t b), (c,b), 0) + +JCFUN(0, int, recompute_total_latencies, 0) +JPFUN(0, jack_nframes_t, port_get_total_latency, (jack_client_t *c, jack_port_t *p), (c,p), 0) +JVFUN(0, port_get_latency_range, (jack_port_t *p, jack_latency_callback_mode_t m, jack_latency_range_t *r), (p,m,r), if (r) {r->min = r->max = 0;}) +JVFUN(0, port_set_latency_range, (jack_port_t *p, jack_latency_callback_mode_t m, jack_latency_range_t *r), (p,m,r),) + +JPFUN(1, void*, port_get_buffer, (jack_port_t *p, jack_nframes_t n), (p,n), NULL) +JPFUN(1, int, port_request_monitor, (jack_port_t *p, int o), (p,o), 0) +JPFUN(1, int, port_ensure_monitor, (jack_port_t *p, int o), (p,o), 0) +JPFUN(1, int, port_monitoring_input, (jack_port_t *p), (p), 0) + +JPFUN(1, const char*, port_name, (const jack_port_t *p), (p), NULL) +JPFUN(1, const char*, port_short_name, (const jack_port_t *p), (p), NULL) +JPFUN(1, int, port_flags, (const jack_port_t *p), (p), 0) +JPFUN(1, const char**, get_ports,(jack_client_t *c, const char *p, const char *t, unsigned long f), (c,p,t,f), NULL) +JPFUN(1, int, port_name_size, (void), (), 0) +JPFUN(1, int, port_type_size, (void), (), 0) +JPFUN(1, size_t, port_type_get_buffer_size, (jack_client_t *c, const char *t), (c,t), 0) +JPFUN(1, jack_port_t*, port_by_name, (jack_client_t *c, const char *n), (c,n), NULL) +JPFUN(1, jack_port_t*, port_by_id, (jack_client_t *c, jack_port_id_t i), (c,i), NULL) +JPFUN(1, jack_port_t*, port_register, (jack_client_t *c, const char *n, const char *t, unsigned long f, unsigned long b), (c,n,t,f,b), NULL) +JPFUN(1, int, port_unregister, (jack_client_t *c, jack_port_t *p), (c,p), 0) +JPFUN(1, const char *, port_type, (const jack_port_t *p), (p), 0) +JPFUN(1, const char **, port_get_connections, (const jack_port_t *p), (p), 0) +JPFUN(1, const char **, port_get_all_connections, (const jack_client_t *c, const jack_port_t *p), (c,p), 0) +JPFUN(1, int, port_set_name, (jack_port_t *p, const char *n), (p,n), -1) +JXFUN(0, int, port_rename, (jack_client_t *c, jack_port_t *p, const char *n), (c,p,n), return jack_port_set_name (p,n);) +JPFUN(1, int, port_get_aliases, (const jack_port_t *port, char* const aliases[2]), (port,aliases), 0) +JPFUN(1, int, port_disconnect, (jack_client_t *c, jack_port_t *p), (c,p), 0) +JPFUN(1, int, connect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1) +JPFUN(1, int, disconnect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1) +JVFUN(0, free, (void *p), (p), free(p);) + +JCFUN(1, jack_nframes_t, cycle_wait, 0) +JVFUN(1, cycle_signal, (jack_client_t *c, int s), (c,s),) +JPFUN(1, int, set_process_thread, (jack_client_t *c, JackThreadCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_thread_init_callback, (jack_client_t *c, JackThreadInitCallback p, void *a), (c,p,a), -1) + +JPFUN(1, int, transport_locate, (jack_client_t *c, jack_nframes_t f), (c,f), 0) +JVFUN(1, transport_start, (jack_client_t *c), (c),) +JVFUN(1, transport_stop, (jack_client_t *c), (c),) +JPFUN(1, jack_nframes_t, get_current_transport_frame, (const jack_client_t *c), (c), 0) +JXFUN(1, jack_transport_state_t, transport_query, (const jack_client_t *c, jack_position_t *p), (c,p), memset(p, 0, sizeof(jack_position_t)); return JackTransportStopped;) +JPFUN(1, int, set_sync_callback, (jack_client_t *c, JackSyncCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_timebase_callback, (jack_client_t *c, int l, JackTimebaseCallback p, void *a), (c,l,p,a), -1) +JCFUN(1, int, release_timebase, 0) + +/* */ +JPFUN(1, uint32_t, midi_get_event_count, (void* p), (p), 0) +JPFUN(1, int, midi_event_get, (jack_midi_event_t *e, void *p, uint32_t i), (e,p,i), -1) +JPFUN(1, int, midi_event_write, (void *b, jack_nframes_t t, const jack_midi_data_t *d, size_t s), (b,t,d,s), -1) +JVFUN(1, midi_clear_buffer, (void *b), (b),) + +/* */ +JPFUN(0, int, set_session_callback, (jack_client_t *c, JackSessionCallback s, void *a), (c,s,a), -1) +JPFUN(0, int, session_reply, (jack_client_t *c, jack_session_event_t *e), (c,e), -1) +JVFUN(0, session_event_free, (jack_session_event_t *e), (e), ) + +/* */ +JPFUN(1, jack_ringbuffer_t *, ringbuffer_create, (size_t s), (s), NULL) +JVFUN(1, ringbuffer_free, (jack_ringbuffer_t *rb), (rb), ) +JVFUN(1, ringbuffer_reset, (jack_ringbuffer_t *rb), (rb), ) +JVFUN(1, ringbuffer_read_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), ) +JVFUN(1, ringbuffer_write_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), ) +JPFUN(1, size_t, ringbuffer_read_space, (const jack_ringbuffer_t *rb), (rb), 0) +JPFUN(1, size_t, ringbuffer_write_space, (const jack_ringbuffer_t *rb), (rb), 0) +JPFUN(1, size_t, ringbuffer_read, (jack_ringbuffer_t *rb, char *d, size_t c), (rb,d,c), 0) +JPFUN(1, size_t, ringbuffer_write, (jack_ringbuffer_t *rb, const char *s, size_t c), (rb,s,c), 0) +JPFUN(0, int, ringbuffer_mlock, (jack_ringbuffer_t *rb), (rb), 0) +JVFUN(0, ringbuffer_get_read_vector, (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *v), (rb,v), if (v) {v->buf=NULL; v->len=0;} ) +JVFUN(0, ringbuffer_get_write_vector, (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *v), (rb,v), if (v) {v->buf=NULL; v->len=0;} ) +JPFUN(0, size_t, ringbuffer_peek, (jack_ringbuffer_t *rb, char *d, size_t c), (rb,d,c), 0) + +/* */ +JCFUN(0, int, client_real_time_priority, 0) +JCFUN(0, int, client_max_real_time_priority, 0) +JPFUN(0, int, acquire_real_time_scheduling, (jack_native_thread_t t, int p), (t,p), 0) +JPFUN(0, int, drop_real_time_scheduling, (jack_native_thread_t t), (t), 0) +JPFUN(0, int, client_stop_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0) +JPFUN(0, int, client_kill_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0) +#ifndef _WIN32 +JVFUN(0, set_thread_creator, (jack_thread_creator_t c), (c),) +#endif +JPFUN(1, int, client_create_thread, \ + (jack_client_t* c, jack_native_thread_t *t, int p, int r, void *(*f)(void*), void *a), (c,t,p,r,f,a), 0) + +#ifndef NO_JACK_METADATA +/* - TODO*/ + +/* */ +JPFUN(0, char *, get_uuid_for_client_name, (jack_client_t* c, const char* n), (c,n), NULL) +JPFUN(0, char *, get_client_name_by_uuid, (jack_client_t* c, const char* u), (c,u), NULL) +JPFUN(0, jack_uuid_t, port_uuid, (const jack_port_t *p), (p), 0) + +/* */ +JPFUN(0, int, set_property, (jack_client_t* c, jack_uuid_t s, const char* k, const char* v, const char* t), (c,s,k,v,t), -1) +JXFUN(0, int, get_property, (jack_uuid_t s, const char* k, char** v, char** t), (s,k,v,t), if (v) *v=NULL; if (t) *t=NULL; return -1;) +JVFUN(0, free_description, (jack_description_t* d, int f), (d,f),) +JXFUN(0, int, get_properties, (jack_uuid_t s, jack_description_t* d), (s,d), if (d) {d->properties = NULL; d->property_cnt = 0;} return -1;) +JXFUN(0, int, get_all_properties, (jack_description_t** d), (d), if (d) *d=NULL; return -1;) +JPFUN(0, int, remove_property, (jack_client_t* c, jack_uuid_t s, const char* k), (c,s,k), -1) +JPFUN(0, int, remove_properties, (jack_client_t* c, jack_uuid_t s), (c,s), -1) +JPFUN(0, int, remove_all_properties, (jack_client_t* c), (c), -1) +JPFUN(0, int, set_property_change_callback, (jack_client_t *c, JackPropertyChangeCallback s, void *a), (c,s,a), -1) +#endif + +/* */ +JCFUN(1, float, get_max_delayed_usecs, 0.0) +JCFUN(1, float, get_xrun_delayed_usecs, 0.0) +JVFUN(0, reset_max_delayed_usecs, (jack_client_t *c), (c),) + +#endif // end USE_WEAK_JACK diff --git a/include/AudioWeakJack.h b/include/AudioWeakJack.h new file mode 100644 index 000000000..e53fced8e --- /dev/null +++ b/include/AudioWeakJack.h @@ -0,0 +1,237 @@ +/* runtime/weak dynamic JACK linking + * + * (C) 2014 Robin Gareus + * + * The wrapped jack API itself is + * (C) 2001 Paul Davis + * (C) 2004 Jack O'Quin + * + * 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, 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; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _WEAK_JACK_H +#define _WEAK_JACK_H + +// LMMS uses LMMS_HAVE_WEAKJACK instead +#ifndef USE_WEAK_JACK +#define USE_WEAK_JACK +#endif + +// LMMS doesn't use metadata.h +#ifndef NO_JACK_METADATA +#define NO_JACK_METADATA +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** check if libjack is available + * + * return 0 if libjack is dynamically linked of was + * successfully dl-opened. Otherwise: + * + * -1: library was not initialized + * -2: libjack was not found + * > 0 bitwise flags: + * 1: a required function was not found in libjack + * 2: jack_client_open was not found in libjack + */ +int have_libjack(void); + +#ifdef __cplusplus +} +#endif + +#ifdef USE_WEAK_JACK + +/* */ +#define jack_client_close WJACK_client_close +#define jack_get_client_name WJACK_get_client_name +#define jack_get_sample_rate WJACK_get_sample_rate +#define jack_get_buffer_size WJACK_get_buffer_size +#define jack_frames_since_cycle_start WJACK_frames_since_cycle_start +#define jack_frame_time WJACK_frame_time +#define jack_last_frame_time WJACK_last_frame_time +#define jack_get_time WJACK_get_time +#define jack_cpu_load WJACK_cpu_load +#define jack_is_realtime WJACK_is_realtime + +#define jack_client_name_size WJACK_client_name_size + +#define jack_set_freewheel WJACK_set_freewheel +#define jack_set_buffer_size WJACK_set_buffer_size + +#define jack_on_shutdown WJACK_on_shutdown +#define jack_on_info_shutdown WJACK_on_info_shutdown +#define jack_set_process_callback WJACK_set_process_callback +#define jack_set_freewheel_callback WJACK_set_freewheel_callback +#define jack_set_buffer_size_callback WJACK_set_buffer_size_callback +#define jack_set_sample_rate_callback WJACK_set_sample_rate_callback +#define jack_set_port_registration_callback WJACK_set_port_registration_callback +#define jack_set_port_connect_callback WJACK_set_port_connect_callback +#define jack_set_graph_order_callback WJACK_set_graph_order_callback +#define jack_set_xrun_callback WJACK_set_xrun_callback +#define jack_set_latency_callback WJACK_set_latency_callback +#define jack_set_error_function WJACK_set_error_function +#define jack_set_info_function WJACK_set_info_function + +#define jack_activate WJACK_activate +#define jack_deactivate WJACK_deactivate + +#define jack_recompute_total_latencies WJACK_recompute_total_latencies +#define jack_port_get_total_latency WJACK_port_get_total_latency +#define jack_port_get_latency_range WJACK_port_get_latency_range +#define jack_port_set_latency_range WJACK_port_set_latency_range +#define jack_port_get_buffer WJACK_port_get_buffer +#define jack_port_request_monitor WJACK_port_request_monitor +#define jack_port_ensure_monitor WJACK_port_ensure_monitor +#define jack_port_monitoring_input WJACK_port_monitoring_input + +#define jack_port_name WJACK_port_name +#define jack_port_short_name WJACK_port_short_name +#define jack_port_flags WJACK_port_flags +#define jack_get_ports WJACK_get_ports +#define jack_port_name_size WJACK_port_name_size +#define jack_port_type_size WJACK_port_type_size +#define jack_port_type_get_buffer_size WJACK_port_type_get_buffer_size +#define jack_port_by_name WJACK_port_by_name +#define jack_port_by_id WJACK_port_by_id +#define jack_port_set_name WJACK_port_set_name +#define jack_port_get_aliases WJACK_port_get_aliases +#define jack_port_rename WJACK_port_rename +#define jack_port_disconnect WJACK_port_disconnect +#define jack_port_register WJACK_port_register +#define jack_port_unregister WJACK_port_unregister +#define jack_port_type WJACK_port_type +#define jack_port_get_connections WJACK_port_get_connections +#define jack_port_get_all_connections WJACK_port_get_all_connections +#define jack_connect WJACK_connect +#define jack_disconnect WJACK_disconnect +#define jack_free WJACK_free + +#define jack_cycle_wait WJACK_cycle_wait +#define jack_cycle_signal WJACK_cycle_signal +#define jack_set_process_thread WJACK_set_process_thread +#define jack_set_thread_init_callback WJACK_set_thread_init_callback + +/* */ +#define jack_get_current_transport_frame WJACK_get_current_transport_frame +#define jack_transport_locate WJACK_transport_locate +#define jack_transport_start WJACK_transport_start +#define jack_transport_stop WJACK_transport_stop +#define jack_transport_query WJACK_transport_query +#define jack_set_sync_callback WJACK_set_sync_callback +#define jack_set_timebase_callback WJACK_set_timebase_callback +#define jack_release_timebase WJACK_release_timebase + +/* */ +#define jack_midi_get_event_count WJACK_midi_get_event_count +#define jack_midi_event_get WJACK_midi_event_get +#define jack_midi_event_write WJACK_midi_event_write +#define jack_midi_clear_buffer WJACK_midi_clear_buffer + +/* */ +#define jack_set_session_callback WJACK_set_session_callback +#define jack_session_reply WJACK_session_reply +#define jack_session_event_free WJACK_session_event_free + +/* */ +#define jack_ringbuffer_create WJACK_ringbuffer_create +#define jack_ringbuffer_free WJACK_ringbuffer_free +#define jack_ringbuffer_reset WJACK_ringbuffer_reset +#define jack_ringbuffer_read_advance WJACK_ringbuffer_read_advance +#define jack_ringbuffer_write_advance WJACK_ringbuffer_write_advance +#define jack_ringbuffer_read_space WJACK_ringbuffer_read_space +#define jack_ringbuffer_write_space WJACK_ringbuffer_write_space +#define jack_ringbuffer_read WJACK_ringbuffer_read +#define jack_ringbuffer_write WJACK_ringbuffer_write +#define jack_ringbuffer_mlock WJACK_ringbuffer_mlock +#define jack_ringbuffer_get_read_vector WJACK_ringbuffer_get_read_vector +#define jack_ringbuffer_get_write_vector WJACK_ringbuffer_get_write_vector +#define jack_ringbuffer_peek WJACK_ringbuffer_peek + +/* */ +#define jack_client_real_time_priority WJACK_client_real_time_priority +#define jack_client_max_real_time_priority WJACK_client_max_real_time_priority +#define jack_acquire_real_time_scheduling WJACK_acquire_real_time_scheduling +#define jack_client_create_thread WJACK_client_create_thread +#define jack_drop_real_time_scheduling WJACK_drop_real_time_scheduling +#define jack_client_stop_thread WJACK_client_stop_thread +#define jack_client_kill_thread WJACK_client_kill_thread +#define jack_set_thread_creator WJACK_set_thread_creator + +#define jack_client_open WJACK_client_client_openXXX + +#ifndef NO_JACK_METADATA +/* */ +#define jack_get_uuid_for_client_name WJACK_get_uuid_for_client_name +#define jack_get_client_name_by_uuid WJACK_get_client_name_by_uuid +#define jack_port_uuid WJACK_port_uuid + +#define jack_set_property WJACK_set_property +#define jack_get_property WJACK_get_property +#define jack_free_description WJACK_free_description +#define jack_get_properties WJACK_get_properties +#define jack_get_all_properties WJACK_get_all_properties +#define jack_remove_property WJACK_remove_property +#define jack_remove_properties WJACK_remove_properties +#define jack_remove_all_properties WJACK_remove_all_properties +#define jack_set_property_change_callback WJACK_set_property_change_callback +#endif + +/* */ +#define jack_get_max_delayed_usecs WJACK_get_max_delayed_usecs +#define jack_get_xrun_delayed_usecs WJACK_get_xrun_delayed_usecs +#define jack_reset_max_delayed_usecs WJACK_reset_max_delayed_usecs + +#endif // end USE_WEAK_JACK + +#include +#include +#include +#include +#include +#include + +#ifndef NO_JACK_METADATA +#include +#endif + +#ifdef USE_WEAK_JACK + +#undef jack_client_open + +/* var-args hack */ + +#ifdef __cplusplus +extern "C" { +#endif +void (* WJACK_get_client_open (void)) (void); +jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...); +#ifdef __cplusplus +} +#endif + +#define jack_client_open(...) \ +( \ + (WJACK_get_client_open() != NULL) \ + ? ((jack_client_t* (*)(const char *, jack_options_t, jack_status_t *, ...))(WJACK_get_client_open()))(__VA_ARGS__) \ + : WJACK_no_client_open(__VA_ARGS__) \ +) + +#endif // end USE_WEAK_JACK + +#endif // _WEAK_JACK_H diff --git a/include/MidiJack.h b/include/MidiJack.h index 9929b57c2..b3634bc34 100644 --- a/include/MidiJack.h +++ b/include/MidiJack.h @@ -28,8 +28,12 @@ #include "lmmsconfig.h" #ifdef LMMS_HAVE_JACK +#ifndef LMMS_HAVE_WEAKJACK #include #include +#else +#include "AudioWeakJack.h" +#endif #include #include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 154ff4f85..a3f944b4f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,7 +8,7 @@ SET(CMAKE_AUTOMOC ON) SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) # Enable C++11 -ADD_DEFINITIONS(-std=c++0x) +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") IF(LMMS_BUILD_APPLE) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5dcfa3c42..c8840997d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,3 +1,7 @@ +IF(LMMS_HAVE_WEAKJACK) + set(WEAKJACK core/audio/AudioWeakJack.c) +ENDIF() + set(LMMS_SRCS ${LMMS_SRCS} core/AutomatableModel.cpp @@ -72,6 +76,7 @@ set(LMMS_SRCS core/audio/AudioFileMP3.cpp core/audio/AudioFileOgg.cpp core/audio/AudioFileWave.cpp + ${WEAKJACK} core/audio/AudioJack.cpp core/audio/AudioOss.cpp core/audio/AudioSndio.cpp diff --git a/src/core/audio/AudioWeakJack.c b/src/core/audio/AudioWeakJack.c new file mode 100644 index 000000000..5cf93cdd8 --- /dev/null +++ b/src/core/audio/AudioWeakJack.c @@ -0,0 +1,273 @@ +/* runtime/weak dynamic JACK linking + * + * (C) 2014 Robin Gareus + * + * 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, 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; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "AudioWeakJack.h" + +#ifndef USE_WEAK_JACK + +int have_libjack (void) { + return 0; +} + +#else + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +static void* lib_open(const char* const so) { +#ifdef _WIN32 + return (void*) LoadLibraryA(so); +#else + return dlopen(so, RTLD_NOW|RTLD_LOCAL); +#endif +} + +static void* lib_symbol(void* const lib, const char* const sym) { +#ifdef _WIN32 + return (void*) GetProcAddress((HMODULE)lib, sym); +#else + return dlsym(lib, sym); +#endif +} + +#if _MSC_VER && !__INTEL_COMPILER +typedef void * pvoid_t; +#define MAPSYM(SYM, FAIL) _j._ ## SYM = (func_t)lib_symbol(lib, "jack_" # SYM); \ + if (!_j._ ## SYM) err |= FAIL; +#elif defined NDEBUG +typedef void * __attribute__ ((__may_alias__)) pvoid_t; +#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \ + if (!_j._ ## SYM) err |= FAIL; +#else +typedef void * __attribute__ ((__may_alias__)) pvoid_t; +#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \ + if (!_j._ ## SYM) { \ + if (FAIL) { \ + fprintf(stderr, "*** WEAK-JACK: required symbol 'jack_%s' was not found\n", "" # SYM); \ + } \ + err |= FAIL; \ + } +#endif + +typedef void (* func_t) (void); + +/* function pointers to the real jack API */ +static struct WeakJack { + func_t _client_open; // special case due to varargs + +#define JCFUN(ERR, RTYPE, NAME, RVAL) func_t _ ## NAME ; +#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) func_t _ ## NAME ; +#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) func_t _ ## NAME ; +#define JVFUN(ERR, NAME, DEF, ARGS, CODE) func_t _ ## NAME ; + +#include "AudioWeakJack.def" + +#undef JCFUN +#undef JPFUN +#undef JXFUN +#undef JVFUN +} _j; + +static int _status = -1; + +__attribute__((constructor)) +static void init_weak_jack(void) +{ + void* lib; + int err = 0; +#ifndef NDEBUG + fprintf(stderr, "*** WEAK-JACK: initializing\n"); +#endif + + memset(&_j, 0, sizeof(_j)); + +#ifdef __APPLE__ + lib = lib_open("libjack.dylib"); + if (!lib) { + lib = lib_open("/usr/local/lib/libjack.dylib"); + } +#elif (defined _WIN32) +# ifdef __x86_64__ + lib = lib_open("libjack64.dll"); +# else + lib = lib_open("libjack.dll"); +# endif +#else + lib = lib_open("libjack.so.0"); +#endif + if (!lib) { +#ifndef NDEBUG + fprintf(stderr, "*** WEAK-JACK: libjack was not found\n"); +#endif + _status = -2; + return; + } + + /* found library, now lookup functions */ + MAPSYM(client_open, 2) + +#define JCFUN(ERR, RTYPE, NAME, RVAL) MAPSYM(NAME, ERR) +#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) MAPSYM(NAME, ERR) +#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) MAPSYM(NAME, ERR) +#define JVFUN(ERR, NAME, DEF, ARGS, CODE) MAPSYM(NAME, ERR) + +#include "AudioWeakJack.def" + +#undef JCFUN +#undef JPFUN +#undef JXFUN +#undef JVFUN + + /* if a required symbol is not found, disable JACK completly */ + if (err) { + _j._client_open = NULL; + } + _status = err; +#ifndef NDEBUG + fprintf(stderr, "*** WEAK-JACK: %s. (%d)\n", err ? "jack is not available" : "OK", _status); +#endif +} + +int have_libjack (void) { + if (_status == -1) { + init_weak_jack(); + } + return _status; +} + +/******************************************************************************* + * helper macros + */ + +#if defined(__GNUC__) && (__GNUC__ > 2) && !defined(NDEBUG) +#define likely(expr) (__builtin_expect (!!(expr), 1)) +#else +#define likely(expr) (expr) +#endif + +#ifndef NDEBUG +# define WJACK_WARNING(NAME) \ + fprintf(stderr, "*** WEAK-JACK: function 'jack_%s' ignored\n", "" # NAME); +#else +# define WJACK_WARNING(NAME) ; +#endif + +/****************************************************************************** + * JACK API wrapper functions. + * + * if a function pointer is set in the static struct WeakJack _j, + * the function is called directly. + * Otherwise a dummy NOOP implementation is provided. + * The latter is mainly for compile-time warnings. + * + * If libjack is not found, jack_client_open() will fail. + * In that case the application should not call any other libjack + * functions. Hence a real implementation is not needed. + * (jack ringbuffer may be an exception for some apps) + */ + +/* dedicated support for jack_client_open(,..) variable arg function macro */ +func_t WJACK_get_client_open(void) { + if (_status == -1) { + init_weak_jack(); + } + return _j._client_open; +} + +/* callback to set status */ +jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...) { + WJACK_WARNING(client_open); + if (status) { *status = JackFailure; } + return NULL; +} + +/******************************************************************************* + * Macros to wrap jack API + */ + +/* abstraction for jack_client functions + * rtype jack_function_name (jack_client_t *client) { return rval; } + */ +#define JCFUN(ERR, RTYPE, NAME, RVAL) \ + RTYPE WJACK_ ## NAME (jack_client_t *client) { \ + if likely(_j._ ## NAME) { \ + return ((RTYPE (*)(jack_client_t *client)) _j._ ## NAME)(client); \ + } else { \ + WJACK_WARNING(NAME) \ + return RVAL; \ + } \ + } + +/* abstraction for NOOP functions with return value + * rtype jack_function_name (ARGS) { return rval; } + */ +#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) \ + RTYPE WJACK_ ## NAME DEF { \ + if likely(_j._ ## NAME) { \ + return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \ + } else { \ + WJACK_WARNING(NAME) \ + return RVAL; \ + } \ + } + +/* abstraction for functions that need custom code. + * e.g. functions with return-value-pointer args, + * use CODE to initialize value + * + * rtype jack_function_name (ARGS) { CODE } + */ +#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) \ + RTYPE WJACK_ ## NAME DEF { \ + if likely(_j._ ## NAME) { \ + return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \ + } else { \ + WJACK_WARNING(NAME) \ + CODE \ + } \ + } + +/* abstraction for void functions with return-value-pointer args + * void jack_function_name (ARGS) { CODE } + */ +#define JVFUN(ERR, NAME, DEF, ARGS, CODE) \ + void WJACK_ ## NAME DEF { \ + if likely(_j._ ## NAME) { \ + ((void (*)DEF) _j._ ## NAME) ARGS; \ + } else { \ + WJACK_WARNING(NAME) \ + CODE \ + } \ + } + +#include "AudioWeakJack.def" + +#undef JCFUN +#undef JPFUN +#undef JXFUN +#undef JVFUN + +#endif // end USE_WEAK_JACK diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index 421d0cc99..aa4505dbb 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -11,6 +11,7 @@ #cmakedefine LMMS_HAVE_ALSA #cmakedefine LMMS_HAVE_FLUIDSYNTH #cmakedefine LMMS_HAVE_JACK +#cmakedefine LMMS_HAVE_WEAKJACK #cmakedefine LMMS_HAVE_MP3LAME #cmakedefine LMMS_HAVE_OGGVORBIS #cmakedefine LMMS_HAVE_OSS From 3e90e3735213f2bc0e151c7886f3519ab7e54804 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Mon, 16 Oct 2017 15:09:30 -0400 Subject: [PATCH 16/27] Add AppImage support to Linux builds (#3688) Add AppImage support --- .travis/linux..install.sh | 2 +- CMakeLists.txt | 9 +- cmake/linux/CMakeLists.txt | 19 +- cmake/linux/lmms | 2 +- cmake/linux/lmms.desktop | 4 +- cmake/linux/lmms.svg | 1540 +++++++++++++++++ cmake/linux/lmms.xml | 6 +- cmake/linux/package_linux.sh.in | 170 ++ .../linux/project.svg | 0 cmake/modules/FindWine.cmake | 22 + plugins/LadspaEffect/CMakeLists.txt | 4 + plugins/papu/CMakeLists.txt | 3 - plugins/vst_base/CMakeLists.txt | 36 +- src/core/ConfigManager.cpp | 11 +- 14 files changed, 1795 insertions(+), 33 deletions(-) create mode 100644 cmake/linux/lmms.svg create mode 100644 cmake/linux/package_linux.sh.in rename data/application-x-lmms-project.svg => cmake/linux/project.svg (100%) diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index 9c7d3c4b1..563e4e7c0 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev - libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev + libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev stk libfluidsynth-dev portaudio19-dev wine-dev g++-multilib libfltk1.3-dev libgig-dev libsoundio-dev" diff --git a/CMakeLists.txt b/CMakeLists.txt index 609107b42..f1f4ef0b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,6 +153,9 @@ IF(WANT_QT5) Qt5::Xml ) + # Resolve Qt5::qmake to full path for use in packaging scripts + GET_TARGET_PROPERTY(QT_QMAKE_EXECUTABLE "${Qt5Core_QMAKE_EXECUTABLE}" IMPORTED_LOCATION) + FIND_PACKAGE(Qt5Test) SET(QT_QTTEST_LIBRARY Qt5::Test) ELSE() @@ -433,7 +436,11 @@ IF(WANT_VST) FIND_PACKAGE(Wine) IF(WINE_FOUND) SET(LMMS_SUPPORT_VST TRUE) - SET(STATUS_VST "OK") + IF(WINE_LIBRARY_FIX) + SET(STATUS_VST "OK, with workaround linking ${WINE_LIBRARY_FIX}") + ELSE() + SET(STATUS_VST "OK") + ENDIF() ELSEIF(WANT_VST_NOWINE) SET(LMMS_SUPPORT_VST TRUE) SET(STATUS_VST "OK") diff --git a/cmake/linux/CMakeLists.txt b/cmake/linux/CMakeLists.txt index 78a7c541a..87f419405 100644 --- a/cmake/linux/CMakeLists.txt +++ b/cmake/linux/CMakeLists.txt @@ -1,4 +1,19 @@ -INSTALL(FILES lmms.png DESTINATION "${DATA_DIR}/pixmaps") -INSTALL(FILES lmms DESTINATION "${DATA_DIR}/menu") +INSTALL(FILES lmms.svg DESTINATION "${DATA_DIR}/icons/hicolor/scalable/apps") +INSTALL(FILES project.svg DESTINATION "${DATA_DIR}/icons/hicolor/scalable/mimetypes/" RENAME "application-x-lmms-project.svg") INSTALL(FILES lmms.desktop DESTINATION "${DATA_DIR}/applications") INSTALL(FILES lmms.xml DESTINATION "${DATA_DIR}/mime/packages") + +# AppImage creation target +SET(APPIMAGE_FILE "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-${VERSION}-linux-${CMAKE_SYSTEM_PROCESSOR}.AppImage") + +CONFIGURE_FILE("package_linux.sh.in" "${CMAKE_BINARY_DIR}/package_linux.sh" @ONLY) + +FILE(REMOVE "${APPIMAGE_FILE}") +ADD_CUSTOM_TARGET(removeappimage + COMMAND rm -f "${APPIMAGE_FILE}" + COMMENT "Removing old AppImage") +ADD_CUSTOM_TARGET(appimage + COMMAND chmod +x "${CMAKE_BINARY_DIR}/package_linux.sh" + COMMAND "${CMAKE_BINARY_DIR}/package_linux.sh" + COMMENT "Generating AppImage") +ADD_DEPENDENCIES(appimage removeappimage) diff --git a/cmake/linux/lmms b/cmake/linux/lmms index 274886185..884f0fed4 100644 --- a/cmake/linux/lmms +++ b/cmake/linux/lmms @@ -1,4 +1,4 @@ ?package(lmms):needs="X11" section="Apps/Sound" \ title="LMMS" hints="Audio" command="/usr/bin/lmms" \ longtitle="LMMS" \ - icon="/usr/share/pixmaps/lmms.png" + icon="/usr/share/icons/hicolor/scalable/apps/lmms.svg" diff --git a/cmake/linux/lmms.desktop b/cmake/linux/lmms.desktop index 6094ccfe1..67e9c2f11 100644 --- a/cmake/linux/lmms.desktop +++ b/cmake/linux/lmms.desktop @@ -1,10 +1,10 @@ [Desktop Entry] Name=LMMS -GenericName=music production suite +GenericName=Music production suite GenericName[ca]=Programari de producció musical GenericName[de]=Software zur Musik-Produktion GenericName[fr]=Ensemble pour la production musicale -Comment=easy music production for everyone! +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 ! Icon=lmms diff --git a/cmake/linux/lmms.svg b/cmake/linux/lmms.svg new file mode 100644 index 000000000..ffe6976ce --- /dev/null +++ b/cmake/linux/lmms.svg @@ -0,0 +1,1540 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cmake/linux/lmms.xml b/cmake/linux/lmms.xml index 016751560..579c3f12f 100644 --- a/cmake/linux/lmms.xml +++ b/cmake/linux/lmms.xml @@ -4,11 +4,7 @@ LMMS project Projecte LMMS - - - - - + diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in new file mode 100644 index 000000000..275baba35 --- /dev/null +++ b/cmake/linux/package_linux.sh.in @@ -0,0 +1,170 @@ +#!/usr/bin/env bash +# Creates Linux ".AppImage" for @PROJECT_NAME_UCASE@ +# +# Depends: linuxdeployqt +# +# Notes: Will attempt to fetch linuxdeployqt automatically (x86_64 only) +# See Also: https://github.com/probonopd/linuxdeployqt/blob/master/BUILDING.md + +set -e + +USERBIN="$HOME/bin" +LINUXDEPLOYQT="$USERBIN/linuxdeployqt" +APPIMAGETOOL="$USERBIN/appimagetool" +VERBOSITY=2 # 3=debug +LOGFILE="@CMAKE_BINARY_DIR@/appimage.log" +APPDIR="@CMAKE_BINARY_DIR@/@PROJECT_NAME_UCASE@.AppDir/" +DESKTOPFILE="${APPDIR}usr/share/applications/lmms.desktop" +STRIP="" + +# Don't strip for Debug|RelWithDebInfo builds +if [[ "@CMAKE_BUILD_TYPE@" == *"Deb"* ]]; then + STRIP="-no-strip" +fi + +# Console colors +RED="\\x1B[1;31m" +GREEN="\\x1B[1;32m" +YELLOW="\\x1B[1;33m" +PLAIN="\\x1B[0m" + +function error { + echo -e " ${PLAIN}[${RED}error${PLAIN}] ${1}" + return 1 +} + +function success { + echo -e " ${PLAIN}[${GREEN}success${PLAIN}] ${1}" +} + +function skipped { + echo -e " ${PLAIN}[${YELLOW}skipped${PLAIN}] ${1}" +} + + +# Check for problematic install locations +INSTALL=$(echo "@CMAKE_INSTALL_PREFIX@" | sed 's/\/*$//g') +if [ "$INSTALL" == "/usr/local" ] || [ "$INSTALL" == "/usr" ] ; then + error "Incompatible CMAKE_INSTALL_PREFIX for creating AppImage: @CMAKE_INSTALL_PREFIX@" +fi + +echo -e "\nWriting verbose output to \"${LOGFILE}\"" + +# Ensure linuxdeployqt uses the same qmake version as cmake +export PATH="$(dirname "@QT_QMAKE_EXECUTABLE@")":$PATH + +# Fetch portable linuxdeployqt if cache is older than $DAYSOLD +echo -e "\nDownloading linuxdeployqt to ${LINUXDEPLOYQT}..." +mkdir -p "$HOME/bin" +DAYSOLD=2 +if env -i which linuxdeployqt > /dev/null 2>&1; then + skipped "System already provides this utility" +elif ! find "$LINUXDEPLOYQT" -mtime -$DAYSOLD 2>/dev/null|grep -q "." > /dev/null 2>&1; then + url="https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-$(uname -p).AppImage" + echo " [.......] Couldn't find linuxdeployqt newer than $DAYSOLD days old" + echo " [.......] Downloading ($(uname -p)): ${url}" + wget "$url" -O "$LINUXDEPLOYQT" -q || (rm "$LINUXDEPLOYQT" && false) + chmod +x "$LINUXDEPLOYQT" + touch "$LINUXDEPLOYQT" + success "Downloaded $LINUXDEPLOYQT" + "$LINUXDEPLOYQT" --appimage-extract > /dev/null 2>&1 + mv "squashfs-root/usr/bin/appimagetool" "$APPIMAGETOOL" + success "Extracted $APPIMAGETOOL" + mv "squashfs-root/usr/bin/mksquashfs" "$USERBIN/mksquashfs" + success "Extracted $USERBIN/mksquashfs" + rm -rf "squashfs-root/" + +else + skipped "$LINUXDEPLOYQT is less than $DAYSOLD days old" +fi + +# Make skeleton AppDir +echo -e "\nCreating ${APPDIR}..." +rm -rf "${APPDIR}" +mkdir -p "${APPDIR}usr" +success "Created ${APPDIR}" + +# Clone install to AppDir +echo -e "\nCopying @CMAKE_INSTALL_PREFIX@ to ${APPDIR}..." +cp -R "@CMAKE_INSTALL_PREFIX@/." "${APPDIR}usr" +rm -rf "${APPDIR}usr/include" +success "${APPDIR}" + +# Copy rawwaves directory for stk/mallets +mkdir -p "${APPDIR}usr/share/stk/" +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 + echo "VirtualBox detected. Forcing libgl software rendering." + export LIBGL_ALWAYS_SOFTWARE=1; +fi +QT_X11_NO_NATIVE_MENUBAR=1 QT_AUTO_SCREEN_SCALE_FACTOR=1 \$DIR/usr/bin/lmms.real "\$@" +EOL + +chmod +x "${APPDIR}usr/bin/lmms" + +# Per https://github.com/probonopd/linuxdeployqt/issues/129 +unset LD_LIBRARY_PATH + +# Ensure linuxdeployqt can find shared objects +export LD_LIBRARY_PATH="${APPDIR}usr/lib/lmms/":$LD_LIBRARY_PATH + +# Handle wine linking +if [ -d "@WINE_LIBRARY_FIX@" ]; then + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@WINE_LIBRARY_FIX@:@WINE_LIBRARY_FIX@wine/ +fi + +# Move executables so linuxdeployqt can find them +ZYNLIB="${APPDIR}usr/lib/lmms/RemoteZynAddSubFx" +VSTLIB="${APPDIR}usr/lib/lmms/RemoteVstPlugin.exe.so" + +ZYNBIN="${APPDIR}usr/bin/RemoteZynAddSubFx" +VSTBIN="${APPDIR}usr/bin/RemoteVstPlugin.exe.so" + +mv "$ZYNLIB" "$ZYNBIN" +mv "$VSTLIB" "$VSTBIN" + +# Patch the desktop file +sed -i 's/.*Exec=.*/Exec=lmms.real/' "$DESKTOPFILE" + +# Fix linking for soft-linked plugins +for file in "${APPDIR}usr/lib/lmms/"*.so; do + thisfile="${APPDIR}usr/lib/lmms/${file##*/}" + executables="${executables} -executable=$thisfile" +done +executables="${executables} -executable=${ZYNBIN}" +executables="${executables} -executable=${VSTBIN}" +executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/imp_1199.so" +executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/imbeq_1197.so" +executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/pitch_scale_1193.so" +executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/pitch_scale_1194.so" + +# Bundle both qt and non-qt dependencies into appimage format +echo -e "\nBundling and relinking system dependencies..." +echo -e ">>>>> linuxdeployqt" > "$LOGFILE" +# shellcheck disable=SC2086 +"$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 +ln -sr "$ZYNBIN" "$ZYNLIB" +ln -sr "$VSTBIN" "$VSTLIB" + +# Remove wine library conflict +rm -f "${APPDIR}/usr/lib/libwine.so.1" + +# Create AppImage +echo -e "\nFinishing the AppImage..." +echo -e "\n\n>>>>> appimagetool" >> "$LOGFILE" +"$APPIMAGETOOL" "${APPDIR}" "@APPIMAGE_FILE@" >> "$LOGFILE" 2>&1 +success "Created @APPIMAGE_FILE@" + +echo -e "\nFinished" diff --git a/data/application-x-lmms-project.svg b/cmake/linux/project.svg similarity index 100% rename from data/application-x-lmms-project.svg rename to cmake/linux/project.svg diff --git a/cmake/modules/FindWine.cmake b/cmake/modules/FindWine.cmake index da76f3ff3..47850dcd1 100644 --- a/cmake/modules/FindWine.cmake +++ b/cmake/modules/FindWine.cmake @@ -7,6 +7,8 @@ # WINE_DEFINITIONS - Compiler switches required for using wine # +LIST(APPEND CMAKE_PREFIX_PATH /opt/wine-stable /opt/wine-devel /opt/wine-staging) + FIND_PATH(WINE_INCLUDE_DIR windows/windows.h PATH_SUFFIXES wine) FIND_LIBRARY(WINE_LIBRARY NAMES wine PATH_SUFFIXES wine i386-linux-gnu/wine) FIND_PROGRAM(WINE_CXX NAMES wineg++ winegcc winegcc64 winegcc32) @@ -14,6 +16,26 @@ FIND_PROGRAM(WINE_CXX NAMES wineg++ winegcc winegcc64 winegcc32) set(WINE_INCLUDE_DIRS ${WINE_INCLUDE_DIR} ) set(WINE_LIBRARIES ${WINE_LIBRARY} ) +# Handle wine linking problems +EXEC_PROGRAM(${WINE_CXX} ARGS "-v -m32 /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT) + +# Debian systems +IF("${WINEBUILD_OUTPUT}" MATCHES ".*x86_64-linux-gnu/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/usr/lib/i386-linux-gnu/" ) +# Fedora systems +ELSEIF("${WINEBUILD_OUTPUT}" MATCHES "/usr/lib/lib64/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/usr/lib/i386/") +# Wine stable +ELSEIF("${WINEBUILD_OUTPUT}" MATCHES "/opt/wine-stable/lib64/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/opt/wine-stable/lib/") +# Wine development +ELSEIF("${WINEBUILD_OUTPUT}" MATCHES "/opt/wine-devel/lib64/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/opt/wine-devel/lib/") +# Wine staging +ELSEIF("${WINEBUILD_OUTPUT}" MATCHES "/opt/wine-staging/lib64/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/opt/wine-staging/lib/") +ENDIF() + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Wine DEFAULT_MSG WINE_LIBRARIES WINE_INCLUDE_DIRS) diff --git a/plugins/LadspaEffect/CMakeLists.txt b/plugins/LadspaEffect/CMakeLists.txt index 8ab9685c7..029cd9168 100644 --- a/plugins/LadspaEffect/CMakeLists.txt +++ b/plugins/LadspaEffect/CMakeLists.txt @@ -2,6 +2,10 @@ INCLUDE(BuildPlugin) # Disable C++11 REMOVE_DEFINITIONS(-std=c++0x) +# Enable C++11 for CXXFLAGS only and not for Windows +IF(NOT LMMS_BUILD_WIN32) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") +ENDIF() BUILD_PLUGIN(ladspaeffect LadspaEffect.cpp LadspaControls.cpp LadspaControlDialog.cpp LadspaSubPluginFeatures.cpp LadspaEffect.h LadspaControls.h LadspaControlDialog.h LadspaSubPluginFeatures.h MOCFILES LadspaEffect.h LadspaControls.h LadspaControlDialog.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/papu/CMakeLists.txt b/plugins/papu/CMakeLists.txt index b45fcf614..20d9bbd20 100644 --- a/plugins/papu/CMakeLists.txt +++ b/plugins/papu/CMakeLists.txt @@ -1,6 +1,3 @@ INCLUDE(BuildPlugin) -# Disable C++11 -REMOVE_DEFINITIONS(-std=c++0x) - BUILD_PLUGIN(papu papu_instrument.cpp papu_instrument.h Basic_Gb_Apu.cpp Basic_Gb_Apu.h gb_apu/Gb_Oscs.cpp gb_apu/Gb_Apu.h gb_apu/Blip_Buffer.cpp gb_apu/Gb_Apu.cpp gb_apu/Gb_Oscs.h gb_apu/blargg_common.h gb_apu/Blip_Buffer.h gb_apu/Multi_Buffer.cpp gb_apu/blargg_source.h gb_apu/Multi_Buffer.h MOCFILES papu_instrument.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index a9a808841..a3f919adf 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -33,30 +33,32 @@ IF(LMMS_BUILD_LINUX AND NOT WANT_VST_NOWINE) IF(LMMS_HOST_X86_64) SET(EXTRA_FLAGS -m32) - - # workaround for broken wineg++ in WINE 1.4 (shipped e.g. with Ubuntu Precise) - EXEC_PROGRAM( ${WINE_CXX} ARGS "-v -m32 /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT) - IF("${WINEBUILD_OUTPUT}" MATCHES ".*x86_64-linux-gnu/wine/libwinecrt0.a.*") - SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs /usr/lib/i386-linux-gnu/wine/libwinecrt0.a -L/usr/lib/i386-linux-gnu/wine/ -luser32 -lkernel32 -lgdi32) - ENDIF() - #The following check works on Fedora systems - IF("${WINEBUILD_OUTPUT}" MATCHES ".*lib64/wine/libwinecrt0.a.*") - SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs /usr/lib/i386/wine/libwinecrt0.a -luser32 -lkernel32 -lgdi32) + IF(WINE_LIBRARY_FIX) + SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs ${WINE_LIBRARY_FIX}wine/libwinecrt0.a -L${WINE_LIBRARY_FIX}wine/ -luser32 -lkernel32 -lgdi32) ENDIF() ENDIF(LMMS_HOST_X86_64) SET(WINE_CXX_FLAGS "" CACHE STRING "Extra flags passed to wineg++") +STRING(REPLACE "include/wine" "include" WINE_INCLUDE_BASE_DIR ${WINE_INCLUDE_DIR}) +STRING(REPLACE "lib/libwine.so" "lib" WINE_LIBRARY_DIR ${WINE_LIBRARY}) STRING(REPLACE " " ";" WINE_BUILD_FLAGS ${CMAKE_CXX_FLAGS} " " ${CMAKE_EXE_LINKER_FLAGS} " " ${WINE_CXX_FLAGS}) ADD_CUSTOM_COMMAND( - SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp" - COMMAND ${WINE_CXX} - ARGS -I${CMAKE_BINARY_DIR} -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_INSTALL_PREFIX}/include/wine/windows -I${CMAKE_INSTALL_PREFIX}/include -I/usr/include/wine/windows ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp -ansi -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer ${WINE_BUILD_FLAGS} -o ../RemoteVstPlugin - # Ensure correct file extension - COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" - TARGET vstbase - OUTPUTS ../RemoteVstPlugin - ) + SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp" + COMMAND ${WINE_CXX} + ARGS -I${CMAKE_BINARY_DIR} + -I${CMAKE_SOURCE_DIR}/include + -I${WINE_INCLUDE_BASE_DIR} + -L${WINE_LIBRARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp + -ansi -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer + ${WINE_BUILD_FLAGS} + -o ../RemoteVstPlugin + # Ensure correct file extension + COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" + TARGET vstbase + OUTPUTS ../RemoteVstPlugin +) SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ../RemoteVstPlugin.exe.so) INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin" "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin.exe.so" DESTINATION "${PLUGIN_DIR}") diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 5ccb4d345..231475002 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -503,7 +503,16 @@ void ConfigManager::loadConfigFile( const QString & configFile ) #elif defined(LMMS_BUILD_APPLE) m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/"; #else - m_stkDir = "/usr/share/stk/rawwaves/"; + if ( qApp->applicationDirPath().startsWith("/tmp/") ) + { + // Assume AppImage bundle + m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/"; + } + else + { + // Fallback to system provided location + m_stkDir = "/usr/share/stk/rawwaves/"; + } #endif } #endif From 6e5d82f9544e237bda6430dbe2016fa9243b79ce Mon Sep 17 00:00:00 2001 From: Umcaruje Date: Mon, 16 Oct 2017 22:08:12 +0200 Subject: [PATCH 17/27] Disable portaudio on Windows --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f1f4ef0b9..c0641c3a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,12 +85,14 @@ IF(LMMS_BUILD_WIN32) SET(WANT_ALSA OFF) SET(WANT_JACK OFF) SET(WANT_PULSEAUDIO OFF) + SET(WANT_PORTAUDIO OFF) SET(WANT_SOUNDIO OFF) SET(WANT_WINMM ON) SET(LMMS_HAVE_WINMM TRUE) SET(STATUS_ALSA "") SET(STATUS_JACK "") SET(STATUS_PULSEAUDIO "") + SET(STATUS_PORTAUDIO "") SET(STATUS_WINMM "OK") SET(STATUS_APPLEMIDI "") From 6fc4577f102e5bb60cc246a426fb9ed8b4ac3be9 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Tue, 17 Oct 2017 20:24:19 +0200 Subject: [PATCH 18/27] PlayHandle: Zero out buffer before processing Fixes buffer noises when instruments don't write the whole buffer, such as bitinvader. Related: * #3884 (comment): https://github.com/LMMS/lmms/pull/3884#issuecomment-337170598 * #3883 # #3383 --- src/core/PlayHandle.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/PlayHandle.cpp b/src/core/PlayHandle.cpp index 5481ea3e2..9e92019a6 100644 --- a/src/core/PlayHandle.cpp +++ b/src/core/PlayHandle.cpp @@ -54,6 +54,7 @@ void PlayHandle::doProcessing() if( m_usesBuffer ) { m_bufferReleased = false; + BufferManager::clear(m_playHandleBuffer, Engine::mixer()->framesPerPeriod()); play( buffer() ); } else From 9d560a310473f03d4de4a49e86d805952d982171 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Tue, 17 Oct 2017 21:46:16 +0200 Subject: [PATCH 19/27] ZynAddSubFx: Fix preset loading (#3891) Fix regression from #1719 Fixes #3886 --- plugins/zynaddsubfx/ZynAddSubFx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 47da5147c..8bba1f10f 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -445,8 +445,8 @@ void ZynAddSubFxInstrument::initPlugin() RemotePlugin::message( IdZasfPresetDirectory ). addString( QSTR_TO_STDSTR( - QString( ConfigManager::inst()->factoryPresetsDir() + - QDir::separator() + "ZynAddSubFX" ) ) ) ); + QDir( ConfigManager::inst()->factoryPresetsDir() + + "/ZynAddSubFX" ).absolutePath() ) ) ); m_remotePlugin->updateSampleRate( Engine::mixer()->processingSampleRate() ); From 20ea945cee6030cb5872ff27c095976c64eaf631 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 18 Oct 2017 10:24:36 +0200 Subject: [PATCH 20/27] Fix typo --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c0641c3a8..8d9aa4ab0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,7 +92,7 @@ IF(LMMS_BUILD_WIN32) SET(STATUS_ALSA "") SET(STATUS_JACK "") SET(STATUS_PULSEAUDIO "") - SET(STATUS_PORTAUDIO "") SET(STATUS_SOUNDIO "") SET(STATUS_WINMM "OK") SET(STATUS_APPLEMIDI "") From 81966fa1a68293a5dfa37408da993bc95ac8402d Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Fri, 13 Oct 2017 20:46:55 +0200 Subject: [PATCH 21/27] Sort note patterns after both position and key --- include/Note.h | 12 ++++++++++-- src/tracks/Pattern.cpp | 23 +---------------------- 2 files changed, 11 insertions(+), 24 deletions(-) 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/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 3fa01462b..cf02d6476 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -213,28 +213,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(); From 8d6cb120b90ec2ad09efefcbf2b31714e7bd0d6f Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 18 Oct 2017 17:02:40 +0200 Subject: [PATCH 22/27] Replace MemoryManager implementation with rpmalloc (#3873) * Replace MemoryManager implementation with rpmalloc Fixes #3865 * Travis: Specify OSX image for Qt5 build --- .gitmodules | 3 + .travis.yml | 1 + include/AutomatableModel.h | 1 + include/MemoryManager.h | 108 ++------------ include/PluginFactory.h | 1 + plugins/carlabase/carla.h | 2 + src/3rdparty/CMakeLists.txt | 4 + src/3rdparty/rpmalloc/CMakeLists.txt | 30 ++++ src/3rdparty/rpmalloc/rpmalloc | 1 + src/CMakeLists.txt | 12 ++ src/core/MemoryManager.cpp | 216 +++++---------------------- src/core/Mixer.cpp | 1 + src/core/MixerWorkerThread.cpp | 1 + src/core/ProjectRenderer.cpp | 1 + src/core/main.cpp | 5 - 15 files changed, 105 insertions(+), 282 deletions(-) create mode 100644 .gitmodules create mode 100644 src/3rdparty/CMakeLists.txt create mode 100644 src/3rdparty/rpmalloc/CMakeLists.txt create mode 160000 src/3rdparty/rpmalloc/rpmalloc 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 76fc62b91..773aed79f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ matrix: - env: QT5=True TARGET_OS=win32 - env: QT5=True TARGET_OS=win64 - os: osx + osx_image: xcode8.2 env: QT5=True before_install: - . ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.before_install.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/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/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/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 a3f944b4f..bd29619b2 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/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/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 7588401c2..56e9c01c0 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -163,6 +163,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 b36ca0055..fc8dd5de2 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" @@ -203,7 +202,6 @@ void fileCheck( QString &file ) int main( int argc, char * * argv ) { // initialize memory managers - MemoryManager::init(); NotePlayHandleManager::init(); // intialize RNG @@ -930,9 +928,6 @@ int main( int argc, char * * argv ) Engine::destroy(); } - // cleanup memory managers - MemoryManager::cleanup(); - // ProjectRenderer::updateConsoleProgress() doesn't return line after render if( coreOnly ) { From 9341acd1291c41abbfbe48a678a57ada741f4bf0 Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Thu, 19 Oct 2017 03:35:55 +0200 Subject: [PATCH 23/27] Remove obsolete function - qSort() --- src/tracks/Pattern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index cf02d6476..e726ca2ee 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -271,7 +271,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); } From de20d76c8e292888827318361bd460aaca79396b Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 24 Oct 2017 00:21:25 +0900 Subject: [PATCH 24/27] Ignore release frames for single-streamed instruments (#3900) Let InstrumentSoundShaping::releaseFrames() ignore release frames for single-streamed instruments. And make it return 0 if m_instrumentTrack->instrument() is NULL. --- src/core/InstrumentSoundShaping.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) 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 ) { From f9ab2dec025672e82e5198d142e45ebc6adcf5af Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Tue, 24 Oct 2017 17:42:30 +0200 Subject: [PATCH 25/27] Automation Point delete radius and size (#3902) * Fix Automation Point delete radius. At lower zoom deleting would miss automation points to delete and at higher zoom it would be too generous and remove neighbouring points. * Increase smallest Automation Point radius. For visibility. The smallest Automatin Point radius was tiny. --- include/AutomationEditor.h | 1 + src/gui/editors/AutomationEditor.cpp | 47 +++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 7 deletions(-) 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/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 1e94c1b50..23dcd3f75 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 < AutomationPattern::quantization() ) + { + 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 ); From 05971cac6acab57370339a8f7fd01375964e2e71 Mon Sep 17 00:00:00 2001 From: Hussam Eddin Alhomsi Date: Tue, 24 Oct 2017 22:01:26 +0300 Subject: [PATCH 26/27] Fix monstro's osc 3 vol knob values. (#3912) --- plugins/monstro/Monstro.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index 99a3be95b..0f8ad5b96 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() ) ); From 9715da332b0c279739414712d4be9f0335147f3a Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Thu, 26 Oct 2017 01:32:06 +0200 Subject: [PATCH 27/27] Deleting Automation Points at coarser quantization Regression from f9ab2dec025672e82e5198d142e45ebc6adcf5af Don't follow quantization as you will have to move the cursor that length before any deletion is made. --- 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 23dcd3f75..ca65b5275 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -688,7 +688,7 @@ void AutomationEditor::removePoints( int x0, int x1 ) int x = x0; int xstep; - if( deltax < AutomationPattern::quantization() ) + if( deltax < 1 ) { return; }