template or tutorial for custom c++ scriptnode?
-
@Christoph-Hart i can't even get round() to work in SNEX ;)
-
@HISEnberg ah yes, that's the one we've been talking about in the hangout, I couldn#t find it anymore for some reason. thanks
-
@HISEnberg I did it, based on @griffinboy's template, yeeha!
This has a simple 8-bit bitcrusher and the right left gain from the demo like so:
void process(float* samples, int numSamples) { for (int i = 0; i < numSamples; ++i) { // Reduce bit depth float max = 255; float val = samples[i]; float rem = fmodf(val,1/max); // Quantize samples[i] = val - rem; // Apply Demo Gain samples[i] *= smoothGain.advance(); } }
now I have to figure out how to make it μ-law instead of linear 8 bit.
I'm afraid the code won't be that simple. If anyone has tried this before I'd appreciate any input. -
@Morphoice Nice you move fast!
-
@HISEnberg not fast enough. 90% is trial and error and me not understanding a thing I do lol
-
@Morphoice said in template or tutorial for custom c++ scriptnode?:
not fast enough
You're plenty fast!
-
@HISEnberg @d-healey actually tried implementing the μ-law compression thing from what I could learn around the web, but it's failing to compile and I cant get a debug version setup on mac as @griffinboy's tutorial only covers PC so far ;) So now I'm stuck lol. But yeah, the problem should be minor and some day I'll find out ;)
float val = samples[i]; // mu-law compress signal float max = 255; float valmu = sgn(val) * log(1+max*abs(val)) / log(1+max); // Quantize to 8 bits float valmu8 = valmu - fmodf(valmu,1/max); // mu-law expand signal float valex = sgn(valmu8) * (1/max) * (pow(max+1,abs(valmu8))-1); // Apply Demo Gain samples[i] = valex * smoothGain.advance();
I basically know too litte about c++ to know if the syntax in these formulas is even valid but I tried ;)
-
@Morphoice said in template or tutorial for custom c++ scriptnode?:
but I tried
Some of us try for years, don't give up after 2 hours :)
-
@d-healey thanks to your encouraging words it just came to me that actually I just needed to implement the sgn (sign) function as C++ doesn't seem to have one by default
template <typename T> int sgn(T val) { return (T(0) < val) - (val < T(0)); }
(yeah that one I copied off the internet lol)
and then it suddenly worked. I can't believe it! I have just made a μ-law bitcrusher without even so much as knowing what I do lol. God this thing sounds fly. Massively less artifacts on quieter sounds and sizzle on loud ones!
-
@Morphoice use
hmath::sign()
:) -
@Christoph-Hart that would have been to easy ;))
In all honesty, I seriously need to learn C++, the things one can do... -
@Morphoice everything should work in your code once you:
- use the real math functions as Chris suggested
- respect the variable types. SNEX is very picky about this which is a good thing, but the errors messages are sometimes not helpful (you get the cast issue at the right line for expressions, but not in variable definitions). In your case, when a var type is
float
, EVERYTHING needs to be float in the expression. (1.0f
)
Another suggestion in SNEX, is that you should try to compile for each single line you add to your code, this is especially true at beginning. Because debugging multiple lines you added will be a nightmare as the console writes everything on the same line (remember the Hise Hang )
This reminds me to have a look at least to fix this issue...
So write a new line -> compile, and if it fails you know where you are -
@ustk aye! I did this in C++ not SNEX though. But I agree it looks more correct this way, although it didn't throw a tantrum without. C++ seems to be a bit forgiving there, which is not at all what I heard about it lol
float val = samples[i]; int max = 255; float valmu = sgn(val) * log( 1.0f + max * abs(val) ) / log( 1.0f + max); float valmu8 = valmu - fmodf(valmu, 1.0f / max); float valex = sgn(valmu8) * (1.0f / max) * ( pow(max + 1.0f, abs(valmu8)) - 1.0f );
-
@Christoph-Hart is there a way to get the custom c++ scriptnode to only process data if there is any sound? as this is intended for one-shots but constantly does the encoding/decoding and a couple of those nodes produce quite a fair amount of cpu
-
@Morphoice I can think of two ways:
- either build a detector that is waiting for a certain value to pass a threshold, then if it does, do your stuff
- tell the node a signal is coming with a modulated parameter, which will move the detection part to the graph
-
@ustk are all nodes generally on? I've read about a hasTail parameter that stops processing when the note is done etc. probably in my case it needs to work only a few ms after a note was fired
-
@Morphoice I reckon hasTail is for the case where DAWs cut the plugin processing between the clips to save CPU on big projects. hastTail allows your plugin to still read the buffers until a minimum threshold has been reached before effectively cutting the sound (i.e. for reverbs, etc...)
If your node is optimised, you shouldn't need to bypass it. If the heavy processing is done then you can just do nothing in the process callback to save CPU using just one branching (if statement)
I don't know how your system is built, but if your sound is triggered with MIDI you can use midi events in C++ node with
handleHiseEvent
, but if it's just reacting to audio then... -
@ustk don't VST3 plugins in general only waste cpu when they detect the presence of an audio signal?
-
@Morphoice might be true but you can‘t rely on every host to implement this, also there are other plugin formats which you need to factor in.
-
In the meantime, written in Faust: https://faustlibraries.grame.fr/libs/basics/#bamulaw_bitcrusher
and the code here https://github.com/grame-cncm/faustlibraries/blob/a222717339e5f528787080e5e5438ffef8da22a9/basics.lib#L2429 and https://github.com/grame-cncm/faustlibraries/blob/a222717339e5f528787080e5e5438ffef8da22a9/basics.lib#L2371