diff --git a/include/MainWindow.h b/include/MainWindow.h index 69b430d33..1a58f868c 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -29,6 +29,7 @@ #include #include #include +#include #include "ConfigManager.h" #include "SubWindow.h" @@ -248,4 +249,11 @@ signals: } ; +class AutoSaveThread : public QThread +{ + Q_OBJECT +public: + void run(); +} ; + #endif diff --git a/include/PluginFactory.h b/include/PluginFactory.h index 209fa655d..b2d0131ad 100644 --- a/include/PluginFactory.h +++ b/include/PluginFactory.h @@ -25,6 +25,8 @@ #ifndef PLUGINFACTORY_H #define PLUGINFACTORY_H +#include + #include #include @@ -39,14 +41,15 @@ public: struct PluginInfo { PluginInfo() : library(nullptr), descriptor(nullptr) {} + const QString name() const; QFileInfo file; - QLibrary* library; + std::shared_ptr library; Plugin::Descriptor* descriptor; - bool isNull() const {return library == 0;} + bool isNull() const {return ! library;} }; - typedef QList PluginInfoList; + typedef QList PluginInfoList; typedef QMultiMap DescriptorMap; PluginFactory(); @@ -80,11 +83,11 @@ public slots: private: DescriptorMap m_descriptors; PluginInfoList m_pluginInfos; - QMap m_pluginByExt; + QMap m_pluginByExt; QHash m_errors; - static PluginFactory* s_instance; + static std::unique_ptr s_instance; }; #define pluginFactory PluginFactory::instance() diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index d016c5acd..b3ac4b676 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -424,6 +424,7 @@ enum RemoteMessageIDs IdChangeSharedMemoryKey, IdChangeInputCount, IdChangeOutputCount, + IdChangeInputOutputCount, IdShowUI, IdHideUI, IdSaveSettingsToString, @@ -919,6 +920,15 @@ public: sendMessage( message( IdChangeOutputCount ).addInt( _i ) ); } + void setInputOutputCount( int i, int o ) + { + m_inputCount = i; + m_outputCount = o; + sendMessage( message( IdChangeInputOutputCount ) + .addInt( i ) + .addInt( o ) ); + } + virtual int inputCount() const { return m_inputCount; @@ -1072,6 +1082,14 @@ RemotePluginBase::message RemotePluginBase::waitForMessage( const message & _wm, bool _busy_waiting ) { +#ifndef BUILD_REMOTE_PLUGIN_CLIENT + if( _busy_waiting ) + { + // No point processing events outside of the main thread + _busy_waiting = QThread::currentThread() == + QCoreApplication::instance()->thread(); + } +#endif while( !isInvalid() ) { #ifndef BUILD_REMOTE_PLUGIN_CLIENT diff --git a/include/Song.h b/include/Song.h index 9c9d0304c..7b517a4c7 100644 --- a/include/Song.h +++ b/include/Song.h @@ -2,7 +2,7 @@ * Song.h - class song - the root of the model-tree * * Copyright (c) 2004-2014 Tobias Doerffel - * + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -254,7 +254,7 @@ public: void addController( Controller * c ); void removeController( Controller * c ); - + const ControllerVector & controllers() const { @@ -325,13 +325,13 @@ private: { return m_playPos[m_playMode].getTicks(); } - + inline f_cnt_t currentFrame() const { - return m_playPos[m_playMode].getTicks() * Engine::framesPerTick() + + return m_playPos[m_playMode].getTicks() * Engine::framesPerTick() + m_playPos[m_playMode].currentFrame(); } - + void setPlayPos( tick_t ticks, PlayModes playMode ); void saveControllerStates( QDomDocument & doc, QDomElement & element ); @@ -367,7 +367,7 @@ private: bool m_loadingProject; - QList m_errors; + QStringList m_errors; PlayModes m_playMode; PlayPos m_playPos[Mode_Count]; diff --git a/plugins/vibed/vibrating_string.h b/plugins/vibed/vibrating_string.h index 6d620ace0..c21ed43cf 100644 --- a/plugins/vibed/vibrating_string.h +++ b/plugins/vibed/vibrating_string.h @@ -130,7 +130,7 @@ private: offset = ( m_randomize / 2.0f - m_randomize ) * r; _dl->data[i] = _scale * - _values[_dl->length - i] + + _values[_dl->length - i - 1] + offset; } for( int i = _pick; i < _dl->length; i++ ) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 03e160a8d..67ce71ea8 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -44,6 +44,10 @@ #ifdef LMMS_BUILD_LINUX +#ifndef NOMINMAX +#define NOMINMAX +#endif + #ifndef O_BINARY #define O_BINARY 0 #endif @@ -67,6 +71,7 @@ #include +#include #include @@ -113,7 +118,7 @@ class RemoteVstPlugin; RemoteVstPlugin * __plugin = NULL; -DWORD __GuiThreadID = 0; +HWND __MessageHwnd = NULL; @@ -244,8 +249,58 @@ public: pthread_mutex_unlock( &m_pluginLock ); } + inline void lockShm() + { + pthread_mutex_lock( &m_shmLock ); + } + + inline void unlockShm() + { + pthread_mutex_unlock( &m_shmLock ); + } + + inline bool isShmValid() + { + return m_shmValid; + } + + inline void setShmIsValid( bool valid ) + { + m_shmValid = valid; + } + + inline bool isProcessing() const + { + return m_processing; + } + + inline void setProcessing( bool processing ) + { + m_processing = processing; + } + + inline void queueMessage( const message & m ) { + m_messageList.push( m ); + } + + inline bool shouldGiveIdle() const + { + return m_shouldGiveIdle; + } + + inline void setShouldGiveIdle( bool shouldGiveIdle ) + { + m_shouldGiveIdle = shouldGiveIdle; + } + + void idle(); + void processUIThreadMessages(); + static DWORD WINAPI processingThread( LPVOID _param ); - static DWORD WINAPI guiEventLoop( LPVOID _param ); + static bool setupMessageWindow(); + static DWORD WINAPI guiEventLoop(); + static LRESULT CALLBACK messageWndProc( HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam ); private: @@ -303,11 +358,18 @@ private: bool m_initialized; pthread_mutex_t m_pluginLock; + bool m_processing; + + std::queue m_messageList; + bool m_shouldGiveIdle; float * * m_inputs; float * * m_outputs; + pthread_mutex_t m_shmLock; + bool m_shmValid; + typedef std::vector VstMidiEventList; VstMidiEventList m_midiEvents; @@ -348,8 +410,13 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : m_windowHeight( 0 ), m_initialized( false ), m_pluginLock(), + m_processing( false ), + m_messageList(), + m_shouldGiveIdle( false ), m_inputs( NULL ), m_outputs( NULL ), + m_shmLock(), + m_shmValid( false ), m_midiEvents(), m_bpm( 0 ), m_currentSamplePos( 0 ), @@ -360,6 +427,7 @@ RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) : { pthread_mutex_init( &m_pluginLock, NULL ); + pthread_mutex_init( &m_shmLock, NULL ); __plugin = this; @@ -458,6 +526,7 @@ RemoteVstPlugin::~RemoteVstPlugin() delete[] m_inputs; delete[] m_outputs; + pthread_mutex_destroy( &m_shmLock ); pthread_mutex_destroy( &m_pluginLock ); } @@ -794,6 +863,16 @@ void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) // now we're ready to fetch sound from VST-plugin + lock(); + lockShm(); + + if( !isShmValid() ) + { + unlockShm(); + unlock(); + return; + } + for( int i = 0; i < inputCount(); ++i ) { m_inputs[i] = &((float *) _in)[i * bufferSize()]; @@ -805,8 +884,6 @@ void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) memset( m_outputs[i], 0, bufferSize() * sizeof( float ) ); } - lock(); - #ifdef OLD_VST_SDK if( m_plugin->flags & effFlagsCanReplacing ) { @@ -822,6 +899,7 @@ void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) } #endif + unlockShm(); unlock(); m_currentSamplePos += bufferSize(); @@ -1338,14 +1416,19 @@ void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len ) void RemoteVstPlugin::updateInOutCount() { + lockShm(); + + setShmIsValid( false ); + + unlockShm(); + delete[] m_inputs; delete[] m_outputs; m_inputs = NULL; m_outputs = NULL; - setInputCount( inputCount() ); - setOutputCount( outputCount() ); + setInputOutputCount( inputCount(), outputCount() ); char buf[64]; sprintf( buf, "inputs: %d output: %d\n", inputCount(), outputCount() ); @@ -1418,8 +1501,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, SHOW_CALLBACK ("amc: audioMasterIdle\n" ); // call application idle routine (this will // call effEditIdle for all open editors too) - PostThreadMessage( __GuiThreadID, - WM_USER, GiveIdle, 0 ); + PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 ); return 0; case audioMasterPinConnected: @@ -1720,8 +1802,7 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, case audioMasterUpdateDisplay: SHOW_CALLBACK( "amc: audioMasterUpdateDisplay\n" ); // something has changed, update 'multi-fx' display - PostThreadMessage( __GuiThreadID, - WM_USER, GiveIdle, 0 ); + PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 ); return 0; #if kVstVersion > 2 @@ -1754,6 +1835,43 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, +void RemoteVstPlugin::idle() +{ + if( isProcessing() ) + { + setShouldGiveIdle( true ); + return; + } + setProcessing( true ); + pluginDispatch( effEditIdle ); + setShouldGiveIdle( false ); + setProcessing( false ); + // We might have received a message whilst idling + processUIThreadMessages(); +} + + + + +void RemoteVstPlugin::processUIThreadMessages() +{ + setProcessing( true ); + while( m_messageList.size() ) + { + processMessage( m_messageList.front() ); + m_messageList.pop(); + if( shouldGiveIdle() ) + { + pluginDispatch( effEditIdle ); + setShouldGiveIdle( false ); + } + } + setProcessing( false ); +} + + + + DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) { RemoteVstPlugin * _this = static_cast( _param ); @@ -1765,9 +1883,14 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) { _this->processMessage( m ); } + else if( m.id == IdChangeSharedMemoryKey ) + { + _this->processMessage( m ); + _this->setShmIsValid( true ); + } else { - PostThreadMessage( __GuiThreadID, + PostMessage( __MessageHwnd, WM_USER, ProcessPluginMessage, (LPARAM) new message( m ) ); @@ -1775,7 +1898,7 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) } // notify GUI thread about shutdown - PostThreadMessage( __GuiThreadID, WM_USER, ClosePlugin, 0 ); + PostMessage( __MessageHwnd, WM_USER, ClosePlugin, 0 ); return 0; } @@ -1783,61 +1906,37 @@ DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param ) -DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param ) +bool RemoteVstPlugin::setupMessageWindow() { - RemoteVstPlugin * _this = static_cast( _param ); - HMODULE hInst = GetModuleHandle( NULL ); if( hInst == NULL ) { - _this->debugMessage( "guiEventLoop(): can't get " + __plugin->debugMessage( "setupMessageWindow(): can't get " "module handle\n" ); - return -1; + return false; } - HWND timerWindow = CreateWindowEx( 0, "LVSL", "dummy", + __MessageHwnd = CreateWindowEx( 0, "LVSL", "dummy", 0, 0, 0, 0, 0, NULL, NULL, hInst, NULL ); + SetWindowLongPtr( __MessageHwnd, GWLP_WNDPROC, + reinterpret_cast( RemoteVstPlugin::messageWndProc ) ); // install GUI update timer - SetTimer( timerWindow, 1000, 50, NULL ); + SetTimer( __MessageHwnd, 1000, 50, NULL ); + return true; +} + + + + +DWORD WINAPI RemoteVstPlugin::guiEventLoop() +{ MSG msg; - - bool quit = false; - while( quit == false && GetMessage( &msg, NULL, 0, 0 ) ) + while( GetMessage( &msg, NULL, 0, 0 ) > 0 ) { TranslateMessage( &msg ); DispatchMessage( &msg ); - - if( msg.message == WM_TIMER && _this->isInitialized() ) - { - // give plugin some idle-time for GUI-update - _this->pluginDispatch( effEditIdle ); - } - else if( msg.message == WM_USER ) - { - switch( msg.wParam ) - { - case ProcessPluginMessage: - { - message * m = (message *) msg.lParam; - _this->processMessage( *m ); - delete m; - break; - } - - case GiveIdle: - _this->pluginDispatch( effEditIdle ); - break; - - case ClosePlugin: - quit = true; - break; - - default: - break; - } - } } return 0; @@ -1846,6 +1945,49 @@ DWORD WINAPI RemoteVstPlugin::guiEventLoop( LPVOID _param ) +LRESULT CALLBACK RemoteVstPlugin::messageWndProc( HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam ) +{ + if( uMsg == WM_TIMER && __plugin->isInitialized() ) + { + // give plugin some idle-time for GUI-update + __plugin->idle(); + return 0; + } + else if( uMsg == WM_USER ) + { + switch( wParam ) + { + case ProcessPluginMessage: + { + message * m = (message *) lParam; + __plugin->queueMessage( *m ); + delete m; + if( !__plugin->isProcessing() ) + { + __plugin->processUIThreadMessages(); + } + return 0; + } + + case GiveIdle: + __plugin->idle(); + return 0; + + case ClosePlugin: + PostQuitMessage(0); + return 0; + + default: + break; + } + } + return DefWindowProc( hwnd, uMsg, wParam, lParam ); +} + + + + int main( int _argc, char * * _argv ) { #ifdef SYNC_WITH_SHM_FIFO @@ -1893,7 +2035,10 @@ int main( int _argc, char * * _argv ) if( __plugin->isInitialized() ) { - __GuiThreadID = GetCurrentThreadId(); + if( RemoteVstPlugin::setupMessageWindow() == false ) + { + return -1; + } if( CreateThread( NULL, 0, RemoteVstPlugin::processingThread, __plugin, 0, NULL ) == NULL ) { @@ -1901,7 +2046,7 @@ int main( int _argc, char * * _argv ) "processingThread\n" ); return -1; } - RemoteVstPlugin::guiEventLoop( __plugin ); + RemoteVstPlugin::guiEventLoop(); } diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 22c9f7c20..9873d28e5 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -389,7 +389,7 @@ int VstPlugin::currentProgram() { lock(); sendMessage( message( IdVstCurrentProgram ) ); - waitForMessage( IdVstCurrentProgram ); + waitForMessage( IdVstCurrentProgram, true ); unlock(); return m_currentProgram; @@ -401,7 +401,7 @@ const QMap & VstPlugin::parameterDump() { lock(); sendMessage( IdVstGetParameterDump ); - waitForMessage( IdVstParameterDump ); + waitForMessage( IdVstParameterDump, true ); unlock(); return m_parameterDump; @@ -528,7 +528,7 @@ void VstPlugin::openPreset( ) QSTR_TO_STDSTR( QDir::toNativeSeparators( ofd.selectedFiles()[0] ) ) ) ); - waitForMessage( IdLoadPresetFile ); + waitForMessage( IdLoadPresetFile, true ); unlock(); } } @@ -540,7 +540,7 @@ void VstPlugin::setProgram( int index ) { lock(); sendMessage( message( IdVstSetProgram ).addInt( index ) ); - waitForMessage( IdVstSetProgram ); + waitForMessage( IdVstSetProgram, true ); unlock(); } @@ -551,7 +551,7 @@ void VstPlugin::rotateProgram( int offset ) { lock(); sendMessage( message( IdVstRotateProgram ).addInt( offset ) ); - waitForMessage( IdVstRotateProgram ); + waitForMessage( IdVstRotateProgram, true ); unlock(); } @@ -562,7 +562,7 @@ void VstPlugin::loadProgramNames() { lock(); sendMessage( message( IdVstProgramNames ) ); - waitForMessage( IdVstProgramNames ); + waitForMessage( IdVstProgramNames, true ); unlock(); } @@ -599,7 +599,7 @@ void VstPlugin::savePreset( ) QSTR_TO_STDSTR( QDir::toNativeSeparators( fns ) ) ) ); - waitForMessage( IdSavePresetFile ); + waitForMessage( IdSavePresetFile, true ); unlock(); } } @@ -611,7 +611,7 @@ void VstPlugin::setParam( int i, float f ) { lock(); sendMessage( message( IdVstSetParameter ).addInt( i ).addFloat( f ) ); - //waitForMessage( IdVstSetParameter ); + //waitForMessage( IdVstSetParameter, true ); unlock(); } @@ -640,7 +640,7 @@ void VstPlugin::loadChunk( const QByteArray & _chunk ) QSTR_TO_STDSTR( QDir::toNativeSeparators( tf.fileName() ) ) ). addInt( _chunk.size() ) ); - waitForMessage( IdLoadSettingsFromFile ); + waitForMessage( IdLoadSettingsFromFile, true ); unlock(); } } @@ -659,7 +659,7 @@ QByteArray VstPlugin::saveChunk() addString( QSTR_TO_STDSTR( QDir::toNativeSeparators( tf.fileName() ) ) ) ); - waitForMessage( IdSaveSettingsToFile ); + waitForMessage( IdSaveSettingsToFile, true ); unlock(); a = tf.readAll(); } diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index cfbca8a7c..936b33cdc 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -42,7 +42,7 @@ qint64 qHash(const QFileInfo& fi) return qHash(fi.absoluteFilePath()); } -PluginFactory* PluginFactory::s_instance = nullptr; +std::unique_ptr PluginFactory::s_instance; PluginFactory::PluginFactory() { @@ -87,9 +87,9 @@ PluginFactory::~PluginFactory() PluginFactory* PluginFactory::instance() { if (s_instance == nullptr) - s_instance = new PluginFactory(); + s_instance.reset(new PluginFactory()); - return s_instance; + return s_instance.get(); } const Plugin::DescriptorList PluginFactory::descriptors() const @@ -109,16 +109,15 @@ const PluginFactory::PluginInfoList& PluginFactory::pluginInfos() const const PluginFactory::PluginInfo PluginFactory::pluginSupportingExtension(const QString& ext) { - PluginInfo* info = m_pluginByExt.value(ext, nullptr); - return info == nullptr ? PluginInfo() : *info; + return m_pluginByExt.value(ext, PluginInfo()); } const PluginFactory::PluginInfo PluginFactory::pluginInfo(const char* name) const { - for (const PluginInfo* info : m_pluginInfos) + for (const PluginInfo& info : m_pluginInfos) { - if (qstrcmp(info->descriptor->name, name) == 0) - return *info; + if (qstrcmp(info.descriptor->name, name) == 0) + return info; } return PluginInfo(); } @@ -150,7 +149,7 @@ void PluginFactory::discoverPlugins() for (const QFileInfo& file : files) { - QLibrary* library = new QLibrary(file.absoluteFilePath()); + auto library = std::make_shared(file.absoluteFilePath()); if (! library->load()) { m_errors[file.baseName()] = library->errorString(); @@ -167,7 +166,7 @@ void PluginFactory::discoverPlugins() descriptorName = descriptorName.mid(3); } - Plugin::Descriptor* pluginDescriptor = (Plugin::Descriptor*) library->resolve(descriptorName.toUtf8().constData()); + Plugin::Descriptor* pluginDescriptor = reinterpret_cast(library->resolve(descriptorName.toUtf8().constData())); if(pluginDescriptor == nullptr) { qWarning() << qApp->translate("PluginFactory", "LMMS plugin %1 does not have a plugin descriptor named %2!"). @@ -175,26 +174,20 @@ void PluginFactory::discoverPlugins() continue; } - PluginInfo* info = new PluginInfo; - info->file = file; - info->library = library; - info->descriptor = pluginDescriptor; + PluginInfo info; + info.file = file; + info.library = library; + info.descriptor = pluginDescriptor; pluginInfos << info; - for (const QString& ext : QString(info->descriptor->supportedFileTypes).split(',')) + for (const QString& ext : QString(info.descriptor->supportedFileTypes).split(',')) { m_pluginByExt.insert(ext, info); } - descriptors.insert(info->descriptor->type, info->descriptor); + descriptors.insert(info.descriptor->type, info.descriptor); } - - for (PluginInfo* info : m_pluginInfos) - { - delete info->library; - delete info; - } m_pluginInfos = pluginInfos; m_descriptors = descriptors; } diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index bb7e39c6a..c7756552c 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -481,6 +481,12 @@ bool RemotePlugin::processMessage( const message & _m ) resizeSharedProcessingMemory(); break; + case IdChangeInputOutputCount: + m_inputCount = _m.getInt( 0 ); + m_outputCount = _m.getInt( 1 ); + resizeSharedProcessingMemory(); + break; + case IdDebugMessage: fprintf( stderr, "RemotePlugin::DebugMessage: %s", _m.getString( 0 ).c_str() ); diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 97ebc2f17..515d5a67f 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -170,7 +170,7 @@ void Song::setTimeSignature() emit dataChanged(); m_oldTicksPerTact = ticksPerTact(); - m_vstSyncController.setTimeSignature( + m_vstSyncController.setTimeSignature( getTimeSigModel().getNumerator(), getTimeSigModel().getDenominator() ); } @@ -247,7 +247,7 @@ void Song::processNextBuffer() // check for looping-mode and act if necessary TimeLineWidget * tl = m_playPos[m_playMode].m_timeLine; - bool checkLoop = + bool checkLoop = tl != NULL && m_exporting == false && tl->loopPointsEnabled(); if( checkLoop ) @@ -275,7 +275,7 @@ void Song::processNextBuffer() // did we play a tick? if( currentFrame >= framesPerTick ) { - int ticks = m_playPos[m_playMode].getTicks() + + int ticks = m_playPos[m_playMode].getTicks() + ( int )( currentFrame / framesPerTick ); m_vstSyncController.setAbsolutePosition( ticks ); @@ -323,11 +323,11 @@ void Song::processNextBuffer() if( checkLoop ) { - m_vstSyncController.startCycle( + m_vstSyncController.startCycle( tl->loopBegin().getTicks(), tl->loopEnd().getTicks() ); // if looping-mode is enabled and we have got - // past the looping range, return to the + // past the looping range, return to the // beginning of the range if( m_playPos[m_playMode] >= tl->loopEnd() ) { @@ -348,10 +348,10 @@ void Song::processNextBuffer() m_playPos[m_playMode].setCurrentFrame( currentFrame ); } - f_cnt_t framesToPlay = + f_cnt_t framesToPlay = Engine::mixer()->framesPerPeriod() - framesPlayed; - f_cnt_t framesLeft = ( f_cnt_t )framesPerTick - + f_cnt_t framesLeft = ( f_cnt_t )framesPerTick - ( f_cnt_t )currentFrame; // skip last frame fraction if( framesLeft == 0 ) @@ -361,7 +361,7 @@ void Song::processNextBuffer() + 1.0f ); continue; } - // do we have samples left in this tick but these are less + // do we have samples left in this tick but these are less // than samples we have to play? if( framesLeft < framesToPlay ) { @@ -602,7 +602,7 @@ void Song::setPlayPos( tick_t ticks, PlayModes playMode ) m_playPos[playMode].setCurrentFrame( 0.0f ); // send a signal if playposition changes during playback - if( isPlaying() ) + if( isPlaying() ) { emit playbackPositionChanged(); emit updateSampleTracks(); @@ -1108,7 +1108,7 @@ void Song::loadProject( const QString & fileName ) // BB-tracks Engine::getBBTrackContainer()->fixIncorrectPositions(); - // Connect controller links to their controllers + // Connect controller links to their controllers // now that everything is loaded ControllerConnection::finalizeConnections(); @@ -1363,8 +1363,8 @@ void Song::exportProject( bool multiExport ) { int stx = efd.selectedNameFilter().indexOf( "(*." ); int etx = efd.selectedNameFilter().indexOf( ")" ); - - if ( stx > 0 && etx > stx ) + + if ( stx > 0 && etx > stx ) { // Get first extension from selected dropdown. // i.e. ".wav" from "WAV-File (*.wav), Dummy-File (*.dum)" @@ -1400,9 +1400,9 @@ void Song::exportProjectMidi() } FileDialog efd( gui->mainWindow() ); - + efd.setFileMode( FileDialog::AnyFile ); - + QStringList types; types << tr("MIDI File (*.mid)"); efd.setNameFilters( types ); @@ -1430,9 +1430,9 @@ void Song::exportProjectMidi() QString export_filename = efd.selectedFiles()[0]; if (!export_filename.endsWith(suffix)) export_filename += suffix; - + // NOTE start midi export - + // instantiate midi export plugin TrackContainer::TrackList tracks; tracks += Engine::getSong()->tracks(); @@ -1519,26 +1519,17 @@ void Song::collectError( const QString error ) bool Song::hasErrors() { - return !m_errors.empty(); + return ( m_errors.length() > 0 ); } QString Song::errorSummary() { - QString errors; - - for ( int i = 0 ; i < m_errors.length() ; i++ ) - { - errors.append( m_errors.value( i ) + "\n" ); - } + QString errors = m_errors.join("\n") + '\n'; errors.prepend( "\n\n" ); errors.prepend( tr( "The following errors occured while loading: " ) ); return errors; } - - - - diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 63b931823..f65c96066 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1541,7 +1541,9 @@ void MainWindow::autoSave() "enablerunningautosave" ).toInt() || ! Engine::getSong()->isPlaying() ) ) { - Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile()); + AutoSaveThread * ast = new AutoSaveThread(); + connect( ast, SIGNAL( finished() ), ast, SLOT( deleteLater() ) ); + ast->start(); autoSaveTimerReset(); // Reset timer } else @@ -1553,3 +1555,11 @@ void MainWindow::autoSave() } } } + + + + +void AutoSaveThread::run() +{ + Engine::getSong()->saveProjectFile(ConfigManager::inst()->recoveryFile()); +} diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index a3b187af3..c54b60d22 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -81,6 +81,7 @@ SongEditor::SongEditor( Song * song ) : m_smoothScroll( ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt() ), m_mode(DrawMode) { + m_zoomingModel->setParent(this); // create time-line int widgetTotal = ConfigManager::inst()->value( "ui", "compacttrackbuttons" ).toInt()==1 ?