From bfc2d535817939ab4a0de189a86a814e9133405e Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Fri, 13 Aug 2004 15:08:37 +0000 Subject: [PATCH] Initial revision --- Make.rules.in | 170 +++++++++ Makefile.in | 199 +++++++++++ README | 47 +++ configure.ac | 614 ++++++++++++++++++++++++++++++++ dssi-vst-scanner.cpp | 371 ++++++++++++++++++++ dssi-vst-server.cpp | 781 +++++++++++++++++++++++++++++++++++++++++ dssi-vst.cpp | 592 +++++++++++++++++++++++++++++++ dssi-vst_gui.cpp | 653 ++++++++++++++++++++++++++++++++++ rdwrops.cpp | 123 +++++++ rdwrops.h | 35 ++ remoteplugin.h | 59 ++++ remotepluginclient.cpp | 421 ++++++++++++++++++++++ remotepluginclient.h | 91 +++++ remotepluginserver.cpp | 381 ++++++++++++++++++++ remotepluginserver.h | 92 +++++ remotevstclient.cpp | 141 ++++++++ remotevstclient.h | 45 +++ vstsynth.cpp | 514 +++++++++++++++++++++++++++ wineapploader.in | 40 +++ 19 files changed, 5369 insertions(+) create mode 100644 Make.rules.in create mode 100644 Makefile.in create mode 100644 README create mode 100644 configure.ac create mode 100644 dssi-vst-scanner.cpp create mode 100644 dssi-vst-server.cpp create mode 100644 dssi-vst.cpp create mode 100644 dssi-vst_gui.cpp create mode 100644 rdwrops.cpp create mode 100644 rdwrops.h create mode 100644 remoteplugin.h create mode 100644 remotepluginclient.cpp create mode 100644 remotepluginclient.h create mode 100644 remotepluginserver.cpp create mode 100644 remotepluginserver.h create mode 100644 remotevstclient.cpp create mode 100644 remotevstclient.h create mode 100644 vstsynth.cpp create mode 100644 wineapploader.in diff --git a/Make.rules.in b/Make.rules.in new file mode 100644 index 0000000..74df25a --- /dev/null +++ b/Make.rules.in @@ -0,0 +1,170 @@ +# Copyright 2000 Francois Gouget for CodeWeavers +# fgouget@codeweavers.com +# +# Global rules shared by all makefiles -*-Makefile-*- +# +# Each individual makefile must define the following variables: +# TOPOBJDIR : top-level object directory +# SRCDIR : source directory for this module +# +# Each individual makefile may define the following additional variables: +# +# SUBDIRS : subdirectories that contain a Makefile +# DLLS : WineLib libraries to be built +# EXES : WineLib executables to be built +# +# CEXTRA : extra c flags (e.g. '-Wall') +# CXXEXTRA : extra c++ flags (e.g. '-Wall') +# WRCEXTRA : extra wrc flags (e.g. '-p _SysRes') +# DEFINES : defines (e.g. -DSTRICT) +# INCLUDE_PATH : additional include path +# LIBRARY_PATH : additional library path +# LIBRARIES : additional Unix libraries to link with +# +# C_SRCS : C sources for the module +# CXX_SRCS : C++ sources for the module +# RC_SRCS : resource source files +# SPEC_SRCS : interface definition files + + +# Where is Wine + +WINE_INCLUDE_ROOT = @WINE_INCLUDE_ROOT@ +WINE_INCLUDE_PATH = @WINE_INCLUDE_PATH@ +WINE_LIBRARY_ROOT = @WINE_LIBRARY_ROOT@ +WINE_LIBRARY_PATH = @WINE_LIBRARY_PATH@ +WINE_DLL_ROOT = @WINE_DLL_ROOT@ +WINE_DLL_PATH = @WINE_DLL_PATH@ + +LDPATH = @LDPATH@ + +# Where are the MFC + +ATL_INCLUDE_ROOT = @ATL_INCLUDE_ROOT@ +ATL_INCLUDE_PATH = @ATL_INCLUDE_PATH@ +MFC_INCLUDE_ROOT = @MFC_INCLUDE_ROOT@ +MFC_INCLUDE_PATH = @MFC_INCLUDE_PATH@ +MFC_LIBRARY_ROOT = @MFC_LIBRARY_ROOT@ +MFC_LIBRARY_PATH = @MFC_LIBRARY_PATH@ + +# Global definitions and options + +GLOBAL_DEFINES = +GLOBAL_INCLUDE_PATH = +GLOBAL_DLL_PATH = +GLOBAL_DLLS = +GLOBAL_LIBRARY_PATH = +GLOBAL_LIBRARIES = + +# First some useful definitions + +SHELL = /bin/sh +CC = @CC@ +CPP = @CPP@ +CXX = @CXX@ +WRC = @WRC@ +CFLAGS = @CFLAGS@ $(CEXTRA) +CXXFLAGS = @CXXFLAGS@ $(CXXEXTRA) +WRCFLAGS = $(WRCEXTRA) +OPTIONS = @OPTIONS@ -D_REENTRANT +LIBS = @LIBS@ $(LIBRARY_PATH) +DIVINCL = $(GLOBAL_INCLUDE_PATH) -I$(SRCDIR) $(INCLUDE_PATH) $(WINE_INCLUDE_PATH) +ALLCFLAGS = $(DIVINCL) $(CFLAGS) $(GLOBAL_DEFINES) $(DEFINES) $(OPTIONS) +ALLCXXFLAGS=$(DIVINCL) $(CXXFLAGS) $(GLOBAL_DEFINES) $(DEFINES) $(OPTIONS) +ALL_DLL_PATH = $(DLL_PATH) $(GLOBAL_DLL_PATH) $(WINE_DLL_PATH) +ALL_LIBRARY_PATH = $(LIBRARY_PATH) $(GLOBAL_LIBRARY_PATH) $(WINE_LIBRARY_PATH) +WINE_LIBRARIES = -lwine -lwine_unicode -lwine_uuid +ALL_LIBRARIES = $(LIBRARIES:%=-l%) $(GLOBAL_LIBRARIES:%=-l%) $(WINE_LIBRARIES) +LDSHARED = @LDSHARED@ +LDXXSHARED= @LDXXSHARED@ +LDDLLFLAGS= @LDDLLFLAGS@ +STRIP = strip +STRIPFLAGS= --strip-unneeded +LN_S = @LN_S@ +RM = rm -f +MV = mv +MKDIR = mkdir -p +WINE = @WINE@ +WINEBUILD = @WINEBUILD@ +@SET_MAKE@ + +# Installation infos + +INSTALL = install +INSTALL_PROGRAM = $(INSTALL) +INSTALL_SCRIPT = $(INSTALL) +INSTALL_DATA = $(INSTALL) -m 644 +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +dlldir = @libdir@/wine + +prog_manext = 1 +conf_manext = 5 + +OBJS = $(C_SRCS:.c=.o) $(CXX_SRCS:.cpp=.o) \ + $(SPEC_SRCS:.spec=.spec.o) +CLEAN_FILES = *.dbg.c *.spec.c y.tab.c y.tab.h lex.yy.c \ + core *.orig *.rej \ + \\\#*\\\# *~ *% .\\\#* + +# Implicit rules + +.SUFFIXES: .cpp .rc .res .spec .spec.c .spec.o + +.c.o: + $(CC) -c $(ALLCFLAGS) -o $@ $< + +.cpp.o: + $(CXX) -c $(ALLCXXFLAGS) -o $@ $< + +.cxx.o: + $(CXX) -c $(ALLCXXFLAGS) -o $@ $< + +.rc.res: + $(LDPATH) $(WRC) $(WRCFLAGS) $(DIVINCL) -o $@ $< + +.PHONY: all install uninstall clean distclean depend dummy + +# 'all' target first in case the enclosing Makefile didn't define any target + +all: Makefile + +# Rules for makefile + +Makefile: Makefile.in $(TOPSRCDIR)/configure + @echo $@ is older than $?, please rerun $(TOPSRCDIR)/configure + @exit 1 + +# Rules for cleaning + +$(SUBDIRS:%=%/__clean__): dummy + cd `dirname $@` && $(MAKE) clean + +$(EXTRASUBDIRS:%=%/__clean__): dummy + -cd `dirname $@` && $(RM) $(CLEAN_FILES) + +clean:: $(SUBDIRS:%=%/__clean__) $(EXTRASUBDIRS:%=%/__clean__) + $(RM) $(CLEAN_FILES) $(RC_SRCS:.rc=.res) $(OBJS) + $(RM) $(DLLS:%=%.dbg.o) $(DLLS:%=%.spec.o) $(DLLS:%=%.so) + $(RM) $(EXES:%=%.dbg.o) $(EXES:%=%.spec.o) $(EXES:%=%.so) $(EXES:%.exe=%) + +# Rules for installing + +$(SUBDIRS:%=%/__install__): dummy + cd `dirname $@` && $(MAKE) install + +$(SUBDIRS:%=%/__uninstall__): dummy + cd `dirname $@` && $(MAKE) uninstall + +# Misc. rules + +$(SUBDIRS): dummy + @cd $@ && $(MAKE) + +dummy: + +# End of global rules diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..4a1a0d7 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,199 @@ + +### Generic autoconf variables + +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = . +SRCDIR = @srcdir@ +VPATH = @srcdir@ + +SUBDIRS = +DLLS = +EXES = dssi-vst-server.exe dssi-vst-scanner.exe dssi-vst_gui.exe + + +### Common settings + +DEFINES = +INCLUDE_PATH = -I/home/cannam/vst/vstsdk2.3/source/common +DLL_PATH = +LIBRARY_PATH = -L. +LIBRARIES = remoteplugin + + +### dssi-vst-server.exe sources and settings + +dssi-vst-server_exe_MODULE = dssi-vst-server.exe +dssi-vst-server_exe_APPMODE = gui +dssi-vst-server_exe_C_SRCS = +dssi-vst-server_exe_CXX_SRCS = dssi-vst-server.cpp +dssi-vst-server_exe_RC_SRCS = +dssi-vst-server_exe_SPEC_SRCS = +dssi-vst-server_exe_DLL_PATH = -L$(WINE_DLL_ROOT) +dssi-vst-server_exe_DLLS = advapi32 \ + comdlg32 \ + gdi32 \ + kernel32 \ + odbc32 \ + ole32 \ + oleaut32 \ + shell32 \ + user32 \ + winspool +dssi-vst-server_exe_LIBRARY_PATH= +dssi-vst-server_exe_LIBRARIES = +dssi-vst-server_exe_DEPENDS = + +dssi-vst-server_exe_OBJS = $(dssi-vst-server_exe_C_SRCS:.c=.o) \ + $(dssi-vst-server_exe_CXX_SRCS:.cpp=.o) \ + $(EXTRA_OBJS) + + +### dssi-vst_gui.exe sources and settings + +dssi-vst_gui_exe_MODULE = dssi-vst_gui.exe +dssi-vst_gui_exe_APPMODE = gui +dssi-vst_gui_exe_C_SRCS = +dssi-vst_gui_exe_CXX_SRCS = dssi-vst_gui.cpp +dssi-vst_gui_exe_RC_SRCS = +dssi-vst_gui_exe_SPEC_SRCS = +dssi-vst_gui_exe_DLL_PATH = -L$(WINE_DLL_ROOT) +dssi-vst_gui_exe_DLLS = advapi32 \ + comdlg32 \ + gdi32 \ + kernel32 \ + odbc32 \ + ole32 \ + oleaut32 \ + shell32 \ + user32 \ + winspool +dssi-vst_gui_exe_LIBRARY_PATH= +dssi-vst_gui_exe_LIBRARIES = lo +dssi-vst_gui_exe_DEPENDS = + +dssi-vst_gui_exe_OBJS = $(dssi-vst_gui_exe_C_SRCS:.c=.o) \ + $(dssi-vst_gui_exe_CXX_SRCS:.cpp=.o) \ + $(EXTRA_OBJS) + + +### dssi-vst-scanner.exe sources and settings + +dssi-vst-scanner_exe_MODULE = dssi-vst-scanner.exe +dssi-vst-scanner_exe_APPMODE = gui +dssi-vst-scanner_exe_C_SRCS = +dssi-vst-scanner_exe_CXX_SRCS = dssi-vst-scanner.cpp +dssi-vst-scanner_exe_RC_SRCS = +dssi-vst-scanner_exe_SPEC_SRCS = +dssi-vst-scanner_exe_DLL_PATH = -L$(WINE_DLL_ROOT) +dssi-vst-scanner_exe_DLLS = advapi32 \ + comdlg32 \ + gdi32 \ + kernel32 \ + odbc32 \ + ole32 \ + oleaut32 \ + shell32 \ + user32 \ + winspool +dssi-vst-scanner_exe_LIBRARY_PATH= +dssi-vst-scanner_exe_LIBRARIES = +dssi-vst-scanner_exe_DEPENDS = + +dssi-vst-scanner_exe_OBJS = $(dssi-vst-scanner_exe_C_SRCS:.c=.o) \ + $(dssi-vst-scanner_exe_CXX_SRCS:.cpp=.o) \ + $(EXTRA_OBJS) + + + +### Global source lists + +C_SRCS = $(dssi-vst-server_exe_C_SRCS) $(dssi-vst-scanner_exe_C_SRCS) $(dssi-vst_gui_exe_C_SRCS) +CXX_SRCS = $(dssi-vst-server_exe_CXX_SRCS) $(dssi-vst-scanner_exe_CXX_SRCS) $(dssi-vst_gui_exe_CXX_SRCS) +RC_SRCS = $(dssi-vst-server_exe_RC_SRCS) $(dssi-vst-scanner_exe_RC_SRCS) $(dssi-vst_gui_exe_RC_SRCS) +SPEC_SRCS = $(dssi-vst-server_exe_SPEC_SRCS) $(dssi-vst-scanner_exe_SPEC_SRCS) $(dssi-vst_gui_exe_SPEC_SRCS) + + + +### Generic autoconf targets + +all: wineapploader $(SUBDIRS) $(DLLS:%=%.so) $(EXES:%=%.so) vstsynth libremoteplugin.a dssi-vst.so + +wineapploader: wineapploader.in + sed -e 's,@bindir\@,$(bindir),g' -e 's,@winelibdir\@,.,g' $(SRCDIR)/wineapploader.in >$@ || $(RM) $@ + +@MAKE_RULES@ + +install:: + _list="$(SUBDIRS)"; for i in $$_list; do (cd $$i; $(MAKE) install) || exit 1; done + _list="$(EXES:%.exe=%)"; for i in $$_list; do $(INSTALL_SCRIPT) $$i $(bindir); done + _list="$(EXES:%=%.so) $(DLLS:%=%.so)"; for i in $$_list; do $(INSTALL_PROGRAM) $$i $(dlldir); done + +uninstall:: + _list="$(SUBDIRS)"; for i in $$_list; do (cd $$i; $(MAKE) uninstall) || exit 1; done + _list="$(EXES:%.exe=%)"; for i in $$_list; do $(RM) $(bindir)/$$i;done + _list="$(EXES:%=%.so) $(DLLS:%=%.so)"; for i in $$_list; do $(RM) $(dlldir)/$$i;done + +clean:: + $(RM) wineapploader + +distclean: clean + $(RM) config.* configure.lineno Make.rules + $(RM) -r autom4te.cache + find . -name Makefile -exec $(RM) {} \; + +### Target specific build rules + +$(dssi-vst-server_exe_MODULE).dbg.c: $(dssi-vst-server_exe_C_SRCS) $(dssi-vst-server_exe_CXX_SRCS) + $(LDPATH) $(WINEBUILD) -o $@ --debug -C$(SRCDIR) $(dssi-vst-server_exe_C_SRCS) $(dssi-vst-server_exe_CXX_SRCS) + +$(dssi-vst-server_exe_MODULE).spec.c: $(dssi-vst-server_exe_SPEC_SRCS) $(dssi-vst-server_exe_RC_SRCS:.rc=.res) $(dssi-vst-server_exe_OBJS) + $(LDPATH) $(WINEBUILD) -fPIC -o $@ --exe $(dssi-vst-server_exe_MODULE) -m$(dssi-vst-server_exe_APPMODE) $(dssi-vst-server_exe_SPEC_SRCS:%=--spec %) $(dssi-vst-server_exe_RC_SRCS:%.rc=%.res) $(dssi-vst-server_exe_OBJS) $(dssi-vst-server_exe_DLL_PATH) $(WINE_DLL_PATH) $(GLOBAL_DLL_PATH) $(dssi-vst-server_exe_DLLS:%=-l%) $(GLOBAL_DLLS:%=-l%) + +$(dssi-vst-server_exe_MODULE).so: $(dssi-vst-server_exe_MODULE).dbg.o $(dssi-vst-server_exe_MODULE).spec.o $(dssi-vst-server_exe_OBJS) $(dssi-vst-server_exe_DEPENDS) + $(LDXXSHARED) $(LDDLLFLAGS) -o $@ $(dssi-vst-server_exe_OBJS) $(dssi-vst-server_exe_MODULE).dbg.o $(dssi-vst-server_exe_MODULE).spec.o $(dssi-vst-server_exe_LIBRARY_PATH) $(ALL_LIBRARY_PATH) $(dssi-vst-server_exe_LIBRARIES:%=-l%) $(ALL_LIBRARIES) $(LIBS) + +### Target specific build rules + +$(dssi-vst_gui_exe_MODULE).dbg.c: $(dssi-vst_gui_exe_C_SRCS) $(dssi-vst_gui_exe_CXX_SRCS) + $(LDPATH) $(WINEBUILD) -o $@ --debug -C$(SRCDIR) $(dssi-vst_gui_exe_C_SRCS) $(dssi-vst_gui_exe_CXX_SRCS) + +$(dssi-vst_gui_exe_MODULE).spec.c: $(dssi-vst_gui_exe_SPEC_SRCS) $(dssi-vst_gui_exe_RC_SRCS:.rc=.res) $(dssi-vst_gui_exe_OBJS) + $(LDPATH) $(WINEBUILD) -fPIC -o $@ --exe $(dssi-vst_gui_exe_MODULE) -m$(dssi-vst_gui_exe_APPMODE) $(dssi-vst_gui_exe_SPEC_SRCS:%=--spec %) $(dssi-vst_gui_exe_RC_SRCS:%.rc=%.res) $(dssi-vst_gui_exe_OBJS) $(dssi-vst_gui_exe_DLL_PATH) $(WINE_DLL_PATH) $(GLOBAL_DLL_PATH) $(dssi-vst_gui_exe_DLLS:%=-l%) $(GLOBAL_DLLS:%=-l%) + +$(dssi-vst_gui_exe_MODULE).so: $(dssi-vst_gui_exe_MODULE).dbg.o $(dssi-vst_gui_exe_MODULE).spec.o $(dssi-vst_gui_exe_OBJS) $(dssi-vst_gui_exe_DEPENDS) + $(LDXXSHARED) $(LDDLLFLAGS) -o $@ $(dssi-vst_gui_exe_OBJS) $(dssi-vst_gui_exe_MODULE).dbg.o $(dssi-vst_gui_exe_MODULE).spec.o $(dssi-vst_gui_exe_LIBRARY_PATH) $(ALL_LIBRARY_PATH) $(dssi-vst_gui_exe_LIBRARIES:%=-l%) $(ALL_LIBRARIES) $(LIBS) + +### Target specific build rules + +$(dssi-vst-scanner_exe_MODULE).dbg.c: $(dssi-vst-scanner_exe_C_SRCS) $(dssi-vst-scanner_exe_CXX_SRCS) + $(LDPATH) $(WINEBUILD) -o $@ --debug -C$(SRCDIR) $(dssi-vst-scanner_exe_C_SRCS) $(dssi-vst-scanner_exe_CXX_SRCS) + +$(dssi-vst-scanner_exe_MODULE).spec.c: $(dssi-vst-scanner_exe_SPEC_SRCS) $(dssi-vst-scanner_exe_RC_SRCS:.rc=.res) $(dssi-vst-scanner_exe_OBJS) + $(LDPATH) $(WINEBUILD) -fPIC -o $@ --exe $(dssi-vst-scanner_exe_MODULE) -m$(dssi-vst-scanner_exe_APPMODE) $(dssi-vst-scanner_exe_SPEC_SRCS:%=--spec %) $(dssi-vst-scanner_exe_RC_SRCS:%.rc=%.res) $(dssi-vst-scanner_exe_OBJS) $(dssi-vst-scanner_exe_DLL_PATH) $(WINE_DLL_PATH) $(GLOBAL_DLL_PATH) $(dssi-vst-scanner_exe_DLLS:%=-l%) $(GLOBAL_DLLS:%=-l%) + +$(dssi-vst-scanner_exe_MODULE).so: $(dssi-vst-scanner_exe_MODULE).dbg.o $(dssi-vst-scanner_exe_MODULE).spec.o $(dssi-vst-scanner_exe_OBJS) $(dssi-vst-scanner_exe_DEPENDS) + $(LDXXSHARED) $(LDDLLFLAGS) -o $@ $(dssi-vst-scanner_exe_OBJS) $(dssi-vst-scanner_exe_MODULE).dbg.o $(dssi-vst-scanner_exe_MODULE).spec.o $(dssi-vst-scanner_exe_LIBRARY_PATH) $(ALL_LIBRARY_PATH) $(dssi-vst-scanner_exe_LIBRARIES:%=-l%) $(ALL_LIBRARIES) $(LIBS) + + +### Client application +vstsynth: vstsynth.cpp remoteplugin.h remotepluginclient.h remotevstclient.o libremoteplugin.a + g++ -g3 -I/usr/local/include -L/usr/local/lib -Wall vstsynth.cpp remotevstclient.o -o vstsynth -L. -lremoteplugin -ljack -lasound + +libremoteplugin.a: remotepluginclient.o remotepluginserver.o rdwrops.o + ar r $@ $^ + +remotepluginclient.o: remotepluginclient.cpp remotepluginclient.h remoteplugin.h + g++ -g3 -Wall remotepluginclient.cpp -c + +remotevstclient.o: remotevstclient.cpp remotevstclient.h remotepluginclient.h remoteplugin.h + g++ -g3 -Wall remotevstclient.cpp -c + +remotepluginserver.o: remotepluginserver.cpp remotepluginserver.h remoteplugin.h + g++ -g3 -Wall remotepluginserver.cpp -c + +dssi-vst-server.o: remoteplugin.h remotepluginserver.h libremoteplugin.a +dssi-vst_gui.o: remoteplugin.h remotepluginserver.h libremoteplugin.a +dssi-vst-scanner.o: remoteplugin.h remotepluginserver.h libremoteplugin.a + +dssi-vst.so: remoteplugin.h remotepluginclient.h libremoteplugin.a dssi-vst.cpp remotevstclient.o + g++ -shared -Wl,-Bsymbolic -g3 -Wall -o dssi-vst.so dssi-vst.cpp remotevstclient.o -L. -lremoteplugin diff --git a/README b/README new file mode 100644 index 0000000..cd966db --- /dev/null +++ b/README @@ -0,0 +1,47 @@ + +dssi-vst: a DSSI plugin wrapper for VST plugins +=============================================== + +This is a ***CURRENTLY INCOMPLETE AND NON-WORKING*** implementation of +a DSSI plugin wrapper for VST effects and instruments with GUI support. + +Source files: + +* dssi-vst.cpp: DSSI plugin implementation (works OK mostly) +* dssi-vst_gui.cpp: DSSI plugin GUI process implementation (not done yet) +* dssi-vst-scanner.cpp: Program that determines what VSTs you have (a bit + like fstconfig in the fst package) and communicates it back to the plugin +* dssi-vst-server.cpp: Program that hosts a single VST with a comms link + to the plugin (a bit like a less general version of Kjetil's vstserver) +* rdwrops.cpp: misc functions +* remotepluginclient.cpp/remotepluginserver.cpp: Code to handle process + separation for audio plugin (not VST specific), used by DSSI plugin & server +* vstsyth.cpp: JACK/aseq host for VSTs using dssi-vst-server (but not using + the actual DSSI plugin). Mostly for test purposes, this does nothing you + can't already do better with vstserver or jack_fst. + +Copyright (c) 2004 Chris Cannam. dssi-vst does not use any code from +vstserver or libfst. It requires recent Wine and the VST SDK headers +to build. Compilation and (especially) installation are currently +very involved, there will be simplifications later. + +I strongly recommend not attempting to use this code at all at the moment. + + +Why not use vstserver or libfst? +-------------------------------- + +It doesn't use libfst because libfst is just too serious a thing to +start messing about with in a plugin whose host doesn't know about it +(because it involves introducing Wine threads into the host). + +Architecturally dssi-vst is a bit like vstserver. The main reason it +doesn't use vstserver is simply that I wrote most of this code quite a +while ago as an exercise, and I've just got used to my own code. + +FWIW most of the incomplete bits of this code are things that +vstserver wouldn't help with (particularly the separate GUI process). + + +Chris Cannam +cannam@all-day-breakfast.com diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..fbe61e4 --- /dev/null +++ b/configure.ac @@ -0,0 +1,614 @@ +dnl Process this file with autoconf to produce a configure script. +dnl Author: Michael Patra +dnl +dnl Francois Gouget for CodeWeavers + +AC_REVISION([configure.ac 1.00]) +AC_INIT(remotepluginclient.cpp) + +NEEDS_MFC=0 + +dnl **** Command-line arguments **** + +AC_SUBST(OPTIONS) + +dnl **** Check for some programs **** + +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_CXX +AC_PROG_CPP +AC_PROG_LN_S + +dnl **** Check for some libraries **** + +dnl Check for -lm for BeOS +AC_CHECK_LIB(m,sqrt) +dnl Check for -lw for Solaris +AC_CHECK_LIB(w,iswalnum) +dnl Check for -lnsl for Solaris +AC_CHECK_FUNCS(gethostbyname,, AC_CHECK_LIB(nsl, gethostbyname, X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl", AC_CHECK_LIB(socket, gethostbyname, X_EXTRA_LIBS="$X_EXTRA_LIBS -lnsl", , -lnsl), -lsocket)) +dnl Check for -lsocket for Solaris +AC_CHECK_FUNCS(connect,,AC_CHECK_LIB(socket,connect)) + +dnl **** Check for gcc strength-reduce bug **** + +if test "x${GCC}" = "xyes" +then + AC_CACHE_CHECK([for gcc strength-reduce bug], ac_cv_c_gcc_strength_bug, + AC_TRY_RUN([ +int main(void) { + static int Array[[3]]; + unsigned int B = 3; + int i; + for(i=0; i],,ac_found=1;ac_cv_pheader_$1="$ac_dir";break) + done + CPPFLAGS="$save_CPPFLAGS" + ifelse([$3],,,[if test -z "$ac_found" + then + $3 + fi + ]) +]) +$1="$ac_cv_pheader_$1" +if test -n "$ac_found" -o -n "[$]$1" +then + AC_MSG_RESULT([$]$1) +else + AC_MSG_RESULT(no) +fi +AC_SUBST($1) +]) + +dnl AC_PATH_LIBRARY(variable,libraries,extra libs,action-if-not-found,default-locations) +AC_DEFUN(AC_PATH_LIBRARY,[ +AC_MSG_CHECKING([for $2]) +AC_CACHE_VAL(ac_cv_plibrary_$1, +[ + ac_found= + ac_dummy="ifelse([$5], , :/usr/local/lib, [$5])" + save_LIBS="$LIBS" + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + for ac_dir in $ac_dummy; do + IFS="$ac_save_ifs" + if test -z "$ac_dir" + then + LIBS="$2 $3 $save_LIBS" + else + LIBS="-L$ac_dir $2 $3 $save_LIBS" + fi + AC_TRY_LINK(,,ac_found=1;ac_cv_plibrary_$1="$ac_dir";break) + done + LIBS="$save_LIBS" + ifelse([$4],,,[if test -z "$ac_found" + then + $4 + fi + ]) +]) +$1="$ac_cv_plibrary_$1" +if test -n "$ac_found" -o -n "[$]$1" +then + AC_MSG_RESULT([$]$1) +else + AC_MSG_RESULT(no) +fi +AC_SUBST($1) +]) + +dnl **** Try to find where winelib is located **** + +LDPATH="" +WINE_INCLUDE_ROOT="" +WINE_INCLUDE_PATH="" +WINE_LIBRARY_ROOT="" +WINE_LIBRARY_PATH="" +WINE_DLL_ROOT="" +WINE_DLL_PATH="" +WINE_TOOL_PATH="" +WINE="" +WINEBUILD="" +WRC="" + +AC_ARG_WITH(wine, +[ --with-wine=DIR the Wine package (or sources) is in DIR], +[if test "$withval" != "no"; then + WINE_ROOT="$withval"; + WINE_INCLUDES=""; + WINE_LIBRARIES=""; + WINE_TOOLS=""; +else + WINE_ROOT=""; +fi]) +if test -n "$WINE_ROOT" +then + WINE_INCLUDE_ROOT="$WINE_ROOT/include:$WINE_ROOT/include/wine:$WINE_ROOT/include/wine/windows:$WINE_ROOT/include/windows" + WINE_LIBRARY_ROOT="$WINE_ROOT:$WINE_ROOT/libs:$WINE_ROOT/lib" + WINE_DLL_ROOT="$WINE_ROOT/dlls:$WINE_ROOT/lib:$WINE_ROOT/lib/wine" + WINE_TOOL_PATH="$WINE_ROOT:$WINE_ROOT/bin:$WINE_ROOT/tools/wrc:$WINE_ROOT/tools/winebuild" +fi + +AC_ARG_WITH(wine-includes, +[ --with-wine-includes=DIR the Wine includes are in DIR], +[if test "$withval" != "no"; then + WINE_INCLUDES="$withval"; +else + WINE_INCLUDES=""; +fi]) +if test -n "$WINE_INCLUDES" +then + WINE_INCLUDE_ROOT="$WINE_INCLUDES" +fi + +AC_ARG_WITH(wine-libraries, +[ --with-wine-libraries=DIR the Wine libraries are in DIR], +[if test "$withval" != "no"; then + WINE_LIBRARIES="$withval"; +else + WINE_LIBRARIES=""; +fi]) +if test -n "$WINE_LIBRARIES" +then + WINE_LIBRARY_ROOT="$WINE_LIBRARIES" +fi + +AC_ARG_WITH(wine-dlls, +[ --with-wine-dlls=DIR the Wine dlls are in DIR], +[if test "$withval" != "no"; then + WINE_DLLS="$withval"; +else + WINE_DLLS=""; +fi]) +if test -n "$WINE_DLLS" +then + WINE_DLL_ROOT="$WINE_DLLS" +fi + +AC_ARG_WITH(wine-tools, +[ --with-wine-tools=DIR the Wine tools are in DIR], +[if test "$withval" != "no"; then + WINE_TOOLS="$withval"; +else + WINE_TOOLS=""; +fi]) +if test -n "$WINE_TOOLS" +then + WINE_TOOL_PATH="$WINE_TOOLS:$WINE_TOOLS/tools/wrc:$WINE_TOOLS/tools/winebuild" +fi + +if test -z "$WINE_INCLUDE_ROOT" +then + WINE_INCLUDE_ROOT=":/usr/include/wine/windows:/usr/include/wine:/usr/local/include/wine/windows:/opt/wine/include/windows:/opt/wine/include/wine"; +else + AC_PATH_FILE(WINE_INCLUDE_ROOT,[windef.h],[ + AC_MSG_ERROR([Could not find the Wine headers (windef.h)]) + ],$WINE_INCLUDE_ROOT) +fi +AC_PATH_HEADER(WINE_INCLUDE_ROOT,[windef.h],[ + AC_MSG_ERROR([Could not include the Wine headers (windef.h)]) +],$WINE_INCLUDE_ROOT) +if test -n "$WINE_INCLUDE_ROOT" +then + WINE_INCLUDE_PATH="-I$WINE_INCLUDE_ROOT" +else + WINE_INCLUDE_PATH="" +fi + +if test -z "$WINE_LIBRARY_ROOT" +then + WINE_LIBRARY_ROOT=":/usr/lib/wine:/usr/local/lib:/usr/local/lib/wine:/opt/wine/lib" +else + AC_PATH_FILE(WINE_LIBRARY_ROOT,[libwine.so],[ + AC_MSG_ERROR([Could not find the Wine libraries (libwine.so)]) + ],$WINE_LIBRARY_ROOT) +fi +AC_PATH_LIBRARY(WINE_LIBRARY_ROOT,[-lwine],[],[ + AC_MSG_ERROR([Could not link with the Wine libraries (libwine.so)]) +],$WINE_LIBRARY_ROOT) +if test -n "$WINE_LIBRARY_ROOT" +then + WINE_LIBRARY_PATH="-L$WINE_LIBRARY_ROOT" + LDPATH="$WINE_LIBRARY_ROOT" +else + WINE_LIBRARY_PATH="" +fi + +save_LIBS="$LIBS" +LIBS="$WINE_LIBRARY_PATH $LIBS" + +AC_CHECK_LIB(wine_unicode,wine_cp_wcstombs,[],[ + AC_MSG_ERROR([Could not find the Wine dlls (libwine_unicode.so)]) +]) +AC_CHECK_LIB(wine_uuid,IID_IUnknown,[],[ + AC_MSG_ERROR([Could not find the Wine dlls (libwine_uuid.so)]) +]) + +LIBS="$save_LIBS" + +if test -z "$WINE_DLL_ROOT" +then + if test -n "$WINE_LIBRARY_ROOT" + then + WINE_DLL_ROOT="$WINE_LIBRARY_ROOT:$WINE_LIBRARY_ROOT/dlls:$WINE_LIBRARY_ROOT/wine" + else + WINE_DLL_ROOT="/lib:/lib/wine:/usr/lib:/usr/lib/wine:/usr/local/lib:/usr/local/lib/wine" + fi +fi +AC_PATH_FILE(WINE_DLL_ROOT,[libntdll.def],[ + AC_MSG_ERROR([Could not find the Wine dlls (libntdll.def)]) +],[$WINE_DLL_ROOT]) +WINE_DLL_PATH="-L$WINE_DLL_ROOT" + +if test -z "$WINE_TOOL_PATH" +then + WINE_TOOL_PATH="$PATH:/usr/local/bin:/opt/wine/bin" +fi +AC_PATH_PROG(WINE,wine,,$WINE_TOOL_PATH) +if test -z "$WINE" +then + AC_MSG_ERROR([Could not find Wine's wine tool]) +fi +AC_PATH_PROG(WINEBUILD,winebuild,,$WINE_TOOL_PATH) +if test -z "$WINEBUILD" +then + AC_MSG_ERROR([Could not find Wine's winebuild tool]) +fi +AC_PATH_PROG(WRC,wrc,,$WINE_TOOL_PATH) +if test -z "$WRC" +then + AC_MSG_ERROR([Could not find Wine's wrc tool]) +fi + +LDPATH="LD_LIBRARY_PATH=\"$LDPATH:\$\$LD_LIBRARY_PATH\"" +AC_SUBST(LDPATH) +AC_SUBST(WINE_INCLUDE_PATH) +AC_SUBST(WINE_LIBRARY_PATH) +AC_SUBST(WINE_DLL_PATH) + +dnl **** Try to find where the MFC are located **** +AC_LANG_CPLUSPLUS() + +if test "x$NEEDS_MFC" = "x1" +then + ATL_INCLUDE_ROOT=""; + ATL_INCLUDE_PATH=""; + MFC_INCLUDE_ROOT=""; + MFC_INCLUDE_PATH=""; + MFC_LIBRARY_ROOT=""; + MFC_LIBRARY_PATH=""; + + AC_ARG_WITH(mfc, + [ --with-mfc=DIR the MFC package (or sources) is in DIR], + [if test "$withval" != "no"; then + MFC_ROOT="$withval"; + ATL_INCLUDES=""; + MFC_INCLUDES=""; + MFC_LIBRARIES=""; + else + MFC_ROOT=""; + fi]) + if test -n "$MFC_ROOT" + then + ATL_INCLUDE_ROOT="$MFC_ROOT"; + MFC_INCLUDE_ROOT="$MFC_ROOT"; + MFC_LIBRARY_ROOT="$MFC_ROOT"; + fi + + AC_ARG_WITH(atl-includes, + [ --with-atl-includes=DIR the ATL includes are in DIR], + [if test "$withval" != "no"; then + ATL_INCLUDES="$withval"; + else + ATL_INCLUDES=""; + fi]) + if test -n "$ATL_INCLUDES" + then + ATL_INCLUDE_ROOT="$ATL_INCLUDES"; + fi + + AC_ARG_WITH(mfc-includes, + [ --with-mfc-includes=DIR the MFC includes are in DIR], + [if test "$withval" != "no"; then + MFC_INCLUDES="$withval"; + else + MFC_INCLUDES=""; + fi]) + if test -n "$MFC_INCLUDES" + then + MFC_INCLUDE_ROOT="$MFC_INCLUDES"; + fi + + AC_ARG_WITH(mfc-libraries, + [ --with-mfc-libraries=DIR the MFC libraries are in DIR], + [if test "$withval" != "no"; then + MFC_LIBRARIES="$withval"; + else + MFC_LIBRARIES=""; + fi]) + if test -n "$MFC_LIBRARIES" + then + MFC_LIBRARY_ROOT="$MFC_LIBRARIES"; + fi + + OLDCPPFLAGS="$CPPFLAGS" + dnl FIXME: We should not have defines in any of the include paths + CPPFLAGS="$WINE_INCLUDE_PATH -I$WINE_INCLUDE_ROOT/msvcrt -D_DLL -D_MT $CPPFLAGS" + ATL_INCLUDE_PATH="-I\$(WINE_INCLUDE_ROOT)/msvcrt -D_DLL -D_MT" + if test -z "$ATL_INCLUDE_ROOT" + then + ATL_INCLUDE_ROOT=":$WINE_INCLUDE_ROOT/atl:/usr/include/atl:/usr/local/include/atl:/opt/mfc/include/atl:/opt/atl/include" + else + ATL_INCLUDE_ROOT="$ATL_INCLUDE_ROOT:$ATL_INCLUDE_ROOT/atl:$ATL_INCLUDE_ROOT/atl/include" + fi + AC_PATH_HEADER(ATL_INCLUDE_ROOT,atldef.h,[ + AC_MSG_ERROR([Could not find the ATL includes]) + ],$ATL_INCLUDE_ROOT) + if test -n "$ATL_INCLUDE_ROOT" + then + ATL_INCLUDE_PATH="$ATL_INCLUDE_PATH -I$ATL_INCLUDE_ROOT" + fi + + MFC_INCLUDE_PATH="$ATL_INCLUDE_PATH" + if test -z "$MFC_INCLUDE_ROOT" + then + MFC_INCLUDE_ROOT=":$WINE_INCLUDE_ROOT/mfc:/usr/include/mfc:/usr/local/include/mfc:/opt/mfc/include/mfc:/opt/mfc/include" + else + MFC_INCLUDE_ROOT="$MFC_INCLUDE_ROOT:$MFC_INCLUDE_ROOT/mfc:$MFC_INCLUDE_ROOT/mfc/include" + fi + AC_PATH_HEADER(MFC_INCLUDE_ROOT,afx.h,[ + AC_MSG_ERROR([Could not find the MFC includes]) + ],$MFC_INCLUDE_ROOT) + if test -n "$MFC_INCLUDE_ROOT" -a "$ATL_INCLUDE_ROOT" != "$MFC_INCLUDE_ROOT" + then + MFC_INCLUDE_PATH="$MFC_INCLUDE_PATH -I$MFC_INCLUDE_ROOT" + fi + CPPFLAGS="$OLDCPPFLAGS" + + if test -z "$MFC_LIBRARY_ROOT" + then + MFC_LIBRARY_ROOT=":$WINE_LIBRARY_ROOT:/usr/lib/mfc:/usr/local/lib:/usr/local/lib/mfc:/opt/mfc/lib"; + else + MFC_LIBRARY_ROOT="$MFC_LIBRARY_ROOT:$MFC_LIBRARY_ROOT/lib:$MFC_LIBRARY_ROOT/mfc/src"; + fi + AC_PATH_LIBRARY(MFC_LIBRARY_ROOT,[-lmfc],[$WINE_LIBRARY_PATH -lwine -lwine_unicode],[ + AC_MSG_ERROR([Could not find the MFC library]) + ],$MFC_LIBRARY_ROOT) + if test -n "$MFC_LIBRARY_ROOT" -a "$MFC_LIBRARY_ROOT" != "$WINE_LIBRARY_ROOT" + then + MFC_LIBRARY_PATH="-L$MFC_LIBRARY_ROOT" + else + MFC_LIBRARY_PATH="" + fi + + AC_SUBST(ATL_INCLUDE_PATH) + AC_SUBST(MFC_INCLUDE_PATH) + AC_SUBST(MFC_LIBRARY_PATH) +fi + +AC_LANG_C() + +dnl **** Generate output files **** + +MAKE_RULES=Make.rules +AC_SUBST_FILE(MAKE_RULES) + +AC_OUTPUT([ +Make.rules +Makefile + ]) + +echo +echo "Configure finished. Do 'make' to build the project." +echo + +dnl Local Variables: +dnl comment-start: "dnl " +dnl comment-end: "" +dnl comment-start-skip: "\\bdnl\\b\\s *" +dnl compile-command: "autoconf" +dnl End: diff --git a/dssi-vst-scanner.cpp b/dssi-vst-scanner.cpp new file mode 100644 index 0000000..90a321a --- /dev/null +++ b/dssi-vst-scanner.cpp @@ -0,0 +1,371 @@ +// -*- c-basic-offset: 4 -*- + +/* + dssi-vst: a DSSI plugin wrapper for VST effects and instruments + Copyright 2004 Chris Cannam +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#include "aeffectx.h" +#include "AEffEditor.hpp" + +#include "remotepluginserver.h" + +#define APPLICATION_CLASS_NAME "dssi_vst" +#define PLUGIN_ENTRY_POINT "main" + +using namespace std; + + +long VSTCALLBACK +hostCallback(AEffect *plugin, long opcode, long index, + long value, void *ptr, float opt) +{ + static VstTimeInfo timeInfo; + + switch (opcode) { + + case audioMasterVersion: + return 2300; + + case audioMasterGetVendorString: + strcpy((char *)ptr, "Chris Cannam"); + break; + + case audioMasterGetProductString: + strcpy((char *)ptr, "DSSI VST Wrapper Plugin Scanner"); + break; + + case audioMasterGetVendorVersion: + return long(RemotePluginVersion * 100); + + case audioMasterGetLanguage: + return kVstLangEnglish; + + case audioMasterCanDo: + if (!strcmp((char*)ptr, "sendVstEvents") || + !strcmp((char*)ptr, "sendVstMidiEvent") || + !strcmp((char*)ptr, "sendVstTimeInfo") || + !strcmp((char*)ptr, "sizeWindow") || + !strcmp((char*)ptr, "supplyIdle")) { + return 1; + } + break; + + case audioMasterGetTime: + timeInfo.samplePos = 0; + timeInfo.sampleRate = 48000; + timeInfo.flags = 0; // don't mark anything valid except default samplePos/Rate + return (long)&timeInfo; + + case audioMasterTempoAt: + // can't support this, return 120bpm + return 120 * 10000; + + case audioMasterGetSampleRate: + plugin->dispatcher(plugin, effSetSampleRate, + 0, 0, NULL, 48000.0); + break; + + case audioMasterGetBlockSize: + plugin->dispatcher(plugin, effSetBlockSize, + 0, 1024, NULL, 0); + break; + + case audioMasterWillReplaceOrAccumulate: + // 0 -> unsupported, 1 -> replace, 2 -> accumulate + return 1; + + case audioMasterGetCurrentProcessLevel: + // 0 -> unsupported, 1 -> gui, 2 -> process, 3 -> midi/timer, 4 -> offline + return 1; + + case audioMasterGetParameterQuantization: + return 1; + + case audioMasterNeedIdle: + // might be nice to handle this better + return 1; + + case audioMasterWantMidi: + // happy to oblige + return 1; + + default: + ; + } + + return 0; +}; + +int WINAPI +WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) +{ + char *destFile = 0; + + cout << "DSSI VST plugin scanner v0.2" << endl; + cout << "Copyright (c) 2004 Chris Cannam" << endl; + + if (cmdline && cmdline[0]) destFile = strdup(cmdline); + + int targetfd = 0; + if (destFile) { + if ((targetfd = open(destFile, O_WRONLY)) < 0) { + cerr << "dssi-vst-scanner: Failed to open output file " << destFile; + perror(" "); + cerr << "dssi-vst-scanner: Defaulting to stdout" << endl; + targetfd = 0; + } + } + + //!!! could do with an option for vst/vsti path, for the moment + // we'll deal only with effects + + char libPath[1024]; + HINSTANCE libHandle = 0; + char *vstDir = getenv("VST_DIR"); + + if (!vstDir) { + cerr << "dssi-vst-scanner: $VST_DIR not set" << endl; + exit(1); + } + + DIR *directory = opendir(vstDir); + if (!directory) { + cerr << "dssi-vst-scanner: couldn't read VST directory \"" + << vstDir << "\" (from $VST_DIR)" << std::endl; + if (targetfd != 0) close(targetfd); + return 1; + } + + struct dirent *entry; + int count = 0; + + char *home = getenv("HOME"); + std::string cacheDir = std::string(home) + "/.dssi-vst"; + bool haveCacheDir = false; + + DIR *test = opendir(cacheDir.c_str()); + if (!test) { + if (mkdir(cacheDir.c_str(), 0755)) { + cerr << "dssi-vst-scanner: failed to create cache directory " << cacheDir; + perror(0); + } else { + haveCacheDir = true; + } + } else { + haveCacheDir = true; + closedir(test); + } + + while ((entry = readdir(directory))) { + + // For each plugin, we write: + // + // unique id (unsigned long) + // dll name (64 chars) + // name (64 chars) + // vendor (64 chars) + // is synth (bool) + // have editor (bool) + // input count (int) + // output count (int) + // + // parameter count (int) + // then for each parameter: + // name (64 chars) + // + // program count (int) + // then for each program: + // name (64 chars) + + std::string libname = entry->d_name; + + if (libname[0] == '.' || + libname.length() < 5 || + (libname.substr(libname.length() - 4) != ".dll" && + libname.substr(libname.length() - 4) != ".DLL")) { + continue; + } + + if (vstDir[strlen(vstDir) - 1] == '/') { + snprintf(libPath, 1024, "%s%s", vstDir, libname.c_str()); + } else { + snprintf(libPath, 1024, "%s/%s", vstDir, libname.c_str()); + } + + std::string libpathstr(libPath); + + if (home && home[0] != '\0') { + if (libpathstr.substr(0, strlen(home)) == std::string(home)) { + libpathstr = libpathstr.substr(strlen(home) + 1); + } + } + + int fd = targetfd; + bool haveCache = false; + bool writingCache = false; + std::string cacheFileName = cacheDir + "/" + libname + ".cache"; + + if (haveCacheDir) { + + struct stat st; + if (!stat(cacheFileName.c_str(), &st)) { + haveCache = true; + } else { + if ((fd = open(cacheFileName.c_str(), O_WRONLY | O_CREAT, 0644)) < 0) { + cerr << "dssi-vst-scanner: Failed to open cache file " << cacheFileName; + perror("for writing"); + fd = targetfd; + } else { + writingCache = true; + } + } + } + + if (!haveCache) { + + int inputs = 0, outputs = 0, params = 0, programs = 0; + char buffer[65]; + unsigned long uniqueId = 0; + bool synth = false, gui = false; + int i = 0; + AEffect *(__stdcall* getInstance)(audioMasterCallback) = 0; + AEffect *plugin = 0; + + libHandle = LoadLibrary(libpathstr.c_str()); + + if (!libHandle) { + cerr << "dssi-vst-scanner: Couldn't load DLL " << libpathstr << endl; + goto done; + } + + getInstance = (AEffect*(__stdcall*)(audioMasterCallback)) + GetProcAddress(libHandle, PLUGIN_ENTRY_POINT); + + if (!getInstance) { + cerr << "dssi-vst-scanner: VST entrypoint \"" << PLUGIN_ENTRY_POINT + << "\" not found in DLL \"" << libpathstr << "\"" << endl; + goto done; + } + + plugin = getInstance(hostCallback); + + if (!plugin) { + cerr << "dssi-vst-scanner: Failed to instantiate plugin in VST DLL \"" + << libpathstr << "\"" << endl; + goto done; + } + + if (plugin->magic != kEffectMagic) { + cerr << "dssi-vst-scanner: Not a VST effect in DLL \"" + << libpathstr << "\"" << endl; + goto done; + } + + if (!plugin->flags & effFlagsCanReplacing) { + cerr << "dssi-vst-scanner: Effect does not support processReplacing (required)" + << endl; + goto done; + } + + uniqueId = 6666 + count; + write(fd, &uniqueId, sizeof(unsigned long)); + + memset(buffer, 0, 65); + snprintf(buffer, 64, "%s", libname.c_str()); + write(fd, buffer, 64); + + memset(buffer, 0, 65); + plugin->dispatcher(plugin, effGetEffectName, 0, 0, buffer, 0); + if (buffer[0] == '\0') { + snprintf(buffer, 64, "%s", libname.c_str()); + } + write(fd, buffer, 64); + + memset(buffer, 0, 65); + plugin->dispatcher(plugin, effGetVendorString, 0, 0, buffer, 0); + write(fd, buffer, 64); + + synth = false; + if (plugin->flags & effFlagsIsSynth) synth = true; + write(fd, &synth, sizeof(bool)); + + gui = false; + if (plugin->flags & effFlagsHasEditor) gui = true; + write(fd, &gui, sizeof(bool)); + + inputs = plugin->numInputs; + write(fd, &inputs, sizeof(int)); + + outputs = plugin->numOutputs; + write(fd, &outputs, sizeof(int)); + + params = plugin->numParams; + write(fd, ¶ms, sizeof(int)); + + for (i = 0; i < params; ++i) { + memset(buffer, 0, 65); + plugin->dispatcher(plugin, effGetParamName, i, 0, buffer, 0); + write(fd, buffer, 64); + } + + programs = plugin->numPrograms; + write(fd, &programs, sizeof(int)); + + for (i = 0; i < programs; ++i) { + memset(buffer, 0, 65); + // effGetProgramName appears to return the name of the + // current program, not program -- though we + // pass in as well, just in case + plugin->dispatcher(plugin, effSetProgram, 0, i, NULL, 0); + plugin->dispatcher(plugin, effGetProgramName, i, 0, buffer, 0); + write(fd, buffer, 64); + } + + done: + if (plugin) plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); + FreeLibrary(libHandle); + } + + if (writingCache) { + close(fd); + } + + if (haveCache || writingCache) { + // need to read from cache as well + if ((fd = open(cacheFileName.c_str(), O_RDONLY)) < 0) { + cerr << "dssi-vst-scanner: Failed to open cache file " << cacheFileName; + perror("for reading"); + } else { + unsigned char c; + while (read(fd, &c, 1) == 1) { + write(targetfd, &c, 1); + } + close(fd); + } + } + + ++count; + } + + closedir(directory); + if (targetfd != 0) { + close(targetfd); + } + + return 0; +} + diff --git a/dssi-vst-server.cpp b/dssi-vst-server.cpp new file mode 100644 index 0000000..20a2561 --- /dev/null +++ b/dssi-vst-server.cpp @@ -0,0 +1,781 @@ +// -*- c-basic-offset: 4 -*- + +/* + dssi-vst: a DSSI plugin wrapper for VST effects and instruments + Copyright 2004 Chris Cannam +*/ + +#include +#include +#include + +#include +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#include "aeffectx.h" +#include "AEffEditor.hpp" + +#include "remotepluginserver.h" + +#define APPLICATION_CLASS_NAME "dssi_vst" +#define PLUGIN_ENTRY_POINT "main" + +static bool inProcessThread = false; +static bool exiting = false; +static HWND hWnd = 0; +static double currentSamplePosition = 0.0; + +static bool ready = false; +static int bufferSize = 0; +static int sampleRate = 0; + +static RemotePluginDebugLevel debugLevel = RemotePluginDebugEvents; + +using namespace std; + +class RemoteVSTServer : public RemotePluginServer +{ +public: + RemoteVSTServer(std::string fileIdentifiers, AEffect *plugin, std::string fallbackName); + virtual ~RemoteVSTServer(); + + virtual bool isReady() { return ready; } + + virtual std::string getName() { return m_name; } + virtual std::string getMaker() { return m_maker; } + virtual void setBufferSize(int); + virtual void setSampleRate(int); + virtual void reset(); + virtual void terminate(); + + virtual int getInputCount() { return m_plugin->numInputs; } + virtual int getOutputCount() { return m_plugin->numOutputs; } + + virtual int getParameterCount() { return m_plugin->numParams; } + virtual std::string getParameterName(int); + virtual void setParameter(int, float); + virtual float getParameter(int); + virtual float getParameterDefault(int); + + virtual int getProgramCount() { return m_plugin->numPrograms; } + virtual std::string getProgramName(int); + virtual void setCurrentProgram(int); + + virtual bool hasMIDIInput() { return m_hasMIDI; } + virtual void sendMIDIData(unsigned char *data, int length); + + virtual void process(float **inputs, float **outputs) { + inProcessThread = true; + // superclass guarantees setBufferSize will be called before this + m_plugin->processReplacing(m_plugin, inputs, outputs, bufferSize); + currentSamplePosition += bufferSize; + inProcessThread = false; + } + + virtual void setDebugLevel(RemotePluginDebugLevel level) { + debugLevel = level; + } + + virtual bool warn(std::string); + +private: + AEffect *m_plugin; + std::string m_name; + std::string m_maker; + bool m_hasMIDI; +}; + +static RemoteVSTServer *remoteVSTServerInstance = 0; + +RemoteVSTServer::RemoteVSTServer(std::string fileIdentifiers, + AEffect *plugin, std::string fallbackName) : + RemotePluginServer(fileIdentifiers), + m_plugin(plugin), + m_name(fallbackName), + m_maker("") +{ + m_plugin->dispatcher(m_plugin, effOpen, 0, 0, NULL, 0); + m_plugin->dispatcher(m_plugin, effMainsChanged, 0, 0, NULL, 0); + + m_hasMIDI = false; + + if (m_plugin->dispatcher(m_plugin, effGetVstVersion, 0, 0, NULL, 0) < 2) { + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: plugin is VST 1.x" << endl; + } + } else { + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: plugin is VST 2.0 or newer" << endl; + } + if ((m_plugin->flags & effFlagsIsSynth)) { + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: plugin is a synth" << endl; + } + m_hasMIDI = true; + } else { + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: plugin is not a synth" << endl; + } + if (m_plugin->dispatcher(m_plugin, effCanDo, 0, 0, (void *)"receiveVstMidiEvent", 0) > 0) { + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: plugin can receive MIDI anyway" << endl; + } + m_hasMIDI = true; + } + } + } + + char buffer[65]; + buffer[0] = '\0'; + m_plugin->dispatcher(m_plugin, effGetEffectName, 0, 0, buffer, 0); + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: plugin name is \"" << buffer + << "\"" << endl; + } + if (buffer[0]) m_name = buffer; + + buffer[0] = '\0'; + m_plugin->dispatcher(m_plugin, effGetVendorString, 0, 0, buffer, 0); + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: vendor string is \"" << buffer + << "\"" << endl; + } + if (buffer[0]) m_maker = buffer; + + m_plugin->dispatcher(m_plugin, effMainsChanged, 0, 1, NULL, 0); +} + +RemoteVSTServer::~RemoteVSTServer() +{ + m_plugin->dispatcher(m_plugin, effClose, 0, 0, NULL, 0); +} + +void +RemoteVSTServer::setBufferSize(int sz) +{ + m_plugin->dispatcher(m_plugin, effMainsChanged, 0, 0, NULL, 0); + m_plugin->dispatcher(m_plugin, effSetBlockSize, 0, sz, NULL, 0); + m_plugin->dispatcher(m_plugin, effMainsChanged, 0, 1, NULL, 0); + + bufferSize = sz; + + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: set buffer size to " << sz << endl; + } +} + +void +RemoteVSTServer::setSampleRate(int sr) +{ + m_plugin->dispatcher(m_plugin, effMainsChanged, 0, 0, NULL, 0); + m_plugin->dispatcher(m_plugin, effSetSampleRate, 0, 0, NULL, (float)sr); + m_plugin->dispatcher(m_plugin, effMainsChanged, 0, 1, NULL, 0); + + sampleRate = sr; + + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: set sample rate to " << sr << endl; + } +} + +void +RemoteVSTServer::reset() +{ + m_plugin->dispatcher(m_plugin, effMainsChanged, 0, 0, NULL, 0); + m_plugin->dispatcher(m_plugin, effMainsChanged, 0, 1, NULL, 0); +} + +void +RemoteVSTServer::terminate() +{ + exiting = true; +} + +std::string +RemoteVSTServer::getParameterName(int p) +{ + char name[24]; + m_plugin->dispatcher(m_plugin, effGetParamName, p, 0, name, 0); + return name; +} + +void +RemoteVSTServer::setParameter(int p, float v) +{ + m_plugin->setParameter(m_plugin, p, v); +} + +float +RemoteVSTServer::getParameter(int p) +{ + return m_plugin->getParameter(m_plugin, p); +} + +float +RemoteVSTServer::getParameterDefault(int p) +{ + //!!! fix + return m_plugin->getParameter(m_plugin, p); +} + +std::string +RemoteVSTServer::getProgramName(int p) +{ + char name[24]; + // effGetProgramName appears to return the name of the current + // program, not program -- though we pass in as + // well, just in case + long prevProgram = + m_plugin->dispatcher(m_plugin, effGetProgram, 0, 0, NULL, 0); + m_plugin->dispatcher(m_plugin, effSetProgram, 0, p, NULL, 0); + m_plugin->dispatcher(m_plugin, effGetProgramName, p, 0, name, 0); + m_plugin->dispatcher(m_plugin, effSetProgram, 0, prevProgram, NULL, 0); + return name; +} + +void +RemoteVSTServer::setCurrentProgram(int p) +{ + m_plugin->dispatcher(m_plugin, effSetProgram, 0, p, 0, 0); +} + +void +RemoteVSTServer::sendMIDIData(unsigned char *data, int len) +{ + std::cerr << "host: sendMIDIData length " << len << std::endl; + +#define MIDI_EVENT_BUFFER_COUNT 1024 + static VstMidiEvent vme[MIDI_EVENT_BUFFER_COUNT]; + static char evbuf[sizeof(VstMidiEvent *) * MIDI_EVENT_BUFFER_COUNT + + sizeof(VstEvents)]; + + VstEvents *vstev = (VstEvents *)evbuf; + vstev->reserved = 0; + + int ix = 0; + int count = len/3; + + if (count > MIDI_EVENT_BUFFER_COUNT) { + std::cerr << "vstserv: WARNING: " << count << " MIDI events received " + << "for " << MIDI_EVENT_BUFFER_COUNT << "-event buffer" + << std::endl; + count = MIDI_EVENT_BUFFER_COUNT; + } + + while (ix < count) { + + vme[ix].type = kVstMidiType; + vme[ix].byteSize = 24; + vme[ix].deltaFrames = 0; + vme[ix].flags = 0; + vme[ix].noteLength = 0; + vme[ix].noteOffset = 0; + vme[ix].detune = 0; + vme[ix].noteOffVelocity = 0; + vme[ix].reserved1 = 0; + vme[ix].reserved2 = 0; + vme[ix].midiData[0] = data[ix*3]; + vme[ix].midiData[1] = data[ix*3+1]; + vme[ix].midiData[2] = data[ix*3+2]; + vme[ix].midiData[3] = 0; + + vstev->events[ix] = (VstEvent *)&vme[ix]; + + if (debugLevel > 1) { + cerr << "dssi-vst-server[2]: MIDI event in: " + << (int)data[ix*3] << " " + << (int)data[ix*3+1] << " " + << (int)data[ix*3+2] << endl; + } + + ++ix; + } + + vstev->numEvents = count; + if (!m_plugin->dispatcher(m_plugin, effProcessEvents, 0, 0, vstev, 0)) { + cerr << "WARNING: " << ix << " MIDI event(s) rejected by plugin" << endl; + } +} + +bool +RemoteVSTServer::warn(std::string warning) +{ + if (hWnd) MessageBox(hWnd, warning.c_str(), "Error", 0); + return true; +} + + +long VSTCALLBACK +hostCallback(AEffect *plugin, long opcode, long index, + long value, void *ptr, float opt) +{ + static VstTimeInfo timeInfo; + + switch (opcode) { + + case audioMasterVersion: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterVersion requested" << endl; + return 2300; + + case audioMasterGetVendorString: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterGetVendorString requested" << endl; + strcpy((char *)ptr, "Chris Cannam"); + break; + + case audioMasterGetProductString: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterGetProductString requested" << endl; + strcpy((char *)ptr, "DSSI VST Wrapper Plugin"); + break; + + case audioMasterGetVendorVersion: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterGetVendorVersion requested" << endl; + return long(RemotePluginVersion * 100); + + case audioMasterGetLanguage: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterGetLanguage requested" << endl; + return kVstLangEnglish; + + case audioMasterCanDo: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterCanDo(" << (char *)ptr + << ") requested" << endl; + if (!strcmp((char*)ptr, "sendVstEvents") || + !strcmp((char*)ptr, "sendVstMidiEvent") || + !strcmp((char*)ptr, "sendVstTimeInfo") || + !strcmp((char*)ptr, "sizeWindow") || + !strcmp((char*)ptr, "supplyIdle")) { + return 1; + } + break; + + case audioMasterGetTime: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterGetTime requested" << endl; + timeInfo.samplePos = currentSamplePosition; + timeInfo.sampleRate = sampleRate; + timeInfo.flags = 0; // don't mark anything valid except default samplePos/Rate + return (long)&timeInfo; + + case audioMasterTempoAt: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterTempoAt requested" << endl; + // can't support this, return 120bpm + return 120 * 10000; + + case audioMasterGetSampleRate: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterGetSampleRate requested" << endl; + if (!sampleRate) { + cerr << "WARNING: Sample rate requested but not yet set" << endl; + } + plugin->dispatcher(plugin, effSetSampleRate, + 0, 0, NULL, (float)sampleRate); + break; + + case audioMasterGetBlockSize: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterGetBlockSize requested" << endl; + if (!bufferSize) { + cerr << "WARNING: Buffer size requested but not yet set" << endl; + } + plugin->dispatcher(plugin, effSetBlockSize, + 0, bufferSize, NULL, 0); + break; + + case audioMasterWillReplaceOrAccumulate: + if (debugLevel > 1) + cerr << "dssi-vst-server[2]: audioMasterWillReplaceOrAccumulate requested" << endl; + // 0 -> unsupported, 1 -> replace, 2 -> accumulate + return 1; + + case audioMasterGetCurrentProcessLevel: + if (debugLevel > 1) { + cerr << "dssi-vst-server[2]: audioMasterGetCurrentProcessLevel requested (level is " << (inProcessThread ? 2 : 1) << ")" << endl; + } + // 0 -> unsupported, 1 -> gui, 2 -> process, 3 -> midi/timer, 4 -> offline + if (inProcessThread) return 2; + else return 1; + + case audioMasterGetParameterQuantization: + if (debugLevel > 1) { + cerr << "dssi-vst-server[2]: audioMasterGetParameterQuantization requested" << endl; + } + return 1; + + case audioMasterNeedIdle: + if (debugLevel > 1) { + cerr << "dssi-vst-server[2]: audioMasterNeedIdle requested" << endl; + } + // might be nice to handle this better + return 1; + + case audioMasterWantMidi: + if (debugLevel > 1) { + cerr << "dssi-vst-server[2]: audioMasterWantMidi requested" << endl; + } + // happy to oblige + return 1; + + case audioMasterSizeWindow: + if (debugLevel > 1) { + cerr << "dssi-vst-server[2]: audioMasterSizeWindow requested" << endl; + } + if (hWnd) { + SetWindowPos(hWnd, 0, 0, 0, + index + 6, + value + 25, + SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOOWNERZORDER | SWP_NOZORDER); + } + return 1; + + default: + if (debugLevel > 0) { + cerr << "dssi-vst-server[0]: unsupported audioMaster callback opcode " + << opcode << endl; + } + } + + return 0; +}; + +DWORD WINAPI +AudioThreadMain(LPVOID parameter) +{ + while (1) { + try { + remoteVSTServerInstance->dispatch(); + } catch (std::string message) { + cerr << "ERROR: Remote VST server instance failed: " << message << endl; + exiting = true; + } catch (RemotePluginClosedException) { + cerr << "ERROR: Remote VST plugin communication failure" << endl; + exiting = true; + } + + if (exiting) return 0; + } +} + +LRESULT WINAPI +MainProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_DESTROY: + PostQuitMessage(0); + exiting = true; + return 0; + } + + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +int WINAPI +WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) +{ + char *libname = 0; + char *fileInfo = 0; + + cout << "DSSI VST plugin server v" << RemotePluginVersion << endl; + cout << "Copyright (c) 2004 Chris Cannam" << endl; + + char *home = getenv("HOME"); + + if (cmdline) { + int offset = 0; + if (cmdline[0] == '"' || cmdline[0] == '\'') offset = 1; + for (int ci = offset; cmdline[ci]; ++ci) { + if (cmdline[ci] == ',') { + libname = strndup(cmdline + offset, ci - offset); + ++ci; + if (cmdline[ci]) { + fileInfo = strdup(cmdline + ci); + int l = strlen(fileInfo); + if (fileInfo[l-1] == '"' || + fileInfo[l-1] == '\'') { + fileInfo[l-1] = '\0'; + } + } + } + } + } + + if (!libname || !libname[0] || !fileInfo || !fileInfo[0]) { + cerr << "Usage: dssi-vst-server ," << endl; + cerr << "(Command line was: " << cmdline << ")" << endl; + exit(2); + } + + cout << "Loading \"" << libname << "\"... "; + if (debugLevel > 0) cout << endl; + + char libPath[1024]; + HINSTANCE libHandle = 0; + char *vstDir = getenv("VSTI_DIR"); + + if (vstDir) { + if (vstDir[strlen(vstDir) - 1] == '/') { + snprintf(libPath, 1024, "%s%s", vstDir, libname); + } else { + snprintf(libPath, 1024, "%s/%s", vstDir, libname); + } + std::string libpathstr(libPath); + + if (home && home[0] != '\0') { + if (libpathstr.substr(0, strlen(home)) == std::string(home)) { + libpathstr = libpathstr.substr(strlen(home) + 1); + } + } + + libHandle = LoadLibrary(libpathstr.c_str()); + + libHandle = LoadLibrary(libPath); + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: " << (libHandle ? "" : "not ") + << "found in " << vstDir << " (from $VSTI_DIR)" << endl; + } + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: $VSTI_DIR not set" << endl; + } + + std::cerr << "home is " << getenv("HOME") << std::endl; + + if (!libHandle) { + vstDir = getenv("VST_DIR"); + if (vstDir) { + snprintf(libPath, 1024, "%s/%s", vstDir, libname); + std::string libpathstr(libPath); + + if (home && home[0] != '\0') { + if (libpathstr.substr(0, strlen(home)) == std::string(home)) { + libpathstr = libpathstr.substr(strlen(home) + 1); + } + } + + libHandle = LoadLibrary(libpathstr.c_str()); + + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: " << (libHandle ? "" : "not ") + << "found in " << libPath << " (from $VST_DIR)" << endl; + + LPVOID lpMsgBuf; + if (FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL )) { + std::cerr << (const char *)lpMsgBuf << std::endl; + } + + LocalFree( lpMsgBuf ); + } + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: $VST_DIR not set" << endl; + } + } + + if (!libHandle) { + libHandle = LoadLibrary(libname); + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: " << (libHandle ? "" : "not ") + << "found in DLL path" << endl; + } + } + + if (!libHandle) { + cerr << "dssi-vst-server: ERROR: Couldn't load VST DLL \"" << libname << "\"" << endl; + return 1; + } + + cout << "done" << endl; + + cout << "Testing VST compatibility... "; + if (debugLevel > 0) cout << endl; + +//!!! better debug level support + + //!!! look for _main as well? + + AEffect *(__stdcall* getInstance)(audioMasterCallback); + getInstance = (AEffect*(__stdcall*)(audioMasterCallback)) + GetProcAddress(libHandle, PLUGIN_ENTRY_POINT); + + if (!getInstance) { + cerr << "dssi-vst-server: ERROR: VST entrypoint \"" << PLUGIN_ENTRY_POINT + << "\" not found in DLL \"" << libname << "\"" << endl; + return 1; + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: VST entrypoint \"" << PLUGIN_ENTRY_POINT + << "\" found" << endl; + } + + AEffect *plugin = getInstance(hostCallback); + + if (!plugin) { + cerr << "dssi-vst-server: ERROR: Failed to instantiate plugin in VST DLL \"" + << libname << "\"" << endl; + return 1; + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: plugin instantiated" << endl; + } + + if (plugin->magic != kEffectMagic) { + cerr << "dssi-vst-server: ERROR: Not a VST effect in DLL \"" << libname << "\"" << endl; + return 1; + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: plugin is a VST" << endl; + } + +#ifdef SHOW_GUI + if (!(plugin->flags & effFlagsHasEditor)) { + cerr << "dssi-vst-server: ERROR: Instrument has no GUI (required)" << endl; + return 1; + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: synth has a GUI" << endl; + } +#endif + + if (!plugin->flags & effFlagsCanReplacing) { + cerr << "dssi-vst-server: ERROR: Instrument does not support processReplacing (required)" + << endl; + return 1; + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: synth supports processReplacing" << endl; + } + + try { + remoteVSTServerInstance = + new RemoteVSTServer(fileInfo, plugin, libname); + } catch (std::string message) { + cerr << "ERROR: Remote VST startup failed: " << message << endl; + return 1; + } catch (RemotePluginClosedException) { + cerr << "ERROR: Remote VST plugin communication failure" << endl; + return 1; + } + +// This needs to be an argument: +#ifdef SHOW_GUI + + cout << "Initialising Windows subsystem... "; + if (debugLevel > 0) cout << endl; + + WNDCLASSEX wclass; + wclass.cbSize = sizeof(WNDCLASSEX); + wclass.style = 0; + wclass.lpfnWndProc = MainProc; + wclass.cbClsExtra = 0; + wclass.cbWndExtra = 0; + wclass.hInstance = hInst; + wclass.hIcon = LoadIcon(hInst, APPLICATION_CLASS_NAME); + wclass.hCursor = LoadCursor(0, IDI_APPLICATION); + wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wclass.lpszMenuName = "MENU_DSSI_VST"; + wclass.lpszClassName = APPLICATION_CLASS_NAME; + wclass.hIconSm = 0; + + if (!RegisterClassEx(&wclass)) { + cerr << "dssi-vst-server: ERROR: Failed to register Windows application class!\n" << endl; + return 1; + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: registered Windows application class \"" << APPLICATION_CLASS_NAME << "\"" << endl; + } + + hWnd = CreateWindow + (APPLICATION_CLASS_NAME, remoteVSTServerInstance->getName().c_str(), + WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + 0, 0, hInst, 0); + if (!hWnd) { + cerr << "dssi-vst-server: ERROR: Failed to create window!\n" << endl; + return 1; + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: created main window" << endl; + } + + plugin->dispatcher(plugin, effEditOpen, 0, 0, hWnd, 0); + ERect *rect = 0; + plugin->dispatcher(plugin, effEditGetRect, 0, 0, &rect, 0); + if (!rect) { + cerr << "dssi-vst-server: ERROR: Plugin failed to report window size\n" << endl; + return 1; + } + + // Seems we need to provide space in here for the titlebar and frame, + // even though we don't know how big they'll be! How crap. + SetWindowPos(hWnd, 0, 0, 0, + rect->right - rect->left + 6, + rect->bottom - rect->top + 25, + SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOOWNERZORDER | SWP_NOZORDER); + + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: sized window" << endl; + } + + ShowWindow(hWnd, SW_SHOWNORMAL); + UpdateWindow(hWnd); + + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: showed window" << endl; + } +#endif + + cout << "done" << endl; + + DWORD threadId = 0; + HANDLE threadHandle = CreateThread(0, 0, AudioThreadMain, 0, 0, &threadId); + if (!threadHandle) { + cerr << "Failed to create audio thread!" << endl; + return 1; //!!!tidy + } else if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: created audio thread" << endl; + } + + ready = true; + + MSG msg; + exiting = false; + while (GetMessage(&msg, 0, 0, 0)) { + DispatchMessage(&msg); + if (exiting) break; + } + + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: cleaning up" << endl; + } + + CloseHandle(threadHandle); + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: closed audio thread" << endl; + } + + delete remoteVSTServerInstance; + + FreeLibrary(libHandle); + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: freed dll" << endl; + } + + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: exiting" << endl; + } + + return 0; +} + diff --git a/dssi-vst.cpp b/dssi-vst.cpp new file mode 100644 index 0000000..6b0e36b --- /dev/null +++ b/dssi-vst.cpp @@ -0,0 +1,592 @@ + +/* -*- c-basic-offset: 4 -*- */ + +/* + dssi-vst: a DSSI plugin wrapper for VST effects and instruments + Copyright 2004 Chris Cannam +*/ + +#include "remotevstclient.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MIDI_BUFFER_SIZE 1024 + +class DSSIVSTPluginInstance +{ +public: + static void freeFields(DSSI_Descriptor &descriptor); + + DSSIVSTPluginInstance(std::string dllName, + unsigned long sampleRate); + virtual ~DSSIVSTPluginInstance(); + + bool isOK() { return m_ok; } + + // LADSPA methods: + + void activate(); + void deactivate(); + void connectPort(unsigned long port, LADSPA_Data *location); + void run(unsigned long sampleCount); + + // DSSI methods: + + const DSSI_Program_Descriptor *getProgram(unsigned long index); + void selectProgram(unsigned long bank, unsigned long program); + void runSynth(unsigned long sampleCount, + snd_seq_event_t *events, unsigned long eventCount); + +protected: + static RemotePluginClient *load(std::string name); + + unsigned long m_sampleRate; + unsigned long m_lastSampleCount; + + LADSPA_Data **m_controlPorts; + LADSPA_Data *m_controlPortsSaved; + unsigned long m_controlPortCount; + + LADSPA_Data **m_audioIns; + unsigned long m_audioInCount; + + LADSPA_Data **m_audioOuts; + unsigned long m_audioOutCount; + + DSSI_Program_Descriptor **m_programs; + unsigned long m_programCount; + + unsigned char m_decodeBuffer[MIDI_BUFFER_SIZE]; + snd_midi_event_t *m_alsaDecoder; + + bool m_pendingProgram; + + RemotePluginClient *m_plugin; + bool m_ok; +}; + +class DSSIVSTPlugin +{ +public: + DSSIVSTPlugin(); + virtual ~DSSIVSTPlugin(); + + DSSI_Descriptor *queryDescriptor(unsigned long index); + + // LADSPA methods: + + static LADSPA_Handle instantiate(const LADSPA_Descriptor *descriptor, + unsigned long sampleRate); + + static void connect_port(LADSPA_Handle instance, + unsigned long port, + LADSPA_Data *location); + + static void activate(LADSPA_Handle instance); + + static void run(LADSPA_Handle instance, + unsigned long sampleCount); + + static void deactivate(LADSPA_Handle instance); + + static void cleanup(LADSPA_Handle instance); + + // DSSI methods: + + static const DSSI_Program_Descriptor *get_program(LADSPA_Handle instance, + unsigned long index); + + static void select_program(LADSPA_Handle instance, + unsigned long bank, unsigned long program); + + static void run_synth(LADSPA_Handle instance, unsigned long sampleCount, + snd_seq_event_t *events, unsigned long eventCount); + +private: + typedef std::pair PluginPair; + typedef std::vector PluginList; + PluginList m_descriptors; +}; + + +#define NO_CONTROL_DATA -10000000000000.0 + +DSSIVSTPluginInstance::DSSIVSTPluginInstance(std::string dllName, + unsigned long sampleRate) : + m_sampleRate(sampleRate), + m_lastSampleCount(0), + m_controlPorts(0), + m_controlPortsSaved(0), + m_controlPortCount(0), + m_audioIns(0), + m_audioInCount(0), + m_audioOuts(0), + m_audioOutCount(0), + m_programs(0), + m_programCount(0), + m_pendingProgram(false), + m_plugin(0), + m_ok(false) +{ + std::cerr << "DSSIVSTPluginInstance::DSSIVSTPluginInstance(" << dllName << ")" << std::endl; + + try { + m_plugin = new RemoteVSTClient(dllName); + + m_controlPortCount = m_plugin->getParameterCount(); + m_controlPorts = new LADSPA_Data*[m_controlPortCount](0); + m_controlPortsSaved = new LADSPA_Data[m_controlPortCount](NO_CONTROL_DATA); + + m_audioInCount = m_plugin->getInputCount(); + m_audioIns = new LADSPA_Data*[m_audioInCount](0); + + m_audioOutCount = m_plugin->getOutputCount(); + m_audioOuts = new LADSPA_Data*[m_audioOutCount](0); + + m_programCount = m_plugin->getProgramCount(); + m_programs = new DSSI_Program_Descriptor*[m_programCount](0); + for (unsigned long i = 0; i < m_programCount; ++i) { + m_programs[i] = new DSSI_Program_Descriptor; + m_programs[i]->Bank = 0; + m_programs[i]->Program = i; + m_programs[i]->Name = strdup(m_plugin->getProgramName(i).c_str()); + } + + //!!! only if needed? + snd_midi_event_new(MIDI_BUFFER_SIZE, &m_alsaDecoder); + if (!m_alsaDecoder) { + std::cerr << "DSSIVSTPluginInstance::DSSIVSTPluginInstance(" + << dllName << "): failed to initialize ALSA MIDI decoder" + << std::endl; + } else { + snd_midi_event_no_status(m_alsaDecoder, 1); + } + + m_ok = true; + + } catch (RemotePluginClosedException) { + std::cerr << "DSSIVSTPluginInstance::DSSIVSTPluginInstance(" + << dllName << "): startup failed" << std::endl; + + m_ok = false; + delete m_plugin; m_plugin = 0; + delete m_controlPorts; m_controlPorts = 0; + delete m_controlPortsSaved; m_controlPortsSaved = 0; + delete m_audioIns; m_audioIns = 0; + delete m_audioOuts; m_audioOuts = 0; + } +} + +DSSIVSTPluginInstance::~DSSIVSTPluginInstance() +{ + if (m_ok) { + m_plugin->terminate(); + delete m_plugin; + } + if (m_alsaDecoder) { + snd_midi_event_free(m_alsaDecoder); + } + + delete m_controlPorts; + delete m_controlPortsSaved; + delete m_audioIns; + delete m_audioOuts; + + for (unsigned long i = 0; i < m_programCount; ++i) { + free((void *)m_programs[i]->Name); + delete m_programs[i]; + } + delete m_programs; +} + +void +DSSIVSTPluginInstance::activate() +{ + if (m_ok) { + try { + m_plugin->setSampleRate(m_sampleRate); + } catch (RemotePluginClosedException) { + m_ok = false; + } + } +} + +void +DSSIVSTPluginInstance::deactivate() +{ + if (m_ok) { + try { + m_plugin->reset(); + } catch (RemotePluginClosedException) { + m_ok = false; + } + } +} + +void +DSSIVSTPluginInstance::connectPort(unsigned long port, LADSPA_Data *location) +{ + std::cerr << "connectPort(" << port << "," << location << ")" << std::endl; + + if (!m_ok) return; + + if (port < m_controlPortCount) { + std::cerr << "(control port)" << std::endl; + m_controlPorts[port] = location; + return; + } + port -= m_controlPortCount; + + if (port < m_audioInCount) { + std::cerr << "(audio in port)" << std::endl; + m_audioIns[port] = location; + return; + } + port -= m_audioInCount; + + if (port < m_audioOutCount) { + std::cerr << "(audio out port)" << std::endl; + m_audioOuts[port] = location; + return; + } +} + +const DSSI_Program_Descriptor * +DSSIVSTPluginInstance::getProgram(unsigned long index) +{ + if (index >= m_programCount) return 0; + m_programs[index]->Name = strdup(m_programs[index]->Name); // host frees + return m_programs[index]; +} + +void +DSSIVSTPluginInstance::selectProgram(unsigned long bank, unsigned long program) +{ + if (bank != 0 || program >= m_programCount) return; + m_plugin->setCurrentProgram(program); + m_pendingProgram = true; +} + +void +DSSIVSTPluginInstance::run(unsigned long sampleCount) +{ + if (m_ok) { + try { + if (sampleCount != m_lastSampleCount) { + m_plugin->setBufferSize(sampleCount); + m_lastSampleCount = sampleCount; + } + + for (unsigned long i = 0; i < m_controlPortCount; ++i) { + + if (!m_controlPorts[i]) continue; + + if (m_controlPortsSaved[i] != *m_controlPorts[i]) { + m_plugin->setParameter(i, *m_controlPorts[i]); + m_controlPortsSaved[i] = *m_controlPorts[i]; + } + } + + m_plugin->process(m_audioIns, m_audioOuts); + + if (m_pendingProgram) { + + for (unsigned long i = 0; i < m_controlPortCount; ++i) { + + if (!m_controlPorts[i]) continue; + + //!!! need to do this elsewhere -- unlike + //setParameter it does actually take IPC time -- + //this is experimental only + + *m_controlPorts[i] = m_plugin->getParameter(i); + } + + m_pendingProgram = false; + } + + } catch (RemotePluginClosedException) { + m_ok = false; + } + } +} + +void +DSSIVSTPluginInstance::runSynth(unsigned long sampleCount, + snd_seq_event_t *events, unsigned long eventCount) +{ + try { + if (m_alsaDecoder) { + + unsigned long index = 0; + + for (unsigned long i = 0; i < eventCount; ++i) { + + snd_seq_event_t *ev = &events[i]; + + if (index >= MIDI_BUFFER_SIZE - 4) break; + + long count = snd_midi_event_decode(m_alsaDecoder, + m_decodeBuffer + index, + MIDI_BUFFER_SIZE - index, + ev); + if (count < 0) { + std::cerr << "WARNING: MIDI decoder error " << count + << " for event type " << ev->type << std::endl; + } else if (count == 0 || count > 3) { + std::cerr << "WARNING: MIDI event of type " << ev->type + << " decoded to " << count << " bytes, discarding" << std::endl; + } else { + index += count; + while (count++ < 3) { + m_decodeBuffer[index++] = '\0'; + } + } + } + + if (index > 0) { + m_plugin->sendMIDIData(m_decodeBuffer, index); + } + } + } catch (RemotePluginClosedException) { + m_ok = false; + } + + run(sampleCount); +} + +void +DSSIVSTPluginInstance::freeFields(DSSI_Descriptor &descriptor) +{ + LADSPA_Descriptor &ldesc = (LADSPA_Descriptor &)*descriptor.LADSPA_Plugin; + + if (ldesc.Name) free((char *)ldesc.Name); + if (ldesc.Maker) free((char *)ldesc.Maker); + if (ldesc.Copyright) free((char *)ldesc.Copyright); + + if (ldesc.PortDescriptors) { + delete[] ldesc.PortDescriptors; + } + + if (ldesc.PortNames) { + for (unsigned long i = 0; i < ldesc.PortCount; ++i) { + free((char *)ldesc.PortNames[i]); + } + delete[] ldesc.PortNames; + } + + if (ldesc.PortRangeHints) { + delete[] ldesc.PortRangeHints; + } +} + + +DSSIVSTPlugin::DSSIVSTPlugin() +{ + std::vector plugins; + RemoteVSTClient::queryPlugins(plugins); + + for (unsigned int p = 0; p < plugins.size(); ++p) { + + DSSI_Descriptor *descriptor = new DSSI_Descriptor; + LADSPA_Descriptor *ldesc = new LADSPA_Descriptor; + descriptor->LADSPA_Plugin = ldesc; + + RemoteVSTClient::PluginRecord &rec = plugins[p]; + + ldesc->UniqueID = rec.uniqueId; + ldesc->Label = strdup(rec.dllName.c_str()); + ldesc->Name = strdup(std::string(rec.pluginName + " VST").c_str()); + ldesc->Maker = strdup(rec.vendorName.c_str()); + ldesc->Copyright = strdup(ldesc->Maker); + + std::cerr << "Plugin name: " << ldesc->Name << std::endl; + + int parameters = rec.parameters; + int inputs = rec.inputs; + int outputs = rec.outputs; + int portCount = parameters + inputs + outputs; + + LADSPA_PortDescriptor *ports = new LADSPA_PortDescriptor[portCount]; + char **names = new char *[portCount]; + LADSPA_PortRangeHint *hints = new LADSPA_PortRangeHint[portCount]; + + for (int i = 0; i < parameters; ++i) { + ports[i] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; + names[i] = strdup(rec.parameterNames[i].c_str()); + hints[i].HintDescriptor = 0; //!!! need this + std::cerr << "Port " << i << ": name " << names[i] << std::endl; + } + + for (int i = 0; i < inputs; ++i) { + int j = i + parameters; + ports[j] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; + char buf[20]; + snprintf(buf, 19, "in%d", i + 1); + names[j] = strdup(buf); + hints[j].HintDescriptor = 0; + } + + for (int i = 0; i < outputs; ++i) { + int j = i + inputs + parameters; + ports[j] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; + char buf[20]; + snprintf(buf, 19, "out%d", i + 1); + names[j] = strdup(buf); + hints[j].HintDescriptor = 0; + } + + ldesc->PortCount = portCount; + ldesc->PortDescriptors = ports; + ldesc->PortNames = names; + ldesc->PortRangeHints = hints; + ldesc->ImplementationData = 0; + + ldesc->instantiate = DSSIVSTPlugin::instantiate; + ldesc->connect_port = DSSIVSTPlugin::connect_port; + ldesc->activate = DSSIVSTPlugin::activate; + ldesc->run = DSSIVSTPlugin::run; + ldesc->run_adding = 0; + ldesc->set_run_adding_gain = 0; + ldesc->deactivate = DSSIVSTPlugin::deactivate; + ldesc->cleanup = DSSIVSTPlugin::cleanup; + + descriptor->DSSI_API_Version = 1; + descriptor->configure = 0; + descriptor->get_program = DSSIVSTPlugin::get_program; + descriptor->select_program = DSSIVSTPlugin::select_program; + descriptor->get_midi_controller_for_port = 0; + + if (rec.isSynth) { + descriptor->run_synth = DSSIVSTPlugin::run_synth; + } else { + descriptor->run_synth = 0; + } + + descriptor->run_synth_adding = 0; + descriptor->run_multiple_synths = 0; + descriptor->run_multiple_synths_adding = 0; + + m_descriptors.push_back(PluginPair(rec.dllName, descriptor)); + } +} + +DSSIVSTPlugin::~DSSIVSTPlugin() +{ + for (PluginList::iterator i = m_descriptors.begin(); i != m_descriptors.end(); ++i) { + DSSIVSTPluginInstance::freeFields(*i->second); + delete i->second->LADSPA_Plugin; + delete i->second; + } +} + + +DSSI_Descriptor * +DSSIVSTPlugin::queryDescriptor(unsigned long index) +{ + if (index < m_descriptors.size()) { + std::cerr << "DSSIVSTPlugin::queryDescriptor: index is " << index + << ", returning " << m_descriptors[index].second->LADSPA_Plugin->Name + << std::endl; + return m_descriptors[index].second; + } else { + return 0; + } +} + +LADSPA_Handle +DSSIVSTPlugin::instantiate(const LADSPA_Descriptor *descriptor, + unsigned long sampleRate) +{ + std::cerr << "DSSIVSTPlugin::instantiate(" << descriptor->Label << ")" << std::endl; + + try { + return (LADSPA_Handle) + (new DSSIVSTPluginInstance(descriptor->Label, sampleRate)); + } catch (std::string e) { + perror(e.c_str()); + } catch (RemotePluginClosedException) { + std::cerr << "Remote plugin closed." << std::endl; + } + return 0; +} + +void +DSSIVSTPlugin::connect_port(LADSPA_Handle instance, + unsigned long port, + LADSPA_Data *location) +{ + ((DSSIVSTPluginInstance *)instance)->connectPort(port, location); +} + +void +DSSIVSTPlugin::activate(LADSPA_Handle instance) +{ + ((DSSIVSTPluginInstance *)instance)->activate(); +} + +void +DSSIVSTPlugin::run(LADSPA_Handle instance, unsigned long sampleCount) +{ + ((DSSIVSTPluginInstance *)instance)->run(sampleCount); +} + +void +DSSIVSTPlugin::deactivate(LADSPA_Handle instance) +{ + ((DSSIVSTPluginInstance *)instance)->deactivate(); +} + +void +DSSIVSTPlugin::cleanup(LADSPA_Handle instance) +{ + delete ((DSSIVSTPluginInstance *)instance); +} + +const DSSI_Program_Descriptor * +DSSIVSTPlugin::get_program(LADSPA_Handle instance, unsigned long index) +{ + return ((DSSIVSTPluginInstance *)instance)->getProgram(index); +} + +void +DSSIVSTPlugin::select_program(LADSPA_Handle instance, unsigned long bank, + unsigned long program) +{ + ((DSSIVSTPluginInstance *)instance)->selectProgram(bank, program); +} + +void +DSSIVSTPlugin::run_synth(LADSPA_Handle instance, unsigned long sampleCount, + snd_seq_event_t *events, unsigned long eventCount) +{ + ((DSSIVSTPluginInstance *)instance)->runSynth(sampleCount, events, + eventCount); +} + +static DSSIVSTPlugin *_plugin = 0; + +const LADSPA_Descriptor * +ladspa_descriptor(unsigned long index) +{ + return 0; +} + +const DSSI_Descriptor * +dssi_descriptor(unsigned long index) +{ + if (!_plugin) { + _plugin = new DSSIVSTPlugin; + } + return _plugin->queryDescriptor(index); +} + diff --git a/dssi-vst_gui.cpp b/dssi-vst_gui.cpp new file mode 100644 index 0000000..7829f22 --- /dev/null +++ b/dssi-vst_gui.cpp @@ -0,0 +1,653 @@ +// -*- c-basic-offset: 4 -*- + +/* + dssi-vst: a DSSI plugin wrapper for VST effects and instruments + Copyright 2004 Chris Cannam +*/ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define WIN32_LEAN_AND_MEAN +#include + +#include "aeffectx.h" +#include "AEffEditor.hpp" + +#define APPLICATION_CLASS_NAME "dssi_vst" +#define PLUGIN_ENTRY_POINT "main" + +static bool inProcessThread = false; +static bool exiting = false; +static HWND hWnd = 0; +static double currentSamplePosition = 0.0; + +static bool ready = false; +static int bufferSize = 0; +static int sampleRate = 0; + +static int debugLevel = 3; + +static AEffect *plugin = 0; +static char *serverurl = 0; + +using std::cout; +using std::cerr; +using std::endl; + +#include "remoteplugin.h" // for RemotePluginVersion + + +long VSTCALLBACK +hostCallback(AEffect *plugin, long opcode, long index, + long value, void *ptr, float opt) +{ + static VstTimeInfo timeInfo; + + switch (opcode) { + + case audioMasterAutomate: + { + float actual = plugin->getParameter(plugin, index); + if (debugLevel > 1) { + cerr << "dssi-vst_gui[2]: audioMasterAutomate(" << index << "," << value << ")" << endl; + cerr << "dssi-vst_gui[2]: actual value " << actual << endl; + } + lo_address hostaddr = lo_address_new(lo_url_get_hostname(serverurl), + lo_url_get_port(serverurl)); + lo_send(hostaddr, + (std::string(lo_url_get_path(serverurl)) + "/control").c_str(), + "if", + index, + actual); + break; + } + + case audioMasterVersion: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterVersion requested" << endl; + return 2300; + + case audioMasterGetVendorString: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterGetVendorString requested" << endl; + strcpy((char *)ptr, "Chris Cannam"); + break; + + case audioMasterGetProductString: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterGetProductString requested" << endl; + strcpy((char *)ptr, "DSSI VST Wrapper Plugin GUI"); + break; + + case audioMasterGetVendorVersion: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterGetVendorVersion requested" << endl; + return long(RemotePluginVersion * 100); + + case audioMasterGetLanguage: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterGetLanguage requested" << endl; + return kVstLangEnglish; + + case audioMasterCanDo: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterCanDo(" << (char *)ptr + << ") requested" << endl; + if (!strcmp((char*)ptr, "sendVstEvents") || + !strcmp((char*)ptr, "sendVstMidiEvent") || + !strcmp((char*)ptr, "sendVstTimeInfo") || + !strcmp((char*)ptr, "sizeWindow") || + !strcmp((char*)ptr, "supplyIdle")) { + return 1; + } + break; + + case audioMasterGetTime: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterGetTime requested" << endl; + timeInfo.samplePos = currentSamplePosition; + timeInfo.sampleRate = sampleRate; + timeInfo.flags = 0; // don't mark anything valid except default samplePos/Rate + return (long)&timeInfo; + + case audioMasterTempoAt: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterTempoAt requested" << endl; + // can't support this, return 120bpm + return 120 * 10000; + + case audioMasterGetSampleRate: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterGetSampleRate requested" << endl; + if (!sampleRate) { + cerr << "WARNING: Sample rate requested but not yet set" << endl; + } + plugin->dispatcher(plugin, effSetSampleRate, + 0, 0, NULL, (float)sampleRate); + break; + + case audioMasterGetBlockSize: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterGetBlockSize requested" << endl; + if (!bufferSize) { + cerr << "WARNING: Buffer size requested but not yet set" << endl; + } + plugin->dispatcher(plugin, effSetBlockSize, + 0, bufferSize, NULL, 0); + break; + + case audioMasterWillReplaceOrAccumulate: + if (debugLevel > 1) + cerr << "dssi-vst_gui[2]: audioMasterWillReplaceOrAccumulate requested" << endl; + // 0 -> unsupported, 1 -> replace, 2 -> accumulate + return 1; + + case audioMasterGetCurrentProcessLevel: + if (debugLevel > 1) { + cerr << "dssi-vst_gui[2]: audioMasterGetCurrentProcessLevel requested (level is " << (inProcessThread ? 2 : 1) << ")" << endl; + } + // 0 -> unsupported, 1 -> gui, 2 -> process, 3 -> midi/timer, 4 -> offline + if (inProcessThread) return 2; + else return 1; + + case audioMasterGetParameterQuantization: + if (debugLevel > 1) { + cerr << "dssi-vst_gui[2]: audioMasterGetParameterQuantization requested" << endl; + } + return 1; + + case audioMasterNeedIdle: + if (debugLevel > 1) { + cerr << "dssi-vst_gui[2]: audioMasterNeedIdle requested" << endl; + } + // might be nice to handle this better + return 1; + + case audioMasterWantMidi: + if (debugLevel > 1) { + cerr << "dssi-vst_gui[2]: audioMasterWantMidi requested" << endl; + } + // happy to oblige + return 1; + + case audioMasterSizeWindow: + if (debugLevel > 1) { + cerr << "dssi-vst_gui[2]: audioMasterSizeWindow requested" << endl; + } + SetWindowPos(hWnd, 0, 0, 0, + index + 6, + value + 25, + SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOOWNERZORDER | SWP_NOZORDER); + return 1; + + default: + if (debugLevel > 0) { + cerr << "dssi-vst_gui[0]: unsupported audioMaster callback opcode " + << opcode << endl; + } + } + + return 0; +}; + +DWORD WINAPI +AudioThreadMain(LPVOID parameter) +{ + while (1) { + //!!! +// if (plugin) plugin->processReplacing(plugin, inputs, outputs, 1024); +// remoteVSTServerInstance->dispatch(); + + //!!! need to at least maintain the pretence of running the thing + + sleep(1); //!!! + + if (exiting) return 0; + } +} + +LRESULT WINAPI +MainProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_DESTROY: + PostQuitMessage(0); + exiting = true; + return 0; + } + + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +void +osc_error(int num, const char *msg, const char *path) +{ + cerr << "Error: liblo server error " << num + << " in path \"" << (path ? path : "(null)") + << "\": " << msg << endl; +} + +int +debug_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data) +{ + int i; + + cerr << "Warning: unhandled OSC message in GUI:" << endl; + + for (i = 0; i < argc; ++i) { + cerr << "arg " << i << ": type '" << types[i] << "': "; + lo_arg_pp((lo_type)types[i], argv[i]); + cerr << endl; + } + + cerr << "(path is <" << path << ">)" << endl; + return 1; +} + +int +program_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data) +{ + if (argc < 2) { + cerr << "Error: too few arguments to program_handler" << endl; + return 1; + } + + const int bank = argv[0]->i; + const int program = argv[1]->i; + + //!!! wrong thread I think -- queue these + + cerr << "program_handler(" << bank << "," << program << ")" << endl; + + plugin->dispatcher(plugin, effSetProgram, 0, program, NULL, 0); + + return 0; +} + +int +configure_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data) +{ + cerr << "configure_handler" << endl; + return 0; +} + +int +show_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data) +{ + cerr << "show_handler" << endl; + + if (hWnd) { + ShowWindow(hWnd, SW_SHOWNORMAL); + UpdateWindow(hWnd); + } + + if (debugLevel > 0) { + cerr << "dssi-vst_gui[1]: showed window" << endl; + } + + return 0; +} + +int +hide_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data) +{ + cerr << "hide_handler" << endl; + ShowWindow(hWnd, SW_HIDE); + + if (debugLevel > 0) { + cerr << "dssi-vst_gui[1]: hid window" << endl; + } + + return 0; +} + +int +quit_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data) +{ + cerr << "quit_handler" << endl; + PostQuitMessage(0); + exiting = true; + return 0; +} + +int +control_handler(const char *path, const char *types, lo_arg **argv, + int argc, void *data, void *user_data) +{ + if (argc < 2) { + cerr << "Error: too few arguments to control_handler" << endl; + return 1; + } + + const int port = argv[0]->i; + const float value = argv[1]->f; + + //!!! wrong thread I think -- queue these + + cerr << "control_handler(" << port << "," << value << ")" << endl; + + plugin->setParameter(plugin, port, value); + + return 0; +} + +int WINAPI +WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) +{ + cout << "DSSI VST plugin GUI host v" << RemotePluginVersion << endl; + cout << "Copyright (c) 2004 Chris Cannam" << endl; + + char *home = getenv("HOME"); + + char *pluginlibname = 0; + char *label = 0; + char *friendlyname = 0; + + if (cmdline) { + int offset = 0; + if (cmdline[0] == '"' || cmdline[0] == '\'') offset = 1; + for (int ci = offset; ; ++ci) { + if (isspace(cmdline[ci]) || !cmdline[ci]) { + if (!serverurl) serverurl = strndup(cmdline + offset, ci - offset); + else if (!pluginlibname) pluginlibname = strndup(cmdline + offset, ci - offset); + else if (!label) { + label = strndup(cmdline + offset, ci - offset); + friendlyname = strdup(cmdline + ci + 1); + break; + } + while (isspace(cmdline[ci])) ++ci; + if (!cmdline[ci]) break; + offset = ci; + } + } + } + + if (friendlyname) { + int l = strlen(friendlyname); + if (friendlyname[l-1] == '"' || + friendlyname[l-1] == '\'') { + friendlyname[l-1] = '\0'; + } + } + + if (!serverurl || !serverurl[0] || + !pluginlibname || !pluginlibname[0] || + !label || !label[0] || + !friendlyname || !friendlyname[0]) { + cerr << "Usage: dssi-vst_gui