├── .classpath ├── .gitignore ├── .project ├── README.md ├── clojurevst.jardesc ├── dist ├── clojurevst-linux.ini ├── clojurevst-linux.so ├── clojurevst-win.ini └── clojurevst.jar ├── lib ├── clojure-contrib.jar ├── clojure.jar ├── jVSTsYstem-1.0beta.jar └── jVSTwRapper-1.0beta.jar ├── manifest.mf ├── profiler4j-settings.p4j └── src └── de └── flupp └── clojurevst ├── ClojureVSTPluginProxy.java ├── PluginConstants.java ├── ProxyTools.java ├── config ├── ClojureVSTPluginConfig.java ├── Parameter.java └── Program.java └── plugins ├── 2polelp.clj ├── 2polelpset.clj ├── cljdelay.clj ├── cljgain.clj └── cljstereogain.clj /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ClojureVST 4 | 5 | 6 | 7 | 8 | 9 | ccw.builder 10 | 11 | 12 | 13 | 14 | org.eclipse.jdt.core.javabuilder 15 | 16 | 17 | 18 | 19 | 20 | ccw.nature 21 | org.eclipse.jdt.core.javanature 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Introduction 2 | ------------ 3 | 4 | This code allows developing VST plugins in [Clojure](http://clojure.org) based on [jVSTWRapper](http://jvstwrapper.sourceforge.net/). 5 | 6 | Status 7 | ------ 8 | 9 | The project is a proof of concept and not actively developed. 10 | 11 | Requirements 12 | ------------ 13 | 14 | * a 32-bit JVM 15 | * a VST host 16 | * jVSTWrapper (included) 17 | * clojure (included) 18 | 19 | Installation & running 20 | ---------------------- 21 | 22 | * Follow [jVSTWrapper's installation instructions](http://jvstwrapper.sourceforge.net/#installation). 23 | 24 | * Copy `lib/clojure.jar`, `lib/clojure-contrib.jar` and `dist/clojurevst.jar` to the folder where you installed jVSTWrapper. 25 | 26 | * Copy the sample config (e.g. `dist/clojurevst-win.ini`) to the folder where you installed jVSTWrapper and rename it according to your plugin's dll name. See for more information. 27 | 28 | * Copy the sample plugins from `src/de/flupp/clojurevst/plugins` to the folder where you installed jVSTWrapper. The plugin to be used is defined in the ini file: 29 | 30 | ClojureVSTPluginFile=cljdelay.clj 31 | ClojureVSTPluginNamespace=de.flupp.clojurevst.cljdelay 32 | 33 | * If `ClojureVSTPluginReload` is set to true, the *.clj files in the plugin folder are monitored for updates and reloaded upon saving. 34 | 35 | * Start your VST host and load the jVSTWrapper plugin dll. 36 | 37 | Development 38 | ----------- 39 | 40 | The project includes an Eclipse project file as well as all required dependencies. 41 | 42 | Copyright Notice 43 | ---------------- 44 | 45 | * "VST" is a trademark of Steinberg Media Technologies GmbH. 46 | 47 | * The code includes a copy of [opaz-plugdk's](https://github.com/thbar/opaz-plugdk) `ProxyTools` class which provides a number of helper functions. 48 | -------------------------------------------------------------------------------- /clojurevst.jardesc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /dist/clojurevst-linux.ini: -------------------------------------------------------------------------------- 1 | ClojureVSTPluginFile=de/flupp/clojurevst/plugins/2polelpset.clj 2 | ClojureVSTPluginNamespace=de.flupp.clojurevst.clj2polelpset 3 | 4 | #ClojureVSTPluginFile=de/flupp/clojurevst/plugins/2polelp.clj 5 | #ClojureVSTPluginNamespace=de.flupp.clojurevst.clj2polelp 6 | 7 | #ClojureVSTPluginFile=de/flupp/clojurevst/plugins/cljdelay.clj 8 | #ClojureVSTPluginNamespace=de.flupp.clojurevst.cljdelay 9 | 10 | #ClojureVSTPluginFile=de/flupp/clojurevst/plugins/cljstereogain.clj 11 | #ClojureVSTPluginNamespace=de.flupp.clojurevst.cljstereogain 12 | 13 | #ClojureVSTPluginFile=de/flupp/clojurevst/plugins/cljgain.clj 14 | #ClojureVSTPluginNamespace=de.flupp.clojurevst.cljgain 15 | 16 | ClojureVSTPluginReload=true 17 | 18 | PluginClass=de/flupp/clojurevst/ClojureVSTPluginProxy 19 | 20 | #Classpath for distribution setup (notice clojurevst.jar) 21 | #ClassPath={WrapperPath}/jVSTwRapper-1.0beta.jar:{WrapperPath}/clojure.jar:{WrapperPath}/clojure-contrib.jar:{WrapperPath}/clojurevst.jar 22 | 23 | #Classpath for TEST setup (notice "../bin" --> directly use eclipse compile output) 24 | ClassPath={WrapperPath}/../lib/jVSTwRapper-1.0beta.jar:{WrapperPath}/../lib/clojure.jar:{WrapperPath}/../lib/clojure-contrib.jar:{WrapperPath}/../bin/ 25 | 26 | SystemClassPath={WrapperPath}/../lib/jVSTsYstem-1.0beta.jar:{WrapperPath}/../lib/clojure.jar:{WrapperPath}/../lib/clojure-contrib.jar:{WrapperPath}/../src/ 27 | IsLoggingEnabled=1 28 | AttachToNativePluginWindow=1 29 | 30 | #JVMOption1=-javaagent:/home/privat/opt/profiler4j-1.0-beta2/agent.jar 31 | 32 | #JVMOption1=-Xdebug 33 | #JVMOption2=-Xnoagent 34 | #JVMOption3=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 35 | #JVMOption4=-Djava.compiler=NONE 36 | 37 | #JVMOption1=-Xnoclassgc 38 | #JVMOption2=-Xloggc:c:/gclog.txt 39 | 40 | #JVMOption1=-verbose:jni 41 | #JVMOption2=-Xcheck:jni -------------------------------------------------------------------------------- /dist/clojurevst-linux.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwu/ClojureVST/26adf8681a42817a21ae25aa87b04826f3e44c73/dist/clojurevst-linux.so -------------------------------------------------------------------------------- /dist/clojurevst-win.ini: -------------------------------------------------------------------------------- 1 | ClojureVSTPluginFile=cljdelay.clj 2 | ClojureVSTPluginNamespace=de.flupp.clojurevst.cljdelay 3 | ClojureVSTPluginReload=false 4 | 5 | PluginClass=de/flupp/clojurevst/ClojureVSTPluginProxy 6 | #PluginClass=jvst/examples/jaydlay/JayDLay 7 | #PluginUIClass=jvst/examples/jaydlay/JayDLayGUI 8 | 9 | ClassPath={WrapperPath}\jVSTwRapper-1.0beta.jar;{WrapperPath}\jVSTeXamples-1.0beta.jar;{WrapperPath}\clojure.jar;{WrapperPath}\clojure-contrib.jar;{WrapperPath}\clojurevst.jar;{WrapperPath}\profile.jar 10 | SystemClassPath={WrapperPath}\jVSTsYstem-1.0beta.jar;{WrapperPath}\clojure.jar;{WrapperPath}\clojure-contrib.jar;{WrapperPath}\profile.jar 11 | IsLoggingEnabled=1 12 | #CustomJVMLocation=C:\Program Files\Java\JDK\... 13 | #CustomJVMRegistryKey=Software\\MyCompanyName\\JavaRuntimeLocation 14 | #RequestJVMVersion=1.4 15 | AttachToNativePluginWindow=1 16 | 17 | #JVMOption1=-javaagent:profile.jar 18 | #JVMOption2=-Dprofile.properties=profile.properties 19 | 20 | #JVMOption1=-Xdebug 21 | #JVMOption2=-Xnoagent 22 | #JVMOption3=-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 23 | #JVMOption4=-Djava.compiler=NONE 24 | 25 | #JVMOption1=-Xnoclassgc 26 | #JVMOption2=-Xloggc:c:\gclog.txt 27 | 28 | #JVMOption1=-verbose:jni 29 | #JVMOption2=-Xcheck:jni 30 | -------------------------------------------------------------------------------- /dist/clojurevst.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwu/ClojureVST/26adf8681a42817a21ae25aa87b04826f3e44c73/dist/clojurevst.jar -------------------------------------------------------------------------------- /lib/clojure-contrib.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwu/ClojureVST/26adf8681a42817a21ae25aa87b04826f3e44c73/lib/clojure-contrib.jar -------------------------------------------------------------------------------- /lib/clojure.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwu/ClojureVST/26adf8681a42817a21ae25aa87b04826f3e44c73/lib/clojure.jar -------------------------------------------------------------------------------- /lib/jVSTsYstem-1.0beta.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwu/ClojureVST/26adf8681a42817a21ae25aa87b04826f3e44c73/lib/jVSTsYstem-1.0beta.jar -------------------------------------------------------------------------------- /lib/jVSTwRapper-1.0beta.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dwu/ClojureVST/26adf8681a42817a21ae25aa87b04826f3e44c73/lib/jVSTwRapper-1.0beta.jar -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | 3 | -------------------------------------------------------------------------------- /profiler4j-settings.p4j: -------------------------------------------------------------------------------- 1 | 2 | 3 | localhost 4 | 7890 5 | 6 | org.apache.*(*) 7 | org.jboss.*(*) 8 | net.sf.jasperreports.*(*) 9 | bsh.*(*) 10 | EDU.oswego.*(*) 11 | org.eclipse.*(*) 12 | org.hsqldb.*(*) 13 | clojure.*__init.*(*) 14 | *(*) 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/ClojureVSTPluginProxy.java: -------------------------------------------------------------------------------- 1 | package de.flupp.clojurevst; 2 | 3 | import java.io.File; 4 | import java.io.FilenameFilter; 5 | import java.util.Hashtable; 6 | 7 | import jvst.wrapper.VSTPluginAdapter; 8 | import clojure.lang.Var; 9 | import de.flupp.clojurevst.config.ClojureVSTPluginConfig; 10 | 11 | public class ClojureVSTPluginProxy extends VSTPluginAdapter { 12 | 13 | private ClojureVSTPluginConfig pluginConfig; 14 | private Var cljProcessReplacing; 15 | private boolean suspendProcessing = false; 16 | private boolean variablesBound = false; 17 | 18 | public ClojureVSTPluginProxy(long wrapper) throws Exception { 19 | super(wrapper); 20 | 21 | loadPlugin(ProxyTools.getIniFileName(ProxyTools.getResourcesFolder(getLogBasePath()), getLogFileName())); 22 | setProgram(0); 23 | 24 | if (pluginConfig.isPluginReload()) { 25 | log("Starting Watcher thread to reload plugin if any .clj file changed"); 26 | new Watcher(this).start(); 27 | } 28 | 29 | log("[ClojureVSTPluginProxy] Successfully loaded Clojure VST plugin " + pluginConfig.getPluginNamespace() + " from file " + pluginConfig.getPluginFilename()); 30 | } 31 | 32 | public void loadPlugin(String iniFileName) throws Exception { 33 | pluginConfig = new ClojureVSTPluginConfig(iniFileName); 34 | cljProcessReplacing = pluginConfig.getProcessReplacing(); 35 | 36 | setNumInputs(pluginConfig.getNumInputs()); 37 | setNumOutputs(pluginConfig.getNumOutputs()); 38 | canProcessReplacing(pluginConfig.canProcessReplacing()); 39 | canMono(pluginConfig.canMono()); 40 | setUniqueID(pluginConfig.getUniqueId()); 41 | } 42 | 43 | public int canDo(String feature) { 44 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_CAN_DO); 45 | if (func == null) { 46 | // use generic method implementation 47 | return pluginConfig.canDo(feature); 48 | } else { 49 | try { 50 | // use method implementation in plugin 51 | Object result = func.invoke(feature); 52 | return (Integer)result; 53 | } catch (Exception e) { 54 | log("[ClojureVSTPluginProxy::canDo] Exception during method execution"); 55 | log(e.getStackTrace().toString()); 56 | throw(new IllegalStateException(e)); 57 | } 58 | } 59 | } 60 | 61 | public int getPlugCategory() { 62 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PLUG_CATEGORY); 63 | if (func == null) { 64 | // use generic method implementation 65 | return pluginConfig.getPluginCategory(); 66 | } else { 67 | try { 68 | // use method implementation in plugin 69 | Object result = func.invoke(); 70 | return (Integer)result; 71 | } catch (Exception e) { 72 | log("[ClojureVSTPluginProxy::getPlugCategory] Exception during method execution"); 73 | log(e.getStackTrace().toString()); 74 | throw(new IllegalStateException(e)); 75 | } 76 | } 77 | } 78 | 79 | public String getProductString() { 80 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PRODUCT_STRING); 81 | if (func == null) { 82 | // use generic method implementation 83 | return pluginConfig.getProductString(); 84 | } else { 85 | try { 86 | // use method implementation in plugin 87 | Object result = func.invoke(); 88 | return result.toString(); 89 | } catch (Exception e) { 90 | log("[ClojureVSTPluginProxy::getProductString] Exception during method execution"); 91 | log(e.getStackTrace().toString()); 92 | throw(new IllegalStateException(e)); 93 | } 94 | } 95 | } 96 | 97 | // TODO Only needed if plugin supports more than one category 98 | public String getProgramNameIndexed(int arg0, int arg1) { 99 | return "TODO"; 100 | } 101 | 102 | public String getVendorString() { 103 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_VENDOR_STRING); 104 | if (func == null) { 105 | // use generic method implementation 106 | return pluginConfig.getVendorString(); 107 | } else { 108 | try { 109 | // use method implementation in plugin 110 | Object result = func.invoke(); 111 | return result.toString(); 112 | } catch (Exception e) { 113 | log("[ClojureVSTPluginProxy::getVendorString] Exception during method execution"); 114 | log(e.getStackTrace().toString()); 115 | throw(new IllegalStateException(e)); 116 | } 117 | } 118 | } 119 | 120 | public boolean setBypass(boolean arg0) { 121 | // TODO Currently no support for software bypass 122 | return false; 123 | } 124 | 125 | public boolean string2Parameter(int idx, String value) { 126 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_STRING2PARAMETER); 127 | if (func == null) { 128 | // use generic method implementation 129 | try { 130 | setParameter(idx, Float.parseFloat(value)); 131 | return true; 132 | } catch (Exception e) { 133 | log("[ClojureVSTPluginProxy::string2Parameter] Unable to set value " + value + " for parameter index " + idx); 134 | log(e.getStackTrace().toString()); 135 | throw(new IllegalStateException(e)); 136 | } 137 | } else { 138 | try { 139 | // use method implementation in plugin 140 | Object result = func.invoke(idx, value); 141 | return (Boolean)result; 142 | } catch (Exception e) { 143 | log("[ClojureVSTPluginProxy::string2Parameter] Exception during method execution"); 144 | log(e.getStackTrace().toString()); 145 | throw(new IllegalStateException(e)); 146 | } 147 | } 148 | } 149 | 150 | public int getNumParams() { 151 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_NUM_PARAMS); 152 | if (func == null) { 153 | // use generic method implementation 154 | return pluginConfig.getNumParams(); 155 | } else { 156 | try { 157 | // use method implementation in plugin 158 | Object result = func.invoke(); 159 | return (Integer)result; 160 | } catch (Exception e) { 161 | log("[ClojureVSTPluginProxy::getNumParams] Exception during method execution"); 162 | log(e.getStackTrace().toString()); 163 | throw(new IllegalStateException(e)); 164 | } 165 | } 166 | } 167 | 168 | public int getNumPrograms() { 169 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_NUM_PROGRAMS); 170 | if (func == null) { 171 | // use generic method implementation 172 | return pluginConfig.getNumPrograms(); 173 | } else { 174 | try { 175 | // use method implementation in plugin 176 | Object result = func.invoke(); 177 | return (Integer)result; 178 | } catch (Exception e) { 179 | log("[ClojureVSTPluginProxy::getNumPrograms] Exception during method execution"); 180 | log(e.getStackTrace().toString()); 181 | throw(new IllegalStateException(e)); 182 | } 183 | } 184 | } 185 | 186 | public float getParameter(int idx) { 187 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PARAMETER); 188 | if (func == null) { 189 | // use generic method implementation 190 | return pluginConfig.getParameterFloatCached(idx); 191 | } else { 192 | try { 193 | // use method implementation in plugin 194 | Object result = func.invoke(); 195 | return ((Double)result).floatValue(); 196 | } catch (Exception e) { 197 | log("[ClojureVSTPluginProxy::getParameter] Exception during method execution"); 198 | log(e.getStackTrace().toString()); 199 | throw(new IllegalStateException(e)); 200 | } 201 | } 202 | } 203 | 204 | public String getParameterDisplay(int idx) { 205 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PARAMETER_DISPLAY); 206 | if (func == null) { 207 | // use generic method implementation 208 | if (pluginConfig.getParameter(idx).hasMultiplier()) { 209 | return Float.toString(pluginConfig.getParameterFloatCached(idx) * pluginConfig.getParameter(idx).getMultiplier()); 210 | } else { 211 | return Float.toString(pluginConfig.getParameterFloatCached(idx)); 212 | } 213 | } else { 214 | try { 215 | // use method implementation in plugin 216 | Object result = func.invoke(idx); 217 | return result.toString(); 218 | } catch (Exception e) { 219 | log("[ClojureVSTPluginProxy::getParameterDisplay] Exception during method execution"); 220 | log(e.getStackTrace().toString()); 221 | throw(new IllegalStateException(e)); 222 | } 223 | } 224 | } 225 | 226 | public String getParameterLabel(int idx) { 227 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PARAMETER_LABEL); 228 | if (func == null) { 229 | // use generic method implementation 230 | return pluginConfig.getParameter(idx).getLabel(); 231 | } else { 232 | try { 233 | // use method implementation in plugin 234 | Object result = func.invoke(idx); 235 | return result.toString(); 236 | } catch (Exception e) { 237 | log("[ClojureVSTPluginProxy::getParameterLabel] Exception during method execution"); 238 | log(e.getStackTrace().toString()); 239 | throw(new IllegalStateException(e)); 240 | } 241 | } 242 | } 243 | 244 | public String getParameterName(int idx) { 245 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PARAMETER_NAME); 246 | if (func == null) { 247 | // use generic method implementation 248 | return pluginConfig.getParameter(idx).getName(); 249 | } else { 250 | try { 251 | // use method implementation in plugin 252 | Object result = func.invoke(idx); 253 | return result.toString(); 254 | } catch (Exception e) { 255 | log("[ClojureVSTPluginProxy::getParameterName] Exception during method execution"); 256 | log(e.getStackTrace().toString()); 257 | throw(new IllegalStateException(e)); 258 | } 259 | } 260 | } 261 | 262 | public int getProgram() { 263 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PROGRAM); 264 | if (func == null) { 265 | // use generic method implementation 266 | return pluginConfig.getCurrentProgram(); 267 | } else { 268 | try { 269 | // use method implementation in plugin 270 | Object result = func.invoke(); 271 | return (Integer)result; 272 | } catch (Exception e) { 273 | log("[ClojureVSTPluginProxy::getProgram] Exception during method execution"); 274 | log(e.getStackTrace().toString()); 275 | throw(new IllegalStateException(e)); 276 | } 277 | } 278 | } 279 | 280 | public String getProgramName() { 281 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_GET_PROGRAM_NAME); 282 | if (func == null) { 283 | // use generic method implementation 284 | return pluginConfig.getProgram(pluginConfig.getCurrentProgram()).getName(); 285 | } else { 286 | try { 287 | // use method implementation in plugin 288 | Object result = func.invoke(); 289 | return result.toString(); 290 | } catch (Exception e) { 291 | log("[ClojureVSTPluginProxy::getProgramName] Exception during method execution"); 292 | log(e.getStackTrace().toString()); 293 | throw(new IllegalStateException(e)); 294 | } 295 | } 296 | } 297 | 298 | public void processReplacing(float[][] inputs, float[][] outputs, int samples) { 299 | if (suspendProcessing) 300 | return; 301 | if (!variablesBound) 302 | pluginConfig.bindThreadBoundVariables(); 303 | 304 | try { 305 | cljProcessReplacing.invoke(inputs, outputs); 306 | } catch (Exception e) { 307 | log("[CloureVSTPluginProxy::processReplacing] Cannot execute process-replacing."); 308 | log(ProxyTools.getStackTraceText(e)); 309 | throw(new IllegalStateException(e)); 310 | } 311 | } 312 | 313 | /** 314 | * Generic implementation of accumulating process. Should only be used 315 | * if the plugin does not implement "process" itself and the host 316 | * application demands for it. 317 | * 318 | * TODO: needs testing 319 | * 320 | * @see jvst.wrapper.VSTPluginAdapter#process(float[][], float[][], int) 321 | */ 322 | public void process(float[][] inputs, float[][] outputs, int samples) { 323 | if (suspendProcessing) 324 | return; 325 | if (!variablesBound) 326 | pluginConfig.bindThreadBoundVariables(); 327 | 328 | float[][] originalOutputs = new float[outputs.length][outputs[0].length]; //assume quadratic (note: not rectangular!) array 329 | System.arraycopy(outputs, 0, originalOutputs, 0, outputs.length); 330 | 331 | //fake process() through a call to processReplacing 332 | processReplacing(inputs, outputs, samples); 333 | 334 | // accumulate output and original output 335 | for (int i = 0; i < outputs.length; i++) { 336 | for (int j = 0; j < outputs[i].length; j++) { 337 | outputs[i][j] = outputs[i][j] + originalOutputs[i][j]; 338 | outputs[i][j] = outputs[i][j] + originalOutputs[i][j]; 339 | } 340 | } 341 | } 342 | 343 | public void setParameter(int idx, float value) { 344 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_SET_PARAMETER); 345 | if (func == null) { 346 | // use generic method implementation 347 | pluginConfig.setParameter(idx, value); 348 | } else { 349 | try { 350 | // use method implementation in plugi 351 | func.invoke(idx, value); 352 | } catch (Exception e) { 353 | log("[ClojureVSTPluginProxy::setParameter] Exception during method execution"); 354 | log(e.getStackTrace().toString()); 355 | throw(new IllegalStateException(e)); 356 | } 357 | } 358 | } 359 | 360 | public void setProgram(int currentProgram) { 361 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_SET_PROGRAM); 362 | if (func == null) { 363 | // use generic method implementation 364 | pluginConfig.setProgram(currentProgram); 365 | } else { 366 | try { 367 | // use method implementation in plugi 368 | func.invoke(currentProgram); 369 | } catch (Exception e) { 370 | log("[ClojureVSTPluginProxy::setProgram] Exception during method execution"); 371 | log(e.getStackTrace().toString()); 372 | throw(new IllegalStateException(e)); 373 | } 374 | } 375 | } 376 | 377 | public void setProgramName(String programName) { 378 | Var func = pluginConfig.getMethod(PluginConstants.METHOD_SET_PROGRAM_NAME); 379 | if (func == null) { 380 | // use generic method implementation 381 | pluginConfig.setCurrentProgramName(programName); 382 | } else { 383 | try { 384 | // use method implementation in plugi 385 | func.invoke(programName); 386 | } catch (Exception e) { 387 | log("[ClojureVSTPluginProxy::setProgramName] Exception during method execution"); 388 | log(e.getStackTrace().toString()); 389 | throw(new IllegalStateException(e)); 390 | } 391 | } 392 | } 393 | 394 | 395 | 396 | private class Watcher extends Thread { 397 | Hashtable toWatch = new Hashtable(); 398 | 399 | ClojureVSTPluginProxy proxy = null; 400 | 401 | public Watcher(ClojureVSTPluginProxy proxy) throws Exception { 402 | this.proxy = proxy; 403 | 404 | //find the location where we loaded the main plugin file from... 405 | File f = new File(this.getContextClassLoader().getResource(proxy.pluginConfig.getPluginFilename()).toURI()).getParentFile(); 406 | log("Watching all .clj files in path: " + f + " for changes"); 407 | File[] files = f.listFiles(new FilenameFilter() { 408 | public boolean accept(File dir, String name) {return name.toLowerCase().endsWith(".clj");} 409 | }); 410 | for (File file : files) { 411 | toWatch.put(file, file.lastModified()); 412 | } 413 | } 414 | 415 | public void run() { 416 | boolean modified = false; 417 | 418 | while(true) { 419 | try { 420 | //log("### +++ ### next round..."); 421 | for (File file : toWatch.keySet()) { 422 | if (file.lastModified() > toWatch.get(file)) { 423 | log("### File: " + file.getName() + " was just modified!"); 424 | toWatch.put(file, file.lastModified()); 425 | modified = true; 426 | } 427 | } 428 | if (modified) reloadPlugin(); 429 | modified = false; 430 | Thread.sleep(1000); 431 | } 432 | catch (Exception e) { 433 | e.printStackTrace(); 434 | } 435 | } 436 | } 437 | 438 | private void reloadPlugin() throws Exception { 439 | int currentProgram = proxy.getProgram(); 440 | 441 | try { 442 | log("Reloading Plugin!"); 443 | 444 | proxy.suspendProcessing = true; 445 | proxy.loadPlugin(ProxyTools.getIniFileName(ProxyTools.getResourcesFolder(getLogBasePath()), getLogFileName())); 446 | proxy.setProgram(currentProgram); 447 | 448 | log("all good :-)"); 449 | proxy.suspendProcessing = false; 450 | } 451 | catch (Throwable t) { 452 | log(ProxyTools.getStackTraceText(t)); 453 | // enable process() calls on the old plugin again 454 | log("### Plugin problem detected, using the version that ran before. Check the error log below for details"); 455 | proxy.suspendProcessing = false; 456 | } 457 | } 458 | } 459 | 460 | } 461 | -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/PluginConstants.java: -------------------------------------------------------------------------------- 1 | package de.flupp.clojurevst; 2 | 3 | public class PluginConstants { 4 | 5 | // Plugin configuration (Clojure) 6 | public static final String PLUGIN_CONFIG = "plugin-config"; 7 | public static final String PLUGIN_CONFIG_CAN_DO = "can-do"; 8 | public static final String PLUGIN_CONFIG_PARAMETERS = "params"; 9 | public static final String PLUGIN_CONFIG_UNIQUE_ID = "unique-id"; 10 | public static final String PLUGIN_CONFIG_PRODUCT_STRING = "product"; 11 | public static final String PLUGIN_CONFIG_NUM_INPUTS = "num-inputs"; 12 | public static final String PLUGIN_CONFIG_NUM_OUTPUTS = "num-outputs"; 13 | public static final String PLUGIN_CONFIG_CAN_PROCESS_REPLACING = "can-process-replacing"; 14 | public static final String PLUGIN_CONFIG_CAN_MONO = "can-mono"; 15 | public static final String PLUGIN_CONFIG_VENDOR_STRING = "vendor"; 16 | public static final String PLUGIN_CONFIG_PLUGIN_CATEGORY = "plugin-category"; 17 | public static final String PLUGIN_CONFIG_PROGRAMS = "programs"; 18 | 19 | // Plugin configuration (INI-File) 20 | public static final String INI_PLUGIN_FILENAME = "ClojureVSTPluginFile"; 21 | public static final String INI_PLUGIN_NAMESPACE = "ClojureVSTPluginNamespace"; 22 | public static final String INI_PLUGIN_RELOAD = "ClojureVSTPluginReload"; 23 | 24 | // Parameter metadata 25 | public static final String PARAM_NAME = "parameter-name"; 26 | public static final String PARAM_LABEL = "parameter-label"; 27 | public static final String PARAM_MULTIPLIER = "parameter-display-multiplier"; 28 | public static final String PARAM_VALUE_IN_PROGRAMS = "value-in-programs"; 29 | public static final String PARAM_THREAD_BOUND = "thread-bound"; 30 | 31 | // Plugin methods 32 | public static final String METHOD_PROCESS_REPLACING = "process-replacing"; 33 | public static final String METHOD_GET_PARAMETER_DISPLAY = "get-parameter-display"; 34 | public static final String METHOD_GET_PARAMETER_LABEL = "get-parameter-label"; 35 | public static final String METHOD_CAN_DO = "can-do"; 36 | public static final String METHOD_GET_PLUG_CATEGORY = "get-plug-category"; 37 | public static final String METHOD_GET_PRODUCT_STRING = "get-product-string"; 38 | public static final String METHOD_GET_PROGRAM_NAME_INDEXED = "get-program-name-indexed"; 39 | public static final String METHOD_GET_VENDOR_STRING = "get-vendor-strin"; 40 | public static final String METHOD_SET_BYPASS = "set-bypass"; 41 | public static final String METHOD_STRING2PARAMETER = "string2parameter"; 42 | public static final String METHOD_GET_NUM_PARAMS = "get-num-params"; 43 | public static final String METHOD_GET_NUM_PROGRAMS = "get-num-programs"; 44 | public static final String METHOD_GET_PARAMETER = "get-parameter"; 45 | public static final String METHOD_GET_PARAMETER_NAME = "get-parameter-name"; 46 | public static final String METHOD_GET_PROGRAM = "get-program"; 47 | public static final String METHOD_GET_PROGRAM_NAME = "get-program-name"; 48 | public static final String METHOD_SET_PARAMETER = "set-parameter"; 49 | public static final String METHOD_SET_PROGRAM = "set-program"; 50 | public static final String METHOD_SET_PROGRAM_NAME = "set-program-name"; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/ProxyTools.java: -------------------------------------------------------------------------------- 1 | package de.flupp.clojurevst; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | 6 | /** 7 | * Utility class (from opaz-plugdk; https://github.com/thbar/opaz-plugdk) 8 | */ 9 | public class ProxyTools { 10 | // Although I wanted to use VSTPluginAdapter.RUNNING_MAC_X instead of this, it raises AWT thread errors. 11 | // Sticking with this one for the moment. 12 | public static boolean useMacOSX() { 13 | String lcOSName = System.getProperty("os.name").toLowerCase(); 14 | return lcOSName.startsWith("mac os x"); 15 | } 16 | 17 | public static String getResourcesFolder(String logBasePath) { 18 | String resourcesFolder = logBasePath; 19 | if (useMacOSX()) // mac os x tweak :o 20 | resourcesFolder += "/../Resources"; 21 | return resourcesFolder; 22 | } 23 | 24 | public static String getIniFileName(String resourcesFolder, String logFileName) { 25 | String iniFileName = logFileName.replaceAll("_java_stdout.txt",""); 26 | if (useMacOSX()) 27 | iniFileName += ".jnilib"; 28 | return resourcesFolder + "/" + iniFileName + ".ini"; 29 | } 30 | 31 | /** 32 | * Get stack trace of an exception as a string for logging purposes. 33 | * 34 | * @param e Exception 35 | * @return 36 | */ 37 | public static String getStackTraceText(Throwable e) { 38 | StringWriter sw = new StringWriter(); 39 | PrintWriter pw = new PrintWriter(sw); 40 | e.printStackTrace(pw); 41 | return sw.getBuffer().toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/config/ClojureVSTPluginConfig.java: -------------------------------------------------------------------------------- 1 | package de.flupp.clojurevst.config; 2 | 3 | import java.io.FileInputStream; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Properties; 9 | 10 | import jvst.wrapper.VSTPluginAdapter; 11 | import clojure.lang.APersistentMap; 12 | import clojure.lang.IPersistentMap; 13 | import clojure.lang.Keyword; 14 | import clojure.lang.PersistentVector; 15 | import clojure.lang.RT; 16 | import clojure.lang.Symbol; 17 | import clojure.lang.Var; 18 | import de.flupp.clojurevst.PluginConstants; 19 | import de.flupp.clojurevst.ProxyTools; 20 | 21 | public class ClojureVSTPluginConfig { 22 | 23 | private String pluginNamespace; 24 | private String pluginFilename; 25 | private boolean pluginReload; 26 | 27 | private int uniqueId; 28 | private String productString; 29 | private String vendorString; 30 | private String vendorVersion; 31 | private int pluginCategory; 32 | private int numInputs; 33 | private int numOutputs; 34 | private boolean canProcessReplacing; 35 | private boolean canMono; 36 | private int currentProgram; 37 | 38 | private List features; 39 | private List parameters; 40 | private List programs; 41 | private Map methods; 42 | 43 | private String iniFileName; 44 | private Var cljLoadString; 45 | private Var cljProcessReplacing; 46 | private Var cljNsPublics; 47 | 48 | public ClojureVSTPluginConfig(String iniFileName) throws Exception { 49 | features = new ArrayList(); 50 | parameters = new ArrayList(); 51 | programs = new ArrayList(); 52 | methods = new HashMap(); 53 | 54 | this.iniFileName = iniFileName; 55 | 56 | cljLoadString = RT.var("clojure.core", "load-string"); 57 | cljNsPublics = RT.var("clojure.core", "ns-publics"); 58 | 59 | parseIni(); 60 | parseConfig(); 61 | parseMethods(); 62 | parseParameters(); 63 | 64 | VSTPluginAdapter.log("[PluginConfig] Successfully configured plugin " 65 | + this.getPluginNamespace() + "/" + this.getPluginFilename() + "\n" 66 | + this.toString()); 67 | } 68 | 69 | public Var getProcessReplacing() { 70 | return cljProcessReplacing; 71 | } 72 | 73 | public String getPluginNamespace() { 74 | return pluginNamespace; 75 | } 76 | 77 | public void setPluginNamespace(String pluginNamespace) { 78 | this.pluginNamespace = pluginNamespace; 79 | } 80 | 81 | public String getPluginFilename() { 82 | return pluginFilename; 83 | } 84 | 85 | public void setPluginFilename(String pluginFilename) { 86 | this.pluginFilename = pluginFilename; 87 | } 88 | 89 | public boolean isPluginReload() { 90 | return pluginReload; 91 | } 92 | 93 | public void setPluginReload(boolean pluginReload) { 94 | this.pluginReload = pluginReload; 95 | } 96 | 97 | public int getPluginCategory() { 98 | return pluginCategory; 99 | } 100 | 101 | public void setPluginCategory(int pluginCategory) { 102 | this.pluginCategory = pluginCategory; 103 | } 104 | 105 | public int getUniqueId() { 106 | return uniqueId; 107 | } 108 | 109 | public void setUniqueId(String uniqueId) { 110 | //extract the first 4 chars and compute a 32bit integer unique id 111 | int id = 0; 112 | for (int i=0; i values; 16 | 17 | public Parameter() { 18 | values = new ArrayList(); 19 | } 20 | 21 | public Parameter(String variable, String name, String label) { 22 | this(); 23 | this.variable = variable; 24 | this.name = name; 25 | this.label = label; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | 36 | public String getLabel() { 37 | return label; 38 | } 39 | 40 | public void setLabel(String label) { 41 | this.label = label; 42 | } 43 | 44 | public String getVariableName() { 45 | return variable; 46 | } 47 | 48 | public void setVariableName(String variable) { 49 | this.variable = variable; 50 | } 51 | 52 | public float getValue(int program) { 53 | return values.get(program); 54 | } 55 | 56 | public void addValue(int program, float value) { 57 | values.add(program, value); 58 | } 59 | 60 | public void setValue(int program, float value) { 61 | values.set(program, value); 62 | } 63 | 64 | public float getMultiplier() { 65 | return multiplier; 66 | } 67 | 68 | public void setMultiplier(float multiplier) { 69 | this.multiplier = multiplier; 70 | } 71 | 72 | public void setHasMultiplier(boolean hasMultiplier) { 73 | this.bMultiplier = hasMultiplier; 74 | } 75 | 76 | public boolean hasMultiplier() { 77 | return this.bMultiplier; 78 | } 79 | 80 | public String toString() 81 | { 82 | final String TAB = " "; 83 | 84 | String retValue = ""; 85 | 86 | retValue = "Parameter ( " 87 | + super.toString() + TAB 88 | + "variable = " + this.variable + TAB 89 | + "name = " + this.name + TAB 90 | + "label = " + this.label + TAB 91 | + "bMultiplier = " + this.bMultiplier + TAB 92 | + "multiplier = " + this.multiplier + TAB 93 | + "values = " + this.values + TAB 94 | + " )"; 95 | 96 | return retValue; 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/config/Program.java: -------------------------------------------------------------------------------- 1 | package de.flupp.clojurevst.config; 2 | 3 | public class Program { 4 | 5 | private String name; 6 | 7 | public String getName() { 8 | return name; 9 | } 10 | 11 | public void setName(String name) { 12 | this.name = name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/plugins/2polelp.clj: -------------------------------------------------------------------------------- 1 | (ns de.flupp.clojurevst.clj2polelp) 2 | 3 | ;;Type : LP 2-pole resonant tweaked butterworth 4 | ;;References : Posted by daniel_jacob_werner [AT] yaho [DOT] com [DOT] au 5 | 6 | ;;Notes : 7 | ;This algorithm is a modified version of the tweaked butterworth lowpass filter by Patrice Tarrabia posted on musicdsp.org's archives. 8 | ;It calculates the coefficients for a second order IIR filter. The resonance is specified in decibels above the DC gain. 9 | ;It can be made suitable to use as a SoundFont 2.0 filter by scaling the output so the overall gain matches the specification 10 | ;(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 11 | ;butterworth highpass algorithm with my "q =" line of code to get a highpass also. 12 | ; 13 | ;How it works: normally q is the constant sqrt(2), and this value controls resonance. At sqrt(2) resonance is 0dB, smaller values 14 | ;increase resonance. By multiplying sqrt(2) by a power ratio we can specify the resonant gain at the cutoff frequency. 15 | ;The resonance power ratio is calculated with a standard formula to convert between decibels and power ratios (the powf statement...). 16 | ;Good Luck, 17 | ;Daniel Werner 18 | ;http://experimentalscene.com/ 19 | 20 | ;Code : 21 | ;float c, csq, resonance, q, a0, a1, a2, b1, b2; 22 | 23 | ;c = 1.0f / (tanf(pi * (cutoff / samplerate))); 24 | ;csq = c * c; 25 | ;resonance = powf(10.0f, -(resonancedB * 0.1f)); 26 | ;q = sqrt(2.0f) * resonance; 27 | ;a0 = 1.0f / (1.0f + (q * c) + (csq)); 28 | ;a1 = 2.0f * a0; 29 | ;a2 = a0; 30 | ;b1 = (2.0f * a0) * (1.0f - csq); 31 | ;b2 = a0 * (1.0f - (q * c) + csq); 32 | 33 | ;;TODO: this is a quite literal conversion from the original algorithm, needs clojurization (use atoms and let special form). 34 | 35 | 36 | ;; config 37 | (def plugin-config {:plugin-category 1 38 | :unique-id "2plp" 39 | :product "clj2plp" 40 | :num-inputs 2 41 | :num-outputs 2 42 | :can-process-replacing true 43 | :can-mono false 44 | :vendor "clojurevst" 45 | :can-do ["2in2out", "plugAsChannelInsert", "plugAsSend"]}) 46 | 47 | ;; parameters 48 | (def #^{:parameter-name "Cutoff" 49 | :parameter-label "hz"} 50 | p1 0.04) 51 | (def #^{:parameter-name "Max Cutoff" 52 | :parameter-label "hz"} 53 | p3 0.81) 54 | (def #^{:parameter-name "Resonance" 55 | :parameter-label "db"} 56 | p2 0.81) 57 | (def #^{:parameter-name "Fine Reso" 58 | :parameter-label "db"} 59 | p4 0.9) 60 | 61 | ;;globals 62 | ;constants 63 | (def p1filtercoeff 0.0007) 64 | (def pi 3.1415926535897932384626433832795) 65 | 66 | ;thread-local vars (use thread-local bindings for them?) 67 | (def p1smooth 0.0) 68 | (def c 0.0) 69 | (def csq 0.0) 70 | (def q 0.0) 71 | (def a0 0.0) 72 | (def a1 0.0) 73 | (def a2 0.0) 74 | (def b1 0.0) 75 | (def b2 0.0) 76 | 77 | ;state maintained across process() calls 78 | (def out11 0.0) 79 | (def out12 0.0) 80 | (def in11 0.0) 81 | (def in12 0.0) 82 | (def out21 0.0) 83 | (def out22 0.0) 84 | (def in21 0.0) 85 | (def in22 0.0) 86 | 87 | 88 | ;; effect 89 | (defn process-replacing [inputs outputs] 90 | (let [in1 (nth inputs 0) 91 | in2 (nth inputs 1) 92 | out1 (nth outputs 0) 93 | out2 (nth outputs 1) 94 | samples (count out1)] 95 | (dotimes [i samples] 96 | (when (= (mod i 20) 0) ;audible param change "lag" 97 | (def p1smooth (+ (* p1filtercoeff p1) (* (- 1.0 p1filtercoeff) p1smooth))) ;parameter smoothing 98 | (def c (/ 1.0 (. java.lang.Math (tan (* pi (/ (+ 0.001 (* p1smooth p3)) 2.15)))))) 99 | (def csq (* c c)) 100 | (def q (* (. java.lang.Math (sqrt 2.0)) (- 1.0 p2) (- 1.0 p4))) 101 | (def a0 (/ 1.0 (+ 1.0 (* q c) csq))) 102 | (def a1 (* 2.0 a0)) 103 | (def a2 a0) 104 | (def b1 (* (* 2.0 a0) (- 1.0 csq))) 105 | (def b2 (* a0 (+ (- 1.0 (* c q)) csq)))) 106 | (aset-float out1 i (- (+ (* (nth in1 i) a0) (* in11 a1) (* in12 a2)) (* out11 b1) (* out12 b2))) 107 | (aset-float out2 i (- (+ (* (nth in2 i) a0) (* in21 a1) (* in22 a2)) (* out21 b1) (* out22 b2))) 108 | (def out12 out11) ;cascade 109 | (def out11 (nth out1 i)) 110 | (def in12 in11) 111 | (def in11 (nth in1 i)) 112 | (def out22 out21) 113 | (def out21 (nth out2 i)) 114 | (def in22 in21) 115 | (def in21 (nth in2 i))))) 116 | -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/plugins/2polelpset.clj: -------------------------------------------------------------------------------- 1 | (ns de.flupp.clojurevst.clj2polelpset) 2 | 3 | ;;Type : LP 2-pole resonant tweaked butterworth 4 | ;;References : Posted by daniel_jacob_werner [AT] yaho [DOT] com [DOT] au 5 | 6 | ;;Notes : 7 | ;This algorithm is a modified version of the tweaked butterworth lowpass filter by Patrice Tarrabia posted on musicdsp.org's archives. 8 | ;It calculates the coefficients for a second order IIR filter. The resonance is specified in decibels above the DC gain. 9 | ;It can be made suitable to use as a SoundFont 2.0 filter by scaling the output so the overall gain matches the specification 10 | ;(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 11 | ;butterworth highpass algorithm with my "q =" line of code to get a highpass also. 12 | ; 13 | ;How it works: normally q is the constant sqrt(2), and this value controls resonance. At sqrt(2) resonance is 0dB, smaller values 14 | ;increase resonance. By multiplying sqrt(2) by a power ratio we can specify the resonant gain at the cutoff frequency. 15 | ;The resonance power ratio is calculated with a standard formula to convert between decibels and power ratios (the powf statement...). 16 | ;Good Luck, 17 | ;Daniel Werner 18 | ;http://experimentalscene.com/ 19 | 20 | ;Code : 21 | ;float c, csq, resonance, q, a0, a1, a2, b1, b2; 22 | 23 | ;c = 1.0f / (tanf(pi * (cutoff / samplerate))); 24 | ;csq = c * c; 25 | ;resonance = powf(10.0f, -(resonancedB * 0.1f)); 26 | ;q = sqrt(2.0f) * resonance; 27 | ;a0 = 1.0f / (1.0f + (q * c) + (csq)); 28 | ;a1 = 2.0f * a0; 29 | ;a2 = a0; 30 | ;b1 = (2.0f * a0) * (1.0f - csq); 31 | ;b2 = a0 * (1.0f - (q * c) + csq); 32 | 33 | ;;TODO: this is a quite literal conversion from the original algorithm, needs clojurization (use atoms and let special form). 34 | 35 | ;; config 36 | (def plugin-config {:plugin-category 1 37 | :unique-id "2plp" 38 | :product "clj2plp" 39 | :num-inputs 2 40 | :num-outputs 2 41 | :can-process-replacing true 42 | :can-mono false 43 | :vendor "clojurevst" 44 | :can-do ["2in2out", "plugAsChannelInsert", "plugAsSend"]}) 45 | 46 | ;; parameters 47 | (def #^{:parameter-name "Cutoff" 48 | :parameter-label "hz"} 49 | p1 0.04) 50 | (def #^{:parameter-name "Max Cutoff" 51 | :parameter-label "hz"} 52 | p3 0.81) 53 | (def #^{:parameter-name "Resonance" 54 | :parameter-label "db"} 55 | p2 0.81) 56 | (def #^{:parameter-name "Fine Reso" 57 | :parameter-label "db"} 58 | p4 0.9) 59 | 60 | ;;globals 61 | (def #^{:thread-bound 1} p1smooth 1.0) 62 | (def #^{:thread-bound 1} p1filtercoeff 0.0007) 63 | (def pi 3.1415926535897932384626433832795) 64 | (def #^{:thread-bound 1} out11 0.0) 65 | (def #^{:thread-bound 1} out12 0.0) 66 | (def #^{:thread-bound 1} in11 0.0) 67 | (def #^{:thread-bound 1} in12 0.0) 68 | (def #^{:thread-bound 1} out21 0.0) 69 | (def #^{:thread-bound 1} out22 0.0) 70 | (def #^{:thread-bound 1} in21 0.0) 71 | (def #^{:thread-bound 1} in22 0.0) 72 | (def #^{:thread-bound 1} c 0.0) 73 | (def #^{:thread-bound 1} csq 0.0) 74 | (def #^{:thread-bound 1} q 0.0) 75 | (def #^{:thread-bound 1} a0 0.0) 76 | (def #^{:thread-bound 1} a1 0.0) 77 | (def #^{:thread-bound 1} a2 0.0) 78 | (def #^{:thread-bound 1} b1 0.0) 79 | (def #^{:thread-bound 1} b2 0.0) 80 | 81 | ;; effect 82 | (defn process-replacing [inputs outputs] 83 | (let [in1 (nth inputs 0) 84 | in2 (nth inputs 1) 85 | out1 (nth outputs 0) 86 | out2 (nth outputs 1) 87 | samples (count out1)] 88 | (dotimes [i samples] 89 | (when (= (mod i 20) 0) ;audible param change "lag" 90 | (set! p1smooth (+ (* p1filtercoeff p1) (* (- 1.0 p1filtercoeff) p1smooth))) ;parameter smoothing 91 | (set! c (/ 1.0 (. java.lang.Math (tan (* pi (/ (+ 0.001 (* p1smooth p3)) 2.15)))))) 92 | (set! csq (* c c)) 93 | (set! q (* (. java.lang.Math (sqrt 2.0)) (- 1.0 p2) (- 1.0 p4))) 94 | (set! a0 (/ 1.0 (+ 1.0 (* q c) csq))) 95 | (set! a1 (* 2.0 a0)) 96 | (set! a2 a0) 97 | (set! b1 (* (* 2.0 a0) (- 1.0 csq))) 98 | (set! b2 (* a0 (+ (- 1.0 (* c q)) csq)))) 99 | (aset-float out1 i (- (+ (* (nth in1 i) a0) (* in11 a1) (* in12 a2)) (* out11 b1) (* out12 b2))) 100 | (aset-float out2 i (- (+ (* (nth in2 i) a0) (* in21 a1) (* in22 a2)) (* out21 b1) (* out22 b2))) 101 | (set! out12 out11) 102 | (set! out11 (nth out1 i)) 103 | (set! in12 in11) 104 | (set! in11 (nth in1 i)) 105 | (set! out22 out21) 106 | (set! out21 (nth out2 i)) 107 | (set! in22 in21) 108 | (set! in21 (nth in2 i))))) 109 | -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/plugins/cljdelay.clj: -------------------------------------------------------------------------------- 1 | (ns de.flupp.clojurevst.cljdelay) 2 | 3 | ;; config 4 | (def plugin-config {:plugin-category 1 5 | :unique-id "cljD" 6 | :product "cljdelay" 7 | :num-inputs 1 8 | :num-outputs 1 9 | :can-process-replacing true 10 | :can-mono true 11 | :vendor "clojurevst" 12 | :can-do ["1in1out", "plugAsChannelInsert", "plugAsSend"]}) 13 | 14 | ;; parameters 15 | (def #^{:parameter-name "Delay" 16 | :parameter-label "" 17 | :parameter-display-multiplier 44099} 18 | pdelay 0.5) 19 | (def #^{:parameter-name "Feedback" 20 | :parameter-label ""} 21 | pfeedback 0.9) 22 | (def #^{:parameter-name "Out" 23 | :parameter-label ""} 24 | pout 0.5) 25 | 26 | (def cursor (atom 0)) 27 | (def buffer (make-array Float/TYPE 44100)) 28 | 29 | ;; effect 30 | (defn process-replacing [inputs outputs] 31 | (let [input (nth inputs 0) 32 | samples (count input) 33 | output (nth outputs 0)] 34 | (dotimes [i samples] 35 | (aset-float output i (* pout (nth buffer @cursor))) 36 | (aset-float buffer @cursor (+ (nth input i) (* (nth output i) pfeedback))) 37 | (swap! cursor inc) 38 | (if (>= @cursor (* pdelay 44099)) 39 | (reset! cursor 0))))) -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/plugins/cljgain.clj: -------------------------------------------------------------------------------- 1 | (ns de.flupp.clojurevst.cljgain) 2 | 3 | ;; config 4 | (def plugin-config {:plugin-category 1 5 | :unique-id "cljG" 6 | :product "cljgain" 7 | :num-inputs 1 8 | :num-outputs 1 9 | :can-process-replacing true 10 | :can-mono true 11 | :vendor "clojurevst" 12 | :can-do ["1in1out", "plugAsChannelInsert", "plugAsSend"] 13 | :programs ["foo", "bar"] }) 14 | 15 | ;; parameters 16 | (def #^{:parameter-name "Gain" 17 | :parameter-label "" 18 | :value-in-programs [0.5, 0.9] } 19 | pgain 0.5) 20 | 21 | (defn get-parameter-display [param] 22 | (str (* pgain 10))) 23 | 24 | (defn get-parameter-label [param] 25 | (str "bla")) 26 | 27 | ;; effect processing 28 | (defn process-replacing [inputs outputs] 29 | (let [input (nth inputs 0) 30 | samples (count input) 31 | output (nth outputs 0)] 32 | (dotimes [i samples] 33 | (aset-float output i (* pgain (nth input i)))))) -------------------------------------------------------------------------------- /src/de/flupp/clojurevst/plugins/cljstereogain.clj: -------------------------------------------------------------------------------- 1 | (ns de.flupp.clojurevst.cljstereogain) 2 | 3 | ;; config 4 | (def plugin-config {:plugin-category 1 5 | :unique-id "clsG" 6 | :product "cljstereogain" 7 | :num-inputs 2 8 | :num-outputs 2 9 | :can-process-replacing true 10 | :can-mono false 11 | :vendor "clojurevst" 12 | :can-do ["2in2out", "plugAsChannelInsert", "plugAsSend"] 13 | :programs ["foo", "bar"] }) 14 | 15 | ;; parameters 16 | (def #^{:parameter-name "Gain" 17 | :parameter-label "" 18 | :value-in-programs [0.5, 0.9] } 19 | pgain 0.5) 20 | 21 | (defn get-parameter-display [param] 22 | (str (* pgain 10))) 23 | 24 | (defn get-parameter-label [param] 25 | (str "bla")) 26 | 27 | ;; effect processing 28 | (defn process-replacing [inputs outputs] 29 | (let [in1 (nth inputs 0) 30 | in2 (nth inputs 1) 31 | out1 (nth outputs 0) 32 | out2 (nth outputs 1) 33 | samples (count in1)] 34 | (dotimes [i samples] 35 | (aset-float out1 i (* pgain (nth in1 i))) 36 | (aset-float out2 i (* pgain (nth in2 i)))))) 37 | --------------------------------------------------------------------------------