code:
/*
 * 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 = NULL;

HWND __MessageHwnd = NULL;
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,
								 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);

	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,
						NULL, (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 = NULL, 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( NULL ),
	m_plugin( NULL ),
	m_window( NULL ),
	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( NULL ),
	m_outputs( NULL ),
	m_shmValid( false ),
	m_midiEvents(),
	m_bpm( 0 ),
	m_currentSamplePos( 0 ),
	m_currentProgram( -1 ),
	m_in( NULL ),
	m_shmID( -1 ),
	m_vstSyncData( NULL )
{
	__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 == NULL )
	{
		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 != NULL )
		{
			delete m_vstSyncData;
			m_vstSyncData = NULL;
		}
	}
#endif

	if( m_libInst != NULL )
	{
		FreeLibrary( m_libInst );
		m_libInst = NULL;
	}

	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( NULL );
	if( hInst == NULL )
	{
		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, NULL, NULL, hInst, NULL );
	if( m_window == NULL )
	{
		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 == NULL )
	{
		return;
	}

	pluginDispatch( effEditClose );
	// Destroying the window takes some time in Wine 1.8.5
	DestroyWindow( m_window );
	m_window = NULL;
}

bool RemoteVstPlugin::load( const std::string & _plugin_file )
{
	if( ( m_libInst = LoadLibraryW( toWString(_plugin_file).c_str() ) ) == NULL )
	{
		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 == NULL )
	{
		mainEntry = (mainEntryPointer)
				GetProcAddress( m_libInst, "VstPluginMain" );
	}
	if( mainEntry == NULL )
	{
		mainEntry = (mainEntryPointer)
				GetProcAddress( m_libInst, "main" );
	}
	if( mainEntry == NULL )
	{
		debugMessage( "could not find entry point\n" );
		return false;
	}

	m_plugin = mainEntry( hostCallback );
	if( m_plugin == NULL )
	{
		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 = NULL;
		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 = NULL;
	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 = NULL;
	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 = NULL;
	m_outputs = NULL;

	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 == NULL )
	{
		__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( NULL );
	if( hInst == NULL )
	{
		__plugin->debugMessage( "setupMessageWindow(): can't get "
							"module handle\n" );
		return false;
	}

	__MessageHwnd = CreateWindowEx( 0, "LVSL", "dummy",
						0, 0, 0, 0, 0, NULL, NULL,
								hInst, NULL );
	// install GUI update timer
	SetTimer( __MessageHwnd, 1000, 50, NULL );

	return true;
}

DWORD WINAPI RemoteVstPlugin::guiEventLoop()
{
	MSG msg;
	while( GetMessage( &msg, NULL, 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( NULL );
	if( hInst == NULL )
	{
		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( NULL, IDI_APPLICATION );
	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = NULL;
	wc.lpszMenuName = NULL;
	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( NULL, 0, RemoteVstPlugin::processingThread,
						__plugin, 0, NULL ) == NULL )
		{
			__plugin->debugMessage( "could not create "
							"processingThread\n" );
			return -1;
		}
		RemoteVstPlugin::guiEventLoop();
	}

	delete __plugin;

	OleUninitialize();
	return 0;

}


code:
cmake_minimum_required(VERSION 3.1)
project(RemoteVstPlugin
	LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)

include(CheckCXXPreprocessor)
include(CheckCXXSourceCompiles)

CHECK_CXX_DEFINE(IS_WIN "_WIN32")
CHECK_CXX_DEFINE(IS_WIN64 "_WIN64")
CHECK_CXX_DEFINE(IS_MINGW "__MINGW32__")

if(NOT IS_WIN)
	message(FATAL_ERROR "Toolchain used does not target windows.")
ENDIF()

if(IS_WIN64 OR CMAKE_SIZEOF_VOID_P EQUAL 8)
	set(BITNESS 64)
else()
	set(BITNESS 32)
	set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/32")
endif()

FOREACH( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )
	STRING(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
	SET("CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
ENDFOREACH()

set(EXE_NAME RemoteVstPlugin${BITNESS})
add_executable(${EXE_NAME} WIN32
	../RemoteVstPlugin.cpp
)

target_link_libraries(${EXE_NAME} ole32)

target_include_directories(${EXE_NAME}
	PRIVATE
		"${LMMS_SOURCE_DIR}/plugins/vst_base/common"
		"${LMMS_SOURCE_DIR}/include"
		"${LMMS_BINARY_DIR}"
)

# Workaround for missing WinMain
if(MSVC)
	set_property(TARGET ${EXE_NAME}
	APPEND
	PROPERTY LINK_FLAGS "/entry:mainCRTStartup"
)
endif()

if(WIN32)
	find_package(Qt5Core REQUIRED)
	target_link_libraries(${EXE_NAME} Qt5::Core)
endif()

if(IS_MINGW)
	SET(CMAKE_REQUIRED_FLAGS "-std=c++14")

	CHECK_CXX_SOURCE_COMPILES("
	#include <mutex>
	int main(int argc, const char* argv[]) {
		std::mutex m;
		return 0;
	}
	" HAS_STD_MUTEX)

	if(NOT HAS_STD_MUTEX)
		target_include_directories(${EXE_NAME} PRIVATE
			"${LMMS_SOURCE_DIR}/src/3rdparty/mingw-std-threads")
		target_compile_definitions(${EXE_NAME} PRIVATE
			-DUSE_MINGW_THREADS_REPLACEMENT)
	endif()
endif()

IF(STRIP)
	ADD_CUSTOM_COMMAND(TARGET ${EXE_NAME} POST_BUILD COMMAND "${STRIP}" "$<TARGET_FILE:${EXE_NAME}>")
ENDIF()

if(BITNESS EQUAL 32)
	INSTALL(TARGETS ${EXE_NAME} RUNTIME DESTINATION "${PLUGIN_DIR}/32")
else()
	INSTALL(TARGETS ${EXE_NAME} RUNTIME DESTINATION "${PLUGIN_DIR}")
endif()

if(BUILD_WITH_EXTERNALPROJECT)
	include(InstallTargetDependencies)
	INSTALL_TARGET_DEPENDENCIES(TARGETS ${EXE_NAME}
			DESTINATION "${PLUGIN_DIR}/32")
else()
	# Needed to deploy dependencies of RemoteVstPlugin
	SET_PROPERTY(GLOBAL APPEND PROPERTY PLUGINS_BUILT "${EXE_NAME}")
endif()

repo_name: CYBERDEViL/LMMS

license: mit


code:
# INSTALL_EXTERNAL_PROJECT: install a project created with ExternalProject_Add in the
# parent project's install time.
#
# Description:
# In a regular scenario, cmake will install external projects
# BEFORE actually building the parent project. Since the building
# process may use installed components from the project.
# We want to give the external project the ability to install
# files directly to the parent's install. Therefore, we have to
# manually trigger the install stage with the parent's INSTALL_PREFIX.
MACRO(INSTALL_EXTERNAL_PROJECT name)
	ExternalProject_Get_Property(${name} BINARY_DIR)

	install(CODE "include(\"${BINARY_DIR}/cmake_install.cmake\")")
ENDMACRO()

IF(LMMS_BUILD_WIN32 AND NOT LMMS_BUILD_WIN64)
	ADD_SUBDIRECTORY(RemoteVstPlugin)
ELSEIF(LMMS_BUILD_WIN64 AND MSVC)
	IF(NOT QT_32_PREFIX)
		SET(LMMS_MSVC_YEAR_FOR_QT ${LMMS_MSVC_YEAR})

		if(LMMS_MSVC_YEAR_FOR_QT EQUAL 2019 AND Qt5_VERSION VERSION_LESS "5.15")
			SET(LMMS_MSVC_YEAR_FOR_QT 2017) # Qt only provides binaries for MSVC 2017, but 2019 is binary compatible
		endif()

		GET_FILENAME_COMPONENT(QT_BIN_DIR ${QT_QMAKE_EXECUTABLE} DIRECTORY)
		SET(QT_32_PREFIX "${QT_BIN_DIR}/../../msvc${LMMS_MSVC_YEAR_FOR_QT}")
	ENDIF()

	#TODO: qt5 installed using vcpkg: I don't know how to detect if the user built the x86 version of qt5 from here. At least not cleanly.
	#So for the moment, we'll allow the built.
	IF(NOT (IS_DIRECTORY ${QT_32_PREFIX} AND EXISTS ${QT_32_PREFIX}/bin/qmake.exe))
		MESSAGE(WARNING "No Qt 32 bit installation found at ${QT_32_PREFIX}. If you're using VCPKG you can ignore this message if you've built x86-windows version of qt5")
	ENDIF()

	ExternalProject_Add(RemoteVstPlugin32
		"${EXTERNALPROJECT_ARGS}"
		CMAKE_GENERATOR "${LMMS_MSVC_GENERATOR}"
		CMAKE_GENERATOR_PLATFORM Win32
		#CMAKE_GENERATOR_TOOLSET "${CMAKE_GENERATOR_TOOLSET}"
		CMAKE_ARGS
			"${EXTERNALPROJECT_CMAKE_ARGS}"
			"-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}"
			"-DCMAKE_PREFIX_PATH=${QT_32_PREFIX}"
	)

	INSTALL_EXTERNAL_PROJECT(RemoteVstPlugin32)
ELSEIF(LMMS_BUILD_LINUX)
	# Use winegcc
	INCLUDE(CheckWineGcc)
	CheckWineGcc(32 "${WINEGCC}" WINEGCC_WORKING)
	IF(NOT WINEGCC_WORKING)
		MESSAGE(WARNING "winegcc fails to complie 32-bit binaries, please make sure you have 32-bit GCC libraries")
		RETURN()
	ENDIF()
	ExternalProject_Add(RemoteVstPlugin32
		"${EXTERNALPROJECT_ARGS}"
		CMAKE_ARGS
			"${EXTERNALPROJECT_CMAKE_ARGS}"
			"-DCMAKE_CXX_COMPILER=${WINEGCC}"
			"-DCMAKE_CXX_FLAGS=-m32"
	)

	INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32" "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe.so" DESTINATION "${PLUGIN_DIR}/32")

ELSEIF(CMAKE_TOOLCHAIN_FILE_32)
	ExternalProject_Add(RemoteVstPlugin32
		"${EXTERNALPROJECT_ARGS}"
		CMAKE_ARGS
			"${EXTERNALPROJECT_CMAKE_ARGS}"
			"-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH_32}"
			"-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE_32}"
	)
	INSTALL_EXTERNAL_PROJECT(RemoteVstPlugin32)
ELSE()
	MESSAGE(WARNING "Can't build RemoteVstPlugin32, unknown environment. Please supply CMAKE_TOOLCHAIN_FILE_32 and optionally CMAKE_PREFIX_PATH_32")
	RETURN()
ENDIF()


code:
IF(LMMS_BUILD_WIN64)
	ADD_SUBDIRECTORY(RemoteVstPlugin)
ELSEIF(LMMS_BUILD_LINUX)
	INCLUDE(CheckWineGcc)
	CheckWineGcc(64 "${WINEGCC}" WINEGCC_WORKING)
	IF(NOT WINEGCC_WORKING)
		MESSAGE(WARNING "winegcc fails to compile 64-bit binaries, please make sure you have 64-bit GCC libraries")
		RETURN()
	ENDIF()
	ExternalProject_Add(RemoteVstPlugin64
		"${EXTERNALPROJECT_ARGS}"
		CMAKE_ARGS
			"${EXTERNALPROJECT_CMAKE_ARGS}"
			"-DCMAKE_CXX_COMPILER=${WINEGCC}"
			"-DCMAKE_CXX_FLAGS=-m64"
	)
	INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin64" "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin64.exe.so" DESTINATION "${PLUGIN_DIR}")
ENDIF()


code:
/*
 * VstPlugin.cpp - implementation of VstPlugin class
 *
 * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "VstPlugin.h"

#include "communication.h"

#include <QtCore/QtEndian>
#include <QtCore/QDebug>
#include <QDir>
#include <QFileInfo>
#include <QLocale>
#include <QTemporaryFile>
#include <QCloseEvent>
#include <QMdiArea>
#include <QMdiSubWindow>

#ifdef LMMS_BUILD_LINUX
#	include <QX11Info>
#	include "X11EmbedContainer.h"
#endif

#include <QWindow>

#include <QDomDocument>

#ifdef LMMS_BUILD_WIN32
#	include <windows.h>
#	include <QLayout>
#endif

#include "ConfigManager.h"
#include "GuiApplication.h"
#include "LocaleHelper.h"
#include "MainWindow.h"
#include "Mixer.h"
#include "PathUtil.h"
#include "Song.h"
#include "FileDialog.h"

#ifdef LMMS_BUILD_LINUX
#	include <X11/Xlib.h>
#endif

namespace PE
{
// Utilities for reading PE file machine type
// See specification at https://msdn.microsoft.com/library/windows/desktop/ms680547(v=vs.85).aspx

// Work around name conflict
#ifdef i386
#	undef i386
#endif

enum class MachineType : uint16_t
{
	unknown = 0x0,
	amd64 = 0x8664,
	i386 = 0x14c,
};

class FileInfo
{
public:
	FileInfo(QString filePath)
		: m_file(filePath)
	{
		m_file.open(QFile::ReadOnly);
		m_map = m_file.map(0, m_file.size());
		if (m_map == nullptr) {
			throw std::runtime_error("Cannot map file");
		}
	}
	~FileInfo()
	{
		m_file.unmap(m_map);
	}

	MachineType machineType()
	{
		int32_t peOffset = qFromLittleEndian(* reinterpret_cast<int32_t*>(m_map + 0x3C));
		uchar* peSignature = m_map + peOffset;
		if (memcmp(peSignature, "PE\0\0", 4)) {
			throw std::runtime_error("Invalid PE file");
		}
		uchar * coffHeader = peSignature + 4;
		uint16_t machineType = qFromLittleEndian(* reinterpret_cast<uint16_t*>(coffHeader));
		return static_cast<MachineType>(machineType);
	}

private:
	QFile m_file;
	uchar* m_map;
};

}

VstPlugin::VstPlugin( const QString & _plugin ) :
	m_plugin( PathUtil::toAbsolute(_plugin) ),
	m_pluginWindowID( 0 ),
	m_embedMethod( gui
			? ConfigManager::inst()->vstEmbedMethod()
			: "headless" ),
	m_version( 0 ),
	m_currentProgram()
{
	setSplittedChannels( true );

	PE::MachineType machineType;
	try {
		PE::FileInfo peInfo(m_plugin);
		machineType = peInfo.machineType();
	} catch (std::runtime_error& e) {
		qCritical() << "Error while determining PE file's machine type: " << e.what();
		machineType = PE::MachineType::unknown;
	}

	switch(machineType)
	{
	case PE::MachineType::amd64:
		tryLoad( REMOTE_VST_PLUGIN_FILEPATH_64 ); // Default: RemoteVstPlugin64
		break;
	case PE::MachineType::i386:
		tryLoad( REMOTE_VST_PLUGIN_FILEPATH_32 ); // Default: 32/RemoteVstPlugin32
		break;
	default:
		m_failed = true;
		return;
	}

	setTempo( Engine::getSong()->getTempo() );

	connect( Engine::getSong(), SIGNAL( tempoChanged( bpm_t ) ),
			this, SLOT( setTempo( bpm_t ) ), Qt::DirectConnection );
	connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
				this, SLOT( updateSampleRate() ) );

	// update once per second
	m_idleTimer.start( 1000 );
	connect( &m_idleTimer, SIGNAL( timeout() ),
				this, SLOT( idleUpdate() ) );
}

VstPlugin::~VstPlugin()
{
	delete m_pluginWidget;
}

void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable )
{
	init( remoteVstPluginExecutable, false, {m_embedMethod} );

	waitForHostInfoGotten();
	if( failed() )
	{
		return;
	}

	lock();

	VstHostLanguages hlang = LanguageEnglish;
	switch( QLocale::system().language() )
	{
		case QLocale::French: hlang = LanguageFrench; break;
		case QLocale::German: hlang = LanguageGerman; break;
		case QLocale::Italian: hlang = LanguageItalian; break;
		case QLocale::Japanese: hlang = LanguageJapanese; break;
		case QLocale::Korean: hlang = LanguageKorean; break;
		case QLocale::Spanish: hlang = LanguageSpanish; break;
		default: break;
	}
	sendMessage( message( IdVstSetLanguage ).addInt( hlang ) );
	sendMessage( message( IdVstLoadPlugin ).addString( QSTR_TO_STDSTR( m_plugin ) ) );

	waitForInitDone();

	unlock();
}

void VstPlugin::loadSettings( const QDomElement & _this )
{
	if( _this.hasAttribute( "program" ) )
	{
		setProgram( _this.attribute( "program" ).toInt() );
	}

	const int num_params = _this.attribute( "numparams" ).toInt();
	// if it exists try to load settings chunk
	if( _this.hasAttribute( "chunk" ) )
	{
		loadChunk( QByteArray::fromBase64(
				_this.attribute( "chunk" ).toUtf8() ) );
	}
	else if( num_params > 0 )
	{
		// no chunk, restore individual parameters
		QMap<QString, QString> dump;
		for( int i = 0; i < num_params; ++i )
		{
			const QString key = "param" +
						QString::number( i );
			dump[key] = _this.attribute( key );
		}
		setParameterDump( dump );
	}
}

void VstPlugin::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	if ( m_embedMethod != "none" )
	{
		if( pluginWidget() != NULL )
		{
			_this.setAttribute( "guivisible", pluginWidget()->isVisible() );
		}
	}
	else
	{
		int visible = isUIVisible();
		if ( visible != -1 )
		{
			_this.setAttribute( "guivisible", visible );
		}
	}

	// try to save all settings in a chunk
	QByteArray chunk = saveChunk();
	if( !chunk.isEmpty() )
	{
		_this.setAttribute( "chunk", QString( chunk.toBase64() ) );
	}
	else
	{
		// plugin doesn't seem to support chunks, therefore save
		// individual parameters
		const QMap<QString, QString> & dump = parameterDump();
		_this.setAttribute( "numparams", dump.size() );
		for( QMap<QString, QString>::const_iterator it = dump.begin();
							it != dump.end(); ++it )
		{
			_this.setAttribute( it.key(), it.value() );
		}
	}

	_this.setAttribute( "program", currentProgram() );
}

void VstPlugin::toggleUI()
{
	if ( m_embedMethod == "none" )
	{
		RemotePlugin::toggleUI();
	}
	else if (pluginWidget())
	{
		toggleEditorVisibility();
	}
}

void VstPlugin::setTempo( bpm_t _bpm )
{
	lock();
	sendMessage( message( IdVstSetTempo ).addInt( _bpm ) );
	unlock();
}

void VstPlugin::updateSampleRate()
{
	lock();
	sendMessage( message( IdSampleRateInformation ).
			addInt( Engine::mixer()->processingSampleRate() ) );
	waitForMessage( IdInformationUpdated, true );
	unlock();
}

int VstPlugin::currentProgram()
{
	lock();
	sendMessage( message( IdVstCurrentProgram ) );
	waitForMessage( IdVstCurrentProgram, true );
	unlock();

	return m_currentProgram;
}

const QMap<QString, QString> & VstPlugin::parameterDump()
{
	lock();
	sendMessage( IdVstGetParameterDump );
	waitForMessage( IdVstParameterDump, true );
	unlock();

	return m_parameterDump;
}

void VstPlugin::setParameterDump( const QMap<QString, QString> & _pdump )
{
	message m( IdVstSetParameterDump );
	m.addInt( _pdump.size() );
	for( QMap<QString, QString>::ConstIterator it = _pdump.begin();
						it != _pdump.end(); ++it )
	{
		const VstParameterDumpItem item =
		{
			( *it ).section( ':', 0, 0 ).toInt(),
			"",
			LocaleHelper::toFloat((*it).section(':', 2, -1))
		} ;
		m.addInt( item.index );
		m.addString( item.shortLabel );
		m.addFloat( item.value );
	}
	lock();
	sendMessage( m );
	unlock();
}

QWidget *VstPlugin::pluginWidget()
{
	return m_pluginWidget;
}

bool VstPlugin::processMessage( const message & _m )
{
	switch( _m.id )
	{
	case IdVstPluginWindowID:
		m_pluginWindowID = _m.getInt();
		if( m_embedMethod == "none"
			&& ConfigManager::inst()->value(
				"ui", "vstalwaysontop" ).toInt() )
		{
#ifdef LMMS_BUILD_WIN32
			// We're changing the owner, not the parent,
			// so this is legal despite MSDN's warning
			SetWindowLongPtr( (HWND)(intptr_t) m_pluginWindowID,
					GWLP_HWNDPARENT,
					(LONG_PTR) gui->mainWindow()->winId() );
#endif

#ifdef LMMS_BUILD_LINUX
			XSetTransientForHint( QX11Info::display(),
					m_pluginWindowID,
					gui->mainWindow()->winId() );
#endif
		}
		break;

	case IdVstPluginEditorGeometry:
		m_pluginGeometry = QSize( _m.getInt( 0 ),
								  _m.getInt( 1 ) );
			break;

		case IdVstPluginName:
			m_name = _m.getQString();
			break;

		case IdVstPluginVersion:
			m_version = _m.getInt();
			break;

		case IdVstPluginVendorString:
			m_vendorString = _m.getQString();
			break;

		case IdVstPluginProductString:
			m_productString = _m.getQString();
			break;

		case IdVstCurrentProgram:
			m_currentProgram = _m.getInt();
			break;

		case IdVstCurrentProgramName:
			m_currentProgramName = _m.getQString();
			break;

		case IdVstProgramNames:
			m_allProgramNames = _m.getQString();
			break;

		case IdVstParameterLabels:
			m_allParameterLabels = _m.getQString();
			break;

		case IdVstParameterDisplays:
			m_allParameterDisplays = _m.getQString();
			break;

		case IdVstPluginUniqueID:
			// TODO: display graphically in case of failure
			printf("unique ID: %s\n", _m.getString().c_str() );
			break;

		case IdVstParameterDump:
		{
			m_parameterDump.clear();
			const int num_params = _m.getInt();
			int p = 0;
			for( int i = 0; i < num_params; ++i )
			{
				VstParameterDumpItem item;
				item.index = _m.getInt( ++p );
				item.shortLabel = _m.getString( ++p );
				item.value = _m.getFloat( ++p );
	m_parameterDump["param" + QString::number( item.index )] =
				QString::number( item.index ) + ":" +
/*uncomented*/				/*QString( item.shortLabel )*/ QString::fromStdString(item.shortLabel) + ":" +
					QString::number( item.value );
			}
			break;
		}
		default:
			return RemotePlugin::processMessage( _m );
	}
	return true;

}

QWidget *VstPlugin::editor()
{
	return m_pluginWidget;
}

void VstPlugin::openPreset( )
{

	FileDialog ofd( NULL, tr( "Open Preset" ), "",
		tr( "Vst Plugin Preset (*.fxp *.fxb)" ) );
	ofd.setFileMode( FileDialog::ExistingFiles );
	if( ofd.exec () == QDialog::Accepted &&
					!ofd.selectedFiles().isEmpty() )
	{
		lock();
		sendMessage( message( IdLoadPresetFile ).
			addString(
				QSTR_TO_STDSTR(
					QDir::toNativeSeparators( ofd.selectedFiles()[0] ) ) )
			);
		waitForMessage( IdLoadPresetFile, true );
		unlock();
	}
}

void VstPlugin::setProgram( int index )
{
	lock();
	sendMessage( message( IdVstSetProgram ).addInt( index ) );
	waitForMessage( IdVstSetProgram, true );
	unlock();
}

void VstPlugin::rotateProgram( int offset )
{
	lock();
	sendMessage( message( IdVstRotateProgram ).addInt( offset ) );
	waitForMessage( IdVstRotateProgram, true );
	unlock();
}

void VstPlugin::loadProgramNames()
{
	lock();
	sendMessage( message( IdVstProgramNames ) );
	waitForMessage( IdVstProgramNames, true );
	unlock();
}

void VstPlugin::loadParameterLabels()
{
	lock();
	sendMessage( message( IdVstParameterLabels ) );
	waitForMessage( IdVstParameterLabels, true );
	unlock();
}

void VstPlugin::loadParameterDisplays()
{
	lock();
	sendMessage( message( IdVstParameterDisplays ) );
	waitForMessage( IdVstParameterDisplays, true );
	unlock();
}

void VstPlugin::savePreset( )
{
	QString presName = currentProgramName().isEmpty() ? tr(": default") : currentProgramName();
	presName.replace("\"", "'"); // QFileDialog unable to handle double quotes properly

	FileDialog sfd( NULL, tr( "Save Preset" ), presName.section(": ", 1, 1) + tr(".fxp"),
		tr( "Vst Plugin Preset (*.fxp *.fxb)" ) );

	if( p_name != "" ) // remember last directory
	{
		sfd.setDirectory( QFileInfo( p_name ).absolutePath() );
	}

	sfd.setAcceptMode( FileDialog::AcceptSave );
	sfd.setFileMode( FileDialog::AnyFile );
	if( sfd.exec () == QDialog::Accepted &&
				!sfd.selectedFiles().isEmpty() && sfd.selectedFiles()[0] != "" )
	{
		QString fns = sfd.selectedFiles()[0];
		p_name = fns;

		if ((fns.toUpper().indexOf(tr(".FXP")) == -1) && (fns.toUpper().indexOf(tr(".FXB")) == -1))
			fns = fns + tr(".fxb");
		else fns = fns.left(fns.length() - 4) + (fns.right( 4 )).toLower();
		lock();
		sendMessage( message( IdSavePresetFile ).
			addString(
				QSTR_TO_STDSTR(
					QDir::toNativeSeparators( fns ) ) )
			);
		waitForMessage( IdSavePresetFile, true );
		unlock();
	}
}

void VstPlugin::setParam( int i, float f )
{
	lock();
	sendMessage( message( IdVstSetParameter ).addInt( i ).addFloat( f ) );
	//waitForMessage( IdVstSetParameter, true );
	unlock();
}

void VstPlugin::idleUpdate()
{
	lock();
	sendMessage( message( IdVstIdleUpdate ) );
	unlock();
}

void VstPlugin::showUI()
{
	if ( m_embedMethod == "none" )
	{
		RemotePlugin::showUI();
	}
	else if ( m_embedMethod != "headless" )
	{
		if (! editor()) {
			qWarning() << "VstPlugin::showUI called before VstPlugin::createUI";
		}
		toggleEditorVisibility( true );
	}
}

void VstPlugin::hideUI()
{
	if ( m_embedMethod == "none" )
	{
		RemotePlugin::hideUI();
	}
	else if ( pluginWidget() != nullptr )
	{
		toggleEditorVisibility( false );
	}
}

// X11Embed only
void VstPlugin::handleClientEmbed()
{
	lock();
	sendMessage( IdShowUI );
	unlock();
}

void VstPlugin::loadChunk( const QByteArray & _chunk )
{
	QTemporaryFile tf;
	if( tf.open() )
	{
		tf.write( _chunk );
		tf.flush();

		lock();
		sendMessage( message( IdLoadSettingsFromFile ).
				addString(
					QSTR_TO_STDSTR(
						QDir::toNativeSeparators( tf.fileName() ) ) ).
				addInt( _chunk.size() ) );
		waitForMessage( IdLoadSettingsFromFile, true );
		unlock();
	}
}

QByteArray VstPlugin::saveChunk()
{
	QByteArray a;
	QTemporaryFile tf;
	if( tf.open() )
	{
		lock();
		sendMessage( message( IdSaveSettingsToFile ).
				addString(
					QSTR_TO_STDSTR(
						QDir::toNativeSeparators( tf.fileName() ) ) ) );
		waitForMessage( IdSaveSettingsToFile, true );
		unlock();
		a = tf.readAll();
	}

	return a;
}

void VstPlugin::toggleEditorVisibility( int visible )
{
	QWidget* w = editor();
	if ( ! w ) {
		return;
	}

	if ( visible < 0 ) {
		visible = ! w->isVisible();
	}
	w->setVisible( visible );
}

void VstPlugin::createUI( QWidget * parent )
{
	if ( m_pluginWidget ) {
		qWarning() << "VstPlugin::createUI called twice";
		m_pluginWidget->setParent( parent );
		return;
	}

	if( m_pluginWindowID == 0 )
	{
		return;
	}

	QWidget* container = nullptr;

#if QT_VERSION >= 0x050100
	if (m_embedMethod == "qt" )
	{
		QWindow* vw = QWindow::fromWinId(m_pluginWindowID);
		container = QWidget::createWindowContainer(vw, parent );
		container->installEventFilter(this);
	} else
#endif

#ifdef LMMS_BUILD_WIN32
	if (m_embedMethod == "win32" )
	{
		QWidget * helper = new QWidget;
		QHBoxLayout * l = new QHBoxLayout( helper );
		QWidget * target = new QWidget( helper );
		l->setSpacing( 0 );
		l->setMargin( 0 );
		l->addWidget( target );

		// we've to call that for making sure, Qt created the windows
		helper->winId();
		HWND targetHandle = (HWND)target->winId();
		HWND pluginHandle = (HWND)(intptr_t)m_pluginWindowID;

		DWORD style = GetWindowLong(pluginHandle, GWL_STYLE);
		style = style & ~(WS_POPUP);
		style = style | WS_CHILD;
		SetWindowLong(pluginHandle, GWL_STYLE, style);
		SetParent(pluginHandle, targetHandle);

		DWORD threadId = GetWindowThreadProcessId(pluginHandle, NULL);
		DWORD currentThreadId = GetCurrentThreadId();
		AttachThreadInput(currentThreadId, threadId, true);

		container = helper;
		RemotePlugin::showUI();

	} else
#endif

#ifdef LMMS_BUILD_LINUX
	if (m_embedMethod == "xembed" )
	{
		if (parent)
		{
			parent->setAttribute(Qt::WA_NativeWindow);
		}
		QX11EmbedContainer * embedContainer = new QX11EmbedContainer( parent );
		connect(embedContainer, SIGNAL(clientIsEmbedded()), this, SLOT(handleClientEmbed()));
		embedContainer->embedClient( m_pluginWindowID );
		container = embedContainer;
	} else
#endif
	{
		qCritical() << "Unknown embed method" << m_embedMethod;
		return;
	}

	container->setFixedSize( m_pluginGeometry );
	container->setWindowTitle( name() );

	m_pluginWidget = container;
}

bool VstPlugin::eventFilter(QObject *obj, QEvent *event)
{
#if QT_VERSION >= 0x050100
	if (embedMethod() == "qt" && obj == m_pluginWidget)
	{
		if (event->type() == QEvent::Show) {
			RemotePlugin::showUI();
		}
		qDebug() << obj << event;
	}
#endif
	return false;
}

QString VstPlugin::embedMethod() const
{
	return m_embedMethod;
}


code:
/*
 * VstPlugin.h - declaration of VstPlugin class
 *
 * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#ifndef _VST_PLUGIN_H
#define _VST_PLUGIN_H

#include <QMap>
#include <QMutex>
#include <QPointer>
#include <QString>
#include <QTimer>
#include <QWidget>

#include "JournallingObject.h"
#include "RemotePlugin.h"

#include "vstbase_export.h"

class vstSubWin;

class VSTBASE_EXPORT VstPlugin : public RemotePlugin, public JournallingObject
{
	Q_OBJECT
public:
	VstPlugin( const QString & _plugin );
	virtual ~VstPlugin();

	void tryLoad( const QString &remoteVstPluginExecutable );

	bool processMessage( const message & _m ) override;

	inline bool hasEditor() const
	{
		return m_pluginWindowID != 0;
	}

	/// Same as pluginWidget(), but can be overwritten in sub-classes to modify
	/// behavior the UI. This is used in VstInstrumentPlugin to wrap the VST UI
	/// in a QMdiSubWindow
	virtual QWidget* editor();

	inline const QString & name() const
	{
		return m_name;
	}

	inline int version() const
	{
		return m_version;
	}

	inline const QString & vendorString() const
	{
		return m_vendorString;
	}

	inline const QString & productString() const
	{
		return m_productString;
	}

	inline const QString& currentProgramName() const
	{
		return m_currentProgramName;
	}

	inline const QString& allProgramNames() const
	{
		return m_allProgramNames;
	}

	inline const QString& allParameterLabels() const
	{
		return m_allParameterLabels;
	}

	inline const QString& allParameterDisplays() const
	{
		return m_allParameterDisplays;
	}

	int currentProgram();

	const QMap<QString, QString> & parameterDump();
	void setParameterDump( const QMap<QString, QString> & _pdump );

	QWidget * pluginWidget();

	void loadSettings( const QDomElement & _this ) override;
	void saveSettings( QDomDocument & _doc, QDomElement & _this ) override;

	virtual QString nodeName() const override
	{
		return "vstplugin";
	}

	virtual void createUI(QWidget *parent);
	bool eventFilter(QObject *obj, QEvent *event) override;

	QString embedMethod() const;

public slots:
	void setTempo( bpm_t _bpm );
	void updateSampleRate();
	void openPreset( void );
	void setProgram( int index );
	void rotateProgram( int offset );
	void loadProgramNames();
	void loadParameterLabels();
	void loadParameterDisplays();
	void savePreset( void );
	void setParam( int i, float f );
	void idleUpdate();

	void showUI() override;
	void hideUI() override;
	void toggleUI() override;

	void handleClientEmbed();

private:
	void loadChunk( const QByteArray & _chunk );
	QByteArray saveChunk();

	void toggleEditorVisibility(int visible = -1);

	QString m_plugin;
	QPointer<QWidget> m_pluginWidget;
	int m_pluginWindowID;
	QSize m_pluginGeometry;
	const QString m_embedMethod;

	QString m_name;
	int m_version;
	QString m_vendorString;
	QString m_productString;
	QString m_currentProgramName;
	QString m_allProgramNames;
	QString m_allParameterLabels;
	QString m_allParameterDisplays;

	QString p_name;

	QMap<QString, QString> m_parameterDump;

	int m_currentProgram;

	QTimer m_idleTimer;

} ;

#endif


code:
/*
 * communication.h - header file defining stuff concerning communication between
 *                   LVSL-server and -client
 *
 * Copyright (c) 2005-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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.
 *
 */

#ifndef _COMMUNICATION_H
#define _COMMUNICATION_H

struct VstParameterDumpItem
{
	int32_t index;
	std::string shortLabel;
	float value;
} ;

enum VstHostLanguages
{
	LanguageEnglish = 1,
	LanguageGerman,
	LanguageFrench,
	LanguageItalian,
	LanguageSpanish,
	LanguageJapanese,
	LanguageKorean
} ;

enum VstRemoteMessageIDs
{
	// vstPlugin -> remoteVstPlugin
	IdVstLoadPlugin = IdUserBase,
	IdVstClosePlugin,
	IdVstSetTempo,
	IdVstSetLanguage,
	IdVstGetParameterCount,
	IdVstGetParameterDump,
	IdVstSetParameterDump,
	IdVstProgramNames,
	IdVstCurrentProgram,
	IdVstCurrentProgramName,
	IdVstSetProgram,
	IdVstRotateProgram,
	IdVstIdleUpdate,
	IdVstParameterDisplays,
	IdVstParameterLabels,

	// remoteVstPlugin -> vstPlugin
	IdVstFailedLoadingPlugin,
	IdVstPluginWindowID,
	IdVstPluginEditorGeometry,
	IdVstPluginName,
	IdVstPluginVersion,
	IdVstPluginVendorString,
	IdVstPluginProductString,
	IdVstPluginPresetsString,
	IdVstPluginUniqueID,
	IdVstSetParameter,
	IdVstParameterCount,
	IdVstParameterDump

} ;

#endif


code:
/*
 * vst_base.cpp - VST-base-code to be used by any LMMS plugins dealing with VST-
 *                plugins
 *
 * Copyright (c) 2006-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "Plugin.h"
#include "embed.h"
#include "vstbase_export.h"

extern "C"
{

Plugin::Descriptor VSTBASE_EXPORT vstbase_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"VST Base",
	"library for all LMMS plugins dealing with VST-plugins",
	"Tobias Doerffel <tobydox/at/users/dot/sf/dot/net>",
	0x0100,
	Plugin::Library,
	NULL,
	NULL
} ;

}


code:
ADD_DEFINITIONS(-DREMOTE_VST_PLUGIN_FILEPATH_32="${REMOTE_VST_PLUGIN_FILEPATH_32}")
ADD_DEFINITIONS(-DREMOTE_VST_PLUGIN_FILEPATH_64="${REMOTE_VST_PLUGIN_FILEPATH_64}")

BUILD_PLUGIN(vstbase
	../vst_base.cpp ../VstPlugin.cpp ../VstPlugin.h ../communication.h
	MOCFILES ../VstPlugin.h
	EXPORT_BASE_NAME vstbase
	LINK SHARED
)

TARGET_INCLUDE_DIRECTORIES(vstbase
	PUBLIC ../
)

IF(LMMS_BUILD_LINUX)
	TARGET_LINK_LIBRARIES(vstbase qx11embedcontainer)
ENDIF()


code:
INCLUDE(BuildPlugin)

LINK_DIRECTORIES(${SAMPLERATE_LIBRARY_DIRS})
LINK_LIBRARIES(${SAMPLERATE_LIBRARIES})
BUILD_PLUGIN(watsyn Watsyn.cpp Watsyn.h MOCFILES Watsyn.h EMBEDDED_RESOURCES *.png)