Files
lmms/plugins/vst_base/RemoteVstPlugin.cpp
DomClark 2c5cda563b Fix kVstTransportChanged flag usage in VST sync
Changed according to feedback from AudioBlast. The flag used to be set most of the time, now it is only set when playback starts/stops, looping is toggled, or playback jumps around.
2018-09-11 22:01:11 +09:00

2184 lines
48 KiB
C++

/*
* 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_PTHREAD_H
#include <pthread.h>
#endif
#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>
#include <vector>
#include <queue>
#include <string>
#include <iostream>
#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;
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();
// 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()
{
pthread_mutex_lock( &m_shmLock );
}
inline bool tryLockShm()
{
return pthread_mutex_trylock( &m_shmLock ) == 0;
}
inline void unlockShm()
{
pthread_mutex_unlock( &m_shmLock );
}
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 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;
pthread_mutex_t 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
{
float lastppqPos;
float 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_shmLock(),
m_shmValid( false ),
m_midiEvents(),
m_bpm( 0 ),
m_currentSamplePos( 0 ),
m_currentProgram( -1 ),
m_in( NULL ),
m_shmID( -1 ),
m_vstSyncData( NULL )
{
pthread_mutex_init( &m_shmLock, 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;
pthread_mutex_destroy( &m_shmLock );
}
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 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( fclose( fp ) )
{
perror( "close" );
}
}
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 );
if (! EMBED) {
showEditor();
}
#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 )
{
// give VstPlugin class a chance to start 32 bit version of RemoteVstPlugin
if( GetLastError() == ERROR_BAD_EXE_FORMAT )
{
sendMessage( IdVstBadDllFormat );
}
return false;
}
typedef AEffect * ( __stdcall * 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];
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;
}
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[32];
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 ( 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, "w" );
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, "r" );
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 ( 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 )
{
__plugin->m_in->lastppqPos += (
__plugin->m_vstSyncData->hasSHM ?
__plugin->m_vstSyncData->m_bpm :
__plugin->m_bpm ) / (float)10340;
_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;
#ifdef LMMS_BUILD_WIN64
return (long long) &_timeInfo;
#else
return (long) &_timeInfo;
#endif
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;
SetWindowPos( __plugin->m_window, 0, 0, 0,
_index + 8, _value + 26,
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 )
{
_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;
}
#ifdef LMMS_BUILD_WIN32
#ifndef __WINPTHREADS_VERSION
// (non-portable) initialization of statically linked pthread library
pthread_win32_process_attach_np();
pthread_win32_thread_attach_np();
#endif
#endif
#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;
#ifdef LMMS_BUILD_WIN32
#ifndef __WINPTHREADS_VERSION
pthread_win32_thread_detach_np();
pthread_win32_process_detach_np();
#endif
#endif
return 0;
}