123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286 |
- /*
- * RemoteVstPlugin.cpp - LMMS VST Support Layer (RemotePlugin client)
- *
- * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
- *
- * This file is part of LMMS - https://lmms.io
- *
- * 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"
- #define BUILD_REMOTE_PLUGIN_CLIENT
- #include "RemotePlugin.h"
- #ifdef LMMS_HAVE_FCNTL_H
- #include <fcntl.h>
- #endif
- #ifdef LMMS_BUILD_LINUX
- #ifndef NOMINMAX
- #define NOMINMAX
- #endif
- #ifndef O_BINARY
- #define O_BINARY 0
- #endif
- #ifdef LMMS_HAVE_SCHED_H
- #include <sched.h>
- #endif
- #include <wine/exception.h>
- #endif
- #define USE_WS_PREFIX
- #include <windows.h>
- #ifdef USE_MINGW_THREADS_REPLACEMENT
- # include <mingw.mutex.h>
- #else
- # include <mutex>
- #endif
- #include <algorithm>
- #include <vector>
- #include <queue>
- #include <string>
- #include <iostream>
- #include <string>
- #include <cstring>
- #include <aeffectx.h>
- #if kVstVersion < 2400
- #define OLD_VST_SDK
- struct ERect
- {
- short top;
- short left;
- short bottom;
- short right;
- } ;
- #endif
- #include "lmms_basics.h"
- #include "Midi.h"
- #include "communication.h"
- #include "IoHelper.h"
- #include "VstSyncData.h"
- #ifdef LMMS_BUILD_WIN32
- #define USE_QT_SHMEM
- #endif
- #ifndef USE_QT_SHMEM
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #endif
- using namespace std;
- static VstHostLanguages hlang = LanguageEnglish;
- static bool EMBED = false;
- static bool EMBED_X11 = false;
- static bool EMBED_WIN32 = false;
- static bool HEADLESS = false;
- class RemoteVstPlugin;
- RemoteVstPlugin * __plugin = nullptr;
- HWND __MessageHwnd = nullptr;
- DWORD __processingThreadId = 0;
- //Returns the last Win32 error, in string format. Returns an empty string if there is no error.
- std::string GetErrorAsString(DWORD errorMessageID)
- {
- //Get the error message, if any.
- if(errorMessageID == 0)
- return std::string(); //No error message has been recorded
- LPSTR messageBuffer = nullptr;
- size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, nullptr);
- std::string message(messageBuffer, size);
- //Free the buffer.
- LocalFree(messageBuffer);
- return message;
- }
- class RemoteVstPlugin : public RemotePluginClient
- {
- public:
- #ifdef SYNC_WITH_SHM_FIFO
- RemoteVstPlugin( key_t _shm_in, key_t _shm_out );
- #else
- RemoteVstPlugin( const char * socketPath );
- #endif
- virtual ~RemoteVstPlugin();
- virtual bool processMessage( const message & _m );
- void init( const std::string & _plugin_file );
- void initEditor();
- void showEditor();
- void hideEditor();
- void destroyEditor();
- virtual void process( const sampleFrame * _in, sampleFrame * _out );
- virtual void processMidiEvent( const MidiEvent& event, const f_cnt_t offset );
- // set given sample-rate for plugin
- virtual void updateSampleRate()
- {
- SuspendPlugin suspend( this );
- pluginDispatch( effSetSampleRate, 0, 0,
- nullptr, (float) sampleRate() );
- }
- // set given buffer-size for plugin
- virtual void updateBufferSize()
- {
- SuspendPlugin suspend( this );
- pluginDispatch( effSetBlockSize, 0, bufferSize() );
- }
- void setResumed( bool resumed )
- {
- m_resumed = resumed;
- pluginDispatch( effMainsChanged, 0, resumed ? 1 : 0 );
- }
- inline bool isResumed() const
- {
- return m_resumed;
- }
- inline bool isInitialized() const
- {
- return m_initialized;
- }
- // set given tempo
- void setBPM( const bpm_t _bpm )
- {
- m_bpm = _bpm;
- }
- // determine VST-version the plugin uses
- inline int pluginVersion()
- {
- return pluginDispatch( effGetVendorVersion );
- }
- // determine name of plugin
- const char * pluginName();
- // determine vendor of plugin
- const char * pluginVendorString();
- // determine product-string of plugin
- const char * pluginProductString();
- // determine name of current program
- const char * programName();
- void getParameterDisplays();
- void getParameterLabels();
- // send name of current program back to host
- void sendCurrentProgramName();
- // do a complete parameter-dump and post it
- void getParameterDump();
- // read parameter-dump and set it for plugin
- void setParameterDump( const message & _m );
- // save settings chunk of plugin into file
- void saveChunkToFile( const std::string & _file );
- // restore settings chunk of plugin from file
- void loadChunkFromFile( const std::string & _file, int _len );
- // restore settings chunk of plugin from file
- void loadPresetFile( const std::string & _file );
- // sets given program index
- void setProgram( int index );
- // rotate current program by given offset
- void rotateProgram( int offset );
- // Load names of presets/programs
- void getProgramNames();
- // Save presets/programs
- void savePreset( const std::string & _file );
- // number of inputs
- virtual int inputCount() const
- {
- if( m_plugin )
- {
- return m_plugin->numInputs;
- }
- return 0;
- }
- // number of outputs
- virtual int outputCount() const
- {
- if( m_plugin )
- {
- return m_plugin->numOutputs;
- }
- return 0;
- }
- // has to be called as soon as input- or output-count changes
- int updateInOutCount();
- inline void lockShm()
- {
- m_shmLock.lock();
- }
- inline bool tryLockShm()
- {
- return m_shmLock.try_lock();
- }
- inline void unlockShm()
- {
- m_shmLock.unlock();
- }
- inline bool isShmValid()
- {
- return m_shmValid;
- }
- inline void setShmIsValid( bool valid )
- {
- m_shmValid = valid;
- }
- inline bool isProcessing() const
- {
- return m_processing;
- }
- inline void setProcessing( bool processing )
- {
- m_processing = processing;
- }
- inline void queueMessage( const message & m ) {
- m_messageList.push( m );
- }
- inline bool shouldGiveIdle() const
- {
- return m_shouldGiveIdle;
- }
- inline void setShouldGiveIdle( bool shouldGiveIdle )
- {
- m_shouldGiveIdle = shouldGiveIdle;
- }
- void idle();
- void processUIThreadMessages();
- static DWORD WINAPI processingThread( LPVOID _param );
- static bool setupMessageWindow();
- static DWORD WINAPI guiEventLoop();
- static LRESULT CALLBACK wndProc( HWND hwnd, UINT uMsg,
- WPARAM wParam, LPARAM lParam );
- private:
- enum GuiThreadMessages
- {
- None,
- ProcessPluginMessage,
- GiveIdle,
- ClosePlugin
- } ;
- struct SuspendPlugin {
- SuspendPlugin( RemoteVstPlugin * plugin ) :
- m_plugin( plugin ),
- m_resumed( plugin->isResumed() )
- {
- if( m_resumed ) { m_plugin->setResumed( false ); }
- }
- ~SuspendPlugin()
- {
- if( m_resumed ) { m_plugin->setResumed( true ); }
- }
- private:
- RemoteVstPlugin * m_plugin;
- bool m_resumed;
- };
- // callback used by plugin for being able to communicate with it's host
- static intptr_t VST_CALL_CONV hostCallback( AEffect * _effect, int32_t _opcode,
- int32_t _index, intptr_t _value,
- void * _ptr, float _opt );
- bool load( const std::string & _plugin_file );
- int pluginDispatch( int cmd, int param1 = 0, int param2 = 0,
- void * p = nullptr, float f = 0 )
- {
- if( m_plugin )
- {
- return m_plugin->dispatcher( m_plugin, cmd, param1, param2, p, f );
- }
- return 0;
- }
- std::string m_shortName;
- HINSTANCE m_libInst;
- AEffect * m_plugin;
- HWND m_window;
- intptr_t m_windowID;
- int m_windowWidth;
- int m_windowHeight;
- bool m_initialized;
- bool m_resumed;
- bool m_processing;
- std::queue<message> m_messageList;
- bool m_shouldGiveIdle;
- float * * m_inputs;
- float * * m_outputs;
- std::mutex m_shmLock;
- bool m_shmValid;
- typedef std::vector<VstMidiEvent> VstMidiEventList;
- VstMidiEventList m_midiEvents;
- bpm_t m_bpm;
- double m_currentSamplePos;
- int m_currentProgram;
- // host to plugin synchronisation data structure
- struct in
- {
- double lastppqPos;
- double m_Timestamp;
- int32_t m_lastFlags;
- } ;
- in * m_in;
- int m_shmID;
- VstSyncData* m_vstSyncData;
- } ;
- #ifdef SYNC_WITH_SHM_FIFO
- RemoteVstPlugin::RemoteVstPlugin( key_t _shm_in, key_t _shm_out ) :
- RemotePluginClient( _shm_in, _shm_out ),
- #else
- RemoteVstPlugin::RemoteVstPlugin( const char * socketPath ) :
- RemotePluginClient( socketPath ),
- #endif
- m_libInst( nullptr ),
- m_plugin( nullptr ),
- m_window( nullptr ),
- m_windowID( 0 ),
- m_windowWidth( 0 ),
- m_windowHeight( 0 ),
- m_initialized( false ),
- m_resumed( false ),
- m_processing( false ),
- m_messageList(),
- m_shouldGiveIdle( false ),
- m_inputs( nullptr ),
- m_outputs( nullptr ),
- m_shmValid( false ),
- m_midiEvents(),
- m_bpm( 0 ),
- m_currentSamplePos( 0 ),
- m_currentProgram( -1 ),
- m_in( nullptr ),
- m_shmID( -1 ),
- m_vstSyncData( nullptr )
- {
- __plugin = this;
- #ifndef USE_QT_SHMEM
- key_t key;
- if( ( key = ftok( VST_SNC_SHM_KEY_FILE, 'R' ) ) == -1 )
- {
- perror( "RemoteVstPlugin.cpp::ftok" );
- }
- else
- { // connect to shared memory segment
- if( ( m_shmID = shmget( key, 0, 0 ) ) == -1 )
- {
- perror( "RemoteVstPlugin.cpp::shmget" );
- }
- else
- { // attach segment
- m_vstSyncData = (VstSyncData *)shmat(m_shmID, 0, 0);
- if( m_vstSyncData == (VstSyncData *)( -1 ) )
- {
- perror( "RemoteVstPlugin.cpp::shmat" );
- }
- }
- }
- #else
- m_vstSyncData = RemotePluginClient::getQtVSTshm();
- #endif
- if( m_vstSyncData == nullptr )
- {
- fprintf(stderr, "RemoteVstPlugin.cpp: "
- "Failed to initialize shared memory for VST synchronization.\n"
- " (VST-host synchronization will be disabled)\n");
- m_vstSyncData = (VstSyncData*) malloc( sizeof( VstSyncData ) );
- m_vstSyncData->isPlaying = true;
- m_vstSyncData->timeSigNumer = 4;
- m_vstSyncData->timeSigDenom = 4;
- m_vstSyncData->ppqPos = 0;
- m_vstSyncData->isCycle = false;
- m_vstSyncData->hasSHM = false;
- m_vstSyncData->m_playbackJumped = false;
- m_vstSyncData->m_sampleRate = sampleRate();
- }
- m_in = ( in* ) new char[ sizeof( in ) ];
- m_in->lastppqPos = 0;
- m_in->m_Timestamp = -1;
- m_in->m_lastFlags = 0;
- // process until we have loaded the plugin
- while( 1 )
- {
- message m = receiveMessage();
- processMessage( m );
- if( m.id == IdVstLoadPlugin || m.id == IdQuit )
- {
- break;
- }
- }
- }
- RemoteVstPlugin::~RemoteVstPlugin()
- {
- destroyEditor();
- setResumed( false );
- pluginDispatch( effClose );
- #ifndef USE_QT_SHMEM
- // detach shared memory segment
- if( shmdt( m_vstSyncData ) == -1)
- {
- if( __plugin->m_vstSyncData->hasSHM )
- {
- perror( "~RemoteVstPlugin::shmdt" );
- }
- if( m_vstSyncData != nullptr )
- {
- delete m_vstSyncData;
- m_vstSyncData = nullptr;
- }
- }
- #endif
- if( m_libInst != nullptr )
- {
- FreeLibrary( m_libInst );
- m_libInst = nullptr;
- }
- delete[] m_inputs;
- delete[] m_outputs;
- }
- bool RemoteVstPlugin::processMessage( const message & _m )
- {
- if (! EMBED)
- {
- switch( _m.id )
- {
- case IdShowUI:
- showEditor();
- return true;
- case IdHideUI:
- hideEditor();
- return true;
- case IdToggleUI:
- if( m_window && IsWindowVisible( m_window ) )
- {
- hideEditor();
- }
- else
- {
- showEditor();
- }
- return true;
- case IdIsUIVisible:
- bool visible = m_window && IsWindowVisible( m_window );
- sendMessage( message( IdIsUIVisible )
- .addInt( visible ? 1 : 0 ) );
- return true;
- }
- }
- else if (EMBED && _m.id == IdShowUI)
- {
- ShowWindow( m_window, SW_SHOWNORMAL );
- UpdateWindow( m_window );
- return true;
- }
- switch( _m.id )
- {
- case IdVstLoadPlugin:
- init( _m.getString() );
- break;
- case IdVstSetTempo:
- setBPM( _m.getInt() );
- break;
- case IdVstSetLanguage:
- hlang = static_cast<VstHostLanguages>( _m.getInt() );
- break;
- case IdVstGetParameterDump:
- getParameterDump();
- break;
- case IdVstSetParameterDump:
- setParameterDump( _m );
- break;
- case IdSaveSettingsToFile:
- saveChunkToFile( _m.getString() );
- sendMessage( IdSaveSettingsToFile );
- break;
- case IdLoadSettingsFromFile:
- loadChunkFromFile( _m.getString( 0 ), _m.getInt( 1 ) );
- sendMessage( IdLoadSettingsFromFile );
- break;
- case IdLoadPresetFile:
- loadPresetFile( _m.getString( 0 ) );
- sendMessage( IdLoadPresetFile );
- break;
- case IdVstSetProgram:
- setProgram( _m.getInt( 0 ) );
- sendMessage( IdVstSetProgram );
- break;
- case IdVstCurrentProgram:
- sendMessage( message( IdVstCurrentProgram ).addInt( m_currentProgram ) );
- break;
- case IdVstRotateProgram:
- rotateProgram( _m.getInt( 0 ) );
- sendMessage( IdVstRotateProgram );
- break;
- case IdVstProgramNames:
- getProgramNames();
- break;
- case IdSavePresetFile:
- savePreset( _m.getString( 0 ) );
- sendMessage( IdSavePresetFile );
- break;
- case IdVstSetParameter:
- m_plugin->setParameter( m_plugin, _m.getInt( 0 ), _m.getFloat( 1 ) );
- //sendMessage( IdVstSetParameter );
- break;
- case IdVstParameterDisplays:
- getParameterDisplays();
- break;
- case IdVstParameterLabels:
- getParameterLabels();
- break;
- case IdVstIdleUpdate:
- {
- int newCurrentProgram = pluginDispatch( effGetProgram );
- if( newCurrentProgram != m_currentProgram )
- {
- m_currentProgram = newCurrentProgram;
- sendCurrentProgramName();
- }
- break;
- }
- default:
- return RemotePluginClient::processMessage( _m );
- }
- return true;
- }
- void RemoteVstPlugin::init( const std::string & _plugin_file )
- {
- if( load( _plugin_file ) == false )
- {
- sendMessage( IdVstFailedLoadingPlugin );
- return;
- }
- updateInOutCount();
- updateBufferSize();
- updateSampleRate();
- /* set program to zero */
- /* i comment this out because it breaks dfx Geometer
- * looks like we cant set programs for it
- *
- pluginDispatch( effSetProgram, 0, 0 ); */
- // request rate and blocksize
- setResumed( true );
- debugMessage( "creating editor\n" );
- initEditor();
- debugMessage( "editor successfully created\n" );
- // now post some information about our plugin
- sendMessage( message( IdVstPluginWindowID ).addInt( m_windowID ) );
- sendMessage( message( IdVstPluginEditorGeometry ).
- addInt( m_windowWidth ).
- addInt( m_windowHeight ) );
- sendMessage( message( IdVstPluginName ).addString( pluginName() ) );
- sendMessage( message( IdVstPluginVersion ).addInt( pluginVersion() ) );
- sendMessage( message( IdVstPluginVendorString ).
- addString( pluginVendorString() ) );
- sendMessage( message( IdVstPluginProductString ).
- addString( pluginProductString() ) );
- sendMessage( message( IdVstParameterCount ).
- addInt( m_plugin->numParams ) );
- sendMessage( IdInitDone );
- m_initialized = true;
- }
- static void close_check( FILE* fp )
- {
- if (!fp) {return;}
- if( fclose( fp ) )
- {
- perror( "fclose" );
- }
- }
- void RemoteVstPlugin::initEditor()
- {
- if( HEADLESS || m_window || !( m_plugin->flags & effFlagsHasEditor ) )
- {
- return;
- }
- HMODULE hInst = GetModuleHandle( nullptr );
- if( hInst == nullptr )
- {
- debugMessage( "initEditor(): can't get module handle\n" );
- return;
- }
- DWORD dwStyle;
- if (EMBED) {
- dwStyle = WS_POPUP | WS_SYSMENU | WS_BORDER;
- } else {
- dwStyle = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX;
- }
- m_window = CreateWindowEx( WS_EX_APPWINDOW, "LVSL", pluginName(),
- dwStyle,
- 0, 0, 10, 10, nullptr, nullptr, hInst, nullptr );
- if( m_window == nullptr )
- {
- debugMessage( "initEditor(): cannot create editor window\n" );
- return;
- }
- pluginDispatch( effEditOpen, 0, 0, m_window );
- ERect * er;
- pluginDispatch( effEditGetRect, 0, 0, &er );
- m_windowWidth = er->right - er->left;
- m_windowHeight = er->bottom - er->top;
- RECT windowSize = { 0, 0, m_windowWidth, m_windowHeight };
- AdjustWindowRect( &windowSize, dwStyle, false );
- SetWindowPos( m_window, 0, 0, 0, windowSize.right - windowSize.left,
- windowSize.bottom - windowSize.top, SWP_NOACTIVATE |
- SWP_NOMOVE | SWP_NOZORDER );
- pluginDispatch( effEditTop );
- #ifdef LMMS_BUILD_LINUX
- m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" );
- #else
- // 64-bit versions of Windows use 32-bit handles for interoperability
- m_windowID = (intptr_t) m_window;
- #endif
- }
- void RemoteVstPlugin::showEditor() {
- if( !EMBED && !HEADLESS && m_window )
- {
- ShowWindow( m_window, SW_SHOWNORMAL );
- }
- }
- void RemoteVstPlugin::hideEditor() {
- if( !EMBED && !HEADLESS && m_window )
- {
- ShowWindow( m_window, SW_HIDE );
- }
- }
- void RemoteVstPlugin::destroyEditor()
- {
- if( m_window == nullptr )
- {
- return;
- }
- pluginDispatch( effEditClose );
- // Destroying the window takes some time in Wine 1.8.5
- DestroyWindow( m_window );
- m_window = nullptr;
- }
- bool RemoteVstPlugin::load( const std::string & _plugin_file )
- {
- if( ( m_libInst = LoadLibraryW( toWString(_plugin_file).c_str() ) ) == nullptr )
- {
- DWORD error = GetLastError();
- debugMessage( "LoadLibrary failed: " + GetErrorAsString(error) );
- return false;
- }
- typedef AEffect * ( VST_CALL_CONV * mainEntryPointer )
- ( audioMasterCallback );
- mainEntryPointer mainEntry = (mainEntryPointer)
- GetProcAddress( m_libInst, "VSTPluginMain" );
- if( mainEntry == nullptr )
- {
- mainEntry = (mainEntryPointer)
- GetProcAddress( m_libInst, "VstPluginMain" );
- }
- if( mainEntry == nullptr )
- {
- mainEntry = (mainEntryPointer)
- GetProcAddress( m_libInst, "main" );
- }
- if( mainEntry == nullptr )
- {
- debugMessage( "could not find entry point\n" );
- return false;
- }
- m_plugin = mainEntry( hostCallback );
- if( m_plugin == nullptr )
- {
- debugMessage( "mainEntry procedure returned NULL\n" );
- return false;
- }
- if( m_plugin->magic != kEffectMagic )
- {
- debugMessage( "File is not a VST plugin\n" );
- return false;
- }
- char id[5];
- sprintf( id, "%c%c%c%c", ((char *)&m_plugin->uniqueID)[3],
- ((char *)&m_plugin->uniqueID)[2],
- ((char *)&m_plugin->uniqueID)[1],
- ((char *)&m_plugin->uniqueID)[0] );
- id[4] = 0;
- sendMessage( message( IdVstPluginUniqueID ).addString( id ) );
- pluginDispatch( effOpen );
- return true;
- }
- void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out )
- {
- // 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 eventsBuffer[sizeof( VstEvents ) + sizeof( VstMidiEvent * ) * MIDI_EVENT_BUFFER_COUNT];
- static VstMidiEvent vme[MIDI_EVENT_BUFFER_COUNT];
- // first sort events chronologically, since some plugins
- // (e.g. Sinnah) can hang if they're out of order
- std::stable_sort( m_midiEvents.begin(), m_midiEvents.end(),
- []( const VstMidiEvent &a, const VstMidiEvent &b )
- {
- return a.deltaFrames < b.deltaFrames;
- } );
- VstEvents* events = (VstEvents *) eventsBuffer;
- events->reserved = 0;
- events->numEvents = m_midiEvents.size();
- int idx = 0;
- for( VstMidiEventList::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();
- pluginDispatch( effProcessEvents, 0, 0, events );
- }
- // now we're ready to fetch sound from VST-plugin
- if( !tryLockShm() )
- {
- return;
- }
- if( !isShmValid() )
- {
- unlockShm();
- return;
- }
- for( int i = 0; i < inputCount(); ++i )
- {
- m_inputs[i] = &((float *) _in)[i * bufferSize()];
- }
- for( int i = 0; i < outputCount(); ++i )
- {
- m_outputs[i] = &((float *) _out)[i * bufferSize()];
- memset( m_outputs[i], 0, bufferSize() * sizeof( float ) );
- }
- #ifdef OLD_VST_SDK
- if( m_plugin->flags & effFlagsCanReplacing )
- {
- #endif
- m_plugin->processReplacing( m_plugin, m_inputs, m_outputs,
- bufferSize() );
- #ifdef OLD_VST_SDK
- }
- else
- {
- m_plugin->process( m_plugin, m_inputs, m_outputs,
- bufferSize() );
- }
- #endif
- unlockShm();
- m_currentSamplePos += bufferSize();
- }
- void RemoteVstPlugin::processMidiEvent( const MidiEvent& event, const f_cnt_t offset )
- {
- VstMidiEvent vme;
- vme.type = kVstMidiType;
- vme.byteSize = 24;
- vme.deltaFrames = offset;
- vme.flags = 0;
- vme.detune = 0;
- vme.noteLength = 0;
- vme.noteOffset = 0;
- vme.noteOffVelocity = 0;
- vme.reserved1 = 0;
- vme.reserved2 = 0;
- vme.midiData[0] = event.type() + event.channel();
- switch( event.type() )
- {
- case MidiPitchBend:
- vme.midiData[1] = event.pitchBend() & 0x7f;
- vme.midiData[2] = event.pitchBend() >> 7;
- break;
- // TODO: handle more special cases
- default:
- vme.midiData[1] = event.key();
- vme.midiData[2] = event.velocity();
- break;
- }
- vme.midiData[3] = 0;
- m_midiEvents.push_back( vme );
- }
- const char * RemoteVstPlugin::pluginName()
- {
- static char buf[32];
- buf[0] = 0;
- pluginDispatch( effGetEffectName, 0, 0, buf );
- buf[31] = 0;
- return buf;
- }
- const char * RemoteVstPlugin::pluginVendorString()
- {
- static char buf[64];
- buf[0] = 0;
- pluginDispatch( effGetVendorString, 0, 0, buf );
- buf[63] = 0;
- return buf;
- }
- const char * RemoteVstPlugin::pluginProductString()
- {
- static char buf[64];
- buf[0] = 0;
- pluginDispatch( effGetProductString, 0, 0, buf );
- buf[63] = 0;
- return buf;
- }
- const char * RemoteVstPlugin::programName()
- {
- static char buf[24];
- memset( buf, 0, sizeof( buf ) );
- pluginDispatch( effGetProgramName, 0, 0, buf );
- buf[23] = 0;
- return buf;
- }
- // join the ParameterDisplays (stringified values without units) and send them to host
- void RemoteVstPlugin::getParameterDisplays()
- {
- std::string paramDisplays;
- static char buf[9]; // buffer for getting string
- for (int i=0; i< m_plugin->numParams; ++i)
- {
- memset( buf, 0, sizeof( buf ) ); // fill with '\0' because got string may not to be ended with '\0'
- pluginDispatch( effGetParamDisplay, i, 0, buf );
- buf[8] = 0;
- // each field shaped like: [length:number][content:string]
- paramDisplays += '0' + strlen(buf); // add length descriptor (length is up to 8)
- paramDisplays += buf;
- }
- sendMessage( message( IdVstParameterDisplays ).addString( paramDisplays.c_str() ) );
- }
- // join the ParameterLabels (units) and send them to host
- void RemoteVstPlugin::getParameterLabels()
- {
- std::string paramLabels;
- static char buf[9]; // buffer for getting string
- for (int i=0; i< m_plugin->numParams; ++i)
- {
- memset( buf, 0, sizeof( buf ) ); // fill with '\0' because got string may not to be ended with '\0'
- pluginDispatch( effGetParamLabel, i, 0, buf );
- buf[8] = 0;
- // each field shaped like: [length:number][content:string]
- paramLabels += '0' + strlen(buf); // add length descriptor (length is up to 8)
- paramLabels += buf;
- }
- sendMessage( message( IdVstParameterLabels ).addString( paramLabels.c_str() ) );
- }
- void RemoteVstPlugin::sendCurrentProgramName()
- {
- char presName[64];
- sprintf( presName, "%d/%d: %s", pluginDispatch( effGetProgram ) + 1, m_plugin->numPrograms, programName() );
- sendMessage( message( IdVstCurrentProgramName ).addString( presName ) );
- }
- void RemoteVstPlugin::getParameterDump()
- {
- message m( IdVstParameterDump );
- m.addInt( m_plugin->numParams );
- for( int i = 0; i < m_plugin->numParams; ++i )
- {
- char paramName[256];
- memset( paramName, 0, sizeof( paramName ) );
- pluginDispatch( effGetParamName, i, 0, paramName );
- paramName[sizeof(paramName)-1] = 0;
- m.addInt( i );
- m.addString( paramName );
- m.addFloat( m_plugin->getParameter( m_plugin, i ) );
- }
- sendMessage( m );
- }
- void RemoteVstPlugin::setParameterDump( const message & _m )
- {
- const int n = _m.getInt( 0 );
- const int params = ( n > m_plugin->numParams ) ?
- m_plugin->numParams : n;
- int p = 0;
- for( int i = 0; i < params; ++i )
- {
- VstParameterDumpItem item;
- item.index = _m.getInt( ++p );
- item.shortLabel = _m.getString( ++p );
- item.value = _m.getFloat( ++p );
- m_plugin->setParameter( m_plugin, item.index, item.value );
- }
- }
- void RemoteVstPlugin::saveChunkToFile( const std::string & _file )
- {
- if( m_plugin->flags & 32 )
- {
- void * chunk = nullptr;
- const int len = pluginDispatch( 23, 0, 0, &chunk );
- if( len > 0 )
- {
- FILE* fp = F_OPEN_UTF8( _file, "wb" );
- if (!fp)
- {
- fprintf( stderr,
- "Error opening file for saving chunk.\n" );
- return;
- }
- if ( fwrite( chunk, 1, len, fp ) != len )
- {
- fprintf( stderr,
- "Error saving chunk to file.\n" );
- }
- close_check( fp );
- }
- }
- }
- void RemoteVstPlugin::setProgram( int program )
- {
- if( isInitialized() == false )
- {
- return;
- }
- if( program < 0 )
- {
- program = 0;
- }
- else if( program >= m_plugin->numPrograms )
- {
- program = m_plugin->numPrograms - 1;
- }
- pluginDispatch( effSetProgram, 0, program );
- sendCurrentProgramName();
- }
- void RemoteVstPlugin::rotateProgram( int offset )
- {
- if( isInitialized() == false )
- {
- return;
- }
- int newProgram = pluginDispatch( effGetProgram ) + offset;
- if( newProgram < 0 )
- {
- newProgram = 0;
- }
- else if( newProgram >= m_plugin->numPrograms )
- {
- newProgram = m_plugin->numPrograms - 1;
- }
- pluginDispatch( effSetProgram, 0, newProgram );
- sendCurrentProgramName();
- }
- void RemoteVstPlugin::getProgramNames()
- {
- char presName[1024+256*30];
- char curProgName[30];
- if (isInitialized() == false) return;
- bool progNameIndexed = ( pluginDispatch( 29, 0, -1, curProgName ) == 1 );
- if (m_plugin->numPrograms > 1) {
- if (progNameIndexed) {
- for (int i = 0; i< (m_plugin->numPrograms >= 256?256:m_plugin->numPrograms); i++)
- {
- pluginDispatch( 29, i, -1, curProgName );
- if (i == 0) sprintf( presName, "%s", curProgName );
- else sprintf( presName + strlen(presName), "|%s", curProgName );
- }
- }
- else
- {
- int currProgram = pluginDispatch( effGetProgram );
- for (int i = 0; i< (m_plugin->numPrograms >= 256?256:m_plugin->numPrograms); i++)
- {
- pluginDispatch( effSetProgram, 0, i );
- if (i == 0) sprintf( presName, "%s", programName() );
- else sprintf( presName + strlen(presName), "|%s", programName() );
- }
- pluginDispatch( effSetProgram, 0, currProgram );
- }
- } else sprintf( presName, "%s", programName() );
- presName[sizeof(presName)-1] = 0;
- sendMessage( message( IdVstProgramNames ).addString( presName ) );
- }
- inline unsigned int endian_swap(unsigned int& x)
- {
- return (x>>24) | ((x<<8) & 0x00FF0000) | ((x>>8) & 0x0000FF00) | (x<<24);
- }
- struct sBank
- {
- unsigned int chunkMagic;
- unsigned int byteSize;
- unsigned int fxMagic;
- unsigned int version;
- unsigned int fxID;
- unsigned int fxVersion;
- unsigned int numPrograms;
- char prgName[28];
- };
- void RemoteVstPlugin::savePreset( const std::string & _file )
- {
- unsigned int chunk_size = 0;
- sBank * pBank = ( sBank* ) new char[ sizeof( sBank ) ];
- char progName[ 128 ] = { 0 };
- char* data = nullptr;
- const bool chunky = ( m_plugin->flags & ( 1 << 5 ) ) != 0;
- bool isPreset = _file.substr( _file.find_last_of( "." ) + 1 ) == "fxp";
- int presNameLen = _file.find_last_of( "/" ) + _file.find_last_of( "\\" ) + 2;
- if (isPreset)
- {
- for (size_t i = 0; i < _file.length() - 4 - presNameLen; i++)
- progName[i] = i < 23 ? _file[presNameLen + i] : 0;
- pluginDispatch( 4, 0, 0, progName );
- }
- if ( chunky )
- chunk_size = pluginDispatch( 23, isPreset, 0, &data );
- else {
- if (isPreset) {
- chunk_size = m_plugin->numParams * sizeof( float );
- data = new char[ chunk_size ];
- unsigned int* toUIntArray = reinterpret_cast<unsigned int*>( data );
- for ( int i = 0; i < m_plugin->numParams; i++ )
- {
- float value = m_plugin->getParameter( m_plugin, i );
- unsigned int * pValue = ( unsigned int * ) &value;
- toUIntArray[ i ] = endian_swap( *pValue );
- }
- } else chunk_size = (((m_plugin->numParams * sizeof( float )) + 56)*m_plugin->numPrograms);
- }
- pBank->chunkMagic = 0x4B6E6343;
- pBank->byteSize = chunk_size + ( chunky ? sizeof( int ) : 0 ) + 48;
- if (!isPreset) pBank->byteSize += 100;
- pBank->byteSize = endian_swap( pBank->byteSize );
- pBank->fxMagic = chunky ? 0x68435046 : 0x6B437846;
- if (!isPreset && chunky) pBank->fxMagic = 0x68434246;
- if (!isPreset &&!chunky) pBank->fxMagic = 0x6B427846;
- pBank->version = 0x01000000;
- unsigned int uIntToFile = (unsigned int) m_plugin->uniqueID;
- pBank->fxID = endian_swap( uIntToFile );
- uIntToFile = (unsigned int) pluginVersion();
- pBank->fxVersion = endian_swap( uIntToFile );
- uIntToFile = (unsigned int) chunky ? m_plugin->numPrograms : m_plugin->numParams;
- if (!isPreset &&!chunky) uIntToFile = (unsigned int) m_plugin->numPrograms;
- pBank->numPrograms = endian_swap( uIntToFile );
- FILE * stream = F_OPEN_UTF8( _file, "wb" );
- if (!stream)
- {
- fprintf( stderr,
- "Error opening file for saving preset.\n" );
- return;
- }
- fwrite ( pBank, 1, 28, stream );
- fwrite ( progName, 1, isPreset ? 28 : 128, stream );
- if ( chunky ) {
- uIntToFile = endian_swap( chunk_size );
- fwrite ( &uIntToFile, 1, 4, stream );
- }
- if (pBank->fxMagic != 0x6B427846 )
- fwrite ( data, 1, chunk_size, stream );
- else {
- int numPrograms = m_plugin->numPrograms;
- int currProgram = pluginDispatch( effGetProgram );
- chunk_size = (m_plugin->numParams * sizeof( float ));
- pBank->byteSize = chunk_size + 48;
- pBank->byteSize = endian_swap( pBank->byteSize );
- pBank->fxMagic = 0x6B437846;
- uIntToFile = (unsigned int) m_plugin->numParams;
- pBank->numPrograms = endian_swap( uIntToFile );
- data = new char[ chunk_size ];
- unsigned int* pValue,* toUIntArray = reinterpret_cast<unsigned int*>( data );
- float value;
- for (int j = 0; j < numPrograms; j++) {
- pluginDispatch( effSetProgram, 0, j );
- pluginDispatch( effGetProgramName, 0, 0, pBank->prgName );
- fwrite ( pBank, 1, 56, stream );
- for ( int i = 0; i < m_plugin->numParams; i++ )
- {
- value = m_plugin->getParameter( m_plugin, i );
- pValue = ( unsigned int * ) &value;
- toUIntArray[ i ] = endian_swap( *pValue );
- }
- fwrite ( data, 1, chunk_size, stream );
- }
- pluginDispatch( effSetProgram, 0, currProgram );
- }
- fclose( stream );
- if ( !chunky )
- delete[] data;
- delete[] (sBank*)pBank;
- }
- void RemoteVstPlugin::loadPresetFile( const std::string & _file )
- {
- void * chunk = nullptr;
- unsigned int * pLen = new unsigned int[ 1 ];
- unsigned int len = 0;
- sBank * pBank = (sBank*) new char[ sizeof( sBank ) ];
- FILE * stream = F_OPEN_UTF8( _file, "rb" );
- if (!stream)
- {
- fprintf( stderr,
- "Error opening file for loading preset.\n" );
- return;
- }
- if ( fread ( pBank, 1, 56, stream ) != 56 )
- {
- fprintf( stderr, "Error loading preset file.\n" );
- }
- pBank->fxID = endian_swap( pBank->fxID );
- pBank->numPrograms = endian_swap( pBank->numPrograms );
- unsigned int toUInt;
- float * pFloat;
- if (m_plugin->uniqueID != pBank->fxID) {
- sendMessage( message( IdVstCurrentProgramName ).
- addString( "Error: Plugin UniqID not match" ) );
- fclose( stream );
- delete[] (unsigned int*)pLen;
- delete[] (sBank*)pBank;
- return;
- }
- if( _file.substr( _file.find_last_of( "." ) + 1 ) != "fxp" )
- fseek ( stream , 156 , SEEK_SET );
- if(pBank->fxMagic != 0x6B427846) {
- if(pBank->fxMagic != 0x6B437846) {
- if ( fread (pLen, 1, 4, stream) != 4 )
- {
- fprintf( stderr,
- "Error loading preset file.\n" );
- }
- chunk = new char[len = endian_swap(*pLen)];
- } else chunk = new char[len = sizeof(float)*pBank->numPrograms];
- if ( fread (chunk, len, 1, stream) != 1 )
- {
- fprintf( stderr, "Error loading preset file.\n" );
- }
- fclose( stream );
- }
- if(_file.substr(_file.find_last_of(".") + 1) == "fxp") {
- pBank->prgName[23] = 0;
- pluginDispatch( 4, 0, 0, pBank->prgName );
- if(pBank->fxMagic != 0x6B437846)
- pluginDispatch( 24, 1, len, chunk );
- else
- {
- unsigned int* toUIntArray = reinterpret_cast<unsigned int*>( chunk );
- for (int i = 0; i < pBank->numPrograms; i++ )
- {
- toUInt = endian_swap( toUIntArray[ i ] );
- pFloat = ( float* ) &toUInt;
- m_plugin->setParameter( m_plugin, i, *pFloat );
- }
- }
- } else {
- if(pBank->fxMagic != 0x6B427846) {
- pluginDispatch( 24, 0, len, chunk );
- } else {
- int numPrograms = pBank->numPrograms;
- unsigned int * toUIntArray;
- int currProgram = pluginDispatch( effGetProgram );
- chunk = new char[ len = sizeof(float)*m_plugin->numParams ];
- toUIntArray = reinterpret_cast<unsigned int *>( chunk );
- for (int i =0; i < numPrograms; i++) {
- if ( fread (pBank, 1, 56, stream) != 56 )
- {
- fprintf( stderr,
- "Error loading preset file.\n" );
- }
- if ( fread (chunk, len, 1, stream) != 1 )
- {
- fprintf( stderr,
- "Error loading preset file.\n" );
- }
- pluginDispatch( effSetProgram, 0, i );
- pBank->prgName[23] = 0;
- pluginDispatch( 4, 0, 0, pBank->prgName );
- for (int j = 0; j < m_plugin->numParams; j++ ) {
- toUInt = endian_swap( toUIntArray[ j ] );
- pFloat = ( float* ) &toUInt;
- m_plugin->setParameter( m_plugin, j, *pFloat );
- }
- }
- pluginDispatch( effSetProgram, 0, currProgram );
- fclose( stream );
- }
- }
- sendCurrentProgramName();
- delete[] (unsigned int*)pLen;
- delete[] (sBank*)pBank;
- delete[] (char*)chunk;
- }
- void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len )
- {
- char * chunk = new char[_len];
- FILE* fp = F_OPEN_UTF8( _file, "rb" );
- if (!fp)
- {
- fprintf( stderr,
- "Error opening file for loading chunk.\n" );
- return;
- }
- if ( fread( chunk, 1, _len, fp ) != _len )
- {
- fprintf( stderr, "Error loading chunk from file.\n" );
- }
- close_check( fp );
- pluginDispatch( effSetChunk, 0, _len, chunk );
- delete[] chunk;
- }
- int RemoteVstPlugin::updateInOutCount()
- {
- if( inputCount() == RemotePluginClient::inputCount() &&
- outputCount() == RemotePluginClient::outputCount() )
- {
- return 1;
- }
-
- if( GetCurrentThreadId() == __processingThreadId )
- {
- debugMessage( "Plugin requested I/O change from processing "
- "thread. Request denied; stability may suffer.\n" );
- return 0;
- }
- lockShm();
- setShmIsValid( false );
- unlockShm();
- delete[] m_inputs;
- delete[] m_outputs;
- m_inputs = nullptr;
- m_outputs = nullptr;
- setInputOutputCount( inputCount(), outputCount() );
- char buf[64];
- sprintf( buf, "inputs: %d output: %d\n", inputCount(), outputCount() );
- debugMessage( buf );
- if( inputCount() > 0 )
- {
- m_inputs = new float * [inputCount()];
- }
- if( outputCount() > 0 )
- {
- m_outputs = new float * [outputCount()];
- }
- return 1;
- }
- //#define DEBUG_CALLBACKS
- #ifdef DEBUG_CALLBACKS
- #define SHOW_CALLBACK __plugin->debugMessage
- #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?
- */
- intptr_t RemoteVstPlugin::hostCallback( AEffect * _effect, int32_t _opcode,
- int32_t _index, intptr_t _value,
- void * _ptr, float _opt )
- {
- static VstTimeInfo _timeInfo;
- #ifdef DEBUG_CALLBACKS
- char buf[64];
- sprintf( buf, "host-callback, opcode = %d\n", (int) _opcode );
- SHOW_CALLBACK( buf );
- #endif
- // workaround for early callbacks by some plugins
- if( __plugin && __plugin->m_plugin == nullptr )
- {
- __plugin->m_plugin = _effect;
- }
- switch( _opcode )
- {
- case audioMasterAutomate:
- SHOW_CALLBACK( "amc: audioMasterAutomate\n" );
- // index, value, returns 0
- 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)
- PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 );
- 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 <true> such that older versions
- // will always return true.
- return 0;
- case audioMasterGetTime:
- SHOW_CALLBACK( "amc: audioMasterGetTime\n" );
- // returns const VstTimeInfo* (or 0 if not supported)
- // <value> should contain a mask indicating which
- // fields are required (see valid masks above), as some
- // items may require extensive conversions
- // Shared memory was initialised? - see song.cpp
- //assert( __plugin->m_vstSyncData != NULL );
- memset( &_timeInfo, 0, sizeof( _timeInfo ) );
- _timeInfo.samplePos = __plugin->m_currentSamplePos;
- _timeInfo.sampleRate = __plugin->m_vstSyncData->hasSHM ?
- __plugin->m_vstSyncData->m_sampleRate :
- __plugin->sampleRate();
- _timeInfo.flags = 0;
- _timeInfo.tempo = __plugin->m_vstSyncData->hasSHM ?
- __plugin->m_vstSyncData->m_bpm :
- __plugin->m_bpm;
- _timeInfo.timeSigNumerator = __plugin->m_vstSyncData->timeSigNumer;
- _timeInfo.timeSigDenominator = __plugin->m_vstSyncData->timeSigDenom;
- _timeInfo.flags |= kVstTempoValid;
- _timeInfo.flags |= kVstTimeSigValid;
- if( __plugin->m_vstSyncData->isCycle )
- {
- _timeInfo.cycleStartPos = __plugin->m_vstSyncData->cycleStart;
- _timeInfo.cycleEndPos = __plugin->m_vstSyncData->cycleEnd;
- _timeInfo.flags |= kVstCyclePosValid;
- _timeInfo.flags |= kVstTransportCycleActive;
- }
- if( __plugin->m_vstSyncData->ppqPos !=
- __plugin->m_in->m_Timestamp )
- {
- _timeInfo.ppqPos = __plugin->m_vstSyncData->ppqPos;
- __plugin->m_in->lastppqPos = __plugin->m_vstSyncData->ppqPos;
- __plugin->m_in->m_Timestamp = __plugin->m_vstSyncData->ppqPos;
- }
- else if( __plugin->m_vstSyncData->isPlaying )
- {
- if( __plugin->m_vstSyncData->hasSHM )
- {
- __plugin->m_in->lastppqPos +=
- __plugin->m_vstSyncData->m_bpm / 60.0
- * __plugin->m_vstSyncData->m_bufferSize
- / __plugin->m_vstSyncData->m_sampleRate;
- }
- else
- {
- __plugin->m_in->lastppqPos +=
- __plugin->m_bpm / 60.0
- * __plugin->bufferSize()
- / __plugin->sampleRate();
- }
- _timeInfo.ppqPos = __plugin->m_in->lastppqPos;
- }
- // _timeInfo.ppqPos = __plugin->m_vstSyncData->ppqPos;
- _timeInfo.flags |= kVstPpqPosValid;
- if( __plugin->m_vstSyncData->isPlaying )
- {
- _timeInfo.flags |= kVstTransportPlaying;
- }
- _timeInfo.barStartPos = ( (int) ( _timeInfo.ppqPos /
- ( 4 *__plugin->m_vstSyncData->timeSigNumer
- / (float) __plugin->m_vstSyncData->timeSigDenom ) ) ) *
- ( 4 * __plugin->m_vstSyncData->timeSigNumer
- / (float) __plugin->m_vstSyncData->timeSigDenom );
- _timeInfo.flags |= kVstBarsValid;
- if( ( _timeInfo.flags & ( kVstTransportPlaying | kVstTransportCycleActive ) ) !=
- ( __plugin->m_in->m_lastFlags & ( kVstTransportPlaying | kVstTransportCycleActive ) )
- || __plugin->m_vstSyncData->m_playbackJumped )
- {
- _timeInfo.flags |= kVstTransportChanged;
- }
- __plugin->m_in->m_lastFlags = _timeInfo.flags;
- return (intptr_t) &_timeInfo;
- case audioMasterProcessEvents:
- SHOW_CALLBACK( "amc: audioMasterProcessEvents\n" );
- // VstEvents* in <ptr>
- return 0;
- case audioMasterIOChanged:
- SHOW_CALLBACK( "amc: audioMasterIOChanged\n" );
- // numInputs, numOutputs, and/or latency has changed
- return __plugin->updateInOutCount();
- #ifdef OLD_VST_SDK
- case audioMasterWantMidi:
- SHOW_CALLBACK( "amc: audioMasterWantMidi\n" );
- // <value> is a filter which is currently ignored
- return 1;
- case audioMasterSetTime:
- SHOW_CALLBACK( "amc: audioMasterSetTime\n" );
- // VstTimenfo* in <ptr>, filter in <value>, 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 <value> (-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 <value> (-1: first to come), returns
- // cEffect*
- return 0;
- case audioMasterGetNextPlug:
- SHOW_CALLBACK( "amc: audioMasterGetNextPlug\n" );
- // output pin in <value> (-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 1;
- case audioMasterGetSpeakerArrangement:
- SHOW_CALLBACK( "amc: audioMasterGetSpeaker"
- "Arrangement\n" );
- // (long)input in <value>, output in <ptr>
- return 0;
- case audioMasterSetOutputSampleRate:
- SHOW_CALLBACK( "amc: audioMasterSetOutputSample"
- "Rate\n" );
- // for variable i/o, sample rate in <opt>
- return 0;
- case audioMasterSetIcon:
- SHOW_CALLBACK( "amc: audioMasterSetIcon\n" );
- // TODO
- // void* in <ptr>, 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 <ptr>
- 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;
- HWND window = __plugin->m_window;
- DWORD dwStyle = GetWindowLongPtr( window, GWL_STYLE );
- RECT windowSize = { 0, 0, (int) _index, (int) _value };
- AdjustWindowRect( &windowSize, dwStyle, false );
- SetWindowPos( window, 0, 0, 0,
- windowSize.right - windowSize.left,
- windowSize.bottom - windowSize.top,
- SWP_NOACTIVATE | SWP_NOMOVE |
- SWP_NOOWNERZORDER | SWP_NOZORDER );
- __plugin->sendMessage(
- message( IdVstPluginEditorGeometry ).
- addInt( __plugin->m_windowWidth ).
- addInt( __plugin->m_windowHeight ) );
- return 1;
- }
- case audioMasterGetSampleRate:
- SHOW_CALLBACK( "amc: audioMasterGetSampleRate\n" );
- return __plugin->sampleRate();
- case audioMasterGetBlockSize:
- SHOW_CALLBACK( "amc: audioMasterGetBlockSize\n" );
- return __plugin->bufferSize();
- case audioMasterGetInputLatency:
- SHOW_CALLBACK( "amc: audioMasterGetInputLatency\n" );
- return __plugin->bufferSize();
- case audioMasterGetOutputLatency:
- SHOW_CALLBACK( "amc: audioMasterGetOutputLatency\n" );
- return __plugin->bufferSize();
- 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 <ptr> with a string identifying the vendor
- // (max 64 char)
- strcpy( (char *) _ptr, "Tobias Doerffel" );
- return 1;
- case audioMasterGetProductString:
- SHOW_CALLBACK( "amc: audioMasterGetProductString\n" );
- // fills <ptr> 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" ) ||
- !strcmp( (char *) _ptr, "supplyIdle" );
- 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
- PostMessage( __MessageHwnd, WM_USER, GiveIdle, 0 );
- return 0;
- #if kVstVersion > 2
- case audioMasterBeginEdit:
- SHOW_CALLBACK( "amc: audioMasterBeginEdit\n" );
- // begin of automation session (when mouse down),
- // parameter index in <index>
- return 0;
- case audioMasterEndEdit:
- SHOW_CALLBACK( "amc: audioMasterEndEdit\n" );
- // end of automation session (when mouse up),
- // parameter index in <index>
- return 0;
- case audioMasterOpenFileSelector:
- SHOW_CALLBACK( "amc: audioMasterOpenFileSelector\n" );
- // open a fileselector window with VstFileSelect*
- // in <ptr>
- return 0;
- #endif
- default:
- SHOW_CALLBACK( "amd: not handled" );
- break;
- }
- return 0;
- }
- void RemoteVstPlugin::idle()
- {
- if( isProcessing() )
- {
- setShouldGiveIdle( true );
- return;
- }
- setProcessing( true );
- pluginDispatch( effEditIdle );
- setShouldGiveIdle( false );
- setProcessing( false );
- // We might have received a message whilst idling
- processUIThreadMessages();
- }
- void RemoteVstPlugin::processUIThreadMessages()
- {
- setProcessing( true );
- while( m_messageList.size() )
- {
- processMessage( m_messageList.front() );
- m_messageList.pop();
- if( shouldGiveIdle() )
- {
- pluginDispatch( effEditIdle );
- setShouldGiveIdle( false );
- }
- }
- setProcessing( false );
- }
- DWORD WINAPI RemoteVstPlugin::processingThread( LPVOID _param )
- {
- __processingThreadId = GetCurrentThreadId();
- RemoteVstPlugin * _this = static_cast<RemoteVstPlugin *>( _param );
- RemotePluginClient::message m;
- while( ( m = _this->receiveMessage() ).id != IdQuit )
- {
- if( m.id == IdStartProcessing
- || m.id == IdMidiEvent
- || m.id == IdVstSetParameter
- || m.id == IdVstSetTempo )
- {
- _this->processMessage( m );
- }
- else if( m.id == IdChangeSharedMemoryKey )
- {
- _this->processMessage( m );
- _this->setShmIsValid( true );
- }
- else
- {
- PostMessage( __MessageHwnd,
- WM_USER,
- ProcessPluginMessage,
- (LPARAM) new message( m ) );
- }
- }
- // notify GUI thread about shutdown
- PostMessage( __MessageHwnd, WM_USER, ClosePlugin, 0 );
- return 0;
- }
- bool RemoteVstPlugin::setupMessageWindow()
- {
- HMODULE hInst = GetModuleHandle( nullptr );
- if( hInst == nullptr )
- {
- __plugin->debugMessage( "setupMessageWindow(): can't get "
- "module handle\n" );
- return false;
- }
- __MessageHwnd = CreateWindowEx( 0, "LVSL", "dummy",
- 0, 0, 0, 0, 0, nullptr, nullptr,
- hInst, nullptr );
- // install GUI update timer
- SetTimer( __MessageHwnd, 1000, 50, nullptr );
- return true;
- }
- DWORD WINAPI RemoteVstPlugin::guiEventLoop()
- {
- MSG msg;
- while( GetMessage( &msg, nullptr, 0, 0 ) > 0 )
- {
- TranslateMessage( &msg );
- DispatchMessage( &msg );
- }
- return 0;
- }
- LRESULT CALLBACK RemoteVstPlugin::wndProc( HWND hwnd, UINT uMsg,
- WPARAM wParam, LPARAM lParam )
- {
- if( uMsg == WM_TIMER && __plugin->isInitialized() )
- {
- // give plugin some idle-time for GUI-update
- __plugin->idle();
- return 0;
- }
- else if( uMsg == WM_USER )
- {
- switch( wParam )
- {
- case ProcessPluginMessage:
- {
- message * m = (message *) lParam;
- __plugin->queueMessage( *m );
- delete m;
- if( !__plugin->isProcessing() )
- {
- __plugin->processUIThreadMessages();
- }
- return 0;
- }
- case GiveIdle:
- __plugin->idle();
- return 0;
- case ClosePlugin:
- PostQuitMessage(0);
- return 0;
- default:
- break;
- }
- }
- else if( uMsg == WM_SYSCOMMAND && (wParam & 0xfff0) == SC_CLOSE )
- {
- __plugin->hideEditor();
- return 0;
- }
- return DefWindowProc( hwnd, uMsg, wParam, lParam );
- }
- int main( int _argc, char * * _argv )
- {
- #ifdef SYNC_WITH_SHM_FIFO
- if( _argc < 4 )
- #else
- if( _argc < 3 )
- #endif
- {
- fprintf( stderr, "not enough arguments\n" );
- return -1;
- }
- OleInitialize(nullptr);
- #ifdef LMMS_BUILD_LINUX
- #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;
- sched_setscheduler( 0, SCHED_FIFO, &sparam );
- #endif
- #endif
- #ifdef LMMS_BUILD_WIN32
- if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) )
- {
- printf( "Notice: could not set high priority.\n" );
- }
- #endif
- HMODULE hInst = GetModuleHandle( nullptr );
- if( hInst == nullptr )
- {
- return -1;
- }
- WNDCLASS wc;
- wc.style = CS_HREDRAW | CS_VREDRAW;
- wc.lpfnWndProc = RemoteVstPlugin::wndProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- wc.hInstance = hInst;
- wc.hIcon = LoadIcon( nullptr, IDI_APPLICATION );
- wc.hCursor = LoadCursor( nullptr, IDC_ARROW );
- wc.hbrBackground = nullptr;
- wc.lpszMenuName = nullptr;
- wc.lpszClassName = "LVSL";
- if( !RegisterClass( &wc ) )
- {
- return -1;
- }
- {
- #ifdef SYNC_WITH_SHM_FIFO
- int embedMethodIndex = 3;
- #else
- int embedMethodIndex = 2;
- #endif
- std::string embedMethod = _argv[embedMethodIndex];
- if ( embedMethod == "none" )
- {
- cerr << "Starting detached." << endl;
- EMBED = EMBED_X11 = EMBED_WIN32 = HEADLESS = false;
- }
- else if ( embedMethod == "win32" )
- {
- cerr << "Starting using Win32-native embedding." << endl;
- EMBED = EMBED_WIN32 = true; EMBED_X11 = HEADLESS = false;
- }
- else if ( embedMethod == "qt" )
- {
- cerr << "Starting using Qt-native embedding." << endl;
- EMBED = true; EMBED_X11 = EMBED_WIN32 = HEADLESS = false;
- }
- else if ( embedMethod == "xembed" )
- {
- cerr << "Starting using X11Embed protocol." << endl;
- EMBED = EMBED_X11 = true; EMBED_WIN32 = HEADLESS = false;
- }
- else if ( embedMethod == "headless" )
- {
- cerr << "Starting without UI." << endl;
- HEADLESS = true; EMBED = EMBED_X11 = EMBED_WIN32 = false;
- }
- else
- {
- cerr << "Unknown embed method " << embedMethod << ". Starting detached instead." << endl;
- EMBED = EMBED_X11 = EMBED_WIN32 = HEADLESS = false;
- }
- }
- // constructor automatically will process messages until it receives
- // a IdVstLoadPlugin message and processes it
- #ifdef SYNC_WITH_SHM_FIFO
- __plugin = new RemoteVstPlugin( atoi( _argv[1] ), atoi( _argv[2] ) );
- #else
- __plugin = new RemoteVstPlugin( _argv[1] );
- #endif
- if( __plugin->isInitialized() )
- {
- if( RemoteVstPlugin::setupMessageWindow() == false )
- {
- return -1;
- }
- if( CreateThread( nullptr, 0, RemoteVstPlugin::processingThread,
- __plugin, 0, nullptr ) == nullptr )
- {
- __plugin->debugMessage( "could not create "
- "processingThread\n" );
- return -1;
- }
- RemoteVstPlugin::guiEventLoop();
- }
- delete __plugin;
- OleUninitialize();
- return 0;
- }
|