1 /*
2  * RemotePlugin.h - base class providing RPC like mechanisms
3  *
4  * Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5  *
6  * This file is part of LMMS - https://lmms.io
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public
19  * License along with this program (see COPYING); if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301 USA.
22  *
23  */
24 
25 #ifndef REMOTE_PLUGIN_H
26 #define REMOTE_PLUGIN_H
27 
28 #include "export.h"
29 #include "MidiEvent.h"
30 #include "VstSyncData.h"
31 
32 #include <vector>
33 #include <cstdio>
34 #include <cstdlib>
35 #include <cstring>
36 #include <string>
37 #include <cassert>
38 
39 
40 #if !(defined(LMMS_HAVE_SYS_IPC_H) && defined(LMMS_HAVE_SEMAPHORE_H))
41 #define SYNC_WITH_SHM_FIFO
42 #define USE_QT_SEMAPHORES
43 
44 #ifdef LMMS_HAVE_PROCESS_H
45 #include <process.h>
46 #endif
47 
48 #include <QtCore/QtGlobal>
49 #include <QtCore/QSystemSemaphore>
50 #endif
51 
52 
53 #ifdef LMMS_HAVE_SYS_SHM_H
54 #include <sys/shm.h>
55 
56 #ifdef LMMS_HAVE_UNISTD_H
57 #include <unistd.h>
58 #endif
59 #else
60 #define USE_QT_SHMEM
61 
62 #include <QtCore/QtGlobal>
63 #include <QtCore/QSharedMemory>
64 
65 #if !defined(LMMS_HAVE_SYS_TYPES_H) || defined(LMMS_BUILD_WIN32)
66 typedef int32_t key_t;
67 #endif
68 #endif
69 
70 
71 #ifdef LMMS_HAVE_LOCALE_H
72 #include <locale.h>
73 #endif
74 
75 #ifdef LMMS_HAVE_PTHREAD_H
76 #include <pthread.h>
77 #endif
78 
79 
80 #ifdef BUILD_REMOTE_PLUGIN_CLIENT
81 #undef EXPORT
82 #define EXPORT
83 #define COMPILE_REMOTE_PLUGIN_BASE
84 
85 #ifndef SYNC_WITH_SHM_FIFO
86 #include <sys/socket.h>
87 #include <sys/un.h>
88 #endif
89 
90 #else
91 #include <QtCore/QMutex>
92 #include <QtCore/QProcess>
93 #include <QtCore/QThread>
94 
95 #ifndef SYNC_WITH_SHM_FIFO
96 #include <poll.h>
97 #include <unistd.h>
98 #endif
99 
100 #endif
101 
102 #ifdef SYNC_WITH_SHM_FIFO
103 // sometimes we need to exchange bigger messages (e.g. for VST parameter dumps)
104 // so set a usable value here
105 const int SHM_FIFO_SIZE = 512*1024;
106 
107 
108 // implements a FIFO inside a shared memory segment
109 class shmFifo
110 {
111 	// need this union to handle different sizes of sem_t on 32 bit
112 	// and 64 bit platforms
113 	union sem32_t
114 	{
115 		int semKey;
116 		char fill[32];
117 	} ;
118 	struct shmData
119 	{
120 		sem32_t dataSem;	// semaphore for locking this
121 					// FIFO management data
122 		sem32_t messageSem;	// semaphore for incoming messages
123 		volatile int32_t startPtr; // current start of FIFO in memory
124 		volatile int32_t endPtr;   // current end of FIFO in memory
125 		char data[SHM_FIFO_SIZE];  // actual data
126 	} ;
127 
128 public:
129 	// constructor for master-side
shmFifo()130 	shmFifo() :
131 		m_invalid( false ),
132 		m_master( true ),
133 		m_shmKey( 0 ),
134 #ifdef USE_QT_SHMEM
135 		m_shmObj(),
136 #else
137 		m_shmID( -1 ),
138 #endif
139 		m_data( NULL ),
140 		m_dataSem( QString::null ),
141 		m_messageSem( QString::null ),
142 		m_lockDepth( 0 )
143 	{
144 #ifdef USE_QT_SHMEM
145 		do
146 		{
147 			m_shmObj.setKey( QString( "%1" ).arg( ++m_shmKey ) );
148 			m_shmObj.create( sizeof( shmData ) );
149 		} while( m_shmObj.error() != QSharedMemory::NoError );
150 
151 		m_data = (shmData *) m_shmObj.data();
152 #else
153 		while( ( m_shmID = shmget( ++m_shmKey, sizeof( shmData ),
154 					IPC_CREAT | IPC_EXCL | 0600 ) ) == -1 )
155 		{
156 		}
157 		m_data = (shmData *) shmat( m_shmID, 0, 0 );
158 #endif
159 		assert( m_data != NULL );
160 		m_data->startPtr = m_data->endPtr = 0;
161 		static int k = 0;
162 		m_data->dataSem.semKey = ( getpid()<<10 ) + ++k;
163 		m_data->messageSem.semKey = ( getpid()<<10 ) + ++k;
164 		m_dataSem.setKey( QString::number( m_data->dataSem.semKey ),
165 						1, QSystemSemaphore::Create );
166 		m_messageSem.setKey( QString::number(
167 						m_data->messageSem.semKey ),
168 						0, QSystemSemaphore::Create );
169 	}
170 
171 	// constructor for remote-/client-side - use _shm_key for making up
172 	// the connection to master
shmFifo(key_t _shm_key)173 	shmFifo( key_t _shm_key ) :
174 		m_invalid( false ),
175 		m_master( false ),
176 		m_shmKey( 0 ),
177 #ifdef USE_QT_SHMEM
178 		m_shmObj( QString::number( _shm_key ) ),
179 #else
180 		m_shmID( shmget( _shm_key, 0, 0 ) ),
181 #endif
182 		m_data( NULL ),
183 		m_dataSem( QString::null ),
184 		m_messageSem( QString::null ),
185 		m_lockDepth( 0 )
186 	{
187 #ifdef USE_QT_SHMEM
188 		if( m_shmObj.attach() )
189 		{
190 			m_data = (shmData *) m_shmObj.data();
191 		}
192 #else
193 		if( m_shmID != -1 )
194 		{
195 			m_data = (shmData *) shmat( m_shmID, 0, 0 );
196 		}
197 #endif
198 		assert( m_data != NULL );
199 		m_dataSem.setKey( QString::number( m_data->dataSem.semKey ) );
200 		m_messageSem.setKey( QString::number(
201 						m_data->messageSem.semKey ) );
202 	}
203 
~shmFifo()204 	~shmFifo()
205 	{
206 		// master?
207 		if( m_master )
208 		{
209 #ifndef USE_QT_SHMEM
210 			shmctl( m_shmID, IPC_RMID, NULL );
211 #endif
212 		}
213 #ifndef USE_QT_SHMEM
214 		shmdt( m_data );
215 #endif
216 	}
217 
isInvalid()218 	inline bool isInvalid() const
219 	{
220 		return m_invalid;
221 	}
222 
invalidate()223 	void invalidate()
224 	{
225 		m_invalid = true;
226 	}
227 
228 	// do we act as master (i.e. not as remote-process?)
isMaster()229 	inline bool isMaster() const
230 	{
231 		return m_master;
232 	}
233 
234 	// recursive lock
lock()235 	inline void lock()
236 	{
237 		if( !isInvalid() && __sync_add_and_fetch( &m_lockDepth, 1 ) == 1 )
238 		{
239 			m_dataSem.acquire();
240 		}
241 	}
242 
243 	// recursive unlock
unlock()244 	inline void unlock()
245 	{
246 		if( __sync_sub_and_fetch( &m_lockDepth, 1) <= 0 )
247 		{
248 			m_dataSem.release();
249 		}
250 	}
251 
252 	// wait until message-semaphore is available
waitForMessage()253 	inline void waitForMessage()
254 	{
255 		if( !isInvalid() )
256 		{
257 			m_messageSem.acquire();
258 		}
259 	}
260 
261 	// increase message-semaphore
messageSent()262 	inline void messageSent()
263 	{
264 		m_messageSem.release();
265 	}
266 
267 
readInt()268 	inline int32_t readInt()
269 	{
270 		int32_t i;
271 		read( &i, sizeof( i ) );
272 		return i;
273 	}
274 
writeInt(const int32_t & _i)275 	inline void writeInt( const int32_t & _i )
276 	{
277 		write( &_i, sizeof( _i ) );
278 	}
279 
readString()280 	inline std::string readString()
281 	{
282 		const int len = readInt();
283 		if( len )
284 		{
285 			char * sc = new char[len + 1];
286 			read( sc, len );
287 			sc[len] = 0;
288 			std::string s( sc );
289 			delete[] sc;
290 			return s;
291 		}
292 		return std::string();
293 	}
294 
295 
writeString(const std::string & _s)296 	inline void writeString( const std::string & _s )
297 	{
298 		const int len = _s.size();
299 		writeInt( len );
300 		write( _s.c_str(), len );
301 	}
302 
303 
messagesLeft()304 	inline bool messagesLeft()
305 	{
306 		if( isInvalid() )
307 		{
308 			return false;
309 		}
310 		lock();
311 		const bool empty = ( m_data->startPtr == m_data->endPtr );
312 		unlock();
313 		return !empty;
314 	}
315 
316 
shmKey()317 	inline int shmKey() const
318 	{
319 		return m_shmKey;
320 	}
321 
322 
323 private:
fastMemCpy(void * _dest,const void * _src,const int _len)324 	static inline void fastMemCpy( void * _dest, const void * _src,
325 							const int _len )
326 	{
327 		// calling memcpy() for just an integer is obsolete overhead
328 		if( _len == 4 )
329 		{
330 			*( (int32_t *) _dest ) = *( (int32_t *) _src );
331 		}
332 		else
333 		{
334 			memcpy( _dest, _src, _len );
335 		}
336 	}
337 
read(void * _buf,int _len)338 	void read( void * _buf, int _len )
339 	{
340 		if( isInvalid() )
341 		{
342 			memset( _buf, 0, _len );
343 			return;
344 		}
345 		lock();
346 		while( isInvalid() == false &&
347 				_len > m_data->endPtr - m_data->startPtr )
348 		{
349 			unlock();
350 #ifndef LMMS_BUILD_WIN32
351 			usleep( 5 );
352 #endif
353 			lock();
354 		}
355 		fastMemCpy( _buf, m_data->data + m_data->startPtr, _len );
356 		m_data->startPtr += _len;
357 		// nothing left?
358 		if( m_data->startPtr == m_data->endPtr )
359 		{
360 			// then reset to 0
361 			m_data->startPtr = m_data->endPtr = 0;
362 		}
363 		unlock();
364 	}
365 
write(const void * _buf,int _len)366 	void write( const void * _buf, int _len )
367 	{
368 		if( isInvalid() || _len > SHM_FIFO_SIZE )
369 		{
370 			return;
371 		}
372 		lock();
373 		while( _len > SHM_FIFO_SIZE - m_data->endPtr )
374 		{
375 			// if no space is left, try to move data to front
376 			if( m_data->startPtr > 0 )
377 			{
378 				memmove( m_data->data,
379 					m_data->data + m_data->startPtr,
380 					m_data->endPtr - m_data->startPtr );
381 				m_data->endPtr = m_data->endPtr -
382 							m_data->startPtr;
383 				m_data->startPtr = 0;
384 			}
385 			unlock();
386 #ifndef LMMS_BUILD_WIN32
387 			usleep( 5 );
388 #endif
389 			lock();
390 		}
391 		fastMemCpy( m_data->data + m_data->endPtr, _buf, _len );
392 		m_data->endPtr += _len;
393 		unlock();
394 	}
395 
396 	volatile bool m_invalid;
397 	bool m_master;
398 	key_t m_shmKey;
399 #ifdef USE_QT_SHMEM
400 	QSharedMemory m_shmObj;
401 #else
402 	int m_shmID;
403 #endif
404 	shmData * m_data;
405 	QSystemSemaphore m_dataSem;
406 	QSystemSemaphore m_messageSem;
407 	volatile int m_lockDepth;
408 
409 } ;
410 #endif
411 
412 
413 
414 enum RemoteMessageIDs
415 {
416 	IdUndefined,
417 	IdHostInfoGotten,
418 	IdInitDone,
419 	IdQuit,
420 	IdSampleRateInformation,
421 	IdBufferSizeInformation,
422 	IdInformationUpdated,
423 	IdMidiEvent,
424 	IdStartProcessing,
425 	IdProcessingDone,
426 	IdChangeSharedMemoryKey,
427 	IdChangeInputCount,
428 	IdChangeOutputCount,
429 	IdChangeInputOutputCount,
430 	IdShowUI,
431 	IdHideUI,
432 	IdToggleUI,
433 	IdIsUIVisible,
434 	IdSaveSettingsToString,
435 	IdSaveSettingsToFile,
436 	IdLoadSettingsFromString,
437 	IdLoadSettingsFromFile,
438 	IdSavePresetFile,
439 	IdLoadPresetFile,
440 	IdDebugMessage,
441 	IdUserBase = 64
442 } ;
443 
444 
445 
446 class EXPORT RemotePluginBase
447 {
448 public:
449 	struct message
450 	{
messagemessage451 		message() :
452 			id( IdUndefined ),
453 			data()
454 		{
455 		}
456 
messagemessage457 		message( const message & _m ) :
458 			id( _m.id ),
459 			data( _m.data )
460 		{
461 		}
462 
messagemessage463 		message( int _id ) :
464 			id( _id ),
465 			data()
466 		{
467 		}
468 
addStringmessage469 		inline message & addString( const std::string & _s )
470 		{
471 			data.push_back( _s );
472 			return *this;
473 		}
474 
addIntmessage475 		message & addInt( int _i )
476 		{
477 			char buf[32];
478 			sprintf( buf, "%d", _i );
479 			data.push_back( std::string( buf ) );
480 			return *this;
481 		}
482 
addFloatmessage483 		message & addFloat( float _f )
484 		{
485 			char buf[32];
486 			sprintf( buf, "%f", _f );
487 			data.push_back( std::string( buf ) );
488 			return *this;
489 		}
490 
491 		inline std::string getString( int _p = 0 ) const
492 		{
493 			return data[_p];
494 		}
495 
496 #ifndef BUILD_REMOTE_PLUGIN_CLIENT
497 		inline QString getQString( int _p = 0 ) const
498 		{
499 			return QString::fromStdString( getString( _p ) );
500 		}
501 #endif
502 
503 		inline int getInt( int _p = 0 ) const
504 		{
505 			return atoi( data[_p].c_str() );
506 		}
507 
getFloatmessage508 		inline float getFloat( int _p ) const
509 		{
510 			return (float) atof( data[_p].c_str() );
511 		}
512 
513 		inline bool operator==( const message & _m ) const
514 		{
515 			return( id == _m.id );
516 		}
517 
518 		int id;
519 
520 	private:
521 		std::vector<std::string> data;
522 
523 		friend class RemotePluginBase;
524 
525 	} ;
526 
527 #ifdef SYNC_WITH_SHM_FIFO
528 	RemotePluginBase( shmFifo * _in, shmFifo * _out );
529 #else
530 	RemotePluginBase();
531 #endif
532 	virtual ~RemotePluginBase();
533 
534 #ifdef SYNC_WITH_SHM_FIFO
reset(shmFifo * in,shmFifo * out)535 	void reset( shmFifo *in, shmFifo *out )
536 	{
537 		delete m_in;
538 		delete m_out;
539 		m_in = in;
540 		m_out = out;
541 	}
542 #endif
543 
544 	int sendMessage( const message & _m );
545 	message receiveMessage();
546 
isInvalid()547 	inline bool isInvalid() const
548 	{
549 #ifdef SYNC_WITH_SHM_FIFO
550 		return m_in->isInvalid() || m_out->isInvalid();
551 #else
552 		return m_invalid;
553 #endif
554 	}
555 
556 	message waitForMessage( const message & _m,
557 						bool _busy_waiting = false );
558 
fetchAndProcessNextMessage()559 	inline message fetchAndProcessNextMessage()
560 	{
561 		message m = receiveMessage();
562 		processMessage( m );
563 		return m;
564 	}
565 
566 #ifndef SYNC_WITH_SHM_FIFO
readInt()567 	inline int32_t readInt()
568 	{
569 		int32_t i;
570 		read( &i, sizeof( i ) );
571 		return i;
572 	}
573 
writeInt(const int32_t & _i)574 	inline void writeInt( const int32_t & _i )
575 	{
576 		write( &_i, sizeof( _i ) );
577 	}
578 
readString()579 	inline std::string readString()
580 	{
581 		const int len = readInt();
582 		if( len )
583 		{
584 			char * sc = new char[len + 1];
585 			read( sc, len );
586 			sc[len] = 0;
587 			std::string s( sc );
588 			delete[] sc;
589 			return s;
590 		}
591 		return std::string();
592 	}
593 
594 
writeString(const std::string & _s)595 	inline void writeString( const std::string & _s )
596 	{
597 		const int len = _s.size();
598 		writeInt( len );
599 		write( _s.c_str(), len );
600 	}
601 #endif
602 
603 #ifndef BUILD_REMOTE_PLUGIN_CLIENT
messagesLeft()604 	inline bool messagesLeft()
605 	{
606 #ifdef SYNC_WITH_SHM_FIFO
607 		return m_in->messagesLeft();
608 #else
609 		struct pollfd pollin;
610 		pollin.fd = m_socket;
611 		pollin.events = POLLIN;
612 
613 		if ( poll( &pollin, 1, 0 ) == -1 )
614 		{
615 			qWarning( "Unexpected poll error." );
616 		}
617 		return pollin.revents & POLLIN;
618 #endif
619 	}
620 
fetchAndProcessAllMessages()621 	inline void fetchAndProcessAllMessages()
622 	{
623 		while( messagesLeft() )
624 		{
625 			fetchAndProcessNextMessage();
626 		}
627 	}
628 
isMainThreadWaiting()629 	static bool isMainThreadWaiting()
630 	{
631 		return waitDepthCounter() > 0;
632 	}
633 #endif
634 
635 	virtual bool processMessage( const message & _m ) = 0;
636 
637 
638 protected:
639 #ifdef SYNC_WITH_SHM_FIFO
in()640 	inline const shmFifo * in() const
641 	{
642 		return m_in;
643 	}
644 
out()645 	inline const shmFifo * out() const
646 	{
647 		return m_out;
648 	}
649 #endif
650 
invalidate()651 	inline void invalidate()
652 	{
653 #ifdef SYNC_WITH_SHM_FIFO
654 		m_in->invalidate();
655 		m_out->invalidate();
656 		m_in->messageSent();
657 #else
658 		m_invalid = true;
659 #endif
660 	}
661 
662 
663 #ifndef SYNC_WITH_SHM_FIFO
664 	int m_socket;
665 #endif
666 
667 
668 private:
669 #ifndef BUILD_REMOTE_PLUGIN_CLIENT
waitDepthCounter()670 	static int & waitDepthCounter()
671 	{
672 		static int waitDepth = 0;
673 		return waitDepth;
674 	}
675 #endif
676 
677 #ifdef SYNC_WITH_SHM_FIFO
678 	shmFifo * m_in;
679 	shmFifo * m_out;
680 #else
read(void * _buf,int _len)681 	void read( void * _buf, int _len )
682 	{
683 		if( isInvalid() )
684 		{
685 			memset( _buf, 0, _len );
686 			return;
687 		}
688 		char * buf = (char *) _buf;
689 		int remaining = _len;
690 		while ( remaining )
691 		{
692 			ssize_t nread = ::read( m_socket, buf, remaining );
693 			switch ( nread )
694 			{
695 				case -1:
696 					fprintf( stderr,
697 						"Error while reading.\n" );
698 				case 0:
699 					invalidate();
700 					memset( _buf, 0, _len );
701 					return;
702 			}
703 			buf += nread;
704 			remaining -= nread;
705 		}
706 	}
707 
write(const void * _buf,int _len)708 	void write( const void * _buf, int _len )
709 	{
710 		if( isInvalid() )
711 		{
712 			return;
713 		}
714 		const char * buf = (const char *) _buf;
715 		int remaining = _len;
716 		while ( remaining )
717 		{
718 			ssize_t nwritten = ::write( m_socket, buf, remaining );
719 			switch ( nwritten )
720 			{
721 				case -1:
722 					fprintf( stderr,
723 						"Error while writing.\n" );
724 				case 0:
725 					invalidate();
726 					return;
727 			}
728 			buf += nwritten;
729 			remaining -= nwritten;
730 		}
731 	}
732 
733 
734 	bool m_invalid;
735 
736 	pthread_mutex_t m_receiveMutex;
737 	pthread_mutex_t m_sendMutex;
738 #endif
739 
740 } ;
741 
742 
743 
744 #ifndef BUILD_REMOTE_PLUGIN_CLIENT
745 
746 
747 class RemotePlugin;
748 
749 class ProcessWatcher : public QThread
750 {
751 public:
752 	ProcessWatcher( RemotePlugin * );
~ProcessWatcher()753 	virtual ~ProcessWatcher()
754 	{
755 	}
756 
757 
stop()758 	void stop()
759 	{
760 		m_quit = true;
761 		quit();
762 	}
763 
reset()764 	void reset()
765 	{
766 		m_quit = false;
767 	}
768 
769 private:
770 	virtual void run();
771 
772 	RemotePlugin * m_plugin;
773 	volatile bool m_quit;
774 
775 } ;
776 
777 
778 class EXPORT RemotePlugin : public QObject, public RemotePluginBase
779 {
780 	Q_OBJECT
781 public:
782 	RemotePlugin();
783 	virtual ~RemotePlugin();
784 
isRunning()785 	inline bool isRunning()
786 	{
787 #ifdef DEBUG_REMOTE_PLUGIN
788 		return true;
789 #else
790 		return m_process.state() != QProcess::NotRunning;
791 #endif
792 	}
793 
794 	bool init( const QString &pluginExecutable, bool waitForInitDoneMsg, QStringList extraArgs = {} );
795 
waitForHostInfoGotten()796 	inline void waitForHostInfoGotten()
797 	{
798 		m_failed = waitForMessage( IdHostInfoGotten ).id
799 							!= IdHostInfoGotten;
800 	}
801 
802 	inline void waitForInitDone( bool _busyWaiting = true )
803 	{
804 		m_failed = waitForMessage( IdInitDone, _busyWaiting ).id != IdInitDone;
805 	}
806 
807 	virtual bool processMessage( const message & _m );
808 
809 	bool process( const sampleFrame * _in_buf, sampleFrame * _out_buf );
810 
811 	void processMidiEvent( const MidiEvent&, const f_cnt_t _offset );
812 
updateSampleRate(sample_rate_t _sr)813 	void updateSampleRate( sample_rate_t _sr )
814 	{
815 		lock();
816 		sendMessage( message( IdSampleRateInformation ).addInt( _sr ) );
817 		waitForMessage( IdInformationUpdated, true );
818 		unlock();
819 	}
820 
821 
toggleUI()822 	virtual void toggleUI()
823 	{
824 		lock();
825 		sendMessage( IdToggleUI );
826 		unlock();
827 	}
828 
isUIVisible()829 	int isUIVisible()
830 	{
831 		lock();
832 		sendMessage( IdIsUIVisible );
833 		unlock();
834 		message m = waitForMessage( IdIsUIVisible );
835 		return m.id != IdIsUIVisible ? -1 : m.getInt() ? 1 : 0;
836 	}
837 
failed()838 	inline bool failed() const
839 	{
840 		return m_failed;
841 	}
842 
lock()843 	inline void lock()
844 	{
845 		m_commMutex.lock();
846 	}
847 
unlock()848 	inline void unlock()
849 	{
850 		m_commMutex.unlock();
851 	}
852 
853 public slots:
854 	virtual void showUI();
855 	virtual void hideUI();
856 
857 protected:
setSplittedChannels(bool _on)858 	inline void setSplittedChannels( bool _on )
859 	{
860 		m_splitChannels = _on;
861 	}
862 
863 
864 private:
865 	void resizeSharedProcessingMemory();
866 
867 
868 	bool m_failed;
869 
870 	QProcess m_process;
871 	ProcessWatcher m_watcher;
872 
873 	QString m_exec;
874 	QStringList m_args;
875 
876 	QMutex m_commMutex;
877 	bool m_splitChannels;
878 #ifdef USE_QT_SHMEM
879 	QSharedMemory m_shmObj;
880 #else
881 	int m_shmID;
882 #endif
883 	size_t m_shmSize;
884 	float * m_shm;
885 
886 	int m_inputCount;
887 	int m_outputCount;
888 
889 #ifndef SYNC_WITH_SHM_FIFO
890 	int m_server;
891 	QString m_socketFile;
892 #endif
893 
894 	friend class ProcessWatcher;
895 
896 
897 private slots:
898 	void processFinished( int exitCode, QProcess::ExitStatus exitStatus );
899 } ;
900 
901 #endif
902 
903 
904 #ifdef BUILD_REMOTE_PLUGIN_CLIENT
905 
906 class RemotePluginClient : public RemotePluginBase
907 {
908 public:
909 #ifdef SYNC_WITH_SHM_FIFO
910 	RemotePluginClient( key_t _shm_in, key_t _shm_out );
911 #else
912 	RemotePluginClient( const char * socketPath );
913 #endif
914 	virtual ~RemotePluginClient();
915 #ifdef USE_QT_SHMEM
916 	VstSyncData * getQtVSTshm();
917 #endif
918 	virtual bool processMessage( const message & _m );
919 
920 	virtual void process( const sampleFrame * _in_buf,
921 					sampleFrame * _out_buf ) = 0;
922 
processMidiEvent(const MidiEvent &,const f_cnt_t)923 	virtual void processMidiEvent( const MidiEvent&, const f_cnt_t /* _offset */ )
924 	{
925 	}
926 
sharedMemory()927 	inline float * sharedMemory()
928 	{
929 		return m_shm;
930 	}
931 
updateSampleRate()932 	virtual void updateSampleRate()
933 	{
934 	}
935 
updateBufferSize()936 	virtual void updateBufferSize()
937 	{
938 	}
939 
sampleRate()940 	inline sample_rate_t sampleRate() const
941 	{
942 		return m_sampleRate;
943 	}
944 
bufferSize()945 	inline fpp_t bufferSize() const
946 	{
947 		return m_bufferSize;
948 	}
949 
setInputCount(int _i)950 	void setInputCount( int _i )
951 	{
952 		m_inputCount = _i;
953 		sendMessage( message( IdChangeInputCount ).addInt( _i ) );
954 	}
955 
setOutputCount(int _i)956 	void setOutputCount( int _i )
957 	{
958 		m_outputCount = _i;
959 		sendMessage( message( IdChangeOutputCount ).addInt( _i ) );
960 	}
961 
setInputOutputCount(int i,int o)962 	void setInputOutputCount( int i, int o )
963 	{
964 		m_inputCount = i;
965 		m_outputCount = o;
966 		sendMessage( message( IdChangeInputOutputCount )
967 				.addInt( i )
968 				.addInt( o ) );
969 	}
970 
inputCount()971 	virtual int inputCount() const
972 	{
973 		return m_inputCount;
974 	}
975 
outputCount()976 	virtual int outputCount() const
977 	{
978 		return m_outputCount;
979 	}
980 
debugMessage(const std::string & _s)981 	void debugMessage( const std::string & _s )
982 	{
983 		sendMessage( message( IdDebugMessage ).addString( _s ) );
984 	}
985 
986 
987 private:
988 	void setShmKey( key_t _key, int _size );
989 	void doProcessing();
990 
991 #ifdef USE_QT_SHMEM
992 	QSharedMemory m_shmObj;
993 	QSharedMemory m_shmQtID;
994 #endif
995 	VstSyncData * m_vstSyncData;
996 	float * m_shm;
997 
998 	int m_inputCount;
999 	int m_outputCount;
1000 
1001 	sample_rate_t m_sampleRate;
1002 	fpp_t m_bufferSize;
1003 
1004 } ;
1005 
1006 #endif
1007 
1008 
1009 
1010 
1011 
1012 #ifdef COMPILE_REMOTE_PLUGIN_BASE
1013 
1014 #ifndef BUILD_REMOTE_PLUGIN_CLIENT
1015 #include <QtCore/QCoreApplication>
1016 #endif
1017 
1018 
1019 #ifdef SYNC_WITH_SHM_FIFO
RemotePluginBase(shmFifo * _in,shmFifo * _out)1020 RemotePluginBase::RemotePluginBase( shmFifo * _in, shmFifo * _out ) :
1021 	m_in( _in ),
1022 	m_out( _out )
1023 #else
1024 RemotePluginBase::RemotePluginBase() :
1025 	m_socket( -1 ),
1026 	m_invalid( false )
1027 #endif
1028 {
1029 #ifdef LMMS_HAVE_LOCALE_H
1030 	// make sure, we're using common ways to print/scan
1031 	// floats to/from strings (',' vs. '.' for decimal point etc.)
1032 	setlocale( LC_NUMERIC, "C" );
1033 #endif
1034 #ifndef SYNC_WITH_SHM_FIFO
1035 	pthread_mutex_init( &m_receiveMutex, NULL );
1036 	pthread_mutex_init( &m_sendMutex, NULL );
1037 #endif
1038 }
1039 
1040 
1041 
1042 
~RemotePluginBase()1043 RemotePluginBase::~RemotePluginBase()
1044 {
1045 #ifdef SYNC_WITH_SHM_FIFO
1046 	delete m_in;
1047 	delete m_out;
1048 #else
1049 	pthread_mutex_destroy( &m_receiveMutex );
1050 	pthread_mutex_destroy( &m_sendMutex );
1051 #endif
1052 }
1053 
1054 
1055 
1056 
sendMessage(const message & _m)1057 int RemotePluginBase::sendMessage( const message & _m )
1058 {
1059 #ifdef SYNC_WITH_SHM_FIFO
1060 	m_out->lock();
1061 	m_out->writeInt( _m.id );
1062 	m_out->writeInt( _m.data.size() );
1063 	int j = 8;
1064 	for( unsigned int i = 0; i < _m.data.size(); ++i )
1065 	{
1066 		m_out->writeString( _m.data[i] );
1067 		j += 4 + _m.data[i].size();
1068 	}
1069 	m_out->unlock();
1070 	m_out->messageSent();
1071 #else
1072 	pthread_mutex_lock( &m_sendMutex );
1073 	writeInt( _m.id );
1074 	writeInt( _m.data.size() );
1075 	int j = 8;
1076 	for( unsigned int i = 0; i < _m.data.size(); ++i )
1077 	{
1078 		writeString( _m.data[i] );
1079 		j += 4 + _m.data[i].size();
1080 	}
1081 	pthread_mutex_unlock( &m_sendMutex );
1082 #endif
1083 
1084 	return j;
1085 }
1086 
1087 
1088 
1089 
receiveMessage()1090 RemotePluginBase::message RemotePluginBase::receiveMessage()
1091 {
1092 #ifdef SYNC_WITH_SHM_FIFO
1093 	m_in->waitForMessage();
1094 	m_in->lock();
1095 	message m;
1096 	m.id = m_in->readInt();
1097 	const int s = m_in->readInt();
1098 	for( int i = 0; i < s; ++i )
1099 	{
1100 		m.data.push_back( m_in->readString() );
1101 	}
1102 	m_in->unlock();
1103 #else
1104 	pthread_mutex_lock( &m_receiveMutex );
1105 	message m;
1106 	m.id = readInt();
1107 	const int s = readInt();
1108 	for( int i = 0; i < s; ++i )
1109 	{
1110 		m.data.push_back( readString() );
1111 	}
1112 	pthread_mutex_unlock( &m_receiveMutex );
1113 #endif
1114 	return m;
1115 }
1116 
1117 
1118 
1119 
waitForMessage(const message & _wm,bool _busy_waiting)1120 RemotePluginBase::message RemotePluginBase::waitForMessage(
1121 							const message & _wm,
1122 							bool _busy_waiting )
1123 {
1124 #ifndef BUILD_REMOTE_PLUGIN_CLIENT
1125 	if( _busy_waiting )
1126 	{
1127 		// No point processing events outside of the main thread
1128 		_busy_waiting = QThread::currentThread() ==
1129 					QCoreApplication::instance()->thread();
1130 	}
1131 
1132 	struct WaitDepthCounter
1133 	{
1134 		WaitDepthCounter( int & depth, bool busy ) :
1135 			m_depth( depth ),
1136 			m_busy( busy )
1137 		{
1138 			if( m_busy ) { ++m_depth; }
1139 		}
1140 
1141 		~WaitDepthCounter()
1142 		{
1143 			if( m_busy ) { --m_depth; }
1144 		}
1145 
1146 		int & m_depth;
1147 		bool m_busy;
1148 	};
1149 
1150 	WaitDepthCounter wdc( waitDepthCounter(), _busy_waiting );
1151 #endif
1152 	while( !isInvalid() )
1153 	{
1154 #ifndef BUILD_REMOTE_PLUGIN_CLIENT
1155 		if( _busy_waiting && !messagesLeft() )
1156 		{
1157 			QCoreApplication::processEvents(
1158 				QEventLoop::ExcludeUserInputEvents, 50 );
1159 			continue;
1160 		}
1161 #endif
1162 		message m = receiveMessage();
1163 		processMessage( m );
1164 		if( m.id == _wm.id )
1165 		{
1166 			return m;
1167 		}
1168 		else if( m.id == IdUndefined )
1169 		{
1170 			return m;
1171 		}
1172 	}
1173 
1174 	return message();
1175 }
1176 
1177 
1178 #endif
1179 
1180 
1181 
1182 
1183 
1184 #ifdef BUILD_REMOTE_PLUGIN_CLIENT
1185 
1186 
1187 #ifdef SYNC_WITH_SHM_FIFO
RemotePluginClient(key_t _shm_in,key_t _shm_out)1188 RemotePluginClient::RemotePluginClient( key_t _shm_in, key_t _shm_out ) :
1189 	RemotePluginBase( new shmFifo( _shm_in ), new shmFifo( _shm_out ) ),
1190 #else
1191 RemotePluginClient::RemotePluginClient( const char * socketPath ) :
1192 	RemotePluginBase(),
1193 #endif
1194 #ifdef USE_QT_SHMEM
1195 	m_shmObj(),
1196 	m_shmQtID( "/usr/bin/lmms" ),
1197 #endif
1198 	m_vstSyncData( NULL ),
1199 	m_shm( NULL ),
1200 	m_inputCount( 0 ),
1201 	m_outputCount( 0 ),
1202 	m_sampleRate( 44100 ),
1203 	m_bufferSize( 0 )
1204 {
1205 #ifndef SYNC_WITH_SHM_FIFO
1206 	struct sockaddr_un sa;
1207 	sa.sun_family = AF_LOCAL;
1208 
1209 	size_t length = strlen( socketPath );
1210 	if ( length >= sizeof sa.sun_path )
1211 	{
1212 		length = sizeof sa.sun_path - 1;
1213 		fprintf( stderr, "Socket path too long.\n" );
1214 	}
1215 	memcpy( sa.sun_path, socketPath, length );
1216 	sa.sun_path[length] = '\0';
1217 
1218 	m_socket = socket( PF_LOCAL, SOCK_STREAM, 0 );
1219 	if ( m_socket == -1 )
1220 	{
1221 		fprintf( stderr, "Could not connect to local server.\n" );
1222 	}
1223 	if ( ::connect( m_socket, (struct sockaddr *) &sa, sizeof sa ) == -1 )
1224 	{
1225 		fprintf( stderr, "Could not connect to local server.\n" );
1226 	}
1227 #endif
1228 
1229 #ifdef USE_QT_SHMEM
1230 	if( m_shmQtID.attach( QSharedMemory::ReadOnly ) )
1231 	{
1232 		m_vstSyncData = (VstSyncData *) m_shmQtID.data();
1233 		m_bufferSize = m_vstSyncData->m_bufferSize;
1234 		m_sampleRate = m_vstSyncData->m_sampleRate;
1235 		sendMessage( IdHostInfoGotten );
1236 		return;
1237 	}
1238 #else
1239 	key_t key;
1240 	int m_shmID;
1241 
1242 	if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 )
1243 	{
1244 		perror( "RemotePluginClient::ftok" );
1245 	}
1246 	else
1247 	{	// connect to shared memory segment
1248 		if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 )
1249 		{
1250 			perror( "RemotePluginClient::shmget" );
1251 		}
1252 		else
1253 		{	// attach segment
1254 			m_vstSyncData = (VstSyncData *)shmat(m_shmID, 0, 0);
1255 			if( m_vstSyncData == (VstSyncData *)( -1 ) )
1256 			{
1257 				perror( "RemotePluginClient::shmat" );
1258 			}
1259 			else
1260 			{
1261 				m_bufferSize = m_vstSyncData->m_bufferSize;
1262 				m_sampleRate = m_vstSyncData->m_sampleRate;
1263 				sendMessage( IdHostInfoGotten );
1264 
1265 				// detach segment
1266 				if( shmdt(m_vstSyncData) == -1 )
1267 				{
1268 					perror("RemotePluginClient::shmdt");
1269 				}
1270 				return;
1271 			}
1272 		}
1273 	}
1274 #endif
1275 
1276 	// if attaching shared memory fails
1277 	sendMessage( IdSampleRateInformation );
1278 	sendMessage( IdBufferSizeInformation );
1279 	if( waitForMessage( IdBufferSizeInformation ).id
1280 						!= IdBufferSizeInformation )
1281 	{
1282 		fprintf( stderr, "Could not get buffer size information\n" );
1283 	}
1284 	sendMessage( IdHostInfoGotten );
1285 }
1286 
1287 
1288 
1289 
~RemotePluginClient()1290 RemotePluginClient::~RemotePluginClient()
1291 {
1292 #ifdef USE_QT_SHMEM
1293 	m_shmQtID.detach();
1294 #endif
1295 	sendMessage( IdQuit );
1296 
1297 #ifndef USE_QT_SHMEM
1298 	shmdt( m_shm );
1299 #endif
1300 
1301 #ifndef SYNC_WITH_SHM_FIFO
1302 	if ( close( m_socket ) == -1)
1303 	{
1304 		fprintf( stderr, "Error freeing resources.\n" );
1305 	}
1306 #endif
1307 }
1308 
1309 
1310 
1311 #ifdef USE_QT_SHMEM
getQtVSTshm()1312 VstSyncData * RemotePluginClient::getQtVSTshm()
1313 {
1314 	return m_vstSyncData;
1315 }
1316 #endif
1317 
1318 
1319 
processMessage(const message & _m)1320 bool RemotePluginClient::processMessage( const message & _m )
1321 {
1322 	message reply_message( _m.id );
1323 	bool reply = false;
1324 	switch( _m.id )
1325 	{
1326 		case IdUndefined:
1327 			return false;
1328 
1329 		case IdSampleRateInformation:
1330 			m_sampleRate = _m.getInt();
1331 			updateSampleRate();
1332 			reply_message.id = IdInformationUpdated;
1333 			reply = true;
1334 			break;
1335 
1336 		case IdBufferSizeInformation:
1337 			// Should LMMS gain the ability to change buffer size
1338 			// without a restart, it must wait for this message to
1339 			// complete processing or else risk VST crashes
1340 			m_bufferSize = _m.getInt();
1341 			updateBufferSize();
1342 			break;
1343 
1344 		case IdQuit:
1345 			return false;
1346 
1347 		case IdMidiEvent:
1348 			processMidiEvent(
1349 				MidiEvent( static_cast<MidiEventTypes>(
1350 							_m.getInt( 0 ) ),
1351 						_m.getInt( 1 ),
1352 						_m.getInt( 2 ),
1353 						_m.getInt( 3 ) ),
1354 							_m.getInt( 4 ) );
1355 			break;
1356 
1357 		case IdStartProcessing:
1358 			doProcessing();
1359 			reply_message.id = IdProcessingDone;
1360 			reply = true;
1361 			break;
1362 
1363 		case IdChangeSharedMemoryKey:
1364 			setShmKey( _m.getInt( 0 ), _m.getInt( 1 ) );
1365 			break;
1366 
1367 		case IdInitDone:
1368 			break;
1369 
1370 		default:
1371 		{
1372 			char buf[64];
1373 			sprintf( buf, "undefined message: %d\n", (int) _m.id );
1374 			debugMessage( buf );
1375 			break;
1376 		}
1377 	}
1378 	if( reply )
1379 	{
1380 		sendMessage( reply_message );
1381 	}
1382 
1383 	return true;
1384 }
1385 
1386 
1387 
1388 
setShmKey(key_t _key,int _size)1389 void RemotePluginClient::setShmKey( key_t _key, int _size )
1390 {
1391 #ifdef USE_QT_SHMEM
1392 	m_shmObj.setKey( QString::number( _key ) );
1393 	if( m_shmObj.attach() || m_shmObj.error() == QSharedMemory::NoError )
1394 	{
1395 		m_shm = (float *) m_shmObj.data();
1396 	}
1397 	else
1398 	{
1399 		char buf[64];
1400 		sprintf( buf, "failed getting shared memory: %d\n", m_shmObj.error() );
1401 		debugMessage( buf );
1402 	}
1403 #else
1404 	if( m_shm != NULL )
1405 	{
1406 		shmdt( m_shm );
1407 		m_shm = NULL;
1408 	}
1409 
1410 	// only called for detaching SHM?
1411 	if( _key == 0 )
1412 	{
1413 		return;
1414 	}
1415 
1416 	int shm_id = shmget( _key, _size, 0 );
1417 	if( shm_id == -1 )
1418 	{
1419 		debugMessage( "failed getting shared memory\n" );
1420 	}
1421 	else
1422 	{
1423 		m_shm = (float *) shmat( shm_id, 0, 0 );
1424 	}
1425 #endif
1426 }
1427 
1428 
1429 
1430 
doProcessing()1431 void RemotePluginClient::doProcessing()
1432 {
1433 	if( m_shm != NULL )
1434 	{
1435 		process( (sampleFrame *)( m_inputCount > 0 ? m_shm : NULL ),
1436 				(sampleFrame *)( m_shm +
1437 					( m_inputCount*m_bufferSize ) ) );
1438 	}
1439 	else
1440 	{
1441 		debugMessage( "doProcessing(): have no shared memory!\n" );
1442 	}
1443 }
1444 
1445 
1446 
1447 #endif
1448 
1449 #define QSTR_TO_STDSTR(s)	std::string( s.toUtf8().constData() )
1450 
1451 #endif
1452