Closed FLamparski closed 2 years ago
Not sure I fully understand the problem but could it possibly be solved using Variable Parts of a Label ?
Otherwise you can all connect to Faust Slack or Discord for easier interactions: https://faust.grame.fr/community/help/
My confusion is that I defined a variable in the global scope but when using groups, it seems to be pushed down to the local scope within a group, which is not what I would expect from just reading the code. Is this behaviour intentional? If so, is it documented properly?
Which variable? Have a look at with Expression
If you look at the screenshot you'll notice that in the first example, each partial ends up with its own sliders for 'freq' and 'q' and its own button for 'gate' despite all 3 of these variables being declared globally. When grouping is removed from the tonalizer
function, there are only 3 controls for freq, gain, and q.
I am not sure how a with
expression will help with that.
UI items are shared if they have the same path in the UI hierarchy. Try that:
declare options "[midi:on][nvoices:8]";
import("stdfaust.lib");
// MIDI hookup: freq is based on note played, gain is velocity, gate is whether the note is on
freq = vslider("freq",200,50,1000,0.01);
gain = vslider("gain",0.5,0,1,0.01);
gate = button("gate");
q = vslider("q", 20, 2, 40, 1);
qcomp = 0.5 - 0.025 * q; // Decrease levels at high Q settings
band(id, freq, q, basegain) = _ : fi.resonbp(freq, q, 0.5) * basegain * gate : _;
tonalizer(freq, q) = _ <:
hgroup("foo", band(0, freq, q, vslider("partial gain 0", 0.5, 0.0, 1.0, 0.01))),
hgroup("foo", band(1, freq * 2, q, vslider("partial gain 1", 1, 0.0, 1.0, 0.01)))
:> /(4);
process = no.noise : tonalizer(freq, q) * gain <: _,_;
Bringing the bands under the same group also works but that's the same as not having the groups at all.
After some fiddling with the groupings, I get a UI closer to my design goals but that's not quite there (and the parameter count agrees):
What I'm actually looking for (implementation in iPlug2 with a version of the DSP with no groups whatsoever as I'm defining the UI in C++):
This is also what I'd expect from just reading the code since I am passing q
from process
to tonalizer
which passes it to each invocation of band
. It makes me wonder whether I can in fact think of eg. q
as a global variable or whether it's more appropriate to think of it as a function that returns the current value of the slider - as in, whether its value is evaluated within process
or whether it's evaluated within the implementation of fi.resonbp
. Coming from a background in more imperative languages I'm leaning towards the "q
is a variable" interpretation. Still, it is kinda surprising to me that a feature documented as merely organising the UI would actually create new lexical scopes that would lead to this sort of thing. If this is the intended behaviour of groups, I think it should be more obviously documented as such.
Can you paste this latest version of the DSP?
Possibly something like:
tonalizer1(freq, q) = _ <: hgroup("Bands", par(i, 4, band(0, freq*(i+1), q, vslider("partial gain %i", 0.5, 0.0, 1.0, 0.01)))) :> /(4);
process = no.noise : hgroup("Master", tonalizer1(freq, q) * gain ) <: _,_;
Here's the reduced version of the example above (keeping it simple with just 2 bands and no envelopes):
declare options "[midi:on][nvoices:8]";
import("stdfaust.lib");
// MIDI hookup: freq is based on note played, gain is velocity, gate is whether the note is on
freq = hslider("freq[hidden:1]",200,50,1000,0.01);
gain = hslider("gain[hidden:1]",0.5,0,1,0.01);
gate = button("gate[hidden:1]");
q = hslider("q", 20, 2, 40, 1);
qcomp = 0.5 - 0.025 * q; // Decrease levels at high Q settings
band(id, freq, q, basegain) = _ : fi.resonbp(freq, q, 1) * basegain * gate * qcomp : _;
tonalizer(freq, q) = _ <:
vgroup("[1]F", band(1, freq, q, vslider("[0]partial gain", 1, 0.0, 1.0, 0.01))),
vgroup("[2]2", band(2, freq * 2, q, vslider("[0]partial gain", 0.25, 0.0, 1.0, 0.01)))
:> /(1);
process = no.noise : hgroup("", tonalizer(freq, q)) * gain <: _, _;
This still shows duplicated q
and freq
controls for each of the bands, whereas I would expect them to be global regardless of having the groups or not - since the groups are in fact useful for arranging the UI to more closely match what I want.
Is that better:
declare options "[midi:on][nvoices:8]";
import("stdfaust.lib");
// MIDI hookup: freq is based on note played, gain is velocity, gate is whether the note is on
freq = vslider("freq[hidden:1]",200,50,1000,0.01);
gain = vslider("gain[hidden:1]",0.5,0,1,0.01);
gate = button("gate[hidden:1]");
q = vslider("q", 20, 2, 40, 1);
qcomp = 0.5 - 0.025 * q; // Decrease levels at high Q settings
band(id, freq, q, basegain) = _ : fi.resonbp(freq, q, 1) * basegain * gate * qcomp : _;
tonalizer(freq, q) = _ <: hgroup("Bands", par(i, 4, band(0, freq*(i+1), q, vslider("Partial gain %i", 1/(i+1), 0.0, 1.0, 0.01)))) :> /(4);
process = no.noise : hgroup("Master", tonalizer(freq, q) * gain) <: _,_;
@FLamparski can we close this one?
@sletz Yep, this looks fine to me. Thank you!
I have this DSP (stripped down version of an instrument I'm working on) - this example extracts two partials from white noise. Note that all code examples were tested in the online IDE on the day of the ticket being opened.
It produces the following DSP UI:
I can also confirm by ear that 'freq', 'gate', and 'q' controls are independent for each partial within the DSP UI. Note that 'freq' and 'gate' are only defined in the global scope, and 'q' is passed down as a parameter to each function called from within 'process'. The example is using
hgroup
for screenshot clarity butvgroup
also has this behaviour. I'm also now very confused about howqcomp
would be calculated with the grouping bug in effect - would it effectively be undefined behaviour, or would it be computed per partial?When I remove the
hgroup
from each band within thetonalizer
function, like below, the UI is now more consistent with how I would expect it to work:This is unexpected because the
vgroup
andhgroup
documentation states that "[it's] is not a signal processor per se and is just a way to label/delimitate part of a Faust code."The issue is also not limited to the Faust IDE UI, as trying to embed this Faust code in an iPlug2 project results in a mismatch between the number of actual DSP parameters (zones) and the parameters exposed to the C++ code (params) as the duplicate params are named the same. The workaround for that will be to remove all vgroups/hgroups from the version of the DSP code that gets put into the plugin - after all I will be providing my own UI so it's not critical that Faust does it for me - but it would be nice to not have to do that, and for use cases where you do want the generated UI, this could be a blocker for some.
The silver lining of this bug is that I will definitely be trying per-partial Q controls in my plugin as it can sound nicer, but I was thinking about doing that at some point anyway...