/* * lvsl_server.cpp - LMMS VST Support Layer Server * * Copyright (c) 2005-2008 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * Code partly taken from (X)FST: * 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., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #include "lmmsconfig.h" #ifdef LMMS_HAVE_PTHREAD_H #include #endif #ifdef LMMS_HAVE_SYS_TYPES_H #include #endif #ifdef LMMS_HAVE_SYS_IPC_H #include #endif #ifdef LMMS_HAVE_SYS_SHM_H #include #endif #ifdef LMMS_HAVE_STDARG_H #include #endif #ifdef LMMS_HAVE_SIGNAL_H #include #endif #ifdef LMMS_HAVE_SCHED_H #include #endif #include #include #include #include #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 #include "types.h" #include "midi.h" #include "communication.h" #ifdef LMMS_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 static hostLanguages hlang = LanguageEnglish; 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( void ); ~VSTPlugin(); void init( const std::string & _plugin_file ); void showEditor( void ) { if( m_window != NULL ) { PostThreadMessageA( m_guiThreadID, WM_USER, ShowEditor, 0 ); } } void process( void ); // enqueue given MIDI-event to be processed the next time process() is // called void enqueueMidiEvent( const midiEvent & _event, const f_cnt_t _frames_ahead ); // set given sample-rate for plugin void setSampleRate( const sample_rate_t _rate ) { m_plugin->dispatcher( m_plugin, effSetSampleRate, 0, 0, NULL, (float) _rate ); m_sampleRate = _rate; } // set given block-size for plugin void setBlockSize( const fpp_t _bsize ); // set given tempo void setBPM( const bpm_t _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 ch_cnt_t inputCount( void ) const { return( m_plugin->numInputs ); } // number of outputs ch_cnt_t 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, ShowEditor, ClosePlugin } ; // 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; Sint32 m_windowXID; Sint16 m_windowWidth; Sint16 m_windowHeight; pthread_mutex_t m_lock; pthread_cond_t m_windowStatusChange; DWORD m_guiThreadID; fpp_t m_blockSize; float * m_shm; float * * m_inputs; float * * m_outputs; std::list m_midiEvents; bpm_t m_bpm; sample_rate_t m_sampleRate; double m_currentSamplePos; } ; VSTPlugin::VSTPlugin( void ) : m_shortName( "" ), m_libInst( NULL ), m_plugin( NULL ), m_window( NULL ), m_windowXID( 0 ), m_windowWidth( 0 ), m_windowHeight( 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 ), m_sampleRate( 44100 ), m_currentSamplePos( 0 ) { } void VSTPlugin::init( const std::string & _plugin_file ) { 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 ); if( m_windowXID != 0 ) { writeValue( VST_PLUGIN_EDITOR_GEOMETRY ); writeValue( m_windowWidth ); writeValue( m_windowHeight ); } 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, ClosePlugin, 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 * ( __stdcall * 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(); Sint16 idx = 0; for( std::list::iterator it = m_midiEvents.begin(); it != m_midiEvents.end(); ++it, ++idx ) { memcpy( &vme[idx], &*it, sizeof( VstMidiEvent ) ); events->events[idx] = (VstEvent *) &vme[idx]; } 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( ch_cnt_t i = 0; i < inputCount(); ++i ) { m_inputs[i] = &m_shm[i * m_blockSize]; } for( ch_cnt_t i = 0; i < outputCount(); ++i ) { m_outputs[i] = &m_shm[( i + inputCount() ) * m_blockSize]; memset( m_outputs[i], 0, m_blockSize * sizeof( float ) ); } #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 m_currentSamplePos += m_blockSize; 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 f_cnt_t _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; switch( _event.m_type ) { case MidiPitchBend: event.midiData[1] = _event.m_data.m_param[0] & 0x7f; event.midiData[2] = _event.m_data.m_param[0] >> 7; break; // TODO: handle more special cases default: event.midiData[1] = _event.key(); event.midiData[2] = _event.velocity(); break; } event.midiData[3] = 0; m_midiEvents.push_back( event ); } void VSTPlugin::setBlockSize( const fpp_t _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 ); for( Sint32 i = 0; i < m_plugin->numParams; ++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 ) ? m_plugin->numParams : 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) ); #if kVstVersion > 2 memcpy( props.categoryLabel, vst_props.categoryLabel, sizeof( props.categoryLabel ) ); #endif props.minValue = vst_props.minInteger; props.maxValue = vst_props.maxInteger; props.step = ( vst_props.flags & kVstParameterUsesFloatStep ) ? vst_props.stepFloat : vst_props.stepInteger; #if kVstVersion > 2 props.category = vst_props.category; #endif 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 | 0600 ) ) == -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 /* TODO: * - complete audioMasterGetTime-handling (bars etc.) * - implement audioMasterProcessEvents * - audioMasterGetVendorVersion: return LMMS-version (config.h!) * - audioMasterGetDirectory: return either VST-plugin-dir or LMMS-workingdir * - audioMasterOpenFileSelector: show QFileDialog? */ 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" ); return( 2300 ); 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 = plugin->m_currentSamplePos; _timeInfo.sampleRate = plugin->m_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( 1 ); case audioMasterSetTime: SHOW_CALLBACK( "amc: audioMasterSetTime\n" ); // VstTimenfo* in , filter in , not // supported return( 0 ); case audioMasterTempoAt: //SHOW_CALLBACK( "amc: audioMasterTempoAt\n" ); return( plugin->m_bpm * 10000 ); case audioMasterGetNumAutomatableParameters: SHOW_CALLBACK( "amc: audioMasterGetNumAutomatable" "Parameters\n" ); return( 5000 ); 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( 1 ); case audioMasterNeedIdle: //SHOW_CALLBACK( "amc: audioMasterNeedIdle\n" ); // plug needs idle calls (outside its editor window) return( 1 ); 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: //SHOW_CALLBACK( "amc: audioMasterSizeWindow\n" ); if( plugin->m_window == 0 ) { return( 0 ); } plugin->m_windowWidth = _index; plugin->m_windowHeight = _value; SetWindowPos( plugin->m_window, 0, 0, 0, _index + 8, _value + 26, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER ); writeValue( VST_PLUGIN_EDITOR_GEOMETRY ); writeValue( plugin->m_windowWidth ); writeValue( plugin->m_windowHeight ); return( 1 ); case audioMasterGetSampleRate: //SHOW_CALLBACK( "amc: audioMasterGetSampleRate\n" ); return( plugin->m_sampleRate ); case audioMasterGetBlockSize: //SHOW_CALLBACK( "amc: audioMasterGetBlockSize\n" ); return( plugin->m_blockSize ); case audioMasterGetInputLatency: //SHOW_CALLBACK( "amc: audioMasterGetInputLatency\n" ); return( plugin->m_blockSize ); case audioMasterGetOutputLatency: //SHOW_CALLBACK( "amc: audioMasterGetOutputLatency\n" ); return( plugin->m_blockSize ); 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, "Tobias Doerffel & others"); return( 1 ); case audioMasterGetProductString: //SHOW_CALLBACK( "amc: audioMasterGetProductString\n" ); // fills with a string with product name // (max 64 char) strcpy( (char *) _ptr, "LMMS VST Support Layer (LVSL)" ); return( 1 ); case audioMasterGetVendorVersion: SHOW_CALLBACK( "amc: audioMasterGetVendorVersion\n" ); // 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" ); return( !strcmp( (char *) _ptr, "sendVstEvents" ) || !strcmp( (char *) _ptr, "sendVstMidiEvent" ) || !strcmp( (char *) _ptr, "sendVstTimeInfo" ) || !strcmp( (char *) _ptr, "sizeWindow" ) ); case audioMasterGetLanguage: //SHOW_CALLBACK( "amc: audioMasterGetLanguage\n" ); return( hlang ); case audioMasterGetDirectory: SHOW_CALLBACK( "amc: audioMasterGetDirectory\n" ); // 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 ); #if kVstVersion > 2 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 ); #endif 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 LMMS_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 ); } ShowWindow( _this->m_window, SW_SHOWMINIMIZED ); ShowWindow( _this->m_window, SW_HIDE ); _this->m_windowXID = (Sint32) 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 ); _this->m_windowWidth = er->right - er->left; _this->m_windowHeight = er->bottom - er->top; SetWindowPos( _this->m_window, 0, 0, 0, _this->m_windowWidth + 8, _this->m_windowHeight + 26, 0 #if 0 SWP_NOACTIVATE /*| SWP_NOREDRAW*/ | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER #endif ); #ifdef LMMS_HAVE_TLS ejmpbuf_valid = false; #else *ejmpbuf_valid = false; #endif 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 ShowEditor: ShowWindow( _this->m_window, SW_SHOWNORMAL ); UpdateWindow( _this->m_window ); break; case ClosePlugin: 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 ); } #ifdef LMMS_HAVE_SCHED_H // try to set realtime-priority struct sched_param sparam; sparam.sched_priority = ( sched_get_priority_max( SCHED_FIFO ) + sched_get_priority_min( SCHED_FIFO ) ) / 2; if( sched_setscheduler( 0, SCHED_FIFO, &sparam ) == -1 ) { lvsMessage( "could not set realtime priority for VST-server" ); } #endif Sint16 cmd; while( ( cmd = readValue() ) != VST_CLOSE_PLUGIN ) { switch( cmd ) { case VST_LOAD_PLUGIN: plugin = new VSTPlugin(); plugin->init( readString() ); break; case VST_SHOW_EDITOR: plugin->showEditor(); break; case VST_PROCESS: plugin->process(); break; case VST_ENQUEUE_MIDI_EVENT: { MidiEventTypes type = readValue(); Sint8 channel = readValue(); Uint16 param1 = readValue(); Uint16 param2 = readValue(); const midiEvent ev = midiEvent( type, channel, param1, param2 ); const f_cnt_t 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_LANGUAGE: hlang = 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 ); }