From 5a9e0bdcef2589e8ff9136f483f9147d06a275b8 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 3 Feb 2014 19:41:54 +0100 Subject: [PATCH 1/6] 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. --- include/RemotePlugin.h | 28 ++-- include/VstSyncController.h | 99 +++++++++++ include/{VST_sync_shm.h => VstSyncData.h} | 17 +- include/song.h | 9 +- plugins/vst_base/RemoteVstPlugin.cpp | 88 +++++----- src/core/VstSyncController.cpp | 196 ++++++++++++++++++++++ src/core/song.cpp | 181 +++----------------- 7 files changed, 392 insertions(+), 226 deletions(-) create mode 100644 include/VstSyncController.h rename include/{VST_sync_shm.h => VstSyncData.h} (83%) create mode 100644 src/core/VstSyncController.cpp diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index 8eb097ebd..0da674289 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -27,7 +27,7 @@ #include "export.h" #include "MidiEvent.h" -#include "VST_sync_shm.h" +#include "VstSyncData.h" #include #include @@ -813,7 +813,7 @@ public: RemotePluginClient( key_t _shm_in, key_t _shm_out ); virtual ~RemotePluginClient(); #ifdef USE_QT_SHMEM - sncVST * getQtVSTshm(); + VstSyncData * getQtVSTshm(); #endif virtual bool processMessage( const message & _m ); @@ -883,7 +883,7 @@ private: QSharedMemory m_shmObj; QSharedMemory m_shmQtID; #endif - sncVST * m_SncVSTplug; + VstSyncData * m_vstSyncData; float * m_shm; int m_inputCount; @@ -1013,7 +1013,7 @@ RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) : m_shmObj(), m_shmQtID( "/usr/bin/lmms" ), #endif - m_SncVSTplug( NULL ), + m_vstSyncData( NULL ), m_shm( NULL ), m_inputCount( 0 ), m_outputCount( 0 ), @@ -1023,9 +1023,9 @@ RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) : #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; + m_vstSyncData = (VstSyncData *) m_shmQtID.data(); + m_bufferSize = m_vstSyncData->m_bufferSize; + m_sampleRate = m_vstSyncData->m_sampleRate; return; } #else @@ -1044,18 +1044,18 @@ RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) : } else { // attach segment - m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); - if( m_SncVSTplug == (sncVST *)( -1 ) ) + m_vstSyncData = (VstSyncData *)shmat(m_shmID, 0, 0); + if( m_vstSyncData == (VstSyncData *)( -1 ) ) { perror( "RemotePluginClient::shmat" ); } else { - m_bufferSize = m_SncVSTplug->m_bufferSize; - m_sampleRate = m_SncVSTplug->m_sampleRate; + m_bufferSize = m_vstSyncData->m_bufferSize; + m_sampleRate = m_vstSyncData->m_sampleRate; // detach segment - if( shmdt(m_SncVSTplug) == -1 ) + if( shmdt(m_vstSyncData) == -1 ) { perror("RemotePluginClient::shmdt"); } @@ -1087,9 +1087,9 @@ RemotePluginClient::~RemotePluginClient() #ifdef USE_QT_SHMEM -sncVST * RemotePluginClient::getQtVSTshm() +VstSyncData * RemotePluginClient::getQtVSTshm() { - return m_SncVSTplug; + return m_vstSyncData; } #endif diff --git a/include/VstSyncController.h b/include/VstSyncController.h new file mode 100644 index 000000000..91c39ea6e --- /dev/null +++ b/include/VstSyncController.h @@ -0,0 +1,99 @@ +/* + * VstSyncController.h - type declarations needed for VST to lmms host sync + * + * Copyright (c) 2014 Tobias Doerffel + * Copyright (c) 2013 Mike Choi + * + * 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_CONTROLLER_H +#define VST_SYNC_CONTROLLER_H + +#include +#include + +#include "VstSyncData.h" + + +class VstSyncController : public QObject +{ + Q_OBJECT +public: + VstSyncController(); + ~VstSyncController(); + + void setAbsolutePosition( int ticks ); + + void setPlaybackState( bool enabled ) + { + m_syncData->isPlaying = enabled; + } + + void setTempo( int newTempo ); + + void setTimeSignature( int num, int denom ) + { + m_syncData->timeSigNumer = num; + m_syncData->timeSigDenom = denom; + } + + void startCycle( int startTick, int endTick ); + + void stopCycle() + { + m_syncData->isCycle = false; + } + + void update(); + + +private slots: + void updateSampleRate(); + + +private: + struct VstSyncData + { + bool isPlaying; + float ppqPos; + int timeSigNumer; + int timeSigDenom; + bool isCycle; + bool hasSHM; + float cycleStart; + float cycleEnd; + int m_bufferSize; + int m_sampleRate; + int m_bpm; + +#ifdef VST_SNC_LATENCY + float m_latency; +#endif + } ; + + VstSyncData* m_syncData; + + int m_shmID; + + QSharedMemory m_shm; + +}; + +#endif diff --git a/include/VST_sync_shm.h b/include/VstSyncData.h similarity index 83% rename from include/VST_sync_shm.h rename to include/VstSyncData.h index 7b6a813d5..9daa3380f 100644 --- a/include/VST_sync_shm.h +++ b/include/VstSyncData.h @@ -1,8 +1,9 @@ /* - * VST_sync_shm.h - type declarations needed for VST to lmms host sync + * VstSyncData.h - type declarations needed for VST to lmms host sync + * + * Copyright (c) 2014 Tobias Doerffel + * Copyright (c) 2013 Mike Choi * - * 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 @@ -22,8 +23,8 @@ * */ -#ifndef _VST_SYNC_SHM_H -#define _VST_SYNC_SHM_H +#ifndef VST_SYNC_DATA_H +#define VST_SYNC_DATA_H // VST sync frequency (in ms), how often will be VST plugin synced // keep it power of two if possible (not used by now) @@ -36,9 +37,11 @@ #define VST_SNC_SHM_KEY_FILE "/dev/null" //#define VST_SNC_SHM_RND_KEY 3561653564469 -struct sncVST + + +struct VstSyncData { - bool isPlayin; + bool isPlaying; float ppqPos; int timeSigNumer; int timeSigDenom; diff --git a/include/song.h b/include/song.h index 5900c8c93..c2ba37fbf 100644 --- a/include/song.h +++ b/include/song.h @@ -32,7 +32,7 @@ #include "AutomatableModel.h" #include "Controller.h" #include "MeterModel.h" -#include "VST_sync_shm.h" +#include "VstSyncController.h" class AutomationTrack; class pattern; @@ -296,8 +296,6 @@ private slots: void updateFramesPerTick(); - void updateSampleRateSHM(); - private: @@ -368,9 +366,8 @@ private: } ; QVector m_actions; - int m_shmID; - sncVST * m_SncVSTplug; - QSharedMemory m_shmQtID; + VstSyncController m_vstSyncController; + friend class engine; friend class songEditor; diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 559ae889b..1c0c66c6e 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -92,7 +92,7 @@ struct ERect #include "Midi.h" #include "communication.h" -#include "VST_sync_shm.h" +#include "VstSyncData.h" #ifdef LMMS_BUILD_WIN32 #define USE_QT_SHMEM @@ -325,7 +325,7 @@ private: in * m_in; int m_shmID; - sncVST * m_SncVSTplug; + VstSyncData* m_vstSyncData; } ; @@ -351,7 +351,7 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : m_currentProgram( -1 ), m_in( NULL ), m_shmID( -1 ), - m_SncVSTplug( NULL ) + m_vstSyncData( NULL ) { pthread_mutex_init( &m_pluginLock, NULL ); @@ -372,29 +372,29 @@ RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) : } else { // attach segment - m_SncVSTplug = (sncVST *)shmat(m_shmID, 0, 0); - if( m_SncVSTplug == (sncVST *)( -1 ) ) + m_vstSyncData = (VstSyncData *)shmat(m_shmID, 0, 0); + if( m_vstSyncData == (VstSyncData *)( -1 ) ) { perror( "RemoteVstPlugin.cpp::shmat" ); } } } #else - m_SncVSTplug = RemotePluginClient::getQtVSTshm(); + m_vstSyncData = RemotePluginClient::getQtVSTshm(); #endif - if( m_SncVSTplug == NULL ) + if( m_vstSyncData == 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_vstSyncData = (VstSyncData*) malloc( sizeof( VstSyncData ) ); + m_vstSyncData->isPlaying = true; + m_vstSyncData->timeSigNumer = 4; + m_vstSyncData->timeSigDenom = 4; + m_vstSyncData->ppqPos = 0; + m_vstSyncData->isCycle = false; + m_vstSyncData->hasSHM = false; + m_vstSyncData->m_sampleRate = sampleRate(); } m_in = ( in* ) new char[ sizeof( in ) ]; @@ -420,16 +420,16 @@ RemoteVstPlugin::~RemoteVstPlugin() { #ifndef USE_QT_SHMEM // detach shared memory segment - if( shmdt( m_SncVSTplug ) == -1) + if( shmdt( m_vstSyncData ) == -1) { - if( __plugin->m_SncVSTplug->hasSHM ) + if( __plugin->m_vstSyncData->hasSHM ) { perror( "~RemoteVstPlugin::shmdt" ); } - if( m_SncVSTplug != NULL ) + if( m_vstSyncData != NULL ) { - delete m_SncVSTplug; - m_SncVSTplug = NULL; + delete m_vstSyncData; + m_vstSyncData = NULL; } } #endif @@ -573,7 +573,7 @@ void RemoteVstPlugin::init( const std::string & _plugin_file ) updateInOutCount(); // some plugins have to set samplerate during init - if( m_SncVSTplug->hasSHM ) + if( m_vstSyncData->hasSHM ) { updateSampleRate(); } @@ -1444,58 +1444,58 @@ intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode, // items may require extensive conversions // Shared memory was initialised? - see song.cpp - //assert( __plugin->m_SncVSTplug != NULL ); + //assert( __plugin->m_vstSyncData != NULL ); memset( &_timeInfo, 0, sizeof( _timeInfo ) ); _timeInfo.samplePos = __plugin->m_currentSamplePos; - _timeInfo.sampleRate = __plugin->m_SncVSTplug->hasSHM ? - __plugin->m_SncVSTplug->m_sampleRate : + _timeInfo.sampleRate = __plugin->m_vstSyncData->hasSHM ? + __plugin->m_vstSyncData->m_sampleRate : __plugin->sampleRate(); _timeInfo.flags = 0; - _timeInfo.tempo = __plugin->m_SncVSTplug->hasSHM ? - __plugin->m_SncVSTplug->m_bpm : + _timeInfo.tempo = __plugin->m_vstSyncData->hasSHM ? + __plugin->m_vstSyncData->m_bpm : __plugin->m_bpm; - _timeInfo.timeSigNumerator = __plugin->m_SncVSTplug->timeSigNumer; - _timeInfo.timeSigDenominator = __plugin->m_SncVSTplug->timeSigDenom; + _timeInfo.timeSigNumerator = __plugin->m_vstSyncData->timeSigNumer; + _timeInfo.timeSigDenominator = __plugin->m_vstSyncData->timeSigDenom; _timeInfo.flags |= kVstTempoValid; _timeInfo.flags |= kVstTimeSigValid; - if( __plugin->m_SncVSTplug->isCycle ) + if( __plugin->m_vstSyncData->isCycle ) { - _timeInfo.cycleStartPos = __plugin->m_SncVSTplug->cycleStart; - _timeInfo.cycleEndPos = __plugin->m_SncVSTplug->cycleEnd; + _timeInfo.cycleStartPos = __plugin->m_vstSyncData->cycleStart; + _timeInfo.cycleEndPos = __plugin->m_vstSyncData->cycleEnd; _timeInfo.flags |= kVstCyclePosValid; _timeInfo.flags |= kVstTransportCycleActive; } - if( __plugin->m_SncVSTplug->ppqPos != + if( __plugin->m_vstSyncData->ppqPos != __plugin->m_in->m_Timestamp ) { - _timeInfo.ppqPos = __plugin->m_SncVSTplug->ppqPos; + _timeInfo.ppqPos = __plugin->m_vstSyncData->ppqPos; _timeInfo.flags |= kVstTransportChanged; - __plugin->m_in->lastppqPos = __plugin->m_SncVSTplug->ppqPos; - __plugin->m_in->m_Timestamp = __plugin->m_SncVSTplug->ppqPos; + __plugin->m_in->lastppqPos = __plugin->m_vstSyncData->ppqPos; + __plugin->m_in->m_Timestamp = __plugin->m_vstSyncData->ppqPos; } - else if( __plugin->m_SncVSTplug->isPlayin ) + else if( __plugin->m_vstSyncData->isPlaying ) { __plugin->m_in->lastppqPos += ( - __plugin->m_SncVSTplug->hasSHM ? - __plugin->m_SncVSTplug->m_bpm : + __plugin->m_vstSyncData->hasSHM ? + __plugin->m_vstSyncData->m_bpm : __plugin->m_bpm ) / (float)10340; _timeInfo.ppqPos = __plugin->m_in->lastppqPos; } -// _timeInfo.ppqPos = __plugin->m_SncVSTplug->ppqPos; +// _timeInfo.ppqPos = __plugin->m_vstSyncData->ppqPos; _timeInfo.flags |= kVstPpqPosValid; - if( __plugin->m_SncVSTplug->isPlayin ) + if( __plugin->m_vstSyncData->isPlaying ) { _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 ); + ( 4 *__plugin->m_vstSyncData->timeSigNumer + / (float) __plugin->m_vstSyncData->timeSigDenom ) ) ) * + ( 4 * __plugin->m_vstSyncData->timeSigNumer + / (float) __plugin->m_vstSyncData->timeSigDenom ); _timeInfo.flags |= kVstBarsValid; diff --git a/src/core/VstSyncController.cpp b/src/core/VstSyncController.cpp new file mode 100644 index 000000000..93243b197 --- /dev/null +++ b/src/core/VstSyncController.cpp @@ -0,0 +1,196 @@ +/* + * VstSyncController.cpp - manage synchronization between LMMS and VST plugins + * + * Copyright (c) 2014 Tobias Doerffel + * Copyright (c) 2013 Mike Choi + * + * 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 + +#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 +#include +#include +#include +#include +#include +#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" + diff --git a/src/core/song.cpp b/src/core/song.cpp index b1a3763e5..ebbb0d8f3 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -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 -#include -#include -#include -#include -#include -#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 ) From fd971ee3a2662939c6d6ad921c02504d7bfdf1c0 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 3 Feb 2014 20:05:51 +0100 Subject: [PATCH 2/6] VstSyncController: print a warning if VST sync support is not enabled Users might wonder why VST sync does not work as they didn't notice the option in the configuration dialog. --- src/core/VstSyncController.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/VstSyncController.cpp b/src/core/VstSyncController.cpp index 93243b197..7d7177c82 100644 --- a/src/core/VstSyncController.cpp +++ b/src/core/VstSyncController.cpp @@ -88,6 +88,10 @@ VstSyncController::VstSyncController() : } #endif } + else + { + qWarning( "VST sync support disabled in your configuration" ); + } if( m_syncData == NULL ) { From e23d5838e79eb7c591f5480fa022f73120ce3f6f Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 3 Feb 2014 20:11:18 +0100 Subject: [PATCH 3/6] Song: removed old action array As of commit c60e7ba8d1a589e66f4bb9ef4c09726b1d6c594f we no longer queue playback actions and process them later, therefore remove old remains. --- include/song.h | 12 ----- src/core/song.cpp | 112 ---------------------------------------------- 2 files changed, 124 deletions(-) diff --git a/include/song.h b/include/song.h index c2ba37fbf..2b85a6ab3 100644 --- a/include/song.h +++ b/include/song.h @@ -354,18 +354,6 @@ private: tick_t m_elapsedTicks; tact_t m_elapsedTacts; - enum Actions - { - ActionStop, - ActionPlaySong, - ActionPlayTrack, - ActionPlayBB, - ActionPlayPattern, - ActionPause, - ActionResumeFromPause - } ; - QVector m_actions; - VstSyncController m_vstSyncController; diff --git a/src/core/song.cpp b/src/core/song.cpp index ebbb0d8f3..241f301a1 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -179,118 +179,6 @@ void song::savePos() { timeLine * tl = m_playPos[m_playMode].m_timeLine; - while( !m_actions.empty() ) - { - switch( m_actions.front() ) - { - case ActionStop: - { - m_playing = false; - m_vstSyncController.setPlaybackState( m_exporting ); - m_recording = true; - if( tl != NULL ) - { - - switch( tl->behaviourAtStop() ) - { - case timeLine::BackToZero: - m_playPos[m_playMode].setTicks( 0 ); - m_elapsedMilliSeconds = 0; - break; - - case timeLine::BackToStart: - if( tl->savedPos() >= 0 ) - { - m_playPos[m_playMode].setTicks( - tl->savedPos().getTicks() ); - m_elapsedMilliSeconds = (((tl->savedPos().getTicks())*60*1000/48)/getTempo()); - tl->savePos( -1 ); - } - break; - - case timeLine::KeepStopPosition: - default: - break; - } - - } - else - { - m_playPos[m_playMode].setTicks( 0 ); - m_elapsedMilliSeconds = 0; - } - - m_playPos[m_playMode].setCurrentFrame( 0 ); - - // remove all note-play-handles that are active - engine::mixer()->clear(); - - break; - } - - case ActionPlaySong: - m_playMode = Mode_PlaySong; - m_playing = true; - m_vstSyncController.setPlaybackState( true ); - Controller::resetFrameCounter(); - break; - - case ActionPlayTrack: - m_playMode = Mode_PlayTrack; - m_playing = true; - m_vstSyncController.setPlaybackState( true ); - break; - - case ActionPlayBB: - m_playMode = Mode_PlayBB; - m_playing = true; - m_vstSyncController.setPlaybackState( true ); - break; - - case ActionPlayPattern: - m_playMode = Mode_PlayPattern; - m_playing = true; - m_vstSyncController.setPlaybackState( true ); - break; - - case ActionPause: - m_playing = false;// just set the play-flag - m_vstSyncController.setPlaybackState( m_exporting ); - m_paused = true; - break; - - case ActionResumeFromPause: - m_playing = true;// just set the play-flag - m_vstSyncController.setPlaybackState( true ); - m_paused = false; - break; - } - - // a second switch for saving pos when starting to play - // anything (need pos for restoring it later in certain - // timeline-modes) - switch( m_actions.front() ) - { - case ActionPlaySong: - case ActionPlayTrack: - case ActionPlayBB: - case ActionPlayPattern: - { - if( tl != NULL ) - { - tl->savePos( m_playPos[m_playMode] ); - } - break; - } - - // keep GCC happy... - default: - break; - } - - m_actions.erase( m_actions.begin() ); - } - if( tl != NULL ) { tl->savePos( m_playPos[m_playMode] ); From 8da3c78f62996aeef875723f66f86c00d551f307 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Mon, 3 Feb 2014 20:13:36 +0100 Subject: [PATCH 4/6] Song: update VstSyncController in new playback control methods As of commit c60e7ba8d1a589e66f4bb9ef4c09726b1d6c594f the whole playback control has been revised. As this was an old patch, it didn't take care of the VST sync functionality. This is fixed now for the most part. Closes #184. --- src/core/song.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/core/song.cpp b/src/core/song.cpp index 241f301a1..f0eec0026 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -420,8 +420,12 @@ void song::playSong() m_playing = true; m_paused = false; + m_vstSyncController.setPlaybackState( true ); + savePos(); - if(QApplication::type() != QApplication::Tty) { + + if( QApplication::type() != QApplication::Tty ) + { engine::updatePlayPauseIcons(); } } @@ -459,6 +463,8 @@ void song::playTrack( track * _trackToPlay ) m_playing = true; m_paused = false; + m_vstSyncController.setPlaybackState( true ); + savePos(); engine::updatePlayPauseIcons(); @@ -478,6 +484,8 @@ void song::playBB() m_playing = true; m_paused = false; + m_vstSyncController.setPlaybackState( true ); + savePos(); engine::updatePlayPauseIcons(); @@ -556,6 +564,8 @@ void song::togglePause() m_paused = true; } + m_vstSyncController.setPlaybackState( m_playing ); + engine::updatePlayPauseIcons(); } @@ -582,8 +592,7 @@ void song::stop() case timeLine::BackToStart: if( tl->savedPos() >= 0 ) { - m_playPos[m_playMode].setTicks( - tl->savedPos().getTicks() ); + m_playPos[m_playMode].setTicks( tl->savedPos().getTicks() ); m_elapsedMilliSeconds = (((tl->savedPos().getTicks())*60*1000/48)/getTempo()); tl->savePos( -1 ); } @@ -602,6 +611,9 @@ void song::stop() m_playPos[m_playMode].setCurrentFrame( 0 ); + m_vstSyncController.setPlaybackState( m_exporting ); + m_vstSyncController.setAbsolutePosition( m_playPos[m_playMode].getTicks() ); + // remove all note-play-handles that are active engine::mixer()->clear(); From f333ce2e656648ebe9fbea47913ad67819216a63 Mon Sep 17 00:00:00 2001 From: Vesa Date: Mon, 3 Feb 2014 21:47:51 +0200 Subject: [PATCH 5/6] Bill's setup icons --- data/themes/default/setup_audio.png | Bin 3388 -> 3838 bytes data/themes/default/setup_directories.png | Bin 2064 -> 2411 bytes data/themes/default/setup_general.png | Bin 3074 -> 3673 bytes data/themes/default/setup_midi.png | Bin 2233 -> 1910 bytes data/themes/default/setup_performance.png | Bin 2141 -> 1893 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/data/themes/default/setup_audio.png b/data/themes/default/setup_audio.png index a297ab1e3f5029e1d410e987350b293426dc0036..c928c72ef7c18caabff27440ff70361696ebb46f 100644 GIT binary patch literal 3838 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 AE&u=k delta 3385 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 delta 2062 zcmV+p2=Vvp5|9v(8Gi%-008|9F$@3z00Lr5M??VshmXv^000NcNklgF*FW~%Wj6_OjNNzU-G6<5v-ABtzvp?r&u>@e z5nnSiW^J9mu&d#I-;*Cr{ufEHyQjXOp4)RW>i+}ar_uUd;|CQVt~p_v9Toe%1AZQ3 z-jzG5<99KnR);Lt(D_~|+-1?j;6-N(!t8(FQJrz4gWqKq)LHwS9y|qYf*Nw|E zXN%MON>mO>fPbSxBxUA)R~_yNF#NgyS39p1ZrT0Ra&z!oQ{}nyA77Sm{_PWLJHik6 zy)8#NEwUs%G~hsg%go2fO3(StkOLFHKJ@;c=^N8WYEowO&hJtB_v30C0we_Bs`khk zKO^d)0NXUimEEEwR2b>5%1RmjT=wo^CC?vDe>1E8Z-1ZpS`k3th9qvE(B)Z3jmrKr$}t+F8Qi=CUIY z5i7yn093oqm&k6AntVn2t>zy@Rw4rMufd7aOc8Mk1R7A*!s8F7%EsmL7P}+>Kj|7^-&4PJZ^=_%(Vx!egWZSY+N+|=PhM>72k z-Lv(1rf1en%`nHZA*SbszezDqy1 z^ZWKO3!p2T6Eb!s4nPh8;$BSYs=^(Sf*TYwDXcw37(xcT)Z#(~2;6vihLMT|1$|kQ zWPaP!1xW_@I640Jagr}0{{M24i3EBoSdY6+ssfs6Rz`^vFB2FaGAO_>20o$bkAEb? zJF3K0X+{}Gm_#dI@-$KHEEm{;3JEvW+{S|g4={{pk;BG_pF+;oIh#R-o&!5iH{ek^ zC2%#7h7lDq5&|DSy!i0q!G!}WwUkkbR}mb*3rJxG6cqC(XU$6675wr7*|4IEUSN-i zGJ#uRhV$XUjRQM2tk|((rx~YGz<-|n5^aS1MOJyr*EAK-0`lZZmRy~(?YjUisgxjf zm>?N&VpW`A;YAD)^0!#zE!|YNojUv=8&<|^K*m5EozN*=A?V9_Wkg3#AqfdTcW@yC zar_ozgnT_K#D(|jwo!#2K(0RaP1$wPf)5D|2I7gsfEpdWIElJli>d$@R)73R==c?u zh#s@ChGU;pt@Yo)r#N~1;vqv{`_bObpH`Prj}uT6Lp+^%k{j5Sp13=rCHz=%BB5ax z-695yhxHVGQTehc!*h2QIjP-#e9p!@EoddFrr0J$0aUOK`R-UxQ31>qEG=65@5xN%xE%0 z(2v+C-5SMA1)=!ed7NeB5jV75UZsMetW~?~5Lk8nQ@&b^#&JyqSSDOlHQmow; zu=}zH>W+zP%R!JY|*pcI?S5n7h8T5J#Jz znTaG)Ob<+qLl<_cNeEVPqM&A@s0bSCUH|}3bydMzTSk2NyI0Q@3Rmz@1)vXBsu@dc zC^>#4w5;PFUsV3lcMUJx9{}K`@!&h3PuxFe?S+FrFWOK&hJSFD%SfTIA)#j*dr#Lb z6=k^LK>+}sTa`sS4(0Bjn_pUlp1>~<6~oy}IVkOF0RF@Uc8+I{Vn#t&(@zx;#$)Ax95FK@mw?%f4TN{enbuc^C-Zyy~1 s;BKiXf8)%^Q^PAaQ{Ay|+rRz?ay|#`VMo0u00000Ne4wvM6N<$f@c-ht^fc4 diff --git a/data/themes/default/setup_general.png b/data/themes/default/setup_general.png index 0795ef56e74a8f5bbab82c0ee236fc2711c0410c..43ae1b19732728202982034f0a5b4e0d13e6fd22 100644 GIT binary patch delta 3672 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 KMNUMnLSTYIyXmh0 diff --git a/data/themes/default/setup_midi.png b/data/themes/default/setup_midi.png index 3bfd3fe75d759455f96c56f0902e6802c92d9521..80cb45e01dbb79927fb5d4dd23d21e4a8f66a3da 100644 GIT binary patch delta 1895 zcmV-t2blP|5%vy{B!2{RLP=Bz2nYy#2xN!=000SaNLh0L01ejw01ejxLMWSf0000P zbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$W^GQTORA_EsZyk&^rEUlMSl!P2#E(Ss?>KtJn#Ub zNPVCTQYDZ;pz<;V-Y8ROGwJjiw`rO-&Bbx-xOL99y?IEy8#f(kfM`sm{E{XAYoE2x z`uEy4Q98^lp zuAYBQ2IAdYKlj-ug5*KC7u8vb@vuyw?Du%OaSiO9A!LpZ+}hj(s3BSe~*dr6s2Nu-R%JJ zHxeQg41I>GRK6ku{sz0-vMfqPNiMs9zrn^Z43-MdkQ&!Lo}zQIOHNoGC`KwX^=DP@&_+(L$y zwsy+pGJgvT89lyU_6qD^pc!ZtLX20oQPmcT$9wi~ip9cGI?Sh+&x+4ikCYC%h0O9w zc)dvJRDA;BMrNK)k)E9)6mDF1d*u_4C&HFx?OPplRR%1}{pGH%sE|^U%`PAe1MkX; z=ktpQ!@zBM)~rmVrH#f&3qlBl5PE#Q>=kT^Zhv-%n<9s&9#8&?l`>T&!0W5)Gfjh{ zblALdkO7L)p>tD=&P_4RYXP_0h2QUggXOp-tP&uE;FF&^u;oE&jHgehw9}2z5t<^+ zw6wHRDwnzT&wtRfwGV&29{|%dDV0h9%*;$PGyRlpeMtZw+)p8dAR5~Y!07weX>Dt# zsedW55|I1F{?BE(d*?Pg26kc?22Y+mW+5|A@76vYOIv#f{)UEDpiu}hrIdOhptE!H zo|cx@=K1t2-~QG&DHIA6i$#tcIl_Vc`xqJ;B9%&U`H#Qn8*jhOz`y`fN+OX6nauL} zIDh^;_s8z?^{;&ufU{@M0%(5h*fDnR+JDKZQ>Os1EDHd~akz5j3V$1Zmmhrpd&FWf zE?v6B#fumD@sECp%jF`U&-1gN|AMa0uJE<1?|lOJZB+&=uX&;=(j>fYlSE>9`;3f? zP%4!;bm$PtWRi)A2>=crJV<9}CkqP;gu`K-f%^J-Y}+Q8T+Tos5I{<)c_wooX zJ~uZxIsrp2a!lb3y8&H`lV7zEX%4A;Pd&a1XRw6X__=N zG?2^XUMaxka*;?Rw18kRNIV|r`t|Gj`8~ULGdcN)`UZas%fAQ90)oNj{(o>|Q>$&; z*tV@5Zr!@2SKl;ECMG6`$K#rpN+nz_m*z`LOH~3))2tF;+cx=pUUQerRW;tz(?cqi z(tL1mkkQdm&3k)$F$_UxM`sAwr_X@TXPpWK14g8&NlyxJ;=~Cp0O0uX<8*g-Ywq!Q zbN~QDLqkNP(JJ2F-mdx4qkl*7`~8~xd_GF0lIAB*o+O*iYJTR-873zuHE(WiCKwFh z_uFBw*So(^DE!(5;BuQg=hO3i>CHEF+5rw9K1?VS(tY@W0|#hrZKYf;YXM3r?Xzpw zE_!-;bZOkSZ5#dl{VXjlw=T(Kl4vxl=c}u$(^&)<7#IN1^9>FTl7C1fs$$ySx1IN{ zUd6WUc%e{mxr7iT65VZ%ln8;YG)gIyQsiDYhElC$!4=!^!Dw23=jX6Kq%ZbJw4qh zlu`r&fv@!S_5E^wet+I``t)g?vV1A zKULj{g%Cj?%uBbP|5MB>rP8mu)YmAU{|uG5*9dss@Y?^ewIQI^*bq=_YzU||HU!if h8v<&L4FR>rzX7@cWIQp)rHKFl002ovPDHLkV1h6-pXmSq delta 2221 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 Q0RR9107*qoM6N<$g13c%p#T5? delta 2128 zcmV-W2(S0$4&4xtB!32COGiWi{{a60|De66lK=n+5=lfsRA}DKnQ3fP*BOSNbM9>3 zH@pC$4VwaqnwF|<8kML@6A{r!RcWJCimFIe+NO!RNKyHb9~DuX5LBt5pt?yUhyo@d zfdB>C1Ve}mjj=J9#cT$z*cfBH&gz-DbI<9IJGN(RBk-7U@_$KJbLK9i_c`bNzH{yw z2h)h)-)3caoZ?vxnYX#sDWGt|Sz$otED#RJEJ2{Mg4bJ4lxN{-16b4)ATKnUjz&>Co?Er>hJVx$U z+;|GWaD(9*JAdRzd*r;+ZcFFHdvfjpt)Y4oDikaLTz9HrS^y50=Z8oGtZ2enJ;|Lq z;rzJk&MXiT+N$96ewU&VzAkvE6nCMaA@k5V@*U8u1+0c6y z&K>{(|9@$VRes(TJ3f`(PZ_}DW+es^fS4C_kzEE81tx)LP;m>P!JPw9A6(c2{k_}> zsjfB0!aITX_*3Zvcy(2_G#p|@Ts~L<+X^z#J|cNUMh{_ENQVcyYvA%J5U}-Pba&IB zb#^Mrn=-&}9?h614Y71AAx*ISV8~>l<1ICkxPQ(}2=_wcAqWL&_o>!D{cm_5aDA%1 zpAtY;hFC5P3T#1fOPGV;^w>5cZY8b9Y?TNm1YAD?S6hi`%nkLC_o5aTfN)CHaHIsGB3Tk`t2vd-@xvEL2Sq# z1rmCmTZ{$pL0Wxcx8{!rc@drJ{&{zpKo9GIt&JJ(V zk~Td6re+{xymj&q;B$VIoR5{*j@wR6U_`BfzL86Mt%ZEeTy%9ZS|4$h}N`I`R z)g(ZX5f_?hD-D*Rj@WQ`CZ*=<9ansTn z%>ydwZ!pX>B4vQg4EfBhC$-4{ux-Am0E(aD`F}_Io;&gB zu2bcEgID4sJ97Xx_RP*Uoct(hQAa|JpEMT$;e`1wLDmwC|32?*sSlL`&KU>r6z4CN z+3x!>a_yZ3nG3!%cIo6+$t+;bFTpbx>iz&%dQdSfnP#K}5bO-*fA^o-bqs9x3-W1HS|EuK#@gMq~nIsgFr zz9kF4Ce+QMB>xKmIe!m>yA`U>L$5`DNcFwABk=c{cC!+D^XjgMpDq9nch+)rrz2@b z_AEu`4IpY8pu;3+>cHx~!SWraBmV+gNpaPg763yIKZYJ2dmD4lLlq0e$p&bO5;S$- zg&l!6D(b?!fN3om?mEEnouyfZ5&EvZ=PAHZfGYGr;|0VelYe20&}#=nn=0zUyVE6p zS^!HHWj!QB)Pn@12MY5L9nFYSVIr0euRRdj{Q7~=cAzny^n9rRJej#a0I&V$T~0o9 zwIS*v#1!WG!^2x%{Ww?woJ*(plmY7Y%yt@%@b{5GMi%tngp*NBi)eY(@cS?C4s3ahXq5yvg&PC9s z)?2EFKl;@>zPBvJ$(a(LGJsR)Zxf8dB@-3aYX9j-<%?VVn=C~=5M%};B>-zkx#+PV zqB(RiTJ_97z2#w3pTPcqOMmGBxMG4^aOgs$>Sr5=Hc5mc`WW_Z#f*wi89)p4$yW3D zQ|pH|_(Hk{dsl77#itBV+iV{B_1oSWUr5)H!k+%!jQ<07vVe`?(`O?90000 Date: Mon, 3 Feb 2014 20:56:23 +0100 Subject: [PATCH 6/6] MidiPort: do not change key of MIDI note events There's no need to transpose MIDI events inside a MidiPort instance. Any correction to the outer world should (if at all) happen in the according MIDI backends. Closes #242. --- src/core/midi/MidiPort.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/core/midi/MidiPort.cpp b/src/core/midi/MidiPort.cpp index ffa2c97dd..0dd0ae760 100644 --- a/src/core/midi/MidiPort.cpp +++ b/src/core/midi/MidiPort.cpp @@ -123,7 +123,6 @@ void MidiPort::processInEvent( const MidiEvent& event, const MidiTime& time ) event.type() == MidiNoteOff || event.type() == MidiKeyPressure ) { - inEvent.setKey( inEvent.key() + KeysPerOctave ); if( inEvent.key() < 0 || inEvent.key() >= NumKeys ) { return; @@ -149,14 +148,6 @@ void MidiPort::processOutEvent( const MidiEvent& event, const MidiTime& time ) { MidiEvent outEvent = event; - if( ( event.type() == MidiNoteOn || event.type() == MidiNoteOff ) && - fixedOutputNote() >= 0 ) - { - // Convert MIDI note number (from spinbox) -> LMMS note number - // that will be converted back when outputted. - outEvent.setKey( fixedOutputNote() - KeysPerOctave ); - } - if( fixedOutputVelocity() >= 0 && event.velocity() > 0 && ( event.type() == MidiNoteOn || event.type() == MidiKeyPressure ) ) {