From 4d3e06f29d4f97b362fb1f69fde977cefd16cf9f Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 22 Jun 2006 00:22:16 +0000 Subject: [PATCH] more details for VST support git-svn-id: svn://localhost/ardour2/trunk@630 d708f5d6-7413-0410-9779-e7cbd77b26cf --- SConstruct | 16 ++ libs/fst/SConscript | 27 ++ libs/fst/fst.c | 24 ++ libs/fst/fst.h | 111 ++++++++ libs/fst/fstinfofile.c | 266 +++++++++++++++++++ libs/fst/jackvst.h | 35 +++ libs/fst/vsti.c | 181 +++++++++++++ libs/fst/vstwin.c | 563 +++++++++++++++++++++++++++++++++++++++++ libs/pbd3/error.cc | 7 + 9 files changed, 1230 insertions(+) create mode 100644 libs/fst/SConscript create mode 100644 libs/fst/fst.c create mode 100644 libs/fst/fst.h create mode 100644 libs/fst/fstinfofile.c create mode 100644 libs/fst/jackvst.h create mode 100644 libs/fst/vsti.c create mode 100644 libs/fst/vstwin.c create mode 100644 libs/pbd3/error.cc diff --git a/SConstruct b/SConstruct index 17b3b9041d..94da428f2c 100644 --- a/SConstruct +++ b/SConstruct @@ -8,6 +8,7 @@ import glob import errno import time import platform +import string from sets import Set import SCons.Node.FS @@ -345,6 +346,21 @@ tarball_bld = Builder (action = tarballer, env.Append (BUILDERS = {'Distribute' : dist_bld}) env.Append (BUILDERS = {'Tarball' : tarball_bld}) +# +# Make sure they know what they are doing +# + +if env['VST']: + sys.stdout.write ("Are you building Ardour for personal use (rather than distributiont to others)? [no]: ") + answer = sys.stdin.readline () + answer = string.strip (string.rstrip (answer)); + if answer != "yes" and answer != "y": + print 'You cannot build Ardour with VST support for distribution to others.\nIt is a violation of several different licenses. VST support disabled.' + env['VST'] = 0; + else: + print "OK, VST support will be enabled" + + # ---------------------------------------------------------------------- # Construction environment setup # ---------------------------------------------------------------------- diff --git a/libs/fst/SConscript b/libs/fst/SConscript new file mode 100644 index 0000000000..0499f11c61 --- /dev/null +++ b/libs/fst/SConscript @@ -0,0 +1,27 @@ +# -*- python -*- + +import os +import os.path +import glob + +fst_src = glob.glob('*.c') + +Import('env install_prefix') +fst = env.Copy(CC="winegcc") +fst.Append (CPPPATH=".") + +hackSDK = fst.Command('vst/aeffectx.h', 'vstsdk2.3/source/common/aeffectx.h', [ + Delete ('${TARGET.dir}'), + Copy ('${TARGET.dir}', '${SOURCE.dir}'), + "sed -i '/struct VstFileType\|struct VstFileSelect/,/};/d' $TARGET" +]) + +a = fst.Object ('fst', 'fst.c') +b = fst.Object ('fstinfofile', 'fstinfofile.c') +c = fst.Object ('vstwin', 'vstwin.c') +d = fst.Object ('vsti', 'vsti.c') + +Default([hackSDK,a,b,c,d]) + +env.Alias('tarball', env.Distribute (env['DISTTREE'], fst_src + ['SConscript'] )) + diff --git a/libs/fst/fst.c b/libs/fst/fst.c new file mode 100644 index 0000000000..cbee5de52a --- /dev/null +++ b/libs/fst/fst.c @@ -0,0 +1,24 @@ +#include +#include + +#include "fst.h" + +void +fst_error (const char *fmt, ...) +{ + va_list ap; + char buffer[512]; + + va_start (ap, fmt); + vsnprintf (buffer, sizeof(buffer), fmt, ap); + fst_error_callback (buffer); + va_end (ap); +} + +void +default_fst_error_callback (const char *desc) +{ + fprintf(stderr, "%s\n", desc); +} + +void (*fst_error_callback)(const char *desc) = &default_fst_error_callback; diff --git a/libs/fst/fst.h b/libs/fst/fst.h new file mode 100644 index 0000000000..61dc09a2a1 --- /dev/null +++ b/libs/fst/fst.h @@ -0,0 +1,111 @@ +#ifndef __fst_fst_h__ +#define __fst_fst_h__ + +#include +#include +#include + +/** + * Display FST error message. + * + * Set via fst_set_error_function(), otherwise a FST-provided + * default will print @a msg (plus a newline) to stderr. + * + * @param msg error message text (no newline at end). + */ +extern void (*fst_error_callback)(const char *msg); + +/** + * Set the @ref fst_error_callback for error message display. + * + * The FST library provides two built-in callbacks for this purpose: + * default_fst_error_callback() and silent_fst_error_callback(). + */ +void fst_set_error_function (void (*func)(const char *)); + +#include + +typedef struct _FST FST; +typedef struct _FSTHandle FSTHandle; +typedef struct _FSTInfo FSTInfo; + +struct _FSTInfo +{ + char *name; + int UniqueID; + char *Category; + + int numInputs; + int numOutputs; + int numParams; + + int wantMidi; + int wantEvents; + int hasEditor; + int canProcessReplacing; // what do we need this for ? + + // i think we should save the parameter Info Stuff soon. + // struct VstParameterInfo *infos; + char **ParamNames; + char **ParamLabels; +}; + +struct _FSTHandle +{ + void* dll; + char* name; + char* nameptr; /* ptr returned from strdup() etc. */ + AEffect* (*main_entry)(audioMasterCallback); + + int plugincnt; +}; + +struct _FST +{ + AEffect* plugin; + void* window; /* win32 HWND */ + int xid; /* X11 XWindow */ + FSTHandle* handle; + int width; + int height; + int destroy; + + struct _FST* next; + + pthread_mutex_t lock; + pthread_cond_t window_status_change; + int been_activated; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +extern int fst_init (void (*sighandler)(int,siginfo_t*,void*)); + +extern FSTHandle* fst_load (const char*); +extern int fst_unload (FSTHandle*); + +extern FST* fst_instantiate (FSTHandle*, audioMasterCallback amc, void* userptr); +extern void fst_close (FST*); + +extern void fst_event_loop_remove_plugin (FST* fst); +extern void fst_event_loop_add_plugin (FST* fst); + +extern int fst_run_editor (FST*); +extern void fst_destroy_editor (FST*); +extern int fst_get_XID (FST*); + +extern int fst_adopt_thread (); +extern void* fst_get_teb(); + +extern void fst_signal_handler (int sig, siginfo_t* info, void* context); + +extern FSTInfo *fst_get_info( char *dllpathname ); +extern void fst_free_info( FSTInfo *info ); + +#ifdef __cplusplus +} +#endif + +#endif /* __fst_fst_h__ */ diff --git a/libs/fst/fstinfofile.c b/libs/fst/fstinfofile.c new file mode 100644 index 0000000000..9560053c7b --- /dev/null +++ b/libs/fst/fstinfofile.c @@ -0,0 +1,266 @@ + +#include "fst.h" +#include "vst/aeffectx.h" + +#include +#include +#include + +#include +#include +#include + + +#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 FSTInfo *load_fst_info_file( char *filename ) { + + FSTInfo *info = (FSTInfo *) malloc( sizeof( FSTInfo ) ); + 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( 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; inumParams; 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; inumParams; 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_fst_info_file( FSTInfo *info, char *filename ) { + + FILE *fp; + int i; + + + if( info == NULL ) { + fst_error( "info is NULL\n" ); + return TRUE; + } + + fp = fopen( filename, "w" ); + + if( fp == NULL ) { + fst_error( "Cant write info file %s\n", filename ); + return TRUE; + } + + fprintf( fp, "%s\n", info->name ); + 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; inumParams; i++ ) { + fprintf( fp, "%s\n", info->ParamNames[i] ); + } + for( i=0; inumParams; i++ ) { + fprintf( fp, "%s\n", info->ParamLabels[i] ); + } + + + fclose( fp ); + + return FALSE; +} + +static char *fst_dllpath_to_infopath( char *dllpath ) { + char *retval; + if( strstr( dllpath, ".dll" ) == NULL ) return NULL; + + retval = strdup( dllpath ); + sprintf( retval + strlen(retval) - 4, ".fst" ); + return retval; +} + +static int fst_info_file_is_valid( char *dllpath ) { + struct stat dllstat, fststat; + char *fstpath = fst_dllpath_to_infopath( dllpath ); + + if( !fstpath ) return FALSE; + + if( stat( dllpath, &dllstat ) ){ fst_error( "dll path %s invalid\n", dllpath ); return TRUE; } + if( stat( fstpath, &fststat ) ) return FALSE; + + free( fstpath ); + if( dllstat.st_mtime > fststat.st_mtime ) + return FALSE; + else + return TRUE; +} + +static int fst_can_midi( FST *fst ) { + AEffect *plugin = fst->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 FSTInfo *fst_info_from_plugin( FST *fst ) { + FSTInfo *info = (FSTInfo *) malloc( sizeof( FSTInfo ) ); + AEffect *plugin; + int i; + + if( ! fst ) { + fst_error( "fst is NULL\n" ); + return NULL; + } + + if( ! info ) return NULL; + + plugin = fst->plugin; + + + info->name = strdup(fst->handle->name ); + info->UniqueID = plugin->uniqueID; + info->Category = strdup( "None" ); // FIXME: + info->numInputs = plugin->numInputs; + info->numOutputs = plugin->numOutputs; + info->numParams = plugin->numParams; + info->wantMidi = fst_can_midi( fst ); + 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; inumParams; i++ ) { + char name[20]; + char label[9]; + plugin->dispatcher (plugin, + effGetParamName, + i, 0, name, 0); + + plugin->dispatcher (plugin, + effGetParamLabel, + i, 0, label, 0); + + info->ParamNames[i] = strdup( name ); + info->ParamLabels[i] = strdup( label ); + } + return info; +} + +// most simple one :) could be sufficient.... +static long simple_master_callback( AEffect *fx, long opcode, long index, long value, void *ptr, float opt ) { + if( opcode == audioMasterVersion ) + return 2; + else + return 0; +} + +FSTInfo *fst_get_info( char *dllpath ) { + + if( fst_info_file_is_valid( dllpath ) ) { + FSTInfo *info; + char *fstpath = fst_dllpath_to_infopath( dllpath ); + + info = load_fst_info_file( fstpath ); + free( fstpath ); + return info; + + } else { + + FSTHandle *h; + FST *fst; + FSTInfo *info; + char *fstpath; + + if( !(h = fst_load( dllpath )) ) return NULL; + if( !(fst = fst_instantiate( h, simple_master_callback, NULL )) ) { + fst_unload( h ); + fst_error( "instantiate failed\n" ); + return NULL; + } + fstpath = fst_dllpath_to_infopath( dllpath ); + if( !fstpath ) { + fst_close( fst ); + fst_unload( h ); + fst_error( "get fst filename failed\n" ); + return NULL; + } + info = fst_info_from_plugin( fst ); + save_fst_info_file( info, fstpath ); + + free( fstpath ); + fst_close( fst ); + fst_unload( h ); + return info; + } +} + +void fst_free_info( FSTInfo *info ) { + + int i; + + for( i=0; inumParams; i++ ) { + free( info->ParamNames[i] ); + free( info->ParamLabels[i] ); + } + free( info->name ); + free( info->Category ); + free( info ); +} + + diff --git a/libs/fst/jackvst.h b/libs/fst/jackvst.h new file mode 100644 index 0000000000..abb9e22e12 --- /dev/null +++ b/libs/fst/jackvst.h @@ -0,0 +1,35 @@ +#ifndef __jack_vst_h__ +#define __jack_vst_h__ + +#include +#include +#include +#include +#include +#include + +typedef struct _JackVST JackVST; + +struct _JackVST { + jack_client_t *client; + FSTHandle* handle; + FST* fst; + float **ins; + float **outs; + jack_port_t **inports; + jack_port_t **outports; + void* userdata; + int bypassed; + int muted; + + int resume_called; + /* For VST/i support */ + + pthread_t midi_thread; + snd_seq_t* seq; + int midiquit; + jack_ringbuffer_t* event_queue; + struct VstEvents* events; +}; + +#endif /* __jack_vst_h__ */ diff --git a/libs/fst/vsti.c b/libs/fst/vsti.c new file mode 100644 index 0000000000..f6d8725ddf --- /dev/null +++ b/libs/fst/vsti.c @@ -0,0 +1,181 @@ +/* + * VST instrument support + * + * Derived from code that was marked: + * Copyright (C) Kjetil S. Matheussen 2004 (k.s.matheussen@notam02.no) + * Alsa-seq midi-code made by looking at the jack-rack source made by Bob Ham. + * + * 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. + * + * $Id: vsti.c,v 1.2 2004/04/07 01:56:23 pauld Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +snd_seq_t * +create_sequencer (const char* client_name, bool isinput) +{ + snd_seq_t * seq; + int err; + + if ((err = snd_seq_open (&seq, "default", SND_SEQ_OPEN_DUPLEX, 0)) != 0) { + fst_error ("Could not open ALSA sequencer, aborting\n\n%s\n\n" + "Make sure you have configure ALSA properly and that\n" + "/proc/asound/seq/clients exists and contains relevant\n" + "devices (%s).", + snd_strerror (err)); + return NULL; + } + + snd_seq_set_client_name (seq, client_name); + + if ((err = snd_seq_create_simple_port (seq, isinput? "Input" : "Output", + (isinput? SND_SEQ_PORT_CAP_WRITE: SND_SEQ_PORT_CAP_READ)| SND_SEQ_PORT_CAP_DUPLEX | + SND_SEQ_PORT_CAP_SUBS_READ|SND_SEQ_PORT_CAP_SUBS_WRITE, + SND_SEQ_PORT_TYPE_APPLICATION|SND_SEQ_PORT_TYPE_SPECIFIC)) != 0) { + fst_error ("Could not create ALSA port: %s", snd_strerror (err)); + snd_seq_close(seq); + return NULL; + } + + return seq; +} + +static void +queue_midi (JackVST *jvst, int val1, int val2, int val3) +{ + struct VstMidiEvent *pevent; + jack_ringbuffer_data_t vec[2]; + + jack_ringbuffer_get_write_vector (jvst->event_queue, vec); + + if (vec[0].len < sizeof (struct VstMidiEvent)) { + fst_error ("event queue has no write space"); + return; + } + + pevent = (struct VstMidiEevent *) vec[0].buf; + + // printf("note: %d\n",note); + + pevent->type = kVstMidiType; + pevent->byteSize = 24; + pevent->deltaFrames = 0; + pevent->flags = 0; + pevent->detune = 0; + pevent->noteLength = 0; + pevent->noteOffset = 0; + pevent->reserved1 = 0; + pevent->reserved2 = 0; + pevent->noteOffVelocity = 0; + pevent->midiData[0] = val1; + pevent->midiData[1] = val2; + pevent->midiData[2] = val3; + pevent->midiData[3] = 0; + + //printf("Sending: %x %x %x\n",val1,val2,val3); + + jack_ringbuffer_write_advance (jvst->event_queue, sizeof (struct VstMidiEvent)); +} + +void *midireceiver(void *arg) +{ + snd_seq_event_t *event; + JackVST *jvst = (JackVST* )arg; + int val; + + while (1) { + + snd_seq_event_input (jvst->seq, &event); + + if (jvst->midiquit) { + break; + } + + switch(event->type){ + case SND_SEQ_EVENT_NOTEON: + queue_midi(jvst,0x90+event->data.note.channel,event->data.note.note,event->data.note.velocity); + //printf("Noteon, channel: %d note: %d vol: %d\n",event->data.note.channel,event->data.note.note,event->data.note.velocity); + break; + case SND_SEQ_EVENT_NOTEOFF: + queue_midi(jvst,0x80+event->data.note.channel,event->data.note.note,0); + //printf("Noteoff, channel: %d note: %d vol: %d\n",event->data.note.channel,event->data.note.note,event->data.note.velocity); + break; + case SND_SEQ_EVENT_KEYPRESS: + //printf("Keypress, channel: %d note: %d vol: %d\n",event->data.note.channel,event->data.note.note,event->data.note.velocity); + queue_midi(jvst,0xa0+event->data.note.channel,event->data.note.note,event->data.note.velocity); + break; + case SND_SEQ_EVENT_CONTROLLER: + queue_midi(jvst,0xb0+event->data.control.channel,event->data.control.param,event->data.control.value); + //printf("Control: %d %d %d\n",event->data.control.channel,event->data.control.param,event->data.control.value); + break; + case SND_SEQ_EVENT_PITCHBEND: + val=event->data.control.value + 0x2000; + queue_midi(jvst,0xe0+event->data.control.channel,val&127,val>>7); + //printf("Pitch: %d %d %d\n",event->data.control.channel,event->data.control.param,event->data.control.value); + break; + case SND_SEQ_EVENT_CHANPRESS: + //printf("chanpress: %d %d %d\n",event->data.control.channel,event->data.control.param,event->data.control.value); + queue_midi(jvst,0xd0+event->data.control.channel,event->data.control.value,0); + break; + case SND_SEQ_EVENT_PGMCHANGE: + //printf("pgmchange: %d %d %d\n",event->data.control.channel,event->data.control.param,event->data.control.value); + queue_midi(jvst,0xc0+event->data.control.channel,event->data.control.value,0); + break; + default: + //printf("Unknown type: %d\n",event->type); + break; + } + } + + return NULL; +} + +void stop_midireceiver (JackVST *jvst) +{ + int err; + snd_seq_event_t event; + snd_seq_t *seq2 = create_sequencer ("jfstquit", true); + + jvst->midiquit = 1; + + snd_seq_connect_to (seq2, 0, snd_seq_client_id (jvst->seq),0); + snd_seq_ev_clear (&event); + snd_seq_ev_set_direct (&event); + snd_seq_ev_set_subs (&event); + snd_seq_ev_set_source (&event, 0); + snd_seq_ev_set_controller (&event,1,0x80,50); + + if ((err = snd_seq_event_output (seq2, &event)) < 0) { + fst_error ("cannot send stop event to midi thread: %s\n", + snd_strerror (err)); + } + + snd_seq_drain_output (seq2); + snd_seq_close (seq2); + pthread_join (jvst->midi_thread,NULL); + snd_seq_close (jvst->seq); +} + + + diff --git a/libs/fst/vstwin.c b/libs/fst/vstwin.c new file mode 100644 index 0000000000..18d35546a2 --- /dev/null +++ b/libs/fst/vstwin.c @@ -0,0 +1,563 @@ +#include +#include +#include +#include +#include +#include +#include + +//#include +//#include +//#include +//#include + +#include "fst.h" + + +struct ERect{ + short top; + short left; + short bottom; + short right; +}; + +static pthread_mutex_t plugin_mutex; +static FST* fst_first = NULL; + +DWORD gui_thread_id = 0; + + + + + + + +/* Define to a macro to generate an assembly function directive */ +#define __ASM_FUNC(name) ".type " __ASM_NAME(name) ",@function" + +/* Define to a macro to generate an assembly name from a C symbol */ +#define __ASM_NAME(name) name + +# define __ASM_GLOBAL_FUNC(name,code) \ + __asm__( ".align 4\n\t" \ + ".globl " __ASM_NAME(#name) "\n\t" \ + __ASM_FUNC(#name) "\n" \ + __ASM_NAME(#name) ":\n\t" \ + code ); + +__ASM_GLOBAL_FUNC( fst_get_teb, ".byte 0x64\n\tmovl 0x18,%eax\n\tret" ); + + + +#define DELAYED_WINDOW + +static LRESULT WINAPI +my_window_proc (HWND w, UINT msg, WPARAM wp, LPARAM lp) +{ + FST* fst; + LRESULT result; + +// if (msg != WM_TIMER) { +// fst_error ("window callback handler, msg = 0x%x win=%p\n", msg, w); +// } + + switch (msg) { + case WM_KEYUP: + case WM_KEYDOWN: + printf( "got a key\n" ); + break; + + case WM_CLOSE: + printf("wtf.\n" ); + PostQuitMessage (0); + case WM_DESTROY: + case WM_NCDESTROY: + /* we should never get these */ + //return 0; + break; + + + case WM_PAINT: +// case WM_ACTIVATE: +#ifdef DELAYED_WINDOW + //if (wp & WA_ACTIVE) { + if ((fst = GetPropA (w, "fst_ptr")) != NULL) { + if (fst->window && !fst->been_activated) { + fst->been_activated = TRUE; +// { +// XSetWindowAttributes attr; +// +// attr.override_redirect = TRUE; +//// XChangeWindowAttributes( thread_display(), fst->xid, CWOverrideRedirect, &attr ); +// } + pthread_cond_signal (&fst->window_status_change); + pthread_mutex_unlock (&fst->lock); + } + } +// } +#endif + break; + + default: + break; + } + + return DefWindowProcA (w, msg, wp, lp ); +} + +static FST* +fst_new () +{ + FST* fst = (FST*) calloc (1, sizeof (FST)); + + pthread_mutex_init (&fst->lock, NULL); + pthread_cond_init (&fst->window_status_change, NULL); + + return fst; +} + +static FSTHandle* +fst_handle_new () +{ + FSTHandle* fst = (FSTHandle*) calloc (1, sizeof (FSTHandle)); + return fst; +} + +#ifdef HAVE_TLS +static __thread int ejmpbuf_valid = FALSE; +static __thread jmp_buf ejmpbuf; +#else +static pthread_key_t ejmpbuf_valid_key; +static pthread_key_t ejmpbuf_key; +#endif + +void debreak( void ) { printf( "debreak\n" ); } + + +int +fst_init (void (*sighandler)(int,siginfo_t*,void*)) +{ + //SharedWineInit (sighandler); + wine_shared_premain(); + + return 0; +} + +DWORD WINAPI gui_event_loop (LPVOID param) +{ + MSG msg; + FST* fst; + char c; + HMODULE hInst; + HWND window; + + gui_thread_id = GetCurrentThreadId (); + + /* create a dummy window for timer events */ + + if ((hInst = GetModuleHandleA (NULL)) == NULL) { + fst_error ("can't get module handle"); + return 1; + } + + if ((window = CreateWindowExA (0, "FST", "dummy", + WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, + hInst, + NULL )) == NULL) { + fst_error ("cannot create dummy timer window"); + } + + if (!SetTimer (window, 1000, 100, NULL)) { + fst_error ("cannot set timer on dummy window"); + } + + while (GetMessageA (&msg, NULL, 0,0)) { + if( msg.message == WM_KEYDOWN ) debreak(); + TranslateMessage( &msg ); + DispatchMessageA (&msg); + + /* handle window creation requests, destroy requests, + and run idle callbacks + */ + + + if( msg.message == WM_TIMER ) { + pthread_mutex_lock (&plugin_mutex); +again: + for (fst = fst_first; fst; fst = fst->next) { + + if (fst->destroy) { + if (fst->window) { + fst->plugin->dispatcher( fst->plugin, effEditClose, 0, 0, NULL, 0.0 ); + CloseWindow (fst->window); + fst->window = NULL; + fst->destroy = FALSE; + } + fst_event_loop_remove_plugin (fst); + fst->been_activated = FALSE; + pthread_mutex_lock (&fst->lock); + pthread_cond_signal (&fst->window_status_change); + pthread_mutex_unlock (&fst->lock); + goto again; + } + + if (fst->window == NULL) { + pthread_mutex_lock (&fst->lock); + if (fst_create_editor (fst)) { + fst_error ("cannot create editor for plugin %s", fst->handle->name); + fst_event_loop_remove_plugin (fst); + pthread_cond_signal (&fst->window_status_change); + pthread_mutex_unlock (&fst->lock); + goto again; + } + /* condition/unlock handled when we receive WM_ACTIVATE */ + } + + fst->plugin->dispatcher (fst->plugin, effEditIdle, 0, 0, NULL, 0); + } + pthread_mutex_unlock (&plugin_mutex); + } + } + printf( "quit........\n" ); + exit(0); + gtk_main_quit(); +} + +void +fst_set_focus (FST* fst) +{ + if (fst->window) { + SetFocus (fst->window); + } +} + +int +wine_shared_premain () +{ + WNDCLASSA wc; + HMODULE hInst; + + if ((hInst = GetModuleHandleA (NULL)) == NULL) { + fst_error ("can't get module handle"); + return -1; + } + wc.style = 0; + wc.lpfnWndProc = my_window_proc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInst; + wc.hIcon = LoadIconA( hInst, "FST"); + wc.hCursor = LoadCursorA( NULL, IDI_APPLICATION ); + wc.hbrBackground = GetStockObject( BLACK_BRUSH ); + wc.lpszMenuName = "MENU_FST"; + wc.lpszClassName = "FST"; + + if (!RegisterClassA(&wc)){ + return 1; + } + + if (CreateThread (NULL, 0, gui_event_loop, NULL, 0, NULL) == NULL) { + fst_error ("could not create new thread proxy"); + return -1; + } + + return 0; +} + +int +fst_run_editor (FST* fst) +{ + pthread_mutex_lock (&plugin_mutex); + + if (fst_first == NULL) { + fst_first = fst; + } else { + FST* p = fst_first; + while (p->next) { + p = p->next; + } + p->next = fst; + } + + printf( "gui_thread_id = %d\n", gui_thread_id ); + if (!PostThreadMessageA (gui_thread_id, WM_USER, 0, 0)) { + fst_error ("could not post message to gui thread"); + return -1; + } + + pthread_mutex_unlock (&plugin_mutex); + + /* wait for the plugin editor window to be created (or not) */ + + pthread_mutex_lock (&fst->lock); + if (!fst->window) { + pthread_cond_wait (&fst->window_status_change, &fst->lock); + } + pthread_mutex_unlock (&fst->lock); + + if (!fst->window) { + fst_error ("no window created for VST plugin editor"); + return -1; + } + + return 0; +} + +int +fst_create_editor (FST* fst) +{ + HMODULE hInst; + char class[20]; + HWND window; + + /* "guard point" to trap errors that occur during plugin loading */ + + /* Note: fst->lock is held while this function is called */ + + if (!(fst->plugin->flags & effFlagsHasEditor)) { + fst_error ("Plugin \"%s\" has no editor", fst->handle->name); + return -1; + } + + if ((hInst = GetModuleHandleA (NULL)) == NULL) { + fst_error ("can't get module handle"); + return 1; + } + +// if ((window = CreateWindowExA (WS_EX_TOOLWINDOW | WS_EX_TRAYWINDOW, "FST", fst->handle->name, + if ((window = CreateWindowExA (0, "FST", fst->handle->name, + (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX), + 0, 0, 1, 1, + NULL, NULL, + hInst, + NULL)) == NULL) { + fst_error ("cannot create editor window"); + return 1; + } + + if (!SetPropA (window, "fst_ptr", fst)) { + fst_error ("cannot set fst_ptr on window"); + } + + fst->window = window; + fst->xid = (int) GetPropA (window, "__wine_x11_whole_window"); + +// XChangeWindowAttributes( XOpenDisplay(NULL), fst->xid, CWOverrideRedirect, ~0 ); + + +#ifdef DELAYED_WINDOW + { + struct ERect* er; + + ShowWindow (fst->window, SW_SHOW); + + fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->window, 0 ); + fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 ); + + fst->width = er->right-er->left; + fst->height = er->bottom-er->top; + + SetWindowPos (fst->window, 0, 0, 0, er->right-er->left+8, er->bottom-er->top+26, SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOZORDER); + } +#else + + pthread_cond_signal (&fst->window_status_change); + pthread_mutex_unlock (&fst->lock); + +#endif + return 0; +} + +void +fst_show_editor (FST* fst) +{ +#ifndef DELAYED_WINDOW + struct ERect* er; + + fst->plugin->dispatcher (fst->plugin, effEditOpen, 0, 0, fst->window, 0 ); + fst->plugin->dispatcher (fst->plugin, effEditGetRect, 0, 0, &er, 0 ); + fst->width = er->right-er->left; + fst->height = er->bottom-er->top; + + SetWindowPos (window, 0, 0, 0, er->right-er->left+8, er->bottom-er->top+26, SWP_NOMOVE | SWP_NOZORDER); + *ejmpbuf_valid = FALSE; +#endif +} + +void +fst_destroy_editor (FST* fst) +{ + FST* p; + FST* prev; + + pthread_mutex_lock (&fst->lock); + if (fst->window) { + fst->destroy = TRUE; + if (!PostThreadMessageA (gui_thread_id, WM_USER, 0, 0)) { + fst_error ("could not post message to gui thread"); + } + pthread_cond_wait (&fst->window_status_change, &fst->lock); + + } + pthread_mutex_unlock (&fst->lock); +} + +void +fst_event_loop_remove_plugin (FST* fst) +{ + FST* p; + FST* prev; + + for (p = fst_first, prev = NULL; p->next; prev = p, p = p->next) { + if (p == fst) { + if (prev) { + prev->next = p->next; + } + } + } + + if (fst_first == fst) { + fst_first = fst_first->next; + } + +} + +FSTHandle* +fst_load (const char *path) +{ + char* buf, *buf2; + FSTHandle* fhandle; + char* period; + + fhandle = fst_handle_new (); + + // XXX: Would be nice to find the correct call for this. + // if the user does not configure Z: to be / we are doomed :( + + if (strstr (path, ".dll") == NULL) { + + buf = (char *) malloc (strlen (path) + 7); + + if( path[0] == '/' ) { + sprintf (buf, "Z:%s.dll", path); + } else { + sprintf (buf, "%s.dll", path); + } + + fhandle->nameptr = strdup (path); + + } else { + + buf = (char *) malloc (strlen (path) + 3); + + if( path[0] == '/' ) { + sprintf (buf, "Z:%s", path); + } else { + sprintf (buf, "%s", path); + } + + fhandle->nameptr = strdup (path); + } + + fhandle->name = basename (fhandle->nameptr); + + /* strip off .dll */ + + if ((period = strrchr (fhandle->name, '.')) != NULL) { + *period = '\0'; + } + + if ((fhandle->dll = LoadLibraryA (buf)) == NULL) { + fst_unload (fhandle); + return NULL; + } + + if ((fhandle->main_entry = GetProcAddress (fhandle->dll, "main")) == NULL) { + fst_unload (fhandle); + return NULL; + } + + return fhandle; +} + +int +fst_unload (FSTHandle* fhandle) +{ + if (fhandle->plugincnt) { + return -1; + } + + if (fhandle->dll) { + FreeLibrary (fhandle->dll); + fhandle->dll = NULL; + } + + if (fhandle->nameptr) { + free (fhandle->nameptr); + fhandle->name = NULL; + } + + free (fhandle); + return 0; +} + +FST* +fst_instantiate (FSTHandle* fhandle, audioMasterCallback amc, void* userptr) +{ + FST* fst = fst_new (); + + if( fhandle == NULL ) { + fst_error( "the handle was NULL\n" ); + return NULL; + } + + if ((fst->plugin = fhandle->main_entry (amc)) == NULL) { + fst_error ("%s could not be instantiated\n", fhandle->name); + free (fst); + return NULL; + } + + fst->handle = fhandle; + fst->plugin->user = userptr; + + if (fst->plugin->magic != kEffectMagic) { + fst_error ("%s is not a VST plugin\n", fhandle->name); + free (fst); + return NULL; + } + + fst->plugin->dispatcher (fst->plugin, effOpen, 0, 0, 0, 0); + //fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0); + + fst->handle->plugincnt++; + + return fst; +} + +void +fst_close (FST* fst) +{ + fst_destroy_editor (fst); + + fst->plugin->dispatcher (fst->plugin, effMainsChanged, 0, 0, NULL, 0); + fst->plugin->dispatcher (fst->plugin, effClose, 0, 0, 0, 0); + + if (fst->handle->plugincnt) { + --fst->handle->plugincnt; + } +} + +int +fst_get_XID (FST* fst) +{ + return fst->xid; +} + +int +fst_adopt_thread () +{ + return 0; +} diff --git a/libs/pbd3/error.cc b/libs/pbd3/error.cc new file mode 100644 index 0000000000..a6f8fb7f8f --- /dev/null +++ b/libs/pbd3/error.cc @@ -0,0 +1,7 @@ +#include + +Transmitter PBD::error (Transmitter::Error); +Transmitter PBD::info (Transmitter::Info); +Transmitter PBD::fatal (Transmitter::Fatal); +Transmitter PBD::warning (Transmitter::Warning); + -- 2.30.2