diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index 5ac33e766..46ffc5e15 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -27,6 +27,7 @@ #include "export.h" #include "midi.h" +#include "VST_sync_shm.h" #include #include @@ -811,7 +812,9 @@ class RemotePluginClient : public RemotePluginBase public: RemotePluginClient( key_t _shm_in, key_t _shm_out ); virtual ~RemotePluginClient(); - +#ifdef USE_QT_SHMEM + sncVST * getQtVSTshm(); +#endif virtual bool processMessage( const message & _m ); virtual void process( const sampleFrame * _in_buf, @@ -879,7 +882,9 @@ private: #ifdef USE_QT_SHMEM QSharedMemory m_shmObj; + QSharedMemory m_shmQtID; #endif + sncVST * m_SncVSTplug; float * m_shm; int m_inputCount; @@ -1007,13 +1012,60 @@ RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) : RemotePluginBase( new shmFifo( _shm_in ), new shmFifo( _shm_out ) ), #ifdef USE_QT_SHMEM m_shmObj(), + m_shmQtID( "/usr/bin/lmms" ), #endif + m_SncVSTplug( NULL ), m_shm( NULL ), m_inputCount( 0 ), m_outputCount( 0 ), m_sampleRate( 44100 ), m_bufferSize( 0 ) { +#ifdef USE_QT_SHMEM + if( m_shmQtID.attach( QSharedMemory::ReadOnly ) ) + { + m_SncVSTplug = (sncVST *) m_shmQtID.data(); + m_bufferSize = m_SncVSTplug->m_bufferSize; + m_sampleRate = m_SncVSTplug->m_sampleRate; + return; + } +#else + key_t key; + int m_shmID; + + if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 ) + { + perror( "RemotePluginClient::ftok" ); + } + else + { // connect to shared memory segment + if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 ) + { + perror( "RemotePluginClient::shmget" ); + } + else + { // attach segment + m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); + if( m_SncVSTplug == (sncVST *)( -1 ) ) + { + perror( "RemotePluginClient::shmat" ); + } + else + { + m_bufferSize = m_SncVSTplug->m_bufferSize; + m_sampleRate = m_SncVSTplug->m_sampleRate; + + // detach segment + if( shmdt(m_SncVSTplug) == -1 ) + { + perror("RemotePluginClient::shmdt"); + } + return; + } + } + } +#endif + // if attaching shared memory fails sendMessage( IdSampleRateInformation ); sendMessage( IdBufferSizeInformation ); } @@ -1023,6 +1075,9 @@ RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) : RemotePluginClient::~RemotePluginClient() { +#ifdef USE_QT_SHMEM + m_shmQtID.detach(); +#endif sendMessage( IdQuit ); #ifndef USE_QT_SHMEM @@ -1032,6 +1087,14 @@ RemotePluginClient::~RemotePluginClient() +#ifdef USE_QT_SHMEM +sncVST * RemotePluginClient::getQtVSTshm() +{ + return m_SncVSTplug; +} +#endif + + bool RemotePluginClient::processMessage( const message & _m ) { diff --git a/include/VST_sync_shm.h b/include/VST_sync_shm.h new file mode 100644 index 000000000..595b21cd9 --- /dev/null +++ b/include/VST_sync_shm.h @@ -0,0 +1,58 @@ +/* + * VST_sync_shm.h - type declarations needed for VST to lmms host sync + * + * Copyright (c) 2004-2013 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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 of the License, 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef _VST_SYNC_SHM_H +#define _VST_SYNC_SHM_H + +// VST sync frequency (in ms), how often will be VST plugin synced +// keep it power of two if possible (not used by now) +//#define VST_SNC_TIMER 1 + +// When defined, latency should be subtracted from song PPQ position +//#define VST_SNC_LATENCY + +// define file for ftok as shared memory shmget key +#define VST_SNC_SHM_KEY_FILE "/dev/null" +//#define VST_SNC_SHM_RND_KEY 3561653564469 + +struct sncVST +{ + bool isPlayin; + double ppqPos; + int timeSigNumer; + int timeSigDenom; + bool isCycle; + bool hasSHM; + double cycleStart; + double cycleEnd; + int m_bufferSize; + int m_sampleRate; + int m_bpm; + +#ifdef VST_SNC_LATENCY + double m_latency; +#endif +} ; + +#endif diff --git a/include/aeffectx.h b/include/aeffectx.h index 1e8dea3f6..393249b03 100644 --- a/include/aeffectx.h +++ b/include/aeffectx.h @@ -114,8 +114,14 @@ const int kEffectMagic = CCONST( 'V', 's', 't', 'P' ); const int kVstLangEnglish = 1; const int kVstMidiType = 1; const int kVstParameterUsesFloatStep = 1 << 2; +const int kVstPpqPosValid = 1 << 9; const int kVstTempoValid = 1 << 10; +const int kVstBarsValid = 1 << 11; +const int kVstCyclePosValid = 1 << 12; +const int kVstTimeSigValid = 1 << 13; const int kVstTransportPlaying = 1 << 1; +const int kVstTransportCycleActive = 1 << 2; +const int kVstTransportChanged = 1; class RemoteVstPlugin; @@ -267,12 +273,18 @@ public: double samplePos; // 08 double sampleRate; - // unconfirmed 10 18 - char empty1[8 + 8]; + // unconfirmed 10 + char empty1[8]; + // 18 + double ppqPos; // 20? double tempo; - // unconfirmed 28 30 38 - char empty2[8 + 8 + 8]; + // 28 + double barStartPos; + // 30? + double cycleStartPos; + // 38? + double cycleEndPos; // 40? int timeSigNumerator; // 44? diff --git a/include/setup_dialog.h b/include/setup_dialog.h index e899cd6e5..d22fd9328 100644 --- a/include/setup_dialog.h +++ b/include/setup_dialog.h @@ -108,6 +108,7 @@ private slots: void toggleAutoSave( bool _enabled ); void toggleOneInstrumentTrackWindow( bool _enabled ); void toggleCompactTrackButtons( bool _enabled ); + void toggleSyncVSTPlugins( bool _enabled ); private: @@ -156,6 +157,7 @@ private: bool m_enableAutoSave; bool m_oneInstrumentTrackWindow; bool m_compactTrackButtons; + bool m_syncVSTPlugins; typedef QMap AswMap; typedef QMap MswMap; diff --git a/include/song.h b/include/song.h index 45c0a6f69..eddfe2d88 100644 --- a/include/song.h +++ b/include/song.h @@ -25,13 +25,14 @@ #ifndef _SONG_H #define _SONG_H +#include #include #include "track_container.h" #include "AutomatableModel.h" #include "Controller.h" #include "MeterModel.h" - +#include "VST_sync_shm.h" class AutomationTrack; class pattern; @@ -249,6 +250,8 @@ private slots: void updateFramesPerTick(); + void updateSampleRateSHM(); + private: @@ -323,6 +326,9 @@ private: } ; QVector m_actions; + int m_shmID; + sncVST * m_SncVSTplug; + QSharedMemory m_shmQtID; friend class engine; friend class songEditor; diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 1231cb1c4..254d70071 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -92,7 +92,20 @@ struct ERect #include "midi.h" #include "communication.h" +#include "VST_sync_shm.h" +#ifdef LMMS_BUILD_WIN32 +#define USE_QT_SHMEM +#endif + +#ifndef USE_QT_SHMEM +#include +#include +#include +#include +#include +#include +#endif static VstHostLanguages hlang = LanguageEnglish; @@ -303,6 +316,18 @@ private: double m_currentSamplePos; int m_currentProgram; + // host to plugin synchronisation data structure + struct in + { + double lastppqPos; + double m_Timestamp; + } ; + + in * m_in; + + int m_shmID; + sncVST * m_SncVSTplug; + } ; @@ -324,23 +349,65 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : m_midiEvents(), m_bpm( 0 ), m_currentSamplePos( 0 ), - m_currentProgram( -1 ) + m_currentProgram( -1 ), + m_in( NULL ), + m_shmID( -1 ), + m_SncVSTplug( NULL ) + { pthread_mutex_init( &m_pluginLock, NULL ); __plugin = this; +#ifndef USE_QT_SHMEM + key_t key; + if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 ) + { + perror( "RemoteVstPlugin.cpp::ftok" ); + } + else + { // connect to shared memory segment + if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 ) + { + perror( "RemoteVstPlugin.cpp::shmget" ); + } + else + { // attach segment + m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); + if( m_SncVSTplug == (sncVST *)( -1 ) ) + { + perror( "RemoteVstPlugin.cpp::shmat" ); + } + } + } +#else + m_SncVSTplug = RemotePluginClient::getQtVSTshm(); +#endif + if( m_SncVSTplug == NULL ) + { + fprintf(stderr, "RemoteVstPlugin.cpp: " + "Failed to initialize shared memory for VST synchronization.\n" + " (VST-host synchronization will be disabled)\n"); + m_SncVSTplug = (sncVST*) malloc( sizeof( sncVST ) ); + m_SncVSTplug->isPlayin = true; + m_SncVSTplug->timeSigNumer = 4; + m_SncVSTplug->timeSigDenom = 4; + m_SncVSTplug->ppqPos = 0; + m_SncVSTplug->isCycle = false; + m_SncVSTplug->hasSHM = false; + m_SncVSTplug->m_sampleRate = sampleRate(); + } + + m_in = ( in* ) new char[ sizeof( in ) ]; + m_in->lastppqPos = 0; + m_in->m_Timestamp = -1; + // process until we have loaded the plugin while( 1 ) { message m = receiveMessage(); processMessage( m ); - //if( m.id == IdVstLoadPlugin || m.id == IdQuit ) - - // IdBufferSizeInformation is sent right after plugin load - // otherwise causes deadlocks to FxMixer/EffectChain - - if( m.id == IdBufferSizeInformation || m.id == IdQuit ) + if( m.id == IdVstLoadPlugin || m.id == IdQuit ) { break; } @@ -352,6 +419,21 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : RemoteVstPlugin::~RemoteVstPlugin() { +#ifndef USE_QT_SHMEM + // detach shared memory segment + if( shmdt( m_SncVSTplug ) == -1) + { + if( __plugin->m_SncVSTplug->hasSHM ) + { + perror( "~RemoteVstPlugin::shmdt" ); + } + if( m_SncVSTplug != NULL ) + { + delete m_SncVSTplug; + m_SncVSTplug = NULL; + } + } +#endif if( m_window != NULL ) { pluginDispatch( effEditClose ); @@ -491,6 +573,12 @@ void RemoteVstPlugin::init( const std::string & _plugin_file ) updateInOutCount(); + // some plugins have to set samplerate during init + if( m_SncVSTplug->hasSHM ) + { + updateSampleRate(); + } + /* set program to zero */ /* i comment this out because it breaks dfx Geometer * looks like we cant set programs for it @@ -1356,16 +1444,62 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, // fields are required (see valid masks above), as some // items may require extensive conversions - memset( &_timeInfo, 0, sizeof( _timeInfo ) ); + // Shared memory was initialised? - see song.cpp + //assert( __plugin->m_SncVSTplug != NULL ); + memset( &_timeInfo, 0, sizeof( _timeInfo ) ); _timeInfo.samplePos = __plugin->m_currentSamplePos; - _timeInfo.sampleRate = __plugin->sampleRate(); + _timeInfo.sampleRate = __plugin->m_SncVSTplug->hasSHM ? + __plugin->m_SncVSTplug->m_sampleRate : + __plugin->sampleRate(); _timeInfo.flags = 0; - _timeInfo.tempo = __plugin->m_bpm; - _timeInfo.timeSigNumerator = 4; - _timeInfo.timeSigDenominator = 4; - _timeInfo.flags |= (/* kVstBarsValid|*/kVstTempoValid ); - _timeInfo.flags |= kVstTransportPlaying; + _timeInfo.tempo = __plugin->m_SncVSTplug->hasSHM ? + __plugin->m_SncVSTplug->m_bpm : + __plugin->m_bpm; + _timeInfo.timeSigNumerator = __plugin->m_SncVSTplug->timeSigNumer; + _timeInfo.timeSigDenominator = __plugin->m_SncVSTplug->timeSigDenom; + _timeInfo.flags |= kVstTempoValid; + _timeInfo.flags |= kVstTimeSigValid; + + if( __plugin->m_SncVSTplug->isCycle ) + { + _timeInfo.cycleStartPos = __plugin->m_SncVSTplug->cycleStart; + _timeInfo.cycleEndPos = __plugin->m_SncVSTplug->cycleEnd; + _timeInfo.flags |= kVstCyclePosValid; + _timeInfo.flags |= kVstTransportCycleActive; + } + + if( __plugin->m_SncVSTplug->ppqPos != + __plugin->m_in->m_Timestamp ) + { + _timeInfo.ppqPos = __plugin->m_SncVSTplug->ppqPos; + _timeInfo.flags |= kVstTransportChanged; + __plugin->m_in->lastppqPos = __plugin->m_SncVSTplug->ppqPos; + __plugin->m_in->m_Timestamp = __plugin->m_SncVSTplug->ppqPos; + } + else if( __plugin->m_SncVSTplug->isPlayin ) + { + __plugin->m_in->lastppqPos += ( + __plugin->m_SncVSTplug->hasSHM ? + __plugin->m_SncVSTplug->m_bpm : + __plugin->m_bpm ) / (double)10340; + _timeInfo.ppqPos = __plugin->m_in->lastppqPos; + } +// _timeInfo.ppqPos = __plugin->m_SncVSTplug->ppqPos; + _timeInfo.flags |= kVstPpqPosValid; + + if( __plugin->m_SncVSTplug->isPlayin ) + { + _timeInfo.flags |= kVstTransportPlaying; + } + _timeInfo.barStartPos = ( (int) ( _timeInfo.ppqPos / + ( 4 *__plugin->m_SncVSTplug->timeSigNumer + / (float) __plugin->m_SncVSTplug->timeSigDenom ) ) ) * + ( 4 * __plugin->m_SncVSTplug->timeSigNumer + / (float) __plugin->m_SncVSTplug->timeSigDenom ); + + _timeInfo.flags |= kVstBarsValid; + #ifdef LMMS_BUILD_WIN64 return (long long) &_timeInfo; #else diff --git a/src/core/song.cpp b/src/core/song.cpp index d233e5946..e1ce8ae86 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -61,6 +61,21 @@ #include "text_float.h" #include "timeline.h" +#ifdef LMMS_BUILD_WIN32 +#ifndef USE_QT_SHMEM +#define USE_QT_SHMEM +#endif +#endif + +#ifndef USE_QT_SHMEM +#include +#include +#include +#include +#include +#include +#endif + tick_t midiTime::s_ticksPerTact = DefaultTicksPerTact; @@ -88,7 +103,10 @@ song::song() : m_length( 0 ), m_trackToPlay( NULL ), m_patternToPlay( NULL ), - m_loopPattern( false ) + m_loopPattern( false ), + m_shmID( -1 ), + m_SncVSTplug( NULL ), + m_shmQtID( "/usr/bin/lmms" ) { connect( &m_tempoModel, SIGNAL( dataChanged() ), this, SLOT( setTempo() ) ); @@ -101,6 +119,59 @@ song::song() : connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateFramesPerTick() ) ); + // handle VST plugins sync + if( configManager::inst()->value( "ui", "syncvstplugins" ).toInt() ) + { + connect( engine::getMixer(), SIGNAL( sampleRateChanged() ), this, + SLOT( updateSampleRateSHM() ) ); +#ifdef USE_QT_SHMEM + if ( !m_shmQtID.create( sizeof( sncVST ) ) ) + { + fprintf(stderr, "song.cpp::m_shmQtID create SHM error: %s\n", + m_shmQtID.errorString().toStdString().c_str() ); + } + m_SncVSTplug = (sncVST *) m_shmQtID.data(); +#else + key_t key; // make the key: + if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 ) + { + perror( "song.cpp::ftok" ); + } + else + { // connect to shared memory segment + if( ( m_shmID = shmget( key, sizeof( sncVST ), + 0644 | IPC_CREAT ) ) == -1 ) + { + perror( "song.cpp::shmget" ); + } + else + { // attach segment + m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); + if( m_SncVSTplug == (sncVST *)( -1 ) ) + { + perror( "song.cpp::shmat" ); + } + } + } +#endif + // if we are connected into shared memory + if( m_SncVSTplug != NULL ) + { + m_SncVSTplug->isPlayin = m_playing | m_exporting; + m_SncVSTplug->hasSHM = true; + m_SncVSTplug->m_sampleRate = + engine::getMixer()->processingSampleRate(); + m_SncVSTplug->m_bufferSize = + engine::getMixer()->framesPerPeriod(); + m_SncVSTplug->timeSigNumer = 4; + m_SncVSTplug->timeSigDenom = 4; + } + } // end of VST plugin sync section + + if( m_SncVSTplug == NULL ) + { + m_SncVSTplug = (sncVST*) malloc( sizeof( sncVST ) ); + } connect( &m_masterVolumeModel, SIGNAL( dataChanged() ), this, SLOT( masterVolumeChanged() ) ); @@ -116,6 +187,24 @@ song::song() : song::~song() { + // detach shared memory, delete it: +#ifdef USE_QT_SHMEM + m_shmQtID.detach(); +#else + if( shmdt( m_SncVSTplug ) == -1) + { + if( m_SncVSTplug->hasSHM ) + { + perror("~song::shmdt"); + } + if( m_SncVSTplug != NULL ) + { + delete m_SncVSTplug; + m_SncVSTplug = NULL; + } + } + shmctl(m_shmID, IPC_RMID, NULL); +#endif m_playing = false; delete m_globalAutomationTrack; } @@ -150,6 +239,13 @@ void song::setTempo() engine::updateFramesPerTick(); + m_SncVSTplug->m_bpm = tempo; + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * tempo / + ( (double) m_SncVSTplug->m_sampleRate * 60 ); +#endif + emit tempoChanged( tempo ); } @@ -162,6 +258,8 @@ void song::setTimeSignature() emit timeSignatureChanged( m_oldTicksPerTact, ticksPerTact() ); emit dataChanged(); m_oldTicksPerTact = ticksPerTact(); + m_SncVSTplug->timeSigNumer = getTimeSigModel().getNumerator(); + m_SncVSTplug->timeSigDenom = getTimeSigModel().getDenominator(); } @@ -178,6 +276,7 @@ void song::doActions() timeLine * tl = m_playPos[m_playMode].m_timeLine; m_playing = false; + m_SncVSTplug->isPlayin = m_exporting; m_recording = true; if( tl != NULL ) { @@ -219,31 +318,37 @@ void song::doActions() case ActionPlaySong: m_playMode = Mode_PlaySong; m_playing = true; + m_SncVSTplug->isPlayin = true; Controller::resetFrameCounter(); break; case ActionPlayTrack: m_playMode = Mode_PlayTrack; m_playing = true; + m_SncVSTplug->isPlayin = true; break; case ActionPlayBB: m_playMode = Mode_PlayBB; m_playing = true; + m_SncVSTplug->isPlayin = true; break; case ActionPlayPattern: m_playMode = Mode_PlayPattern; m_playing = true; + m_SncVSTplug->isPlayin = true; break; case ActionPause: m_playing = false;// just set the play-flag + m_SncVSTplug->isPlayin = m_exporting; m_paused = true; break; case ActionResumeFromPause: m_playing = true;// just set the play-flag + m_SncVSTplug->isPlayin = true; m_paused = false; break; } @@ -360,8 +465,14 @@ void song::processNextBuffer() while( total_frames_played < engine::getMixer()->framesPerPeriod() ) { - f_cnt_t played_frames = engine::getMixer() - ->framesPerPeriod() - total_frames_played; + f_cnt_t played_frames = ( m_SncVSTplug->m_bufferSize = engine::getMixer() + ->framesPerPeriod() ) - total_frames_played; + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * + m_SncVSTplug->m_bpm / + ( (double) m_SncVSTplug->m_sampleRate * 60 ); +#endif float current_frame = m_playPos[m_playMode].currentFrame(); // did we play a tick? @@ -369,6 +480,14 @@ void song::processNextBuffer() { int ticks = m_playPos[m_playMode].getTicks() + (int)( current_frame / frames_per_tick ); + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (double)48 ) - + m_SncVSTplug->m_latency; +#else + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (double)48 ); +#endif + // did we play a whole tact? if( ticks >= midiTime::ticksPerTact() ) { @@ -402,18 +521,37 @@ void song::processNextBuffer() // offset ticks = ticks % ( max_tact * midiTime::ticksPerTact() ); +#ifdef VST_SNC_LATENCY + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) + / (double)48 ) + - m_SncVSTplug->m_latency; +#else + m_SncVSTplug->ppqPos = ( ( ticks + 0 ) + / (double)48 ); +#endif } } m_playPos[m_playMode].setTicks( ticks ); if( check_loop ) { + m_SncVSTplug->isCycle = true; + m_SncVSTplug->cycleStart = + ( tl->loopBegin().getTicks() ) + / (double)48; + m_SncVSTplug->cycleEnd = + ( tl->loopEnd().getTicks() ) + / (double)48; if( m_playPos[m_playMode] >= tl->loopEnd() ) { m_playPos[m_playMode].setTicks( tl->loopBegin().getTicks() ); } } + else + { + m_SncVSTplug->isCycle = false; + } current_frame = fmodf( current_frame, frames_per_tick ); m_playPos[m_playMode].setCurrentFrame( current_frame ); @@ -637,6 +775,7 @@ void song::startExport() doActions(); m_exporting = true; + m_SncVSTplug->isPlayin = true; } @@ -647,6 +786,7 @@ void song::stopExport() stop(); m_exporting = false; m_exportLoop = false; + m_SncVSTplug->isPlayin = m_playing; } @@ -1216,6 +1356,19 @@ void song::updateFramesPerTick() +void song::updateSampleRateSHM() +{ + m_SncVSTplug->m_sampleRate = engine::getMixer()->processingSampleRate(); + +#ifdef VST_SNC_LATENCY + m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * m_SncVSTplug->m_bpm / + ( (double) m_SncVSTplug->m_sampleRate * 60 ); +#endif +} + + + + void song::setModified() { if( !m_loadingProject ) diff --git a/src/gui/setup_dialog.cpp b/src/gui/setup_dialog.cpp index 26566d19b..b8f38f9bf 100644 --- a/src/gui/setup_dialog.cpp +++ b/src/gui/setup_dialog.cpp @@ -117,7 +117,9 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : m_oneInstrumentTrackWindow( configManager::inst()->value( "ui", "oneinstrumenttrackwindow" ).toInt() ), m_compactTrackButtons( configManager::inst()->value( "ui", - "compacttrackbuttons" ).toInt() ) + "compacttrackbuttons" ).toInt() ), + m_syncVSTPlugins( configManager::inst()->value( "ui", + "syncvstplugins" ).toInt() ) { setWindowIcon( embed::getIconPixmap( "setup_general" ) ); setWindowTitle( tr( "Setup LMMS" ) ); @@ -186,7 +188,7 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : tabWidget * misc_tw = new tabWidget( tr( "MISC" ), general ); - misc_tw->setFixedHeight( 156 ); + misc_tw->setFixedHeight( 174 ); ledCheckBox * enable_tooltips = new ledCheckBox( tr( "Enable tooltips" ), @@ -247,6 +249,15 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) : this, SLOT( toggleCompactTrackButtons( bool ) ) ); + ledCheckBox * syncVST = new ledCheckBox( + tr( "Sync VST plugins to host playback" ), + misc_tw ); + syncVST->move( 10, 144 ); + syncVST->setChecked( m_syncVSTPlugins ); + connect( syncVST, SIGNAL( toggled( bool ) ), + this, SLOT( toggleSyncVSTPlugins( bool ) ) ); + + gen_layout->addWidget( bufsize_tw ); gen_layout->addSpacing( 10 ); @@ -774,6 +785,8 @@ void setupDialog::accept() QString::number( m_oneInstrumentTrackWindow ) ); configManager::inst()->setValue( "ui", "compacttrackbuttons", QString::number( m_compactTrackButtons ) ); + configManager::inst()->setValue( "ui", "syncvstplugins", + QString::number( m_syncVSTPlugins ) ); configManager::inst()->setWorkingDir( m_workingDir ); configManager::inst()->setVSTDir( m_vstDir ); @@ -956,6 +969,15 @@ void setupDialog::toggleCompactTrackButtons( bool _enabled ) +void setupDialog::toggleSyncVSTPlugins( bool _enabled ) +{ + m_syncVSTPlugins = _enabled; +} + + + + + void setupDialog::toggleOneInstrumentTrackWindow( bool _enabled ) { m_oneInstrumentTrackWindow = _enabled;