You’ll have to use the « Recursive Composition » operator (AKA : tilde ~ character) to express computation on a signal and its past state. Look at the syntax documentation here: https://faustdoc.grame.fr/manual/syntax/
Hello-
I am new to faust and for my work I need to implement a slew rate limiter similar to:
~~~
int16_t srlimit_update(int16_t x_raw_input,
int16_t x_prev_limit_out,
int16_t maxchange)
{
// 1. Compute change in input
int32_t delta = (int32_t)x_raw_input - x_prev_limit_out;
// 2. Limit this change to within acceptable limits
if (delta > maxchange)
delta = maxchange;
if (delta < -maxchange)
delta = -maxchange;
// 3. Apply the limited change to
// compute the new adjusted output.
// Use this as the next value
// of x_prev_limit_out
return x_prev_limit_out + (int16_t)delta;
}
~~~
You’ll have to use the « Recursive Composition » operator (AKA : tilde ~
character) to express computation on a signal and its past state. Look at
the syntax documentation here: https://faustdoc.grame.fr/manual/syntax/
Hello-
I am new to faust and for my work I need to implement a slew rate
limiter similar to:
~~~
int16_t srlimit_update(int16_t x_raw_input,
int16_t x_prev_limit_out,
int16_t maxchange)
{
// 1. Compute change in input
int32_t delta = (int32_t)x_raw_input - x_prev_limit_out;
// 2. Limit this change to within acceptable limits
if (delta > maxchange)
delta = maxchange;
if (delta < -maxchange)
delta = -maxchange;
// 3. Apply the limited change to
// compute the new adjusted output.
// Use this as the next value
// of x_prev_limit_out
return x_prev_limit_out + (int16_t)delta;
}
~~~
Thank you Stéphane and Julius for your quick responses-
For some reason I was having some issues getting the results I wanted with the examples you sent and landed on this:
Which doesn't produce what I want either. Perhaps there is something wrong with the algorithm I referencing, or I am missing something. It feels like I need to do more research. But just so you know I am trying to implement an algorithm that will slew on a square wave like the image shown below, with a variable slew rate (to adjust the slope of the slew).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Conceptually, the slew of a square wave is an impulse train (alternating
signs). Merely clipping that impulse train does not widen the transition
interval, and only limits the amplitude. You could introduce an FIR filter
(impulse response constant over the duration of the slew interval) to
convert each impulse into a square pulse, which would integrate to give the
shape you're looking for. However, this is still an approximation to slew
limiting in op-amps and such. For more accuracy, the width of your FIR
filter would need to depend on both the slew limit and the amplitude it is
shooting for. In other words, real slew limiting is a nonlinear filter.
(Nonlinearity is easy to see by considering that the slew-limited slope
does not change as a function of target amplitude.) If you need this
level, then you could make it along the lines of amp_follower and follow
the waveform until the slew limit is reached after which it would approach
the waveform at maximum slope.
Thank you Stéphane and Julius for your quick responses-
For some reason I was having some issues getting the results I wanted with
the examples you sent and landed on this:
~~~
slewRateLim(x,mc) = //x-x':max(-mc,min(mc)):+~x';///
//x-x': max(-mc) : min(mc):+~x';
x - x': max(-0.99,min(0.99)): + ~(-x'*mc);
process = os.square(1) <: slewRateLim(_,0.99);
~~~
Which doesn't produce what I want either. Perhaps there is something wrong
with the algorithm I referencing, or I am missing something. It feels like
I need to do more research. But just so you know I am trying to implement
an algorithm that will slew on a square wave like the image shown below,
with a variable slew rate (to adjust the slope of the slew).
Thank you for the advice Julius-
that sounds more along the lines of what I was thinking - essentially I am trying to recreate the functionality of the rampsmooth~ object from Max, if you're familiar with that? I am going to keep plugging away at this and see what I can achieve. Seems like a good way to get to know Faust. I'll report any discoveries here-
wishing you well-
Mark-David
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thank you again Julius for your previous reponse.
Honestly - I wasn't able to get any results from the above, but I am not sure if I understand how to implement what your describing. Meanwhile, I did some poking around and I found a gen~ versionof the rampsmooth~ in Max that works exactly how I would expect:
There are likely other issues, but where I am feeling most unsure is whether variables are holding values, or if they are reset when the function is reevaluated.
Also, should everything be in the letrec? or is that creating issues as well?
Thank you in advance for any pointers you may have,
Mark-David
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I cannot resist little Faust puzzles like this.
Here is a solution for rising slew limiting:
slew_lim_plus(dxm,x) = x,dxm : (+, : min : (+~))~*(-1); // where dxm =
maximum upgoing slope
To derive this, I followed the logic in the theorem that proves Faust can
encode any block diagram (one of the early papers).
Including falling slew-limiting is left as an exercise.
Possibly useless hint:
Thank you again Julius for your previous reponse.
Honestly - I wasn't able to get any results from the above, but I am not
sure if I understand how to implement what your describing. Meanwhile, I
did some poking around and I found a gen~ versionof the rampsmooth~ in Max
that works exactly how I would expect:
ramp = 200;
process = os.square(10)<: slr(ramp, ramp);
~~~
There are likely other issues, but where I am feeling most unsure is
whether variables are holding values, or if they are reset when the
function is reevaluated.
Also, should everything be in the letrec? or is that creating issues as
well?
Thank you in advance for any pointers you may have,
Mark-David
Thank you again, Julius, for your generous replies.
In the example you gave (slew_lim_plus(dxm,x) = x,dxm : (+, : min : (+~))~(-1); ) I couldn't get it to compile . First I get "syntax error, unexpected SEQ," but when I remove the first ': ' I get "syntax error, unexpected RPAR" - so, empirically I tried removing the recursion '~' on the '+' and removed the variables until it started working until ended up with this:
The results are not what I was expecting from wha tyou were describing, so I am pretty sure I broke it-
Sorry if this is a pain- it feels like I just dove into something pretty deep on the get go, c'est la vie :)
MD
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thank you again, Julius, for your generous replies.
In the example you gave (slew_lim_plus(dxm,x) = x,dxm : (+, : min :
(+~))~(-1); ) I couldn't get it to compile . First I get "syntax error,
unexpected SEQ," but when I remove the first ': ' I get "syntax error,
unexpected RPAR" - so, empirically I tried removing the recursion '~' on
the '+' and removed the variables until it started working until ended up
with this:
~~~
import("stdfaust.lib");
slew_lim_plus(x, dxm)= (+, min : (+))~*(-1); // where dxm = maximum
upgoing slope
process = os.square(0.5)<: slew_lim_plus(_,30);
~~~
The results are not what I was expecting from wha tyou were describing, so
I am pretty sure I broke it-
Sorry if this is a pain- it feels like I just dove into something pretty
deep on the get go, c'est la vie :)
MD
import("stdfaust.lib");
dxm = 0.1; // max upgoing slope
slew_lim_plus(dxm,x) = x,dxm : (+, : min : (+~))~*(-1);
slew_lim_minus(dxm,x) = -slew_lim_plus(dxm,-x);
process = 1 : slew_lim_plus(dxm);
Remember that it only handles upgoing slew-limiting.
To get downgoing as well, I would bring out the output of the subtraction
of the output from the input and use its sign to switch between up and down
using select2.
import("stdfaust.lib");
dxm = 0.1; // max upgoing slope
slew_lim_plus(dxm,x) = x,dxm : (+, : min : (+~))~*(-1);
slew_lim_minus(dxm,x) = -slew_lim_plus(dxm,-x);
process = 1 : slew_lim_plus(dxm);
Remember that it only handles upgoing slew-limiting.
To get downgoing as well, I would bring out the output of the subtraction
of the output from the input and use its sign to switch between up and down
using select2.
Wow, thank you Julius!!
This works!
We are using this in a filter chain that is used in the analysis of biophysical signals captured at audio rate. This was a key componet in going forward. Faust will enable us to share our work on a variety of platforms. (see: http://www.ndstudiolab.com/pacis). Hopefully the slew limiter is useful to others as well.
Kind regards,
Mark-David
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Wow, thank you Julius!!
This works!
We are using this in a filter chain that is used in the analysis of
biophysical signals captured at audio rate. This was a key componet in
going forward. Faust will enable us to share our work on a variety of
platforms. (see: http://www.ndstudiolab.com/pacis). Hopefully the slew
limiter is useful to others as well.
Kind regards,
Mark-David
Next step: use the OFDR, the « Optical Faust Diagram Recognition » tool to automatically write the manually designed diagram into ready to use Faust code… ((-;
Wow, thank you Julius!!
This works!
We are using this in a filter chain that is used in the analysis of
biophysical signals captured at audio rate. This was a key componet in
going forward. Faust will enable us to share our work on a variety of
platforms. (see: http://www.ndstudiolab.com/pacis). Hopefully the slew
limiter is useful to others as well.
Kind regards,
Mark-David
Thanks Julius -
It's great to see that this contributed in some way to FAUST in general. I found the video helpful as well. Things have been progressing nicely on our end. Working with the block diagrams has proven very useful in our efforts of designing and debugging the system.
Wishing you all the best-
Mark-David
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello-
I am new to faust and for my work I need to implement a slew rate limiter similar to:
(from: https://www.embeddedrelated.com/showarticle/646.php)
I have made some meager attempts. for part 2 I was imagining something like this would work:
max(min(delta, maxchange),-maxchange);
but I am having trouble wrapping my head around how to imlement steps 1 and 3 correctly.
Here's my broken solution so far...
Thanks in advance for any pointers you may have-
kind regards,
Mark-David
Last edit: Mark-David Hosale 2020-09-12
Welcome Mark-David !
You’ll have to use the « Recursive Composition » operator (AKA : tilde ~ character) to express computation on a signal and its past state. Look at the syntax documentation here: https://faustdoc.grame.fr/manual/syntax/
So in your example:
limiter(maxchange) = (- : max(-maxchange) : min(maxchange)) ~ _;
R = 0.5;
process = limiter(R);
The generated C++ code for the DSP loop is then:
virtual void compute(int count, FAUSTFLOAT inputs, FAUSTFLOAT outputs) {
FAUSTFLOAT input0 = inputs[0];
FAUSTFLOAT output0 = outputs[0];
for (int i = 0; (i < count); i = (i + 1)) {
fRec0[0] = std::min<float>(0.5f, std::max<float>(-0.5f, (fRec0[1] - float(input0[i]))));
output0[i] = FAUSTFLOAT(fRec0[0]);
fRec0[1] = fRec0[0];
}
}</float></float>
You can look at the SVG block diagram using the Faust Web IDE (diagram tab). Here is a link on the code:
https://faustide.grame.fr/?autorun=1&voices=0&name=limiter&inline=CmxpbWl0ZXIobWF4Y2hhbmdlKSA9ICgtIDogbWF4KC1tYXhjaGFuZ2UpIDogbWluKG1heGNoYW5nZSkpIH4gXzsKUiA9IDAuNTsKCnByb2Nlc3MgPSBsaW1pdGVyKFIpOw%3D%3D
HTH,
Stéphane
Yes, you need to integrate delta after limiting it and before adding it
back in, something like
process(x) = x - x' : max(-maxchange,min(maxchange)) : +~*(0.99);
where I chose 0.99 in place of 1 (or +~_) to use a "leaky" integrator
instead of one that could drift away at dc.
This is a discrete approximation to "differentiate : clip : integrate"
On Sat, Sep 12, 2020 at 12:11 AM Stephane Letz letz@users.sourceforge.net
wrote:
--
Julius O. Smith III jos@ccrma.stanford.edu
Professor of Music and, by courtesy, Electrical Engineering
CCRMA, Stanford University
http://ccrma.stanford.edu/~jos/
Thank you Stéphane and Julius for your quick responses-
For some reason I was having some issues getting the results I wanted with the examples you sent and landed on this:
Which doesn't produce what I want either. Perhaps there is something wrong with the algorithm I referencing, or I am missing something. It feels like I need to do more research. But just so you know I am trying to implement an algorithm that will slew on a square wave like the image shown below, with a variable slew rate (to adjust the slope of the slew).
Conceptually, the slew of a square wave is an impulse train (alternating
signs). Merely clipping that impulse train does not widen the transition
interval, and only limits the amplitude. You could introduce an FIR filter
(impulse response constant over the duration of the slew interval) to
convert each impulse into a square pulse, which would integrate to give the
shape you're looking for. However, this is still an approximation to slew
limiting in op-amps and such. For more accuracy, the width of your FIR
filter would need to depend on both the slew limit and the amplitude it is
shooting for. In other words, real slew limiting is a nonlinear filter.
(Nonlinearity is easy to see by considering that the slew-limited slope
does not change as a function of target amplitude.) If you need this
level, then you could make it along the lines of amp_follower and follow
the waveform until the slew limit is reached after which it would approach
the waveform at maximum slope.
On Sat, Sep 12, 2020 at 3:24 PM Mark-David Hosale mdhosale@users.sourceforge.net wrote:
--
Julius O. Smith III jos@ccrma.stanford.edu
Professor of Music and, by courtesy, Electrical Engineering
CCRMA, Stanford University
http://ccrma.stanford.edu/~jos/
Thank you for the advice Julius-
that sounds more along the lines of what I was thinking - essentially I am trying to recreate the functionality of the rampsmooth~ object from Max, if you're familiar with that? I am going to keep plugging away at this and see what I can achieve. Seems like a good way to get to know Faust. I'll report any discoveries here-
wishing you well-
Mark-David
Thank you again Julius for your previous reponse.
Honestly - I wasn't able to get any results from the above, but I am not sure if I understand how to implement what your describing. Meanwhile, I did some poking around and I found a gen~ versionof the rampsmooth~ in Max that works exactly how I would expect:
And, I was trying to implement it in Faust with the following:
There are likely other issues, but where I am feeling most unsure is whether variables are holding values, or if they are reset when the function is reevaluated.
Also, should everything be in the letrec? or is that creating issues as well?
Thank you in advance for any pointers you may have,
Mark-David
I cannot resist little Faust puzzles like this.
Here is a solution for rising slew limiting:
slew_lim_plus(dxm,x) = x,dxm : (+, : min : (+~))~*(-1); // where dxm =
maximum upgoing slope
To derive this, I followed the logic in the theorem that proves Faust can
encode any block diagram (one of the early papers).
Including falling slew-limiting is left as an exercise.
Possibly useless hint:
slew_lim_minus(dxm,x) = -slew_lim_plus(dxm,-x);
Isn't Faust fun?
On Sun, Sep 13, 2020 at 8:10 PM Mark-David Hosale mdhosale@users.sourceforge.net wrote:
--
Julius O. Smith III jos@ccrma.stanford.edu
Professor of Music and, by courtesy, Electrical Engineering
CCRMA, Stanford University
http://ccrma.stanford.edu/~jos/
Thank you again, Julius, for your generous replies.
In the example you gave (slew_lim_plus(dxm,x) = x,dxm : (+, : min : (+~))~(-1); ) I couldn't get it to compile . First I get "syntax error, unexpected SEQ," but when I remove the first ': ' I get "syntax error, unexpected RPAR" - so, empirically I tried removing the recursion '~' on the '+' and removed the variables until it started working until ended up with this:
The results are not what I was expecting from wha tyou were describing, so I am pretty sure I broke it-
Sorry if this is a pain- it feels like I just dove into something pretty deep on the get go, c'est la vie :)
MD
There is a missing '_' in your copy. Here is what I have:
slew_lim_plus(dxm,x) = x,dxm : (+, : min : (+~))~*(-1);
and it compiles.
Let me know how it goes.
On Tue, Sep 15, 2020 at 10:31 AM Mark-David Hosale mdhosale@users.sourceforge.net wrote:
--
Julius O. Smith III jos@ccrma.stanford.edu
Professor of Music and, by courtesy, Electrical Engineering
CCRMA, Stanford University
http://ccrma.stanford.edu/~jos/
Thank you Julius,
Here is what I am seeing...
MD
Ok, try this one:
import("stdfaust.lib");
dxm = 0.1; // max upgoing slope
slew_lim_plus(dxm,x) = x,dxm : (+, : min : (+~))~*(-1);
slew_lim_minus(dxm,x) = -slew_lim_plus(dxm,-x);
process = 1 : slew_lim_plus(dxm);
Remember that it only handles upgoing slew-limiting.
To get downgoing as well, I would bring out the output of the subtraction
of the output from the input and use its sign to switch between up and down
using select2.
On Tue, Sep 15, 2020 at 7:35 PM Mark-David Hosale mdhosale@users.sourceforge.net wrote:
--
Julius O. Smith III jos@ccrma.stanford.edu
Professor of Music and, by courtesy, Electrical Engineering
CCRMA, Stanford University
http://ccrma.stanford.edu/~jos/
Spoiler alert: This might be a complete slew limiter:
slew_limiter(slopeMax) = (+ : step_calc(slopeMax) : (+~_))~*(-1) with {
step_calc(slopeMax,xmy) = xmy <: min(slopeMax),max(-slopeMax) :
select2(xmy<0);
};
On Tue, Sep 15, 2020 at 10:09 PM Julius O. Smith jos@users.sourceforge.net
wrote:
--
Julius O. Smith III jos@ccrma.stanford.edu
Professor of Music and, by courtesy, Electrical Engineering
CCRMA, Stanford University
http://ccrma.stanford.edu/~jos/
Wow, thank you Julius!!
This works!
We are using this in a filter chain that is used in the analysis of biophysical signals captured at audio rate. This was a key componet in going forward. Faust will enable us to share our work on a variety of platforms. (see: http://www.ndstudiolab.com/pacis). Hopefully the slew limiter is useful to others as well.
Kind regards,
Mark-David
Hi Mark-David,
Thanks for the interesting background on your application!
Since I'll be teaching Faust at CCRMA pretty soon, I decided to make a
tutorial video on this:
https://www.youtube.com/watch?v=3WY0ikTFAe4
(in case you might be interested)
Cheers,
Julius
On Wed, Sep 16, 2020 at 10:05 AM Mark-David Hosale mdhosale@users.sourceforge.net wrote:
--
Julius O. Smith III jos@ccrma.stanford.edu
Professor of Music and, by courtesy, Electrical Engineering
CCRMA, Stanford University
http://ccrma.stanford.edu/~jos/
Thanks Julius, very helpful !
Next step: use the OFDR, the « Optical Faust Diagram Recognition » tool to automatically write the manually designed diagram into ready to use Faust code… ((-;
Stéphane
It can be done!
- Julius
Last edit: Julius Smith 2020-10-06
Thanks Julius -
It's great to see that this contributed in some way to FAUST in general. I found the video helpful as well. Things have been progressing nicely on our end. Working with the block diagrams has proven very useful in our efforts of designing and debugging the system.
Wishing you all the best-
Mark-David
Yann has a much slicker solution:
slew_limiter(m) = \(y).(max(y-m) : min(y+m)) ~_;
It will be hard to beat that one!
Thanks! I'll check it out!