From 2f31133a794e6ec8cb026c34c5cad656fc304734 Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Mon, 16 Aug 2004 15:00:06 +0000 Subject: [PATCH] * various updates for a mostly working GUI and install procedure --- Makefile.in | 32 ++- wineapploader.in => apploader.in | 27 ++- dssi-vst-scanner.cpp | 395 +++++++++++++++++-------------- dssi-vst-server.cpp | 50 +++- dssi-vst.cpp | 12 +- dssi-vst_gui.cpp | 140 ++++++++--- paths.cpp | 40 ++++ paths.h | 23 ++ rdwrops.cpp | 24 +- remotepluginclient.cpp | 26 +- remotevstclient.cpp | 188 ++++++++++++--- 11 files changed, 674 insertions(+), 283 deletions(-) rename wineapploader.in => apploader.in (73%) create mode 100644 paths.cpp create mode 100644 paths.h diff --git a/Makefile.in b/Makefile.in index 4a1a0d7..42b135d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,4 +1,10 @@ +### Most commonly changed configuration variables + +TARGET_DSSI_DIR = /usr/local/lib/dssi +INCLUDE_PATH = -I$(HOME)/vst/vstsdk2.3/source/common + + ### Generic autoconf variables TOPSRCDIR = @top_srcdir@ @@ -14,7 +20,6 @@ EXES = dssi-vst-server.exe dssi-vst-scanner.exe dssi-vst_gui.ex ### Common settings DEFINES = -INCLUDE_PATH = -I/home/cannam/vst/vstsdk2.3/source/common DLL_PATH = LIBRARY_PATH = -L. LIBRARIES = remoteplugin @@ -116,25 +121,26 @@ SPEC_SRCS = $(dssi-vst-server_exe_SPEC_SRCS) $(dssi-vst-scanner_exe_ ### Generic autoconf targets -all: wineapploader $(SUBDIRS) $(DLLS:%=%.so) $(EXES:%=%.so) vstsynth libremoteplugin.a dssi-vst.so +all: apploader $(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) $@ +apploader: apploader.in + sed -e 's,@bindir\@,$(bindir),g' -e 's,@winelibdir\@,.,g' $(SRCDIR)/apploader.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 +install:: dssi-vst.so dssi-vst-scanner.exe.so dssi-vst-server.exe.so dssi-vst_gui.exe.so apploader + $(MKDIR) $(TARGET_DSSI_DIR) + $(INSTALL) dssi-vst.so $(TARGET_DSSI_DIR) + $(MKDIR) $(TARGET_DSSI_DIR)/dssi-vst + $(INSTALL) dssi-vst-scanner.exe.so dssi-vst-server.exe.so dssi-vst_gui.exe.so apploader $(TARGET_DSSI_DIR)/dssi-vst/ + ( cd $(TARGET_DSSI_DIR)/dssi-vst/ ; rm -f dssi-vst-scanner dssi-vst-server dssi-vst_gui ; ln -s apploader dssi-vst-scanner ; ln -s apploader dssi-vst-server ; ln -s apploader dssi-vst_gui ; chmod a+x * ../dssi-vst.so ) 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 + $(RM) $(TARGET_DSSI_DIR)/dssi-vst.so + $(RM) -r $(TARGET_DSSI_DIR)/dssi-vst clean:: - $(RM) wineapploader + $(RM) apploader distclean: clean $(RM) config.* configure.lineno Make.rules @@ -179,7 +185,7 @@ $(dssi-vst-scanner_exe_MODULE).so: $(dssi-vst-scanner_exe_MODULE).dbg.o $(dssi-v 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 +libremoteplugin.a: remotepluginclient.o remotepluginserver.o rdwrops.o paths.o ar r $@ $^ remotepluginclient.o: remotepluginclient.cpp remotepluginclient.h remoteplugin.h diff --git a/wineapploader.in b/apploader.in similarity index 73% rename from wineapploader.in rename to apploader.in index 5f8d694..58da0a6 100644 --- a/wineapploader.in +++ b/apploader.in @@ -1,22 +1,14 @@ #!/bin/sh # # Wrapper script to start a Winelib application once it is installed -# -# Copyright (C) 2002 Alexandre Julliard +# Based on wineapploader, Copyright (C) 2002 Alexandre Julliard +# Modified to support starting application from same directory as +# this script # determine the app Winelib library name appname=`basename "$0" .exe`.exe -#allow Wine to load Winelib application from the current directory -export WINEDLLPATH=$WINEDLLPATH:@winelibdir@ - -# first try explicit WINELOADER -if [ -x "$WINELOADER" ]; then exec "$WINELOADER" "$appname" "$@"; fi - -# then default bin directory -if [ -x "@bindir@/wine" ]; then exec "@bindir@/wine" "$appname" "$@"; fi - -# now try the directory containing $0 +# and app directory appdir="" case "$0" in */*) @@ -34,6 +26,17 @@ case "$0" in done ;; esac + +# allow Wine to load Winelib application from the current directory and app directory +export WINEDLLPATH=$WINEDLLPATH:"$appdir":. + +# first try explicit WINELOADER +if [ -x "$WINELOADER" ]; then exec "$WINELOADER" "$appname" "$@"; fi + +# then default bin directory +if [ -x "@bindir@/wine" ]; then exec "@bindir@/wine" "$appname" "$@"; fi + +# now try the directory containing $0 if [ -x "$appdir/wine" ]; then exec "$appdir/wine" "$appname" "$@"; fi # finally look in PATH diff --git a/dssi-vst-scanner.cpp b/dssi-vst-scanner.cpp index 90a321a..7f1e23c 100644 --- a/dssi-vst-scanner.cpp +++ b/dssi-vst-scanner.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -22,6 +23,7 @@ #include "AEffEditor.hpp" #include "remotepluginserver.h" +#include "paths.h" #define APPLICATION_CLASS_NAME "dssi_vst" #define PLUGIN_ENTRY_POINT "main" @@ -133,239 +135,274 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) //!!! could do with an option for vst/vsti path, for the moment // we'll deal only with effects - char libPath[1024]; +// char libPath[1024]; HINSTANCE libHandle = 0; - char *vstDir = getenv("VST_DIR"); + +#ifdef NOT_DEFINED +//!!! + char *vstDir = getenv("VST_DIR"); if (!vstDir) { cerr << "dssi-vst-scanner: $VST_DIR not set" << endl; exit(1); } +#endif + + std::vector vstPath = Paths::getPath + ("VST_PATH", "/usr/local/lib/vst:/usr/lib/vst", "/vst"); - 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); - } + for (size_t i = 0; i < vstPath.size(); ++i) { + + std::string vstDir = vstPath[i]; - 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")) { + DIR *directory = opendir(vstDir.c_str()); + if (!directory) { +// cerr << "dssi-vst-scanner: couldn't read VST directory \"" +// << vstDir << "\"" << std::endl; continue; } - if (vstDir[strlen(vstDir) - 1] == '/') { - snprintf(libPath, 1024, "%s%s", vstDir, libname.c_str()); + 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 { - snprintf(libPath, 1024, "%s/%s", vstDir, libname.c_str()); + 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; + } - std::string libpathstr(libPath); - - if (home && home[0] != '\0') { - if (libpathstr.substr(0, strlen(home)) == std::string(home)) { - libpathstr = libpathstr.substr(strlen(home) + 1); +#ifdef NOT_DEFINED +//!!! + if (vstDir[strlen(vstDir) - 1] == '/') { + snprintf(libPath, 1024, "%s%s", vstDir, libname.c_str()); + } else { + snprintf(libPath, 1024, "%s/%s", vstDir, libname.c_str()); } - } - int fd = targetfd; - bool haveCache = false; - bool writingCache = false; - std::string cacheFileName = cacheDir + "/" + libname + ".cache"; + std::string libpathstr(libPath); - if (haveCacheDir) { + if (home && home[0] != '\0') { + if (libpathstr.substr(0, strlen(home)) == std::string(home)) { + libpathstr = libpathstr.substr(strlen(home) + 1); + } + } +#endif + 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; + struct stat st; + if (!stat(cacheFileName.c_str(), &st)) { + haveCache = true; } else { - writingCache = true; + 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; + if (!haveCache) { - libHandle = LoadLibrary(libpathstr.c_str()); + 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; + std::string libPath; - if (!libHandle) { - cerr << "dssi-vst-scanner: Couldn't load DLL " << libpathstr << endl; - goto done; - } - - getInstance = (AEffect*(__stdcall*)(audioMasterCallback)) - GetProcAddress(libHandle, PLUGIN_ENTRY_POINT); + if (vstDir[vstDir.length()-1] == '/') { + libPath = vstDir + libname; + } else { + libPath = vstDir + "/" + libname; + } + + libHandle = LoadLibrary(libPath.c_str()); + cerr << "dssi-vst-scanner: " << (libHandle ? "" : "not ") + << "found in " << libPath << endl; + + if (!libHandle) { + if (home && home[0] != '\0') { + if (libPath.substr(0, strlen(home)) == home) { + libPath = libPath.substr(strlen(home) + 1); + } + libHandle = LoadLibrary(libPath.c_str()); + cerr << "dssi-vst-scanner: " << (libHandle ? "" : "not ") + << "found in " << libPath << endl; + } + } + + if (!libHandle) { + cerr << "dssi-vst-scanner: Couldn't load DLL " << libPath << endl; + goto done; + } - if (!getInstance) { - cerr << "dssi-vst-scanner: VST entrypoint \"" << PLUGIN_ENTRY_POINT - << "\" not found in DLL \"" << libpathstr << "\"" << endl; - goto done; - } + getInstance = (AEffect*(__stdcall*)(audioMasterCallback)) + GetProcAddress(libHandle, PLUGIN_ENTRY_POINT); - plugin = getInstance(hostCallback); + if (!getInstance) { + cerr << "dssi-vst-scanner: VST entrypoint \"" << PLUGIN_ENTRY_POINT + << "\" not found in DLL \"" << libPath << "\"" << endl; + goto done; + } - if (!plugin) { - cerr << "dssi-vst-scanner: Failed to instantiate plugin in VST DLL \"" - << libpathstr << "\"" << endl; - goto done; - } + plugin = getInstance(hostCallback); - if (plugin->magic != kEffectMagic) { - cerr << "dssi-vst-scanner: Not a VST effect in DLL \"" - << libpathstr << "\"" << endl; - goto done; - } + if (!plugin) { + cerr << "dssi-vst-scanner: Failed to instantiate plugin in VST DLL \"" + << libPath << "\"" << endl; + goto done; + } - if (!plugin->flags & effFlagsCanReplacing) { - cerr << "dssi-vst-scanner: Effect does not support processReplacing (required)" - << endl; - goto done; - } + if (plugin->magic != kEffectMagic) { + cerr << "dssi-vst-scanner: Not a VST effect in DLL \"" + << libPath << "\"" << endl; + goto done; + } - uniqueId = 6666 + count; - write(fd, &uniqueId, sizeof(unsigned long)); + if (!plugin->flags & effFlagsCanReplacing) { + cerr << "dssi-vst-scanner: Effect does not support processReplacing (required)" + << endl; + goto done; + } - memset(buffer, 0, 65); - snprintf(buffer, 64, "%s", libname.c_str()); - write(fd, buffer, 64); + uniqueId = 6666 + count; + write(fd, &uniqueId, sizeof(unsigned long)); - memset(buffer, 0, 65); - plugin->dispatcher(plugin, effGetEffectName, 0, 0, buffer, 0); - if (buffer[0] == '\0') { + memset(buffer, 0, 65); snprintf(buffer, 64, "%s", libname.c_str()); - } - write(fd, buffer, 64); + write(fd, buffer, 64); - memset(buffer, 0, 65); - plugin->dispatcher(plugin, effGetVendorString, 0, 0, buffer, 0); - 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); - synth = false; - if (plugin->flags & effFlagsIsSynth) synth = true; - write(fd, &synth, sizeof(bool)); + memset(buffer, 0, 65); + plugin->dispatcher(plugin, effGetVendorString, 0, 0, buffer, 0); + write(fd, buffer, 64); - gui = false; - if (plugin->flags & effFlagsHasEditor) gui = true; - write(fd, &gui, sizeof(bool)); + synth = false; + if (plugin->flags & effFlagsIsSynth) synth = true; + write(fd, &synth, sizeof(bool)); - inputs = plugin->numInputs; - write(fd, &inputs, sizeof(int)); + gui = false; + if (plugin->flags & effFlagsHasEditor) gui = true; + write(fd, &gui, sizeof(bool)); - outputs = plugin->numOutputs; - write(fd, &outputs, sizeof(int)); + inputs = plugin->numInputs; + write(fd, &inputs, sizeof(int)); - params = plugin->numParams; - write(fd, ¶ms, sizeof(int)); + outputs = plugin->numOutputs; + write(fd, &outputs, sizeof(int)); - for (i = 0; i < params; ++i) { - memset(buffer, 0, 65); - plugin->dispatcher(plugin, effGetParamName, i, 0, buffer, 0); - write(fd, buffer, 64); - } + params = plugin->numParams; + write(fd, ¶ms, sizeof(int)); - programs = plugin->numPrograms; - write(fd, &programs, sizeof(int)); + for (i = 0; i < params; ++i) { + memset(buffer, 0, 65); + plugin->dispatcher(plugin, effGetParamName, i, 0, buffer, 0); + write(fd, buffer, 64); + } - 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); - } + 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); - } + done: + if (plugin) plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); + FreeLibrary(libHandle); + } - if (writingCache) { - close(fd); - } + 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); + 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); } - close(fd); } + + ++count; } - ++count; + closedir(directory); } - closedir(directory); if (targetfd != 0) { close(targetfd); } - + return 0; } + diff --git a/dssi-vst-server.cpp b/dssi-vst-server.cpp index 20a2561..3e796d1 100644 --- a/dssi-vst-server.cpp +++ b/dssi-vst-server.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -22,6 +23,8 @@ #include "remotepluginserver.h" +#include "paths.h" + #define APPLICATION_CLASS_NAME "dssi_vst" #define PLUGIN_ENTRY_POINT "main" @@ -519,8 +522,51 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) cout << "Loading \"" << libname << "\"... "; if (debugLevel > 0) cout << endl; - char libPath[1024]; +// char libPath[1024]; HINSTANCE libHandle = 0; + + + + std::vector vstPath = Paths::getPath + ("VST_PATH", "/usr/local/lib/vst:/usr/lib/vst", "/vst"); + + for (size_t i = 0; i < vstPath.size(); ++i) { + + std::string vstDir = vstPath[i]; + std::string libPath; + + if (vstDir[vstDir.length()-1] == '/') { + libPath = vstDir + libname; + } else { + libPath = vstDir + "/" + libname; + } + + libHandle = LoadLibrary(libPath.c_str()); + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: " << (libHandle ? "" : "not ") + << "found in " << libPath << endl; + } + + if (!libHandle) { + if (home && home[0] != '\0') { + if (libPath.substr(0, strlen(home)) == home) { + libPath = libPath.substr(strlen(home) + 1); + } + libHandle = LoadLibrary(libPath.c_str()); + if (debugLevel > 0) { + cerr << "dssi-vst-server[1]: " << (libHandle ? "" : "not ") + << "found in " << libPath << endl; + } + } + } + + if (libHandle) break; + } + +#ifdef NOT_DEFINED + + + char *vstDir = getenv("VSTI_DIR"); if (vstDir) { @@ -589,6 +635,8 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) } } +#endif + if (!libHandle) { libHandle = LoadLibrary(libname); if (debugLevel > 0) { diff --git a/dssi-vst.cpp b/dssi-vst.cpp index 6b0e36b..c4ea15f 100644 --- a/dssi-vst.cpp +++ b/dssi-vst.cpp @@ -190,8 +190,10 @@ DSSIVSTPluginInstance::~DSSIVSTPluginInstance() { if (m_ok) { m_plugin->terminate(); - delete m_plugin; } + + delete m_plugin; + if (m_alsaDecoder) { snd_midi_event_free(m_alsaDecoder); } @@ -393,7 +395,13 @@ DSSIVSTPluginInstance::freeFields(DSSI_Descriptor &descriptor) DSSIVSTPlugin::DSSIVSTPlugin() { std::vector plugins; - RemoteVSTClient::queryPlugins(plugins); + + try { + RemoteVSTClient::queryPlugins(plugins); + } catch (std::string error) { + std::cerr << "DSSIVSTPlugin: Error on plugin query: " << error << std::endl; + return; + } for (unsigned int p = 0; p < plugins.size(); ++p) { diff --git a/dssi-vst_gui.cpp b/dssi-vst_gui.cpp index 7829f22..a7a5aeb 100644 --- a/dssi-vst_gui.cpp +++ b/dssi-vst_gui.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -15,6 +16,7 @@ #include #include +#include #define WIN32_LEAN_AND_MEAN #include @@ -22,10 +24,12 @@ #include "aeffectx.h" #include "AEffEditor.hpp" +#include "paths.h" + #define APPLICATION_CLASS_NAME "dssi_vst" #define PLUGIN_ENTRY_POINT "main" -static bool inProcessThread = false; +//!!!static bool inProcessThread = false; static bool exiting = false; static HWND hWnd = 0; static double currentSamplePosition = 0.0; @@ -37,7 +41,12 @@ static int sampleRate = 0; static int debugLevel = 3; static AEffect *plugin = 0; -static char *serverurl = 0; +static lo_server oscserver = 0; + +static char *hosturl = 0; +static char *hosthostname = 0; +static char *hostport = 0; +static char *hostpath = 0; using std::cout; using std::cerr; @@ -61,10 +70,9 @@ hostCallback(AEffect *plugin, long opcode, long index, 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_address hostaddr = lo_address_new(hosthostname, hostport); lo_send(hostaddr, - (std::string(lo_url_get_path(serverurl)) + "/control").c_str(), + (std::string(hostpath) + "/control").c_str(), "if", index, actual); @@ -153,11 +161,12 @@ hostCallback(AEffect *plugin, long opcode, long index, case audioMasterGetCurrentProcessLevel: if (debugLevel > 1) { - cerr << "dssi-vst_gui[2]: audioMasterGetCurrentProcessLevel requested (level is " << (inProcessThread ? 2 : 1) << ")" << endl; +//!!! 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; +//!!! if (inProcessThread) return 2; +//!!! else return 1; + return 2; case audioMasterGetParameterQuantization: if (debugLevel > 1) { @@ -203,14 +212,39 @@ hostCallback(AEffect *plugin, long opcode, long index, DWORD WINAPI AudioThreadMain(LPVOID parameter) { + float **inputs, **outputs; + + if (plugin && plugin->numInputs > 0) { + inputs = new float *[plugin->numInputs]; + for (int i = 0; i < plugin->numInputs; ++i) { + inputs[i] = new float[1024]; + } + } else { + inputs = 0; + } + + if (plugin && plugin->numOutputs > 0) { + outputs = new float *[plugin->numOutputs]; + for (int i = 0; i < plugin->numOutputs; ++i) { + outputs[i] = new float[1024]; + } + } else { + outputs = 0; + } + while (1) { + + lo_server_recv_noblock(oscserver, 30); + + if (plugin) plugin->processReplacing(plugin, inputs, outputs, 1024); + //!!! // if (plugin) plugin->processReplacing(plugin, inputs, outputs, 1024); // remoteVSTServerInstance->dispatch(); //!!! need to at least maintain the pretence of running the thing - sleep(1); //!!! + usleep(30); //!!! if (exiting) return 0; } @@ -364,7 +398,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) 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); + if (!hosturl) hosturl = strndup(cmdline + offset, ci - offset); else if (!pluginlibname) pluginlibname = strndup(cmdline + offset, ci - offset); else if (!label) { label = strndup(cmdline + offset, ci - offset); @@ -386,7 +420,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) } } - if (!serverurl || !serverurl[0] || + if (!hosturl || !hosturl[0] || !pluginlibname || !pluginlibname[0] || !label || !label[0] || !friendlyname || !friendlyname[0]) { @@ -400,8 +434,47 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) cout << "Loading \"" << libname << "\"... "; if (debugLevel > 0) cout << endl; - char libPath[1024]; +// char libPath[1024]; HINSTANCE libHandle = 0; + + std::vector vstPath = Paths::getPath + ("VST_PATH", "/usr/local/lib/vst:/usr/lib/vst", "/vst"); + + for (size_t i = 0; i < vstPath.size(); ++i) { + + std::string vstDir = vstPath[i]; + std::string libPath; + + if (vstDir[vstDir.length()-1] == '/') { + libPath = vstDir + libname; + } else { + libPath = vstDir + "/" + libname; + } + + libHandle = LoadLibrary(libPath.c_str()); + if (debugLevel > 0) { + cerr << "dssi-vst_gui[1]: " << (libHandle ? "" : "not ") + << "found in " << libPath << endl; + } + + if (!libHandle) { + if (home && home[0] != '\0') { + if (libPath.substr(0, strlen(home)) == home) { + libPath = libPath.substr(strlen(home) + 1); + } + libHandle = LoadLibrary(libPath.c_str()); + if (debugLevel > 0) { + cerr << "dssi-vst_gui[1]: " << (libHandle ? "" : "not ") + << "found in " << libPath << endl; + } + } + } + + if (libHandle) break; + } + +#ifdef NOT_DEFINED + char *vstDir = getenv("VSTI_DIR"); if (vstDir) { @@ -469,6 +542,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) cerr << "dssi-vst_gui[1]: $VST_DIR not set" << endl; } } +#endif if (!libHandle) { libHandle = LoadLibrary(libname); @@ -480,6 +554,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) if (!libHandle) { cerr << "dssi-vst_gui: ERROR: Couldn't load VST DLL \"" << libname << "\"" << endl; + //!!! pop up an error dialog return 1; } @@ -497,6 +572,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) if (!getInstance) { cerr << "dssi-vst_gui: ERROR: VST entrypoint \"" << PLUGIN_ENTRY_POINT << "\" not found in DLL \"" << libname << "\"" << endl; + //!!! pop up an error dialog return 1; } else if (debugLevel > 0) { cerr << "dssi-vst_gui[1]: VST entrypoint \"" << PLUGIN_ENTRY_POINT @@ -522,19 +598,12 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) if (!(plugin->flags & effFlagsHasEditor)) { cerr << "dssi-vst_gui: ERROR: Instrument has no GUI (required)" << endl; + //!!! pop up an error dialog return 1; } else if (debugLevel > 0) { cerr << "dssi-vst_gui[1]: synth has a GUI" << endl; } - if (!plugin->flags & effFlagsCanReplacing) { - cerr << "dssi-vst_gui: ERROR: Instrument does not support processReplacing (required)" - << endl; - return 1; - } else if (debugLevel > 0) { - cerr << "dssi-vst_gui[1]: synth supports processReplacing" << endl; - } - cout << "Initialising Windows subsystem... "; if (debugLevel > 0) cout << endl; @@ -593,24 +662,26 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) cout << "done" << endl; - lo_server_thread thread = lo_server_thread_new(NULL, osc_error); - lo_server_thread_add_method(thread, "/dssi/control", "if", control_handler, 0); - lo_server_thread_add_method(thread, "/dssi/program", "ii", program_handler, 0); - lo_server_thread_add_method(thread, "/dssi/configure", "ss", configure_handler, 0); - lo_server_thread_add_method(thread, "/dssi/show", "", show_handler, 0); - lo_server_thread_add_method(thread, "/dssi/hide", "", hide_handler, 0); - lo_server_thread_add_method(thread, "/dssi/quit", "", quit_handler, 0); - lo_server_thread_add_method(thread, NULL, NULL, debug_handler, 0); - lo_server_thread_start(thread); + oscserver = lo_server_new(NULL, osc_error); + lo_server_add_method(oscserver, "/dssi/control", "if", control_handler, 0); + lo_server_add_method(oscserver, "/dssi/program", "ii", program_handler, 0); + lo_server_add_method(oscserver, "/dssi/configure", "ss", configure_handler, 0); + lo_server_add_method(oscserver, "/dssi/show", "", show_handler, 0); + lo_server_add_method(oscserver, "/dssi/hide", "", hide_handler, 0); + lo_server_add_method(oscserver, "/dssi/quit", "", quit_handler, 0); + lo_server_add_method(oscserver, NULL, NULL, debug_handler, 0); - cout << "started lo thread (url is " << lo_server_thread_get_url(thread) << ")" << endl; + hosthostname = lo_url_get_hostname(hosturl); + hostport = lo_url_get_port(hosturl); + hostpath = lo_url_get_path(hosturl); - lo_address hostaddr = lo_address_new(lo_url_get_hostname(serverurl), - lo_url_get_port(serverurl)); + cout << "created lo server (url is " << lo_server_get_url(oscserver) << ") - update path is " << std::string(hostpath) << "/update" << endl; + + lo_address hostaddr = lo_address_new(hosthostname, hostport); lo_send(hostaddr, - (std::string(lo_url_get_path(serverurl)) + "/update").c_str(), + (std::string(hostpath) + "/update").c_str(), "s", - (std::string(lo_server_thread_get_url(thread)) + "dssi").c_str()); + (std::string(lo_server_get_url(oscserver)) + "dssi").c_str()); DWORD threadId = 0; HANDLE threadHandle = CreateThread(0, 0, AudioThreadMain, 0, 0, &threadId); @@ -625,6 +696,7 @@ WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow) MSG msg; exiting = false; + while (GetMessage(&msg, 0, 0, 0)) { DispatchMessage(&msg); if (exiting) break; diff --git a/paths.cpp b/paths.cpp new file mode 100644 index 0000000..acf621a --- /dev/null +++ b/paths.cpp @@ -0,0 +1,40 @@ +// -*- c-basic-offset: 4 -*- + +/* + dssi-vst: a DSSI plugin wrapper for VST effects and instruments + Copyright 2004 Chris Cannam +*/ + +#include "paths.h" + +#include + +std::vector +Paths::getPath(std::string envVar, std::string deflt, std::string defltHomeRelPath) +{ + std::vector pathList; + std::string path; + + char *cpath = getenv(envVar.c_str()); + if (cpath) path = cpath; + + if (path == "") { + path = deflt; + char *home = getenv("HOME"); + if (home && (defltHomeRelPath != "")) { + path = std::string(home) + defltHomeRelPath + ":" + path; + } + std::cerr << envVar << " not set, defaulting to " << path << std::endl; + } + + std::string::size_type index = 0, newindex = 0; + + while ((newindex = path.find(':', index)) < path.size()) { + pathList.push_back(path.substr(index, newindex - index)); + index = newindex + 1; + } + + pathList.push_back(path.substr(index)); + + return pathList; +} diff --git a/paths.h b/paths.h new file mode 100644 index 0000000..9adb030 --- /dev/null +++ b/paths.h @@ -0,0 +1,23 @@ + +// -*- c-basic-offset: 4 -*- + +/* + dssi-vst: a DSSI plugin wrapper for VST effects and instruments + Copyright 2004 Chris Cannam +*/ + +#ifndef _PATHS_H_ +#define _PATHS_H_ + +#include +#include + +class Paths +{ +public: + static std::vector getPath(std::string envVar, + std::string deflt, + std::string defltHomeRelPath); +}; + +#endif diff --git a/rdwrops.cpp b/rdwrops.cpp index 9fddca0..2c06cb6 100644 --- a/rdwrops.cpp +++ b/rdwrops.cpp @@ -7,25 +7,35 @@ #include "rdwrops.h" +#include + + extern void rdwr_tryRead(int fd, void *buf, size_t count, const char *file, int line) { ssize_t r = 0; while ((r = read(fd, buf, count)) < (ssize_t)count) { - - if (r < 0) { - char message[100]; - sprintf(message, "Read failed on fd %d at %s:%d", fd, file, line); - perror(message); - throw RemotePluginClosedException(); - } else if (r == 0) { + + if (r == 0) { // end of file throw RemotePluginClosedException(); + } else if (r < 0) { + if (errno != EAGAIN) { + char message[100]; + sprintf(message, "Read failed on fd %d at %s:%d", fd, file, line); + perror(message); + throw RemotePluginClosedException(); + } + r = 0; } buf = (void *)(((char *)buf) + r); count -= r; + + if (count > 0) { + usleep(20); + } } } diff --git a/remotepluginclient.cpp b/remotepluginclient.cpp index 2a17776..1e1c65e 100644 --- a/remotepluginclient.cpp +++ b/remotepluginclient.cpp @@ -86,7 +86,6 @@ RemotePluginClient::RemotePluginClient() : m_shmFd = open(m_shmFileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (m_shmFd < 0) { - //!!! unlink all on errors cleanup(); throw((std::string)"Failed to open or create shared memory file"); } @@ -100,12 +99,29 @@ RemotePluginClient::~RemotePluginClient() void RemotePluginClient::syncStartup() { - //!!! First one should be done using a nonblocking sleeping loop, with a - // timeout for remote plugin failure + // The first (write) fd we open in a nonblocking call, with a + // short retry loop so we can easily give up if the other end + // doesn't appear to be responding - if ((m_controlRequestFd = open(m_controlRequestFileName, O_WRONLY)) < 0) { + bool connected = false; + + for (int attempt = 0; attempt < 6; ++attempt) { + + if ((m_controlRequestFd = + open(m_controlRequestFileName, O_WRONLY | O_NONBLOCK)) >= 0) { + connected = true; + break; + } else if (errno != ENXIO) { + // an actual error occurred + break; + } + + sleep(1); + } + + if (!connected) { cleanup(); - throw((std::string)"Failed to open FIFO"); + throw((std::string)"Plugin server timed out on startup"); } if ((m_controlResponseFd = open(m_controlResponseFileName, O_RDONLY)) < 0) { diff --git a/remotevstclient.cpp b/remotevstclient.cpp index b874f11..4209e22 100644 --- a/remotevstclient.cpp +++ b/remotevstclient.cpp @@ -9,10 +9,13 @@ #include #include +#include #include #include +#include #include "rdwrops.h" +#include "paths.h" RemoteVSTClient::RemoteVSTClient(std::string dllName) : RemotePluginClient() @@ -21,19 +24,64 @@ RemoteVSTClient::RemoteVSTClient(std::string dllName) : std::string arg = dllName + "," + getFileIdentifiers(); const char *argStr = arg.c_str(); - std::cerr << "RemoteVSTClient: executing wine dssi-vst-server.exe.so " << argStr << std::endl; - - if ((child = fork()) < 0) { - cleanup(); - throw((std::string)"Fork failed"); - } else if (child == 0) { // child process - if (execlp("wine", "wine", "dssi-vst-server.exe.so", argStr, 0)) { - perror("Exec failed"); - exit(1); + // We want to run the dssi-vst-server script, which runs wine + // dssi-vst-server.exe.so. We expect to find this script in the + // same subdirectory of a directory in the DSSI_PATH as a host + // would look for the GUI for this plugin: one called dssi-vst. + // See also RemoteVSTClient::queryPlugins below. + + std::vector dssiPath = Paths::getPath + ("DSSI_PATH", "/usr/local/lib/dssi:/usr/lib/dssi", "/.dssi"); + + bool found = false; + + for (size_t i = 0; i < dssiPath.size(); ++i) { + + std::string subDir = dssiPath[i] + "/dssi-vst"; + + DIR *directory = opendir(subDir.c_str()); + if (!directory) { + continue; + } + closedir(directory); + + struct stat st; + std::string fileName = subDir + "/dssi-vst-server"; + + if (stat(fileName.c_str(), &st)) { + continue; + } + + if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) || + !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + + std::cerr << "RemoteVSTClient: file " << fileName + << " exists but can't be executed" << std::endl; + continue; + } + + found = true; + + std::cerr << "RemoteVSTClient: executing " + << fileName << " " << argStr << std::endl; + + if ((child = fork()) < 0) { + cleanup(); + throw((std::string)"Fork failed"); + } else if (child == 0) { // child process + if (execlp(fileName.c_str(), fileName.c_str(), argStr, 0)) { + perror("Exec failed"); + exit(1); + } } } - syncStartup(); + if (!found) { + cleanup(); + throw((std::string)"Failed to find dssi-vst-server executable"); + } else { + syncStartup(); + } } RemoteVSTClient::~RemoteVSTClient() @@ -43,37 +91,117 @@ RemoteVSTClient::~RemoteVSTClient() void RemoteVSTClient::queryPlugins(std::vector &plugins) { - char tmpFileBase[60]; + char fifoFile[60]; - sprintf(tmpFileBase, "/tmp/rplugin_qry_XXXXXX"); - if (mkstemp(tmpFileBase) < 0) { + sprintf(fifoFile, "/tmp/rplugin_qry_XXXXXX"); + if (mkstemp(fifoFile) < 0) { throw((std::string)"Failed to obtain temporary filename"); } - unlink(tmpFileBase); - if (mkfifo(tmpFileBase, 0666)) { //!!! what permission is correct here? - perror(tmpFileBase); + unlink(fifoFile); + if (mkfifo(fifoFile, 0666)) { //!!! what permission is correct here? + perror(fifoFile); throw((std::string)"Failed to create FIFO"); } + // We open the fd nonblocking, then start the scanner, then wait + // to see whether the scanner starts sending anything on it. If + // no input is available after a certain time, give up. + + int fd = -1; + + if ((fd = open(fifoFile, O_RDONLY | O_NONBLOCK)) < 0) { + unlink(fifoFile); + throw((std::string)"Failed to open FIFO"); + } + + // We want to run the dssi-vst-scanner script, which runs wine + // dssi-vst-scanner.exe.so. We expect to find this script in the + // same subdirectory of a directory in the DSSI_PATH as a host + // would look for the GUI for this plugin: one called dssi-vst. + // See also the RemoteVSTClient constructor above. + + std::vector dssiPath = Paths::getPath + ("DSSI_PATH", "/usr/local/lib/dssi:/usr/lib/dssi", "/.dssi"); + + bool found = false; pid_t child; - - if ((child = fork()) < 0) { - unlink(tmpFileBase); - throw((std::string)"Fork failed"); - } else if (child == 0) { // child process - if (execlp("wine", "wine", "dssi-vst-scanner.exe.so", tmpFileBase, 0)) { - perror("Exec failed"); - exit(1); + + for (size_t i = 0; i < dssiPath.size(); ++i) { + + std::string subDir = dssiPath[i] + "/dssi-vst"; + + DIR *directory = opendir(subDir.c_str()); + if (!directory) { + continue; + } + closedir(directory); + + struct stat st; + std::string fileName = subDir + "/dssi-vst-scanner"; + + if (stat(fileName.c_str(), &st)) { + continue; } + + if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) || + !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + + std::cerr << "RemoteVSTClient: file " << fileName + << " exists but can't be executed" << std::endl; + continue; + } + + found = true; + + std::cerr << "RemoteVSTClient: executing " + << fileName << " " << fifoFile << std::endl; + + if ((child = fork()) < 0) { + unlink(fifoFile); + throw((std::string)"Fork failed"); + } else if (child == 0) { // child process + if (execlp(fileName.c_str(), fileName.c_str(), fifoFile, 0)) { + perror("Exec failed"); + unlink(fifoFile); + exit(1); + } + } + } + + if (!found) { + unlink(fifoFile); + throw((std::string)"Failed to find dssi-vst-scanner executable"); } - //!!! again, should do this via nonblocking loop + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN; + int sec; - int fd = -1; - if ((fd = open(tmpFileBase, O_RDONLY)) < 0) { - unlink(tmpFileBase); - throw((std::string)"Failed to open FIFO"); + for (sec = 0; sec < 6; ++sec) { + + int rv = poll(&pfd, 1, 1000); + + if (rv < 0) { + if (errno == EINTR || errno == EAGAIN) { + // try again + sleep(1); + continue; + } else { + close(fd); + unlink(fifoFile); + throw ((std::string)"Plugin scanner startup failed."); + } + } else if (rv > 0) { + break; + } + } + + if (sec >= 6) { + close(fd); + unlink(fifoFile); + throw ((std::string)"Plugin scanner timed out on startup."); } try { @@ -136,6 +264,6 @@ RemoteVSTClient::queryPlugins(std::vector &plugins) } close(fd); - unlink(tmpFileBase); + unlink(fifoFile); }