From 0ef2997ecef5277dd3c4fff3cbe61100ae8a7e25 Mon Sep 17 00:00:00 2001 From: Mike Choi Date: Thu, 7 Feb 2013 21:54:06 +0100 Subject: [PATCH] VST to host sync This patch should bring VST to host synchronization for LMMS. (e.g. for plugins like dBlue Glitch, TAL Filters). Synchronization is done via shared memory, missing song time positions are reccalculated and added to PPQ position sync values (SHM - common input interface for sync of all VST plugins) --- include/RemotePlugin.h | 65 ++++++++++- include/VST_sync_shm.h | 58 ++++++++++ include/aeffectx.h | 20 +++- include/setup_dialog.h | 2 + include/song.h | 8 +- plugins/vst_base/RemoteVstPlugin.cpp | 162 ++++++++++++++++++++++++--- src/core/song.cpp | 159 +++++++++++++++++++++++++- src/gui/setup_dialog.cpp | 26 ++++- 8 files changed, 475 insertions(+), 25 deletions(-) create mode 100644 include/VST_sync_shm.h 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;