diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2011-09-20 20:29:47 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2011-09-20 20:29:47 +0000 |
commit | d4433b9ab384196bb5b8876890863d7939339ee2 (patch) | |
tree | 669f503a04696c7869a14d5c5f6239024ba43e76 /libs | |
parent | e09e0035a62ce9f8876b6204682bbb3db474d477 (diff) |
(native) Linux VST support from LinuxDSP
git-svn-id: svn://localhost/ardour2/branches/3.0@10101 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
28 files changed, 3911 insertions, 67 deletions
diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h index 68daadd674..05ca38e43b 100644 --- a/libs/ardour/ardour/audioengine.h +++ b/libs/ardour/ardour/audioengine.h @@ -189,8 +189,6 @@ public: void get_physical_outputs (DataType type, std::vector<std::string>&); void get_physical_inputs (DataType type, std::vector<std::string>&); - void update_total_latencies (); - Port *get_port_by_name (const std::string &); enum TransportState { diff --git a/libs/ardour/ardour/buffer_set.h b/libs/ardour/ardour/buffer_set.h index 724eef6a24..930d708989 100644 --- a/libs/ardour/ardour/buffer_set.h +++ b/libs/ardour/ardour/buffer_set.h @@ -29,7 +29,7 @@ #include "ardour/data_type.h" #include "ardour/types.h" -#ifdef VST_SUPPORT +#if defined VST_SUPPORT || defined LXVST_SUPPORT #include "evoral/MIDIEvent.hpp" struct VstEvents; struct VstMidiEvent; @@ -118,7 +118,7 @@ public: void flush_lv2_midi(bool input, size_t i); #endif -#ifdef VST_SUPPORT +#if defined VST_SUPPORT || defined LXVST_SUPPORT VstEvents* get_vst_midi (size_t); #endif @@ -176,7 +176,7 @@ private: LV2Buffers _lv2_buffers; #endif -#ifdef VST_SUPPORT +#if defined VST_SUPPORT || defined LXVST_SUPPORT class VSTBuffer { public: VSTBuffer (size_t); diff --git a/libs/ardour/ardour/jack_port.h b/libs/ardour/ardour/jack_port.h index 0bb0752d55..edbabaae04 100644 --- a/libs/ardour/ardour/jack_port.h +++ b/libs/ardour/ardour/jack_port.h @@ -104,7 +104,6 @@ class JackPort : public virtual Port, public PortConnectableByName { jack_port_t* _port; int disconnect (); - void recompute_total_latency() const; std::set<std::string> _named_connections; }; diff --git a/libs/ardour/ardour/lxvst_plugin.h b/libs/ardour/ardour/lxvst_plugin.h new file mode 100755 index 0000000000..092a786f95 --- /dev/null +++ b/libs/ardour/ardour/lxvst_plugin.h @@ -0,0 +1,123 @@ +/* + Copyright (C) 2004 Paul Davis + + 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; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_lxvst_plugin_h__ +#define __ardour_lxvst_plugin_h__ + +#include <list> +#include <map> +#include <set> +#include <vector> +#include <string> +#include <dlfcn.h> + +#include "pbd/stateful.h" +#include "ardour/plugin.h" + +struct _VSTFXHandle; +struct _VSTFX; +typedef struct _VSTFXHandle VSTFXHandle; +typedef struct _VSTFX VSTFX; +class AEffect; + +namespace ARDOUR { +class AudioEngine; +class Session; + +class LXVSTPlugin : public ARDOUR::Plugin +{ + public: + LXVSTPlugin (ARDOUR::AudioEngine&, ARDOUR::Session&, VSTFXHandle* handle); + LXVSTPlugin (const LXVSTPlugin &); + ~LXVSTPlugin (); + + /* Plugin interface */ + + std::string unique_id() const; + const char * label() const; + const char * name() const; + const char * maker() const; + uint32_t parameter_count() const; + float default_value (uint32_t port); + framecnt_t signal_latency() const; + void set_parameter (uint32_t port, float val); + float get_parameter (uint32_t port) const; + int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const; + std::set<Evoral::Parameter> automatable() const; + uint32_t nth_parameter (uint32_t port, bool& ok) const; + void activate (); + void deactivate (); + int set_block_size (pframes_t); + + int connect_and_run (BufferSet&, + ChanMapping in, ChanMapping out, + pframes_t nframes, framecnt_t offset); + + std::string describe_parameter (Evoral::Parameter); + std::string state_node_name() const { return "lxvst"; } + void print_parameter (uint32_t, char*, uint32_t len) const; + + bool parameter_is_audio(uint32_t i) const { return false; } + bool parameter_is_control(uint32_t i) const { return true; } + bool parameter_is_input(uint32_t i) const { return true; } + bool parameter_is_output(uint32_t i) const { return false; } + + bool load_preset (PresetRecord); + int first_user_preset_index () const; + + bool has_editor () const; + + int set_state (XMLNode const &, int); + + AEffect * plugin () const { return _plugin; } + VSTFX * vstfx () const { return _vstfx; } + +private: + + void do_remove_preset (std::string name); + std::string do_save_preset (std::string name); + gchar* get_chunk (bool) const; + int set_chunk (gchar const *, bool); + XMLTree * presets_tree () const; + std::string presets_file () const; + void find_presets (); + bool load_user_preset (PresetRecord); + bool load_plugin_preset (PresetRecord); + void add_state (XMLNode *) const; + + VSTFXHandle* handle; + VSTFX* _vstfx; + AEffect* _plugin; + bool been_resumed; +}; + +class LXVSTPluginInfo : public PluginInfo +{ + public: + LXVSTPluginInfo (); + ~LXVSTPluginInfo () {} + + PluginPtr load (Session& session); +}; + +typedef boost::shared_ptr<LXVSTPluginInfo> LXVSTPluginInfoPtr; + +} // namespace ARDOUR + +#endif /* __ardour_lxvst_plugin_h__ */ diff --git a/libs/ardour/ardour/plugin_manager.h b/libs/ardour/ardour/plugin_manager.h index 9ba3d8324e..ae534837ce 100644 --- a/libs/ardour/ardour/plugin_manager.h +++ b/libs/ardour/ardour/plugin_manager.h @@ -43,6 +43,7 @@ class PluginManager : public boost::noncopyable { ~PluginManager (); ARDOUR::PluginInfoList &vst_plugin_info (); + ARDOUR::PluginInfoList &lxvst_plugin_info (); ARDOUR::PluginInfoList &ladspa_plugin_info (); ARDOUR::PluginInfoList &lv2_plugin_info (); ARDOUR::PluginInfoList &au_plugin_info (); @@ -51,6 +52,7 @@ class PluginManager : public boost::noncopyable { int add_ladspa_directory (std::string dirpath); int add_vst_directory (std::string dirpath); + int add_lxvst_directory (std::string dirpath); static PluginManager* the_manager() { return _manager; } @@ -95,6 +97,7 @@ class PluginManager : public boost::noncopyable { ARDOUR::PluginInfoList _empty_plugin_info; ARDOUR::PluginInfoList* _vst_plugin_info; + ARDOUR::PluginInfoList* _lxvst_plugin_info; ARDOUR::PluginInfoList* _ladspa_plugin_info; ARDOUR::PluginInfoList* _lv2_plugin_info; ARDOUR::PluginInfoList* _au_plugin_info; @@ -103,13 +106,16 @@ class PluginManager : public boost::noncopyable { std::string ladspa_path; std::string vst_path; + std::string lxvst_path; void ladspa_refresh (); void vst_refresh (); + void lxvst_refresh (); void add_lrdf_data (const std::string &path); void add_ladspa_presets (); void add_vst_presets (); + void add_lxvst_presets (); void add_presets (std::string domain); void au_refresh (); @@ -118,6 +124,9 @@ class PluginManager : public boost::noncopyable { int vst_discover_from_path (std::string path); int vst_discover (std::string path); + + int lxvst_discover_from_path (std::string path); + int lxvst_discover (std::string path); int ladspa_discover_from_path (std::string path); int ladspa_discover (std::string path); diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h index 5693bac890..0ec31e3e1b 100644 --- a/libs/ardour/ardour/rc_configuration_vars.h +++ b/libs/ardour/ardour/rc_configuration_vars.h @@ -140,6 +140,7 @@ CONFIG_VARIABLE (bool, hiding_groups_deactivates_groups, "hiding-groups-deactiva CONFIG_VARIABLE (bool, verify_remove_last_capture, "verify-remove-last-capture", true) CONFIG_VARIABLE (bool, no_new_session_dialog, "no-new-session-dialog", false) CONFIG_VARIABLE (bool, use_vst, "use-vst", true) +CONFIG_VARIABLE (bool, use_lxvst, "use-lxvst", true) CONFIG_VARIABLE (bool, save_history, "save-history", true) CONFIG_VARIABLE (int32_t, saved_history_depth, "save-history-depth", 20) CONFIG_VARIABLE (int32_t, history_depth, "history-depth", 20) diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 68cf66bd25..2dbb2f08ac 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -741,6 +741,15 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi long value, void* ptr, float opt); + + /*Native linuxVST support*/ + + static long lxvst_callback (AEffect* effect, + long opcode, + long index, + long value, + void* ptr, + float opt); static PBD::Signal0<void> SendFeedback; diff --git a/libs/ardour/ardour/sndfile_helpers.h b/libs/ardour/ardour/sndfile_helpers.h index ed84820b8d..02f486a975 100644 --- a/libs/ardour/ardour/sndfile_helpers.h +++ b/libs/ardour/ardour/sndfile_helpers.h @@ -42,10 +42,9 @@ extern const char * const sndfile_endian_formats_strings[SNDFILE_ENDIAN_FORMATS+ extern int sndfile_endian_formats[SNDFILE_ENDIAN_FORMATS]; -int sndfile_bitdepth_format_from_string (std::string); -int sndfile_header_format_from_string (std::string); -int sndfile_endian_format_from_string (std::string); -std::string sndfile_file_ending_from_string (std::string); +int sndfile_bitdepth_format_by_index (int); +int sndfile_header_format_by_index (int); +int sndfile_endian_format_by_index (int); int sndfile_data_width (int format); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index e21c588c62..9579c87b7b 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -449,7 +449,8 @@ namespace ARDOUR { AudioUnit, LADSPA, LV2, - VST + VST, + LXVST, }; enum RunContext { diff --git a/libs/ardour/ardour/vestige/aeffectx.h b/libs/ardour/ardour/vestige/aeffectx.h new file mode 100755 index 0000000000..dda128f45e --- /dev/null +++ b/libs/ardour/ardour/vestige/aeffectx.h @@ -0,0 +1,316 @@ +/* + * aeffectx.h - simple header to allow VeSTige compilation and eventually work + * + * Copyright (c) 2006 Javier Serrano Polo <jasp00/at/users.sourceforge.net> + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef _AEFFECTX_H +#define _AEFFECTX_H + +#include <stdint.h> + +#define audioMasterAutomate 0 +#define audioMasterVersion 1 +#define audioMasterCurrentId 2 +#define audioMasterIdle 3 +#define audioMasterPinConnected 4 +// unsupported? 5 +#define audioMasterWantMidi 6 +#define audioMasterGetTime 7 +#define audioMasterProcessEvents 8 +#define audioMasterSetTime 9 +#define audioMasterTempoAt 10 +#define audioMasterGetNumAutomatableParameters 11 +#define audioMasterGetParameterQuantization 12 +#define audioMasterIOChanged 13 +#define audioMasterNeedIdle 14 +#define audioMasterSizeWindow 15 +#define audioMasterGetSampleRate 16 +#define audioMasterGetBlockSize 17 +#define audioMasterGetInputLatency 18 +#define audioMasterGetOutputLatency 19 +#define audioMasterGetPreviousPlug 20 +#define audioMasterGetNextPlug 21 +#define audioMasterWillReplaceOrAccumulate 22 +#define audioMasterGetCurrentProcessLevel 23 +#define audioMasterGetAutomationState 24 +#define audioMasterOfflineStart 25 +#define audioMasterOfflineRead 26 +#define audioMasterOfflineWrite 27 +#define audioMasterOfflineGetCurrentPass 28 +#define audioMasterOfflineGetCurrentMetaPass 29 +#define audioMasterSetOutputSampleRate 30 +// unsupported? 31 +#define audioMasterGetSpeakerArrangement 31 // deprecated in 2.4? +#define audioMasterGetVendorString 32 +#define audioMasterGetProductString 33 +#define audioMasterGetVendorVersion 34 +#define audioMasterVendorSpecific 35 +#define audioMasterSetIcon 36 +#define audioMasterCanDo 37 +#define audioMasterGetLanguage 38 +#define audioMasterOpenWindow 39 +#define audioMasterCloseWindow 40 +#define audioMasterGetDirectory 41 +#define audioMasterUpdateDisplay 42 +#define audioMasterBeginEdit 43 +#define audioMasterEndEdit 44 +#define audioMasterOpenFileSelector 45 +#define audioMasterCloseFileSelector 46// currently unused +#define audioMasterEditFile 47// currently unused +#define audioMasterGetChunkFile 48// currently unused +#define audioMasterGetInputSpeakerArrangement 49 // currently unused + +#define effFlagsHasEditor 1 +// very likely +#define effFlagsCanReplacing (1 << 4) +// currently unused +#define effFlagsIsSynth (1 << 8) + +#define effOpen 0 +//currently unused +#define effClose 1 +// currently unused +#define effSetProgram 2 +// currently unused +#define effGetProgram 3 +// currently unused +#define effGetProgramName 5 +#define effGetParamLabel 6 +// currently unused +#define effGetParamName 8 +// this is a guess +#define effSetSampleRate 10 +#define effSetBlockSize 11 +#define effMainsChanged 12 +#define effEditGetRect 13 +#define effEditOpen 14 +#define effEditClose 15 +#define effEditIdle 19 +#define effProcessEvents 25 +#define effGetEffectName 45 +// missing +#define effGetParameterProperties 47 +#define effGetVendorString 47 +#define effGetProductString 48 +#define effGetVendorVersion 49 +// currently unused +#define effCanDo 51 +// currently unused +#define effGetVstVersion 58 + +#ifdef WORDS_BIGENDIAN +// "VstP" +#define kEffectMagic 0x50747356 +#else +// "PtsV" +#define kEffectMagic 0x56737450 +#endif + +#define kVstLangEnglish 1 +#define kVstMidiType 1 +#define kVstTransportPlaying (1 << 1) + +/* validity flags for a VstTimeINfo structure this info comes from the web */ + +#define kVstNanosValid (1 << 8) +#define kVstPpqPosValid (1 << 9) +#define kVstTempoValid (1 << 10) +#define kVstBarsValid (1 << 11) +#define kVstCyclePosValid (1 << 12) +#define kVstTimeSigValid (1 << 13) +#define kVstSmpteValid (1 << 14) +#define kVstClockValid (1 << 15) + +#define kVstTransportChanged 1 + +typedef struct VstMidiEvent +{ + // 00 + int type; + // 04 + int byteSize; + // 08 + int deltaFrames; + // 0c? + int flags; + // 10? + int noteLength; + // 14? + int noteOffset; + // 18 + char midiData[4]; + // 1c? + char detune; + // 1d? + char noteOffVelocity; + // 1e? + char reserved1; + // 1f? + char reserved2; + +} VstMidiEvent; + + + + +typedef struct VstEvent +{ + char dump[sizeof( VstMidiEvent )]; + +} VstEvent ; + + + + +typedef struct VstEvents +{ + // 00 + int numEvents; + // 04 + int reserved; + // 08 + VstEvent * events[]; +} VstEvents; + +/* constants from http://www.rawmaterialsoftware.com/juceforum/viewtopic.php?t=3740&sid=183f74631fee71a493316735e2b9f28b */ + +enum Vestige2StringConstants +{ + VestigeMaxNameLen = 64, + VestigeMaxLabelLen = 64, + VestigeMaxShortLabelLen = 8, + VestigeMaxCategLabelLen = 24, + VestigeMaxFileNameLen = 100 +}; + +/* this struct taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ +struct VstParameterProperties +{ + float stepFloat; /* float step */ + float smallStepFloat; /* small float step */ + float largeStepFloat; /* large float step */ + char label[VestigeMaxLabelLen]; /* parameter label */ + int32_t flags; /* @see VstParameterFlags */ + int32_t minInteger; /* integer minimum */ + int32_t maxInteger; /* integer maximum */ + int32_t stepInteger; /* integer step */ + int32_t largeStepInteger; /* large integer step */ + char shortLabel[VestigeMaxShortLabelLen]; /* short label, recommended: 6 + delimiter */ + int16_t displayIndex; /* index where this parameter should be displayed (starting with 0) */ + int16_t category; /* 0: no category, else group index + 1 */ + int16_t numParametersInCategory; /* number of parameters in category */ + int16_t reserved; /* zero */ + char categoryLabel[VestigeMaxCategLabelLen]; /* category label, e.g. "Osc 1" */ + char future[16]; /* reserved for future use */ +}; + +/* this enum taken from http://asseca.com/vst-24-specs/efGetParameterProperties.html */ +enum VstParameterFlags +{ + kVstParameterIsSwitch = 1 << 0, /* parameter is a switch (on/off) */ + kVstParameterUsesIntegerMinMax = 1 << 1, /* minInteger, maxInteger valid */ + kVstParameterUsesFloatStep = 1 << 2, /* stepFloat, smallStepFloat, largeStepFloat valid */ + kVstParameterUsesIntStep = 1 << 3, /* stepInteger, largeStepInteger valid */ + kVstParameterSupportsDisplayIndex = 1 << 4, /* displayIndex valid */ + kVstParameterSupportsDisplayCategory = 1 << 5, /* category, etc. valid */ + kVstParameterCanRamp = 1 << 6 /* set if parameter value can ramp up/down */ +}; + +typedef struct AEffect +{ + // Never use c++!!! + // 00-03 + int magic; + // dispatcher 04-07 + int (* dispatcher)( struct AEffect * , int , int , int , void * , float ); + // process, quite sure 08-0b + void (* process)( struct AEffect * , float * * , float * * , int ); + // setParameter 0c-0f + void (* setParameter)( struct AEffect * , int , float ); + // getParameter 10-13 + float (* getParameter)( struct AEffect * , int ); + // programs 14-17 + int numPrograms; + // Params 18-1b + int numParams; + // Input 1c-1f + int numInputs; + // Output 20-23 + int numOutputs; + // flags 24-27 + int flags; + // Fill somewhere 28-2b + void * user; + // Zeroes 2c-2f 30-33 34-37 38-3b + char empty3[4 + 4 + 4 + 4]; + // 1.0f 3c-3f + float unkown_float; + // An object? pointer 40-43 + char empty4[4]; + // Zeroes 44-47 + char empty5[4]; + // Id 48-4b + char unused_id[4]; + // Don't know 4c-4f + char unknown1[4]; + // processReplacing 50-53 + void (* processReplacing)( struct AEffect * , float * * , float * * , int ); + + int uniqueID; + +} AEffect; + + + + +typedef struct VstTimeInfo +{ + /* info from online documentation of VST provided by Steinberg */ + + double samplePos; + double sampleRate; + double nanoSeconds; + double ppqPos; + double tempo; + double barStartPos; + double cycleStartPos; + double cycleEndPos; + double timeSigNumerator; + double timeSigDenominator; + long smpteOffset; + long smpteFrameRate; + long samplesToNextClock; + long flags; + +} VstTimeInfo; + + +typedef long int (* audioMasterCallback)( AEffect * , long int , long int , + long int , void * , float ); +// we don't use it, may be noise +#define VSTCALLBACK + + + + +#endif diff --git a/libs/ardour/ardour/vstfx.h b/libs/ardour/ardour/vstfx.h new file mode 100755 index 0000000000..e59998dd99 --- /dev/null +++ b/libs/ardour/ardour/vstfx.h @@ -0,0 +1,176 @@ +#ifndef __vstfx_h__ +#define __vstfx_h__ + +#include <setjmp.h> +#include <signal.h> +#include <pthread.h> +#include <stdio.h> + +/******************************************************************************************/ +/*VSTFX - an engine to manage native linux VST plugins - derived from FST for Windows VSTs*/ +/******************************************************************************************/ + +extern void (*vstfx_error_callback)(const char *msg); + +void vstfx_set_error_function (void (*func)(const char *)); + +void vstfx_error (const char *fmt, ...); + +/*We will use the vestige headers*/ + +#define VESTIGE_HEADER + +#include <ardour/vestige/aeffectx.h> + +typedef struct _VSTFX VSTFX; +typedef struct _VSTFXHandle VSTFXHandle; +typedef struct _VSTFXInfo VSTFXInfo; +typedef struct _VSTFXKey VSTFXKey; + + +/*Struct to contain the info about a plugin*/ + +struct _VSTFXInfo +{ + char *name; + char *creator; + int UniqueID; + char *Category; + + int numInputs; + int numOutputs; + int numParams; + + int wantMidi; + int wantEvents; + int hasEditor; + int canProcessReplacing; + + /* i think we should save the parameter Info Stuff soon. */ + // struct VstParameterInfo *infos; + char **ParamNames; + char **ParamLabels; +}; + +/*The AEffect which contains the info about a plugin instance*/ + +typedef struct AEffect * (*main_entry_t)(audioMasterCallback); + +/*A handle used to identify a plugin to vstfx*/ + +struct _VSTFXHandle +{ + void* dll; + char* name; + char* nameptr; /* ptr returned from strdup() etc. */ + + //struct AEffect* (*main_entry)(audioMasterCallback); + + main_entry_t main_entry; + + int plugincnt; +}; + +/*No key forwarding enabled in vstfx at the moment - maybe +not required*/ + + +struct _VSTFXKey +{ + /** virtual-key code, or 0 if this _VSTFXKey is a `character' key */ + int special; + /** `character' key, or 0 if this _VSTFXKey is a virtual-key */ + int character; +}; + + + +/*Structure used to describe the instance of VSTFX responsible for +a particular plugin instance. These are connected together in a +linked list*/ + +struct _VSTFX +{ + struct AEffect* plugin; + int window; /* The plugin's parent X11 XWindow */ + int plugin_ui_window; /*The ID of the plugin UI window created by the plugin*/ + int xid; /* X11 XWindow */ + + int want_resize; /*Set to signal the plugin resized its UI*/ + void* extra_data; /*Pointer to any extra data*/ + + void* event_callback_thisptr; + void (*eventProc) (void* event); + + VSTFXHandle* handle; + + int width; + int height; + int wantIdle; + int destroy; + int vst_version; + int has_editor; + + int program_set_without_editor; + + int want_program; + int want_chunk; + int n_pending_keys; +unsigned char* wanted_chunk; + int wanted_chunk_size; + int current_program; + float *want_params; + float *set_params; + + VSTFXKey pending_keys[16]; + + int dispatcher_wantcall; + int dispatcher_opcode; + int dispatcher_index; + int dispatcher_val; + void * dispatcher_ptr; + float dispatcher_opt; + int dispatcher_retval; + + struct _VSTFX* next; + pthread_mutex_t lock; + pthread_cond_t window_status_change; + pthread_cond_t plugin_dispatcher_called; + pthread_cond_t window_created; + int been_activated; +}; + +/*API to vstfx*/ + +extern int vstfx_launch_editor(VSTFX* vstfx); + +extern int vstfx_init (void* possible_hmodule); +extern void vstfx_exit (); + +extern VSTFXHandle* vstfx_load (const char*); +extern int vstfx_unload (VSTFXHandle*); + +extern VSTFX* vstfx_instantiate (VSTFXHandle*, audioMasterCallback amc, void* userptr); +extern void vstfx_close (VSTFX*); + +extern int vstfx_create_editor (VSTFX* vstfx); +extern int vstfx_run_editor (VSTFX*); +extern void vstfx_destroy_editor (VSTFX*); +extern int vstfx_get_XID (VSTFX*); +extern void vstfx_move_window_into_view (VSTFX*); + +extern VSTFXInfo* vstfx_get_info (char *dllpathname); +extern void vstfx_free_info (VSTFXInfo *info); +extern void vstfx_event_loop_remove_plugin (VSTFX* fst); +extern int vstfx_call_dispatcher(VSTFX *vstfx, int opcode, int index, int val, void *ptr, float opt ); + +/** Load a plugin state from a file.**/ + +extern int vstfx_load_state (VSTFX* vstfx, char * filename); + +/** Save a plugin state to a file.**/ + +extern int vstfx_save_state (VSTFX* vstfx, char * filename); + + +#endif /* __vstfx_h__ */ diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc index 81fb700d3c..f64a325138 100644 --- a/libs/ardour/audio_diskstream.cc +++ b/libs/ardour/audio_diskstream.cc @@ -490,7 +490,20 @@ AudioDiskstream::process (framepos_t transport_frame, pframes_t nframes, bool ca AudioPort* const ap = _io->audio (n); assert(ap); assert(rec_nframes <= (framecnt_t) ap->get_audio_buffer(nframes).capacity()); - memcpy (chaninfo->current_capture_buffer, ap->get_audio_buffer (nframes).data(rec_offset), sizeof (Sample) * rec_nframes); + + Sample *bbuf = ap->get_audio_buffer (nframes).data(rec_offset); + + + for (jack_nframes_t _xx = 0; _xx != rec_nframes; ++_xx) { + if (bbuf[_xx] != 0.0) { + cerr << name() << " @ " << transport_frame << " + " << _xx << " = " << bbuf[_xx] + << endl; + break; + } + } + + + memcpy (chaninfo->current_capture_buffer, bbuf, sizeof (Sample) * rec_nframes); } else { diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index 89f3e94bfb..ae71817679 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -1414,13 +1414,6 @@ AudioEngine::request_buffer_size (pframes_t nframes) return jack_set_buffer_size (_priv_jack, nframes); } -void -AudioEngine::update_total_latencies () -{ - GET_PRIVATE_JACK_POINTER (_jack); - jack_recompute_total_latencies (_priv_jack); -} - string AudioEngine::make_port_name_relative (string portname) const { diff --git a/libs/ardour/buffer_set.cc b/libs/ardour/buffer_set.cc index 56245882d6..6061d360f9 100644 --- a/libs/ardour/buffer_set.cc +++ b/libs/ardour/buffer_set.cc @@ -41,6 +41,10 @@ #include "vestige/aeffectx.h" #endif +#ifdef LXVST_SUPPORT +#include "ardour/vestige/aeffectx.h" +#endif + namespace ARDOUR { /** Create a new, empty BufferSet */ @@ -77,13 +81,14 @@ BufferSet::clear() _count.reset(); _available.reset(); -#ifdef VST_SUPPORT +#if defined VST_SUPPORT || defined LXVST_SUPPORT for (VSTBuffers::iterator i = _vst_buffers.begin(); i != _vst_buffers.end(); ++i) { delete *i; } _vst_buffers.clear (); #endif + } /** Set up this BufferSet so that its data structures mirror a PortSet's buffers. @@ -192,7 +197,7 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac } #endif -#ifdef VST_SUPPORT +#if defined VST_SUPPORT || defined LXVST_SUPPORT // As above but for VST if (type == DataType::MIDI) { while (_vst_buffers.size() < _buffers[type].size()) { @@ -299,7 +304,7 @@ BufferSet::flush_lv2_midi(bool input, size_t i) #endif /* LV2_SUPPORT */ -#ifdef VST_SUPPORT +#if defined VST_SUPPORT || defined LXVST_SUPPORT VstEvents* BufferSet::get_vst_midi (size_t b) diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index d8f06d9a8a..48a7dc0da0 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -33,6 +33,10 @@ #include <fst.h> #endif +#ifdef LXVST_SUPPORT +#include "ardour/vstfx.h" +#endif + #ifdef HAVE_AUDIOUNITS #include "ardour/audio_unit.h" #endif @@ -290,6 +294,9 @@ ARDOUR::init (bool use_vst, bool try_optimization) } Config->set_use_vst (use_vst); +#ifdef LXVST_SUPPORT + Config->set_use_lxvst(true); +#endif Profile = new RuntimeProfile; @@ -300,6 +307,12 @@ ARDOUR::init (bool use_vst, bool try_optimization) } #endif +#ifdef LXVST_SUPPORT + if (Config->get_use_lxvst() && vstfx_init (0)) { + return -1; + } +#endif + #ifdef HAVE_AUDIOUNITS AUPluginInfo::load_cached_info (); #endif @@ -372,6 +385,10 @@ ARDOUR::cleanup () #ifdef VST_SUPPORT fst_exit (); #endif + +#ifdef LXVST_SUPPOR + vstfx_exit(); +#endif return 0; } diff --git a/libs/ardour/lxvst_plugin.cc b/libs/ardour/lxvst_plugin.cc new file mode 100755 index 0000000000..cd352dc49e --- /dev/null +++ b/libs/ardour/lxvst_plugin.cc @@ -0,0 +1,830 @@ +/* + Copyright (C) 2004 Paul Davis + + 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; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/**********************************************************************/ +/*Native linuxVST (LXVST) variant of vst_plugin.cc etc */ +/**********************************************************************/ + + +#include <algorithm> +#include <vector> +#include <string> +#include <cctype> + +#include <cstdlib> +#include <cstdio> // so libraptor doesn't complain +#include <cmath> +#include <dirent.h> +#include <cstring> // for memmove +#include <sys/stat.h> +#include <cerrno> + +#include <glibmm/miscutils.h> + +#include <lrdf.h> + +/*Include for the new native vst engine - vstfx.h*/ + +#include <ardour/vstfx.h> + +#include "pbd/compose.h" +#include "pbd/error.h" +#include "pbd/pathscanner.h" +#include "pbd/xml++.h" + +#include "ardour/session.h" +#include "ardour/audioengine.h" +#include "ardour/filesystem_paths.h" +#include "ardour/lxvst_plugin.h" +#include "ardour/buffer_set.h" +#include "ardour/audio_buffer.h" +#include "ardour/midi_buffer.h" + +#include "pbd/stl_delete.h" + +#include "i18n.h" +#include <locale.h> + +using namespace std; +using namespace ARDOUR; +using namespace PBD; +using std::min; +using std::max; + +LXVSTPlugin::LXVSTPlugin (AudioEngine& e, Session& session, VSTFXHandle* h) + : Plugin (e, session) +{ + handle = h; + + /*Instantiate the plugin and return a VSTFX* */ + + if ((_vstfx = vstfx_instantiate (handle, Session::lxvst_callback, this)) == 0) + { + throw failed_constructor(); + } + + /*Call into vstfx to get a pointer to the instance of the VST plugin*/ + + _plugin = _vstfx->plugin; + _plugin->user = this; + + /* set rate and blocksize */ + + _plugin->dispatcher (_plugin, effSetSampleRate, 0, 0, NULL, (float) session.frame_rate()); + + _plugin->dispatcher (_plugin, effSetBlockSize, 0, session.get_block_size(), NULL, 0.0f); + + /* set program to zero */ + + _plugin->dispatcher (_plugin, effSetProgram, 0, 0, NULL, 0.0f); + + // Plugin::setup_controls (); +} + +LXVSTPlugin::LXVSTPlugin (const LXVSTPlugin &other) + : Plugin (other) +{ + handle = other.handle; + + if ((_vstfx = vstfx_instantiate (handle, Session::lxvst_callback, this)) == 0) + { + throw failed_constructor(); + } + _plugin = _vstfx->plugin; + + // Plugin::setup_controls (); +} + +LXVSTPlugin::~LXVSTPlugin () +{ + deactivate (); + vstfx_close (_vstfx); +} + +int LXVSTPlugin::set_block_size (pframes_t nframes) +{ + deactivate (); + _plugin->dispatcher (_plugin, effSetBlockSize, 0, nframes, NULL, 0.0f); + activate (); + return 0; +} + +float LXVSTPlugin::default_value (uint32_t port) +{ + return 0; +} + +void LXVSTPlugin::set_parameter (uint32_t which, float val) +{ + _plugin->setParameter (_plugin, which, val); + + if (_vstfx->want_program == -1 && _vstfx->want_chunk == 0) + { + /* Heinous hack: Plugin::set_parameter below updates the `modified' status of the + current preset, but if _vstfx->want_program is not -1 then there is a preset + setup pending or in progress, which we don't want any `modified' updates + to happen for. So we only do this if _vstfx->want_program is -1. + */ + Plugin::set_parameter (which, val); + } +} + +float LXVSTPlugin::get_parameter (uint32_t which) const +{ + return _plugin->getParameter (_plugin, which); + +} + +uint32_t LXVSTPlugin::nth_parameter (uint32_t n, bool& ok) const +{ + ok = true; + return n; +} + +/** Get VST chunk as base64-encoded data. + * @param single true for single program, false for all programs. + * @return 0-terminated base64-encoded data; must be passed to g_free () by caller. + */ +gchar *LXVSTPlugin::get_chunk (bool single) const +{ + guchar* data; + int32_t data_size = _plugin->dispatcher (_plugin, 23 /* effGetChunk */, single ? 1 : 0, 0, &data, 0); + if (data_size == 0) + { + return 0; + } + + return g_base64_encode (data, data_size); +} + +/** Set VST chunk from base64-encoded data. + * @param 0-terminated base64-encoded data. + * @param single true for single program, false for all programs. + * @return 0 on success, non-0 on failure + */ +int LXVSTPlugin::set_chunk (gchar const * data, bool single) +{ + gsize size = 0; + guchar* raw_data = g_base64_decode (data, &size); + int const r = _plugin->dispatcher (_plugin, 24 /* effSetChunk */, single ? 1 : 0, size, raw_data, 0); + g_free (raw_data); + return r; +} + +void LXVSTPlugin::add_state (XMLNode* root) const +{ + LocaleGuard lg (X_("POSIX")); + + if (_vstfx->current_program != -1) + { + char buf[32]; + snprintf (buf, sizeof (buf), "%d", _vstfx->current_program); + root->add_property ("current-program", buf); + } + + if (_plugin->flags & 32 /* effFlagsProgramsChunks */) + { + gchar* data = get_chunk (false); + if (data == 0) + { + return; + } + + /* store information */ + + XMLNode* chunk_node = new XMLNode (X_("chunk")); + + chunk_node->add_content (data); + g_free (data); + + root->add_child_nocopy (*chunk_node); + + } + else + { + XMLNode* parameters = new XMLNode ("parameters"); + + for (int32_t n = 0; n < _plugin->numParams; ++n) + { + char index[64]; + char val[32]; + snprintf (index, sizeof (index), "param_%d", n); + snprintf (val, sizeof (val), "%.12g", _plugin->getParameter (_plugin, n)); + parameters->add_property (index, val); + } + + root->add_child_nocopy (*parameters); + } +} + +int LXVSTPlugin::set_state (const XMLNode& node, int version) +{ + LocaleGuard lg (X_("POSIX")); + + if (node.name() != state_node_name()) + { + error << _("Bad node sent to VSTPlugin::set_state") << endmsg; + return 0; + } + + const XMLProperty* prop; + + if ((prop = node.property ("current-program")) != 0) + { + _vstfx->want_program = atoi (prop->value().c_str()); + } + + XMLNode* child; + int ret = -1; + + if ((child = find_named_node (node, X_("chunk"))) != 0) + { + XMLPropertyList::const_iterator i; + XMLNodeList::const_iterator n; + int ret = -1; + + for (n = child->children ().begin (); n != child->children ().end (); ++n) + { + if ((*n)->is_content ()) + { + /* XXX: this may be dubious for the same reasons that we delay + execution of load_preset. + */ + ret = set_chunk ((*n)->content().c_str(), false); + } + } + + } + else if ((child = find_named_node (node, X_("parameters"))) != 0) + { + XMLPropertyList::const_iterator i; + + for (i = child->properties().begin(); i != child->properties().end(); ++i) + { + int32_t param; + float val; + + sscanf ((*i)->name().c_str(), "param-%d", ¶m); //This was param_%d (from vst_plugin) which caused all sorts of odd behaviour + sscanf ((*i)->value().c_str(), "%f", &val); + + _plugin->setParameter (_plugin, param, val); + } + + /* program number is not knowable */ + + _vstfx->current_program = -1; + + ret = 0; + } + + Plugin::set_state (node, version); + return ret; +} + +int LXVSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) const +{ + VstParameterProperties prop; + + desc.min_unbound = false; + desc.max_unbound = false; + prop.flags = 0; + + /* old style */ + + char label[64]; + label[0] = '\0'; + + _plugin->dispatcher (_plugin, effGetParamName, which, 0, label, 0); + + desc.label = label; + desc.integer_step = false; + desc.lower = 0.0f; + desc.upper = 1.0f; + desc.step = 0.01f; + desc.smallstep = 0.005f; + desc.largestep = 0.1f; + desc.toggled = false; + desc.logarithmic = false; + desc.sr_dependent = false; + + return 0; +} + +bool LXVSTPlugin::load_preset (PresetRecord r) +{ + bool s; + + if (r.user) + { + s = load_user_preset (r); + } + else + { + s = load_plugin_preset (r); + } + + if (s) + { + Plugin::load_preset (r); + } + + return s; +} + +bool LXVSTPlugin::load_plugin_preset (PresetRecord r) +{ + /* This is a plugin-provided preset. + We can't dispatch directly here; too many plugins expects only one GUI thread. + */ + + /* Extract the index of this preset from the URI */ + int id; + int index; + int const p = sscanf (r.uri.c_str(), "VST:%d:%d", &id, &index); + assert (p == 2); + + _vstfx->want_program = index; + return true; +} + +bool LXVSTPlugin::load_user_preset (PresetRecord r) +{ + /* This is a user preset; we load it, and this code also knows about the + non-direct-dispatch thing. + */ + + boost::shared_ptr<XMLTree> t (presets_tree ()); + if (t == 0) + { + return false; + } + + XMLNode* root = t->root (); + + for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) + { + XMLProperty* uri = (*i)->property (X_("uri")); + XMLProperty* label = (*i)->property (X_("label")); + + assert (uri); + assert (label); + + if (label->value() != r.label) + { + continue; + } + + if (_plugin->flags & 32 /* effFlagsProgramsChunks */) + { + + /* Load a user preset chunk from our XML file and send it via a circuitous route to the plugin */ + + if (_vstfx->wanted_chunk) + { + g_free (_vstfx->wanted_chunk); + } + + for (XMLNodeList::const_iterator j = (*i)->children().begin(); j != (*i)->children().end(); ++j) + { + if ((*j)->is_content ()) + { + /* we can't dispatch directly here; too many plugins expect only one GUI thread */ + gsize size = 0; + guchar* raw_data = g_base64_decode ((*j)->content().c_str(), &size); + _vstfx->wanted_chunk = raw_data; + _vstfx->wanted_chunk_size = size; + _vstfx->want_chunk = 1; + return true; + } + } + + return false; + + } + else + { + for (XMLNodeList::const_iterator j = (*i)->children().begin(); j != (*i)->children().end(); ++j) + { + if ((*j)->name() == X_("Parameter")) + { + XMLProperty* index = (*j)->property (X_("index")); + XMLProperty* value = (*j)->property (X_("value")); + + assert (index); + assert (value); + + set_parameter (atoi (index->value().c_str()), atof (value->value().c_str ())); + } + } + return true; + } + } + return false; +} + +string LXVSTPlugin::do_save_preset (string name) +{ + boost::shared_ptr<XMLTree> t (presets_tree ()); + if (t == 0) + { + return ""; + } + + XMLNode* p = 0; + /* XXX: use of _presets.size() + 1 for the unique ID here is dubious at best */ + string const uri = string_compose (X_("VST:%1:%2"), unique_id (), _presets.size() + 1); + + if (_plugin->flags & 32 /* effFlagsProgramsChunks */) + { + + p = new XMLNode (X_("ChunkPreset")); + p->add_property (X_("uri"), uri); + p->add_property (X_("label"), name); + gchar* data = get_chunk (true); + p->add_content (string (data)); + g_free (data); + + } + else + { + + p = new XMLNode (X_("Preset")); + p->add_property (X_("uri"), uri); + p->add_property (X_("label"), name); + + for (uint32_t i = 0; i < parameter_count(); ++i) + { + if (parameter_is_input (i)) + { + XMLNode* c = new XMLNode (X_("Parameter")); + c->add_property (X_("index"), string_compose ("%1", i)); + c->add_property (X_("value"), string_compose ("%1", get_parameter (i))); + p->add_child_nocopy (*c); + } + } + } + + t->root()->add_child_nocopy (*p); + + sys::path f = ARDOUR::user_config_directory (); + f /= "presets"; + f /= presets_file (); + + t->write (f.to_string ()); + return uri; +} + +void LXVSTPlugin::do_remove_preset (string name) +{ + boost::shared_ptr<XMLTree> t (presets_tree ()); + if (t == 0) + { + return; + } + + t->root()->remove_nodes_and_delete (X_("label"), name); + + sys::path f = ARDOUR::user_config_directory (); + f /= "presets"; + f /= presets_file (); + + t->write (f.to_string ()); +} + +string LXVSTPlugin::describe_parameter (Evoral::Parameter param) +{ + char name[64] = "Unkown"; + _plugin->dispatcher (_plugin, effGetParamName, param.id(), 0, name, 0); + return name; +} + +framecnt_t LXVSTPlugin::signal_latency () const +{ + if (_user_latency) + { + return _user_latency; + } + +#ifdef VESTIGE_HEADER + return *((framecnt_t *) (((char *) &_plugin->flags) + 12)); /* initialDelay */ +#else + return _plugin->initial_delay; +#endif +} + +set<Evoral::Parameter> LXVSTPlugin::automatable () const +{ + set<Evoral::Parameter> ret; + + for (uint32_t i = 0; i < parameter_count(); ++i) + { + ret.insert (ret.end(), Evoral::Parameter(PluginAutomation, 0, i)); + } + + return ret; +} + +int LXVSTPlugin::connect_and_run (BufferSet& bufs, + ChanMapping in_map, ChanMapping out_map, + pframes_t nframes, framecnt_t offset) +{ + Plugin::connect_and_run (bufs, in_map, out_map, nframes, offset); + + float *ins[_plugin->numInputs]; + float *outs[_plugin->numOutputs]; + int32_t i; + + const uint32_t nbufs = bufs.count().n_audio(); + + int in_index = 0; + for (i = 0; i < (int32_t) _plugin->numInputs; ++i) + { + ins[i] = bufs.get_audio(min((uint32_t) in_index, nbufs - 1)).data() + offset; + in_index++; + } + + int out_index = 0; + for (i = 0; i < (int32_t) _plugin->numOutputs; ++i) + { + outs[i] = bufs.get_audio(min((uint32_t) out_index, nbufs - 1)).data() + offset; + out_index++; + } + + if (bufs.count().n_midi() > 0) + { + VstEvents* v = bufs.get_vst_midi (0); + _plugin->dispatcher (_plugin, effProcessEvents, 0, 0, v, 0); + } + + /* we already know it can support processReplacing */ + +#ifdef LXVST_32BIT + _plugin->processReplacing (_plugin, ins, outs, nframes); +#endif + +#if defined LXVST_64BIT && defined VESTIGE_HEADER + + /*Vestige doesn't work for 64Bit - some of the AEffect struct member types + appear not to be correct which throws the data alignment. We have two choices + + 1) Fix Vestige - preferable from a technical standpoint, but perhaps + not viable without affecting its 'clean room' status + + 2) Correct for the alignment error - a bit of a kludge, but it can work, + assuming the following data types / sizes on x86-64 + + char 1Byte : Byte aligned + int 4Bytes : 4Byte aligned + long 8Bytes : 8Byte aligned + pointers 8Bytes : 8Byte aligned + + This gives an offset of 8 Bytes - inclusive of padding + to translate to the correct address for processReplacing*/ + + ((AEffect*)(((char*)(_plugin)) + 8))->processReplacing(_plugin, ins, outs, nframes); + +#elif defined LXVST_64BIT + + _plugin->processReplacing(_plugin, ins, outs, nframes); + +#endif + + return 0; +} + +void LXVSTPlugin::deactivate () +{ + _plugin->dispatcher (_plugin, effMainsChanged, 0, 0, NULL, 0.0f); +} + +void LXVSTPlugin::activate () +{ + _plugin->dispatcher (_plugin, effMainsChanged, 0, 1, NULL, 0.0f); +} + +string LXVSTPlugin::unique_id() const +{ + char buf[32]; + +#if defined LXVST_64BIT && defined VESTIGE_HEADER + + /*The vestige header appears not to have correct data + alignment in AEffect struct for 64Bit, possibly due + to incorrect data types - see previous comments*/ + + snprintf (buf, sizeof (buf), "%d", *((int32_t*) &((AEffect*)(((char*)(_plugin)) + 12))->unused_id)); + +#elif defined LXVST_32BIT && defined VESTIGE_HEADER + + snprintf (buf, sizeof (buf), "%d", *((int32_t*) &_plugin->unused_id)); + +#else + + snprintf (buf, sizeof (buf), "%d", _plugin->uniqueID); + +#endif + + return string (buf); +} + + +const char * LXVSTPlugin::name () const +{ + return handle->name; +} + +const char * LXVSTPlugin::maker () const +{ + return _info->creator.c_str(); +} + +const char * LXVSTPlugin::label () const +{ + return handle->name; +} + +uint32_t LXVSTPlugin::parameter_count() const +{ + return _plugin->numParams; +} + +bool LXVSTPlugin::has_editor () const +{ + return _plugin->flags & effFlagsHasEditor; +} + +void LXVSTPlugin::print_parameter (uint32_t param, char *buf, uint32_t len) const +{ + char *first_nonws; + + _plugin->dispatcher (_plugin, 7 /* effGetParamDisplay */, param, 0, buf, 0); + + if (buf[0] == '\0') + { + return; + } + + first_nonws = buf; + while (*first_nonws && isspace (*first_nonws)) + { + first_nonws++; + } + if (*first_nonws == '\0') + { + return; + } + + memmove (buf, first_nonws, strlen (buf) - (first_nonws - buf) + 1); +} + +PluginPtr LXVSTPluginInfo::load (Session& session) +{ + try { + PluginPtr plugin; + + if (Config->get_use_lxvst()) + { + VSTFXHandle* handle; + + handle = vstfx_load(path.c_str()); + + if (handle == NULL) + { + error << string_compose(_("LXVST: cannot load module from \"%1\""), path) << endmsg; + } + else + { + plugin.reset (new LXVSTPlugin (session.engine(), session, handle)); + } + } + else + { + error << _("You asked ardour to not use any LXVST plugins") << endmsg; + return PluginPtr ((Plugin*) 0); + } + + plugin->set_info(PluginInfoPtr(new LXVSTPluginInfo(*this))); + return plugin; + } + + catch (failed_constructor &err) + { + return PluginPtr ((Plugin*) 0); + } +} + +void LXVSTPlugin::find_presets () +{ + /* Built-in presets */ + + int const vst_version = _plugin->dispatcher (_plugin, effGetVstVersion, 0, 0, NULL, 0); + for (int i = 0; i < _plugin->numPrograms; ++i) + { + PresetRecord r (string_compose (X_("LXVST:%1:%2"), unique_id (), i), "", false); + + if (vst_version >= 2) + { + char buf[256]; + if (_plugin->dispatcher (_plugin, 29, i, 0, buf, 0) == 1) + { + r.label = buf; + } + else + { + r.label = string_compose (_("Preset %1"), i); + } + } + else + { + r.label = string_compose (_("Preset %1"), i); + } + + _presets.insert (make_pair (r.uri, r)); + } + + /* User presets from our XML file */ + + boost::shared_ptr<XMLTree> t (presets_tree ()); + + if (t) + { + XMLNode* root = t->root (); + for (XMLNodeList::const_iterator i = root->children().begin(); i != root->children().end(); ++i) + { + XMLProperty* uri = (*i)->property (X_("uri")); + XMLProperty* label = (*i)->property (X_("label")); + + assert (uri); + assert (label); + + PresetRecord r (uri->value(), label->value(), true); + _presets.insert (make_pair (r.uri, r)); + } + } +} + +/** @return XMLTree with our user presets; could be a new one if no existing + * one was found, or 0 if one was present but badly-formatted. + */ +XMLTree * LXVSTPlugin::presets_tree () const +{ + XMLTree* t = new XMLTree; + + sys::path p = ARDOUR::user_config_directory (); + p /= "presets"; + + if (!is_directory (p)) + { + create_directory (p); + } + + p /= presets_file (); + + if (!exists (p)) + { + t->set_root (new XMLNode (X_("LXVSTPresets"))); + return t; + } + + t->set_filename (p.to_string ()); + if (!t->read ()) + { + delete t; + return 0; + } + + return t; +} + +/** @return Index of the first user preset in our lists */ +int LXVSTPlugin::first_user_preset_index () const +{ + return _plugin->numPrograms; +} + +string LXVSTPlugin::presets_file () const +{ + return string_compose ("lxvst-%1", unique_id ()); +} + +LXVSTPluginInfo::LXVSTPluginInfo() +{ + type = ARDOUR::LXVST; +} + diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc index 90e9146af1..153c624a5a 100644 --- a/libs/ardour/plugin.cc +++ b/libs/ardour/plugin.cc @@ -139,6 +139,12 @@ ARDOUR::find_plugin(Session& session, string identifier, PluginType type) break; #endif +#ifdef LXVST_SUPPORT + case ARDOUR::LXVST: + plugs = mgr->lxvst_plugin_info(); + break; +#endif + #ifdef HAVE_AUDIOUNITS case ARDOUR::AudioUnit: plugs = mgr->au_plugin_info(); @@ -170,6 +176,19 @@ ARDOUR::find_plugin(Session& session, string identifier, PluginType type) } #endif +#ifdef LXVST_SUPPORT + /* hmm, we didn't find it. could be because in older versions of Ardour. + we used to store the name of a VST plugin, not its unique ID. so try + again. + */ + + for (i = plugs.begin(); i != plugs.end(); ++i) { + if (identifier == (*i)->name){ + return (*i)->load (session); + } + } +#endif + return PluginPtr ((Plugin*) 0); } diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index 268d551116..0260f7d3aa 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -45,6 +45,10 @@ #include "ardour/vst_plugin.h" #endif +#ifdef LXVST_SUPPORT +#include "ardour/lxvst_plugin.h" +#endif + #ifdef HAVE_AUDIOUNITS #include "ardour/audio_unit.h" #endif @@ -570,6 +574,9 @@ PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other) #ifdef VST_SUPPORT boost::shared_ptr<VSTPlugin> vp; #endif +#ifdef LXVST_SUPPORT + boost::shared_ptr<LXVSTPlugin> lxvp; +#endif #ifdef HAVE_AUDIOUNITS boost::shared_ptr<AUPlugin> ap; #endif @@ -584,6 +591,10 @@ PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other) } else if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (other)) != 0) { return boost::shared_ptr<Plugin> (new VSTPlugin (*vp)); #endif +#ifdef LXVST_SUPPORT + } else if ((lxvp = boost::dynamic_pointer_cast<LXVSTPlugin> (other)) != 0) { + return boost::shared_ptr<Plugin> (new LXVSTPlugin (*lxvp)); +#endif #ifdef HAVE_AUDIOUNITS } else if ((ap = boost::dynamic_pointer_cast<AUPlugin> (other)) != 0) { return boost::shared_ptr<Plugin> (new AUPlugin (*ap)); @@ -852,6 +863,8 @@ PluginInsert::set_state(const XMLNode& node, int version) type = ARDOUR::LV2; } else if (prop->value() == X_("vst")) { type = ARDOUR::VST; + } else if (prop->value() == X_("lxvst")) { + type = ARDOUR::LXVST; } else if (prop->value() == X_("audiounit")) { type = ARDOUR::AudioUnit; } else { @@ -872,6 +885,14 @@ PluginInsert::set_state(const XMLNode& node, int version) prop = node.property ("id"); } #endif + +#ifdef LXVST_SUPPORT + /*There shouldn't be any older sessions with linuxVST support.. but anyway..*/ + + if (type == ARDOUR::LXVST) { + prop = node.property ("id"); + } +#endif /* recheck */ if (prop == 0) { diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc index 912fc20dcd..6279140795 100644 --- a/libs/ardour/plugin_manager.cc +++ b/libs/ardour/plugin_manager.cc @@ -36,6 +36,12 @@ #include <cstring> #endif // VST_SUPPORT +#ifdef LXVST_SUPPORT +#include <ardour/vstfx.h> +#include <pbd/basename.h> +#include <cstring> +#endif //LXVST_SUPPORT + #include <glibmm/miscutils.h> #include "pbd/pathscanner.h" @@ -56,6 +62,10 @@ #include "ardour/vst_plugin.h" #endif +#ifdef LXVST_SUPPORT +#include "ardour/lxvst_plugin.h" +#endif + #ifdef HAVE_AUDIOUNITS #include "ardour/audio_unit.h" #include <Carbon/Carbon.h> @@ -74,6 +84,7 @@ PluginManager* PluginManager::_manager = 0; PluginManager::PluginManager () : _vst_plugin_info(0) + , _lxvst_plugin_info(0) , _ladspa_plugin_info(0) , _lv2_plugin_info(0) , _au_plugin_info(0) @@ -107,6 +118,12 @@ PluginManager::PluginManager () } #endif /* VST_SUPPORT */ +#ifdef LXVST_SUPPORT + if (Config->get_use_lxvst()) { + add_lxvst_presets(); + } +#endif /* Native LinuxVST support*/ + if ((s = getenv ("LADSPA_PATH"))) { ladspa_path = s; } @@ -117,6 +134,12 @@ PluginManager::PluginManager () vst_path = s; } + if ((s = getenv ("LXVST_PATH"))) { + vst_path = s; + } else if ((s = getenv ("LXVST_PLUGINS"))) { + vst_path = s; + } + if (_manager == 0) { _manager = this; } @@ -154,6 +177,13 @@ PluginManager::refresh () vst_refresh (); } #endif // VST_SUPPORT + +#ifdef LXVST_SUPPORT + if(Config->get_use_lxvst()) { + lxvst_refresh(); + } +#endif //Native linuxVST SUPPORT + #ifdef HAVE_AUDIOUNITS au_refresh (); #endif @@ -268,6 +298,13 @@ PluginManager::add_vst_presets() { add_presets ("vst"); } + +void +PluginManager::add_lxvst_presets() +{ + add_presets ("lxvst"); +} + void PluginManager::add_presets(string domain) { @@ -586,6 +623,111 @@ PluginManager::vst_discover (string path) #endif // VST_SUPPORT +#ifdef LXVST_SUPPORT + +void +PluginManager::lxvst_refresh () +{ + if (_lxvst_plugin_info) + _lxvst_plugin_info->clear (); + else + _lxvst_plugin_info = new ARDOUR::PluginInfoList(); + + if (lxvst_path.length() == 0) { + lxvst_path = "/usr/local/lib/lxvst:/usr/lib/lxvst"; + } + + lxvst_discover_from_path (lxvst_path); +} + +int +PluginManager::add_lxvst_directory (string path) +{ + if (lxvst_discover_from_path (path) == 0) { + lxvst_path += ':'; + lxvst_path += path; + return 0; + } + return -1; +} + +static bool lxvst_filter (const string& str, void *arg) +{ + /* Not a dotfile, has a prefix before a period, suffix is "so" */ + + return str[0] != '.' && (str.length() > 3 && str.find (".so") == (str.length() - 3)); +} + +int +PluginManager::lxvst_discover_from_path (string path) +{ + PathScanner scanner; + vector<string *> *plugin_objects; + vector<string *>::iterator x; + int ret = 0; + + info << "Discovering linuxVST plugins along " << path << endmsg; + + plugin_objects = scanner (lxvst_path, lxvst_filter, 0, true, true); + + if (plugin_objects) { + for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) { + lxvst_discover (**x); + } + } + + info << "Done linuxVST discover" << endmsg; + + vector_delete (plugin_objects); + return ret; +} + +int +PluginManager::lxvst_discover (string path) +{ + VSTFXInfo* finfo; + char buf[32]; + + if ((finfo = vstfx_get_info (const_cast<char *> (path.c_str()))) == 0) { + warning << "Cannot get linuxVST information from " << path << endmsg; + return -1; + } + + if (!finfo->canProcessReplacing) { + warning << string_compose (_("linuxVST plugin %1 does not support processReplacing, and so cannot be used in ardour at this time"), + finfo->name) + << endl; + } + + PluginInfoPtr info(new LXVSTPluginInfo); + + if (!strcasecmp ("The Unnamed plugin", finfo->name)) { + info->name = PBD::basename_nosuffix (path); + } else { + info->name = finfo->name; + } + + + snprintf (buf, sizeof (buf), "%d", finfo->UniqueID); + info->unique_id = buf; + info->category = "linuxVSTs"; + info->path = path; + info->creator = finfo->creator; + info->index = 0; + info->n_inputs.set_audio (finfo->numInputs); + info->n_outputs.set_audio (finfo->numOutputs); + info->n_inputs.set_midi (finfo->wantMidi ? 1 : 0); + info->type = ARDOUR::LXVST; + + _lxvst_plugin_info->push_back (info); + vstfx_free_info (finfo); + + return 0; +} + +#endif // LXVST_SUPPORT + + PluginManager::PluginStatusType PluginManager::get_status (const PluginInfoPtr& pi) { @@ -625,6 +767,9 @@ PluginManager::save_statuses () case VST: ofs << "VST"; break; + case LXVST: + ofs << "LXVST"; + break; } ofs << ' '; @@ -709,6 +854,8 @@ PluginManager::load_statuses () type = LV2; } else if (stype == "VST") { type = VST; + } else if (stype == "LXVST") { + type = LXVST; } else { error << string_compose (_("unknown plugin type \"%1\" - ignored"), stype) << endmsg; @@ -749,6 +896,18 @@ PluginManager::vst_plugin_info () } ARDOUR::PluginInfoList& +PluginManager::lxvst_plugin_info () +{ +#ifdef LXVST_SUPPORT + if (!_lxvst_plugin_info) + lxvst_refresh(); + return *_lxvst_plugin_info; +#else + return _empty_plugin_info; +#endif +} + +ARDOUR::PluginInfoList& PluginManager::ladspa_plugin_info () { if (!_ladspa_plugin_info) diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 06ca70c9e9..11f510126a 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -970,6 +970,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "vst" || + prop->value() == "lxvst" || prop->value() == "audiounit") { processor.reset (new PluginInsert (_session)); @@ -2382,6 +2383,7 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || prop->value() == "vst" || + prop->value() == "lxvst" || prop->value() == "audiounit") { processor.reset (new PluginInsert(_session)); diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index c5b32b943b..d5eb687497 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -685,6 +685,8 @@ Session::hookup_io () /* Tell all IO objects to connect themselves together */ + cerr << "Enable IO connections, state = " << _state_of_the_state << endl; + IO::enable_connecting (); MIDI::Port::MakeConnections (); @@ -4336,6 +4338,7 @@ Session::update_latency (bool playback) if (playback) { /* reverse the list so that we work backwards from the last route to run to the first */ reverse (r->begin(), r->end()); + cerr << "\n!!! I JUST REVERSED THE ROUTE LIST (" << r->size() << ")!!!\n\n"; } /* compute actual latency values for the given direction and store them all in per-port @@ -4378,14 +4381,15 @@ Session::post_playback_latency () set_worst_playback_latency (); boost::shared_ptr<RouteList> r = routes.reader (); + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { if (!(*i)->is_hidden() && ((*i)->active())) { _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ()); } + } - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - (*i)->set_latency_compensation (_worst_track_latency); - } + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { + (*i)->set_latency_compensation (_worst_track_latency); } } @@ -4485,15 +4489,6 @@ Session::update_latency_compensation (bool force_whole_graph) DEBUG_TRACE (DEBUG::Latency, string_compose ("worst signal processing latency: %1 (changed ? %2)\n", _worst_track_latency, (some_track_latency_changed ? "yes" : "no"))); - if (force_whole_graph || some_track_latency_changed) { - /* trigger a full recompute of latency numbers for the graph. - everything else that we need to do will be done in the latency - callback. - */ - _engine.update_total_latencies (); - return; // everything else will be done in the latency callback - } - DEBUG_TRACE(DEBUG::Latency, "---------------------------- DONE update latency compensation\n\n") } diff --git a/libs/ardour/session_lxvst.cc b/libs/ardour/session_lxvst.cc new file mode 100755 index 0000000000..17c81e753f --- /dev/null +++ b/libs/ardour/session_lxvst.cc @@ -0,0 +1,364 @@ +/* + Copyright (C) 2004 + + 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; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/******************************************************/ +/*The big 'audio master' callback for linuxVST plugins*/ +/******************************************************/ + + +#include <stdbool.h> +#include <cstdio> + +#include <ardour/vstfx.h> +#include <ardour/vestige/aeffectx.h> + +#include <ardour/session.h> +#include <ardour/tempo.h> +#include <ardour/lxvst_plugin.h> + +#include "i18n.h" + +#define DEBUG_CALLBACKS +static int debug_callbacks = -1; + +#ifdef DEBUG_CALLBACKS +#define SHOW_CALLBACK if (debug_callbacks) printf +#else +#define SHOW_CALLBACK(...) +#endif + +using namespace ARDOUR; + +long Session::lxvst_callback (AEffect* effect, + long opcode, + long index, + long value, + void* ptr, + float opt) +{ + static VstTimeInfo _timeInfo; + LXVSTPlugin* plug; + Session* session; + + if (debug_callbacks < 0) + { + debug_callbacks = (getenv ("ARDOUR_DEBUG_VST_CALLBACKS") != 0); + } + + if (effect && effect->user) + { + plug = (LXVSTPlugin*) (effect->user); + session = &plug->session(); + SHOW_CALLBACK ("am callback 0x%x, opcode = %ld, plugin = \"%s\" ", (unsigned int)pthread_self(), opcode, plug->name()); + } + else + { + plug = 0; + session = 0; + SHOW_CALLBACK ("am callback 0x%x, opcode = %ld", (unsigned int)pthread_self(), opcode); + } + + switch(opcode){ + + case audioMasterAutomate: + SHOW_CALLBACK ("amc: audioMasterAutomate\n"); + + // index, value, returns 0 + if (effect) { + effect->setParameter (effect, index, opt); + } + return 0; + + case audioMasterVersion: + SHOW_CALLBACK ("amc: audioMasterVersion\n"); + // vst version, currently 2 (0 for older) + return 2; + + case audioMasterCurrentId: + SHOW_CALLBACK ("amc: audioMasterCurrentId\n"); + // returns the unique id of a plug that's currently + // loading + return 0; + + case audioMasterIdle: + SHOW_CALLBACK ("amc: audioMasterIdle\n"); + // call application idle routine (this will + // call effEditIdle for all open editors too) + if (effect) { + effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); + } + return 0; + + case audioMasterPinConnected: + SHOW_CALLBACK ("amc: audioMasterPinConnected\n"); + // inquire if an input or output is beeing connected; + // index enumerates input or output counting from zero: + // value is 0 for input and != 0 otherwise. note: the + // return value is 0 for <true> such that older versions + // will always return true. + return 1; + + case audioMasterWantMidi: + SHOW_CALLBACK ("amc: audioMasterWantMidi\n"); + // <value> is a filter which is currently ignored + 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 + memset(&_timeInfo, 0, sizeof(_timeInfo)); + if (session) { + _timeInfo.samplePos = session->transport_frame(); + _timeInfo.sampleRate = session->frame_rate(); + _timeInfo.flags = 0; + + if (value & (kVstTempoValid)) { + const Tempo& t (session->tempo_map().tempo_at (session->transport_frame())); + _timeInfo.tempo = t.beats_per_minute (); + _timeInfo.flags |= (kVstTempoValid); + } + if (value & (kVstBarsValid)) { + const Meter& m (session->tempo_map().meter_at (session->transport_frame())); + _timeInfo.timeSigNumerator = m.beats_per_bar (); + _timeInfo.timeSigDenominator = m.note_divisor (); + _timeInfo.flags |= (kVstBarsValid); + } + + if (session->transport_speed() != 0.0f) { + _timeInfo.flags |= kVstTransportPlaying; + } + } + + return (long)&_timeInfo; + + case audioMasterProcessEvents: + SHOW_CALLBACK ("amc: audioMasterProcessEvents\n"); + // VstEvents* in <ptr> + return 0; + + case audioMasterSetTime: + SHOW_CALLBACK ("amc: audioMasterSetTime\n"); + // VstTimenfo* in <ptr>, filter in <value>, not supported + + case audioMasterTempoAt: + SHOW_CALLBACK ("amc: audioMasterTempoAt\n"); + // returns tempo (in bpm * 10000) at sample frame location passed in <value> + if (session) { + const Tempo& t (session->tempo_map().tempo_at (value)); + return t.beats_per_minute() * 1000; + } else { + return 0; + } + break; + + case audioMasterGetNumAutomatableParameters: + SHOW_CALLBACK ("amc: audioMasterGetNumAutomatableParameters\n"); + return 0; + + case audioMasterGetParameterQuantization: + SHOW_CALLBACK ("amc: audioMasterGetParameterQuantization\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 0; + + case audioMasterIOChanged: + SHOW_CALLBACK ("amc: audioMasterIOChanged\n"); + // numInputs and/or numOutputs has changed + return 0; + + case audioMasterNeedIdle: + SHOW_CALLBACK ("amc: audioMasterNeedIdle\n"); + // plug needs idle calls (outside its editor window) + if (plug) { + plug->vstfx()->wantIdle = 1; + } + return 0; + + case audioMasterSizeWindow: + SHOW_CALLBACK ("amc: audioMasterSizeWindow\n"); + // index: width, value: height + return 0; + + case audioMasterGetSampleRate: + SHOW_CALLBACK ("amc: audioMasterGetSampleRate\n"); + if (session) { + return session->frame_rate(); + } + return 0; + + case audioMasterGetBlockSize: + SHOW_CALLBACK ("amc: audioMasterGetBlockSize\n"); + if (session) { + return session->get_block_size(); + } + return 0; + + case audioMasterGetInputLatency: + SHOW_CALLBACK ("amc: audioMasterGetInputLatency\n"); + return 0; + + case audioMasterGetOutputLatency: + SHOW_CALLBACK ("amc: audioMasterGetOutputLatency\n"); + return 0; + + 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* + + case audioMasterWillReplaceOrAccumulate: + SHOW_CALLBACK ("amc: audioMasterWillReplaceOrAccumulate\n"); + // returns: 0: not supported, 1: replace, 2: accumulate + return 0; + + case audioMasterGetCurrentProcessLevel: + SHOW_CALLBACK ("amc: audioMasterGetCurrentProcessLevel\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"); + 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: audioMasterOfflineGetCurrentPass\n"); + case audioMasterOfflineGetCurrentMetaPass: + SHOW_CALLBACK ("amc: audioMasterOfflineGetCurrentMetaPass\n"); + return 0; + + case audioMasterSetOutputSampleRate: + SHOW_CALLBACK ("amc: audioMasterSetOutputSampleRate\n"); + // for variable i/o, sample rate in <opt> + return 0; + + case audioMasterGetSpeakerArrangement: + SHOW_CALLBACK ("amc: audioMasterGetSpeakerArrangement\n"); + // (long)input in <value>, output in <ptr> + return 0; + + case audioMasterGetVendorString: + SHOW_CALLBACK ("amc: audioMasterGetVendorString\n"); + // fills <ptr> with a string identifying the vendor (max 64 char) + strcpy ((char*) ptr, "Linux Audio Systems"); + return 0; + + case audioMasterGetProductString: + SHOW_CALLBACK ("amc: audioMasterGetProductString\n"); + // fills <ptr> with a string with product name (max 64 char) + strcpy ((char*) ptr, "Ardour"); + return 0; + + case audioMasterGetVendorVersion: + SHOW_CALLBACK ("amc: audioMasterGetVendorVersion\n"); + // returns vendor-specific version + return 900; + + case audioMasterVendorSpecific: + SHOW_CALLBACK ("amc: audioMasterVendorSpecific\n"); + // no definition, vendor specific handling + return 0; + + case audioMasterSetIcon: + SHOW_CALLBACK ("amc: audioMasterSetIcon\n"); + // void* in <ptr>, format not defined yet + return 0; + + case audioMasterCanDo: + SHOW_CALLBACK ("amc: audioMasterCanDo\n"); + // string in ptr, see below + return 0; + + case audioMasterGetLanguage: + SHOW_CALLBACK ("amc: audioMasterGetLanguage\n"); + // see enum + return 0; + + case audioMasterOpenWindow: + SHOW_CALLBACK ("amc: audioMasterOpenWindow\n"); + // returns platform specific ptr + return 0; + + case audioMasterCloseWindow: + SHOW_CALLBACK ("amc: audioMasterCloseWindow\n"); + // close window, platform specific handle in <ptr> + return 0; + + 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 + if (effect) { + effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); + } + return 0; + + case audioMasterBeginEdit: + SHOW_CALLBACK ("amc: audioMasterBeginEdit\n"); + // begin of automation session (when mouse down), parameter index in <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; + + default: + SHOW_CALLBACK ("LXVST master dispatcher: undefed: %d\n", (int)opcode); + break; + } + + return 0; +} + diff --git a/libs/ardour/sndfile_helpers.cc b/libs/ardour/sndfile_helpers.cc index aa54f648a5..07281e6901 100644 --- a/libs/ardour/sndfile_helpers.cc +++ b/libs/ardour/sndfile_helpers.cc @@ -92,55 +92,32 @@ int sndfile_endian_formats[SNDFILE_ENDIAN_FORMATS] = { }; int -sndfile_header_format_from_string (string str) +sndfile_header_format_by_index (int index) { - for (int n = 0; sndfile_header_formats_strings[n]; ++n) { - if (str == sndfile_header_formats_strings[n]) { - return sndfile_header_formats[n]; - } + if (index >= 0 && index < SNDFILE_HEADER_FORMATS) { + return sndfile_header_formats[index]; } return -1; } int -sndfile_bitdepth_format_from_string (string str) +sndfile_bitdepth_format_by_index (int index) { - for (int n = 0; sndfile_bitdepth_formats_strings[n]; ++n) { - if (str == sndfile_bitdepth_formats_strings[n]) { - return sndfile_bitdepth_formats[n]; - } + if (index >= 0 && index < SNDFILE_BITDEPTH_FORMATS) { + return sndfile_bitdepth_formats[index]; } return -1; } int -sndfile_endian_format_from_string (string str) +sndfile_endian_format_by_index (int index) { - for (int n = 0; sndfile_endian_formats_strings[n]; ++n) { - if (str == sndfile_endian_formats_strings[n]) { - return sndfile_endian_formats[n]; - } + if (index >= 0 && index < SNDFILE_ENDIAN_FORMATS) { + return sndfile_endian_formats[index]; } return -1; } -string -sndfile_file_ending_from_string (string str) -{ - static vector<string> file_endings; - - if (file_endings.empty()) { - file_endings = I18N((const char **) sndfile_file_endings_strings); - } - - for (int n = 0; sndfile_header_formats_strings[n]; ++n) { - if (str == sndfile_header_formats_strings[n]) { - return file_endings[n]; - } - } - return 0; -} - int sndfile_data_width (int format) { diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 3a5ea0084a..c86f62de0b 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -75,7 +75,7 @@ legalize_for_path (const string& str) pos += 1; } - return legal; + return string (legal); } string diff --git a/libs/ardour/vstfx.cc b/libs/ardour/vstfx.cc new file mode 100755 index 0000000000..8c5192eb22 --- /dev/null +++ b/libs/ardour/vstfx.cc @@ -0,0 +1,32 @@ +#include <stdio.h> +#include <stdarg.h> + +#include <ardour/vstfx.h> + +/***********************************************************/ +/* VSTFX - A set of modules for managing linux VST plugins */ +/* vstfx.cc, vstfxwin.cc and vstfxinfofile.cc */ +/***********************************************************/ + + +/*Simple error handler stuff for VSTFX*/ + +void vstfx_error (const char *fmt, ...) +{ + va_list ap; + char buffer[512]; + + va_start (ap, fmt); + vsnprintf (buffer, sizeof(buffer), fmt, ap); + vstfx_error_callback (buffer); + va_end (ap); +} + +/*default error handler callback*/ + +void default_vstfx_error_callback (const char *desc) +{ + fprintf(stderr, "%s\n", desc); +} + +void (*vstfx_error_callback)(const char *desc) = &default_vstfx_error_callback; diff --git a/libs/ardour/vstfxinfofile.cc b/libs/ardour/vstfxinfofile.cc new file mode 100755 index 0000000000..2d54f4d1cd --- /dev/null +++ b/libs/ardour/vstfxinfofile.cc @@ -0,0 +1,382 @@ +/***********************************************************/ +/*vstfx infofile - module to manage info files */ +/*containing cached information about a plugin. e.g. its */ +/*name, creator etc etc */ +/***********************************************************/ + +/*This is largely unmodified from the original (C code) FST vstinfofile module*/ + +#include "ardour/vstfx.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> +#include <libgen.h> + +#define MAX_STRING_LEN 256 + +#define FALSE 0 +#define TRUE !FALSE + +static char* read_string(FILE *fp) +{ + char buf[MAX_STRING_LEN]; + + fgets( buf, MAX_STRING_LEN, fp ); + + if(strlen(buf) < MAX_STRING_LEN) + { + if(strlen(buf)) + buf[strlen(buf)-1] = 0; + + return strdup(buf); + } + else + { + return NULL; + } +} + +static VSTFXInfo* load_vstfx_info_file(char *filename) +{ + VSTFXInfo *info = (VSTFXInfo*) malloc(sizeof(VSTFXInfo)); + FILE *fp; + int i; + + if(info == NULL) + return NULL; + + fp = fopen(filename, "r"); + + if(fp == NULL) + { + free( info ); + return NULL; + } + + if((info->name = read_string(fp)) == NULL) goto error; + if((info->creator = read_string(fp)) == NULL) goto error; + if(1 != fscanf(fp, "%d\n", &info->UniqueID)) goto error; + if((info->Category = read_string(fp)) == NULL) goto error; + if(1 != fscanf(fp, "%d\n", &info->numInputs)) goto error; + if(1 != fscanf(fp, "%d\n", &info->numOutputs)) goto error; + if(1 != fscanf(fp, "%d\n", &info->numParams)) goto error; + if(1 != fscanf(fp, "%d\n", &info->wantMidi)) goto error; + if(1 != fscanf(fp, "%d\n", &info->hasEditor)) goto error; + if(1 != fscanf(fp, "%d\n", &info->canProcessReplacing)) goto error; + + if((info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams)) == NULL) goto error; + for(i=0; i<info->numParams; i++) + { + if((info->ParamNames[i] = read_string(fp)) == NULL) goto error; + } + if((info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams)) == NULL) goto error; + + for(i=0; i < info->numParams; i++) + { + if((info->ParamLabels[i] = read_string(fp)) == NULL) goto error; + } + + fclose( fp ); + return info; + +error: + fclose( fp ); + free( info ); + return NULL; +} + +static int save_vstfx_info_file(VSTFXInfo *info, char *filename) +{ + FILE *fp; + int i; + + if(info == NULL) + { + vstfx_error("** ERROR ** VSTFXinfofile : info ptr is NULL\n"); + return TRUE; + } + + fp = fopen(filename, "w"); + + if(fp == NULL) + { + vstfx_error("** WARNING ** VSTFX : Can't write info file %s\n", filename); + return TRUE; + } + + fprintf( fp, "%s\n", info->name ); + fprintf( fp, "%s\n", info->creator ); + fprintf( fp, "%d\n", info->UniqueID ); + fprintf( fp, "%s\n", info->Category ); + fprintf( fp, "%d\n", info->numInputs ); + fprintf( fp, "%d\n", info->numOutputs ); + fprintf( fp, "%d\n", info->numParams ); + fprintf( fp, "%d\n", info->wantMidi ); + fprintf( fp, "%d\n", info->hasEditor ); + fprintf( fp, "%d\n", info->canProcessReplacing ); + + for(i=0; i < info->numParams; i++) + { + fprintf(fp, "%s\n", info->ParamNames[i]); + } + + for(i=0; i < info->numParams; i++) + { + fprintf(fp, "%s\n", info->ParamLabels[i]); + } + + fclose( fp ); + + return FALSE; +} + +static char* vstfx_dllpath_to_infopath(char *dllpath) +{ + char* retval; + char* dir_path; + char* base_name; + + if(strstr(dllpath, ".so" ) == NULL) + return NULL; + + /*Allocate space for the filename - need strlen + 1 for the terminating'0', +1 because .so is three + chars, and .fsi is four chars and +1 because we have a '.' at the beginning*/ + + retval = (char*)malloc(strlen(dllpath) + 3); + + dir_path = strdup(dllpath); + base_name = strdup(dllpath); + + sprintf(retval, "%s/.%s", dirname(dir_path), basename(base_name)); + sprintf(retval + strlen(retval) - 3, ".fsi"); + + free(dir_path); + free(base_name); + + return retval; +} + +static int vstfx_info_file_is_valid(char *dllpath) +{ + struct stat dllstat; + struct stat vstfxstat; + + char *vstfxpath = vstfx_dllpath_to_infopath(dllpath); + + if(!vstfxpath) + return FALSE; + + if(stat(dllpath, &dllstat)) + { + vstfx_error( "** ERROR ** VSTFXinfofile : .so path %s invalid\n", dllpath ); + return TRUE; + } + + if(stat(vstfxpath, &vstfxstat)) + return FALSE; + + free(vstfxpath); + + if(dllstat.st_mtime > vstfxstat.st_mtime) + return FALSE; + else + return TRUE; +} + +static int vstfx_can_midi(VSTFX *vstfx) +{ + struct AEffect *plugin = vstfx->plugin; + + int vst_version = plugin->dispatcher (plugin, effGetVstVersion, 0, 0, NULL, 0.0f); + + if (vst_version >= 2) + { + /* should we send it VST events (i.e. MIDI) */ + + if ((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher (plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0)) + return TRUE; + } + return FALSE; +} + +static VSTFXInfo* vstfx_info_from_plugin(VSTFX *vstfx) +{ + + VSTFXInfo* info = (VSTFXInfo*) malloc(sizeof(VSTFXInfo)); + + struct AEffect *plugin; + int i; + + /*We need to init the creator because some plugins + fail to implement getVendorString, and so won't stuff the + string with any name*/ + + char creator[65] = "Unknown\0"; + + if(!vstfx) + { + vstfx_error( "** ERROR ** VSTFXinfofile : vstfx ptr is NULL\n" ); + return NULL; + } + + if(!info) + return NULL; + + plugin = vstfx->plugin; + + info->name = strdup(vstfx->handle->name ); + + /*If the plugin doesn't bother to implement GetVendorString we will + have pre-stuffed the string with 'Unkown' */ + + plugin->dispatcher (plugin, effGetVendorString, 0, 0, creator, 0); + + /*Some plugins DO implement GetVendorString, but DON'T put a name in it + so if its just a zero length string we replace it with 'Unknown' */ + + if (strlen(creator) == 0) + { + info->creator = strdup("Unknown"); + } + else + { + info->creator = strdup (creator); + } + +#if defined LXVST_64BIT && defined VESTIGE_HEADER + + /*On 64Bit the data alignment in AEffect struct is + incorrect using vestige. see lxvst_plugin.cc*/ + + info->UniqueID = *((int32_t *) &((AEffect*)(((char*)(plugin)) + 12))->unused_id); + +#elif defined LXVST_32BIT && defined VESTIGE_HEADER + + info->UniqueID = *((int32_t *) &plugin->unused_id); + +#else + + info->UniqueID = plugin->uniqueID; + +#endif + + info->Category = strdup("None"); // FIXME: + info->numInputs = plugin->numInputs; + info->numOutputs = plugin->numOutputs; + info->numParams = plugin->numParams; + info->wantMidi = vstfx_can_midi(vstfx); + info->hasEditor = plugin->flags & effFlagsHasEditor ? TRUE : FALSE; + info->canProcessReplacing = plugin->flags & effFlagsCanReplacing ? TRUE : FALSE; + info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams); + info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams); + for(i=0; i < info->numParams; i++) + { + char name[64]; + char label[64]; + + /*Not all plugins give parameters labels as well as names*/ + + strcpy(name, "No Name"); + strcpy(label, "No Label"); + + plugin->dispatcher (plugin, effGetParamName, i, 0, name, 0); + info->ParamNames[i] = strdup(name); + + plugin->dispatcher (plugin, effGetParamLabel, i, 0, label, 0); + info->ParamLabels[i] = strdup(label); + } + return info; +} + +/* A simple 'dummy' audiomaster callback which should be ok, +we will only be instantiating the plugin in order to get its info*/ + +static long simple_master_callback(struct AEffect *fx, long opcode, long index, long value, void *ptr, float opt) +{ + + if(opcode == audioMasterVersion) + return 2; + else + return 0; +} + +/*Try to get plugin info - first by looking for a .fsi cache of the +data, and if that doesn't exist, load the plugin, get its data and +then cache it for future ref*/ + +VSTFXInfo *vstfx_get_info(char *dllpath) +{ + if( vstfx_info_file_is_valid(dllpath)) + { + VSTFXInfo *info; + char *vstfxpath = vstfx_dllpath_to_infopath(dllpath); + + info = load_vstfx_info_file(vstfxpath); + free(vstfxpath); + + return info; + } + else + { + VSTFXHandle *h; + VSTFX *vstfx; + VSTFXInfo *info; + + char *vstfxpath; + + if(!(h = vstfx_load(dllpath))) + return NULL; + + if(!(vstfx = vstfx_instantiate(h, simple_master_callback, NULL))) + { + vstfx_unload(h); + vstfx_error( "** ERROR ** VSTFXinfofile : Instantiate failed\n" ); + return NULL; + } + + vstfxpath = vstfx_dllpath_to_infopath(dllpath); + + if(!vstfxpath) + { + vstfx_close(vstfx); + vstfx_unload(h); + vstfx_error( "** ERROR ** VSTFXinfofile : get vstfx filename failed\n" ); + return NULL; + } + + info = vstfx_info_from_plugin(vstfx); + + save_vstfx_info_file(info, vstfxpath); + + free(vstfxpath); + + vstfx_close(vstfx); + vstfx_unload(h); + + return info; + } +} + +void vstfx_free_info(VSTFXInfo *info ) +{ + int i; + + for(i=0; i < info->numParams; i++) + { + free(info->ParamNames[i]); + free(info->ParamLabels[i]); + } + + free(info->name); + free(info->creator); + free(info->Category); + free(info); +} + + diff --git a/libs/ardour/vstfxwin.cc b/libs/ardour/vstfxwin.cc new file mode 100755 index 0000000000..a1a7d73909 --- /dev/null +++ b/libs/ardour/vstfxwin.cc @@ -0,0 +1,1399 @@ +/******************************************************************/ +/** VSTFX - An engine based on FST for handling linuxVST plugins **/ +/******************************************************************/ + +/*This is derived from the original FST (C code) with some tweaks*/ + + +/** EDITOR tab stops at 4 **/ + +#include <stdlib.h> +#include <stdio.h> +#include <jack/jack.h> +#include <jack/thread.h> +#include <libgen.h> + +#include <pthread.h> +#include <signal.h> +#include <glib.h> + +#include <ardour/vstfx.h> + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <dlfcn.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <pthread.h> + +struct ERect{ + short top; + short left; + short bottom; + short right; +}; + +static pthread_mutex_t plugin_mutex; + +static VSTFX* vstfx_first = NULL; + +const char magic[] = "VSTFX Plugin State v002"; + +int gui_thread_id = 0; +static int gui_quit = 0; + +/*This will be our connection to X*/ + +Display* LXVST_XDisplay = NULL; + +/*The thread handle for the GUI event loop*/ + +pthread_t LXVST_gui_event_thread; + +#define DELAYED_WINDOW 1 + +/*Util functions to get the value of a property attached to an XWindow*/ + +bool LXVST_xerror; + +int TempErrorHandler(Display *display, XErrorEvent *e) +{ + LXVST_xerror = true; + + return 0; +} + +#ifdef LXVST_32BIT + +int getXWindowProperty(Window window, Atom atom) +{ + int result = 0; + int userSize; + unsigned long bytes; + unsigned long userCount; + unsigned char *data; + Atom userType; + LXVST_xerror = false; + + /*Use our own Xerror handler while we're in here - in an + attempt to stop the brain dead default Xerror behaviour of + qutting the entire application because of e.g. an invalid + window ID*/ + + XErrorHandler olderrorhandler = XSetErrorHandler(TempErrorHandler); + + XGetWindowProperty( LXVST_XDisplay, //The display + window, //The Window + atom, //The property + 0, //Offset into the data + 1, //Number of 32Bit chunks of data + false, //false = don't delete the property + AnyPropertyType, //Required property type mask + &userType, //Actual type returned + &userSize, //Actual format returned + &userCount, //Actual number of items stored in the returned data + &bytes, //Number of bytes remaining if a partial read + &data); //The actual data read + + if(LXVST_xerror == false && userCount == 1) + result = *(int*)data; + + XSetErrorHandler(olderrorhandler); + + /*Hopefully this will return zero if the property is not set*/ + + return result; +} + +#endif + +#ifdef LXVST_64BIT + +/********************************************************************/ +/* This is untested - have no 64Bit plugins which use this */ +/* system of passing an eventProc address */ +/********************************************************************/ + +long getXWindowProperty(Window window, Atom atom) +{ + long result = 0; + int userSize; + unsigned long bytes; + unsigned long userCount; + unsigned char *data; + Atom userType; + LXVST_xerror = false; + + /*Use our own Xerror handler while we're in here - in an + attempt to stop the brain dead default Xerror behaviour of + qutting the entire application because of e.g. an invalid + window ID*/ + + XErrorHandler olderrorhandler = XSetErrorHandler(TempErrorHandler); + + XGetWindowProperty( LXVST_XDisplay, + window, + atom, + 0, + 2, + false, + AnyPropertyType, + &userType, + &userSize, + &userCount, + &bytes, + &data); + + if(LXVST_xerror == false && userCount == 1) + result = *(long*)data; + + XSetErrorHandler(olderrorhandler); + + /*Hopefully this will return zero if the property is not set*/ + + return result; +} + +#endif + +/*The event handler - called from within the main GUI thread to +dispatch events to any VST UIs which have callbacks stuck to them*/ + +static void dispatch_x_events(XEvent* event, VSTFX* vstfx) +{ + /*Handle some of the Events we might be interested in*/ + + switch(event->type) + { + /*Configure event - when the window is resized or first drawn*/ + + case ConfigureNotify: + { + Window window = event->xconfigure.event; + + int width = event->xconfigure.width; + int height = event->xconfigure.height; + + /*If we get a config notify on the parent window XID then we need to see + if the size has been changed - some plugins re-size their UI window e.g. + when opening a preset manager (you might think that should be spawned as a new window...) */ + + /*if the size has changed, we flag this so that in lxvst_pluginui.cc we can make the + change to the GTK parent window in ardour, from its UI thread*/ + + if(window == (Window)(vstfx->window)) + { + if((width!=vstfx->width) || (height!=vstfx->height)) + { + vstfx->width = width; + vstfx->height = height; + vstfx->want_resize = 1; + + /*QUIRK : Loomer plugins not only resize the UI but throw it into some random + position at the same time. We need to re-position the window at the origin of + the parent window*/ + + if(vstfx->plugin_ui_window) + XMoveWindow(LXVST_XDisplay, vstfx->plugin_ui_window, 0, 0); + } + } + + break; + + } + + /*Reparent Notify - when the plugin UI is reparented into + our Host Window we will get an event here... probably... */ + + case ReparentNotify: + { + Window ParentWindow = event->xreparent.parent; + + /*If the ParentWindow matches the window for the vstfx instance then + the Child window must be the XID of the pluginUI window created by the + plugin, so we need to see if it has a callback stuck to it, and if so + set that up in the vstfx */ + + /***********************************************************/ + /* 64Bit --- This mechanism is not 64Bit compatible at the */ + /* present time */ + /***********************************************************/ + + if(ParentWindow == (Window)(vstfx->window)) + { + Window PluginUIWindowID = event->xreparent.window; + + vstfx->plugin_ui_window = PluginUIWindowID; +#ifdef LXVST_32BIT + int result = getXWindowProperty(PluginUIWindowID, XInternAtom(LXVST_XDisplay, "_XEventProc", false)); + + if(result == 0) + vstfx->eventProc = NULL; + else + vstfx->eventProc = (void (*) (void* event))result; +#endif +#ifdef LXVST_64BIT + long result = getXWindowProperty(PluginUIWindowID, XInternAtom(LXVST_XDisplay, "_XEventProc", false)); + + if(result == 0) + vstfx->eventProc = NULL; + else + vstfx->eventProc = (void (*) (void* event))result; +#endif + } + break; + } + + case ClientMessage: + { + Window window = event->xany.window; + Atom message_type = event->xclient.message_type; + + /*The only client message we are interested in is to signal + that the plugin parent window is now valid and can be passed + to effEditOpen when the editor is launched*/ + + if(window == (Window)(vstfx->window)) + { + char* message = XGetAtomName(LXVST_XDisplay, message_type); + + if(strcmp(message,"LaunchEditor") == 0) + { + + if(event->xclient.data.l[0] == 0x0FEEDBAC) + vstfx_launch_editor(vstfx); + } + + XFree(message); + } + break; + } + + default: + break; + } + + /* Some VSTs built with toolkits e.g. JUCE will manager their own UI + autonomously in the plugin, running the UI in its own thread, so once + we have created a parent window for the plugin, its UI takes care of + itself.*/ + + /*Other types register a callback as an Xwindow property on the plugin + UI window after they create it. If that is the case, we need to call it + here, passing the XEvent into it*/ + + if(vstfx->eventProc == NULL) + return; + + vstfx->eventProc((void*)event); +} + + +/*Create and return a pointer to a new vstfx instance*/ + +static VSTFX* vstfx_new () +{ + VSTFX* vstfx = (VSTFX*) calloc (1, sizeof (VSTFX)); + + /*Mutexes*/ + + pthread_mutex_init (&vstfx->lock, NULL); + pthread_cond_init (&vstfx->window_status_change, NULL); + pthread_cond_init (&vstfx->plugin_dispatcher_called, NULL); + pthread_cond_init (&vstfx->window_created, NULL); + + /*Safe values*/ + + vstfx->want_program = -1; + vstfx->want_chunk = 0; + vstfx->current_program = -1; + vstfx->n_pending_keys = 0; + vstfx->has_editor = 0; + vstfx->program_set_without_editor = 0; + vstfx->window = 0; + vstfx->plugin_ui_window = 0; + vstfx->eventProc = NULL; + vstfx->extra_data = NULL; + vstfx->want_resize = 0; + + return vstfx; +} + +/*Create and return a pointer to a new VSTFX handle*/ + +static VSTFXHandle* vstfx_handle_new() +{ + VSTFXHandle* vstfx = (VSTFXHandle*)calloc(1, sizeof (VSTFXHandle)); + return vstfx; +} + +/** This is the main gui event loop for the plugin, we also need to pass +any Xevents to all the UI callbacks plugins 'may' have registered on their +windows, that is if they don't manage their own UIs **/ + +void* gui_event_loop (void* ptr) +{ + + VSTFX* vstfx; + int LXVST_sched_event_timer = 0; + int LXVST_sched_timer_interval = 50; //ms + XEvent event; + + /*The 'Forever' loop - runs the plugin UIs etc - based on the FST gui event loop*/ + + while (!gui_quit) + { + /* handle window creation requests, destroy requests, + and run idle callbacks */ + + /*Look at the XEvent queue - if there are any XEvents we need to handle them, + including passing them to all the plugin (eventProcs) we are currently managing*/ + + if(LXVST_XDisplay) + { + /*See if there are any events in the queue*/ + + int num_events = XPending(LXVST_XDisplay); + + /*process them if there are any*/ + + while(num_events) + { + XNextEvent(LXVST_XDisplay, &event); + + /*Call dispatch events, with the event, for each plugin in the linked list*/ + + for (vstfx = vstfx_first; vstfx; vstfx = vstfx->next) + { + pthread_mutex_lock(&vstfx->lock); + + dispatch_x_events(&event, vstfx); + + pthread_mutex_unlock(&vstfx->lock); + } + + num_events--; + } + } + + /*We don't want to use all the CPU.. */ + + usleep(1000); + + LXVST_sched_event_timer++; + + LXVST_sched_event_timer = LXVST_sched_event_timer & 0x00FFFFFF; + + /*See if its time for us to do a scheduled event pass on all the plugins*/ + + if((LXVST_sched_timer_interval!=0) && (!(LXVST_sched_event_timer% LXVST_sched_timer_interval))) + { + pthread_mutex_lock (&plugin_mutex); + +again: + /*Parse through the linked list of plugins*/ + + for (vstfx = vstfx_first; vstfx; vstfx = vstfx->next) + { + pthread_mutex_lock (&vstfx->lock); + + /*Window scheduled for destruction*/ + + if (vstfx->destroy) + { + if (vstfx->window) + { + vstfx->plugin->dispatcher( vstfx->plugin, effEditClose, 0, 0, NULL, 0.0 ); + + XDestroyWindow (LXVST_XDisplay, vstfx->window); + vstfx->window = 0; //FIXME - probably safe to assume we never have an XID of 0 but not explicitly true + vstfx->destroy = FALSE; + } + + vstfx_event_loop_remove_plugin (vstfx); + vstfx->been_activated = FALSE; + pthread_cond_signal (&vstfx->window_status_change); + pthread_mutex_unlock (&vstfx->lock); + + goto again; + } + + /*Window does not yet exist - scheduled for creation*/ + + if (vstfx->window == 0) //FIXME - probably safe to assume 0 is not a valid XID but not explicitly true + { + if (vstfx_create_editor (vstfx)) + { + vstfx_error ("** ERROR ** VSTFX : Cannot create editor for plugin %s", vstfx->handle->name); + vstfx_event_loop_remove_plugin (vstfx); + pthread_cond_signal (&vstfx->window_status_change); + pthread_mutex_unlock (&vstfx->lock); + goto again; + } + else + { + /* condition/unlock: it was signalled & unlocked in fst_create_editor() */ + } + } + + /*Scheduled for setting a new program*/ + + if (vstfx->want_program != -1 ) + { + if (vstfx->vst_version >= 2) + { + vstfx->plugin->dispatcher (vstfx->plugin, 67 /* effBeginSetProgram */, 0, 0, NULL, 0); + } + + vstfx->plugin->dispatcher (vstfx->plugin, effSetProgram, 0, vstfx->want_program, NULL, 0); + + if (vstfx->vst_version >= 2) + { + vstfx->plugin->dispatcher (vstfx->plugin, 68 /* effEndSetProgram */, 0, 0, NULL, 0); + } + + /* did it work? */ + + vstfx->current_program = vstfx->plugin->dispatcher (vstfx->plugin, 3, /* effGetProgram */ 0, 0, NULL, 0); + vstfx->want_program = -1; + } + + /*scheduled call to dispatcher*/ + + if(vstfx->dispatcher_wantcall) + { + vstfx->dispatcher_retval = vstfx->plugin->dispatcher( vstfx->plugin, + vstfx->dispatcher_opcode, + vstfx->dispatcher_index, + vstfx->dispatcher_val, + vstfx->dispatcher_ptr, + vstfx->dispatcher_opt ); + vstfx->dispatcher_wantcall = 0; + pthread_cond_signal (&vstfx->plugin_dispatcher_called); + } + + /*Call the editor Idle function in the plugin*/ + + vstfx->plugin->dispatcher (vstfx->plugin, effEditIdle, 0, 0, NULL, 0); + + if(vstfx->wantIdle) + vstfx->plugin->dispatcher (vstfx->plugin, 53, 0, 0, NULL, 0); + + pthread_mutex_unlock (&vstfx->lock); + } + pthread_mutex_unlock (&plugin_mutex); + } + } + + /*Drop out to here if we set gui_quit to 1 */ + + return NULL; +} + +/*The VSTFX Init function - this needs to be called before the VSTFX engine +can be accessed, it gets the UI thread running, opens a connection to X etc +normally started in globals.cc*/ + +int vstfx_init (void* ptr) +{ + + int thread_create_result; + + pthread_attr_t thread_attributes; + + /*Init the attribs to defaults*/ + + pthread_attr_init(&thread_attributes); + + /*Make sure the thread is joinable - this should be the default anyway - + so we can join to it on vstfx_exit*/ + + pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_JOINABLE); + + + /*This is where we need to open a connection to X, and start the GUI thread*/ + + /*Open our connection to X - all linuxVST plugin UIs handled by the LXVST engine + will talk to X down this connection - X cannot handle multi-threaded access via + the same Display* */ + + if(LXVST_XDisplay==NULL) + LXVST_XDisplay = XOpenDisplay(NULL); //We might be able to make this open a specific screen etc + + /*Drop out and report the error if we fail to connect to X */ + + if(LXVST_XDisplay==NULL) + { + vstfx_error ("** ERROR ** VSTFX: Failed opening connection to X"); + + return -1; + } + + /*We have a connection to X - so start the gui event loop*/ + + /*Create the thread - use default attrs for now, don't think we need anything special*/ + + thread_create_result = pthread_create(&LXVST_gui_event_thread, NULL, gui_event_loop, NULL); + + if(thread_create_result!=0) + { + /*There was a problem starting the GUI event thread*/ + + vstfx_error ("** ERROR ** VSTFX: Failed starting GUI event thread"); + + XCloseDisplay(LXVST_XDisplay); + + return -1; + } + + return 0; +} + +/*The vstfx Quit function*/ + +void vstfx_exit() +{ + gui_quit = 1; + + /*We need to pthread_join the gui_thread here so + we know when it has stopped*/ + + pthread_join(LXVST_gui_event_thread, NULL); +} + +/*Adds a new plugin (VSTFX) instance to the linked list*/ + +int vstfx_run_editor (VSTFX* vstfx) +{ + pthread_mutex_lock (&plugin_mutex); + + /*Add the new VSTFX instance to the linked list*/ + + if (vstfx_first == NULL) + { + vstfx_first = vstfx; + } + else + { + VSTFX* p = vstfx_first; + + while (p->next) + { + p = p->next; + } + p->next = vstfx; + + /*Mark the new end of the list*/ + + vstfx->next = NULL; + } + + pthread_mutex_unlock (&plugin_mutex); + + /* wait for the plugin editor window to be created (or not) */ + + pthread_mutex_lock (&vstfx->lock); + + if (!vstfx->window) + { + pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock); + } + + pthread_mutex_unlock (&vstfx->lock); + + if (!vstfx->window) + { + return -1; + } + + return 0; +} + +/*Set up a call to the plugins 'dispatcher' function*/ + +int vstfx_call_dispatcher (VSTFX *vstfx, int opcode, int index, int val, void *ptr, float opt) +{ + pthread_mutex_lock (&vstfx->lock); + + /*Set up the opcode and parameters*/ + + vstfx->dispatcher_opcode = opcode; + vstfx->dispatcher_index = index; + vstfx->dispatcher_val = val; + vstfx->dispatcher_ptr = ptr; + vstfx->dispatcher_opt = opt; + + /*Signal that we want the call to happen*/ + + vstfx->dispatcher_wantcall = 1; + + /*Wait for the call to happen*/ + + pthread_cond_wait (&vstfx->plugin_dispatcher_called, &vstfx->lock); + pthread_mutex_unlock (&vstfx->lock); + + /*Return the result*/ + + return vstfx->dispatcher_retval; +} + +/*Creates an editor for the plugin - normally called from within the gui event loop +after run_editor has added the plugin (editor) to the linked list*/ + +int vstfx_create_editor (VSTFX* vstfx) +{ + Window parent_window; + + int x_size = 1; + int y_size = 1; + + /* Note: vstfx->lock is held while this function is called */ + + if (!(vstfx->plugin->flags & effFlagsHasEditor)) + { + vstfx_error ("** ERROR ** VSTFX: Plugin \"%s\" has no editor", vstfx->handle->name); + return -1; + } + + + /*Create an XWindow for the plugin to inhabit*/ + + parent_window = XCreateSimpleWindow(LXVST_XDisplay, + DefaultRootWindow(LXVST_XDisplay), + 0, + 0, + x_size, + y_size, + 0, + 0, + 0); + + /*Select the events we are interested in receiving - we need Substructure notify so that + if the plugin resizes its window - e.g. Loomer Manifold then we get a message*/ + + XSelectInput(LXVST_XDisplay, + parent_window, + SubstructureNotifyMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask); + + vstfx->window = parent_window; + + vstfx->xid = parent_window; //vstfx->xid will be referenced to connect to GTK UI in ardour later + + /*Because the plugin may be operating on a different Display* to us, and therefore + the two event queues can be asynchronous, although we have created the window on + our display, we can't guarantee it exists in the server yet, which will + cause BadWindow crashes if the plugin tries to use it. + + It would be nice to use CreateNotify events here, but they don't get + through on all window managers, so instead we pass a client message + into out queue, after the XCreateWindow. When this message pops out + in our event handler, it will trigger the second stage of plugin + Editor instantiation, and by then the Window should be valid...*/ + + XClientMessageEvent event; + + /*Create an atom to identify our message (only if it doesn't already exist)*/ + + Atom WindowActiveAtom = XInternAtom(LXVST_XDisplay, "LaunchEditor", false); + + event.type = ClientMessage; + event.send_event = true; + event.window = parent_window; + event.message_type = WindowActiveAtom; + + event.format = 32; //Data format + event.data.l[0] = 0x0FEEDBAC; //Something we can recognize later + + /*Push the event into the queue on our Display*/ + + XSendEvent(LXVST_XDisplay, parent_window, FALSE, NoEventMask, (XEvent*)&event); + + /*Unlock - and we are done for the first part of staring the Editor...*/ + + pthread_mutex_unlock (&vstfx->lock); + + return 0; +} + +int vstfx_launch_editor(VSTFX* vstfx) +{ + /*This is the second stage of launching the editor (see vstfx_create editor) + we get called here in response to receiving the ClientMessage on our Window, + therefore it's about as safe (as can be) to assume that the Window we created + is now valid in the XServer and can be passed to the plugin in effEditOpen + without generating BadWindow errors when the plugin reparents itself into our + parent window*/ + + if(vstfx->been_activated) + return 0; + + Window parent_window; + struct ERect* er; + + int x_size = 1; + int y_size = 1; + + parent_window = vstfx->window; + + /*Open the editor - Bah! we have to pass the int windowID as a void pointer - yuck + it gets cast back to an int as the parent window XID in the plugin - and we have to pass the + Display* as a long */ + + /**************************************************************/ + /* 64Bit --- parent window is an int passed as a void* so */ + /* that should be ok for 64Bit machines */ + /* */ + /* Display is passed in as a long - ok on arch's where sizeof */ + /* long = 8 */ + /* */ + /* Most linux VST plugins open a connection to X on their own */ + /* Display anyway so it may not matter */ + /* */ + /* linuxDSP VSTs don't use the host Display* at all */ + /**************************************************************/ + + vstfx->plugin->dispatcher (vstfx->plugin, effEditOpen, 0, (long)LXVST_XDisplay, (void*)(parent_window), 0 ); + + /*QUIRK - some plugins need a slight delay after opening the editor before you can + ask the window size or they might return zero - specifically discoDSP */ + + usleep(100000); + + /*Now we can find out how big the parent window should be (and try) to resize it*/ + + vstfx->plugin->dispatcher (vstfx->plugin, effEditGetRect, 0, 0, &er, 0 ); + + x_size = er->right - er->left; + y_size = er->bottom - er->top; + + vstfx->width = x_size; + vstfx->height = y_size; + + XResizeWindow(LXVST_XDisplay, parent_window, x_size, y_size); + + XFlush (LXVST_XDisplay); + + /*Not sure if we need to map the window or if the plugin will do it for us + it should be ok because XReparentWindow generates a Map event*/ + + /*mark the editor as activated - mainly so that vstfx_get_XID + will know it is valid*/ + + vstfx->been_activated = TRUE; + + pthread_cond_signal (&vstfx->window_status_change); + return 0; +} + +/*May not be needed in the XLib version*/ + +void vstfx_move_window_into_view (VSTFX* vstfx) +{ + + /*This is probably the equivalent of Mapping an XWindow + but we most likely don't need it because the window + will be Mapped by XReparentWindow*/ + +} + +/*Destroy the editor window*/ + +void vstfx_destroy_editor (VSTFX* vstfx) +{ + pthread_mutex_lock (&vstfx->lock); + if (vstfx->window) + { + vstfx->destroy = TRUE; + pthread_cond_wait (&vstfx->window_status_change, &vstfx->lock); + } + pthread_mutex_unlock (&vstfx->lock); +} + +/*Remove a vstfx instance from the linked list parsed by the +event loop*/ + +void vstfx_event_loop_remove_plugin (VSTFX* vstfx) +{ + /*This only ever gets called from within our GUI thread + so we don't need to lock here - if we did there would be + a deadlock anyway*/ + + VSTFX* p; + VSTFX* prev; + + for(p = vstfx_first, prev = NULL; p; prev = p, p = p->next) + { + if(p == vstfx) + { + if(prev) + { + prev->next = p->next; + break; + } + } + } + + if (vstfx_first == vstfx) + vstfx_first = vstfx_first->next; +} + +/*This loads the plugin shared library*/ + +void* vstfx_load_vst_library(const char* path) +{ + void* dll; + char* full_path; + char* envdup; + char* lxvst_path; + size_t len1; + size_t len2; + + /*Try and load the shared library pointed to by the path - + NOTE: You have to give RTLD_LAZY or RTLD_NOW to dlopen or + you get some occasional failures to load - dlerror reports + invalid arguments*/ + + if ((dll = dlopen (path, RTLD_LOCAL | RTLD_LAZY)) != NULL) + return dll; + + /*We didn't find the library so try and get the path specified in the + env variable LXVST_PATH*/ + + envdup = getenv ("LXVST_PATH"); + + /*Path not specified - not much more we can do*/ + + if (envdup == NULL) + return NULL; + + /*Copy the path into envdup*/ + + envdup = strdup (envdup); + + if (envdup == NULL) + return NULL; + + len2 = strlen(path); + + /*Try all the possibilities in the path - deliminated by : */ + + lxvst_path = strtok (envdup, ":"); + + while (lxvst_path != NULL) + { + vstfx_error ("\"%s\"", lxvst_path); + len1 = strlen(lxvst_path); + + full_path = (char*)malloc(len1 + 1 + len2 + 1); + memcpy(full_path, lxvst_path, len1); + full_path[len1] = '/'; + memcpy(full_path + len1 + 1, path, len2); + full_path[len1 + 1 + len2] = '\0'; + + /*Try and load the library*/ + + if ((dll = dlopen(full_path, RTLD_LOCAL | RTLD_LAZY)) != NULL) + { + /*Succeeded */ + break; + } + + /*Try again*/ + + lxvst_path = strtok (NULL, ":"); + } + + /*Free the path*/ + + free(envdup); + + return dll; +} + +/*This loads up a plugin, given the path to its .so file and + finds its main entry point etc*/ + +VSTFXHandle* vstfx_load (const char *path) +{ + char* buf = NULL; + VSTFXHandle* fhandle; + int i; + + /*Create a new handle we can use to reference the plugin*/ + + fhandle = vstfx_handle_new(); + + /*See if we have .so appended to the path - if not we need to make sure it is added*/ + + if (strstr (path, ".so") == NULL) + { + + /*Append the .so to the path - Make sure the path has enough space*/ + + buf = (char *)malloc(strlen(path) + 4); //The .so and a terminating zero + + sprintf (buf, "%s.so", path); + + fhandle->nameptr = strdup (path); + + } + else + { + /*We already have .so appened to the filename*/ + + buf = strdup(path); + + fhandle->nameptr = strdup (path); + } + + /*Use basename to shorten the path and then strip off the .so - the old VST problem, + we don't know anything about its name until we load and instantiate the plugin + which we don't want to do at this point*/ + + for(i=0; i < (int)strlen(fhandle->nameptr); i++) + { + if(fhandle->nameptr[i] == '.') + fhandle->nameptr[i] = 0; + } + + + fhandle->name = basename (fhandle->nameptr); + + /*call load_vstfx_library to actually load the .so into memory*/ + + if ((fhandle->dll = vstfx_load_vst_library (buf)) == NULL) + { + vstfx_unload (fhandle); + + free(buf); + + return NULL; + } + + /*Find the main entry point into the plugin*/ + + if ((fhandle->main_entry = (main_entry_t) dlsym(fhandle->dll, "main")) == NULL) + { + /*If it can't be found, unload the plugin and return a NULL handle*/ + + vstfx_unload (fhandle); + + free(buf); + + return NULL; + } + + free(buf); + + /*return the handle of the plugin*/ + + return fhandle; +} + +/*This unloads a plugin*/ + +int vstfx_unload (VSTFXHandle* fhandle) +{ + if (fhandle->plugincnt) + { + /*Still have plugin instances - can't unload the library + - actually dlclose keeps an instance count anyway*/ + + return -1; + } + + /*Valid plugin loaded?*/ + + if (fhandle->dll) + { + dlclose(fhandle->dll); + fhandle->dll = NULL; + } + + if (fhandle->nameptr) + { + free (fhandle->nameptr); + fhandle->name = NULL; + } + + /*Don't need the plugin handle any more*/ + + free (fhandle); + return 0; +} + +/*This instantiates a plugin*/ + +VSTFX* vstfx_instantiate (VSTFXHandle* fhandle, audioMasterCallback amc, void* userptr) +{ + VSTFX* vstfx = vstfx_new (); + + if(fhandle == NULL) + { + vstfx_error( "** ERROR ** VSTFX : The handle was NULL\n" ); + return NULL; + } + + if ((vstfx->plugin = fhandle->main_entry (amc)) == NULL) + { + vstfx_error ("** ERROR ** VSTFX : %s could not be instantiated :(\n", fhandle->name); + free (vstfx); + return NULL; + } + + vstfx->handle = fhandle; + vstfx->plugin->user = userptr; + + if (vstfx->plugin->magic != kEffectMagic) + { + vstfx_error ("** ERROR ** VSTFX : %s is not a VST plugin\n", fhandle->name); + free (vstfx); + return NULL; + } + + vstfx->plugin->dispatcher (vstfx->plugin, effOpen, 0, 0, 0, 0); + + /*May or May not need to 'switch the plugin on' here - unlikely + since FST doesn't and most plugins start up 'On' by default - I think this is the least of our worries*/ + + //vstfx->plugin->dispatcher (vstfx->plugin, effMainsChanged, 0, 1, NULL, 0); + + vstfx->vst_version = vstfx->plugin->dispatcher (vstfx->plugin, effGetVstVersion, 0, 0, 0, 0); + + vstfx->handle->plugincnt++; + vstfx->wantIdle = 0; + + return vstfx; +} + +/*Close a vstfx instance*/ + +void vstfx_close (VSTFX* vstfx) +{ + vstfx_destroy_editor(vstfx); + + if(vstfx->plugin) + { + vstfx->plugin->dispatcher (vstfx->plugin, effMainsChanged, 0, 0, NULL, 0); + + /*Calling dispatcher with effClose will cause the plugin's destructor to + be called, which will also remove the editor if it exists*/ + + vstfx->plugin->dispatcher (vstfx->plugin, effClose, 0, 0, 0, 0); + } + + if (vstfx->handle->plugincnt) + vstfx->handle->plugincnt--; + + /*vstfx_unload will unload the dll if the instance count allows - + we need to do this because some plugins keep their own instance count + and (JUCE) manages the plugin UI in its own thread. When the plugins + internal instance count reaches zero, JUCE stops the UI thread and won't + restart it until the next time the library is loaded. If we don't unload + the lib JUCE will never restart*/ + + + if (vstfx->handle->plugincnt) + { + return; + } + + /*Valid plugin loaded - so we can unload it and NULL the pointer + to it. We can't free the handle here because we don't know what else + might need it. It should be / is freed when the plugin is deleted*/ + + if (vstfx->handle->dll) + { + dlclose(vstfx->handle->dll); //dlclose keeps its own reference count + vstfx->handle->dll = NULL; + } +} + + +/*Get the XID of the plugin editor window*/ + +int vstfx_get_XID (VSTFX* vstfx) +{ + int id; + + /*Wait for the lock to become free - otherwise + the window might be in the process of being + created and we get bad Window errors when trying + to embed it in the GTK UI*/ + + pthread_mutex_lock(&vstfx->lock); + + /*The Window may be scheduled for creation + but not actually created by the gui_event_loop yet - + + spin here until it has been activated. Possible + deadlock if the window never gets activated but + should not be called here if the window doesn't + exist or will never exist*/ + + while(!(vstfx->been_activated)) + usleep(1000); + + id = vstfx->xid; + + pthread_mutex_unlock(&vstfx->lock); + + /*Finally it might be safe to return the ID - + problems will arise if we return either a zero ID + and GTK tries to socket it or if we return an ID + which hasn't yet become real to the server*/ + + return id; +} + +float htonf (float v) +{ + float result; + char * fin = (char*)&v; + char * fout = (char*)&result; + fout[0] = fin[3]; + fout[1] = fin[2]; + fout[2] = fin[1]; + fout[3] = fin[0]; + return result; +} + + +/*load_state and save_state do not appear to be needed (yet) in ardour +- untested at the moment, these are just replicas of the fst code*/ + + +#if 0 +int vstfx_load_state (VSTFX* vstfx, char * filename) +{ + FILE* f = fopen (filename, "rb"); + if(f) + { + char testMagic[sizeof (magic)]; + fread (&testMagic, sizeof (magic), 1, f); + if (strcmp (testMagic, magic)) + { + printf ("File corrupt\n"); + return FALSE; + } + + char productString[64]; + char vendorString[64]; + char effectName[64]; + char testString[64]; + unsigned length; + int success; + + fread (&length, sizeof (unsigned), 1, f); + length = htonl (length); + fread (productString, length, 1, f); + productString[length] = 0; + printf ("Product string: %s\n", productString); + + success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, testString, 0); + + if (success == 1) + { + if (strcmp (testString, productString) != 0) + { + printf ("Product string mismatch! Plugin has: %s\n", testString); + fclose (f); + return FALSE; + } + } + else if (length != 0) + { + printf ("Product string mismatch! Plugin has none.\n", testString); + fclose (f); + return FALSE; + } + + fread (&length, sizeof (unsigned), 1, f); + length = htonl (length); + fread (effectName, length, 1, f); + effectName[length] = 0; + printf ("Effect name: %s\n", effectName); + + success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, testString, 0); + + if(success == 1) + { + if(strcmp(testString, effectName)!= 0) + { + printf ("Effect name mismatch! Plugin has: %s\n", testString); + fclose (f); + return FALSE; + } + } + else if(length != 0) + { + printf ("Effect name mismatch! Plugin has none.\n", testString); + fclose (f); + return FALSE; + } + + fread (&length, sizeof (unsigned), 1, f); + length = htonl (length); + fread (vendorString, length, 1, f); + vendorString[length] = 0; + + printf ("Vendor string: %s\n", vendorString); + + success = vstfx_call_dispatcher(vstfx, effGetVendorString, 0, 0, testString, 0); + if(success == 1) + { + if (strcmp(testString, vendorString)!= 0) + { + printf ("Vendor string mismatch! Plugin has: %s\n", testString); + fclose (f); + return FALSE; + } + } + else if(length != 0) + { + printf ("Vendor string mismatch! Plugin has none.\n", testString); + fclose (f); + return FALSE; + } + + int numParam; + unsigned i; + fread (&numParam, sizeof (int), 1, f); + numParam = htonl (numParam); + + for (i = 0; i < numParam; ++i) + { + float val; + fread (&val, sizeof (float), 1, f); + val = htonf (val); + + pthread_mutex_lock(&vstfx->lock ); + vstfx->plugin->setParameter(vstfx->plugin, i, val); + pthread_mutex_unlock(&vstfx->lock ); + } + + int bytelen; + + fread (&bytelen, sizeof (int), 1, f); + bytelen = htonl (bytelen); + + if (bytelen) + { + char * buf = malloc (bytelen); + fread (buf, bytelen, 1, f); + + vstfx_call_dispatcher(vstfx, 24, 0, bytelen, buf, 0); + free (buf); + } + } + else + { + printf ("Could not open state file\n"); + return FALSE; + } + return TRUE; + +} +#endif + +int vstfx_save_state (VSTFX* vstfx, char * filename) +{ + FILE* f = fopen (filename, "wb"); + if (f) + { + int bytelen; + int numParams = vstfx->plugin->numParams; + int i; + char productString[64]; + char effectName[64]; + char vendorString[64]; + int success; + + /* write header */ + + fprintf(f, "<plugin_state>\n"); + + success = vstfx_call_dispatcher(vstfx, effGetProductString, 0, 0, productString, 0); + + if(success == 1) + { + fprintf (f, " <check field=\"productString\" value=\"%s\"/>\n", productString); + } + else + { + printf ("No product string\n"); + } + + success = vstfx_call_dispatcher(vstfx, effGetEffectName, 0, 0, effectName, 0); + + if(success == 1) + { + fprintf (f, " <check field=\"effectName\" value=\"%s\"/>\n", effectName); + printf ("Effect name: %s\n", effectName); + } + else + { + printf ("No effect name\n"); + } + + success = vstfx_call_dispatcher(vstfx, effGetVendorString, 0, 0, vendorString, 0); + + if( success == 1 ) + { + fprintf (f, " <check field=\"vendorString\" value=\"%s\"/>\n", vendorString); + printf ("Vendor string: %s\n", vendorString); + } + else + { + printf ("No vendor string\n"); + } + + + if(vstfx->plugin->flags & 32 ) + { + numParams = 0; + } + + for(i=0; i < numParams; i++) + { + float val; + + pthread_mutex_lock( &vstfx->lock ); + val = vstfx->plugin->getParameter(vstfx->plugin, i ); + pthread_mutex_unlock( &vstfx->lock ); + fprintf( f, " <param index=\"%d\" value=\"%f\"/>\n", i, val ); + } + + if(vstfx->plugin->flags & 32 ) + { + printf( "getting chunk...\n" ); + void * chunk; + bytelen = vstfx_call_dispatcher(vstfx, 23, 0, 0, &chunk, 0 ); + printf( "got tha chunk..\n" ); + if( bytelen ) + { + if( bytelen < 0 ) + { + printf( "Chunke len < 0 !!! Not saving chunk.\n" ); + } + else + { + //char *encoded = g_base64_encode( chunk, bytelen ); + //fprintf( f, " <chunk size=\"%d\">\n %s\n </chunk>\n", bytelen, encoded ); + //g_free( encoded ); + } + } + } + + fprintf( f, "</plugin_state>\n" ); + fclose( f ); + } + else + { + printf ("Could not open state file\n"); + return FALSE; + } + return TRUE; +} + diff --git a/libs/ardour/wscript b/libs/ardour/wscript index e4d435c0a9..b347e73e7e 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -396,6 +396,11 @@ def build(bld): obj.source += [ 'vst_plugin.cc', 'session_vst.cc' ] obj.includes += [ '../fst' ] obj.defines += [ 'VST_SUPPORT' ] + + if bld.env['LXVST_SUPPORT']: + obj.source += [ 'lxvst_plugin.cc', 'session_lxvst.cc', 'vstfx.cc', 'vstfxwin.cc', 'vstfxinfofile.cc' ] + obj.defines += [ 'LXVST_SUPPORT' ] + if bld.env['COREAUDIO']: obj.source += [ 'coreaudiosource.cc', 'caimportable.cc' ] |