Skip to content

Commit

Permalink
Move widget modulation warning in -wall model.
Browse files Browse the repository at this point in the history
  • Loading branch information
sletz committed Nov 3, 2023
1 parent 6fe6699 commit a47d732
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 36 deletions.
31 changes: 0 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,3 @@
# EXPERIMENTAL WIDGET MODULATION CONSTRUCT

Faust is a very modular language, and it's easy to reuse any Faust program as a component of another, thanks to the `component()` construct. For example: `component("freeverb.dsp")` lets you integrate a fully functional reverb, with its user interface, inside another program.

But how to control the parameters of this reverb other than by hand via the user interface? How to get the equivalent of a "voltage control"? How to modulate one of the parameters with an LFO, for example? Well, if the reverb designer didn't anticipate this from the beginning, it's simply impossible!

It is a lack in the Faust language that limits the reusability of existing code. A technique to fill this gap is to write the code in such a way as to separate the audio algorithm itself from its user interface. This approach makes it easy to reuse the audio algorithm in another context or with another user interface. It is a good practice, but it's not very convenient.

The new language construct presented here avoids just this. It can be used on an existing component, for example, to modulate a slider, replace one slider with another, replace a slider with a constant, etc., without modifying the component's source code!

Its simplest form is the following:

`["Wet" -> freeverb]`

It adds an input to the freeverb, that is used to modulate the "Wet" slider. An LFO, for example, can be connected to this input:

`lfo(10, 0.5), _, _ : ["Wet" -> freeverb]`

The "Wet" label designates the slider to modulate. Of course, this presupposes knowing the names of the sliders. But these names appear on the user interface, so it's easy enough. If several widgets have the same name, adding the names of the surrounding groups, for example: `"h:group/h:subgroup/label"` can help distinguish them.

Multiple sliders can be indicated as in: `["Wet", "Damp", "RoomSize" -> freeverb]`. In this case, three new inputs are added.

We haven't said how sliders are modulated. By default, when nothing is specified, the modulation is a multiplication. The previous example is equivalent to the explicit form `["Wet":*, "Damp":*, "RoomSize":* -> freeverb]`. Please note that the `:` sign used here is just a visual separator, it is not the sequential composition operator.

The multiplication can be replaced by any other circuit with at most two inputs and exactly one output. For example, one could write `["Wet", "Damp", "RoomSize":+ -> freeverb]` to indicate that the `"RoomSize"` parameter is modulated by the addition of an offset signal.

Again, the only constraint on the modulation circuit is that it must have only one output and at most two inputs. We can therefore have 0->1, `1->1`, or `2->1` circuits. Only `2->1` circuits create additional inputs. Moreover, `0->1` circuits lead to the elimination of the slider.

We can therefore rewrite `lfo(10, 0.5), _, _ : ["Wet" -> freeverb]` as follows: `["Wet":*(lfo(10, 0.5)) -> freeverb]`. The latter form does not lead to the creation of additional input, as the LFO is placed inside the reverb. The form `["Wet":0.75 -> freeverb]` results in the deletion of the "Wet" slider, replaced by the constant 0.75. Finally, the form `["Wet":+(hslider("More Wet", 0, 0, 1, 0.1)) -> freeverb]` adds a second slider to the freeverb interface.


# Faust - Programming Language for Audio Applications and Plugins

## Grame, Centre National de Creation Musicale: <https://www.grame.fr>
Expand Down
12 changes: 7 additions & 5 deletions compiler/evaluate/eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ static Tree eval(Tree exp, Tree visited, Tree localValEnv)
Tree id;
Tree result;

// std::cerr << "eval : " << boxpp(exp) << " in env " << envpp(localValEnv) << std::endl;
// cerr << "eval : " << boxpp(exp) << " in env " << envpp(localValEnv) << endl;

if (!getEvalProperty(exp, localValEnv, result)) {
gGlobal->gLoopDetector.detect(cons(exp, localValEnv));
Expand Down Expand Up @@ -338,7 +338,7 @@ static Tree realeval(Tree exp, Tree visited, Tree localValEnv)

xtended* xt = (xtended*)getUserData(exp);

// std::cerr << "realeval : " << boxpp(exp) << " in env " << envpp(localValEnv) << std::endl;
// cerr << "realeval : " << boxpp(exp) << " in env " << envpp(localValEnv) << endl;

// Constants
//-----------
Expand Down Expand Up @@ -697,7 +697,9 @@ static Tree realeval(Tree exp, Tree visited, Tree localValEnv)
Tree mbody = bm.self(ebody);

if (mbody == ebody) {
std::cerr << "Warning, no modulation of: " << *elabel << " took place in: " << boxpp(ebody) << std::endl;
stringstream error;
error << "WARNING : no modulation of: '" << *elabel << "' took place in: " << boxpp(ebody) << endl;
gWarningMessages.push_back(error.str());
}

// if we have a slot, we need to wrap the modulation body in a symbolic box
Expand Down Expand Up @@ -868,7 +870,7 @@ static void writeIdentValue(string& dst, const string& format, const string& ide
*/
static string evalLabel(const char* src, Tree visited, Tree localValEnv)
{
// std::cerr << "Eval Label : " << src;
// cerr << "Eval Label : " << src;
int state = 0; // current state
string dst; // label once evaluated
string ident; // current identifier
Expand Down Expand Up @@ -935,7 +937,7 @@ static string evalLabel(const char* src, Tree visited, Tree localValEnv)
}

} else {
cerr << "ASSERT : evallabel, undefined state : " << state << std::endl;
cerr << "ASSERT : evallabel, undefined state : " << state << endl;
faustassert(false);
}
}
Expand Down

0 comments on commit a47d732

Please sign in to comment.