Files
lmms/include/RemotePlugin.h

1452 lines
25 KiB
C++

/*
* RemotePlugin.h - base class providing RPC like mechanisms
*
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of LMMS - https://lmms.io
*
* 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 REMOTE_PLUGIN_H
#define REMOTE_PLUGIN_H
#include "export.h"
#include "MidiEvent.h"
#include "VstSyncData.h"
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cassert>
#if !(defined(LMMS_HAVE_SYS_IPC_H) && defined(LMMS_HAVE_SEMAPHORE_H))
#define SYNC_WITH_SHM_FIFO
#define USE_QT_SEMAPHORES
#ifdef LMMS_HAVE_PROCESS_H
#include <process.h>
#endif
#include <QtCore/QtGlobal>
#include <QtCore/QSystemSemaphore>
#endif
#ifdef LMMS_HAVE_SYS_SHM_H
#include <sys/shm.h>
#ifdef LMMS_HAVE_UNISTD_H
#include <unistd.h>
#endif
#else
#define USE_QT_SHMEM
#include <QtCore/QtGlobal>
#include <QtCore/QSharedMemory>
#if !defined(LMMS_HAVE_SYS_TYPES_H) || defined(LMMS_BUILD_WIN32)
typedef int32_t key_t;
#endif
#endif
#ifdef LMMS_HAVE_LOCALE_H
#include <locale.h>
#endif
#ifdef LMMS_HAVE_PTHREAD_H
#include <pthread.h>
#endif
#ifdef BUILD_REMOTE_PLUGIN_CLIENT
#undef EXPORT
#define EXPORT
#define COMPILE_REMOTE_PLUGIN_BASE
#ifndef SYNC_WITH_SHM_FIFO
#include <sys/socket.h>
#include <sys/un.h>
#endif
#else
#include <QtCore/QMutex>
#include <QtCore/QProcess>
#include <QtCore/QThread>
#ifndef SYNC_WITH_SHM_FIFO
#include <poll.h>
#include <unistd.h>
#endif
#endif
#ifdef SYNC_WITH_SHM_FIFO
// sometimes we need to exchange bigger messages (e.g. for VST parameter dumps)
// so set a usable value here
const int SHM_FIFO_SIZE = 512*1024;
// implements a FIFO inside a shared memory segment
class shmFifo
{
// need this union to handle different sizes of sem_t on 32 bit
// and 64 bit platforms
union sem32_t
{
int semKey;
char fill[32];
} ;
struct shmData
{
sem32_t dataSem; // semaphore for locking this
// FIFO management data
sem32_t messageSem; // semaphore for incoming messages
volatile int32_t startPtr; // current start of FIFO in memory
volatile int32_t endPtr; // current end of FIFO in memory
char data[SHM_FIFO_SIZE]; // actual data
} ;
public:
// constructor for master-side
shmFifo() :
m_invalid( false ),
m_master( true ),
m_shmKey( 0 ),
#ifdef USE_QT_SHMEM
m_shmObj(),
#else
m_shmID( -1 ),
#endif
m_data( NULL ),
m_dataSem( QString::null ),
m_messageSem( QString::null ),
m_lockDepth( 0 )
{
#ifdef USE_QT_SHMEM
do
{
m_shmObj.setKey( QString( "%1" ).arg( ++m_shmKey ) );
m_shmObj.create( sizeof( shmData ) );
} while( m_shmObj.error() != QSharedMemory::NoError );
m_data = (shmData *) m_shmObj.data();
#else
while( ( m_shmID = shmget( ++m_shmKey, sizeof( shmData ),
IPC_CREAT | IPC_EXCL | 0600 ) ) == -1 )
{
}
m_data = (shmData *) shmat( m_shmID, 0, 0 );
#endif
assert( m_data != NULL );
m_data->startPtr = m_data->endPtr = 0;
static int k = 0;
m_data->dataSem.semKey = ( getpid()<<10 ) + ++k;
m_data->messageSem.semKey = ( getpid()<<10 ) + ++k;
m_dataSem.setKey( QString::number( m_data->dataSem.semKey ),
1, QSystemSemaphore::Create );
m_messageSem.setKey( QString::number(
m_data->messageSem.semKey ),
0, QSystemSemaphore::Create );
}
// constructor for remote-/client-side - use _shm_key for making up
// the connection to master
shmFifo( key_t _shm_key ) :
m_invalid( false ),
m_master( false ),
m_shmKey( 0 ),
#ifdef USE_QT_SHMEM
m_shmObj( QString::number( _shm_key ) ),
#else
m_shmID( shmget( _shm_key, 0, 0 ) ),
#endif
m_data( NULL ),
m_dataSem( QString::null ),
m_messageSem( QString::null ),
m_lockDepth( 0 )
{
#ifdef USE_QT_SHMEM
if( m_shmObj.attach() )
{
m_data = (shmData *) m_shmObj.data();
}
#else
if( m_shmID != -1 )
{
m_data = (shmData *) shmat( m_shmID, 0, 0 );
}
#endif
assert( m_data != NULL );
m_dataSem.setKey( QString::number( m_data->dataSem.semKey ) );
m_messageSem.setKey( QString::number(
m_data->messageSem.semKey ) );
}
~shmFifo()
{
// master?
if( m_master )
{
#ifndef USE_QT_SHMEM
shmctl( m_shmID, IPC_RMID, NULL );
#endif
}
#ifndef USE_QT_SHMEM
shmdt( m_data );
#endif
}
inline bool isInvalid() const
{
return m_invalid;
}
void invalidate()
{
m_invalid = true;
}
// do we act as master (i.e. not as remote-process?)
inline bool isMaster() const
{
return m_master;
}
// recursive lock
inline void lock()
{
if( !isInvalid() && __sync_add_and_fetch( &m_lockDepth, 1 ) == 1 )
{
m_dataSem.acquire();
}
}
// recursive unlock
inline void unlock()
{
if( __sync_sub_and_fetch( &m_lockDepth, 1) <= 0 )
{
m_dataSem.release();
}
}
// wait until message-semaphore is available
inline void waitForMessage()
{
if( !isInvalid() )
{
m_messageSem.acquire();
}
}
// increase message-semaphore
inline void messageSent()
{
m_messageSem.release();
}
inline int32_t readInt()
{
int32_t i;
read( &i, sizeof( i ) );
return i;
}
inline void writeInt( const int32_t & _i )
{
write( &_i, sizeof( _i ) );
}
inline std::string readString()
{
const int len = readInt();
if( len )
{
char * sc = new char[len + 1];
read( sc, len );
sc[len] = 0;
std::string s( sc );
delete[] sc;
return s;
}
return std::string();
}
inline void writeString( const std::string & _s )
{
const int len = _s.size();
writeInt( len );
write( _s.c_str(), len );
}
inline bool messagesLeft()
{
if( isInvalid() )
{
return false;
}
lock();
const bool empty = ( m_data->startPtr == m_data->endPtr );
unlock();
return !empty;
}
inline int shmKey() const
{
return m_shmKey;
}
private:
static inline void fastMemCpy( void * _dest, const void * _src,
const int _len )
{
// calling memcpy() for just an integer is obsolete overhead
if( _len == 4 )
{
*( (int32_t *) _dest ) = *( (int32_t *) _src );
}
else
{
memcpy( _dest, _src, _len );
}
}
void read( void * _buf, int _len )
{
if( isInvalid() )
{
memset( _buf, 0, _len );
return;
}
lock();
while( isInvalid() == false &&
_len > m_data->endPtr - m_data->startPtr )
{
unlock();
#ifndef LMMS_BUILD_WIN32
usleep( 5 );
#endif
lock();
}
fastMemCpy( _buf, m_data->data + m_data->startPtr, _len );
m_data->startPtr += _len;
// nothing left?
if( m_data->startPtr == m_data->endPtr )
{
// then reset to 0
m_data->startPtr = m_data->endPtr = 0;
}
unlock();
}
void write( const void * _buf, int _len )
{
if( isInvalid() || _len > SHM_FIFO_SIZE )
{
return;
}
lock();
while( _len > SHM_FIFO_SIZE - m_data->endPtr )
{
// if no space is left, try to move data to front
if( m_data->startPtr > 0 )
{
memmove( m_data->data,
m_data->data + m_data->startPtr,
m_data->endPtr - m_data->startPtr );
m_data->endPtr = m_data->endPtr -
m_data->startPtr;
m_data->startPtr = 0;
}
unlock();
#ifndef LMMS_BUILD_WIN32
usleep( 5 );
#endif
lock();
}
fastMemCpy( m_data->data + m_data->endPtr, _buf, _len );
m_data->endPtr += _len;
unlock();
}
volatile bool m_invalid;
bool m_master;
key_t m_shmKey;
#ifdef USE_QT_SHMEM
QSharedMemory m_shmObj;
#else
int m_shmID;
#endif
shmData * m_data;
QSystemSemaphore m_dataSem;
QSystemSemaphore m_messageSem;
volatile int m_lockDepth;
} ;
#endif
enum RemoteMessageIDs
{
IdUndefined,
IdHostInfoGotten,
IdInitDone,
IdQuit,
IdSampleRateInformation,
IdBufferSizeInformation,
IdInformationUpdated,
IdMidiEvent,
IdStartProcessing,
IdProcessingDone,
IdChangeSharedMemoryKey,
IdChangeInputCount,
IdChangeOutputCount,
IdChangeInputOutputCount,
IdShowUI,
IdHideUI,
IdToggleUI,
IdIsUIVisible,
IdSaveSettingsToString,
IdSaveSettingsToFile,
IdLoadSettingsFromString,
IdLoadSettingsFromFile,
IdSavePresetFile,
IdLoadPresetFile,
IdDebugMessage,
IdUserBase = 64
} ;
class EXPORT RemotePluginBase
{
public:
struct message
{
message() :
id( IdUndefined ),
data()
{
}
message( const message & _m ) :
id( _m.id ),
data( _m.data )
{
}
message( int _id ) :
id( _id ),
data()
{
}
inline message & addString( const std::string & _s )
{
data.push_back( _s );
return *this;
}
message & addInt( int _i )
{
char buf[32];
sprintf( buf, "%d", _i );
data.push_back( std::string( buf ) );
return *this;
}
message & addFloat( float _f )
{
char buf[32];
sprintf( buf, "%f", _f );
data.push_back( std::string( buf ) );
return *this;
}
inline std::string getString( int _p = 0 ) const
{
return data[_p];
}
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
inline QString getQString( int _p = 0 ) const
{
return QString::fromStdString( getString( _p ) );
}
#endif
inline int getInt( int _p = 0 ) const
{
return atoi( data[_p].c_str() );
}
inline float getFloat( int _p ) const
{
return (float) atof( data[_p].c_str() );
}
inline bool operator==( const message & _m ) const
{
return( id == _m.id );
}
int id;
private:
std::vector<std::string> data;
friend class RemotePluginBase;
} ;
#ifdef SYNC_WITH_SHM_FIFO
RemotePluginBase( shmFifo * _in, shmFifo * _out );
#else
RemotePluginBase();
#endif
virtual ~RemotePluginBase();
#ifdef SYNC_WITH_SHM_FIFO
void reset( shmFifo *in, shmFifo *out )
{
delete m_in;
delete m_out;
m_in = in;
m_out = out;
}
#endif
int sendMessage( const message & _m );
message receiveMessage();
inline bool isInvalid() const
{
#ifdef SYNC_WITH_SHM_FIFO
return m_in->isInvalid() || m_out->isInvalid();
#else
return m_invalid;
#endif
}
message waitForMessage( const message & _m,
bool _busy_waiting = false );
inline message fetchAndProcessNextMessage()
{
message m = receiveMessage();
processMessage( m );
return m;
}
#ifndef SYNC_WITH_SHM_FIFO
inline int32_t readInt()
{
int32_t i;
read( &i, sizeof( i ) );
return i;
}
inline void writeInt( const int32_t & _i )
{
write( &_i, sizeof( _i ) );
}
inline std::string readString()
{
const int len = readInt();
if( len )
{
char * sc = new char[len + 1];
read( sc, len );
sc[len] = 0;
std::string s( sc );
delete[] sc;
return s;
}
return std::string();
}
inline void writeString( const std::string & _s )
{
const int len = _s.size();
writeInt( len );
write( _s.c_str(), len );
}
#endif
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
inline bool messagesLeft()
{
#ifdef SYNC_WITH_SHM_FIFO
return m_in->messagesLeft();
#else
struct pollfd pollin;
pollin.fd = m_socket;
pollin.events = POLLIN;
if ( poll( &pollin, 1, 0 ) == -1 )
{
qWarning( "Unexpected poll error." );
}
return pollin.revents & POLLIN;
#endif
}
inline void fetchAndProcessAllMessages()
{
while( messagesLeft() )
{
fetchAndProcessNextMessage();
}
}
static bool isMainThreadWaiting()
{
return waitDepthCounter() > 0;
}
#endif
virtual bool processMessage( const message & _m ) = 0;
protected:
#ifdef SYNC_WITH_SHM_FIFO
inline const shmFifo * in() const
{
return m_in;
}
inline const shmFifo * out() const
{
return m_out;
}
#endif
inline void invalidate()
{
#ifdef SYNC_WITH_SHM_FIFO
m_in->invalidate();
m_out->invalidate();
m_in->messageSent();
#else
m_invalid = true;
#endif
}
#ifndef SYNC_WITH_SHM_FIFO
int m_socket;
#endif
private:
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
static int & waitDepthCounter()
{
static int waitDepth = 0;
return waitDepth;
}
#endif
#ifdef SYNC_WITH_SHM_FIFO
shmFifo * m_in;
shmFifo * m_out;
#else
void read( void * _buf, int _len )
{
if( isInvalid() )
{
memset( _buf, 0, _len );
return;
}
char * buf = (char *) _buf;
int remaining = _len;
while ( remaining )
{
ssize_t nread = ::read( m_socket, buf, remaining );
switch ( nread )
{
case -1:
fprintf( stderr,
"Error while reading.\n" );
case 0:
invalidate();
memset( _buf, 0, _len );
return;
}
buf += nread;
remaining -= nread;
}
}
void write( const void * _buf, int _len )
{
if( isInvalid() )
{
return;
}
const char * buf = (const char *) _buf;
int remaining = _len;
while ( remaining )
{
ssize_t nwritten = ::write( m_socket, buf, remaining );
switch ( nwritten )
{
case -1:
fprintf( stderr,
"Error while writing.\n" );
case 0:
invalidate();
return;
}
buf += nwritten;
remaining -= nwritten;
}
}
bool m_invalid;
pthread_mutex_t m_receiveMutex;
pthread_mutex_t m_sendMutex;
#endif
} ;
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
class RemotePlugin;
class ProcessWatcher : public QThread
{
public:
ProcessWatcher( RemotePlugin * );
virtual ~ProcessWatcher()
{
}
void stop()
{
m_quit = true;
quit();
}
void reset()
{
m_quit = false;
}
private:
virtual void run();
RemotePlugin * m_plugin;
volatile bool m_quit;
} ;
class EXPORT RemotePlugin : public QObject, public RemotePluginBase
{
Q_OBJECT
public:
RemotePlugin();
virtual ~RemotePlugin();
inline bool isRunning()
{
#ifdef DEBUG_REMOTE_PLUGIN
return true;
#else
return m_process.state() != QProcess::NotRunning;
#endif
}
bool init( const QString &pluginExecutable, bool waitForInitDoneMsg, QStringList extraArgs = {} );
inline void waitForHostInfoGotten()
{
m_failed = waitForMessage( IdHostInfoGotten ).id
!= IdHostInfoGotten;
}
inline void waitForInitDone( bool _busyWaiting = true )
{
m_failed = waitForMessage( IdInitDone, _busyWaiting ).id != IdInitDone;
}
virtual bool processMessage( const message & _m );
bool process( const sampleFrame * _in_buf, sampleFrame * _out_buf );
void processMidiEvent( const MidiEvent&, const f_cnt_t _offset );
void updateSampleRate( sample_rate_t _sr )
{
lock();
sendMessage( message( IdSampleRateInformation ).addInt( _sr ) );
waitForMessage( IdInformationUpdated, true );
unlock();
}
virtual void toggleUI()
{
lock();
sendMessage( IdToggleUI );
unlock();
}
int isUIVisible()
{
lock();
sendMessage( IdIsUIVisible );
unlock();
message m = waitForMessage( IdIsUIVisible );
return m.id != IdIsUIVisible ? -1 : m.getInt() ? 1 : 0;
}
inline bool failed() const
{
return m_failed;
}
inline void lock()
{
m_commMutex.lock();
}
inline void unlock()
{
m_commMutex.unlock();
}
public slots:
virtual void showUI();
virtual void hideUI();
protected:
inline void setSplittedChannels( bool _on )
{
m_splitChannels = _on;
}
private:
void resizeSharedProcessingMemory();
bool m_failed;
QProcess m_process;
ProcessWatcher m_watcher;
QString m_exec;
QStringList m_args;
QMutex m_commMutex;
bool m_splitChannels;
#ifdef USE_QT_SHMEM
QSharedMemory m_shmObj;
#else
int m_shmID;
#endif
size_t m_shmSize;
float * m_shm;
int m_inputCount;
int m_outputCount;
#ifndef SYNC_WITH_SHM_FIFO
int m_server;
QString m_socketFile;
#endif
friend class ProcessWatcher;
private slots:
void processFinished( int exitCode, QProcess::ExitStatus exitStatus );
} ;
#endif
#ifdef BUILD_REMOTE_PLUGIN_CLIENT
class RemotePluginClient : public RemotePluginBase
{
public:
#ifdef SYNC_WITH_SHM_FIFO
RemotePluginClient( key_t _shm_in, key_t _shm_out );
#else
RemotePluginClient( const char * socketPath );
#endif
virtual ~RemotePluginClient();
#ifdef USE_QT_SHMEM
VstSyncData * getQtVSTshm();
#endif
virtual bool processMessage( const message & _m );
virtual void process( const sampleFrame * _in_buf,
sampleFrame * _out_buf ) = 0;
virtual void processMidiEvent( const MidiEvent&, const f_cnt_t /* _offset */ )
{
}
inline float * sharedMemory()
{
return m_shm;
}
virtual void updateSampleRate()
{
}
virtual void updateBufferSize()
{
}
inline sample_rate_t sampleRate() const
{
return m_sampleRate;
}
inline fpp_t bufferSize() const
{
return m_bufferSize;
}
void setInputCount( int _i )
{
m_inputCount = _i;
sendMessage( message( IdChangeInputCount ).addInt( _i ) );
}
void setOutputCount( int _i )
{
m_outputCount = _i;
sendMessage( message( IdChangeOutputCount ).addInt( _i ) );
}
void setInputOutputCount( int i, int o )
{
m_inputCount = i;
m_outputCount = o;
sendMessage( message( IdChangeInputOutputCount )
.addInt( i )
.addInt( o ) );
}
virtual int inputCount() const
{
return m_inputCount;
}
virtual int outputCount() const
{
return m_outputCount;
}
void debugMessage( const std::string & _s )
{
sendMessage( message( IdDebugMessage ).addString( _s ) );
}
private:
void setShmKey( key_t _key, int _size );
void doProcessing();
#ifdef USE_QT_SHMEM
QSharedMemory m_shmObj;
QSharedMemory m_shmQtID;
#endif
VstSyncData * m_vstSyncData;
float * m_shm;
int m_inputCount;
int m_outputCount;
sample_rate_t m_sampleRate;
fpp_t m_bufferSize;
} ;
#endif
#ifdef COMPILE_REMOTE_PLUGIN_BASE
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
#include <QtCore/QCoreApplication>
#endif
#ifdef SYNC_WITH_SHM_FIFO
RemotePluginBase::RemotePluginBase( shmFifo * _in, shmFifo * _out ) :
m_in( _in ),
m_out( _out )
#else
RemotePluginBase::RemotePluginBase() :
m_socket( -1 ),
m_invalid( false )
#endif
{
#ifdef LMMS_HAVE_LOCALE_H
// make sure, we're using common ways to print/scan
// floats to/from strings (',' vs. '.' for decimal point etc.)
setlocale( LC_NUMERIC, "C" );
#endif
#ifndef SYNC_WITH_SHM_FIFO
pthread_mutex_init( &m_receiveMutex, NULL );
pthread_mutex_init( &m_sendMutex, NULL );
#endif
}
RemotePluginBase::~RemotePluginBase()
{
#ifdef SYNC_WITH_SHM_FIFO
delete m_in;
delete m_out;
#else
pthread_mutex_destroy( &m_receiveMutex );
pthread_mutex_destroy( &m_sendMutex );
#endif
}
int RemotePluginBase::sendMessage( const message & _m )
{
#ifdef SYNC_WITH_SHM_FIFO
m_out->lock();
m_out->writeInt( _m.id );
m_out->writeInt( _m.data.size() );
int j = 8;
for( unsigned int i = 0; i < _m.data.size(); ++i )
{
m_out->writeString( _m.data[i] );
j += 4 + _m.data[i].size();
}
m_out->unlock();
m_out->messageSent();
#else
pthread_mutex_lock( &m_sendMutex );
writeInt( _m.id );
writeInt( _m.data.size() );
int j = 8;
for( unsigned int i = 0; i < _m.data.size(); ++i )
{
writeString( _m.data[i] );
j += 4 + _m.data[i].size();
}
pthread_mutex_unlock( &m_sendMutex );
#endif
return j;
}
RemotePluginBase::message RemotePluginBase::receiveMessage()
{
#ifdef SYNC_WITH_SHM_FIFO
m_in->waitForMessage();
m_in->lock();
message m;
m.id = m_in->readInt();
const int s = m_in->readInt();
for( int i = 0; i < s; ++i )
{
m.data.push_back( m_in->readString() );
}
m_in->unlock();
#else
pthread_mutex_lock( &m_receiveMutex );
message m;
m.id = readInt();
const int s = readInt();
for( int i = 0; i < s; ++i )
{
m.data.push_back( readString() );
}
pthread_mutex_unlock( &m_receiveMutex );
#endif
return m;
}
RemotePluginBase::message RemotePluginBase::waitForMessage(
const message & _wm,
bool _busy_waiting )
{
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
if( _busy_waiting )
{
// No point processing events outside of the main thread
_busy_waiting = QThread::currentThread() ==
QCoreApplication::instance()->thread();
}
struct WaitDepthCounter
{
WaitDepthCounter( int & depth, bool busy ) :
m_depth( depth ),
m_busy( busy )
{
if( m_busy ) { ++m_depth; }
}
~WaitDepthCounter()
{
if( m_busy ) { --m_depth; }
}
int & m_depth;
bool m_busy;
};
WaitDepthCounter wdc( waitDepthCounter(), _busy_waiting );
#endif
while( !isInvalid() )
{
#ifndef BUILD_REMOTE_PLUGIN_CLIENT
if( _busy_waiting && !messagesLeft() )
{
QCoreApplication::processEvents(
QEventLoop::ExcludeUserInputEvents, 50 );
continue;
}
#endif
message m = receiveMessage();
processMessage( m );
if( m.id == _wm.id )
{
return m;
}
else if( m.id == IdUndefined )
{
return m;
}
}
return message();
}
#endif
#ifdef BUILD_REMOTE_PLUGIN_CLIENT
#ifdef SYNC_WITH_SHM_FIFO
RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) :
RemotePluginBase( new shmFifo( _shm_in ), new shmFifo( _shm_out ) ),
#else
RemotePluginClient::RemotePluginClient( const char * socketPath ) :
RemotePluginBase(),
#endif
#ifdef USE_QT_SHMEM
m_shmObj(),
m_shmQtID( "/usr/bin/lmms" ),
#endif
m_vstSyncData( NULL ),
m_shm( NULL ),
m_inputCount( 0 ),
m_outputCount( 0 ),
m_sampleRate( 44100 ),
m_bufferSize( 0 )
{
#ifndef SYNC_WITH_SHM_FIFO
struct sockaddr_un sa;
sa.sun_family = AF_LOCAL;
size_t length = strlen( socketPath );
if ( length >= sizeof sa.sun_path )
{
length = sizeof sa.sun_path - 1;
fprintf( stderr, "Socket path too long.\n" );
}
memcpy( sa.sun_path, socketPath, length );
sa.sun_path[length] = '\0';
m_socket = socket( PF_LOCAL, SOCK_STREAM, 0 );
if ( m_socket == -1 )
{
fprintf( stderr, "Could not connect to local server.\n" );
}
if ( ::connect( m_socket, (struct sockaddr *) &sa, sizeof sa ) == -1 )
{
fprintf( stderr, "Could not connect to local server.\n" );
}
#endif
#ifdef USE_QT_SHMEM
if( m_shmQtID.attach( QSharedMemory::ReadOnly ) )
{
m_vstSyncData = (VstSyncData *) m_shmQtID.data();
m_bufferSize = m_vstSyncData->m_bufferSize;
m_sampleRate = m_vstSyncData->m_sampleRate;
sendMessage( IdHostInfoGotten );
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_vstSyncData = (VstSyncData *)shmat(m_shmID, 0, 0);
if( m_vstSyncData == (VstSyncData *)( -1 ) )
{
perror( "RemotePluginClient::shmat" );
}
else
{
m_bufferSize = m_vstSyncData->m_bufferSize;
m_sampleRate = m_vstSyncData->m_sampleRate;
sendMessage( IdHostInfoGotten );
// detach segment
if( shmdt(m_vstSyncData) == -1 )
{
perror("RemotePluginClient::shmdt");
}
return;
}
}
}
#endif
// if attaching shared memory fails
sendMessage( IdSampleRateInformation );
sendMessage( IdBufferSizeInformation );
if( waitForMessage( IdBufferSizeInformation ).id
!= IdBufferSizeInformation )
{
fprintf( stderr, "Could not get buffer size information\n" );
}
sendMessage( IdHostInfoGotten );
}
RemotePluginClient::~RemotePluginClient()
{
#ifdef USE_QT_SHMEM
m_shmQtID.detach();
#endif
sendMessage( IdQuit );
#ifndef USE_QT_SHMEM
shmdt( m_shm );
#endif
#ifndef SYNC_WITH_SHM_FIFO
if ( close( m_socket ) == -1)
{
fprintf( stderr, "Error freeing resources.\n" );
}
#endif
}
#ifdef USE_QT_SHMEM
VstSyncData * RemotePluginClient::getQtVSTshm()
{
return m_vstSyncData;
}
#endif
bool RemotePluginClient::processMessage( const message & _m )
{
message reply_message( _m.id );
bool reply = false;
switch( _m.id )
{
case IdUndefined:
return false;
case IdSampleRateInformation:
m_sampleRate = _m.getInt();
updateSampleRate();
reply_message.id = IdInformationUpdated;
reply = true;
break;
case IdBufferSizeInformation:
// Should LMMS gain the ability to change buffer size
// without a restart, it must wait for this message to
// complete processing or else risk VST crashes
m_bufferSize = _m.getInt();
updateBufferSize();
break;
case IdQuit:
return false;
case IdMidiEvent:
processMidiEvent(
MidiEvent( static_cast<MidiEventTypes>(
_m.getInt( 0 ) ),
_m.getInt( 1 ),
_m.getInt( 2 ),
_m.getInt( 3 ) ),
_m.getInt( 4 ) );
break;
case IdStartProcessing:
doProcessing();
reply_message.id = IdProcessingDone;
reply = true;
break;
case IdChangeSharedMemoryKey:
setShmKey( _m.getInt( 0 ), _m.getInt( 1 ) );
break;
case IdInitDone:
break;
default:
{
char buf[64];
sprintf( buf, "undefined message: %d\n", (int) _m.id );
debugMessage( buf );
break;
}
}
if( reply )
{
sendMessage( reply_message );
}
return true;
}
void RemotePluginClient::setShmKey( key_t _key, int _size )
{
#ifdef USE_QT_SHMEM
m_shmObj.setKey( QString::number( _key ) );
if( m_shmObj.attach() || m_shmObj.error() == QSharedMemory::NoError )
{
m_shm = (float *) m_shmObj.data();
}
else
{
char buf[64];
sprintf( buf, "failed getting shared memory: %d\n", m_shmObj.error() );
debugMessage( buf );
}
#else
if( m_shm != NULL )
{
shmdt( m_shm );
m_shm = NULL;
}
// only called for detaching SHM?
if( _key == 0 )
{
return;
}
int shm_id = shmget( _key, _size, 0 );
if( shm_id == -1 )
{
debugMessage( "failed getting shared memory\n" );
}
else
{
m_shm = (float *) shmat( shm_id, 0, 0 );
}
#endif
}
void RemotePluginClient::doProcessing()
{
if( m_shm != NULL )
{
process( (sampleFrame *)( m_inputCount > 0 ? m_shm : NULL ),
(sampleFrame *)( m_shm +
( m_inputCount*m_bufferSize ) ) );
}
else
{
debugMessage( "doProcessing(): have no shared memory!\n" );
}
}
#endif
#define QSTR_TO_STDSTR(s) std::string( s.toUtf8().constData() )
#endif