Dynamic DSP Libraries
-
Hi Hasan,
there is a new Effect Slot module, which is basically a empty shell that can hold a HISE effect. It can load and unload effects dynamically using scripting methods.
Now for loading impulse responses (or any sort of audio file), you'll need another object type called
AudioSampleProcessor
(this is a special type of module that handles audio files). It has a methodsetFile
which you can pass the filename.This javascript snippet shows the example usage:
// Get a reference to the slot const var slot = Synth.getSlotFX("Effect Slot"); const var EmptyButton = Content.addButton("EmptyButton", 10, 10); Content.setPropertiesFromJSON("EmptyButton", { "radioGroup": "1" }); inline function onEmptyButtonControl(component, value) { // Clears the slot by loading an empty fx slot.setEffect("EmptyFX"); }; EmptyButton.setControlCallback(onEmptyButtonControl); const var Convolution = Content.addButton("Convolution", 170, 10); Content.setPropertiesFromJSON("Convolution", { "radioGroup": "1" }); inline function onConvolutionControl(component, value) { // This returns a effect object, but we need a // Audio Sample Processor object to load audio files local module = slot.setEffect("Convolution"); // This returns the effect as audio sample processor // (a special subtype of module that uses audio files like the looper // or the convolution reverb local moduleAsIr = Synth.getAudioSampleProcessor("Convolution"); moduleAsIr.setFile("{PROJECT_FOLDER}British.aif"); }; Convolution.setControlCallback(onConvolutionControl);
This is the full HISE snippet for the example:
HiseSnippet 1195.3oc2Ws0SibCE1AXjVlsopaUesRV4ofDkMY2scq5pUERffxVtIBhReh5LiShKdrGY6A1TD+25Oo9Peu834BiSXJjH01GZj3hO97Y+4uywmiyIJY.UqkJTsme1zXJp1m3MXpvLo6DBSf5uKX2amjPl7Lp1f5LMln0zPTsZqtu0gZquFJ8yu+8cHbhHfVZBgNWxBnGvhXlRq+71+.iy6QBomwhb79Ma2OPJ5J4xDfLq50BESBthLldDw51JdnqYzaznZs7d8q1RcU6g+5NaM2mCC5.+d7r1QAIJEUXNGfip4U6OgO071KjYjpAFhgBq4ZcjgSGLQdiHaqOmoYC4T6f1nA.mxL2SxCsGdqUT2ILd3IE5mFAqxIkp4pYp4W3cHKjcu8RU8yRm.WhvUZqsxiQu1OM8ZUE8p4Pu0xn2K7FDnXwlxYrb6yys92vtrIQq7Gq48xWh2mZvDrhNhBhb.EajXyDJVykl59PDUavWSToiwuGmlas0XpY.Lt2EMar2nQz.C1NrwFuqtuCj8hhMS6jXLRAfrqTXfn3VjvvLa.1RGZrItcK6Oa7N+BO0T64JlpLLptmRF8gAGez7ntstOF2PQfL78Uxj3FeGtQ6F08uCVn59LAmIn3QIh.CCngT3f1tOJIuYfLJVJfcbSf17D5F08gUccPa5xoDk9d8.ObJlKgsRLFSDXpcovi9H3qcVKeyTibN16hTE4N3m59N6q0w78tKgyGBWSZVEwlSNAqWK4IomiJkSGGrx4aWP8bVXKqd5f9o0yylvzPplIQIzPRGMK2QN7Wf+rIdXhAeCEKnzPXxLDoktvCHQwbZY5bNDatpMdfIodMhwoZ.FWFP33HYXBf4834iMtmWqDudEbyFwyYGQmu75LRDWPhLXMIXcLMfA6nNYnAtUhkiJ1byDhAmnoZWFh4rqno6.WZiFYqCbnrlBbBxJ50T0v4NP6n6qbuGlJPY5y8xSEmwRvVonGvilMt8jSO9C608rK6c7A6t2o20QwLL8jsHrQNItNKU0ItOLCXiTf08cRSNRZnGKZtg+s9q6e2ClYznplJe43TUUyZa+ndDXMEIQCophjvb+fpqyVa1ewpMGjcIxwQonufYNNld+3rCYgkVn6ObtlJOTtVSOLU3VooG1P.kewFZMrRt6fqlzFCeZdKfrZCHFT3utmSIFTpnXaml17dgf6DnKfW95AzCaNAsHsYcDyr8Msu6HeB6J41Ypu87nYlotq7RzLs0B2qeQo6K7NgYBlTMeWoB9ZCF+Ky27ml.wyzJTkjcMudW7e76Px4xy7xdOPJMJHV5iBlkOyc2a0EiOOAGddgd3lflQDWKtDYW0zYe96O8a+H0LqID5.fkhfYBt8ihS3Z5ATwXyD2rzb9zWDmXbsaK0l8B3uz6QJ3hhrac1swHxGS+2sQn+wDLzoRPEDiOjXTL6xeTRz.3k5ATH8QHnbscKWwVJHabqB1LfJBaU7t67IaaGWKex1ES9+l8HhDnjWFjUSzls8rTKf1JRijq6cncLtcQYPqlGAecfKCBr2B+p1HT0Hd0Ri30KMh2rzH95kFw2rzHd6Ri3aeDD1u60NIFYTVMYD5u.4+KURC
(I've just committed a small fix that allows dynamic creation of audio sample processors, so you need this version for the example to work).
During development, HISE will use the actual file found in the project directory. In compiled plugins, those audio files will be embedded into the binary and their file reference is resolved internally (unless you opt out of this, then it will look in the AppData folder of the plugin). Now if you compile for iOS, it will just copy the AudioFiles subdirectory in the App bundle and resolve the path correctly, so you shouldn't worry about this.
BTW, If you're mixing HISE effects with external libraries wrapped into Script FX, I'd recommend to build one ScriptFX per wrapped effect and use the same mechanism with the shells with scripted FX (check out the
exportState
andrestoreState
functions of Effect modules which allow you to save whole states including script contents etc..The
core
FX module is available in both HISE and compiled plugins. -
HISE Standalone is crashing when I call setEffect with "ScriptFX". Hope you fix that soon.
-
Yupp, I'll take a look (this feature is pretty new and I just made some quick tests with script processors)
-
Alright, this is fixed. Let me know if there are more issues, there might be some edge cases with dynamic creation of ScriptFX.
-
I am getting some weird thing.
The effect in effect slot is still working when I set it bypassed. It is bypassed only when I set effect slot bypassed. The bypass button of effect itself does not working. -
And I want to get the name of effect in Effect Slot, such as "IRVerb" or "Script FX Delay".
-
The Bypass works now.
However the ID will be mercilessly overwritten as soon as you change the effect in the slot, so I wouldn't count too much on that information. For which purpose do you need this?
-
I am going to swap effects of slots on runtime. And so I want to know the effect name or type of each slot on runtime.
-
I have tried to change script fx name as customized one like "3BandEQ", and "Save File as XML Backup". Then I can see "3BandEQ.js" saved in "ScriptProcessors" folder.
However, when I restart HISE standalone and reload backup XML file I can see all the script fx names become same as "ScriptFX". And when I save file as XML backup again all the script js files are lost except one.
Please check these issues. -
Hello, @Christoph-Hart! Any answers here?
-
Sorry, I am on the train and internet is terrible.
You don't need the IDs for swapping the effects on runtime. Calling
SlotFX.setEffect(typeId)
returns a reference to the created module. Just store this module and change its parameters using the ordinary scripting methods.This example lets you toggle between a SimpleGain and a ScriptFX with a simple script and lets you control the first parameter from a external slider. It uses the Base64 encoded state feature:
/* This is a Base64 exported state: 1. select the module, 2. Edit->Create Base64 encoded state 3. Paste the string from the clipboard This contains the whole state (script, parameter values, etc) (actually it's just the encoded XML element) */ const var scriptState = "383.3ocYR0sRCCCE9D0JtB5yPoW0AiQ20hHroBhnNVGh2l0dzELKmRRp5T7AY24qiuE9lnIsc5DCgP99NmSx24mwZJGMFRCrcltrDAVXPVtVTZO6V37S9Cb3xRtwfE.iscCIv9LXDoLjD6WpEJaRbignyUBqfKEFtUPp3tGteXXtySazibczEJZVzQQtPsnx1mWT3YRh8mw8hRc6tGFte3cUpbe7QkZrjqwozXIeYhgunThS3VrWzLIk+Pl3Era3qgcBeayXpSsgdGRxmyUJTZ9mWjxqBMISTUKlg5dNAJq940bkkSKDVRmYcemAX6MjJVlMmdRArsCF.2HLhYRrAjQRxeKEHkuBbcI151eRf0zovlZ7W1ezzuT4MkpM9XXzbgrX759mA.XBUYEp6uja0hmAVvUUKxnJcNNpM6cbrs.en03TO1AgLTUTC9xsZMNviYsFGr1Hz1y.2PPqHcxwVO4bP6nRlTTfZP3FT1Mv2Rg5RJv5ri64de0pONF9FXYl+RE"; const var Slot = Synth.getSlotFX("Slot"); Slot.setEffect("EmptyFX"); // Use this variable to store the current Effect reg currentEffect; const var ClearButton = Content.addButton("ClearButton", 10, 11); // [JSON ClearButton] Content.setPropertiesFromJSON("ClearButton", { "saveInPreset": 0, "radioGroup": "1" }); // [/JSON ClearButton] inline function onClearButtonControl(component, value) { // Load a Simple Gain module and store it in the variable. currentEffect = Slot.setEffect("SimpleGain"); FirstParameter.set("enabled", true); FirstParameter.setValue(currentEffect.getAttribute(0)); }; ClearButton.setControlCallback(onClearButtonControl); const var ScriptButton = Content.addButton("ScriptButton", 150, 10); // [JSON ScriptButton] Content.setPropertiesFromJSON("ScriptButton", { "saveInPreset": 0, "radioGroup": "1" }); // [/JSON ScriptButton] inline function onScriptButtonControl(component, value) { FirstParameter.set("enabled", true); // Create a empty script fx and store it currentEffect = Slot.setEffect("ScriptFX"); // Load the exported state (loads the script, compiles it and restores the // interface values) currentEffect.restoreState(scriptState); FirstParameter.setValue(currentEffect.getAttribute(0)); // ... You can do any sort of initialisation of this effect here // (etc. load impulse responses, set table curves, etc). }; ScriptButton.setControlCallback(onScriptButtonControl); const var FirstParameter = Content.addKnob("FirstParameter", 300, 0); // [JSON FirstParameter] Content.setPropertiesFromJSON("FirstParameter", { "enabled": false, "saveInPreset": 0 }); // [/JSON FirstParameter] inline function onFirstParameterControl(component, value) { // Check if a effect was loaded and set its first attribute. if(currentEffect) currentEffect.setAttribute(0, value); }; FirstParameter.setControlCallback(onFirstParameterControl);
Example Patch:
HiseSnippet 2070.3ocuX01bhibDV30jZMW7U4Rke.SwWB9NexBvurrakbGfAuXavr.FicpT2MHM.isjF4YFgAtr+Wymxmy+fjdjDKBLmW65pao7KnY5tmmtmmtaZZxYlDgfw0R7Ucl5QzR7GS1dpqbT4QXpqVsi0R70IqiERBGEtToodXgfXokHwqNQsPhs1TK30+8GJgswtljEKoo0kQMImScnxEq9y+3YTa6pXKRGpSLo2+GqYxbKyrY9.ddURCMOr4c3gjFXkXajTaLk7fPKgQx74z42ks+rh555y5Osdtm52y0L84bhqrKntVhjI9evqDIqXQkLdaIVR.atYIl0z1iXO3FdzcoBZeah5grZsALEtbUlskx4UqpUdD01p47PnPCrRyEAzWEFP+KIqSsneZ8EA1+TvFnEZDOzlXimBdY+7vyXcvKQL3sYH79ljsM4TO4hcTX6OGs5uB5B2Tai+0WkbuuE0YDUffevnRXA4v8QjIdLtjXgDJz+1sSscpsxpiDDahoDIGQPNLKeaxtHXib5Hkq98+8xbBH8mrgqIyZtI.wxqiZp3gApKjbp6Pz.NyI3YSapWeFlaABtcJzVAHB3RRHVKBj3gQLaRnwPYDAveWjGlCLKE2dL11mH1EQjl6.1HC1T5issmhnx+p.cquHD1yAUu5mi.mwA3Tf3e6damBNLPlwXNJz3A2an+FJc92jWOOy75VFhVkKWtRgiMNUV5foMYWYTj9gbFidOmUZjaitmLJmsg0rJmelSqVdGz4nhWma+6o9UJX6VSXdvwkG1rPgFNsmjaemGt4zSpWsUYtosjebwt8Z1U1syMWbX27G0tP494mzR9vfJ5TgY4ZiKbduiYme6wGdkWkSws5SG5N8xR2O3rJUkW1zKu7DIoWO4z13Yz9lypb5Mcm8gOHaJbmj04pN4ut0n237vaF0x7P4IUkj7lW50mbzGt6F9s2+.aVuZjqGMz2syn146xuZ140t66ZZmuBGm+9glkH3o87ZKFZcRqINSu7zN2Tv4pambeo50Z24xyrGdfUihmdeg8M5e2csO63tsbt1j3Tr2g0u8zt10crZUjKJWUO26Oez0s3Eu8CslPNqx6uyuTeyZYOHKo0.iYrw12bzUYIyl42Y+524UuPudy5Oj26nCJ3TTuWoKuth2g92hMF4Tr63Ku7rItmZ1ngW8CM6yE5DWi7ctHawgm24xNkKLQbS8FioWKpdBO66mkcpdtlMu+8lSdn6E62u4gtsr6zYvMMyWsS15iy0Z3AsNc7Ab5g6aQL7tnQ0BU6cs820pR52ox.hyQZayj.4HnRq9PhT8b0dYRq9e5cBDW8VcAQVYv.HoIS5JNdxoU6Es6d6gtTnxE.dNXPJFpEfjLffy3goHQk7Pg5ucJNY370BWJvNwvTYaBlWxWJYt.zJCIOfn5XKqv0xjNl.o2Ek0.9M6NuSAk+wosunQbC7OSMWevCfRHdDtjRDUg7UknqZqeAxYQoE3wjZtM4DPmzuEYravpbrEkcBm46AqkNa5sS8wnCcuGcpamh5ZScInA9tlRJ3HP6jE6qvDmYmwj43wbAzsaXdOjBCHXKvlmyvVPYr1TGOHdpZuEUoBgcshBtTIBVVEhmG30AkWJzptZW45KzjJKFbCBUo1pJkKjMmWBRIblzDWkAsfXhjC.6WUxtJbmYoCUwiJJgBi88kjLF6nT9iAWxwBAJciBCkg5a8g9qYVWLRobb9ZPMsmhbDWBE63.E8vHN8HtDeV9wJl62BAYoyUENdLGItHeFRxy+RCPPTGMLhnRdi5LfFLYI1zyg7DnXTx+VwnpA8jVpYKJiMrQXOu4s4TtB0lHTTW0ICAP0YGHTnwnvkAe.1jD0HbmUAkdjNAs0xDqE2uYJZj6.eNNz0LejI1EYw.XBgKvsPrA.3nRJ1lJvgWWCBK6QBiViH7HmP8JCz.WGoBAHHgy2FpQBHGtHEpt6.pPxfRk.rFOueud.HBSUhyDVetxZ3JqjrrbnX4zkybY8yjdYI.dSdCHcYorkkE4ylu7HKFlwLmY9Vz.LDK1csoQqlvrxQutDlkE4yjxDkKLhXdGhNPkMDdy8.VDbSAT2f7A3xgJEnAJaivyoIpaG5fk4QJ54J7SwRLq4.3cwtZeLC8wWtq0uTVIlu2fIIW3lYmT+RpsR8wGsyfAqaqHSYS3qaW0XP7mPsLt9N8I74dUjbvmxe4YDR87lQvLjJESPlaMHG6BOxmdNzImuhg1mbt3KsvohuZfyrFwVrziGLQKhdCu6wSn.yIo5AikKO7jpWZzFPvZowSpoLlfJmFe3zWvDUFO6A9dtv8aR1jJMGsd7twZvKLr1u23MZ9zsSFlBs.ralrZuuvCiFgkWmL7SBG.i+PvCKCjUX7u54Ajm2jvoRNuOavwG6w0L5ah+cRfyJfgJ08fYQ+TSZTsk5VkdkdCp5+qqif5uPU6nt.wp23wIvTpjNrl13oYDX0mhrE376h5ayLuqMcF4Q0M7BcsRJAxXNB65RrEu7pKatbr90OuX8p0RVxAVTCHNFexhEOtZ0ZX0sX9Rp6v5XnCvDsDIa36zl4yMIki7d025yFp5LgOandVkj0l3ZYL+qDJZyrpmSDsY14atnDUhDQfDfiLf470QTk11TKBWiZoHupqTsfPZTV8OOXv+4G9h.0uDmgC1jy9IyvHgJi50Aq.wD2fustsRVW8LJ67Xf5xzgZQ+ISSUIluGtHWuF4dwZj+Eqw9uXMN3EqwguXMN5EqwadBMTe6hE8kLmvFNZZ+evFvqZl
-
Thanks for your reply.
I have one more question.
If I use a dynamic library which will be loaded in the plugin or standalone app, then shall I have to add such library file(like .dylib) in the installer?
And if so, where should I have to install such library? -
On OSX, the library must be at
Application Support/Company/Product/lib
On Windows:
%APPDATA%/Company/Product/dll
But iOS apps can't use dynamic libraries, so you have to build a static library and link the HISE project with it (this is a bit complicated, but NSK uses this method and it works fine).
-
Hello, @Christoph-Hart!
I am going to add script fx like following.<?xml version="1.0" encoding="UTF-8"?>
<Processor Type="ScriptFX" ID="ScriptFX" Bypassed="0" Script="function prepareToPlay(sampleRate, blockSize) { vkfx_ampEq.prepareToPlay(sampleRate, blockSize); } function processBlock(channels) { vkfx_ampEq >> channels; } function onControl(number, value) { } ">
<EditorStates BodyShown="1" Visible="1" Solo="0" onInitOpen="0" prepareToPlayOpen="0"
processBlockOpen="1" onControlOpen="0" Folded="0"/>
<ChildProcessors/>
<RoutingMatrix NumSourceChannels="2" Channel0="0" Send0="-1" Channel1="1" Send1="-1"/>
<Content/>
</Processor>But when I make BASE64 encoded state and call restoreState on runtime, HISE freezes and then crashes.
I guess it is because of function "processBlock". If I let it empty then it won't crash.
Please check this issue. -
Ah yes, it's a deadlock situation where the audio thread holds the compile lock and the message thread the swap lock.
This is fixed now. However beware the AmpReverb is producing nasty bursts if uninitialised (take a look at the NSK code how I tamed it). Definitely keep your speakers down until you are safe :)
Edit: Set these parameters:
vkfx_ampEq.setParameter(vkfx_ampEq.Volume, 0.5); vkfx_ampEq.setParameter(4, 0.0); // Reverb Level (enum is defect) vkfx_ampEq.setParameter(5, 1.0); // Amp Bypass (enum is defect)
-
ok, I got. Thanks.
-
Hello, man!
It still crashes when I add the code "vkfx_ampEq >> channels;" in processBlock function.
-
Is the crash happening if you compile or if you select the effect with the drop down?
-
It crashes when I compile script.
-
I added "vkfx_ampEq >> channels;" line in processBlock function and then compiled that script first.
And next I created Base64 encoded state and set it as a const string variable.
Finally when I compile Midi Script Processor, it freezes and becomes not responding.
Please resolve this issue asap.