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)
This commit is contained in:
Mike Choi
2013-02-07 21:54:06 +01:00
committed by Tobias Doerffel
parent 7682bc6ef9
commit 0ef2997ece
8 changed files with 475 additions and 25 deletions

View File

@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#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 )

View File

@@ -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;