diff --git a/ChangeLog b/ChangeLog index 6679d48e4..ea44f238a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2006-01-30 Tobias Doerffel + + * plugins/vestige/fstclient.h: + * plugins/vestige/fstclient.cpp: + - renamed to lvsl_client.* + - when waiting for plugin to have finished process()ing, usleep() some + micro-seconds for allowing better scheduling + + * plugins/vestige/lvsl_server.c: + - merged code from fstserver.cpp, fstserver.h and fstcore.c into one + source-file and removed rests of the old FST-API which makes all + the VST-code MUCH more cleaner and less redundant (LVSL = LMMS VST + Support Layer) + - VST SDK 2.4 compatibility (no need to fix headers with 2.4!) + 2006-01-29 Tobias Doerffel * plugins/bit_invader/bit_invader.cpp: @@ -6,7 +21,7 @@ them in a loop * plugins/bit_invader/graph.cpp: - - update after settings new sample + - update after setting new sample * src/tracks/pattern.cpp: - disable auto-cleanup during pattern-freeze diff --git a/Makefile.am b/Makefile.am index e8a27b254..ae488fd0e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -295,7 +295,7 @@ lmms_SOURCES = \ -EXTRA_DIST = $(lmms_EMBEDDED_RESOURCES) aeffectx_h_fix.patch +EXTRA_DIST = $(lmms_EMBEDDED_RESOURCES) vst_sdk23_headers.diff CLEANFILES = $(lmms_MOC) ./embedded_resources.h diff --git a/configure.in b/configure.in index 980901d21..dd3bdaca7 100644 --- a/configure.in +++ b/configure.in @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.50) -AC_INIT(lmms, 0.1.2-cvs20060129, tobydox/at/users/dot/sourceforge/dot/net) -AM_INIT_AUTOMAKE(lmms, 0.1.2-cvs20060129) +AC_INIT(lmms, 0.1.2-cvs20060130, tobydox/at/users/dot/sourceforge/dot/net) +AM_INIT_AUTOMAKE(lmms, 0.1.2-cvs20060130) AM_CONFIG_HEADER(config.h) @@ -162,16 +162,15 @@ AC_ARG_WITH(vst, [enable support for builtin VST-plugin-hosting]), [ with_vst=yes ], [ with_vst=no ]) AH_TEMPLATE(HAVE_VST_AEFFECTX_H, [Define to 1 if you have the header file.]) if test "x$with_vst" = "xyes" ; then + + ORIG_CFLAGS=$CFLAGS + ORIG_CPPFLAGS=$CPPFLAGS + CFLAGS="$CLFAGS -I./include" + CPPFLAGS="$CPPFLAGS -I./include" AC_CHECK_HEADER(aeffectx.h, HAVE_VST_AEFFECTX_H="true") - if test -z "$HAVE_VST_AEFFECTX_H" ; then - ORIG_CFLAGS=$CFLAGS - ORIG_CPPFLAGS=$CPPFLAGS - CFLAGS="$CLFAGS -I./include" - CPPFLAGS="$CPPFLAGS -I./include" - AC_CHECK_HEADER(aeffectx.h, HAVE_VST_AEFFECTX_H="true") - CFLAGS="$ORIG_CFLAGS" - CPPFLAGS="$ORIG_CPPFLAGS" - fi + CFLAGS="$ORIG_CFLAGS" + CPPFLAGS="$ORIG_CPPFLAGS" + AC_CHECK_PROG(WINEGCC, winegcc, /usr/bin/winegcc,,/usr/bin) AC_CHECK_LIB([wine], [wine_init]) if test ! -z "$WINEGCC" ; then @@ -649,8 +648,8 @@ else echo " = http://www.steinberg.de/Steinberg/ygrabit/vstsdk/OnlineDoc/source/common/AEffect.h" echo " = http://www.steinberg.de/Steinberg/ygrabit/vstsdk/OnlineDoc/source/common/aeffectx.h" echo " =" - echo " = and put the mentioned files into /usr/include/ or ./include. Make sure, they're" - echo " = patched with file vst_sdk23_headers.diff!" + echo " = and put the mentioned files into /usr/include/ or ./include. Make sure," + echo "= they're patched with file vst_sdk23_headers.diff!" echo " = Otherwise (now!) configure will disable LMMS's support for built-in VST-" echo " = plugin-usage. If you do not intend to use VST-plugins with LMMS you can " echo " = ignore this warning." diff --git a/plugins/vestige/Makefile.am b/plugins/vestige/Makefile.am index c87fd679d..536d1d4b2 100644 --- a/plugins/vestige/Makefile.am +++ b/plugins/vestige/Makefile.am @@ -21,19 +21,26 @@ EMBEDDED_RESOURCES = $(wildcard *png) EXTRA_DIST = $(EMBEDDED_RESOURCES) -CLEANFILES = $(MOC_FILES) ./embedded_resources.h xfst.exe.so pkglib_LTLIBRARIES = libvestige.la -libvestige_la_SOURCES = vestige.cpp vestige.h fstclient.cpp fstclient.h +libvestige_la_SOURCES = vestige.cpp vestige.h lvsl_client.cpp lvsl_client.h $(libvestige_la_SOURCES): ./embedded_resources.h -CC = winegcc -pkglib_PROGRAMS = xfst_server -xfst_server_SOURCES = fstserver.cpp fstcore.c fstserver.h communication.h -xfst_server_LDFLAGS = -mwindows -L/usr/X11R6/lib -lX11 -lpthread -o $(pkglib_PROGRAMS) -xfst_server_LINK = wineg++ + +CC = wineg++ +pkglib_PROGRAMS = lvsl_server +lvsl_server_SOURCES = lvsl_server.c communication.h +lvsl_server_LDFLAGS = -mwindows -L/usr/X11R6/lib -lX11 -lpthread -o $(pkglib_PROGRAMS) +lvsl_server_LINK = wineg++ nobase_pkglib_DATA = $(pkglib_PROGRAMS).exe.so +CLEANFILES = $(MOC_FILES) ./embedded_resources.h $(nobase_pkglib_DATA) + + +install-exec-hook: + cd $(DESTDIR)$(pkglibdir) ; \ + strip $(nobase_pkglib_DATA) + diff --git a/plugins/vestige/communication.h b/plugins/vestige/communication.h index 2de5c0cb3..b03850bd6 100644 --- a/plugins/vestige/communication.h +++ b/plugins/vestige/communication.h @@ -1,8 +1,14 @@ #ifndef _COMMUNICATION_H #define _COMMUNICATION_H +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_UNISTD_H #include -#include +#endif + #include diff --git a/plugins/vestige/lvsl_client.cpp b/plugins/vestige/lvsl_client.cpp new file mode 100644 index 000000000..6c22a165b --- /dev/null +++ b/plugins/vestige/lvsl_client.cpp @@ -0,0 +1,538 @@ +/* + * lvsl_client.cpp - client for LVSL Server + * + * Copyright (c) 2005-2006 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include "qxembed.h" + +#define QX11EmbedWidget QXEmbed +#define embedInto embed + +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#include + +#ifdef HAVE_SYS_IPC_H +#include +#endif + +#ifdef HAVE_SYS_SHM_H +#include +#endif + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#ifdef HAVE_SYS_TIME_H +#include +#endif + + +#include "templates.h" +#include "config_mgr.h" +#include "lmms_main_win.h" +#include "lvsl_client.h" + + + +remoteVSTPlugin::remoteVSTPlugin( const QString & _plugin ) : + m_failed( TRUE ), + m_plugin( _plugin ), + m_pluginWidget( NULL ), + m_pluginWID( 0 ), + m_pluginPID( -1 ), + m_serverInFD( -1 ), + m_serverOutFD( -1 ), + m_serverMutex(), + m_name( "" ), + m_version( 0 ), + m_vendorString( "" ), + m_productString( "" ), + m_inputCount( 0 ), + m_outputCount( 0 ), + m_shmID( -1 ), + m_shm( NULL ), + m_shmSize( 0 ) +{ + pipe( m_pipes[0] ); + pipe( m_pipes[1] ); + + if( ( m_pluginPID = fork() ) < 0 ) + { + printf( "fork() failed!\n" ); + return; + } + else if( m_pluginPID == 0 ) + { + dup2( m_pipes[0][0], 0 ); + dup2( m_pipes[1][1], 1 ); + QString lvsl_server_exec = configManager::inst()->pluginDir() + + "lvsl_server"; + execlp( lvsl_server_exec. +#ifdef QT4 + toAscii().constData(), +#else + ascii(), +#endif + lvsl_server_exec. +#ifdef QT4 + toAscii().constData(), +#else + ascii(), +#endif + "", NULL ); + return; + } + m_serverInFD = m_pipes[1][0]; + m_serverOutFD = m_pipes[0][1]; + + lock(); + writeValueS( VST_LOAD_PLUGIN ); + writeStringS( m_plugin. +#ifdef QT4 + toAscii().constData() +#else + ascii() +#endif + ); + unlock(); + + while( 1 ) + { + Sint16 cmd = VST_UNDEFINED_CMD; + if( messagesLeft() == TRUE ) + { + cmd = processNextMessage(); + } + if( cmd == VST_INITIALIZATION_DONE ) + { + m_failed = FALSE; + break; + } + else if( cmd == VST_FAILED_LOADING_PLUGIN ) + { + break; + } +#ifdef QT4 + QApplication::processEvents( QEventLoop::AllEvents, 50 ); +#else + qApp->processEvents( 50 ); +#endif + } +} + + + + +remoteVSTPlugin::~remoteVSTPlugin() +{ + if( m_failed == FALSE ) + { + setShmKeyAndSize( 0, 0 ); + // tell server to quit and wait for acknowledge + writeValueS( VST_CLOSE_PLUGIN ); + QTime t; + t.start(); + while( t.elapsed() < 1000 ) + { + if( messagesLeft() == TRUE && + processNextMessage() == VST_QUIT_ACK ) + { + //m_pluginPID = 0; + break; + } + } + if( m_pluginWidget != NULL ) + { + m_pluginWidget->hide(); + delete m_pluginWidget; + } + // timeout? +/* if( m_pluginPID != 0 ) + {*/ + kill( m_pluginPID, SIGTERM ); + //} + + // close all sides of our pipes + close( m_pipes[0][0] ); + close( m_pipes[0][1] ); + close( m_pipes[1][0] ); + close( m_pipes[1][1] ); +/* close( m_serverInFD ); + close( m_serverOutFD );*/ + } +} + + + +#include + + +void remoteVSTPlugin::showEditor( void ) +{ + if( m_pluginWidget != NULL ) + { + m_pluginWidget->show(); + return; + } + if( m_pluginWID == 0 ) + { + return; + } + + XWindowAttributes attr; + XGetWindowAttributes( +#ifdef QT4 + QX11Info::display(), +#else + qt_xdisplay(), +#endif + m_pluginWID, &attr ); + + m_pluginWidget = new QWidget( lmmsMainWin::inst()->workspace() ); + m_pluginWidget->setFixedSize( attr.width, attr.height ); + m_pluginWidget->setWindowTitle( name() ); + + QX11EmbedWidget * xe = new QX11EmbedWidget( m_pluginWidget ); + xe->embedInto( m_pluginWID ); + xe->setFixedSize( attr.width, attr.height ); + //xe->setAutoDelete( FALSE ); + m_pluginWidget->show(); + xe->show(); + + lock(); + writeValueS( VST_SHOW_EDITOR ); + unlock(); +} + + + + +void remoteVSTPlugin::hideEditor( void ) +{ + if( m_pluginWidget != NULL ) + { + m_pluginWidget->hide(); + } +} + + + + +void remoteVSTPlugin::process( const sampleFrame * _in_buf, + sampleFrame * _out_buf ) +{ + const Uint32 frames = mixer::inst()->framesPerAudioBuffer(); + + if( m_shm == NULL ) + { + // m_shm being zero means we didn't initialize everything so + // far so process one message each time (and hope we get + // information like SHM-key etc.) until we process + // messages in later stage of this procedure + if( m_shmSize == 0 && messagesLeft() == TRUE ) + { + (void) processNextMessage(); + } + mixer::inst()->clearAudioBuffer( _out_buf, frames ); + return; + } + + memset( m_shm, 0, m_shmSize ); + + Uint8 inputs = tMax( m_inputCount, DEFAULT_CHANNELS ); + + if( _in_buf != NULL && inputs > 0 ) + { + for( Uint8 ch = 0; ch < inputs; ++ch ) + { + for( Uint32 frame = 0; frame < frames; ++frame ) + { + m_shm[ch*frames+frame] = _in_buf[frame][ch]; + } + } + } + + lock(); + writeValueS( VST_PROCESS ); + unlock(); + + if( _out_buf != NULL && m_outputCount > 0 ) + { + // wait until server signals that process()ing is done + while( processNextMessage() != VST_PROCESS_DONE ) + { + // allow better scheduling (we're just waiting) + usleep( 10 ); + } + + Uint8 outputs = tMax( m_outputCount, DEFAULT_CHANNELS ); + if( outputs != DEFAULT_CHANNELS ) + { + // clear buffer, if plugin doesn't fill up both channels + mixer::inst()->clearAudioBuffer( _out_buf, frames ); + } + for( Uint8 ch = 0; ch < outputs; ++ch ) + { + for( Uint32 frame = 0; frame < frames; ++frame ) + { + _out_buf[frame][ch] = m_shm[(m_inputCount+ch)* + frames+frame]; + } + } + } +} + + + + +void remoteVSTPlugin::enqueueMidiEvent( const midiEvent & _event, + const Uint32 _frames_ahead ) +{ + lock(); + writeValueS( VST_ENQUEUE_MIDI_EVENT ); + writeValueS( _event ); + writeValueS( _frames_ahead ); + unlock(); +} + + + + +void remoteVSTPlugin::setBPM( Uint16 _bpm ) +{ + lock(); + writeValueS( VST_BPM ); + writeValueS( _bpm ); + unlock(); +} + + + + +const QMap & remoteVSTPlugin::parameterDump( void ) +{ + writeValueS( VST_GET_PARAMETER_DUMP ); + + while( processNextMessage() != VST_PARAMETER_DUMP ) + { + } + + return( m_parameterDump ); +} + + + + +void remoteVSTPlugin::setParameterDump( const QMap & _pdump ) +{ + writeValueS( VST_SET_PARAMETER_DUMP ); + writeValueS( _pdump.size() ); + for( QMap::const_iterator it = _pdump.begin(); + it != _pdump.end(); ++it ) + { + vstParameterDumpItem dump_item = + { + ( *it ).section( ':', 0, 0 ).toInt(), + "", + ( *it ).section( ':', 1, 1 ).toFloat() + } ; + writeValueS( dump_item ); + } +} + + + + +void remoteVSTPlugin::setShmKeyAndSize( Uint16 _key, size_t _size ) +{ + if( m_shm != NULL && m_shmSize > 0 ) + { + shmdt( m_shm ); + m_shm = NULL; + m_shmSize = 0; + } + // only called for detaching SHM? + if( _size == 0 ) + { + return; + } + + int shm_id = shmget( _key, _size, 0 ); + if( shm_id == -1 ) + { + printf( "failed getting shared memory\n" ); + } + else + { + m_shm = (float *) shmat( shm_id, 0, 0 ); + // TODO: error-checking + } +} + + + + +void remoteVSTPlugin::setPluginXID( const Sint32 _plugin_xid ) +{ + m_pluginWID = _plugin_xid; +} + + + + +bool remoteVSTPlugin::messagesLeft( void ) const +{ + fd_set rfds; + FD_ZERO( &rfds ); + FD_SET( m_serverInFD, &rfds ); + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 1; // can we use 0 here? + return( select( m_serverInFD + 1, &rfds, NULL, NULL, &tv ) > 0 ); +} + + + + +Sint16 remoteVSTPlugin::processNextMessage( void ) +{ + fd_set rfds; + FD_ZERO( &rfds ); + FD_SET( m_serverInFD, &rfds ); + if( select( m_serverInFD + 1, &rfds, NULL, NULL, NULL ) <= 0 ) + { + return( VST_UNDEFINED_CMD ); + } + + lock(); + Sint16 cmd = readValueS(); + switch( cmd ) + { + case VST_DEBUG_MSG: + printf( "debug message from server: %s\n", + readStringS().c_str() ); + break; + + case VST_GET_SAMPLE_RATE: + writeValueS( VST_SAMPLE_RATE ); + // handle is the same + writeValueS( mixer::inst()->sampleRate() ); + break; + + case VST_GET_BUFFER_SIZE: + writeValueS( VST_BUFFER_SIZE ); + // handle is the same + writeValueS( + mixer::inst()->framesPerAudioBuffer() ); + break; + + case VST_SHM_KEY_AND_SIZE: + { + Uint16 shm_key = readValueS(); + size_t shm_size = readValueS(); + setShmKeyAndSize( shm_key, shm_size ); + break; + } + + case VST_INPUT_COUNT: + m_inputCount = readValueS(); + break; + + case VST_OUTPUT_COUNT: + m_outputCount = readValueS(); + break; + + case VST_PLUGIN_XID: + setPluginXID( readValueS() ); + break; + + case VST_PLUGIN_NAME: + m_name = readStringS().c_str(); + break; + + case VST_PLUGIN_VERSION: + m_version = readValueS(); + break; + + case VST_PLUGIN_VENDOR_STRING: + m_vendorString = readStringS().c_str(); + break; + + case VST_PLUGIN_PRODUCT_STRING: + m_productString = readStringS().c_str(); + break; + + case VST_PARAMETER_DUMP: + { + m_parameterDump.clear(); + const Sint32 num_params = readValueS(); + for( Sint32 i = 0; i < num_params; ++i ) + { + vstParameterDumpItem dump_item = + readValueS(); + m_parameterDump["param" + QString::number( dump_item.index )] = + QString::number( dump_item.index ) + ":" + +// QString( dump_item.shortLabel ) + ":" + + QString::number( dump_item.value ); + } + break; + } + + case VST_PROCESS_DONE: + case VST_QUIT_ACK: + case VST_UNDEFINED_CMD: + default: + break; + } + unlock(); + + return( cmd ); +} + + diff --git a/plugins/vestige/lvsl_client.h b/plugins/vestige/lvsl_client.h new file mode 100644 index 000000000..b7ae03015 --- /dev/null +++ b/plugins/vestige/lvsl_client.h @@ -0,0 +1,180 @@ +/* + * lvsl_client.h - client for LVSL Server + * + * Copyright (c) 2005-2006 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _LVSL_CLIENT_H +#define _LVSL_CLIENT_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "mixer.h" +#include "communication.h" +#include "midi.h" + + + +class remoteVSTPlugin +{ +public: + remoteVSTPlugin( const QString & _plugin ); + ~remoteVSTPlugin(); + + void showEditor( void ); + void hideEditor( void ); + + inline const QString & name( void ) const + { + return( m_name ); + } + + inline Sint32 version( void ) const + { + return( m_version ); + } + + inline const QString & vendorString( void ) const + { + return( m_vendorString ); + } + + inline const QString & productString( void ) const + { + return( m_productString ); + } + + void FASTCALL process( const sampleFrame * _in_buf, + sampleFrame * _out_buf ); + void FASTCALL enqueueMidiEvent( const midiEvent & _event, + const Uint32 _frames_ahead ); + void FASTCALL setBPM( const Uint16 _bpm ); + + const QMap & parameterDump( void ); + void setParameterDump( const QMap & _pdump ); + + + inline Uint8 inputCount( void ) const + { + return( m_inputCount ); + } + + inline Uint8 outputCount( void ) const + { + return( m_outputCount ); + } + + inline QWidget * pluginWidget( void ) + { + return( m_pluginWidget ); + } + + inline bool failed( void ) const + { + return( m_failed ); + } + + +private: + template + inline T readValueS( void ) const + { + return( ::readValue( m_serverInFD ) ); + } + + template + inline void writeValueS( const T & _i ) const + { + ::writeValue( _i, m_serverOutFD ); + } + + inline std::string readStringS( void ) const + { + return( ::readString( m_serverInFD ) ); + } + + inline void writeStringS( const char * _str ) const + { + ::writeString( _str, m_serverOutFD ); + } + + inline void lock( void ) + { + m_serverMutex.lock(); + } + + inline void unlock( void ) + { + m_serverMutex.unlock(); + } + + bool messagesLeft( void ) const; + Sint16 processNextMessage( void ); + + void FASTCALL setShmKeyAndSize( const Uint16 _key, const size_t _size ); + void FASTCALL setPluginXID( const Sint32 _plugin_xid ); + + + + bool m_failed; + QString m_plugin; + QWidget * m_pluginWidget; + Sint32 m_pluginWID; + + int m_pluginPID; + int m_pipes[2][2]; + int m_serverInFD; + int m_serverOutFD; + + QMutex m_serverMutex; + + QString m_name; + Sint32 m_version; + QString m_vendorString; + QString m_productString; + + QMap m_parameterDump; + + Uint8 m_inputCount; + Uint8 m_outputCount; + + int m_shmID; + float * m_shm; + size_t m_shmSize; + +} ; + + +#endif diff --git a/plugins/vestige/lvsl_server.c b/plugins/vestige/lvsl_server.c new file mode 100644 index 000000000..2e1fb4437 --- /dev/null +++ b/plugins/vestige/lvsl_server.c @@ -0,0 +1,1164 @@ +/* + * lvsl_server.cpp - LMMS VST Support Layer Server + * + * Copyright (c) 2005-2006 Tobias Doerffel + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * Code partly taken from XFST: + * Copyright (c) 2004 Paul Davis + * Copyright (c) 2004 Torben Hohn + * Copyright (c) 2002 Kjetil S. Matheussen + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_PTHREAD_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_IPC_H +#include +#endif + +#ifdef HAVE_SYS_SHM_H +#include +#endif + +#ifdef HAVE_STDARG_H +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + +#include +#include + + +#include +#include + + +#include "types.h" +#include "midi.h" +#include "communication.h" + + +#include + +#if kVstVersion < 2400 + +#define OLD_VST_SDK + +#define VstInt32 long int +#define VstIntPtr long int + +struct ERect +{ + short top; + short left; + short bottom; + short right; +} ; + +#endif + + +#ifdef HAVE_TLS +static __thread int ejmpbuf_valid = false; +static __thread jmp_buf ejmpbuf; +#else +static pthread_key_t ejmpbuf_valid_key; +static pthread_key_t ejmpbuf_key; +#endif + + +class VSTPlugin; + +VSTPlugin * plugin = NULL; + + + +void lvsMessage( const char * _fmt, ... ) +{ + va_list ap; + char buffer[512]; + + va_start( ap, _fmt ); + vsnprintf( buffer, sizeof( buffer ), _fmt, ap ); + writeValue( VST_DEBUG_MSG, 1 ); + writeString( buffer, 1 ); + va_end( ap ); +} + + + + +class VSTPlugin +{ +public: + VSTPlugin( const std::string & _plugin_file ); + + ~VSTPlugin(); + + void showEditor( void ) + { + if( m_window != NULL ) + { + PostThreadMessageA( m_guiThreadID, WM_USER, SHOW_EDITOR, + 0 ); + } + } + + void process( void ); + + // enqueue given MIDI-event to be processed the next time process() is + // called + void enqueueMidiEvent( const midiEvent & _event, + const Uint32 _frames_ahead ); + + // set given sample-rate for plugin + void setSampleRate( const Sint32 _rate ) + { + m_plugin->dispatcher( m_plugin, effSetSampleRate, 0, 0, NULL, + (float) _rate ); + } + + // set given block-size for plugin + void setBlockSize( const Uint32 _bsize ); + + // set given tempo + void setBPM( const Uint16 _bpm ) + { + m_bpm = _bpm; + } + + // determine VST-version the plugin uses + Sint32 pluginVersion( void ) const + { + return( m_plugin->dispatcher( m_plugin, + effGetVendorVersion, 0, 0, NULL, 0.0f ) ); + } + + // determine name of plugin + const char * pluginName( void ) const; + + // determine vendor of plugin + const char * pluginVendorString( void ) const; + + // determine product-string of plugin + const char * pluginProductString( void ) const; + + // do a complete parameter-dump and post it + void getParameterDump( void ) const; + + // read parameter-dump and set it for plugin + void setParameterDump( void ); + + // post properties of specified parameter + void getParameterProperties( const Sint32 _idx ); + + // number of inputs + Uint8 inputCount( void ) const + { + return( m_plugin->numInputs ); + } + + // number of outputs + Uint8 outputCount( void ) const + { + return( m_plugin->numOutputs ); + } + + // called once at initialization and everytime number of inputs/outputs + // or block-size changes and is responsible for resizing shared memory + // and inform client about changed shm-keys, input/output-count etc. + void resizeSharedMemory( void ); + + +private: + enum guiThreadMessages + { + NONE, SHOW_EDITOR, CLOSE_PLUGIN + } ; + + // callback used by plugin for being able to communicate with it's host + static VstIntPtr hostCallback( AEffect * _effect, VstInt32 _opcode, + VstInt32 _index, VstIntPtr _value, + void * _ptr, float _opt ); + + static DWORD WINAPI guiEventLoop( LPVOID _param ); + + + bool load( const std::string & _plugin_file ); + + + std::string m_shortName; + + HINSTANCE m_libInst; + + AEffect * m_plugin; + HWND m_window; + int m_windowXID; + + pthread_mutex_t m_lock; + pthread_cond_t m_windowStatusChange; + DWORD m_guiThreadID; + + + Uint32 m_blockSize; + float * m_shm; + + float * * m_inputs; + float * * m_outputs; + + std::vector m_midiEvents; + + Uint16 m_bpm; + +} ; + + + + +VSTPlugin::VSTPlugin( const std::string & _plugin_file ) : + m_shortName( "" ), + m_libInst( NULL ), + m_plugin( NULL ), + m_window( NULL ), + m_windowXID( 0 ), + m_lock(), + m_windowStatusChange(), + m_guiThreadID( 0 ), + m_blockSize( 0 ), + m_shm( NULL ), + m_inputs( NULL ), + m_outputs( NULL ), + m_midiEvents(), + m_bpm( 0 ) +{ + if( load( _plugin_file ) == false ) + { + writeValue( VST_FAILED_LOADING_PLUGIN ); + return; + } + + /* set program to zero */ + /* i comment this out because it breaks dfx Geometer + * looks like we cant set programs for it + * + m_plugin->dispatcher( m_plugin, effSetProgram, 0, 0, NULL, 0.0f); */ + // request rate and blocksize + writeValue( VST_GET_SAMPLE_RATE ); + writeValue( VST_GET_BUFFER_SIZE ); + + + m_plugin->dispatcher( m_plugin, effMainsChanged, 0, 1, NULL, 0.0f ); + + + if( CreateThread( NULL, 0, guiEventLoop, this, 0, NULL ) == NULL ) + { + lvsMessage( "could not create GUI-thread" ); + return; + } + pthread_cond_wait( &m_windowStatusChange, &m_lock); + + + // now post some information about our plugin + writeValue( VST_PLUGIN_XID ); + writeValue( m_windowXID ); + + writeValue( VST_PLUGIN_NAME ); + writeString( pluginName() ); + + writeValue( VST_PLUGIN_VERSION ); + writeValue( pluginVersion() ); + + writeValue( VST_PLUGIN_VENDOR_STRING ); + writeString( pluginVendorString() ); + + writeValue( VST_PLUGIN_PRODUCT_STRING ); + writeString( pluginProductString() ); + + writeValue( VST_PARAMETER_COUNT ); + writeValue( (Sint32) m_plugin->numParams ); + + // tell client that we've done everything so far + writeValue( VST_INITIALIZATION_DONE ); +} + + + + +VSTPlugin::~VSTPlugin() +{ + // acknowledge quit + writeValue( VST_QUIT_ACK ); + + if( m_window != NULL ) + { + // notify GUI-thread + if( !PostThreadMessageA( m_guiThreadID, WM_USER, + CLOSE_PLUGIN, 0 ) ) + { + //lvsMessage( "could not post message to gui thread" ); + } + pthread_cond_wait( &m_windowStatusChange, &m_lock ); + m_plugin->dispatcher( m_plugin, effEditClose, 0, 0, NULL, 0.0 ); + CloseWindow( m_window ); + m_window = NULL; + } + + if( m_libInst != NULL ) + { + FreeLibrary( m_libInst ); + m_libInst = NULL; + } + + delete[] m_inputs; + delete[] m_outputs; + + if( m_shm != NULL ) + { + shmdt( m_shm ); + } +} + + + + +bool VSTPlugin::load( const std::string & _plugin_file ) +{ + if( ( m_libInst = LoadLibraryA( _plugin_file.c_str() ) ) == + NULL ) + { + return( false ); + } + + char * tmp = strdup( _plugin_file.c_str() ); + m_shortName = basename( tmp ); + free( tmp ); + + typedef AEffect * ( * mainEntry )( audioMasterCallback ); + mainEntry main_entry = (mainEntry) GetProcAddress( m_libInst, + "main" ); + if( main_entry == NULL ) + { + return( false ); + } + + m_plugin = main_entry( hostCallback ); + if( m_plugin == NULL ) + { + return( false ); + } + + m_plugin->user = this; + + if( m_plugin->magic != kEffectMagic ) + { + lvsMessage( "%s is not a VST plugin\n", _plugin_file.c_str() ); + } + + m_plugin->dispatcher( m_plugin, effOpen, 0, 0, 0, 0 ); + + return( true ); +} + + + +void VSTPlugin::process( void ) +{ + // first we gonna post all MIDI-events we enqueued so far + if( m_midiEvents.size() ) + { + // since MIDI-events are not received immediately, we + // have to have them stored somewhere even after + // dispatcher-call, so we create static copies of the + // data and post them +#define MIDI_EVENT_BUFFER_COUNT 1024 + static char event_buf[sizeof(VstMidiEvent * ) * + MIDI_EVENT_BUFFER_COUNT + + sizeof( VstEvents )]; + static VstMidiEvent vme[MIDI_EVENT_BUFFER_COUNT]; + VstEvents * events = (VstEvents *) event_buf; + events->reserved = 0; + events->numEvents = m_midiEvents.size(); + for( unsigned int i = 0; i < m_midiEvents.size(); ++i ) + { + memcpy( &vme[i], &m_midiEvents[i], + sizeof( VstMidiEvent ) ); + events->events[i] = (VstEvent *) &vme[i]; + } + m_midiEvents.clear(); + m_plugin->dispatcher( m_plugin, effProcessEvents, 0, 0, events, + 0.0f ); + } + + // now we're ready to fetch sound from VST-plugin + + for( int i = 0; i < inputCount(); ++i ) + { + m_inputs[i] = &m_shm[i * m_blockSize]; + } + + for( int i = 0; i < outputCount(); ++i ) + { + m_outputs[i] = &m_shm[( i + inputCount() ) * + m_blockSize]; + } + +#ifdef OLD_VST_SDK + if( m_plugin->flags & effFlagsCanReplacing ) + { +#endif + m_plugin->processReplacing( m_plugin, m_inputs, + m_outputs, + m_blockSize ); +#ifdef OLD_VST_SDK + } + else + { + m_plugin->process( m_plugin, m_inputs, m_outputs, + m_blockSize ); + } +#endif + + writeValue( VST_PROCESS_DONE ); + + // give plugin some idle-time for GUI-update and so on... + m_plugin->dispatcher( m_plugin, effEditIdle, 0, 0, NULL, 0 ); + +} + + + + +void VSTPlugin::enqueueMidiEvent( const midiEvent & _event, + const Uint32 _frames_ahead ) +{ + VstMidiEvent event; + + event.type = kVstMidiType; + event.byteSize = 24; + event.deltaFrames = _frames_ahead; + event.flags = 0; + event.detune = 0; + event.noteLength = 0; + event.noteOffset = 0; + event.noteOffVelocity = 0; + event.reserved1 = 0; + event.reserved2 = 0; + event.midiData[0] = _event.m_type + _event.m_channel; + event.midiData[1] = _event.key(); + event.midiData[2] = _event.velocity(); + event.midiData[3] = 0; + m_midiEvents.push_back( event ); +} + + + + +void VSTPlugin::setBlockSize( const Uint32 _bsize ) +{ + if( _bsize == m_blockSize ) + { + return; + } + m_blockSize = _bsize; + resizeSharedMemory(); + m_plugin->dispatcher( m_plugin, effSetBlockSize, 0, _bsize, NULL, + 0.0f ); +} + + + + +const char * VSTPlugin::pluginName( void ) const +{ + static char buf[32]; + buf[0] = 0; + m_plugin->dispatcher( m_plugin, effGetEffectName, 0, 0, buf, 0.0f ); + buf[31] = 0; + return( buf ); +} + + + + +const char * VSTPlugin::pluginVendorString( void ) const +{ + static char buf[64]; + buf[0] = 0; + m_plugin->dispatcher( m_plugin, effGetVendorString, 0, 0, buf, 0.0f ); + buf[63] = 0; + return( buf ); +} + + + + +const char * VSTPlugin::pluginProductString( void ) const +{ + static char buf[64]; + buf[0] = 0; + m_plugin->dispatcher( m_plugin, effGetProductString, 0, 0, buf, 0.0f ); + buf[63] = 0; + return( buf ); +} + + + + +void VSTPlugin::getParameterDump( void ) const +{ + VstParameterProperties vst_props; + vstParameterDumpItem dump_item; + writeValue( VST_PARAMETER_DUMP ); + writeValue( (Sint32) m_plugin->numParams - 1 ); + for( Sint32 i = 0; i < m_plugin->numParams - 1; ++i ) + { + dump_item.index = i; + m_plugin->dispatcher( m_plugin, effGetParameterProperties, i, 0, + &vst_props, 0.0f ); + memcpy( dump_item.shortLabel, vst_props.shortLabel, + sizeof( dump_item.shortLabel ) ); + dump_item.value = m_plugin->getParameter( m_plugin, i ); + writeValue( dump_item ); + } +} + + + + +void VSTPlugin::setParameterDump( void ) +{ + const Sint32 sz = readValue(); + const Sint32 params = ( sz > m_plugin->numParams - 1 ) ? + m_plugin->numParams - 1 : sz; + for( Sint32 i = 0; i < params; ++i ) + { + vstParameterDumpItem dump_item = + readValue(); + m_plugin->setParameter( m_plugin, dump_item.index, + dump_item.value ); + } +} + + + + +void VSTPlugin::getParameterProperties( const Sint32 _idx ) +{ + VstParameterProperties vst_props; + m_plugin->dispatcher( m_plugin, effGetParameterProperties, _idx, 0, + &vst_props, 0.0f ); + vstParamProperties props; + memcpy( props.label, vst_props.label, sizeof( props.label ) ); + memcpy( props.shortLabel, vst_props.shortLabel, + sizeof( props.shortLabel) ); + memcpy( props.categoryLabel, vst_props.categoryLabel, + sizeof( props.categoryLabel ) ); + props.minValue = vst_props.minInteger; + props.maxValue = vst_props.maxInteger; + props.step = ( vst_props.flags & kVstParameterUsesFloatStep ) ? + vst_props.stepFloat : + vst_props.stepInteger; + props.category = vst_props.category; + writeValue( VST_PARAMETER_PROPERTIES ); + writeValue( props ); +} + + + + +void VSTPlugin::resizeSharedMemory( void ) +{ + delete[] m_inputs; + delete[] m_outputs; + + size_t s = ( inputCount() + outputCount() ) * m_blockSize * + sizeof( float ); + if( m_shm != NULL ) + { + shmdt( m_shm ); + } + + int shm_id; + Uint16 shm_key = 0; + while( ( shm_id = shmget( ++shm_key, s, IPC_CREAT | IPC_EXCL | + 0666 ) ) == -1 ) + { + } + + m_shm = (float *) shmat( shm_id, 0, 0 ); + + if( inputCount() > 0 ) + { + m_inputs = new float * [inputCount()]; + } + if( outputCount() > 0 ) + { + m_outputs = new float * [outputCount()]; + } + + writeValue( VST_INPUT_COUNT ); + writeValue( inputCount() ); + + writeValue( VST_OUTPUT_COUNT ); + writeValue( outputCount() ); + + writeValue( VST_SHM_KEY_AND_SIZE ); + writeValue( shm_key ); + writeValue( s ); +} + + + +#define DEBUG_CALLBACKS +#ifdef DEBUG_CALLBACKS +#define SHOW_CALLBACK lvsMessage +#else +#define SHOW_CALLBACK(...) +#endif + + +VstIntPtr VSTPlugin::hostCallback( AEffect * _effect, VstInt32 _opcode, + VstInt32 _index, VstIntPtr _value, + void * _ptr, float _opt ) +{ + static VstTimeInfo _timeInfo; + //SHOW_CALLBACK( "host-callback, opcode = %d\n", (int) _opcode ); + switch( _opcode ) + { + case audioMasterAutomate: + //SHOW_CALLBACK( "amc: audioMasterAutomate\n" ); + // index, value, returns 0 + _effect->setParameter( _effect, _index, _opt ); + return( 0 ); + + case audioMasterVersion: + //SHOW_CALLBACK( "amc: audioMasterVersion\n" ); + // vst version, currently 2 (0 for older) + return( 2 ); + + case audioMasterCurrentId: + SHOW_CALLBACK( "amc: audioMasterCurrentId\n" ); + // returns the unique id of a plug that's currently + // loading + return( 0 ); + + case audioMasterIdle: + //SHOW_CALLBACK ("amc: audioMasterIdle\n" ); + // call application idle routine (this will + // call effEditIdle for all open editors too) + _effect->dispatcher( _effect, effEditIdle, 0, 0, NULL, + 0.0f ); + return( 0 ); + + case audioMasterPinConnected: + SHOW_CALLBACK( "amc: audioMasterPinConnected\n" ); + // inquire if an input or output is beeing connected; + // index enumerates input or output counting from zero: + // value is 0 for input and != 0 otherwise. note: the + // return value is 0 for such that older versions + // will always return true. + return( 1 ); + + case audioMasterGetTime: + //SHOW_CALLBACK( "amc: audioMasterGetTime\n" ); + // returns const VstTimeInfo* (or 0 if not supported) + // should contain a mask indicating which + // fields are required (see valid masks above), as some + // items may require extensive conversions + + memset( &_timeInfo, 0, sizeof( _timeInfo ) ); + + _timeInfo.samplePos = 0; + // TODO: _timeInfo.sampleRate = mixer::inst()->sampleRate(); + _timeInfo.flags = 0; + _timeInfo.tempo = plugin->m_bpm; + _timeInfo.timeSigNumerator = 4; + _timeInfo.timeSigDenominator = 4; + _timeInfo.flags |= (/* kVstBarsValid|*/kVstTempoValid ); + _timeInfo.flags |= kVstTransportPlaying; + + return( (long) &_timeInfo ); + + case audioMasterProcessEvents: + //SHOW_CALLBACK( "amc: audioMasterProcessEvents\n" ); + // VstEvents* in + return( 0 ); + + case audioMasterIOChanged: + plugin->resizeSharedMemory(); + SHOW_CALLBACK( "amc: audioMasterIOChanged\n" ); + // numInputs and/or numOutputs has changed + return( 0 ); + +#ifdef OLD_VST_SDK + case audioMasterWantMidi: + //SHOW_CALLBACK( "amc: audioMasterWantMidi\n" ); + // is a filter which is currently ignored + return( 0 ); + + case audioMasterSetTime: + SHOW_CALLBACK( "amc: audioMasterSetTime\n" ); + // VstTimenfo* in , filter in , not + // supported + + case audioMasterTempoAt: + SHOW_CALLBACK( "amc: audioMasterTempoAt\n" ); + return( plugin->m_bpm * 10000 ); + + case audioMasterGetNumAutomatableParameters: + SHOW_CALLBACK( "amc: audioMasterGetNumAutomatable" + "Parameters\n" ); + return( 0 ); + + case audioMasterGetParameterQuantization: + SHOW_CALLBACK( "amc: audioMasterGetParameter\n" + "Quantization\n" ); + // returns the integer value for +1.0 representation, + // or 1 if full single float precision is maintained + // in automation. parameter index in (-1: all, + // any) + return( 0 ); + + case audioMasterNeedIdle: + //SHOW_CALLBACK( "amc: audioMasterNeedIdle\n" ); + // plug needs idle calls (outside its editor window) + return( 0 ); + + case audioMasterGetPreviousPlug: + SHOW_CALLBACK( "amc: audioMasterGetPreviousPlug\n" ); + // input pin in (-1: first to come), returns + // cEffect* + return( 0 ); + + case audioMasterGetNextPlug: + SHOW_CALLBACK( "amc: audioMasterGetNextPlug\n" ); + // output pin in (-1: first to come), returns + // cEffect* + return( 0 ); + + case audioMasterWillReplaceOrAccumulate: + SHOW_CALLBACK( "amc: audioMasterWillReplaceOr" + "Accumulate\n" ); + // returns: 0: not supported, 1: replace, 2: accumulate + return( 0 ); + + case audioMasterGetSpeakerArrangement: + SHOW_CALLBACK( "amc: audioMasterGetSpeaker" + "Arrangement\n" ); + // (long)input in , output in + return( 0 ); + + case audioMasterSetOutputSampleRate: + SHOW_CALLBACK( "amc: audioMasterSetOutputSample" + "Rate\n" ); + // for variable i/o, sample rate in + return( 0 ); + + case audioMasterSetIcon: + SHOW_CALLBACK( "amc: audioMasterSetIcon\n" ); + // TODO + // void* in , format not defined yet + return( 0 ); + + case audioMasterOpenWindow: + SHOW_CALLBACK( "amc: audioMasterOpenWindow\n" ); + // TODO + // returns platform specific ptr + return( 0 ); + + case audioMasterCloseWindow: + SHOW_CALLBACK( "amc: audioMasterCloseWindow\n" ); + // TODO + // close window, platform specific handle in + return( 0 ); +#endif + + case audioMasterSizeWindow: + // TODO using lmms-main-window-size + SHOW_CALLBACK( "amc: audioMasterSizeWindow\n" ); + // index: width, value: height + return( 0 ); + + case audioMasterGetSampleRate: + // TODO using mixer-call + SHOW_CALLBACK( "amc: audioMasterGetSampleRate\n" ); + return( 0 ); + + case audioMasterGetBlockSize: + // TODO using mixer-call + SHOW_CALLBACK( "amc: audioMasterGetBlockSize\n" ); + return( 0 ); + + case audioMasterGetInputLatency: + // TODO using mixer-call + SHOW_CALLBACK( "amc: audioMasterGetInputLatency\n" ); + return( 0 ); + + case audioMasterGetOutputLatency: + // TODO using mixer-call + SHOW_CALLBACK( "amc: audioMasterGetOutputLatency\n" ); + return( 0 ); + + case audioMasterGetCurrentProcessLevel: + SHOW_CALLBACK( "amc: audioMasterGetCurrentProcess" + "Level\n" ); + // returns: 0: not supported, + // 1: currently in user thread (gui) + // 2: currently in audio thread (where process is + // called) + // 3: currently in 'sequencer' thread (midi, timer etc) + // 4: currently offline processing and thus in user + // thread + // other: not defined, but probably pre-empting user + // thread. + return( 0 ); + + case audioMasterGetAutomationState: + SHOW_CALLBACK( "amc: audioMasterGetAutomationState\n" ); + // returns 0: not supported, 1: off, 2:read, 3:write, + // 4:read/write offline + return( 0 ); + + case audioMasterOfflineStart: + SHOW_CALLBACK( "amc: audioMasterOfflineStart\n" ); + return( 0 ); + + case audioMasterOfflineRead: + SHOW_CALLBACK( "amc: audioMasterOfflineRead\n" ); + // ptr points to offline structure, see below. + // return 0: error, 1 ok + return( 0 ); + + case audioMasterOfflineWrite: + SHOW_CALLBACK( "amc: audioMasterOfflineWrite\n" ); + // same as read + return( 0 ); + + case audioMasterOfflineGetCurrentPass: + SHOW_CALLBACK( "amc: audioMasterOfflineGetCurrent" + "Pass\n" ); + return( 0 ); + + case audioMasterOfflineGetCurrentMetaPass: + SHOW_CALLBACK( "amc: audioMasterOfflineGetCurrentMeta" + "Pass\n"); + return( 0 ); + + case audioMasterGetVendorString: + SHOW_CALLBACK( "amc: audioMasterGetVendorString\n" ); + // fills with a string identifying the vendor + // (max 64 char) + strcpy( (char *) _ptr, "LAD"); + return( 0 ); + + case audioMasterGetProductString: + SHOW_CALLBACK( "amc: audioMasterGetProductString\n" ); + // fills with a string with product name + // (max 64 char) + strcpy( (char *) _ptr, "XFST-Server" ); + return( 0 ); + + case audioMasterGetVendorVersion: + SHOW_CALLBACK( "amc: audioMasterGetVendorVersion\n" ); + // TODO + // returns vendor-specific version + return( 1000 ); + + case audioMasterVendorSpecific: + SHOW_CALLBACK( "amc: audioMasterVendorSpecific\n" ); + // no definition, vendor specific handling + return( 0 ); + + case audioMasterCanDo: + SHOW_CALLBACK( "amc: audioMasterCanDo\n" ); + // string in ptr, see below + return( 0 ); + + case audioMasterGetLanguage: + SHOW_CALLBACK( "amc: audioMasterGetLanguage\n" ); + // TODO + // see enum + return( 0 ); + + case audioMasterGetDirectory: + SHOW_CALLBACK( "amc: audioMasterGetDirectory\n" ); + // TODO + // get plug directory, FSSpec on MAC, else char* + return( 0 ); + + case audioMasterUpdateDisplay: + //SHOW_CALLBACK( "amc: audioMasterUpdateDisplay\n" ); + // something has changed, update 'multi-fx' display + _effect->dispatcher( _effect, effEditIdle, 0, 0, NULL, + 0.0f ); + return( 0 ); + + case audioMasterBeginEdit: + SHOW_CALLBACK( "amc: audioMasterBeginEdit\n" ); + // begin of automation session (when mouse down), + // parameter index in + return( 0 ); + + case audioMasterEndEdit: + SHOW_CALLBACK( "amc: audioMasterEndEdit\n" ); + // end of automation session (when mouse up), + // parameter index in + return( 0 ); + + case audioMasterOpenFileSelector: + SHOW_CALLBACK( "amc: audioMasterOpenFileSelector\n" ); + // open a fileselector window with VstFileSelect* + // in + return( 0 ); + + default: + SHOW_CALLBACK( "VST master dispatcher: undefined: " + "%d\n", (int) _opcode ); + break; + } + + return( 0 ); +} + + + + +DWORD WINAPI VSTPlugin::guiEventLoop( LPVOID _param ) +{ + VSTPlugin * _this = static_cast( _param ); + _this->m_guiThreadID = GetCurrentThreadId(); + + // "guard point" to trap errors that occur during plugin loading +#ifdef HAVE_TLS + if( sigsetjmp( ejmpbuf, 1 ) ) + { + lvsMessage( "creating the editor for %s failed", + _this->m_shortName.c_str() ); + pthread_cond_signal( &_this->m_windowStatusChange ); + return( 1 ); + } + + ejmpbuf_valid = true; +#else + jmp_buf * ejmpbuf = new jmp_buf[1]; + int * ejmpbuf_valid = new int; + *ejmpbuf_valid = false; + + pthread_key_create( &ejmpbuf_key, NULL ); + pthread_setspecific( ejmpbuf_key, ejmpbuf ); + pthread_key_create( &ejmpbuf_valid_key, NULL ); + pthread_setspecific( ejmpbuf_valid_key, ejmpbuf_valid ); + + if( sigsetjmp( *ejmpbuf, 1 ) ) + { + exit( 1 ); + } + + *ejmpbuf_valid = true; +#endif + + // Note: m_lock is held while this function is called + if( !( _this->m_plugin->flags & effFlagsHasEditor ) ) + { + pthread_cond_signal( &_this->m_windowStatusChange ); + return( 1 ); + } + + + HMODULE hInst = GetModuleHandleA( NULL ); + if( hInst == NULL ) + { + lvsMessage( "can't get module handle" ); + pthread_cond_signal( &_this->m_windowStatusChange ); + return( 1 ); + } + + if( ( _this->m_window = CreateWindowExA( + 0, "LVSL", _this->m_shortName.c_str(), + ( WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & + ~WS_MAXIMIZEBOX ), + 0, 0, 1, 1, NULL, NULL, hInst, NULL ) ) == NULL ) + { + lvsMessage( "cannot create editor window" ); + pthread_cond_signal( &_this->m_windowStatusChange ); + return( 1 ); + } + + _this->m_windowXID = (int) GetPropA( _this->m_window, + "__wine_x11_whole_window" ); + + + _this->m_plugin->dispatcher( _this->m_plugin, effEditOpen, 0, 0, + _this->m_window, 0 ); + + ERect * er; + _this->m_plugin->dispatcher( _this->m_plugin, effEditGetRect, 0, 0, + &er, 0 ); + +/* const int width = er->right - er->left; + const int height = er->bottom - er->top;*/ + + SetWindowPos( _this->m_window, 0, 0, 0, er->right - er->left + 8, + er->bottom-er->top + 26, SWP_NOACTIVATE | SWP_NOREDRAW | + SWP_NOMOVE | SWP_NOZORDER ); + *ejmpbuf_valid = false; + + pthread_cond_signal( &_this->m_windowStatusChange ); + + + MSG msg; + + bool quit = false; + while( quit == false && GetMessageA( &msg, NULL, 0, 0 ) ) + { + TranslateMessage( &msg ); + DispatchMessageA( &msg ); + + if( msg.message == WM_USER ) + { + switch( msg.wParam ) + { + case SHOW_EDITOR: + ShowWindow( _this->m_window, SW_SHOW ); + break; + + case CLOSE_PLUGIN: + quit = true; + break; + + default: + break; + } + } + } + + pthread_cond_signal( &_this->m_windowStatusChange ); + + return( 0 ); +} + + + + +int main( void ) +{ + // WINE-startup + HMODULE hInst = GetModuleHandleA( NULL ); + if( hInst == NULL ) + { + lvsMessage( "can't get module handle" ); + return( -1 ); + } + + WNDCLASSA wc; + wc.style = 0; + wc.lpfnWndProc = DefWindowProcA; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInst; + wc.hIcon = LoadIconA( hInst, "LVSL" ); + wc.hCursor = LoadCursorA( NULL, IDI_APPLICATION ); + wc.hbrBackground = (HBRUSH) GetStockObject( BLACK_BRUSH ); + wc.lpszMenuName = "MENU_LVSL"; + wc.lpszClassName = "LVSL"; + + if( !RegisterClassA( &wc ) ) + { + return( -1 ); + } + + Sint16 cmd; + while( ( cmd = readValue() ) != VST_CLOSE_PLUGIN ) + { + switch( cmd ) + { + case VST_LOAD_PLUGIN: + plugin = new VSTPlugin( readString() ); + break; + + case VST_SHOW_EDITOR: + plugin->showEditor(); + break; + + case VST_PROCESS: + plugin->process(); + break; + + case VST_ENQUEUE_MIDI_EVENT: + { + const midiEvent ev = readValue(); + const Uint32 fr_ahead = readValue(); + plugin->enqueueMidiEvent( ev, fr_ahead ); + break; + } + + case VST_SAMPLE_RATE: + plugin->setSampleRate( readValue() ); + break; + + + case VST_BUFFER_SIZE: + plugin->setBlockSize( readValue() ); + break; + + case VST_BPM: + plugin->setBPM( readValue() ); + break; + + case VST_GET_PARAMETER_DUMP: + plugin->getParameterDump(); + break; + + case VST_SET_PARAMETER_DUMP: + plugin->setParameterDump(); + break; + + case VST_GET_PARAMETER_PROPERTIES: + plugin->getParameterProperties( + readValue() ); + break; + + default: + lvsMessage( "unhandled message: %d\n", + (int) cmd ); + break; + } + } + + delete plugin; + + return( 0 ); + +} + diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index b9416e174..46e4b81a4 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -59,7 +59,7 @@ #include "spc_bg_hndl_widget.h" #include "vestige.h" #include "text_float.h" -#include "fstclient.h" +#include "lvsl_client.h" #include "embed.cpp"