1 /*
2 * RemoteVstPlugin.cpp - LMMS VST Support Layer (RemotePlugin client)
3 *
4 * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5 *
6 * This file is part of LMMS - https://lmms.io
7 *
8 * Code partly taken from (X)FST:
9 * Copyright (c) 2004 Paul Davis
10 * Copyright (c) 2004 Torben Hohn
11 * Copyright (c) 2002 Kjetil S. Matheussen
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public
24 * License along with this program (see COPYING); if not, write to the
25 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301 USA.
27 *
28 */
29
30
31 #include "lmmsconfig.h"
32
33 #define BUILD_REMOTE_PLUGIN_CLIENT
34
35 #include "RemotePlugin.h"
36
37 #ifdef LMMS_HAVE_PTHREAD_H
38 #include <pthread.h>
39 #endif
40
41 #ifdef LMMS_HAVE_FCNTL_H
42 #include <fcntl.h>
43 #endif
44
45 #ifdef LMMS_BUILD_LINUX
46
47 #ifndef NOMINMAX
48 #define NOMINMAX
49 #endif
50
51 #ifndef O_BINARY
52 #define O_BINARY 0
53 #endif
54
55 #ifdef LMMS_HAVE_SCHED_H
56 #include <sched.h>
57 #endif
58
59 #include <wine/exception.h>
60
61 #endif
62
63 #define USE_WS_PREFIX
64 #include <windows.h>
65
66 #include <algorithm>
67 #include <vector>
68 #include <queue>
69 #include <string>
70 #include <iostream>
71
72 #include <aeffectx.h>
73
74 #if kVstVersion < 2400
75
76 #define OLD_VST_SDK
77
78 struct ERect
79 {
80 short top;
81 short left;
82 short bottom;
83 short right;
84 } ;
85
86 #endif
87
88
89 #include "lmms_basics.h"
90 #include "Midi.h"
91 #include "communication.h"
92 #include "IoHelper.h"
93
94 #include "VstSyncData.h"
95
96 #ifdef LMMS_BUILD_WIN32
97 #define USE_QT_SHMEM
98 #endif
99
100 #ifndef USE_QT_SHMEM
101 #include <stdio.h>
102 #include <stdlib.h>
103 #include <sys/types.h>
104 #include <sys/ipc.h>
105 #include <sys/shm.h>
106 #endif
107
108 using namespace std;
109
110 static VstHostLanguages hlang = LanguageEnglish;
111
112 static bool EMBED = false;
113 static bool EMBED_X11 = false;
114 static bool EMBED_WIN32 = false;
115 static bool HEADLESS = false;
116
117 class RemoteVstPlugin;
118
119 RemoteVstPlugin * __plugin = NULL;
120
121 HWND __MessageHwnd = NULL;
122 DWORD __processingThreadId = 0;
123
124
125
126 class RemoteVstPlugin : public RemotePluginClient
127 {
128 public:
129 #ifdef SYNC_WITH_SHM_FIFO
130 RemoteVstPlugin( key_t _shm_in, key_t _shm_out );
131 #else
132 RemoteVstPlugin( const char * socketPath );
133 #endif
134 virtual ~RemoteVstPlugin();
135
136 virtual bool processMessage( const message & _m );
137
138 void init( const std::string & _plugin_file );
139 void initEditor();
140 void showEditor();
141 void hideEditor();
142 void destroyEditor();
143
144 virtual void process( const sampleFrame * _in, sampleFrame * _out );
145
146
147 virtual void processMidiEvent( const MidiEvent& event, const f_cnt_t offset );
148
149 // set given sample-rate for plugin
updateSampleRate()150 virtual void updateSampleRate()
151 {
152 SuspendPlugin suspend( this );
153 pluginDispatch( effSetSampleRate, 0, 0,
154 NULL, (float) sampleRate() );
155 }
156
157 // set given buffer-size for plugin
updateBufferSize()158 virtual void updateBufferSize()
159 {
160 SuspendPlugin suspend( this );
161 pluginDispatch( effSetBlockSize, 0, bufferSize() );
162 }
163
setResumed(bool resumed)164 void setResumed( bool resumed )
165 {
166 m_resumed = resumed;
167 pluginDispatch( effMainsChanged, 0, resumed ? 1 : 0 );
168 }
169
isResumed() const170 inline bool isResumed() const
171 {
172 return m_resumed;
173 }
174
isInitialized() const175 inline bool isInitialized() const
176 {
177 return m_initialized;
178 }
179
180
181 // set given tempo
setBPM(const bpm_t _bpm)182 void setBPM( const bpm_t _bpm )
183 {
184 m_bpm = _bpm;
185 }
186
187 // determine VST-version the plugin uses
pluginVersion()188 inline int pluginVersion()
189 {
190 return pluginDispatch( effGetVendorVersion );
191 }
192
193 // determine name of plugin
194 const char * pluginName();
195
196 // determine vendor of plugin
197 const char * pluginVendorString();
198
199 // determine product-string of plugin
200 const char * pluginProductString();
201
202 // determine name of current program
203 const char * programName();
204
205 // send name of current program back to host
206 void sendCurrentProgramName();
207
208 // do a complete parameter-dump and post it
209 void getParameterDump();
210
211 // read parameter-dump and set it for plugin
212 void setParameterDump( const message & _m );
213
214 // save settings chunk of plugin into file
215 void saveChunkToFile( const std::string & _file );
216
217 // restore settings chunk of plugin from file
218 void loadChunkFromFile( const std::string & _file, int _len );
219
220 // restore settings chunk of plugin from file
221 void loadPresetFile( const std::string & _file );
222
223 // sets given program index
224 void setProgram( int index );
225
226 // rotate current program by given offset
227 void rotateProgram( int offset );
228
229 // Load names of presets/programs
230 void getProgramNames();
231
232 // Save presets/programs
233 void savePreset( const std::string & _file );
234
235 // number of inputs
inputCount() const236 virtual int inputCount() const
237 {
238 if( m_plugin )
239 {
240 return m_plugin->numInputs;
241 }
242 return 0;
243 }
244
245 // number of outputs
outputCount() const246 virtual int outputCount() const
247 {
248 if( m_plugin )
249 {
250 return m_plugin->numOutputs;
251 }
252 return 0;
253 }
254
255 // has to be called as soon as input- or output-count changes
256 int updateInOutCount();
257
lockShm()258 inline void lockShm()
259 {
260 pthread_mutex_lock( &m_shmLock );
261 }
262
tryLockShm()263 inline bool tryLockShm()
264 {
265 return pthread_mutex_trylock( &m_shmLock ) == 0;
266 }
267
unlockShm()268 inline void unlockShm()
269 {
270 pthread_mutex_unlock( &m_shmLock );
271 }
272
isShmValid()273 inline bool isShmValid()
274 {
275 return m_shmValid;
276 }
277
setShmIsValid(bool valid)278 inline void setShmIsValid( bool valid )
279 {
280 m_shmValid = valid;
281 }
282
isProcessing() const283 inline bool isProcessing() const
284 {
285 return m_processing;
286 }
287
setProcessing(bool processing)288 inline void setProcessing( bool processing )
289 {
290 m_processing = processing;
291 }
292
queueMessage(const message & m)293 inline void queueMessage( const message & m ) {
294 m_messageList.push( m );
295 }
296
shouldGiveIdle() const297 inline bool shouldGiveIdle() const
298 {
299 return m_shouldGiveIdle;
300 }
301
setShouldGiveIdle(bool shouldGiveIdle)302 inline void setShouldGiveIdle( bool shouldGiveIdle )
303 {
304 m_shouldGiveIdle = shouldGiveIdle;
305 }
306
307 void idle();
308 void processUIThreadMessages();
309
310 static DWORD WINAPI processingThread( LPVOID _param );
311 static bool setupMessageWindow();
312 static DWORD WINAPI guiEventLoop();
313 static LRESULT CALLBACK wndProc( HWND hwnd, UINT uMsg,
314 WPARAM wParam, LPARAM lParam );
315
316
317 private:
318 enum GuiThreadMessages
319 {
320 None,
321 ProcessPluginMessage,
322 GiveIdle,
323 ClosePlugin
324 } ;
325
326 struct SuspendPlugin {
SuspendPluginRemoteVstPlugin::SuspendPlugin327 SuspendPlugin( RemoteVstPlugin * plugin ) :
328 m_plugin( plugin ),
329 m_resumed( plugin->isResumed() )
330 {
331 if( m_resumed ) { m_plugin->setResumed( false ); }
332 }
333
~SuspendPluginRemoteVstPlugin::SuspendPlugin334 ~SuspendPlugin()
335 {
336 if( m_resumed ) { m_plugin->setResumed( true ); }
337 }
338
339 private:
340 RemoteVstPlugin * m_plugin;
341 bool m_resumed;
342 };
343
344 // callback used by plugin for being able to communicate with it's host
345 static intptr_t hostCallback( AEffect * _effect, int32_t _opcode,
346 int32_t _index, intptr_t _value,
347 void * _ptr, float _opt );
348
349
350 bool load( const std::string & _plugin_file );
351
pluginDispatch(int cmd,int param1=0,int param2=0,void * p=NULL,float f=0)352 int pluginDispatch( int cmd, int param1 = 0, int param2 = 0,
353 void * p = NULL, float f = 0 )
354 {
355 if( m_plugin )
356 {
357 return m_plugin->dispatcher( m_plugin, cmd, param1, param2, p, f );
358 }
359 return 0;
360 }
361
362
363 std::string m_shortName;
364
365 HINSTANCE m_libInst;
366
367 AEffect * m_plugin;
368 HWND m_window;
369 intptr_t m_windowID;
370 int m_windowWidth;
371 int m_windowHeight;
372
373 bool m_initialized;
374 bool m_resumed;
375
376 bool m_processing;
377
378 std::queue<message> m_messageList;
379 bool m_shouldGiveIdle;
380
381
382 float * * m_inputs;
383 float * * m_outputs;
384
385 pthread_mutex_t m_shmLock;
386 bool m_shmValid;
387
388 typedef std::vector<VstMidiEvent> VstMidiEventList;
389 VstMidiEventList m_midiEvents;
390
391 bpm_t m_bpm;
392 double m_currentSamplePos;
393 int m_currentProgram;
394
395 // host to plugin synchronisation data structure
396 struct in
397 {
398 double lastppqPos;
399 double m_Timestamp;
400 int32_t m_lastFlags;
401 } ;
402
403 in * m_in;
404
405 int m_shmID;
406 VstSyncData* m_vstSyncData;
407
408 } ;
409
410
411
412
413 #ifdef SYNC_WITH_SHM_FIFO
RemoteVstPlugin(key_t _shm_in,key_t _shm_out)414 RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) :
415 RemotePluginClient( _shm_in, _shm_out ),
416 #else
417 RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) :
418 RemotePluginClient( socketPath ),
419 #endif
420 m_libInst( NULL ),
421 m_plugin( NULL ),
422 m_window( NULL ),
423 m_windowID( 0 ),
424 m_windowWidth( 0 ),
425 m_windowHeight( 0 ),
426 m_initialized( false ),
427 m_resumed( false ),
428 m_processing( false ),
429 m_messageList(),
430 m_shouldGiveIdle( false ),
431 m_inputs( NULL ),
432 m_outputs( NULL ),
433 m_shmLock(),
434 m_shmValid( false ),
435 m_midiEvents(),
436 m_bpm( 0 ),
437 m_currentSamplePos( 0 ),
438 m_currentProgram( -1 ),
439 m_in( NULL ),
440 m_shmID( -1 ),
441 m_vstSyncData( NULL )
442 {
443 pthread_mutex_init( &m_shmLock, NULL );
444
445 __plugin = this;
446
447 #ifndef USE_QT_SHMEM
448 key_t key;
449 if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 )
450 {
451 perror( "RemoteVstPlugin.cpp::ftok" );
452 }
453 else
454 { // connect to shared memory segment
455 if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 )
456 {
457 perror( "RemoteVstPlugin.cpp::shmget" );
458 }
459 else
460 { // attach segment
461 m_vstSyncData = (VstSyncData *)shmat(m_shmID, 0, 0);
462 if( m_vstSyncData == (VstSyncData *)( -1 ) )
463 {
464 perror( "RemoteVstPlugin.cpp::shmat" );
465 }
466 }
467 }
468 #else
469 m_vstSyncData = RemotePluginClient::getQtVSTshm();
470 #endif
471 if( m_vstSyncData == NULL )
472 {
473 fprintf(stderr, "RemoteVstPlugin.cpp: "
474 "Failed to initialize shared memory for VST synchronization.\n"
475 " (VST-host synchronization will be disabled)\n");
476 m_vstSyncData = (VstSyncData*) malloc( sizeof( VstSyncData ) );
477 m_vstSyncData->isPlaying = true;
478 m_vstSyncData->timeSigNumer = 4;
479 m_vstSyncData->timeSigDenom = 4;
480 m_vstSyncData->ppqPos = 0;
481 m_vstSyncData->isCycle = false;
482 m_vstSyncData->hasSHM = false;
483 m_vstSyncData->m_playbackJumped = false;
484 m_vstSyncData->m_sampleRate = sampleRate();
485 }
486
487 m_in = ( in* ) new char[ sizeof( in ) ];
488 m_in->lastppqPos = 0;
489 m_in->m_Timestamp = -1;
490 m_in->m_lastFlags = 0;
491
492 // process until we have loaded the plugin
493 while( 1 )
494 {
495 message m = receiveMessage();
496 processMessage( m );
497 if( m.id == IdVstLoadPlugin || m.id == IdQuit )
498 {
499 break;
500 }
501 }
502 }
503
504
505
506
~RemoteVstPlugin()507 RemoteVstPlugin::~RemoteVstPlugin()
508 {
509 destroyEditor();
510 setResumed( false );
511 pluginDispatch( effClose );
512 #ifndef USE_QT_SHMEM
513 // detach shared memory segment
514 if( shmdt( m_vstSyncData ) == -1)
515 {
516 if( __plugin->m_vstSyncData->hasSHM )
517 {
518 perror( "~RemoteVstPlugin::shmdt" );
519 }
520 if( m_vstSyncData != NULL )
521 {
522 delete m_vstSyncData;
523 m_vstSyncData = NULL;
524 }
525 }
526 #endif
527
528 if( m_libInst != NULL )
529 {
530 FreeLibrary( m_libInst );
531 m_libInst = NULL;
532 }
533
534 delete[] m_inputs;
535 delete[] m_outputs;
536
537 pthread_mutex_destroy( &m_shmLock );
538 }
539
540
541
542
processMessage(const message & _m)543 bool RemoteVstPlugin::processMessage( const message & _m )
544 {
545 if (! EMBED)
546 {
547 switch( _m.id )
548 {
549 case IdShowUI:
550 showEditor();
551 return true;
552
553 case IdHideUI:
554 hideEditor();
555 return true;
556
557 case IdToggleUI:
558 if( m_window && IsWindowVisible( m_window ) )
559 {
560 hideEditor();
561 }
562 else
563 {
564 showEditor();
565 }
566 return true;
567
568 case IdIsUIVisible:
569 bool visible = m_window && IsWindowVisible( m_window );
570 sendMessage( message( IdIsUIVisible )
571 .addInt( visible ? 1 : 0 ) );
572 return true;
573 }
574 }
575 else if (EMBED && _m.id == IdShowUI)
576 {
577 ShowWindow( m_window, SW_SHOWNORMAL );
578 UpdateWindow( m_window );
579 return true;
580 }
581
582 switch( _m.id )
583 {
584 case IdVstLoadPlugin:
585 init( _m.getString() );
586 break;
587
588 case IdVstSetTempo:
589 setBPM( _m.getInt() );
590 break;
591
592 case IdVstSetLanguage:
593 hlang = static_cast<VstHostLanguages>( _m.getInt() );
594 break;
595
596 case IdVstGetParameterDump:
597 getParameterDump();
598 break;
599
600 case IdVstSetParameterDump:
601 setParameterDump( _m );
602 break;
603
604 case IdSaveSettingsToFile:
605 saveChunkToFile( _m.getString() );
606 sendMessage( IdSaveSettingsToFile );
607 break;
608
609 case IdLoadSettingsFromFile:
610 loadChunkFromFile( _m.getString( 0 ), _m.getInt( 1 ) );
611 sendMessage( IdLoadSettingsFromFile );
612 break;
613
614 case IdLoadPresetFile:
615 loadPresetFile( _m.getString( 0 ) );
616 sendMessage( IdLoadPresetFile );
617 break;
618
619 case IdVstSetProgram:
620 setProgram( _m.getInt( 0 ) );
621 sendMessage( IdVstSetProgram );
622 break;
623
624 case IdVstCurrentProgram:
625 sendMessage( message( IdVstCurrentProgram ).addInt( m_currentProgram ) );
626 break;
627
628 case IdVstRotateProgram:
629 rotateProgram( _m.getInt( 0 ) );
630 sendMessage( IdVstRotateProgram );
631 break;
632
633 case IdVstProgramNames:
634 getProgramNames();
635 break;
636
637 case IdSavePresetFile:
638 savePreset( _m.getString( 0 ) );
639 sendMessage( IdSavePresetFile );
640 break;
641
642 case IdVstSetParameter:
643 m_plugin->setParameter( m_plugin, _m.getInt( 0 ), _m.getFloat( 1 ) );
644 //sendMessage( IdVstSetParameter );
645 break;
646
647
648 case IdVstIdleUpdate:
649 {
650 int newCurrentProgram = pluginDispatch( effGetProgram );
651 if( newCurrentProgram != m_currentProgram )
652 {
653 m_currentProgram = newCurrentProgram;
654 sendCurrentProgramName();
655 }
656
657 break;
658 }
659
660 default:
661 return RemotePluginClient::processMessage( _m );
662 }
663 return true;
664 }
665
666
667
668
init(const std::string & _plugin_file)669 void RemoteVstPlugin::init( const std::string & _plugin_file )
670 {
671 if( load( _plugin_file ) == false )
672 {
673 sendMessage( IdVstFailedLoadingPlugin );
674 return;
675 }
676
677 updateInOutCount();
678 updateBufferSize();
679 updateSampleRate();
680
681 /* set program to zero */
682 /* i comment this out because it breaks dfx Geometer
683 * looks like we cant set programs for it
684 *
685 pluginDispatch( effSetProgram, 0, 0 ); */
686 // request rate and blocksize
687
688 setResumed( true );
689
690 debugMessage( "creating editor\n" );
691 initEditor();
692 debugMessage( "editor successfully created\n" );
693
694
695 // now post some information about our plugin
696 sendMessage( message( IdVstPluginWindowID ).addInt( m_windowID ) );
697
698 sendMessage( message( IdVstPluginEditorGeometry ).
699 addInt( m_windowWidth ).
700 addInt( m_windowHeight ) );
701
702 sendMessage( message( IdVstPluginName ).addString( pluginName() ) );
703 sendMessage( message( IdVstPluginVersion ).addInt( pluginVersion() ) );
704 sendMessage( message( IdVstPluginVendorString ).
705 addString( pluginVendorString() ) );
706 sendMessage( message( IdVstPluginProductString ).
707 addString( pluginProductString() ) );
708 sendMessage( message( IdVstParameterCount ).
709 addInt( m_plugin->numParams ) );
710
711 sendMessage( IdInitDone );
712
713 m_initialized = true;
714 }
715
716
717
718
close_check(FILE * fp)719 static void close_check( FILE* fp )
720 {
721 if (!fp) {return;}
722 if( fclose( fp ) )
723 {
724 perror( "close" );
725 }
726 }
727
728
729
730
initEditor()731 void RemoteVstPlugin::initEditor()
732 {
733 if( HEADLESS || m_window || !( m_plugin->flags & effFlagsHasEditor ) )
734 {
735 return;
736 }
737
738
739 HMODULE hInst = GetModuleHandle( NULL );
740 if( hInst == NULL )
741 {
742 debugMessage( "initEditor(): can't get module handle\n" );
743 return;
744 }
745
746
747 DWORD dwStyle;
748 if (EMBED) {
749 dwStyle = WS_POPUP | WS_SYSMENU | WS_BORDER;
750 } else {
751 dwStyle = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX;
752 }
753
754 m_window = CreateWindowEx( WS_EX_APPWINDOW, "LVSL", pluginName(),
755 dwStyle,
756 0, 0, 10, 10, NULL, NULL, hInst, NULL );
757 if( m_window == NULL )
758 {
759 debugMessage( "initEditor(): cannot create editor window\n" );
760 return;
761 }
762
763
764 pluginDispatch( effEditOpen, 0, 0, m_window );
765
766 ERect * er;
767 pluginDispatch( effEditGetRect, 0, 0, &er );
768
769 m_windowWidth = er->right - er->left;
770 m_windowHeight = er->bottom - er->top;
771
772 RECT windowSize = { 0, 0, m_windowWidth, m_windowHeight };
773 AdjustWindowRect( &windowSize, dwStyle, false );
774 SetWindowPos( m_window, 0, 0, 0, windowSize.right - windowSize.left,
775 windowSize.bottom - windowSize.top, SWP_NOACTIVATE |
776 SWP_NOMOVE | SWP_NOZORDER );
777 pluginDispatch( effEditTop );
778
779 #ifdef LMMS_BUILD_LINUX
780 m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" );
781 #else
782 // 64-bit versions of Windows use 32-bit handles for interoperability
783 m_windowID = (intptr_t) m_window;
784 #endif
785 }
786
787
788
789
showEditor()790 void RemoteVstPlugin::showEditor() {
791 if( !EMBED && !HEADLESS && m_window )
792 {
793 ShowWindow( m_window, SW_SHOWNORMAL );
794 }
795 }
796
797
798
799
hideEditor()800 void RemoteVstPlugin::hideEditor() {
801 if( !EMBED && !HEADLESS && m_window )
802 {
803 ShowWindow( m_window, SW_HIDE );
804 }
805 }
806
807
808
809
destroyEditor()810 void RemoteVstPlugin::destroyEditor()
811 {
812 if( m_window == NULL )
813 {
814 return;
815 }
816
817 pluginDispatch( effEditClose );
818 // Destroying the window takes some time in Wine 1.8.5
819 DestroyWindow( m_window );
820 m_window = NULL;
821 }
822
823
824
825
load(const std::string & _plugin_file)826 bool RemoteVstPlugin::load( const std::string & _plugin_file )
827 {
828 if( ( m_libInst = LoadLibraryW( toWString(_plugin_file).c_str() ) ) == NULL )
829 {
830 // give VstPlugin class a chance to start 32 bit version of RemoteVstPlugin
831 if( GetLastError() == ERROR_BAD_EXE_FORMAT )
832 {
833 sendMessage( IdVstBadDllFormat );
834 }
835 return false;
836 }
837
838 typedef AEffect * ( __stdcall * mainEntryPointer )
839 ( audioMasterCallback );
840 mainEntryPointer mainEntry = (mainEntryPointer)
841 GetProcAddress( m_libInst, "VSTPluginMain" );
842 if( mainEntry == NULL )
843 {
844 mainEntry = (mainEntryPointer)
845 GetProcAddress( m_libInst, "VstPluginMain" );
846 }
847 if( mainEntry == NULL )
848 {
849 mainEntry = (mainEntryPointer)
850 GetProcAddress( m_libInst, "main" );
851 }
852 if( mainEntry == NULL )
853 {
854 debugMessage( "could not find entry point\n" );
855 return false;
856 }
857
858 m_plugin = mainEntry( hostCallback );
859 if( m_plugin == NULL )
860 {
861 debugMessage( "mainEntry procedure returned NULL\n" );
862 return false;
863 }
864
865 if( m_plugin->magic != kEffectMagic )
866 {
867 debugMessage( "File is not a VST plugin\n" );
868 return false;
869 }
870
871
872 char id[5];
873 sprintf( id, "%c%c%c%c", ((char *)&m_plugin->uniqueID)[3],
874 ((char *)&m_plugin->uniqueID)[2],
875 ((char *)&m_plugin->uniqueID)[1],
876 ((char *)&m_plugin->uniqueID)[0] );
877 id[4] = 0;
878 sendMessage( message( IdVstPluginUniqueID ).addString( id ) );
879
880 pluginDispatch( effOpen );
881
882 return true;
883 }
884
885
886
887
process(const sampleFrame * _in,sampleFrame * _out)888 void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out )
889 {
890 // first we gonna post all MIDI-events we enqueued so far
891 if( m_midiEvents.size() )
892 {
893 // since MIDI-events are not received immediately, we
894 // have to have them stored somewhere even after
895 // dispatcher-call, so we create static copies of the
896 // data and post them
897 #define MIDI_EVENT_BUFFER_COUNT 1024
898 static char eventsBuffer[sizeof( VstEvents ) + sizeof( VstMidiEvent * ) * MIDI_EVENT_BUFFER_COUNT];
899 static VstMidiEvent vme[MIDI_EVENT_BUFFER_COUNT];
900
901 // first sort events chronologically, since some plugins
902 // (e.g. Sinnah) can hang if they're out of order
903 std::stable_sort( m_midiEvents.begin(), m_midiEvents.end(),
904 []( const VstMidiEvent &a, const VstMidiEvent &b )
905 {
906 return a.deltaFrames < b.deltaFrames;
907 } );
908
909 VstEvents* events = (VstEvents *) eventsBuffer;
910 events->reserved = 0;
911 events->numEvents = m_midiEvents.size();
912
913 int idx = 0;
914 for( VstMidiEventList::iterator it = m_midiEvents.begin(); it != m_midiEvents.end(); ++it, ++idx )
915 {
916 memcpy( &vme[idx], &*it, sizeof( VstMidiEvent ) );
917 events->events[idx] = (VstEvent *) &vme[idx];
918 }
919
920 m_midiEvents.clear();
921 pluginDispatch( effProcessEvents, 0, 0, events );
922 }
923
924 // now we're ready to fetch sound from VST-plugin
925
926 if( !tryLockShm() )
927 {
928 return;
929 }
930
931 if( !isShmValid() )
932 {
933 unlockShm();
934 return;
935 }
936
937 for( int i = 0; i < inputCount(); ++i )
938 {
939 m_inputs[i] = &((float *) _in)[i * bufferSize()];
940 }
941
942 for( int i = 0; i < outputCount(); ++i )
943 {
944 m_outputs[i] = &((float *) _out)[i * bufferSize()];
945 memset( m_outputs[i], 0, bufferSize() * sizeof( float ) );
946 }
947
948 #ifdef OLD_VST_SDK
949 if( m_plugin->flags & effFlagsCanReplacing )
950 {
951 #endif
952 m_plugin->processReplacing( m_plugin, m_inputs, m_outputs,
953 bufferSize() );
954 #ifdef OLD_VST_SDK
955 }
956 else
957 {
958 m_plugin->process( m_plugin, m_inputs, m_outputs,
959 bufferSize() );
960 }
961 #endif
962
963 unlockShm();
964
965 m_currentSamplePos += bufferSize();
966 }
967
968
969
970
processMidiEvent(const MidiEvent & event,const f_cnt_t offset)971 void RemoteVstPlugin::processMidiEvent( const MidiEvent& event, const f_cnt_t offset )
972 {
973 VstMidiEvent vme;
974
975 vme.type = kVstMidiType;
976 vme.byteSize = 24;
977 vme.deltaFrames = offset;
978 vme.flags = 0;
979 vme.detune = 0;
980 vme.noteLength = 0;
981 vme.noteOffset = 0;
982 vme.noteOffVelocity = 0;
983 vme.reserved1 = 0;
984 vme.reserved2 = 0;
985 vme.midiData[0] = event.type() + event.channel();
986
987 switch( event.type() )
988 {
989 case MidiPitchBend:
990 vme.midiData[1] = event.pitchBend() & 0x7f;
991 vme.midiData[2] = event.pitchBend() >> 7;
992 break;
993 // TODO: handle more special cases
994 default:
995 vme.midiData[1] = event.key();
996 vme.midiData[2] = event.velocity();
997 break;
998 }
999 vme.midiData[3] = 0;
1000
1001 m_midiEvents.push_back( vme );
1002 }
1003
1004
1005
1006
pluginName()1007 const char * RemoteVstPlugin::pluginName()
1008 {
1009 static char buf[32];
1010 buf[0] = 0;
1011 pluginDispatch( effGetEffectName, 0, 0, buf );
1012 buf[31] = 0;
1013 return buf;
1014 }
1015
1016
1017
1018
pluginVendorString()1019 const char * RemoteVstPlugin::pluginVendorString()
1020 {
1021 static char buf[64];
1022 buf[0] = 0;
1023 pluginDispatch( effGetVendorString, 0, 0, buf );
1024 buf[63] = 0;
1025 return buf;
1026 }
1027
1028
1029
1030
pluginProductString()1031 const char * RemoteVstPlugin::pluginProductString()
1032 {
1033 static char buf[64];
1034 buf[0] = 0;
1035 pluginDispatch( effGetProductString, 0, 0, buf );
1036 buf[63] = 0;
1037 return buf;
1038 }
1039
1040
1041
1042
programName()1043 const char * RemoteVstPlugin::programName()
1044 {
1045 static char buf[24];
1046
1047 memset( buf, 0, sizeof( buf ) );
1048
1049 pluginDispatch( effGetProgramName, 0, 0, buf );
1050
1051 buf[23] = 0;
1052
1053 return buf;
1054 }
1055
1056
1057
sendCurrentProgramName()1058 void RemoteVstPlugin::sendCurrentProgramName()
1059 {
1060 char presName[64];
1061 sprintf( presName, "%d/%d: %s", pluginDispatch( effGetProgram ) + 1, m_plugin->numPrograms, programName() );
1062
1063 sendMessage( message( IdVstCurrentProgramName ).addString( presName ) );
1064 }
1065
1066
1067
getParameterDump()1068 void RemoteVstPlugin::getParameterDump()
1069 {
1070 message m( IdVstParameterDump );
1071 m.addInt( m_plugin->numParams );
1072
1073 for( int i = 0; i < m_plugin->numParams; ++i )
1074 {
1075 char paramName[256];
1076 memset( paramName, 0, sizeof( paramName ) );
1077 pluginDispatch( effGetParamName, i, 0, paramName );
1078 paramName[sizeof(paramName)-1] = 0;
1079
1080 m.addInt( i );
1081 m.addString( paramName );
1082 m.addFloat( m_plugin->getParameter( m_plugin, i ) );
1083 }
1084
1085 sendMessage( m );
1086 }
1087
1088
1089
1090
setParameterDump(const message & _m)1091 void RemoteVstPlugin::setParameterDump( const message & _m )
1092 {
1093 const int n = _m.getInt( 0 );
1094 const int params = ( n > m_plugin->numParams ) ?
1095 m_plugin->numParams : n;
1096 int p = 0;
1097 for( int i = 0; i < params; ++i )
1098 {
1099 VstParameterDumpItem item;
1100 item.index = _m.getInt( ++p );
1101 item.shortLabel = _m.getString( ++p );
1102 item.value = _m.getFloat( ++p );
1103 m_plugin->setParameter( m_plugin, item.index, item.value );
1104 }
1105 }
1106
1107
1108
1109
saveChunkToFile(const std::string & _file)1110 void RemoteVstPlugin::saveChunkToFile( const std::string & _file )
1111 {
1112 if( m_plugin->flags & 32 )
1113 {
1114 void * chunk = NULL;
1115 const int len = pluginDispatch( 23, 0, 0, &chunk );
1116 if( len > 0 )
1117 {
1118 FILE* fp = F_OPEN_UTF8( _file, "wb" );
1119 if (!fp)
1120 {
1121 fprintf( stderr,
1122 "Error opening file for saving chunk.\n" );
1123 return;
1124 }
1125 if ( fwrite( chunk, 1, len, fp ) != len )
1126 {
1127 fprintf( stderr,
1128 "Error saving chunk to file.\n" );
1129 }
1130 close_check( fp );
1131 }
1132 }
1133 }
1134
1135
1136
1137
setProgram(int program)1138 void RemoteVstPlugin::setProgram( int program )
1139 {
1140 if( isInitialized() == false )
1141 {
1142 return;
1143 }
1144
1145 if( program < 0 )
1146 {
1147 program = 0;
1148 }
1149 else if( program >= m_plugin->numPrograms )
1150 {
1151 program = m_plugin->numPrograms - 1;
1152 }
1153 pluginDispatch( effSetProgram, 0, program );
1154
1155 sendCurrentProgramName();
1156 }
1157
1158
1159
1160
rotateProgram(int offset)1161 void RemoteVstPlugin::rotateProgram( int offset )
1162 {
1163 if( isInitialized() == false )
1164 {
1165 return;
1166 }
1167
1168 int newProgram = pluginDispatch( effGetProgram ) + offset;
1169
1170 if( newProgram < 0 )
1171 {
1172 newProgram = 0;
1173 }
1174 else if( newProgram >= m_plugin->numPrograms )
1175 {
1176 newProgram = m_plugin->numPrograms - 1;
1177 }
1178 pluginDispatch( effSetProgram, 0, newProgram );
1179
1180 sendCurrentProgramName();
1181 }
1182
1183
1184
1185
getProgramNames()1186 void RemoteVstPlugin::getProgramNames()
1187 {
1188 char presName[1024+256*30];
1189 char curProgName[30];
1190 if (isInitialized() == false) return;
1191 bool progNameIndexed = ( pluginDispatch( 29, 0, -1, curProgName ) == 1 );
1192
1193 if (m_plugin->numPrograms > 1) {
1194 if (progNameIndexed) {
1195 for (int i = 0; i< (m_plugin->numPrograms >= 256?256:m_plugin->numPrograms); i++)
1196 {
1197 pluginDispatch( 29, i, -1, curProgName );
1198 if (i == 0) sprintf( presName, "%s", curProgName );
1199 else sprintf( presName + strlen(presName), "|%s", curProgName );
1200 }
1201 }
1202 else
1203 {
1204 int currProgram = pluginDispatch( effGetProgram );
1205 for (int i = 0; i< (m_plugin->numPrograms >= 256?256:m_plugin->numPrograms); i++)
1206 {
1207 pluginDispatch( effSetProgram, 0, i );
1208 if (i == 0) sprintf( presName, "%s", programName() );
1209 else sprintf( presName + strlen(presName), "|%s", programName() );
1210 }
1211 pluginDispatch( effSetProgram, 0, currProgram );
1212 }
1213 } else sprintf( presName, "%s", programName() );
1214
1215 presName[sizeof(presName)-1] = 0;
1216
1217 sendMessage( message( IdVstProgramNames ).addString( presName ) );
1218 }
1219
1220
1221
1222
endian_swap(unsigned int & x)1223 inline unsigned int endian_swap(unsigned int& x)
1224 {
1225 return (x>>24) | ((x<<8) & 0x00FF0000) | ((x>>8) & 0x0000FF00) | (x<<24);
1226 }
1227
1228 struct sBank
1229 {
1230 unsigned int chunkMagic;
1231 unsigned int byteSize;
1232 unsigned int fxMagic;
1233 unsigned int version;
1234 unsigned int fxID;
1235 unsigned int fxVersion;
1236 unsigned int numPrograms;
1237 char prgName[28];
1238 };
1239
savePreset(const std::string & _file)1240 void RemoteVstPlugin::savePreset( const std::string & _file )
1241 {
1242 unsigned int chunk_size = 0;
1243 sBank * pBank = ( sBank* ) new char[ sizeof( sBank ) ];
1244 char progName[ 128 ] = { 0 };
1245 char* data = NULL;
1246 const bool chunky = ( m_plugin->flags & ( 1 << 5 ) ) != 0;
1247 bool isPreset = _file.substr( _file.find_last_of( "." ) + 1 ) == "fxp";
1248 int presNameLen = _file.find_last_of( "/" ) + _file.find_last_of( "\\" ) + 2;
1249
1250 if (isPreset)
1251 {
1252 for (size_t i = 0; i < _file.length() - 4 - presNameLen; i++)
1253 progName[i] = i < 23 ? _file[presNameLen + i] : 0;
1254 pluginDispatch( 4, 0, 0, progName );
1255 }
1256 if ( chunky )
1257 chunk_size = pluginDispatch( 23, isPreset, 0, &data );
1258 else {
1259 if (isPreset) {
1260 chunk_size = m_plugin->numParams * sizeof( float );
1261 data = new char[ chunk_size ];
1262 unsigned int* toUIntArray = reinterpret_cast<unsigned int*>( data );
1263 for ( int i = 0; i < m_plugin->numParams; i++ )
1264 {
1265 float value = m_plugin->getParameter( m_plugin, i );
1266 unsigned int * pValue = ( unsigned int * ) &value;
1267 toUIntArray[ i ] = endian_swap( *pValue );
1268 }
1269 } else chunk_size = (((m_plugin->numParams * sizeof( float )) + 56)*m_plugin->numPrograms);
1270 }
1271
1272 pBank->chunkMagic = 0x4B6E6343;
1273 pBank->byteSize = chunk_size + ( chunky ? sizeof( int ) : 0 ) + 48;
1274 if (!isPreset) pBank->byteSize += 100;
1275 pBank->byteSize = endian_swap( pBank->byteSize );
1276 pBank->fxMagic = chunky ? 0x68435046 : 0x6B437846;
1277 if (!isPreset && chunky) pBank->fxMagic = 0x68434246;
1278 if (!isPreset &&!chunky) pBank->fxMagic = 0x6B427846;
1279
1280 pBank->version = 0x01000000;
1281 unsigned int uIntToFile = (unsigned int) m_plugin->uniqueID;
1282 pBank->fxID = endian_swap( uIntToFile );
1283 uIntToFile = (unsigned int) pluginVersion();
1284 pBank->fxVersion = endian_swap( uIntToFile );
1285 uIntToFile = (unsigned int) chunky ? m_plugin->numPrograms : m_plugin->numParams;
1286 if (!isPreset &&!chunky) uIntToFile = (unsigned int) m_plugin->numPrograms;
1287 pBank->numPrograms = endian_swap( uIntToFile );
1288
1289 FILE * stream = F_OPEN_UTF8( _file, "wb" );
1290 if (!stream)
1291 {
1292 fprintf( stderr,
1293 "Error opening file for saving preset.\n" );
1294 return;
1295 }
1296 fwrite ( pBank, 1, 28, stream );
1297 fwrite ( progName, 1, isPreset ? 28 : 128, stream );
1298 if ( chunky ) {
1299 uIntToFile = endian_swap( chunk_size );
1300 fwrite ( &uIntToFile, 1, 4, stream );
1301 }
1302 if (pBank->fxMagic != 0x6B427846 )
1303 fwrite ( data, 1, chunk_size, stream );
1304 else {
1305 int numPrograms = m_plugin->numPrograms;
1306 int currProgram = pluginDispatch( effGetProgram );
1307 chunk_size = (m_plugin->numParams * sizeof( float ));
1308 pBank->byteSize = chunk_size + 48;
1309 pBank->byteSize = endian_swap( pBank->byteSize );
1310 pBank->fxMagic = 0x6B437846;
1311 uIntToFile = (unsigned int) m_plugin->numParams;
1312 pBank->numPrograms = endian_swap( uIntToFile );
1313 data = new char[ chunk_size ];
1314 unsigned int* pValue,* toUIntArray = reinterpret_cast<unsigned int*>( data );
1315 float value;
1316 for (int j = 0; j < numPrograms; j++) {
1317 pluginDispatch( effSetProgram, 0, j );
1318 pluginDispatch( effGetProgramName, 0, 0, pBank->prgName );
1319 fwrite ( pBank, 1, 56, stream );
1320 for ( int i = 0; i < m_plugin->numParams; i++ )
1321 {
1322 value = m_plugin->getParameter( m_plugin, i );
1323 pValue = ( unsigned int * ) &value;
1324 toUIntArray[ i ] = endian_swap( *pValue );
1325 }
1326 fwrite ( data, 1, chunk_size, stream );
1327 }
1328 pluginDispatch( effSetProgram, 0, currProgram );
1329 }
1330 fclose( stream );
1331
1332 if ( !chunky )
1333 delete[] data;
1334 delete[] (sBank*)pBank;
1335
1336 }
1337
1338
1339
1340
loadPresetFile(const std::string & _file)1341 void RemoteVstPlugin::loadPresetFile( const std::string & _file )
1342 {
1343 void * chunk = NULL;
1344 unsigned int * pLen = new unsigned int[ 1 ];
1345 unsigned int len = 0;
1346 sBank * pBank = (sBank*) new char[ sizeof( sBank ) ];
1347 FILE * stream = F_OPEN_UTF8( _file, "rb" );
1348 if (!stream)
1349 {
1350 fprintf( stderr,
1351 "Error opening file for loading preset.\n" );
1352 return;
1353 }
1354 if ( fread ( pBank, 1, 56, stream ) != 56 )
1355 {
1356 fprintf( stderr, "Error loading preset file.\n" );
1357 }
1358 pBank->fxID = endian_swap( pBank->fxID );
1359 pBank->numPrograms = endian_swap( pBank->numPrograms );
1360 unsigned int toUInt;
1361 float * pFloat;
1362
1363 if (m_plugin->uniqueID != pBank->fxID) {
1364 sendMessage( message( IdVstCurrentProgramName ).
1365 addString( "Error: Plugin UniqID not match" ) );
1366 fclose( stream );
1367 delete[] (unsigned int*)pLen;
1368 delete[] (sBank*)pBank;
1369 return;
1370 }
1371
1372 if( _file.substr( _file.find_last_of( "." ) + 1 ) != "fxp" )
1373 fseek ( stream , 156 , SEEK_SET );
1374
1375 if(pBank->fxMagic != 0x6B427846) {
1376 if(pBank->fxMagic != 0x6B437846) {
1377 if ( fread (pLen, 1, 4, stream) != 4 )
1378 {
1379 fprintf( stderr,
1380 "Error loading preset file.\n" );
1381 }
1382 chunk = new char[len = endian_swap(*pLen)];
1383 } else chunk = new char[len = sizeof(float)*pBank->numPrograms];
1384 if ( fread (chunk, len, 1, stream) != 1 )
1385 {
1386 fprintf( stderr, "Error loading preset file.\n" );
1387 }
1388 fclose( stream );
1389 }
1390
1391 if(_file.substr(_file.find_last_of(".") + 1) == "fxp") {
1392 pBank->prgName[23] = 0;
1393 pluginDispatch( 4, 0, 0, pBank->prgName );
1394 if(pBank->fxMagic != 0x6B437846)
1395 pluginDispatch( 24, 1, len, chunk );
1396 else
1397 {
1398 unsigned int* toUIntArray = reinterpret_cast<unsigned int*>( chunk );
1399 for (int i = 0; i < pBank->numPrograms; i++ )
1400 {
1401 toUInt = endian_swap( toUIntArray[ i ] );
1402 pFloat = ( float* ) &toUInt;
1403 m_plugin->setParameter( m_plugin, i, *pFloat );
1404 }
1405 }
1406 } else {
1407 if(pBank->fxMagic != 0x6B427846) {
1408 pluginDispatch( 24, 0, len, chunk );
1409 } else {
1410 int numPrograms = pBank->numPrograms;
1411 unsigned int * toUIntArray;
1412 int currProgram = pluginDispatch( effGetProgram );
1413 chunk = new char[ len = sizeof(float)*m_plugin->numParams ];
1414 toUIntArray = reinterpret_cast<unsigned int *>( chunk );
1415 for (int i =0; i < numPrograms; i++) {
1416 if ( fread (pBank, 1, 56, stream) != 56 )
1417 {
1418 fprintf( stderr,
1419 "Error loading preset file.\n" );
1420 }
1421 if ( fread (chunk, len, 1, stream) != 1 )
1422 {
1423 fprintf( stderr,
1424 "Error loading preset file.\n" );
1425 }
1426 pluginDispatch( effSetProgram, 0, i );
1427 pBank->prgName[23] = 0;
1428 pluginDispatch( 4, 0, 0, pBank->prgName );
1429 for (int j = 0; j < m_plugin->numParams; j++ ) {
1430 toUInt = endian_swap( toUIntArray[ j ] );
1431 pFloat = ( float* ) &toUInt;
1432 m_plugin->setParameter( m_plugin, j, *pFloat );
1433 }
1434 }
1435 pluginDispatch( effSetProgram, 0, currProgram );
1436 fclose( stream );
1437 }
1438 }
1439
1440 sendCurrentProgramName();
1441
1442 delete[] (unsigned int*)pLen;
1443 delete[] (sBank*)pBank;
1444 delete[] (char*)chunk;
1445 }
1446
1447
1448
1449
loadChunkFromFile(const std::string & _file,int _len)1450 void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len )
1451 {
1452 char * chunk = new char[_len];
1453
1454 FILE* fp = F_OPEN_UTF8( _file, "rb" );
1455 if (!fp)
1456 {
1457 fprintf( stderr,
1458 "Error opening file for loading chunk.\n" );
1459 return;
1460 }
1461 if ( fread( chunk, 1, _len, fp ) != _len )
1462 {
1463 fprintf( stderr, "Error loading chunk from file.\n" );
1464 }
1465 close_check( fp );
1466
1467 pluginDispatch( effSetChunk, 0, _len, chunk );
1468
1469 delete[] chunk;
1470 }
1471
1472
1473
1474
updateInOutCount()1475 int RemoteVstPlugin::updateInOutCount()
1476 {
1477 if( inputCount() == RemotePluginClient::inputCount() &&
1478 outputCount() == RemotePluginClient::outputCount() )
1479 {
1480 return 1;
1481 }
1482
1483 if( GetCurrentThreadId() == __processingThreadId )
1484 {
1485 debugMessage( "Plugin requested I/O change from processing "
1486 "thread. Request denied; stability may suffer.\n" );
1487 return 0;
1488 }
1489
1490 lockShm();
1491
1492 setShmIsValid( false );
1493
1494 unlockShm();
1495
1496 delete[] m_inputs;
1497 delete[] m_outputs;
1498
1499 m_inputs = NULL;
1500 m_outputs = NULL;
1501
1502 setInputOutputCount( inputCount(), outputCount() );
1503
1504 char buf[64];
1505 sprintf( buf, "inputs: %d output: %d\n", inputCount(), outputCount() );
1506 debugMessage( buf );
1507
1508 if( inputCount() > 0 )
1509 {
1510 m_inputs = new float * [inputCount()];
1511 }
1512
1513 if( outputCount() > 0 )
1514 {
1515 m_outputs = new float * [outputCount()];
1516 }
1517
1518 return 1;
1519 }
1520
1521
1522
1523 //#define DEBUG_CALLBACKS
1524 #ifdef DEBUG_CALLBACKS
1525 #define SHOW_CALLBACK __plugin->debugMessage
1526 #else
1527 #define SHOW_CALLBACK(...)
1528 #endif
1529
1530
1531 /* TODO:
1532 * - complete audioMasterGetTime-handling (bars etc.)
1533 * - implement audioMasterProcessEvents
1534 * - audioMasterGetVendorVersion: return LMMS-version (config.h!)
1535 * - audioMasterGetDirectory: return either VST-plugin-dir or LMMS-workingdir
1536 * - audioMasterOpenFileSelector: show QFileDialog?
1537 */
hostCallback(AEffect * _effect,int32_t _opcode,int32_t _index,intptr_t _value,void * _ptr,float _opt)1538 intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode,
1539 int32_t _index, intptr_t _value,
1540 void * _ptr, float _opt )
1541 {
1542 static VstTimeInfo _timeInfo;
1543 #ifdef DEBUG_CALLBACKS
1544 char buf[64];
1545 sprintf( buf, "host-callback, opcode = %d\n", (int) _opcode );
1546 SHOW_CALLBACK( buf );
1547 #endif
1548
1549 // workaround for early callbacks by some plugins
1550 if( __plugin && __plugin->m_plugin == NULL )
1551 {
1552 __plugin->m_plugin = _effect;
1553 }
1554
1555 switch( _opcode )
1556 {
1557 case audioMasterAutomate:
1558 SHOW_CALLBACK( "amc: audioMasterAutomate\n" );
1559 // index, value, returns 0
1560 return 0;
1561
1562 case audioMasterVersion:
1563 SHOW_CALLBACK( "amc: audioMasterVersion\n" );
1564 return 2300;
1565
1566 case audioMasterCurrentId:
1567 SHOW_CALLBACK( "amc: audioMasterCurrentId\n" );
1568 // returns the unique id of a plug that's currently
1569 // loading
1570 return 0;
1571
1572 case audioMasterIdle:
1573 SHOW_CALLBACK ("amc: audioMasterIdle\n" );
1574 // call application idle routine (this will
1575 // call effEditIdle for all open editors too)
1576 PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 );
1577 return 0;
1578
1579 case audioMasterPinConnected:
1580 SHOW_CALLBACK( "amc: audioMasterPinConnected\n" );
1581 // inquire if an input or output is beeing connected;
1582 // index enumerates input or output counting from zero:
1583 // value is 0 for input and != 0 otherwise. note: the
1584 // return value is 0 for <true> such that older versions
1585 // will always return true.
1586 return 0;
1587
1588 case audioMasterGetTime:
1589 SHOW_CALLBACK( "amc: audioMasterGetTime\n" );
1590 // returns const VstTimeInfo* (or 0 if not supported)
1591 // <value> should contain a mask indicating which
1592 // fields are required (see valid masks above), as some
1593 // items may require extensive conversions
1594
1595 // Shared memory was initialised? - see song.cpp
1596 //assert( __plugin->m_vstSyncData != NULL );
1597
1598 memset( &_timeInfo, 0, sizeof( _timeInfo ) );
1599 _timeInfo.samplePos = __plugin->m_currentSamplePos;
1600 _timeInfo.sampleRate = __plugin->m_vstSyncData->hasSHM ?
1601 __plugin->m_vstSyncData->m_sampleRate :
1602 __plugin->sampleRate();
1603 _timeInfo.flags = 0;
1604 _timeInfo.tempo = __plugin->m_vstSyncData->hasSHM ?
1605 __plugin->m_vstSyncData->m_bpm :
1606 __plugin->m_bpm;
1607 _timeInfo.timeSigNumerator = __plugin->m_vstSyncData->timeSigNumer;
1608 _timeInfo.timeSigDenominator = __plugin->m_vstSyncData->timeSigDenom;
1609 _timeInfo.flags |= kVstTempoValid;
1610 _timeInfo.flags |= kVstTimeSigValid;
1611
1612 if( __plugin->m_vstSyncData->isCycle )
1613 {
1614 _timeInfo.cycleStartPos = __plugin->m_vstSyncData->cycleStart;
1615 _timeInfo.cycleEndPos = __plugin->m_vstSyncData->cycleEnd;
1616 _timeInfo.flags |= kVstCyclePosValid;
1617 _timeInfo.flags |= kVstTransportCycleActive;
1618 }
1619
1620 if( __plugin->m_vstSyncData->ppqPos !=
1621 __plugin->m_in->m_Timestamp )
1622 {
1623 _timeInfo.ppqPos = __plugin->m_vstSyncData->ppqPos;
1624 __plugin->m_in->lastppqPos = __plugin->m_vstSyncData->ppqPos;
1625 __plugin->m_in->m_Timestamp = __plugin->m_vstSyncData->ppqPos;
1626 }
1627 else if( __plugin->m_vstSyncData->isPlaying )
1628 {
1629 if( __plugin->m_vstSyncData->hasSHM )
1630 {
1631 __plugin->m_in->lastppqPos +=
1632 __plugin->m_vstSyncData->m_bpm / 60.0
1633 * __plugin->m_vstSyncData->m_bufferSize
1634 / __plugin->m_vstSyncData->m_sampleRate;
1635 }
1636 else
1637 {
1638 __plugin->m_in->lastppqPos +=
1639 __plugin->m_bpm / 60.0
1640 * __plugin->bufferSize()
1641 / __plugin->sampleRate();
1642 }
1643 _timeInfo.ppqPos = __plugin->m_in->lastppqPos;
1644 }
1645 // _timeInfo.ppqPos = __plugin->m_vstSyncData->ppqPos;
1646 _timeInfo.flags |= kVstPpqPosValid;
1647
1648 if( __plugin->m_vstSyncData->isPlaying )
1649 {
1650 _timeInfo.flags |= kVstTransportPlaying;
1651 }
1652 _timeInfo.barStartPos = ( (int) ( _timeInfo.ppqPos /
1653 ( 4 *__plugin->m_vstSyncData->timeSigNumer
1654 / (float) __plugin->m_vstSyncData->timeSigDenom ) ) ) *
1655 ( 4 * __plugin->m_vstSyncData->timeSigNumer
1656 / (float) __plugin->m_vstSyncData->timeSigDenom );
1657
1658 _timeInfo.flags |= kVstBarsValid;
1659
1660 if( ( _timeInfo.flags & ( kVstTransportPlaying | kVstTransportCycleActive ) ) !=
1661 ( __plugin->m_in->m_lastFlags & ( kVstTransportPlaying | kVstTransportCycleActive ) )
1662 || __plugin->m_vstSyncData->m_playbackJumped )
1663 {
1664 _timeInfo.flags |= kVstTransportChanged;
1665 }
1666 __plugin->m_in->m_lastFlags = _timeInfo.flags;
1667
1668 #ifdef LMMS_BUILD_WIN64
1669 return (long long) &_timeInfo;
1670 #else
1671 return (long) &_timeInfo;
1672 #endif
1673
1674 case audioMasterProcessEvents:
1675 SHOW_CALLBACK( "amc: audioMasterProcessEvents\n" );
1676 // VstEvents* in <ptr>
1677 return 0;
1678
1679 case audioMasterIOChanged:
1680 SHOW_CALLBACK( "amc: audioMasterIOChanged\n" );
1681 // numInputs, numOutputs, and/or latency has changed
1682 return __plugin->updateInOutCount();
1683
1684 #ifdef OLD_VST_SDK
1685 case audioMasterWantMidi:
1686 SHOW_CALLBACK( "amc: audioMasterWantMidi\n" );
1687 // <value> is a filter which is currently ignored
1688 return 1;
1689
1690 case audioMasterSetTime:
1691 SHOW_CALLBACK( "amc: audioMasterSetTime\n" );
1692 // VstTimenfo* in <ptr>, filter in <value>, not
1693 // supported
1694 return 0;
1695
1696 case audioMasterTempoAt:
1697 SHOW_CALLBACK( "amc: audioMasterTempoAt\n" );
1698 return __plugin->m_bpm * 10000;
1699
1700 case audioMasterGetNumAutomatableParameters:
1701 SHOW_CALLBACK( "amc: audioMasterGetNumAutomatable"
1702 "Parameters\n" );
1703 return 5000;
1704
1705 case audioMasterGetParameterQuantization:
1706 SHOW_CALLBACK( "amc: audioMasterGetParameter\n"
1707 "Quantization\n" );
1708 // returns the integer value for +1.0 representation,
1709 // or 1 if full single float precision is maintained
1710 // in automation. parameter index in <value> (-1: all,
1711 // any)
1712 return 1;
1713
1714 case audioMasterNeedIdle:
1715 SHOW_CALLBACK( "amc: audioMasterNeedIdle\n" );
1716 // plug needs idle calls (outside its editor window)
1717 return 1;
1718
1719 case audioMasterGetPreviousPlug:
1720 SHOW_CALLBACK( "amc: audioMasterGetPreviousPlug\n" );
1721 // input pin in <value> (-1: first to come), returns
1722 // cEffect*
1723 return 0;
1724
1725 case audioMasterGetNextPlug:
1726 SHOW_CALLBACK( "amc: audioMasterGetNextPlug\n" );
1727 // output pin in <value> (-1: first to come), returns
1728 // cEffect*
1729 return 0;
1730
1731 case audioMasterWillReplaceOrAccumulate:
1732 SHOW_CALLBACK( "amc: audioMasterWillReplaceOr"
1733 "Accumulate\n" );
1734 // returns: 0: not supported, 1: replace, 2: accumulate
1735 return 1;
1736
1737 case audioMasterGetSpeakerArrangement:
1738 SHOW_CALLBACK( "amc: audioMasterGetSpeaker"
1739 "Arrangement\n" );
1740 // (long)input in <value>, output in <ptr>
1741 return 0;
1742
1743 case audioMasterSetOutputSampleRate:
1744 SHOW_CALLBACK( "amc: audioMasterSetOutputSample"
1745 "Rate\n" );
1746 // for variable i/o, sample rate in <opt>
1747 return 0;
1748
1749 case audioMasterSetIcon:
1750 SHOW_CALLBACK( "amc: audioMasterSetIcon\n" );
1751 // TODO
1752 // void* in <ptr>, format not defined yet
1753 return 0;
1754
1755 case audioMasterOpenWindow:
1756 SHOW_CALLBACK( "amc: audioMasterOpenWindow\n" );
1757 // TODO
1758 // returns platform specific ptr
1759 return 0;
1760
1761 case audioMasterCloseWindow:
1762 SHOW_CALLBACK( "amc: audioMasterCloseWindow\n" );
1763 // TODO
1764 // close window, platform specific handle in <ptr>
1765 return 0;
1766 #endif
1767
1768 case audioMasterSizeWindow:
1769 {
1770 SHOW_CALLBACK( "amc: audioMasterSizeWindow\n" );
1771 if( __plugin->m_window == 0 )
1772 {
1773 return 0;
1774 }
1775 __plugin->m_windowWidth = _index;
1776 __plugin->m_windowHeight = _value;
1777 HWND window = __plugin->m_window;
1778 DWORD dwStyle = GetWindowLongPtr( window, GWL_STYLE );
1779 RECT windowSize = { 0, 0, (int) _index, (int) _value };
1780 AdjustWindowRect( &windowSize, dwStyle, false );
1781 SetWindowPos( window, 0, 0, 0,
1782 windowSize.right - windowSize.left,
1783 windowSize.bottom - windowSize.top,
1784 SWP_NOACTIVATE | SWP_NOMOVE |
1785 SWP_NOOWNERZORDER | SWP_NOZORDER );
1786 __plugin->sendMessage(
1787 message( IdVstPluginEditorGeometry ).
1788 addInt( __plugin->m_windowWidth ).
1789 addInt( __plugin->m_windowHeight ) );
1790 return 1;
1791 }
1792
1793 case audioMasterGetSampleRate:
1794 SHOW_CALLBACK( "amc: audioMasterGetSampleRate\n" );
1795 return __plugin->sampleRate();
1796
1797 case audioMasterGetBlockSize:
1798 SHOW_CALLBACK( "amc: audioMasterGetBlockSize\n" );
1799
1800 return __plugin->bufferSize();
1801
1802 case audioMasterGetInputLatency:
1803 SHOW_CALLBACK( "amc: audioMasterGetInputLatency\n" );
1804 return __plugin->bufferSize();
1805
1806 case audioMasterGetOutputLatency:
1807 SHOW_CALLBACK( "amc: audioMasterGetOutputLatency\n" );
1808 return __plugin->bufferSize();
1809
1810 case audioMasterGetCurrentProcessLevel:
1811 SHOW_CALLBACK( "amc: audioMasterGetCurrentProcess"
1812 "Level\n" );
1813 // returns: 0: not supported,
1814 // 1: currently in user thread (gui)
1815 // 2: currently in audio thread (where process is
1816 // called)
1817 // 3: currently in 'sequencer' thread (midi, timer etc)
1818 // 4: currently offline processing and thus in user
1819 // thread
1820 // other: not defined, but probably pre-empting user
1821 // thread.
1822 return 0;
1823
1824 case audioMasterGetAutomationState:
1825 SHOW_CALLBACK( "amc: audioMasterGetAutomationState\n" );
1826 // returns 0: not supported, 1: off, 2:read, 3:write,
1827 // 4:read/write offline
1828 return 0;
1829
1830 case audioMasterOfflineStart:
1831 SHOW_CALLBACK( "amc: audioMasterOfflineStart\n" );
1832 return 0;
1833
1834 case audioMasterOfflineRead:
1835 SHOW_CALLBACK( "amc: audioMasterOfflineRead\n" );
1836 // ptr points to offline structure, see below.
1837 // return 0: error, 1 ok
1838 return 0;
1839
1840 case audioMasterOfflineWrite:
1841 SHOW_CALLBACK( "amc: audioMasterOfflineWrite\n" );
1842 // same as read
1843 return 0;
1844
1845 case audioMasterOfflineGetCurrentPass:
1846 SHOW_CALLBACK( "amc: audioMasterOfflineGetCurrent"
1847 "Pass\n" );
1848 return 0;
1849
1850 case audioMasterOfflineGetCurrentMetaPass:
1851 SHOW_CALLBACK( "amc: audioMasterOfflineGetCurrentMeta"
1852 "Pass\n");
1853 return 0;
1854
1855 case audioMasterGetVendorString:
1856 SHOW_CALLBACK( "amc: audioMasterGetVendorString\n" );
1857 // fills <ptr> with a string identifying the vendor
1858 // (max 64 char)
1859 strcpy( (char *) _ptr, "Tobias Doerffel" );
1860 return 1;
1861
1862 case audioMasterGetProductString:
1863 SHOW_CALLBACK( "amc: audioMasterGetProductString\n" );
1864 // fills <ptr> with a string with product name
1865 // (max 64 char)
1866 strcpy( (char *) _ptr,
1867 "LMMS VST Support Layer (LVSL)" );
1868 return 1;
1869
1870 case audioMasterGetVendorVersion:
1871 SHOW_CALLBACK( "amc: audioMasterGetVendorVersion\n" );
1872 // returns vendor-specific version
1873 return 1000;
1874
1875 case audioMasterVendorSpecific:
1876 SHOW_CALLBACK( "amc: audioMasterVendorSpecific\n" );
1877 // no definition, vendor specific handling
1878 return 0;
1879
1880 case audioMasterCanDo:
1881 SHOW_CALLBACK( "amc: audioMasterCanDo\n" );
1882 return !strcmp( (char *) _ptr, "sendVstEvents" ) ||
1883 !strcmp( (char *) _ptr, "sendVstMidiEvent" ) ||
1884 !strcmp( (char *) _ptr, "sendVstTimeInfo" ) ||
1885 !strcmp( (char *) _ptr, "sizeWindow" ) ||
1886 !strcmp( (char *) _ptr, "supplyIdle" );
1887
1888 case audioMasterGetLanguage:
1889 SHOW_CALLBACK( "amc: audioMasterGetLanguage\n" );
1890 return hlang;
1891
1892 case audioMasterGetDirectory:
1893 SHOW_CALLBACK( "amc: audioMasterGetDirectory\n" );
1894 // get plug directory, FSSpec on MAC, else char*
1895 return 0;
1896
1897 case audioMasterUpdateDisplay:
1898 SHOW_CALLBACK( "amc: audioMasterUpdateDisplay\n" );
1899 // something has changed, update 'multi-fx' display
1900 PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 );
1901 return 0;
1902
1903 #if kVstVersion > 2
1904 case audioMasterBeginEdit:
1905 SHOW_CALLBACK( "amc: audioMasterBeginEdit\n" );
1906 // begin of automation session (when mouse down),
1907 // parameter index in <index>
1908 return 0;
1909
1910 case audioMasterEndEdit:
1911 SHOW_CALLBACK( "amc: audioMasterEndEdit\n" );
1912 // end of automation session (when mouse up),
1913 // parameter index in <index>
1914 return 0;
1915
1916 case audioMasterOpenFileSelector:
1917 SHOW_CALLBACK( "amc: audioMasterOpenFileSelector\n" );
1918 // open a fileselector window with VstFileSelect*
1919 // in <ptr>
1920 return 0;
1921 #endif
1922 default:
1923 SHOW_CALLBACK( "amd: not handled" );
1924 break;
1925 }
1926
1927 return 0;
1928 }
1929
1930
1931
1932
idle()1933 void RemoteVstPlugin::idle()
1934 {
1935 if( isProcessing() )
1936 {
1937 setShouldGiveIdle( true );
1938 return;
1939 }
1940 setProcessing( true );
1941 pluginDispatch( effEditIdle );
1942 setShouldGiveIdle( false );
1943 setProcessing( false );
1944 // We might have received a message whilst idling
1945 processUIThreadMessages();
1946 }
1947
1948
1949
1950
processUIThreadMessages()1951 void RemoteVstPlugin::processUIThreadMessages()
1952 {
1953 setProcessing( true );
1954 while( m_messageList.size() )
1955 {
1956 processMessage( m_messageList.front() );
1957 m_messageList.pop();
1958 if( shouldGiveIdle() )
1959 {
1960 pluginDispatch( effEditIdle );
1961 setShouldGiveIdle( false );
1962 }
1963 }
1964 setProcessing( false );
1965 }
1966
1967
1968
1969
processingThread(LPVOID _param)1970 DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param )
1971 {
1972 __processingThreadId = GetCurrentThreadId();
1973
1974 RemoteVstPlugin * _this = static_cast<RemoteVstPlugin *>( _param );
1975
1976 RemotePluginClient::message m;
1977 while( ( m = _this->receiveMessage() ).id != IdQuit )
1978 {
1979 if( m.id == IdStartProcessing
1980 || m.id == IdMidiEvent
1981 || m.id == IdVstSetParameter
1982 || m.id == IdVstSetTempo )
1983 {
1984 _this->processMessage( m );
1985 }
1986 else if( m.id == IdChangeSharedMemoryKey )
1987 {
1988 _this->processMessage( m );
1989 _this->setShmIsValid( true );
1990 }
1991 else
1992 {
1993 PostMessage( __MessageHwnd,
1994 WM_USER,
1995 ProcessPluginMessage,
1996 (LPARAM) new message( m ) );
1997 }
1998 }
1999
2000 // notify GUI thread about shutdown
2001 PostMessage( __MessageHwnd, WM_USER, ClosePlugin, 0 );
2002
2003 return 0;
2004 }
2005
2006
2007
2008
setupMessageWindow()2009 bool RemoteVstPlugin::setupMessageWindow()
2010 {
2011 HMODULE hInst = GetModuleHandle( NULL );
2012 if( hInst == NULL )
2013 {
2014 __plugin->debugMessage( "setupMessageWindow(): can't get "
2015 "module handle\n" );
2016 return false;
2017 }
2018
2019 __MessageHwnd = CreateWindowEx( 0, "LVSL", "dummy",
2020 0, 0, 0, 0, 0, NULL, NULL,
2021 hInst, NULL );
2022 // install GUI update timer
2023 SetTimer( __MessageHwnd, 1000, 50, NULL );
2024
2025 return true;
2026 }
2027
2028
2029
2030
guiEventLoop()2031 DWORD WINAPI RemoteVstPlugin::guiEventLoop()
2032 {
2033 MSG msg;
2034 while( GetMessage( &msg, NULL, 0, 0 ) > 0 )
2035 {
2036 TranslateMessage( &msg );
2037 DispatchMessage( &msg );
2038 }
2039
2040 return 0;
2041 }
2042
2043
2044
2045
wndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)2046 LRESULT CALLBACK RemoteVstPlugin::wndProc( HWND hwnd, UINT uMsg,
2047 WPARAM wParam, LPARAM lParam )
2048 {
2049 if( uMsg == WM_TIMER && __plugin->isInitialized() )
2050 {
2051 // give plugin some idle-time for GUI-update
2052 __plugin->idle();
2053 return 0;
2054 }
2055 else if( uMsg == WM_USER )
2056 {
2057 switch( wParam )
2058 {
2059 case ProcessPluginMessage:
2060 {
2061 message * m = (message *) lParam;
2062 __plugin->queueMessage( *m );
2063 delete m;
2064 if( !__plugin->isProcessing() )
2065 {
2066 __plugin->processUIThreadMessages();
2067 }
2068 return 0;
2069 }
2070
2071 case GiveIdle:
2072 __plugin->idle();
2073 return 0;
2074
2075 case ClosePlugin:
2076 PostQuitMessage(0);
2077 return 0;
2078
2079 default:
2080 break;
2081 }
2082 }
2083 else if( uMsg == WM_SYSCOMMAND && (wParam & 0xfff0) == SC_CLOSE )
2084 {
2085 __plugin->hideEditor();
2086 return 0;
2087 }
2088
2089 return DefWindowProc( hwnd, uMsg, wParam, lParam );
2090 }
2091
2092
2093
2094
main(int _argc,char ** _argv)2095 int main( int _argc, char * * _argv )
2096 {
2097 #ifdef SYNC_WITH_SHM_FIFO
2098 if( _argc < 4 )
2099 #else
2100 if( _argc < 3 )
2101 #endif
2102 {
2103 fprintf( stderr, "not enough arguments\n" );
2104 return -1;
2105 }
2106
2107 OleInitialize(nullptr);
2108
2109 #ifdef LMMS_BUILD_WIN32
2110 #ifndef __WINPTHREADS_VERSION
2111 // (non-portable) initialization of statically linked pthread library
2112 pthread_win32_process_attach_np();
2113 pthread_win32_thread_attach_np();
2114 #endif
2115 #endif
2116
2117 #ifdef LMMS_BUILD_LINUX
2118 #ifdef LMMS_HAVE_SCHED_H
2119 // try to set realtime-priority
2120 struct sched_param sparam;
2121 sparam.sched_priority = ( sched_get_priority_max( SCHED_FIFO ) +
2122 sched_get_priority_min( SCHED_FIFO ) ) / 2;
2123 sched_setscheduler( 0, SCHED_FIFO, &sparam );
2124 #endif
2125 #endif
2126
2127 #ifdef LMMS_BUILD_WIN32
2128 if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
2129 {
2130 printf( "Notice: could not set high priority.\n" );
2131 }
2132 #endif
2133
2134 HMODULE hInst = GetModuleHandle( NULL );
2135 if( hInst == NULL )
2136 {
2137 return -1;
2138 }
2139
2140 WNDCLASS wc;
2141 wc.style = CS_HREDRAW | CS_VREDRAW;
2142 wc.lpfnWndProc = RemoteVstPlugin::wndProc;
2143 wc.cbClsExtra = 0;
2144 wc.cbWndExtra = 0;
2145 wc.hInstance = hInst;
2146 wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
2147 wc.hCursor = LoadCursor( NULL, IDC_ARROW );
2148 wc.hbrBackground = NULL;
2149 wc.lpszMenuName = NULL;
2150 wc.lpszClassName = "LVSL";
2151
2152 if( !RegisterClass( &wc ) )
2153 {
2154 return -1;
2155 }
2156
2157 {
2158 #ifdef SYNC_WITH_SHM_FIFO
2159 int embedMethodIndex = 3;
2160 #else
2161 int embedMethodIndex = 2;
2162 #endif
2163 std::string embedMethod = _argv[embedMethodIndex];
2164
2165 if ( embedMethod == "none" )
2166 {
2167 cerr << "Starting detached." << endl;
2168 EMBED = EMBED_X11 = EMBED_WIN32 = HEADLESS = false;
2169 }
2170 else if ( embedMethod == "win32" )
2171 {
2172 cerr << "Starting using Win32-native embedding." << endl;
2173 EMBED = EMBED_WIN32 = true; EMBED_X11 = HEADLESS = false;
2174 }
2175 else if ( embedMethod == "qt" )
2176 {
2177 cerr << "Starting using Qt-native embedding." << endl;
2178 EMBED = true; EMBED_X11 = EMBED_WIN32 = HEADLESS = false;
2179 }
2180 else if ( embedMethod == "xembed" )
2181 {
2182 cerr << "Starting using X11Embed protocol." << endl;
2183 EMBED = EMBED_X11 = true; EMBED_WIN32 = HEADLESS = false;
2184 }
2185 else if ( embedMethod == "headless" )
2186 {
2187 cerr << "Starting without UI." << endl;
2188 HEADLESS = true; EMBED = EMBED_X11 = EMBED_WIN32 = false;
2189 }
2190 else
2191 {
2192 cerr << "Unknown embed method " << embedMethod << ". Starting detached instead." << endl;
2193 EMBED = EMBED_X11 = EMBED_WIN32 = HEADLESS = false;
2194 }
2195 }
2196
2197 // constructor automatically will process messages until it receives
2198 // a IdVstLoadPlugin message and processes it
2199 #ifdef SYNC_WITH_SHM_FIFO
2200 __plugin = new RemoteVstPlugin( atoi( _argv[1] ), atoi( _argv[2] ) );
2201 #else
2202 __plugin = new RemoteVstPlugin( _argv[1] );
2203 #endif
2204
2205 if( __plugin->isInitialized() )
2206 {
2207 if( RemoteVstPlugin::setupMessageWindow() == false )
2208 {
2209 return -1;
2210 }
2211 if( CreateThread( NULL, 0, RemoteVstPlugin::processingThread,
2212 __plugin, 0, NULL ) == NULL )
2213 {
2214 __plugin->debugMessage( "could not create "
2215 "processingThread\n" );
2216 return -1;
2217 }
2218 RemoteVstPlugin::guiEventLoop();
2219 }
2220
2221
2222 delete __plugin;
2223
2224 OleUninitialize();
2225
2226 #ifdef LMMS_BUILD_WIN32
2227 #ifndef __WINPTHREADS_VERSION
2228 pthread_win32_thread_detach_np();
2229 pthread_win32_process_detach_np();
2230 #endif
2231 #endif
2232
2233 return 0;
2234
2235 }
2236
2237