Home to Foleys Finest Audio

Faust Architecture File for Plugin Gui Magic

It would be really great if someone could implement a Faust Architecture File https://faustdoc.grame.fr/manual/architectures/
For Plugin Gui Magic. It could be modifications of the existing Juce architecture files and would provide a GREAT GUI option (as the existing Juce VST GUI works but is not that good). I have looked at this and can work on it but would need some direction/assistance. Is anybody up for this. It would provide some “instant” gratification for the very powerful Faust DSP language and a way to use Plugin Gui Magic to many plugin variations easily.

Not sure if you saw my other post but indeed I am working on this. One thing to note is that at this point in time, the support is not “seamless” as Faust does some things in sort of funny ways and to the extent I am able to figure out, PGM is not expecting some of the things that Faust does do and so ignores them or interprets them in a strange way. For example,
a) Faust bargraphs come over as sliders and don’t mesh up with PGM’s level meters. At the moment it seems sliders work well and I’m not so sure about anything else. Bargraphs, no, although if you can handle a slider that goes up and down on its own, you might be able to live with it.
b) Right now I am working on trying to make a way for slider [style:menu] to create hooks for a PGM ComboBox. This looks like it wants to make some changes in the way PGM works.

Unfortunately these have resulted in me going into a deep dive on both Faust and PGM, not to mention C++. I can sort of find my way though this stuff intuitively but I’m not an expert in any of this (yet). I mean, it’s fortunate and unfortunate, depending.

FYI I have forked Faust from master-dev to my own repository. I can work on making changes to the way Faust works, then when I get something working I push it to master-dev on mine. Then I do a pull request from that the the Grame Faust master-dev respository. Some of the changes have already made it into Faust master-dev, while others are still waiting in that PR. I haven’t pushed my WIP on ComboBoxes because I don’t have anything working yet. All this as a way of saying, this is not in the main Faust release yet so you’ll need to get the code from my forked repo and build it if you want to try it.

With the changes I’ve made you can #undef PLUGIN_MAGIC and it builds as straight Juce again.

Philosophically, PGM opens up a lot of questions. Its intent is really to separate the GUI from the data. It also is intended as a platform for developing custom (bespoke) components. Faust itself can’t do some of the things that PGM does (e.g. filter graphs, FFTs), so you’re left with the option of still having to add those things in C++. Once you learn where they live, it’s not difficult, but it does make managing it all a little precarious since you’re quite likely to overwrite some manual changes by a poorly timed automatic step. Ah but THAT’s why they call it work! Or, another possibility would be to expand PGM’s features into Faust. For example, let’s make Faust “Menus” real combo boxes instead of some odd variation on a slider? Let’s add a “plot” object (somehow) to Faust? But then you’re left with things that only would work in the PGM world.

Also, what someone else wants to accomplish with PGM and Faust might differ from what I want to accomplish. So at the very moment I’m pursuing getting a slider [style:menu] to work just as a regular old ComboBox, because then I’ll be able to complete a couple projects.

It’s pretty clear that you will work in all of these places if you want to pursue this route:
a) Faust code
b) Faust architecture and faust2juce file
c) Faust-generated C++
d) PGM library C++ to figure out what it’s really doing with the ValueTrees etc. and maybe even change the way some things work.

An alternative I could have pursued would have been to develop Faust components as a custom set of PGM components rather than trying to wedge something in to the current ones. I’m not at the point of comfort of doing that yet. My intuitive explorations have exposed the path I’m currently mining but it may turn out it’s not the best solution.

Anyway please join the fun. I have it partially working and I’d appreciate some help with (at least some of) the rest.

Thanks for all your work on this. I will take a look and see if I can help out. The Faust Architecture files are quite beastly, but are generating code for quite a few targets so it does seem that a PGM target could be developed. If you have any specific things you would like me to look at / assist with, let me know.

I’m fairly confident that what I’ve done so far in fact accomplishes what you asked for. It sets up basic hooks for you to more easily add PGM features. Like I said, there are some gaps. So if you like, check the Faust code out from my repo, build it, and try it. In order for me to ask you to help, you’re going to at least need to do that. So let’s start there.

I usually have at least one day a week I can work on projects like this. I will work on this tomorrow afternoon and Friday. By the way, I posted a Faust VST project to the KVR contest KVR Contest Link and want to improve it. That is why PGM peaks my interest.

1 Like

Oh yeah, I saw that. Well since your project is all sliders I think the limitations won’t slow you down for now. By the way it looks like Stephane merged my PR into master-dev at Faust, so you might as well take it from there.

Finally started using PGM with Faust. The integration into the standard 2.3.3.1 version is nice.
Another week or so should give me a bit of experience with it.

Thanks for the Great Work!

Thanks for checking it out! There are some limitations currently but I am chipping away at them.

Sorry @Digital_Larry for not been very responsive (on Slack) right now. I have no time to go deep inside PGM and your issues right now, but fully support a proper PGM architecture integration in Faust. Keep up the good work, now the help of @skraninger !

1 Like

@sletz if nothing else this opportunity has taken me into some far reaches of the code, where months ago I could only imagine going (cough).

The issues are localized in the interface code that I wrote to bridge the gap between Faust Sliders which are based on juce::AudioParameterFloat and PGM ComboBoxes which use juce::AudioParameterChoice. It partially works (the ComboBox entries are properly loaded) but somehow the scaling is off, so that the DSP doesn’t work.

I have traced the following places in both Juce and PGM versions of the same Faust generated and hand modified C++ code.

  • uiTypedItemReal constructor (where fCache value is inititalized)
  • modifyZone
  • reflectZone
  • setValue
  • juce::ComboBox::valueChanged

Here’s what I notice.

  1. In PGM, “reflectZone” is never called. This appears to be conditional upon detecting a change from the cached value (in updateZone()). So I am wondering if something has already updated the cached value by the time that comparison is made.
  2. In PGM, selecting the “Sawtooth” option only triggers the ComboBox::valueChanged breakpoint. In this case, the ComboBox itself returns 0.5 and the scaling function converts it to 2. I don’t know why for this particular case the other functions are not called. Also, the DSP does not change from whatever it was previously doing. I am guessing that for whatever reason 0.5 is not considered a valid value but I am not getting any exceptions or assert failures.
  3. The selection chosen at startup is 3 for Juce, 2 for PGM. I don’t know if this is a unique symptom or side effect of the other issues. This is currently the least of my worries.

There are other differences, such as the number of times certain calls are made, and the order of calls. I don’t know whether some of these are just normal differences in the way PGM does things vs. possibly root cause of the issues.

====================================
So that is the summary of where the project lies currently.

Digging a little deeper on my suspicion in point #1 above.

Here’s what happens when I change the selection in PGM.

  1. ComboBox::valueChanged (1)

  2. this may be the point where the cache value is getting updated.
  3. FaustPlugInAudioParameterChoice::setValue (0)
  4. UiItem::modifyZone (1)

and then it runs. reflectZone is never hit.

In the Juce build, I also have a breakpoint on ComboBoxParameterAttachment::comboBoxChanged() and it is never hit.

While I could change the code in step #2 to do nothing, that is down in the Juce classes and probably not a really great idea. Maybe I need to try to disable this listener somehow to keep it from doing anything. Or maybe I need to handle it differently so that it doesn’t break.

Here’s the sequence in the Juce build:

  1. ComboBox::valueChanged (1)
  2. UiItem::modifyZone (1)
  3. FaustPlugInAudioParameterFloat::reflectZone (1)
  4. FaustPlugInAudioParameterFloat::setValue (0)

Working on integrating PGM with Faust as well.

The issue I am trying to fix is:

  1. The PGM plugin editor works the 1st time and does update the DSP with correct values.
  2. Close the PGM plugin editor and open the generic plugin window (that just shows the VST3 values) and the correct values are shown. The generic plugin window can update the values.
  3. Re-opening the PGM plugin editor shows the values as the default value, but the DSP is still using the modified value.

Have spent a lot of time on this with not very much progress, but have identified one area that seems to be causing the issue.

The PGM controls seem to be responding to the AudioParameterFloat value as returned from the processor (as part of the FaustPluginAudioParameterFloat struct). However, the value is never getting updated. The value that is updated is in the uiTypedItemReal.fZone (element in fGUI.fZoneMap).

The setValue() method is overridden in the FaustPluginAudioParameterFloat, so the method in the base AudioParameterFloat are not being called, and it can’t be called from FaustPluginAudioParameterFloat because the method is private. The modifyZone method that is called by the overriding setValue() is updating the fZone associated with the parameter. This value is the one used by the DSP, but never read by PGM when re-loading the editor.

Running tests with PGM and the direct use of AudioParameterFloat show that the AudioParameterFloat value is updated when ever the PGM editor changes. Reloading the editor shows the correct value based upon the AudioParameterFloat value.

Still searching for a solution to this. One suggestion from a JUCE forum was to inherit from AudioProcessorParameter and implement the functions needed for AudioParameterFloat within FaustPluginAudioParameterFloat. This would allow setting “value”.

Digital Larry, don’t know if you are still working on this but one change that may help your issue is in the following code (essentially setting the fCache after updateZone. It seemed like nothing was getting updated in updateZone, because the fCache and *fZone matched (fZoneMap[z] = c>cache()) matched so c->reflectZone() was never called

CODE:

void modifyZone(FAUSTFLOAT v)
{
// SPK CHANGE: Put after fGUI->updateZone
// It appears that updateZone ends up call ReflectZone that updates the fCache as well?
//fCache = v;
if (*fZone != v) {
*fZone = v;
fGUI->updateZone(fZone);
fCache = v;
}
}

void updateZone(FAUSTFLOAT* z)
{
FAUSTFLOAT v = z;
clist
cl = fZoneMap[z];
for (const auto& c : *cl) {
FAUSTFLOAT cmp = c->cache();
if (cmp != v)
{
c->reflectZone();
}
}
}

Just thought I would provide some information and maybe get some information.
Thanks for all of your work Digital Larry

Created a “copy” of AudioParameterFloat called FaustAudioParamterFloat (put it in FaustPluginProcessor.cpp) with one change. The setValue() function is public not private.
Then in FaustPluginAudioParameterFloat, inherited from FaustAudioParameterFloat instead of AudioParameterFloat.

Then in FaustPlugingAudioParameterFloat setValue becomes:

virtual void setValue(float newValue) override
{
    modifyZone(FAUSTFLOAT(range.convertFrom0to1(newValue)));

    // Needs to update Value in RangedParameter
    FaustAudioParameterFloat::setValue(newValue);
} 

Hooray !!! This fixed the PGM editor to bring back the parameter when re-opened.

This could probably be improved by putting more into FaustAudioParameterFloat, but seems to be working as is.

If anyone wants the code for FaustAudioParameterFloat I can send it to you.

All of the other FaustPluginAudioParameter…s should probably be changed in the same way.

Glad you made some progress on this. I would suggest a pull request to Faust if you are confident it’s a general purpose solution.

I spent a month or two digging into this, however as I already have a full time job for which I GET PAID, I got to where I was not able to move forward without help, and the idea of making a new set of custom components and trying to instantiate everything properly from Faust’s code generator was just too much for me to take. PGM is a nice tool and maybe the way to use it with Faust is to do ALL the UI stuff using normal Juce/PGM methods and just pull in the DSP code and stitch it together.

Once you do that, you have thrown one of the main advantages of using Faust (incredibly fast prototyping) out the window. Why bother?

I got pretty burned out on this and haven’t really done anything more with Faust in awhile.

Thank you @skraninger for digging in so deep.
For my understanding, does the Faust AudioProcessor use RangedAudioProcessorParameter?
PluginGuiMagic does nothing special here, it uses the stock SliderParameterAttachment.

For a moment I thought I might have forgotten to call sendInitialUpdate() when creating the attachment (if you want to try it yourself, it is in
foleys_gui_magic/General/foleys_MagicJUCEFactories.cpp)
But if that is the problem, it should not work anywhere. And so far I didn’t observe that issue.

From your report it sounds there is a general problem with faust<->juce and redundant information?

Let me know if I can help you further

@skraninger let me know if you want to try to collaborate on taking the integration of PGM with Faust further. I am not really in the mood at the moment, but if there was someone to bounce ideas off that might change my attitude. You’d want to start by reading my threads here. Start at the end rather than the beginning because I had many false starts.

Daniel,

This is a bit of a simplification, and really don’t completely understand the Faust code but:

The Faust generated DSP does not currently use any of the JUCE parameters directly.
It uses it’s own array structure to contain the values that it uses for it’s DSP.
The JUCE interface used by Faust does create JUCE controls that are paired with a Faust set of struct objects that are declared like:

struct FaustPlugInAudioParameterFloat : public juce::AudioParameterFloat, public uiOwnedItem {
FaustPlugInAudioParameterFloat(GUI* gui, FAUSTFLOAT* zone, const std::string& path, const std::string& label, FAUSTFLOAT init, FAUSTFLOAT min, FAUSTFLOAT max, FAUSTFLOAT step)
:juce::AudioParameterFloat(path, label, float(min), float(max), float(init)), uiOwnedItem(gui, zone)

One for each of the AudioParameter… types.
The setValue() function is overridden to set the Faust used “array” element used by the Faust DSP.

virtual void setValue(float newValue) override
{
    modifyZone(FAUSTFLOAT(range.convertFrom0to1(newValue)));
} 

The Value in the JUCE parameter object is really not used by Faust (as far as I can tell).
In the original version of the Faust-JUCE code, Value does not get updated at all. It always remains at the default value.
Creating a derivative of the parent … and modifying the setValue() to update both the Faust array and the JUCE Value keeps the two values in sync. The Faust array value is used by the DSP, and the JUCE Value by the GUI.
My modification creates a FaustAudioParamterFloat than inherits from RangedAudioParameter and has a public setValue().
It can be called from the overridden setValue() like:

virtual void setValue(float newValue) override
{
    modifyZone(FAUSTFLOAT(range.convertFrom0to1(newValue)));
    // Needs to update Value in RangedParameter
    FaustAudioParameterFloat::setValue(newValue);
} 

One improvement that I could see, rather than having both the Faust array and JUCE Value, the JUCE Value would some how refer directly to the Faust array element, but it seems like the JUCE Value does a lot of stuff …).

Also:

I am going to try to write a “Preset Save/Recall” component for PGM similar to the one in ChowMatrix (also written using PGM).
It would be really great if you could do a generic one. Definitely much easier for you.

Digital Larry,

I would like to collaborate with you as much as possible.

I also do application programming for my “real job” so this project is my “fun” project and I spend time at it when motivated.
I am also not a C++ expert, but have been programming for a longer time than I care to say.
I plan to continue working on the Faust-JUCE-PGM connection, as I think it could become a great “rapid development” environment.
Slogging through the code to get it to that point will take time.
I will put posts in this discussion when I get stuck or have produced something.
To the extent that you have specific pieces of the code you would want me to contribute to, I will be happy to do what I can
The more specific the issue and description, the better.

What I did to finally figure my issue out was to create the simplest stereo gain processor in Faust, and used that to test the issues.
I could post that project on GITHUB.

My plans are:

  1. Update the “EchoMatrix” with a PGM GUI (I have a version of this working now, still some issues with LOG controls).
  2. Add Preset save/recall functionality to the PGM GUI (This would be a nice addition Daniel … kind of like ChowMatrix?).
  3. Add midi/ableton sync to get beat sync’ed delays.
  4. Add “macro” controls to the PGM GUI, with a way to connect them to other controls (Daniel?).

As to updating the Faust GIT repository, I will try to do a pull from the Faust repository and update Faust2Juce when I get far enough along.

Steve

Copyright © 2020-2021 Foleys Finest Audio UG. All rights reserved.