/* * RemotePluginClient.h * * Copyright (c) 2008-2014 Tobias Doerffel * * 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_CLIENT_H #define REMOTE_PLUGIN_CLIENT_H #include "RemotePluginBase.h" 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; } ; #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( nullptr ), m_shm( nullptr ), 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( _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 != nullptr ) { shmdt( m_shm ); m_shm = nullptr; } // 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 != nullptr ) { process( (sampleFrame *)( m_inputCount > 0 ? m_shm : nullptr ), (sampleFrame *)( m_shm + ( m_inputCount*m_bufferSize ) ) ); } else { debugMessage( "doProcessing(): have no shared memory!\n" ); } } #endif // REMOTE_PLUGIN_CLIENT_H