Browse files

Initial import.

  • Loading branch information...
0 parents commit 8ab8f7435759244b2ac29d34370431f3490e029c dwu committed Feb 20, 2011
10 .classpath
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="lib" path="lib/clojure-contrib.jar"/>
+ <classpathentry kind="lib" path="lib/clojure.jar" sourcepath="/Clojure"/>
+ <classpathentry kind="lib" path="lib/jVSTsYstem-1.0beta.jar"/>
+ <classpathentry kind="lib" path="lib/jVSTwRapper-1.0beta.jar"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
23 .project
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>ClojureVST</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>ccw.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>ccw.nature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
19 clojurevst.jardesc
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<jardesc>
+ <jar path="ClojureVST/dist/clojurevst.jar"/>
+ <options buildIfNeeded="true" compress="true" descriptionLocation="/ClojureVST/clojurevst.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
+ <storedRefactorings deprecationInfo="true" structuralOnly="false"/>
+ <selectedProjects/>
+ <manifest generateManifest="true" manifestLocation="/ClojureVST/manifest.mf" manifestVersion="1.0" reuseManifest="false" saveManifest="true" usesManifest="true">
+ <sealing sealJar="false">
+ <packagesToSeal/>
+ <packagesToUnSeal/>
+ </sealing>
+ </manifest>
+ <selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
+ <javaElement handleIdentifier="=ClojureVST/src"/>
+ <file path="/ClojureVST/.classpath"/>
+ <file path="/ClojureVST/todo.txt"/>
+ <file path="/ClojureVST/.project"/>
+ </selectedElements>
+</jardesc>
38 dist/clojurevst-linux.ini
@@ -0,0 +1,38 @@
+ClojureVSTPluginFile=de/flupp/clojurevst/plugins/2polelp.clj
+ClojureVSTPluginNamespace=de.flupp.clojurevst.clj2polelp
+
+#ClojureVSTPluginFile=de/flupp/clojurevst/plugins/cljdelay.clj
+#ClojureVSTPluginNamespace=de.flupp.clojurevst.cljdelay
+
+#ClojureVSTPluginFile=de/flupp/clojurevst/plugins/cljstereogain.clj
+#ClojureVSTPluginNamespace=de.flupp.clojurevst.cljstereogain
+
+#ClojureVSTPluginFile=de/flupp/clojurevst/plugins/cljgain.clj
+#ClojureVSTPluginNamespace=de.flupp.clojurevst.cljgain
+
+ClojureVSTPluginReload=true
+
+PluginClass=de/flupp/clojurevst/ClojureVSTPluginProxy
+
+#Classpath for distribution setup (notice clojurevst.jar)
+#ClassPath={WrapperPath}/jVSTwRapper-1.0beta.jar:{WrapperPath}/clojure.jar:{WrapperPath}/clojure-contrib.jar:{WrapperPath}/clojurevst.jar
+
+#Classpath for TEST setup (notice "../bin" --> directly use eclipse compile output)
+ClassPath={WrapperPath}/../lib/jVSTwRapper-1.0beta.jar:{WrapperPath}/../lib/clojure.jar:{WrapperPath}/../lib/clojure-contrib.jar:{WrapperPath}/../bin/
+
+SystemClassPath={WrapperPath}/../lib/jVSTsYstem-1.0beta.jar:{WrapperPath}/../lib/clojure.jar:{WrapperPath}/../lib/clojure-contrib.jar:{WrapperPath}/../src/
+IsLoggingEnabled=1
+AttachToNativePluginWindow=1
+
+#JVMOption1=-javaagent:/home/privat/opt/profiler4j-1.0-beta2/agent.jar
+
+#JVMOption1=-Xdebug
+#JVMOption2=-Xnoagent
+#JVMOption3=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#JVMOption4=-Djava.compiler=NONE
+
+#JVMOption1=-Xnoclassgc
+#JVMOption2=-Xloggc:c:/gclog.txt
+
+#JVMOption1=-verbose:jni
+#JVMOption2=-Xcheck:jni
BIN dist/clojurevst-linux.so
Binary file not shown.
29 dist/clojurevst-win.ini
@@ -0,0 +1,29 @@
+ClojureVSTPluginFile=cljdelay.clj
+ClojureVSTPluginNamespace=de.flupp.clojurevst.cljdelay
+ClojureVSTPluginReload=false
+
+PluginClass=de/flupp/clojurevst/ClojureVSTPluginProxy
+#PluginClass=jvst/examples/jaydlay/JayDLay
+#PluginUIClass=jvst/examples/jaydlay/JayDLayGUI
+
+ClassPath={WrapperPath}\jVSTwRapper-1.0beta.jar;{WrapperPath}\jVSTeXamples-1.0beta.jar;{WrapperPath}\clojure.jar;{WrapperPath}\clojure-contrib.jar;{WrapperPath}\clojurevst.jar;{WrapperPath}\profile.jar
+SystemClassPath={WrapperPath}\jVSTsYstem-1.0beta.jar;{WrapperPath}\clojure.jar;{WrapperPath}\clojure-contrib.jar;{WrapperPath}\profile.jar
+IsLoggingEnabled=1
+#CustomJVMLocation=C:\Program Files\Java\JDK\...
+#CustomJVMRegistryKey=Software\\MyCompanyName\\JavaRuntimeLocation
+#RequestJVMVersion=1.4
+AttachToNativePluginWindow=1
+
+#JVMOption1=-javaagent:profile.jar
+#JVMOption2=-Dprofile.properties=profile.properties
+
+#JVMOption1=-Xdebug
+#JVMOption2=-Xnoagent
+#JVMOption3=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#JVMOption4=-Djava.compiler=NONE
+
+#JVMOption1=-Xnoclassgc
+#JVMOption2=-Xloggc:c:\gclog.txt
+
+#JVMOption1=-verbose:jni
+#JVMOption2=-Xcheck:jni
BIN dist/clojurevst.jar
Binary file not shown.
BIN lib/clojure-contrib.jar
Binary file not shown.
BIN lib/clojure.jar
Binary file not shown.
BIN lib/jVSTsYstem-1.0beta.jar
Binary file not shown.
BIN lib/jVSTwRapper-1.0beta.jar
Binary file not shown.
2 manifest.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+
17 profiler4j-settings.p4j
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Profiler4jProject>
+ <Host>localhost</Host>
+ <Port>7890</Port>
+ <Rules access="PRIVATE" beanProps="true">
+ <Rule action="REJECT">org.apache.*(*)</Rule>
+ <Rule action="REJECT">org.jboss.*(*)</Rule>
+ <Rule action="REJECT">net.sf.jasperreports.*(*)</Rule>
+ <Rule action="REJECT">bsh.*(*)</Rule>
+ <Rule action="REJECT">EDU.oswego.*(*)</Rule>
+ <Rule action="REJECT">org.eclipse.*(*)</Rule>
+ <Rule action="REJECT">org.hsqldb.*(*)</Rule>
+ <Rule action="REJECT">clojure.*__init.*(*)</Rule>
+ <Rule action="ACCEPT">*(*)</Rule>
+ </Rules>
+</Profiler4jProject>
+
460 src/de/flupp/clojurevst/ClojureVSTPluginProxy.java
@@ -0,0 +1,460 @@
+package de.flupp.clojurevst;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.Hashtable;
+
+import jvst.wrapper.VSTPluginAdapter;
+import clojure.lang.Var;
+import de.flupp.clojurevst.config.ClojureVSTPluginConfig;
+
+public class ClojureVSTPluginProxy extends VSTPluginAdapter {
+
+ private ClojureVSTPluginConfig pluginConfig;
+ private Var cljProcessReplacing;
+ private boolean suspendProcessing = false;
+ private boolean variablesBound = false;
+
+ public ClojureVSTPluginProxy(long wrapper) throws Exception {
+ super(wrapper);
+
+ loadPlugin(ProxyTools.getIniFileName(ProxyTools.getResourcesFolder(getLogBasePath()), getLogFileName()));
+ setProgram(0);
+
+ if (pluginConfig.isPluginReload()) {
+ log("Starting Watcher thread to reload plugin if any .clj file changed");
+ new Watcher(this).start();
+ }
+
+ log("[ClojureVSTPluginProxy] Successfully loaded Clojure VST plugin " + pluginConfig.getPluginNamespace() + " from file " + pluginConfig.getPluginFilename());
+ }
+
+ public void loadPlugin(String iniFileName) throws Exception {
+ pluginConfig = new ClojureVSTPluginConfig(iniFileName);
+ cljProcessReplacing = pluginConfig.getProcessReplacing();
+
+ setNumInputs(pluginConfig.getNumInputs());
+ setNumOutputs(pluginConfig.getNumOutputs());
+ canProcessReplacing(pluginConfig.canProcessReplacing());
+ canMono(pluginConfig.canMono());
+ setUniqueID(pluginConfig.getUniqueId());
+ }
+
+ public int canDo(String feature) {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_CAN_DO);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.canDo(feature);
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke(feature);
+ return (Integer)result;
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::canDo] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public int getPlugCategory() {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PLUG_CATEGORY);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getPluginCategory();
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke();
+ return (Integer)result;
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getPlugCategory] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public String getProductString() {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PRODUCT_STRING);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getProductString();
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke();
+ return result.toString();
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getProductString] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ // TODO Only needed if plugin supports more than one category
+ public String getProgramNameIndexed(int arg0, int arg1) {
+ return "TODO";
+ }
+
+ public String getVendorString() {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_VENDOR_STRING);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getVendorString();
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke();
+ return result.toString();
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getVendorString] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public boolean setBypass(boolean arg0) {
+ // TODO Currently no support for software bypass
+ return false;
+ }
+
+ public boolean string2Parameter(int idx, String value) {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_STRING2PARAMETER);
+ if (func == null) {
+ // use generic method implementation
+ try {
+ setParameter(idx, Float.parseFloat(value));
+ return true;
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::string2Parameter] Unable to set value " + value + " for parameter index " + idx);
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke(idx, value);
+ return (Boolean)result;
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::string2Parameter] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public int getNumParams() {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_NUM_PARAMS);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getNumParams();
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke();
+ return (Integer)result;
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getNumParams] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public int getNumPrograms() {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_NUM_PROGRAMS);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getNumPrograms();
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke();
+ return (Integer)result;
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getNumPrograms] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public float getParameter(int idx) {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PARAMETER);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getParameterFloatCached(idx);
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke();
+ return ((Double)result).floatValue();
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getParameter] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public String getParameterDisplay(int idx) {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PARAMETER_DISPLAY);
+ if (func == null) {
+ // use generic method implementation
+ if (pluginConfig.getParameter(idx).hasMultiplier()) {
+ return Float.toString(pluginConfig.getParameterFloatCached(idx) * pluginConfig.getParameter(idx).getMultiplier());
+ } else {
+ return Float.toString(pluginConfig.getParameterFloatCached(idx));
+ }
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke(idx);
+ return result.toString();
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getParameterDisplay] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public String getParameterLabel(int idx) {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PARAMETER_LABEL);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getParameter(idx).getLabel();
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke(idx);
+ return result.toString();
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getParameterLabel] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public String getParameterName(int idx) {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PARAMETER_NAME);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getParameter(idx).getName();
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke(idx);
+ return result.toString();
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getParameterName] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public int getProgram() {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PROGRAM);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getCurrentProgram();
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke();
+ return (Integer)result;
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getProgram] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public String getProgramName() {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PROGRAM_NAME);
+ if (func == null) {
+ // use generic method implementation
+ return pluginConfig.getProgram(pluginConfig.getCurrentProgram()).getName();
+ } else {
+ try {
+ // use method implementation in plugin
+ Object result = func.invoke();
+ return result.toString();
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::getProgramName] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public void processReplacing(float[][] inputs, float[][] outputs, int samples) {
+ if (suspendProcessing)
+ return;
+ if (!variablesBound)
+ pluginConfig.bindThreadBoundVariables();
+
+ try {
+ cljProcessReplacing.invoke(inputs, outputs);
+ } catch (Exception e) {
+ log("[CloureVSTPluginProxy::processReplacing] Cannot execute process-replacing.");
+ log(ProxyTools.getStackTraceText(e));
+ throw(new IllegalStateException(e));
+ }
+ }
+
+ /**
+ * Generic implementation of accumulating process. Should only be used
+ * if the plugin does not implement "process" itself and the host
+ * application demands for it.
+ *
+ * TODO: needs testing
+ *
+ * @see jvst.wrapper.VSTPluginAdapter#process(float[][], float[][], int)
+ */
+ public void process(float[][] inputs, float[][] outputs, int samples) {
+ if (suspendProcessing)
+ return;
+ if (!variablesBound)
+ pluginConfig.bindThreadBoundVariables();
+
+ float[][] originalOutputs = new float[outputs.length][outputs[0].length]; //assume quadratic (note: not rectangular!) array
+ System.arraycopy(outputs, 0, originalOutputs, 0, outputs.length);
+
+ //fake process() through a call to processReplacing
+ processReplacing(inputs, outputs, samples);
+
+ // accumulate output and original output
+ for (int i = 0; i < outputs.length; i++) {
+ for (int j = 0; j < outputs[i].length; j++) {
+ outputs[i][j] = outputs[i][j] + originalOutputs[i][j];
+ outputs[i][j] = outputs[i][j] + originalOutputs[i][j];
+ }
+ }
+ }
+
+ public void setParameter(int idx, float value) {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_SET_PARAMETER);
+ if (func == null) {
+ // use generic method implementation
+ pluginConfig.setParameter(idx, value);
+ } else {
+ try {
+ // use method implementation in plugi
+ func.invoke(idx, value);
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::setParameter] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public void setProgram(int currentProgram) {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_SET_PROGRAM);
+ if (func == null) {
+ // use generic method implementation
+ pluginConfig.setProgram(currentProgram);
+ } else {
+ try {
+ // use method implementation in plugi
+ func.invoke(currentProgram);
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::setProgram] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+ public void setProgramName(String programName) {
+ Var func = pluginConfig.getMethod(PluginConstants.METHOD_SET_PROGRAM_NAME);
+ if (func == null) {
+ // use generic method implementation
+ pluginConfig.setCurrentProgramName(programName);
+ } else {
+ try {
+ // use method implementation in plugi
+ func.invoke(programName);
+ } catch (Exception e) {
+ log("[ClojureVSTPluginProxy::setProgramName] Exception during method execution");
+ log(e.getStackTrace().toString());
+ throw(new IllegalStateException(e));
+ }
+ }
+ }
+
+
+
+ private class Watcher extends Thread {
+ Hashtable<File, Long> toWatch = new Hashtable<File, Long>();
+
+ ClojureVSTPluginProxy proxy = null;
+
+ public Watcher(ClojureVSTPluginProxy proxy) throws Exception {
+ this.proxy = proxy;
+
+ //find the location where we loaded the main plugin file from...
+ File f = new File(this.getContextClassLoader().getResource(proxy.pluginConfig.getPluginFilename()).toURI()).getParentFile();
+ log("Watching all .clj files in path: " + f + " for changes");
+ File[] files = f.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {return name.toLowerCase().endsWith(".clj");}
+ });
+ for (File file : files) {
+ toWatch.put(file, file.lastModified());
+ }
+ }
+
+ public void run() {
+ boolean modified = false;
+
+ while(true) {
+ try {
+ //log("### +++ ### next round...");
+ for (File file : toWatch.keySet()) {
+ if (file.lastModified() > toWatch.get(file)) {
+ log("### File: " + file.getName() + " was just modified!");
+ toWatch.put(file, file.lastModified());
+ modified = true;
+ }
+ }
+ if (modified) reloadPlugin();
+ modified = false;
+ Thread.sleep(1000);
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void reloadPlugin() throws Exception {
+ int currentProgram = proxy.getProgram();
+
+ try {
+ log("Reloading Plugin!");
+
+ proxy.suspendProcessing = true;
+ proxy.loadPlugin(ProxyTools.getIniFileName(ProxyTools.getResourcesFolder(getLogBasePath()), getLogFileName()));
+ proxy.setProgram(currentProgram);
+
+ log("all good :-)");
+ proxy.suspendProcessing = false;
+ }
+ catch (Throwable t) {
+ log(ProxyTools.getStackTraceText(t));
+ // enable process() calls on the old plugin again
+ log("### Plugin problem detected, using the version that ran before. Check the error log below for details");
+ proxy.suspendProcessing = false;
+ }
+ }
+ }
+
+}
52 src/de/flupp/clojurevst/PluginConstants.java
@@ -0,0 +1,52 @@
+package de.flupp.clojurevst;
+
+public class PluginConstants {
+
+ // Plugin configuration (Clojure)
+ public static final String PLUGIN_CONFIG = "plugin-config";
+ public static final String PLUGIN_CONFIG_CAN_DO = "can-do";
+ public static final String PLUGIN_CONFIG_PARAMETERS = "params";
+ public static final String PLUGIN_CONFIG_UNIQUE_ID = "unique-id";
+ public static final String PLUGIN_CONFIG_PRODUCT_STRING = "product";
+ public static final String PLUGIN_CONFIG_NUM_INPUTS = "num-inputs";
+ public static final String PLUGIN_CONFIG_NUM_OUTPUTS = "num-outputs";
+ public static final String PLUGIN_CONFIG_CAN_PROCESS_REPLACING = "can-process-replacing";
+ public static final String PLUGIN_CONFIG_CAN_MONO = "can-mono";
+ public static final String PLUGIN_CONFIG_VENDOR_STRING = "vendor";
+ public static final String PLUGIN_CONFIG_PLUGIN_CATEGORY = "plugin-category";
+ public static final String PLUGIN_CONFIG_PROGRAMS = "programs";
+
+ // Plugin configuration (INI-File)
+ public static final String INI_PLUGIN_FILENAME = "ClojureVSTPluginFile";
+ public static final String INI_PLUGIN_NAMESPACE = "ClojureVSTPluginNamespace";
+ public static final String INI_PLUGIN_RELOAD = "ClojureVSTPluginReload";
+
+ // Parameter metadata
+ public static final String PARAM_NAME = "parameter-name";
+ public static final String PARAM_LABEL = "parameter-label";
+ public static final String PARAM_MULTIPLIER = "parameter-display-multiplier";
+ public static final String PARAM_VALUE_IN_PROGRAMS = "value-in-programs";
+ public static final String PARAM_THREAD_BOUND = "thread-bound";
+
+ // Plugin methods
+ public static final String METHOD_PROCESS_REPLACING = "process-replacing";
+ public static final String METHOD_GET_PARAMETER_DISPLAY = "get-parameter-display";
+ public static final String METHOD_GET_PARAMETER_LABEL = "get-parameter-label";
+ public static final String METHOD_CAN_DO = "can-do";
+ public static final String METHOD_GET_PLUG_CATEGORY = "get-plug-category";
+ public static final String METHOD_GET_PRODUCT_STRING = "get-product-string";
+ public static final String METHOD_GET_PROGRAM_NAME_INDEXED = "get-program-name-indexed";
+ public static final String METHOD_GET_VENDOR_STRING = "get-vendor-strin";
+ public static final String METHOD_SET_BYPASS = "set-bypass";
+ public static final String METHOD_STRING2PARAMETER = "string2parameter";
+ public static final String METHOD_GET_NUM_PARAMS = "get-num-params";
+ public static final String METHOD_GET_NUM_PROGRAMS = "get-num-programs";
+ public static final String METHOD_GET_PARAMETER = "get-parameter";
+ public static final String METHOD_GET_PARAMETER_NAME = "get-parameter-name";
+ public static final String METHOD_GET_PROGRAM = "get-program";
+ public static final String METHOD_GET_PROGRAM_NAME = "get-program-name";
+ public static final String METHOD_SET_PARAMETER = "set-parameter";
+ public static final String METHOD_SET_PROGRAM = "set-program";
+ public static final String METHOD_SET_PROGRAM_NAME = "set-program-name";
+
+}
43 src/de/flupp/clojurevst/ProxyTools.java
@@ -0,0 +1,43 @@
+package de.flupp.clojurevst;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Utility class (from opaz-plugdk; https://github.com/thbar/opaz-plugdk)
+ */
+public class ProxyTools {
+ // Although I wanted to use VSTPluginAdapter.RUNNING_MAC_X instead of this, it raises AWT thread errors.
+ // Sticking with this one for the moment.
+ public static boolean useMacOSX() {
+ String lcOSName = System.getProperty("os.name").toLowerCase();
+ return lcOSName.startsWith("mac os x");
+ }
+
+ public static String getResourcesFolder(String logBasePath) {
+ String resourcesFolder = logBasePath;
+ if (useMacOSX()) // mac os x tweak :o
+ resourcesFolder += "/../Resources";
+ return resourcesFolder;
+ }
+
+ public static String getIniFileName(String resourcesFolder, String logFileName) {
+ String iniFileName = logFileName.replaceAll("_java_stdout.txt","");
+ if (useMacOSX())
+ iniFileName += ".jnilib";
+ return resourcesFolder + "/" + iniFileName + ".ini";
+ }
+
+ /**
+ * Get stack trace of an exception as a string for logging purposes.
+ *
+ * @param e Exception
+ * @return
+ */
+ public static String getStackTraceText(Throwable e) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ return sw.getBuffer().toString();
+ }
+}
562 src/de/flupp/clojurevst/config/ClojureVSTPluginConfig.java
@@ -0,0 +1,562 @@
+package de.flupp.clojurevst.config;
+
+import java.io.FileInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import jvst.wrapper.VSTPluginAdapter;
+import clojure.lang.APersistentMap;
+import clojure.lang.IPersistentMap;
+import clojure.lang.Keyword;
+import clojure.lang.PersistentVector;
+import clojure.lang.RT;
+import clojure.lang.Symbol;
+import clojure.lang.Var;
+import de.flupp.clojurevst.PluginConstants;
+import de.flupp.clojurevst.ProxyTools;
+
+public class ClojureVSTPluginConfig {
+
+ private String pluginNamespace;
+ private String pluginFilename;
+ private boolean pluginReload;
+
+ private int uniqueId;
+ private String productString;
+ private String vendorString;
+ private String vendorVersion;
+ private int pluginCategory;
+ private int numInputs;
+ private int numOutputs;
+ private boolean canProcessReplacing;
+ private boolean canMono;
+ private int currentProgram;
+
+ private List<String> features;
+ private List<Parameter> parameters;
+ private List<Program> programs;
+ private Map<String, Var> methods;
+
+ private String iniFileName;
+ private Var cljLoadString;
+ private Var cljProcessReplacing;
+ private Var cljNsPublics;
+
+ public ClojureVSTPluginConfig(String iniFileName) throws Exception {
+ features = new ArrayList<String>();
+ parameters = new ArrayList<Parameter>();
+ programs = new ArrayList<Program>();
+ methods = new HashMap<String, Var>();
+
+ this.iniFileName = iniFileName;
+
+ cljLoadString = RT.var("clojure.core", "load-string");
+ cljNsPublics = RT.var("clojure.core", "ns-publics");
+
+ parseIni();
+ parseConfig();
+ parseMethods();
+ parseParameters();
+
+ VSTPluginAdapter.log("[PluginConfig] Successfully configured plugin "
+ + this.getPluginNamespace() + "/" + this.getPluginFilename() + "\n"
+ + this.toString());
+ }
+
+ public Var getProcessReplacing() {
+ return cljProcessReplacing;
+ }
+
+ public String getPluginNamespace() {
+ return pluginNamespace;
+ }
+
+ public void setPluginNamespace(String pluginNamespace) {
+ this.pluginNamespace = pluginNamespace;
+ }
+
+ public String getPluginFilename() {
+ return pluginFilename;
+ }
+
+ public void setPluginFilename(String pluginFilename) {
+ this.pluginFilename = pluginFilename;
+ }
+
+ public boolean isPluginReload() {
+ return pluginReload;
+ }
+
+ public void setPluginReload(boolean pluginReload) {
+ this.pluginReload = pluginReload;
+ }
+
+ public int getPluginCategory() {
+ return pluginCategory;
+ }
+
+ public void setPluginCategory(int pluginCategory) {
+ this.pluginCategory = pluginCategory;
+ }
+
+ public int getUniqueId() {
+ return uniqueId;
+ }
+
+ public void setUniqueId(String uniqueId) {
+ //extract the first 4 chars and compute a 32bit integer unique id
+ int id = 0;
+ for (int i=0; i<uniqueId.length() && i<4; i++)
+ id |= (uniqueId.charAt(i) << (i*8));
+
+ this.uniqueId = id;
+ }
+
+ public String getProductString() {
+ return productString;
+ }
+
+ public void setProductString(String productString) {
+ this.productString = productString;
+ }
+
+ public String getVendorString() {
+ return vendorString;
+ }
+
+ public void setVendorString(String vendorString) {
+ this.vendorString = vendorString;
+ }
+
+ public String getVendorVersion() {
+ return vendorVersion;
+ }
+
+ public void setVendorVersion(String vendorVersion) {
+ this.vendorVersion = vendorVersion;
+ }
+
+ public int getNumInputs() {
+ return numInputs;
+ }
+
+ public void setNumInputs(int numInputs) {
+ this.numInputs = numInputs;
+ }
+
+ public int getNumOutputs() {
+ return numOutputs;
+ }
+
+ public void setNumOutputs(int numOutputs) {
+ this.numOutputs = numOutputs;
+ }
+
+ public boolean canProcessReplacing() {
+ return canProcessReplacing;
+ }
+
+ public void setCanProcessReplacing(boolean canProcessReplacing) {
+ this.canProcessReplacing = canProcessReplacing;
+ }
+
+ public boolean canMono() {
+ return canMono;
+ }
+
+ public void setCanMono(boolean canMono) {
+ this.canMono = canMono;
+ }
+
+ public int getCurrentProgram() {
+ return currentProgram;
+ }
+
+ public void setProgram(int currentProgram) {
+ this.currentProgram = currentProgram;
+ for (int i = 0; i < parameters.size(); i++) {
+ resetParameter(i, parameters.get(i).getValue(currentProgram));
+ }
+ }
+
+ public void addProgram(Program program) {
+ programs.add(program);
+ }
+
+ public Program getProgram(int idx) {
+ return programs.get(idx);
+ }
+
+ public int getNumPrograms() {
+ return programs.size();
+ }
+
+ public void addFeature(String feature) {
+ features.add(feature);
+ }
+
+ public int canDo(String feature) {
+ if (features.contains(feature)) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
+ public void addParameter(Parameter param) {
+ parameters.add(param);
+ }
+
+ public void addParameter(String name, String label, String unit) {
+ Parameter param = new Parameter(name, label, unit);
+ parameters.add(param);
+ }
+
+ public void setParameter(int index, float value) {
+ resetParameter(index, value);
+ parameters.get(index).setValue(currentProgram, value);
+ }
+
+ public void resetParameter(int index, float value) {
+ try {
+ Var cljParameter = getCljVarForParameter(index);
+ cljParameter.doReset(value);
+ } catch (Exception e) {
+ VSTPluginAdapter.log("[PluginConfig::setParameter] Cannot set parameter " + parameters.get(0).getName() + " to value " + value);
+ VSTPluginAdapter.log(ProxyTools.getStackTraceText(e));
+ throw(new IllegalStateException(e));
+ }
+ }
+
+ public Parameter getParameter(int idx) {
+ return parameters.get(idx);
+ }
+
+ public int getNumParams() {
+ return parameters.size();
+ }
+
+ public void parseIni() {
+ try {
+ Properties iniProperties = new Properties();
+ iniProperties.load(new FileInputStream(iniFileName));
+
+ setPluginFilename(iniProperties.getProperty(PluginConstants.INI_PLUGIN_FILENAME));
+ setPluginNamespace(iniProperties.getProperty(PluginConstants.INI_PLUGIN_NAMESPACE));
+ setPluginReload(Boolean.parseBoolean(iniProperties.getProperty(PluginConstants.INI_PLUGIN_RELOAD)));
+
+ } catch (Exception e) {
+ VSTPluginAdapter.log("[PluginConfig::parseIni] Error parsing INI file " + iniFileName);
+ VSTPluginAdapter.log(ProxyTools.getStackTraceText(e));
+ throw(new IllegalStateException(e));
+ }
+ }
+
+ public void parseConfig() {
+ try {
+ RT.loadResourceScript(getPluginFilename());
+ Var cljConfig = RT.var(getPluginNamespace(), PluginConstants.PLUGIN_CONFIG);
+
+ Object result = cljConfig.get();
+ if (result instanceof APersistentMap) {
+ APersistentMap config = (APersistentMap) result;
+
+ setUniqueId(config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_UNIQUE_ID))).toString());
+ setProductString(config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_PRODUCT_STRING))).toString());
+ setNumInputs(Integer.valueOf(config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_NUM_INPUTS))).toString()));
+ setNumOutputs(Integer.valueOf(config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_NUM_OUTPUTS))).toString()));
+ setCanProcessReplacing(Boolean.valueOf(config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_CAN_PROCESS_REPLACING))).toString()));
+ setCanMono(Boolean.valueOf(config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_CAN_MONO))).toString()));
+ setVendorString(config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_VENDOR_STRING))).toString());
+ setPluginCategory(Integer.valueOf(config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_PLUGIN_CATEGORY))).toString()));
+
+ result = config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_CAN_DO)));
+ if (result instanceof PersistentVector) {
+ PersistentVector canDo = (PersistentVector) result;
+ for (Object o : canDo) {
+ if (o instanceof String) {
+ this.addFeature((String)o);
+ }
+ }
+ }
+ else throw new IllegalStateException("can-do not of type PersistentVector");
+
+ result = config.get(Keyword.intern(Symbol.intern(PluginConstants.PLUGIN_CONFIG_PROGRAMS)));
+ if (result instanceof PersistentVector) {
+ PersistentVector progs = (PersistentVector) result;
+ for (Object o : progs) {
+ if (o instanceof String) {
+ Program p = new Program();
+ p.setName(o.toString());
+ programs.add(p);
+ }
+ }
+ }
+ else {
+ Program p = new Program();
+ p.setName("Default");
+ programs.add(p);
+ }
+ }
+ else throw new IllegalStateException("var plugin-config not of type APersistentMap");
+ }
+ catch (IllegalStateException ix) {
+ throw ix;
+ }
+ catch (Exception e) {
+ VSTPluginAdapter.log("[PluginConfig::parseConfig] Error parsing config in plugin " + getPluginNamespace() + "/" + getPluginFilename());
+ VSTPluginAdapter.log(ProxyTools.getStackTraceText(e));
+ throw(new IllegalStateException(e));
+ }
+ }
+
+ private float getParameterFloatInternal(int index) {
+ try {
+ Var cljParam = getCljVarForParameter(index);
+ Object result = cljParam.get();
+ if (result instanceof Double) {
+ return ((Double)result).floatValue();
+ }
+ } catch (Exception e) {
+ VSTPluginAdapter.log("[PluginConfig::getParameterFloatInternal] Cannot get value for parameter " + parameters.get(0).getName() + " in plugin " + getPluginFilename());
+ VSTPluginAdapter.log(ProxyTools.getStackTraceText(e));
+ throw(new IllegalStateException(e));
+ }
+ return -1;
+ }
+
+ /**
+ * Get current parameter value from cache without querying the clojure runtime.
+ * Used e.g. for displaying purposes.
+ *
+ * @param index Index of queried parameter
+ * @return Float value of parameter
+ */
+ public float getParameterFloatCached(int index) {
+ return parameters.get(index).getValue(currentProgram);
+ }
+
+ public void parseParameters() {
+ int index = 0;
+ try {
+ Object result = cljNsPublics.invoke(Symbol.intern(getPluginNamespace()));
+ if (result instanceof APersistentMap) {
+ APersistentMap publics = (APersistentMap) result;
+ for (Object publicElement : publics.values()) {
+ if (publicElement instanceof Var) {
+ IPersistentMap meta = ((Var) publicElement).meta();
+ if (meta.containsKey(Keyword.intern(Symbol.intern(PluginConstants.PARAM_NAME)))) {
+ Parameter parameter = new Parameter();
+ parameters.add(index, parameter);
+ parameter.setVariableName(meta.valAt(Keyword.intern(Symbol.intern("name"))).toString());
+ parameter.setName(meta.valAt(Keyword.intern(Symbol.intern(PluginConstants.PARAM_NAME))).toString());
+ parameter.setLabel(meta.valAt(Keyword.intern(Symbol.intern(PluginConstants.PARAM_LABEL))).toString());
+
+ // check whether parameter multiplier exists
+ if (meta.containsKey(Keyword.intern(Symbol.intern(PluginConstants.PARAM_MULTIPLIER)))) {
+ parameter.setMultiplier(Float.valueOf(meta.valAt(Keyword.intern(Symbol.intern(PluginConstants.PARAM_MULTIPLIER))).toString()));
+ parameter.setHasMultiplier(true);
+ }
+
+ // check whether there are program values defined
+ if (meta.containsKey(Keyword.intern(Symbol.intern(PluginConstants.PARAM_VALUE_IN_PROGRAMS)))) {
+ result = meta.valAt(Keyword.intern(Symbol.intern(PluginConstants.PARAM_VALUE_IN_PROGRAMS)));
+ if (result instanceof PersistentVector) {
+ PersistentVector values = (PersistentVector) result;
+
+ for (int programIndex = 0; programIndex < values.size(); programIndex++) {
+ Object o = values.get(programIndex);
+ if (o instanceof Double) {
+ parameter.addValue(programIndex, ((Double)o).floatValue());
+ }
+ }
+ }
+ } else {
+ parameter.addValue(currentProgram, getParameterFloatInternal(index));
+ }
+
+ index++;
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ VSTPluginAdapter.log("[PluginConfig::parseParameters] Cannot parse params in plugin " + getPluginNamespace() + "/" + getPluginFilename());
+ VSTPluginAdapter.log(ProxyTools.getStackTraceText(e));
+ throw(new IllegalStateException(e));
+ }
+ }
+
+ public void bindThreadBoundVariables() {
+ try {
+ Object result = cljNsPublics.invoke(Symbol.intern(getPluginNamespace()));
+ if (result instanceof APersistentMap) {
+ APersistentMap publics = (APersistentMap) result;
+ for (Object publicElement : publics.values()) {
+ if (publicElement instanceof Var) {
+ IPersistentMap meta = ((Var) publicElement).meta();
+ if (meta.containsKey(Keyword.intern(Symbol.intern(PluginConstants.PARAM_THREAD_BOUND)))) {
+ if ("1".equals(meta.valAt(Keyword.intern(Symbol.intern(PluginConstants.PARAM_THREAD_BOUND))).toString())) {
+ Var.pushThreadBindings(RT.map(publicElement, ((Var)publicElement).get()));
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ VSTPluginAdapter.log("[PluginConfig::parseParameters] Cannot parse params in plugin " + getPluginNamespace() + "/" + getPluginFilename());
+ VSTPluginAdapter.log(ProxyTools.getStackTraceText(e));
+ throw(new IllegalStateException(e));
+ }
+ }
+
+ private Var getCljVar(String namespace, String variableName) {
+ try {
+ return RT.var(namespace, variableName);
+ } catch (Exception e) {
+ VSTPluginAdapter.log("[PluginConfig::getCljVar] Cannot retrieve variable " + namespace + "/" + variableName);
+ VSTPluginAdapter.log(ProxyTools.getStackTraceText(e));
+ throw(new IllegalStateException(e));
+ }
+ }
+
+ private Var getCljVarForParameter(int index) {
+ return getCljVar(getPluginNamespace(), parameters.get(index).getVariableName());
+ }
+
+ private String getParameterName(int index) {
+ return parameters.get(index).getName();
+ }
+
+ private String getParameterVariableName(int index) {
+ return parameters.get(index).getName();
+ }
+
+ private void parseMethods() {
+ try {
+ Object result = cljNsPublics.invoke(Symbol.intern(getPluginNamespace()));
+ if (result instanceof APersistentMap) {
+ APersistentMap publics = (APersistentMap) result;
+ for (Object publicElement : publics.values()) {
+ if (publicElement instanceof Var) {
+ IPersistentMap meta = ((Var) publicElement).meta();
+ if (meta.containsKey(Keyword.intern(Symbol.intern("arglists")))) {
+ String methodName = meta.valAt(Keyword.intern(Symbol.intern("name"))).toString();
+ if (PluginConstants.METHOD_PROCESS_REPLACING.equals(methodName)) {
+ // process-replacing
+ cljProcessReplacing = RT.var(getPluginNamespace(), PluginConstants.METHOD_PROCESS_REPLACING);
+ } else if (PluginConstants.METHOD_GET_PARAMETER_DISPLAY.equals(methodName)) {
+ // get-parameter-display
+ methods.put(PluginConstants.METHOD_GET_PARAMETER_DISPLAY, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_PARAMETER_DISPLAY));
+ } else if (PluginConstants.METHOD_GET_PARAMETER_LABEL.equals(methodName)) {
+ // get-parameter-label
+ methods.put(PluginConstants.METHOD_GET_PARAMETER_LABEL, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_PARAMETER_LABEL));
+ } else if (PluginConstants.METHOD_CAN_DO.equals(methodName)) {
+ // can-do
+ methods.put(PluginConstants.METHOD_CAN_DO, RT.var(getPluginNamespace(), PluginConstants.METHOD_CAN_DO));
+ } else if (PluginConstants.METHOD_GET_PLUG_CATEGORY.equals(methodName)) {
+ // get-plug-category
+ methods.put(PluginConstants.METHOD_GET_PLUG_CATEGORY, RT.var(getPluginNamespace(),PluginConstants.METHOD_GET_PLUG_CATEGORY));
+ } else if (PluginConstants.METHOD_GET_PRODUCT_STRING.equals(methodName)) {
+ // get-product-string
+ methods.put(PluginConstants.METHOD_GET_PRODUCT_STRING, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_PRODUCT_STRING));
+ } else if (PluginConstants.METHOD_GET_PROGRAM_NAME_INDEXED.equals(methodName)) {
+ // get-program-name-indexed
+ methods.put(PluginConstants.METHOD_GET_PROGRAM_NAME_INDEXED, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_PROGRAM_NAME_INDEXED));
+ } else if (PluginConstants.METHOD_GET_VENDOR_STRING.equals(methodName)) {
+ // get-vendor-string
+ methods.put(PluginConstants.METHOD_GET_VENDOR_STRING, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_VENDOR_STRING));
+ } else if (PluginConstants.METHOD_SET_BYPASS.equals(methodName)) {
+ // set-bypass
+ methods.put(PluginConstants.METHOD_SET_BYPASS, RT.var(getPluginNamespace(), PluginConstants.METHOD_SET_BYPASS));
+ } else if (PluginConstants.METHOD_STRING2PARAMETER.equals(methodName)) {
+ // string2parameter
+ methods.put(PluginConstants.METHOD_STRING2PARAMETER, RT.var(getPluginNamespace(), PluginConstants.METHOD_STRING2PARAMETER));
+ } else if (PluginConstants.METHOD_GET_NUM_PARAMS.equals(methodName)) {
+ // get-num-params
+ methods.put(PluginConstants.METHOD_GET_NUM_PARAMS, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_NUM_PARAMS));
+ } else if (PluginConstants.METHOD_GET_NUM_PROGRAMS.equals(methodName)) {
+ // get-num-programs
+ methods.put(PluginConstants.METHOD_GET_NUM_PROGRAMS, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_NUM_PROGRAMS));
+ } else if (PluginConstants.METHOD_GET_PARAMETER.equals(methodName)) {
+ // get-parameter
+ methods.put(PluginConstants.METHOD_GET_PARAMETER, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_PARAMETER));
+ } else if (PluginConstants.METHOD_GET_PARAMETER_NAME.equals(methodName)) {
+ // get-parameter-name
+ methods.put(PluginConstants.METHOD_GET_PARAMETER_NAME, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_PARAMETER_NAME));
+ } else if (PluginConstants.METHOD_GET_PROGRAM.equals(methodName)) {
+ // get-program
+ methods.put(PluginConstants.METHOD_GET_PROGRAM, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_PROGRAM));
+ } else if (PluginConstants.METHOD_GET_PROGRAM_NAME.equals(methodName)) {
+ // get-program-name
+ methods.put(PluginConstants.METHOD_GET_PROGRAM_NAME, RT.var(getPluginNamespace(), PluginConstants.METHOD_GET_PROGRAM_NAME));
+ } else if (PluginConstants.METHOD_SET_PARAMETER.equals(methodName)) {
+ // set-parameter
+ methods.put(PluginConstants.METHOD_SET_PARAMETER, RT.var(getPluginNamespace(), PluginConstants.METHOD_SET_PARAMETER));
+ } else if (PluginConstants.METHOD_SET_PROGRAM.equals(methodName)) {
+ // set-program
+ methods.put(PluginConstants.METHOD_SET_PROGRAM, RT.var(getPluginFilename(), PluginConstants.METHOD_SET_PROGRAM));
+ } else if (PluginConstants.METHOD_SET_PROGRAM_NAME.equals(methodName)) {
+ // set-program-name
+ methods.put(PluginConstants.METHOD_SET_PROGRAM_NAME, RT.var(getPluginNamespace(), PluginConstants.METHOD_SET_PROGRAM_NAME));
+ }
+ }
+ }
+ }
+ }
+
+ // check for required plugin methods
+ if (cljProcessReplacing == null) {
+ VSTPluginAdapter.log("[PluginConfig::parseMethods] Plugin does not define method process-replacing.");
+ throw(new IllegalStateException("[PluginConfig::parseMethods] Plugin does not define method process-replacing."));
+ }
+ } catch (Exception e) {
+ VSTPluginAdapter.log("[PluginConfig::parseMethods] Cannot parse methods in plugin " + getPluginNamespace() + "/" + getPluginFilename());
+ VSTPluginAdapter.log(ProxyTools.getStackTraceText(e));
+ throw(new IllegalStateException(e));
+ }
+ }
+
+ public void setCurrentProgramName(String programName) {
+ programs.get(currentProgram).setName(programName);
+ }
+
+ public Var getMethod(String methodName) {
+ return methods.get(methodName);
+ }
+
+ public String toString() {
+ final String TAB = " ";
+
+ String retValue = "";
+
+ retValue = "PluginConfig ( "
+ + super.toString() + TAB
+ + "pluginNamespace = " + this.pluginNamespace + TAB
+ + "pluginFilename = " + this.pluginFilename + TAB
+ + "pluginCategory = " + this.pluginCategory + TAB
+ + "uniqueId = " + this.uniqueId + TAB
+ + "productString = " + this.productString + TAB
+ + "vendorString = " + this.vendorString + TAB
+ + "vendorVersion = " + this.vendorVersion + TAB
+ + "numInputs = " + this.numInputs + TAB
+ + "numOutputs = " + this.numOutputs + TAB
+ + "canProcessReplacing = " + this.canProcessReplacing + TAB
+ + "canMono = " + this.canMono + TAB
+ + "currentProgram = " + this.currentProgram + TAB
+ + "features = " + this.features + TAB
+ + "parameters = " + this.parameters + TAB
+ + "programs = " + this.programs + TAB
+ + "iniFileName = " + this.iniFileName + TAB
+ + "cljLoadString = " + this.cljLoadString + TAB
+ + "cljProcessReplacing = " + this.cljProcessReplacing + TAB
+ + "cljNsPublics = " + this.cljNsPublics + TAB
+ + " )";
+
+ return retValue;
+ }
+
+}
99 src/de/flupp/clojurevst/config/Parameter.java
@@ -0,0 +1,99 @@
+package de.flupp.clojurevst.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Parameter {
+
+ private String variable;
+ private String name;
+ private String label;
+
+ private boolean bMultiplier;
+ private float multiplier;
+
+ private List<Float> values;
+
+ public Parameter() {
+ values = new ArrayList<Float>();
+ }
+
+ public Parameter(String variable, String name, String label) {
+ this();
+ this.variable = variable;
+ this.name = name;
+ this.label = label;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getVariableName() {
+ return variable;
+ }
+
+ public void setVariableName(String variable) {
+ this.variable = variable;
+ }
+
+ public float getValue(int program) {
+ return values.get(program);
+ }
+
+ public void addValue(int program, float value) {
+ values.add(program, value);
+ }
+
+ public void setValue(int program, float value) {
+ values.set(program, value);
+ }
+
+ public float getMultiplier() {
+ return multiplier;
+ }
+
+ public void setMultiplier(float multiplier) {
+ this.multiplier = multiplier;
+ }
+
+ public void setHasMultiplier(boolean hasMultiplier) {
+ this.bMultiplier = hasMultiplier;
+ }
+
+ public boolean hasMultiplier() {
+ return this.bMultiplier;
+ }
+
+ public String toString()
+ {
+ final String TAB = " ";
+
+ String retValue = "";
+
+ retValue = "Parameter ( "
+ + super.toString() + TAB
+ + "variable = " + this.variable + TAB
+ + "name = " + this.name + TAB
+ + "label = " + this.label + TAB
+ + "bMultiplier = " + this.bMultiplier + TAB
+ + "multiplier = " + this.multiplier + TAB
+ + "values = " + this.values + TAB
+ + " )";
+
+ return retValue;
+ }
+
+}
15 src/de/flupp/clojurevst/config/Program.java
@@ -0,0 +1,15 @@
+package de.flupp.clojurevst.config;
+
+public class Program {
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
115 src/de/flupp/clojurevst/plugins/2polelp.clj
@@ -0,0 +1,115 @@
+(ns de.flupp.clojurevst.clj2polelp)
+
+;;Type : LP 2-pole resonant tweaked butterworth
+;;References : Posted by daniel_jacob_werner [AT] yaho [DOT] com [DOT] au
+
+;;Notes :
+;This algorithm is a modified version of the tweaked butterworth lowpass filter by Patrice Tarrabia posted on musicdsp.org's archives.
+;It calculates the coefficients for a second order IIR filter. The resonance is specified in decibels above the DC gain.
+;It can be made suitable to use as a SoundFont 2.0 filter by scaling the output so the overall gain matches the specification
+;(i.e. if resonance is 6dB then you should scale the output by -3dB). Note that you can replace the sqrt(2) values in the standard
+;butterworth highpass algorithm with my "q =" line of code to get a highpass also.
+;
+;How it works: normally q is the constant sqrt(2), and this value controls resonance. At sqrt(2) resonance is 0dB, smaller values
+;increase resonance. By multiplying sqrt(2) by a power ratio we can specify the resonant gain at the cutoff frequency.
+;The resonance power ratio is calculated with a standard formula to convert between decibels and power ratios (the powf statement...).
+;Good Luck,
+;Daniel Werner
+;http://experimentalscene.com/
+
+;Code :
+;float c, csq, resonance, q, a0, a1, a2, b1, b2;
+
+;c = 1.0f / (tanf(pi * (cutoff / samplerate)));
+;csq = c * c;
+;resonance = powf(10.0f, -(resonancedB * 0.1f));
+;q = sqrt(2.0f) * resonance;
+;a0 = 1.0f / (1.0f + (q * c) + (csq));
+;a1 = 2.0f * a0;
+;a2 = a0;
+;b1 = (2.0f * a0) * (1.0f - csq);
+;b2 = a0 * (1.0f - (q * c) + csq);
+
+;;TODO: this is a quite literal conversion from the original algorithm, needs clojurization (use atoms and let special form).
+
+
+;; config
+(def plugin-config {:plugin-category 1
+ :unique-id "2plp"
+ :product "clj2plp"
+ :num-inputs 2
+ :num-outputs 2
+ :can-process-replacing true
+ :can-mono false
+ :vendor "clojurevst"
+ :can-do ["2in2out", "plugAsChannelInsert", "plugAsSend"]})
+
+;; parameters
+(def #^{:parameter-name "Cutoff"
+ :parameter-label "hz"}
+ p1 0.04)
+(def #^{:parameter-name "Max Cutoff"
+ :parameter-label "hz"}
+ p3 0.81)
+(def #^{:parameter-name "Resonance"
+ :parameter-label "db"}
+ p2 0.81)
+(def #^{:parameter-name "Fine Reso"
+ :parameter-label "db"}
+ p4 0.9)
+
+;;globals
+;constants
+(def p1filtercoeff 0.0007)
+(def pi 3.1415926535897932384626433832795)
+
+;thread-local vars (use thread-local bindings for them?)
+(def p1smooth 0.0)
+(def c 0.0)
+(def csq 0.0)
+(def q 0.0)
+(def a0 0.0)
+(def a1 0.0)
+(def a2 0.0)
+(def b1 0.0)
+(def b2 0.0)
+
+;state maintained across process() calls
+(def out11 0.0)
+(def out12 0.0)
+(def in11 0.0)
+(def in12 0.0)
+(def out21 0.0)
+(def out22 0.0)
+(def in21 0.0)
+(def in22 0.0)
+
+
+;; effect
+(defn process-replacing [inputs outputs]
+ (let [in1 (nth inputs 0)
+ in2 (nth inputs 1)
+ out1 (nth outputs 0)
+ out2 (nth outputs 1)
+ samples (count out1)]
+ (dotimes [i samples]
+ (when (= (mod i 20) 0) ;audible param change "lag"
+ (def p1smooth (+ (* p1filtercoeff p1) (* (- 1.0 p1filtercoeff) p1smooth))) ;parameter smoothing
+ (def c (/ 1.0 (. java.lang.Math (tan (* pi (/ (+ 0.001 (* p1smooth p3)) 2.15))))))
+ (def csq (* c c))
+ (def q (* (. java.lang.Math (sqrt 2.0)) (- 1.0 p2) (- 1.0 p4)))
+ (def a0 (/ 1.0 (+ 1.0 (* q c) csq)))
+ (def a1 (* 2.0 a0))
+ (def a2 a0)
+ (def b1 (* (* 2.0 a0) (- 1.0 csq)))
+ (def b2 (* a0 (+ (- 1.0 (* c q)) csq))))
+ (aset-float out1 i (- (+ (* (nth in1 i) a0) (* in11 a1) (* in12 a2)) (* out11 b1) (* out12 b2)))
+ (aset-float out2 i (- (+ (* (nth in2 i) a0) (* in21 a1) (* in22 a2)) (* out21 b1) (* out22 b2)))
+ (def out12 out11) ;cascade
+ (def out11 (nth out1 i))
+ (def in12 in11)
+ (def in11 (nth in1 i))
+ (def out22 out21)
+ (def out21 (nth out2 i))
+ (def in22 in21)
+ (def in21 (nth in2 i)))))
108 src/de/flupp/clojurevst/plugins/2polelpset.clj
@@ -0,0 +1,108 @@
+(ns de.flupp.clojurevst.clj2polelpset)
+
+;;Type : LP 2-pole resonant tweaked butterworth
+;;References : Posted by daniel_jacob_werner [AT] yaho [DOT] com [DOT] au
+
+;;Notes :
+;This algorithm is a modified version of the tweaked butterworth lowpass filter by Patrice Tarrabia posted on musicdsp.org's archives.
+;It calculates the coefficients for a second order IIR filter. The resonance is specified in decibels above the DC gain.
+;It can be made suitable to use as a SoundFont 2.0 filter by scaling the output so the overall gain matches the specification
+;(i.e. if resonance is 6dB then you should scale the output by -3dB). Note that you can replace the sqrt(2) values in the standard
+;butterworth highpass algorithm with my "q =" line of code to get a highpass also.
+;
+;How it works: normally q is the constant sqrt(2), and this value controls resonance. At sqrt(2) resonance is 0dB, smaller values
+;increase resonance. By multiplying sqrt(2) by a power ratio we can specify the resonant gain at the cutoff frequency.
+;The resonance power ratio is calculated with a standard formula to convert between decibels and power ratios (the powf statement...).
+;Good Luck,
+;Daniel Werner
+;http://experimentalscene.com/
+
+;Code :
+;float c, csq, resonance, q, a0, a1, a2, b1, b2;
+
+;c = 1.0f / (tanf(pi * (cutoff / samplerate)));
+;csq = c * c;
+;resonance = powf(10.0f, -(resonancedB * 0.1f));
+;q = sqrt(2.0f) * resonance;
+;a0 = 1.0f / (1.0f + (q * c) + (csq));
+;a1 = 2.0f * a0;
+;a2 = a0;
+;b1 = (2.0f * a0) * (1.0f - csq);
+;b2 = a0 * (1.0f - (q * c) + csq);
+
+;;TODO: this is a quite literal conversion from the original algorithm, needs clojurization (use atoms and let special form).
+
+;; config
+(def plugin-config {:plugin-category 1
+ :unique-id "2plp"
+ :product "clj2plp"
+ :num-inputs 2
+ :num-outputs 2
+ :can-process-replacing true
+ :can-mono false
+ :vendor "clojurevst"
+ :can-do ["2in2out", "plugAsChannelInsert", "plugAsSend"]})
+
+;; parameters
+(def #^{:parameter-name "Cutoff"
+ :parameter-label "hz"}
+ p1 0.04)
+(def #^{:parameter-name "Max Cutoff"
+ :parameter-label "hz"}
+ p3 0.81)
+(def #^{:parameter-name "Resonance"
+ :parameter-label "db"}
+ p2 0.81)
+(def #^{:parameter-name "Fine Reso"
+ :parameter-label "db"}
+ p4 0.9)
+
+;;globals
+(def #^{:thread-bound 1} p1smooth 1.0)
+(def #^{:thread-bound 1} p1filtercoeff 0.0007)
+(def pi 3.1415926535897932384626433832795)
+(def #^{:thread-bound 1} out11 0.0)
+(def #^{:thread-bound 1} out12 0.0)
+(def #^{:thread-bound 1} in11 0.0)
+(def #^{:thread-bound 1} in12 0.0)
+(def #^{:thread-bound 1} out21 0.0)
+(def #^{:thread-bound 1} out22 0.0)
+(def #^{:thread-bound 1} in21 0.0)
+(def #^{:thread-bound 1} in22 0.0)
+(def #^{:thread-bound 1} c 0.0)
+(def #^{:thread-bound 1} csq 0.0)
+(def #^{:thread-bound 1} q 0.0)
+(def #^{:thread-bound 1} a0 0.0)
+(def #^{:thread-bound 1} a1 0.0)
+(def #^{:thread-bound 1} a2 0.0)
+(def #^{:thread-bound 1} b1 0.0)
+(def #^{:thread-bound 1} b2 0.0)
+
+;; effect
+(defn process-replacing [inputs outputs]
+ (let [in1 (nth inputs 0)
+ in2 (nth inputs 1)
+ out1 (nth outputs 0)
+ out2 (nth outputs 1)
+ samples (count out1)]
+ (dotimes [i samples]
+ (when (= (mod i 20) 0) ;audible param change "lag"
+ (set! p1smooth (+ (* p1filtercoeff p1) (* (- 1.0 p1filtercoeff) p1smooth))) ;parameter smoothing
+ (set! c (/ 1.0 (. java.lang.Math (tan (* pi (/ (+ 0.001 (* p1smooth p3)) 2.15))))))
+ (set! csq (* c c))
+ (set! q (* (. java.lang.Math (sqrt 2.0)) (- 1.0 p2) (- 1.0 p4)))
+ (set! a0 (/ 1.0 (+ 1.0 (* q c) csq)))
+ (set! a1 (* 2.0 a0))
+ (set! a2 a0)
+ (set! b1 (* (* 2.0 a0) (- 1.0 csq)))
+ (set! b2 (* a0 (+ (- 1.0 (* c q)) csq))))
+ (aset-float out1 i (- (+ (* (nth in1 i) a0) (* in11 a1) (* in12 a2)) (* out11 b1) (* out12 b2)))
+ (aset-float out2 i (- (+ (* (nth in2 i) a0) (* in21 a1) (* in22 a2)) (* out21 b1) (* out22 b2)))
+ (set! out12 out11)
+ (set! out11 (nth out1 i))
+ (set! in12 in11)
+ (set! in11 (nth in1 i))
+ (set! out22 out21)
+ (set! out21 (nth out2 i))
+ (set! in22 in21)
+ (set! in21 (nth in2 i)))))
39 src/de/flupp/clojurevst/plugins/cljdelay.clj
@@ -0,0 +1,39 @@
+(ns de.flupp.clojurevst.cljdelay)
+
+;; config
+(def plugin-config {:plugin-category 1
+ :unique-id "cljD"
+ :product "cljdelay"
+ :num-inputs 1
+ :num-outputs 1
+ :can-process-replacing true
+ :can-mono true
+ :vendor "clojurevst"
+ :can-do ["1in1out", "plugAsChannelInsert", "plugAsSend"]})
+
+;; parameters
+(def #^{:parameter-name "Delay"
+ :parameter-label ""
+ :parameter-display-multiplier 44099}
+ pdelay 0.5)
+(def #^{:parameter-name "Feedback"
+ :parameter-label ""}
+ pfeedback 0.9)
+(def #^{:parameter-name "Out"
+ :parameter-label ""}
+ pout 0.5)
+
+(def cursor (atom 0))
+(def buffer (make-array Float/TYPE 44100))
+
+;; effect
+(defn process-replacing [inputs outputs]
+ (let [input (nth inputs 0)
+ samples (count input)
+ output (nth outputs 0)]
+ (dotimes [i samples]
+ (aset-float output i (* pout (nth buffer @cursor)))
+ (aset-float buffer @cursor (+ (nth input i) (* (nth output i) pfeedback)))
+ (swap! cursor inc)
+ (if (>= @cursor (* pdelay 44099))
+ (reset! cursor 0)))))
33 src/de/flupp/clojurevst/plugins/cljgain.clj
@@ -0,0 +1,33 @@
+(ns de.flupp.clojurevst.cljgain)
+
+;; config
+(def plugin-config {:plugin-category 1
+ :unique-id "cljG"
+ :product "cljgain"
+ :num-inputs 1
+ :num-outputs 1
+ :can-process-replacing true
+ :can-mono true
+ :vendor "clojurevst"
+ :can-do ["1in1out", "plugAsChannelInsert", "plugAsSend"]
+ :programs ["foo", "bar"] })
+
+;; parameters
+(def #^{:parameter-name "Gain"
+ :parameter-label ""
+ :value-in-programs [0.5, 0.9] }
+ pgain 0.5)
+
+(defn get-parameter-display [param]
+ (str (* pgain 10)))
+
+(defn get-parameter-label [param]
+ (str "bla"))
+
+;; effect processing
+(defn process-replacing [inputs outputs]
+ (let [input (nth inputs 0)
+ samples (count input)
+ output (nth outputs 0)]
+ (dotimes [i samples]
+ (aset-float output i (* pgain (nth input i))))))
36 src/de/flupp/clojurevst/plugins/cljstereogain.clj
@@ -0,0 +1,36 @@
+(ns de.flupp.clojurevst.cljstereogain)
+
+;; config
+(def plugin-config {:plugin-category 1
+ :unique-id "clsG"
+ :product "cljstereogain"
+ :num-inputs 2
+ :num-outputs 2
+ :can-process-replacing true
+ :can-mono false
+ :vendor "clojurevst"
+ :can-do ["2in2out", "plugAsChannelInsert", "plugAsSend"]
+ :programs ["foo", "bar"] })
+
+;; parameters
+(def #^{:parameter-name "Gain"
+ :parameter-label ""
+ :value-in-programs [0.5, 0.9] }
+ pgain 0.5)
+
+(defn get-parameter-display [param]
+ (str (* pgain 10)))
+
+(defn get-parameter-label [param]
+ (str "bla"))
+
+;; effect processing
+(defn process-replacing [inputs outputs]
+ (let [in1 (nth inputs 0)
+ in2 (nth inputs 1)
+ out1 (nth outputs 0)
+ out2 (nth outputs 1)
+ samples (count in1)]
+ (dotimes [i samples]
+ (aset-float out1 i (* pgain (nth in1 i)))
+ (aset-float out2 i (* pgain (nth in2 i))))))

0 comments on commit 8ab8f74

Please sign in to comment.