New Feature: SuspendOnSilence
-
Hi everybody,
I'm currently profiling a project I'm working on and noticed a rather significant optimization possibility: when there is no signal input, some FX can be suspended until a signal is detected and save some CPU. This might highly improve the idle performance of your plugin, which is important for big DAW sessions.
This is not a new idea (some DAWs implement this already) and in certain parts of the HISE codebase this is already implemented, but now I refactored it to a general system and also allow DSP networks to set the flag.
HiseSnippet 1427.3oc2X0raabCDlqrobkSbRSQP+AnG1C8fCfqgTRZZAxAKaYqTgFYK3UwMsWLn2kxhP6RtkKkrUK58donmys9Zza8Qn25w9J32f1gbWIsqzZAaE2DzJCXHNbH42Ly2LbnZIEtznHgDYUp8vPJx51XmgbU2ZcILNpwtHq6faRhTTocrncFFRhhndHKqkdlVfUokQlOWr0NDeB2kNQDBcjf4ReNKfolHsU0uh46Wm3QayBRo8iq1vUvqI7E8A7rDtLJj31ibJcehVsBXjUw87XJgzQQTzHPmcDdCc5JNiGq+QrH1I9T8fJHGXihEip0k460ZjsFgPV3VSr7khs76iax7XikOwC7tlIrmrhz9.qByCRUtFPxJEjVNFR2C63JYgpIynwysvM3P.oCAb0ogRrtHqeGWS.JvUaFP5QqKgAiWv5Oob4Mrg+8fm1oO2UwDbaAeeghd.e8Gr5OrZoU+wUsmdpNcxcN8wHE99TYtSqitx4sv048CNgJ2vd.wuOcrhf4m0mV7p4Scis5TJJ3M3L0Agzjw0E9dZek96yFAPItM3aunwtDEQGTRjA5ERkJlFNV6RG.r53PTI7tzndJQHvqmI9ALGgWeehJKcRm2jLA3OxDC0AJdDSMLcd00fiUdtbrqJDuGtES41MeLVHGLBdp+MvXRl4Z385zg5pl.vkw0e4hlFV9ZmFtZRZHbl5C+1ICsq+xJ4l.9S3Ij8PIMjHosEs7ICWOhDD5SODv3F1m3Kb64v9d5rYHgwPXGsFq61kv4T+nEIQp3MTwIzgh9JF+zlDkjcNT9b+9ANPYZWZsDzAxrJnyghGWVOVyKbnbOyf+F9jLYE8XqjIqLZxToe6SUmIj8LgijuirVI12GYbxG247Jns88EmUSDDxRXoPLvHqkveXXWAm4pEEqwHjtcfnObHIv8KIQsILeMs1oeDTnv6.tCnr4ZLqBGQkQlMdEb4Mg+.Z99BOXph0ItficXKhpqNeQW6AHmT4ltiIoYvZZdhdKFcMWrWBfXf1z0WxoiOsHR3NOnlstbSAqkRU9IlgpGA4eEhOmFQGom0k3iNRy.zKBoOkXOnAwXspqg6vN+QO7XC2CkwF9fT1PZkt5.+0ClERAy6iiBDBUWp2wgi7DYQ6GaPKP82LGMuwfbQcwQ5HvtF94f2gHsODxhQnIUGaSjmRUQIWWvotwjQiE0vSuMJBuKZbPUKaEb7thREp0vYrQXsRSFO4jmTlsI47ojcwVNJZntNRZ8b5QOK1gkVSsMM5fmYq2tuRD.0IL03Qo.Rwo.xu9pW8aaMCPp9KUmBHF8tbfbWriIzAkUL8ANEf9lpyCCWCmwEyACkv6wIPEPOzLaDJEe7tXw.H.YJdW4ImmkJ9QoRbxp1apTmzY3IbsLH7VXHt1cSyDWCLUHMlxxSslOO8hYoGnpUuQ3oWr0kvSmFiWNfKFu86JYCnna9jrwK8a8d5ecvO+maktpvkWjH2Zd4WxXGgnW.wb+3qcmp2Idxagc.Br4oel6tdeyX6ulLfZ+LJPs0MHVYNu96Otpu9K7J+5uCbUvw2VR3QghnrddZ.qsfqswIBeQD7XG52cntlbZ40DDYtScwV0AiL203PT8klh6i5WHUqtKksqpkuZcUksq+T5mSSnK+V34o2LOc3s3qaxz5doQXzgoKGuGe.0GRLLX78fLgNj99pQRyRraJ3hQcONIpeHEZ88zSoYKTkmAssRQfFmFK49UOj5SIoYveR03VILsWrX9hq+ulPtwqODGCWac1n8+ceU5R+u9UouQd+0ahyHf3JEGmz6tlm9NFIfcyM+HekvM0isqfFDeMJV+vKT.Tg6XWWsK9SgBl4ulGt.q4QKvZd7BrlOaAVySVf074KvZ9h4tF8kKIsboS4.As1yzxgk0ndmAlN5eLgWzyA
This is a simple overdrive going into a 16x oversample node. With the new
SuspendOnSilence
flag enabled, the CPU usage goes down to literally 0% if there is no signal present. You can try to disable the flag to check the effect (it's in the DspNetwork properties, next to theAllowCompilation
andHasTail
properties. The flag will be passed to the node compilation, so the HardcodedFX will inherit the behaviour of the network.Be aware that this flag should not be used if your algorithm produces any sound from silence (eg. a test tone generator or a background noise player), because it will obviously mute the output when the input is silent.
You can also see the suspend state in the Module Tree, if a FX is suspended, it will show a S over the peak meters like this:
I've tested it rather thorougly and tried to cater in all use cases (polyphonic FX will also be suspended if the voice is not producing any sound), but please let me know if you find any issues.
There's also a preprocessor macro in the
hi_dsp
module (HISE_SUSPENSION_TAIL_MS
) which defines the time that the processing stays active after the first silence detection (the silence detection is pretty sensitive, so it looks for a -90dB threshhold), if you prefer a different default value, let me know.In my project it yields a ~40% CPU improvement in idle mode and ~10% CPU improvement when in usage (because some channels of the plugin are not always busy). I also improved the performance of reading the peak values of routing matrixes, so this might also be a contributing factor though...
-
@Christoph-Hart Nice one! I have to test this…
-
@ustk very much welcomed!
-
@Christoph-Hart noice
-
@Christoph-Hart That's great!
-
@Christoph-Hart this is good for both instruments and FX plug-ins?
-
@DanH Yes, it works in both targets.
-
@Christoph-Hart Great, now I want to recompile all my projects and make benchmarks ... Nice improvement!
-
I'm out of my element here (and thus the questions). Will use "gate" for shorthand.
-
Why wouldn't the gate close only on absolute silence? (So many questions here. Does real-time dithering in DAW necessarily provide dither noise in audio sent to plugins? Is there even such thing as "absolute silence" in the audio flowing to a plugin? If there is, must this be computed (e.g., based on sample rate and bit depth), and can it be?)
-
How long before the gate closes?
-
How long after the threshold is met before the gate opens?
Thank you.
-
-
@clevername27 the „gate“ opens immediately and closes with a „hold time“ of 500 ms.
Dithering is supposed to improve the sound quality of quiet signals (ballpark -40 - -50 dB) - the noise itself doesn‘t matter and can be cut away without issues. Also it won‘t cut the sound but just not process it, so even if you‘re really sensible about the lower dynamic end this won‘t have any real world sound impact.
-
@Christoph-Hart so so so useful. I’ve made a script to bypass unused modules in the past for that but this is the real solution. I love it
-
@Christoph-Hart This is fantastic update! God bless HISE.
Nice that the new feature "gate" is working with RNBO well! -
@Christoph-Hart I found 2 bugs regarding this new feature
At first, I am not using Scriptnode
I am only using theprocessBlock
CB to perform my recordings and other stuff that are waiting for a flag to start, but since it is now suspended (S
), evidently nothing happens.So I created an embedded DSP and disabled the
SuspendOnSilence
flag of the main DSP container -> theS
disappeared successfully.
But still, theprocessBlock
CB isn't firing (a simple console print shows nothing)Going back to the node to double check -> the
SuspendOnSilence
button disappearedRestart Hise and project:
S
isn't showingSuspendOnSilence
button doesn't reappearprocessBlock
CB still doesn't fire by itself
Now I need to sleep, the rooster is waking up already... Need a SuspendRoosterOnSilence
-
@ustk you have a rooster?!
-
@DanH not compatible with my cats, but my neighbor has many, with hens, geese... Joys of a 30 souls village :)
-
@Christoph-Hart So apparently
processBlock
is bypassed when a network is present, which I think is normal behaviour.
But then, without a network, there's no possibility to set theSuspendOnSilence
flag which makesprocessBlock
to be always gated when no signal is present. -
I changed it so that it doesn't suspend the script processors by default. I could add a scripting method that controls this behaviour, but using script processors without a DspNetwork is so 2016 that I don't want to spend too much time with this :)
-
@Christoph-Hart Mmm great! Thanks!
Yeah, that's old school but it's the only way (that I think, since you helped me a while back on that matter) to record a buffer from an input stream. -
@ustk said in New Feature: SuspendOnSilence:
@Christoph-Hart Mmm great! Thanks!
Yeah, that's old school but it's the only way (that I think, since you helped me a while back on that matter) to record a buffer from an input stream.So your using the process block to record incoming audio? Is that right? If so: "How are you doing that!!"
-
@Lindon Basically, you create a buffer and just copy the incoming stream into it. Since processBlock is continually firing, just set a flag (here the recordIndex is used as a flag) to tell when you want it to record the buffer.
reg recBuffer = Buffer.create(yourLength); reg recordIndex = -1; function processBlock(channels) { if (recordIndex != -1) { local numSamples = Math.min(recBuffer.length - recordIndex, channels[0].length); local temp = Buffer.referTo(recBuffer, recordIndex, numSamples); if (numSamples == channels[0].length) { channels[0] >> temp; } else { local s = Buffer.referTo(channels[0], 0, numSamples); s >> temp; } recordIndex += numSamples; if (recordIndex >= recBuffer.length) recordIndex = -1; } }
A simpler version to understand would simply be to copy the samples one by one in a for loop, but it's a bit greedy on cpu cycles. So this version Christoph gave me is much lighter since the copy process is made per block.
Bear in mind that you can't test this in Hise standalone because the inputs aren't activated.
Though I don't use the Hise plugin version for this, it should work (and the same for the exported project of course, it can't work in standalone unless activating the inputs)