/* * RemotePlugin.h - base class providing RPC like mechanisms * * Copyright (c) 2008-2014 Tobias Doerffel * * This file is part of LMMS - http://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 #include #include #include #include #include #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 #endif #include #include #endif #ifdef LMMS_HAVE_SYS_SHM_H #include #ifdef LMMS_HAVE_UNISTD_H #include #endif #else #define USE_QT_SHMEM #include #include #if !defined(LMMS_HAVE_SYS_TYPES_H) || defined(LMMS_BUILD_WIN32) typedef int32_t key_t; #endif #endif #ifdef LMMS_HAVE_LOCALE_H #include #endif #ifdef LMMS_HAVE_PTHREAD_H #include #endif #ifdef BUILD_REMOTE_PLUGIN_CLIENT #undef EXPORT #define EXPORT #define COMPILE_REMOTE_PLUGIN_BASE #ifndef SYNC_WITH_SHM_FIFO #include #include #endif #else #include #include #include #ifndef SYNC_WITH_SHM_FIFO #include #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, IdInitDone, IdQuit, IdSampleRateInformation, IdBufferSizeInformation, IdMidiEvent, IdStartProcessing, IdProcessingDone, IdChangeSharedMemoryKey, IdChangeInputCount, IdChangeOutputCount, IdShowUI, IdHideUI, 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 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(); } } #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: #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 quit() { m_quit = true; } 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 ); 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 ) ); unlock(); } void showUI() { lock(); sendMessage( IdShowUI ); unlock(); } void hideUI() { lock(); sendMessage( IdHideUI ); unlock(); } inline bool failed() const { return m_failed; } inline void lock() { m_commMutex.lock(); } inline void unlock() { m_commMutex.unlock(); } protected: inline void setSplittedChannels( bool _on ) { m_splitChannels = _on; } private: void resizeSharedProcessingMemory(); bool m_failed; QProcess m_process; ProcessWatcher m_watcher; 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 ) ); } 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 #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 ) { 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; 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; // detach segment if( shmdt(m_vstSyncData) == -1 ) { perror("RemotePluginClient::shmdt"); } return; } } } #endif // if attaching shared memory fails sendMessage( IdSampleRateInformation ); sendMessage( IdBufferSizeInformation ); } 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(); break; case IdBufferSizeInformation: m_bufferSize = _m.getInt(); updateBufferSize(); break; case IdQuit: return false; case IdMidiEvent: processMidiEvent( MidiEvent( static_cast( _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