Moved VST sync functionality into new VstSyncController class
First attempt to clean up the mess in the Song class by moving the VST sync functionality into its own class and just calling a few functions from it.
This commit is contained in:
196
src/core/VstSyncController.cpp
Normal file
196
src/core/VstSyncController.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* VstSyncController.cpp - manage synchronization between LMMS and VST plugins
|
||||
*
|
||||
* Copyright (c) 2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
|
||||
* Copyright (c) 2013 Mike Choi <rdavidian71/at/gmail/dot/com>
|
||||
*
|
||||
* This file is part of LMMS - 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
#include "config_mgr.h"
|
||||
#include "engine.h"
|
||||
#include "lmmsconfig.h"
|
||||
#include "Mixer.h"
|
||||
#include "VstSyncController.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
|
||||
|
||||
|
||||
VstSyncController::VstSyncController() :
|
||||
m_syncData( NULL ),
|
||||
m_shmID( -1 ),
|
||||
m_shm( "/usr/bin/lmms" )
|
||||
{
|
||||
if( configManager::inst()->value( "ui", "syncvstplugins" ).toInt() )
|
||||
{
|
||||
connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSampleRate() ) );
|
||||
|
||||
#ifdef USE_QT_SHMEM
|
||||
if ( m_shm.create( sizeof( VstSyncData ) ) )
|
||||
{
|
||||
m_syncData = (VstSyncData*) m_shm.data();
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << QString( "Failed to allocate shared memory for VST sync: %1" ).arg( m_shm.errorString() );
|
||||
}
|
||||
#else
|
||||
key_t key; // make the key:
|
||||
if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 )
|
||||
{
|
||||
qWarning( "VstSyncController: ftok() failed" );
|
||||
}
|
||||
else
|
||||
{ // connect to shared memory segment
|
||||
if( ( m_shmID = shmget( key, sizeof( VstSyncData ), 0644 | IPC_CREAT ) ) == -1 )
|
||||
{
|
||||
qWarning( "VstSyncController: shmget() failed" );
|
||||
}
|
||||
else
|
||||
{ // attach segment
|
||||
m_syncData = (VstSyncData *)shmat( m_shmID, 0, 0 );
|
||||
if( m_syncData == (VstSyncData *)( -1 ) )
|
||||
{
|
||||
qWarning( "VstSyncController: shmat() failed" );
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if( m_syncData == NULL )
|
||||
{
|
||||
m_syncData = new VstSyncData;
|
||||
m_syncData->hasSHM = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_syncData->hasSHM = true;
|
||||
}
|
||||
|
||||
m_syncData->isPlaying = false;
|
||||
m_syncData->m_bufferSize = engine::mixer()->framesPerPeriod();
|
||||
m_syncData->timeSigNumer = 4;
|
||||
m_syncData->timeSigDenom = 4;
|
||||
|
||||
updateSampleRate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
VstSyncController::~VstSyncController()
|
||||
{
|
||||
if( m_syncData->hasSHM == false )
|
||||
{
|
||||
delete m_syncData;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef USE_QT_SHMEM
|
||||
if( m_shm.data() )
|
||||
{
|
||||
// detach shared memory, delete it:
|
||||
m_shm.detach();
|
||||
}
|
||||
#else
|
||||
if( shmdt( m_syncData ) != -1 )
|
||||
{
|
||||
shmctl( m_shmID, IPC_RMID, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning( "VstSyncController: shmdt() failed" );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VstSyncController::setAbsolutePosition( int ticks )
|
||||
{
|
||||
#ifdef VST_SNC_LATENCY
|
||||
m_syncData->ppqPos = ( ( ticks + 0 ) / (float)48 ) - m_syncData->m_latency;
|
||||
#else
|
||||
m_syncData->ppqPos = ( ( ticks + 0 ) / (float)48 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VstSyncController::setTempo( int newTempo )
|
||||
{
|
||||
m_syncData->m_bpm = newTempo;
|
||||
|
||||
#ifdef VST_SNC_LATENCY
|
||||
m_syncData->m_latency = m_syncData->m_bufferSize * newTempo / ( (float) m_syncData->m_sampleRate * 60 );
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VstSyncController::startCycle( int startTick, int endTick )
|
||||
{
|
||||
m_syncData->isCycle = true;
|
||||
m_syncData->cycleStart = startTick / (float)48;
|
||||
m_syncData->cycleEnd = endTick / (float)48;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VstSyncController::update()
|
||||
{
|
||||
m_syncData->m_bufferSize = engine::mixer()->framesPerPeriod();
|
||||
|
||||
#ifdef VST_SNC_LATENCY
|
||||
m_syncData->m_latency = m_syncData->m_bufferSize * m_syncData->m_bpm / ( (float) m_syncData->m_sampleRate * 60 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VstSyncController::updateSampleRate()
|
||||
{
|
||||
m_syncData->m_sampleRate = engine::mixer()->processingSampleRate();
|
||||
|
||||
#ifdef VST_SNC_LATENCY
|
||||
m_syncData->m_latency = m_syncData->m_bufferSize * m_syncData->m_bpm / ( (float) m_syncData->m_sampleRate * 60 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include "moc_VstSyncController.cxx"
|
||||
|
||||
@@ -63,20 +63,6 @@
|
||||
#include "timeline.h"
|
||||
#include "PeakController.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;
|
||||
|
||||
@@ -108,10 +94,7 @@ song::song() :
|
||||
m_loopPattern( false ),
|
||||
m_elapsedMilliSeconds( 0 ),
|
||||
m_elapsedTicks( 0 ),
|
||||
m_elapsedTacts( 0 ),
|
||||
m_shmID( -1 ),
|
||||
m_SncVSTplug( NULL ),
|
||||
m_shmQtID( "/usr/bin/lmms" )
|
||||
m_elapsedTacts( 0 )
|
||||
{
|
||||
connect( &m_tempoModel, SIGNAL( dataChanged() ),
|
||||
this, SLOT( setTempo() ) );
|
||||
@@ -124,60 +107,6 @@ song::song() :
|
||||
connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this,
|
||||
SLOT( updateFramesPerTick() ) );
|
||||
|
||||
// handle VST plugins sync
|
||||
if( configManager::inst()->value( "ui", "syncvstplugins" ).toInt() )
|
||||
{
|
||||
connect( engine::mixer(), 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::mixer()->processingSampleRate();
|
||||
m_SncVSTplug->m_bufferSize =
|
||||
engine::mixer()->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() ) );
|
||||
/* connect( &m_masterPitchModel, SIGNAL( dataChanged() ),
|
||||
@@ -191,24 +120,6 @@ 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 )
|
||||
{
|
||||
free( m_SncVSTplug );
|
||||
m_SncVSTplug = NULL;
|
||||
}
|
||||
}
|
||||
shmctl(m_shmID, IPC_RMID, NULL);
|
||||
#endif
|
||||
m_playing = false;
|
||||
delete m_globalAutomationTrack;
|
||||
}
|
||||
@@ -243,12 +154,7 @@ void song::setTempo()
|
||||
|
||||
engine::updateFramesPerTick();
|
||||
|
||||
m_SncVSTplug->m_bpm = tempo;
|
||||
|
||||
#ifdef VST_SNC_LATENCY
|
||||
m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * tempo /
|
||||
( (float) m_SncVSTplug->m_sampleRate * 60 );
|
||||
#endif
|
||||
m_vstSyncController.setTempo( tempo );
|
||||
|
||||
emit tempoChanged( tempo );
|
||||
}
|
||||
@@ -262,8 +168,8 @@ void song::setTimeSignature()
|
||||
emit timeSignatureChanged( m_oldTicksPerTact, ticksPerTact() );
|
||||
emit dataChanged();
|
||||
m_oldTicksPerTact = ticksPerTact();
|
||||
m_SncVSTplug->timeSigNumer = getTimeSigModel().getNumerator();
|
||||
m_SncVSTplug->timeSigDenom = getTimeSigModel().getDenominator();
|
||||
|
||||
m_vstSyncController.setTimeSignature( getTimeSigModel().getNumerator(), getTimeSigModel().getDenominator() );
|
||||
}
|
||||
|
||||
|
||||
@@ -280,7 +186,7 @@ void song::savePos()
|
||||
case ActionStop:
|
||||
{
|
||||
m_playing = false;
|
||||
m_SncVSTplug->isPlayin = m_exporting;
|
||||
m_vstSyncController.setPlaybackState( m_exporting );
|
||||
m_recording = true;
|
||||
if( tl != NULL )
|
||||
{
|
||||
@@ -325,37 +231,37 @@ void song::savePos()
|
||||
case ActionPlaySong:
|
||||
m_playMode = Mode_PlaySong;
|
||||
m_playing = true;
|
||||
m_SncVSTplug->isPlayin = true;
|
||||
m_vstSyncController.setPlaybackState( true );
|
||||
Controller::resetFrameCounter();
|
||||
break;
|
||||
|
||||
case ActionPlayTrack:
|
||||
m_playMode = Mode_PlayTrack;
|
||||
m_playing = true;
|
||||
m_SncVSTplug->isPlayin = true;
|
||||
m_vstSyncController.setPlaybackState( true );
|
||||
break;
|
||||
|
||||
case ActionPlayBB:
|
||||
m_playMode = Mode_PlayBB;
|
||||
m_playing = true;
|
||||
m_SncVSTplug->isPlayin = true;
|
||||
m_vstSyncController.setPlaybackState( true );
|
||||
break;
|
||||
|
||||
case ActionPlayPattern:
|
||||
m_playMode = Mode_PlayPattern;
|
||||
m_playing = true;
|
||||
m_SncVSTplug->isPlayin = true;
|
||||
m_vstSyncController.setPlaybackState( true );
|
||||
break;
|
||||
|
||||
case ActionPause:
|
||||
m_playing = false;// just set the play-flag
|
||||
m_SncVSTplug->isPlayin = m_exporting;
|
||||
m_vstSyncController.setPlaybackState( m_exporting );
|
||||
m_paused = true;
|
||||
break;
|
||||
|
||||
case ActionResumeFromPause:
|
||||
m_playing = true;// just set the play-flag
|
||||
m_SncVSTplug->isPlayin = true;
|
||||
m_vstSyncController.setPlaybackState( true );
|
||||
m_paused = false;
|
||||
break;
|
||||
}
|
||||
@@ -472,28 +378,17 @@ void song::processNextBuffer()
|
||||
while( total_frames_played
|
||||
< engine::mixer()->framesPerPeriod() )
|
||||
{
|
||||
f_cnt_t played_frames = ( m_SncVSTplug->m_bufferSize = engine::mixer()
|
||||
->framesPerPeriod() ) - total_frames_played;
|
||||
m_vstSyncController.update();
|
||||
|
||||
#ifdef VST_SNC_LATENCY
|
||||
m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize *
|
||||
m_SncVSTplug->m_bpm /
|
||||
( (float) m_SncVSTplug->m_sampleRate * 60 );
|
||||
#endif
|
||||
f_cnt_t played_frames = engine::mixer()->framesPerPeriod() - total_frames_played;
|
||||
|
||||
float current_frame = m_playPos[m_playMode].currentFrame();
|
||||
// did we play a tick?
|
||||
if( current_frame >= frames_per_tick )
|
||||
{
|
||||
int ticks = m_playPos[m_playMode].getTicks()
|
||||
+ (int)( current_frame / frames_per_tick );
|
||||
int ticks = m_playPos[m_playMode].getTicks() + (int)( current_frame / frames_per_tick );
|
||||
|
||||
#ifdef VST_SNC_LATENCY
|
||||
m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (float)48 ) -
|
||||
m_SncVSTplug->m_latency;
|
||||
#else
|
||||
m_SncVSTplug->ppqPos = ( ( ticks + 0 ) / (float)48 );
|
||||
#endif
|
||||
m_vstSyncController.setAbsolutePosition( ticks );
|
||||
|
||||
// did we play a whole tact?
|
||||
if( ticks >= MidiTime::ticksPerTact() )
|
||||
@@ -526,39 +421,26 @@ void song::processNextBuffer()
|
||||
{
|
||||
// then start from beginning and keep
|
||||
// offset
|
||||
ticks = ticks % ( max_tact *
|
||||
MidiTime::ticksPerTact() );
|
||||
#ifdef VST_SNC_LATENCY
|
||||
m_SncVSTplug->ppqPos = ( ( ticks + 0 )
|
||||
/ (float)48 )
|
||||
- m_SncVSTplug->m_latency;
|
||||
#else
|
||||
m_SncVSTplug->ppqPos = ( ( ticks + 0 )
|
||||
/ (float)48 );
|
||||
#endif
|
||||
ticks = ticks % ( max_tact * MidiTime::ticksPerTact() );
|
||||
|
||||
m_vstSyncController.setAbsolutePosition( ticks );
|
||||
}
|
||||
}
|
||||
m_playPos[m_playMode].setTicks( ticks );
|
||||
|
||||
if( check_loop )
|
||||
{
|
||||
m_SncVSTplug->isCycle = true;
|
||||
m_SncVSTplug->cycleStart =
|
||||
( tl->loopBegin().getTicks() )
|
||||
/ (float)48;
|
||||
m_SncVSTplug->cycleEnd =
|
||||
( tl->loopEnd().getTicks() )
|
||||
/ (float)48;
|
||||
m_vstSyncController.startCycle( tl->loopBegin().getTicks(), tl->loopEnd().getTicks() );
|
||||
|
||||
if( m_playPos[m_playMode] >= tl->loopEnd() )
|
||||
{
|
||||
m_playPos[m_playMode].setTicks(
|
||||
tl->loopBegin().getTicks() );
|
||||
m_playPos[m_playMode].setTicks( tl->loopBegin().getTicks() );
|
||||
m_elapsedMilliSeconds = ((tl->loopBegin().getTicks())*60*1000/48)/getTempo();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SncVSTplug->isCycle = false;
|
||||
m_vstSyncController.stopCycle();
|
||||
}
|
||||
|
||||
current_frame = fmodf( current_frame, frames_per_tick );
|
||||
@@ -852,7 +734,8 @@ void song::startExport()
|
||||
playSong();
|
||||
|
||||
m_exporting = true;
|
||||
m_SncVSTplug->isPlayin = true;
|
||||
|
||||
m_vstSyncController.setPlaybackState( true );
|
||||
}
|
||||
|
||||
|
||||
@@ -863,7 +746,8 @@ void song::stopExport()
|
||||
stop();
|
||||
m_exporting = false;
|
||||
m_exportLoop = false;
|
||||
m_SncVSTplug->isPlayin = m_playing;
|
||||
|
||||
m_vstSyncController.setPlaybackState( m_playing );
|
||||
}
|
||||
|
||||
|
||||
@@ -1453,19 +1337,6 @@ void song::updateFramesPerTick()
|
||||
|
||||
|
||||
|
||||
void song::updateSampleRateSHM()
|
||||
{
|
||||
m_SncVSTplug->m_sampleRate = engine::mixer()->processingSampleRate();
|
||||
|
||||
#ifdef VST_SNC_LATENCY
|
||||
m_SncVSTplug->m_latency = m_SncVSTplug->m_bufferSize * m_SncVSTplug->m_bpm /
|
||||
( (float) m_SncVSTplug->m_sampleRate * 60 );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void song::setModified()
|
||||
{
|
||||
if( !m_loadingProject )
|
||||
|
||||
Reference in New Issue
Block a user