Menu

Slew Rate Limiter

Help
2020-09-12
2020-10-06
  • Mark-David Hosale

    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;
    }
    

    (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...

    slewRateLim(mc)= x_prev_limit_out
    with {
    
        delta = _ - _';
        x_prev_limit_out = max(min(delta, mc),-mc) + delta;
    
    };
    

    Thanks in advance for any pointers you may have-
    kind regards,
    Mark-David

     

    Last edit: Mark-David Hosale 2020-09-12
    • Stephane Letz

      Stephane Letz - 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

      Le 12 sept. 2020 à 06:16, Mark-David Hosale mdhosale@users.sourceforge.net a écrit :

      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;
      }
      ~~~

      (from: https://www.embeddedrelated.com/showarticle/646.php)

      I have made some meager attempts. for part 3 I was imagining eomthing 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...

      ~~~
      slewRateLim(mc)= x_prev_limit_out
      with {

      delta = _ - _';
      x_prev_limit_out = max(min(delta, mc),-mc) + delta;

      };
      ~~~

      Thanks in advance for any ointers you may have-
      kind regards,
      Mark-David


      Slew Rate Limiter


      Sent from sourceforge.net because you indicated interest in https://sourceforge.net/p/faudiostream/discussion/347046/

      To unsubscribe from further messages, please visit https://sourceforge.net/auth/subscriptions/

       
      • Julius O. Smith

        Julius O. Smith - 2020-09-12

        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"

        • Julius

        On Sat, Sep 12, 2020 at 12:11 AM Stephane Letz letz@users.sourceforge.net
        wrote:

        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

        Le 12 sept. 2020 à 06:16, Mark-David Hosale mdhosale@users.sourceforge.net a écrit :

        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;
        }
        ~~~

        (from: https://www.embeddedrelated.com/showarticle/646.php)

        I have made some meager attempts. for part 3 I was imagining eomthing
        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...

        ~~~
        slewRateLim(mc)= x_prev_limit_out
        with {

        delta = _ - _';
        x_prev_limit_out = max(min(delta, mc),-mc) + delta;

        };
        ~~~

        Thanks in advance for any ointers you may have-
        kind regards,
        Mark-David


        Slew Rate Limiter


        Sent from sourceforge.net because you indicated interest in <
        https://sourceforge.net/p/faudiostream/discussion/347046/>

        To unsubscribe from further messages, please visit <
        https://sourceforge.net/auth/subscriptions/>


        Slew Rate Limiter


        Sent from sourceforge.net because you indicated interest in <
        https://sourceforge.net/p/faudiostream/discussion/347046/>

        To unsubscribe from further messages, please visit <
        https://sourceforge.net/auth/subscriptions/>

        --
        Julius O. Smith III jos@ccrma.stanford.edu
        Professor of Music and, by courtesy, Electrical Engineering
        CCRMA, Stanford University
        http://ccrma.stanford.edu/~jos/

         
  • Mark-David Hosale

    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).

     
    • Julius O. Smith

      Julius O. Smith - 2020-09-13

      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.

      • Julius

      On Sat, Sep 12, 2020 at 3:24 PM Mark-David Hosale mdhosale@users.sourceforge.net wrote:

      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).


      Slew Rate Limiter


      Sent from sourceforge.net because you indicated interest in <
      https://sourceforge.net/p/faudiostream/discussion/347046/>

      To unsubscribe from further messages, please visit <
      https://sourceforge.net/auth/subscriptions/>

      --
      Julius O. Smith III jos@ccrma.stanford.edu
      Professor of Music and, by courtesy, Electrical Engineering
      CCRMA, Stanford University
      http://ccrma.stanford.edu/~jos/

       
  • Mark-David Hosale

    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

     
  • Mark-David Hosale

    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:

    History value, prev, stepcounter, increment;
    input, rampup, rampdown = in1, in2, in3;
    
    //input = abs(input);
    
    // reset ramp if input has changed, or if ramp parameters change:
    if (input != prev || delta(rampup) || delta(rampdown)) {
        direction = (input - value);
        if (direction > 0) {
            stepcounter = rampup;
            increment = direction / rampup;
        } else {
            stepcounter = rampdown;
            increment = direction / rampdown;
        }
        prev = input;   
    }
    
    // keep incrementing until stepcounter counts down to zero
    if (stepcounter != 0) {
        stepcounter -= 1;
        value += increment;
    } else {
        value = input;
    }
    
    out = value;
    

    And, I was trying to implement it in Faust with the following:

    slr(rampup, rampdown) = outsig
    letrec {
    
        'input = _;
        'value = _';
    
        'hasChanged= (input !=prev) | (rampup != rampup') | (rampdown != rampdown');
    
        'direction = input-value;
        'stepcounter = 
            stepcounter,
            (rampdown, rampup:select2(direction > 0))
            :select2(hasChanged)<:_,_-1
            :select2(stepcounter != 0):_;
    
        'prev= prev,input:select2(hasChanged):_;
    
        'increment =
            0,
            (direction/rampdown, direction/rampup:select2(direction > 0))
            :select2(hasChanged):_;
    
        'outsig = input,value+increment:select2(stepcounter != 0):_;
    };
    
    
    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

     
    • Julius O. Smith

      Julius O. Smith - 2020-09-14

      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:

      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:

      ~~~
      History value, prev, stepcounter, increment;
      input, rampup, rampdown = in1, in2, in3;

      //input = abs(input);

      // reset ramp if input has changed, or if ramp parameters change:
      if (input != prev || delta(rampup) || delta(rampdown)) {
      direction = (input - value);
      if (direction > 0) {
      stepcounter = rampup;
      increment = direction / rampup;
      } else {
      stepcounter = rampdown;
      increment = direction / rampdown;
      }
      prev = input;
      }

      // keep incrementing until stepcounter counts down to zero
      if (stepcounter != 0) {
      stepcounter -= 1;
      value += increment;
      } else {
      value = input;
      }

      out = value;
      ~~~

      And, I was trying to implement it in Faust with the following:
      ~~~
      slr(rampup, rampdown) = outsig
      letrec {

      'input = _;
      'value = _';
      
      'hasChanged= (input !=prev) | (rampup != rampup') | (rampdown !=
      

      rampdown');

      'direction = input-value;
      'stepcounter =
          stepcounter,
          (rampdown, rampup:select2(direction > 0))
          :select2(hasChanged)<:_,_-1
          :select2(stepcounter != 0):_;
      
      'prev= prev,input:select2(hasChanged):_;
      
      'increment =
          0,
          (direction/rampdown, direction/rampup:select2(direction > 0))
          :select2(hasChanged):_;
      
      'outsig = input,value+increment:select2(stepcounter != 0):_;
      

      };

      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


      Slew Rate Limiter


      Sent from sourceforge.net because you indicated interest in <
      https://sourceforge.net/p/faudiostream/discussion/347046/>

      To unsubscribe from further messages, please visit <
      https://sourceforge.net/auth/subscriptions/>

      --
      Julius O. Smith III jos@ccrma.stanford.edu
      Professor of Music and, by courtesy, Electrical Engineering
      CCRMA, Stanford University
      http://ccrma.stanford.edu/~jos/

       
  • Mark-David Hosale

    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

     
    • Julius O. Smith

      Julius O. Smith - 2020-09-15

      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.

      • Julius

      On Tue, Sep 15, 2020 at 10:31 AM Mark-David Hosale mdhosale@users.sourceforge.net wrote:

      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


      Slew Rate Limiter


      Sent from sourceforge.net because you indicated interest in <
      https://sourceforge.net/p/faudiostream/discussion/347046/>

      To unsubscribe from further messages, please visit <
      https://sourceforge.net/auth/subscriptions/>

      --
      Julius O. Smith III jos@ccrma.stanford.edu
      Professor of Music and, by courtesy, Electrical Engineering
      CCRMA, Stanford University
      http://ccrma.stanford.edu/~jos/

       
  • Mark-David Hosale

    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

     
    • Julius O. Smith

      Julius O. Smith - 2020-10-04

      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:

      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


      Slew Rate Limiter


      Sent from sourceforge.net because you indicated interest in <
      https://sourceforge.net/p/faudiostream/discussion/347046/>

      To unsubscribe from further messages, please visit <
      https://sourceforge.net/auth/subscriptions/>

      --
      Julius O. Smith III jos@ccrma.stanford.edu
      Professor of Music and, by courtesy, Electrical Engineering
      CCRMA, Stanford University
      http://ccrma.stanford.edu/~jos/

       
      • Stephane Letz

        Stephane Letz - 2020-10-04

        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

        Le 4 oct. 2020 à 07:16, Julius O. Smith jos@users.sourceforge.net a écrit :

        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:

        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


        Slew Rate Limiter


        Sent from sourceforge.net because you indicated interest in <
        https://sourceforge.net/p/faudiostream/discussion/347046/>

        To unsubscribe from further messages, please visit <
        https://sourceforge.net/auth/subscriptions/>

        --
        Julius O. Smith III jos@ccrma.stanford.edu
        Professor of Music and, by courtesy, Electrical Engineering
        CCRMA, Stanford University
        http://ccrma.stanford.edu/~jos/


        Slew Rate Limiter


        Sent from sourceforge.net because you indicated interest in https://sourceforge.net/p/faudiostream/discussion/347046/

        To unsubscribe from further messages, please visit https://sourceforge.net/auth/subscriptions/

         
        • Julius O. Smith

          Julius O. Smith - 2020-10-04

          It can be done!
          - Julius

           

          Last edit: Julius Smith 2020-10-06
      • Mark-David Hosale

        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

         
  • Julius Smith

    Julius Smith - 2020-10-06

    Yann has a much slicker solution:

    slew_limiter(m) = \(y).(max(y-m) : min(y+m)) ~_;

    It will be hard to beat that one!

    • Julius
     
  • Mark-David Hosale

    Thanks! I'll check it out!

     

Log in to post a comment.