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