/*---------------------------------------------------------------------------*/
/*
  File: powerlim.cpp

  Mean power limiter.
*/
/*---------------------------------------------------------------------------*/

#include "platform.h"
#include "powerlim.h"

#define FILE_ "powerlim.cpp"

/*---------------------------------------------------------------------------*/

#define POWER_LIMITER_TIME_CONSTANT        0x8000

void power_limiter_init(t_power_limiter *p_powerlim, word max_level, word slow_time_constant, dword overdrive)
{
    dword d;

    p_powerlim->sqr_max_level = ((dword) max_level) * ((dword) max_level);
    p_powerlim->gain = 0x80000000;
    p_powerlim->headroom = p_powerlim->sqr_max_level;
    p_powerlim->slow_time_constant = slow_time_constant;
    d = (((overdrive >> 16) * ((dword) POWER_LIMITER_TIME_CONSTANT)) << 1) +
        (((dword)((overdrive & 0xffff) * ((dword) POWER_LIMITER_TIME_CONSTANT))) >> 15) +
        POWER_LIMITER_TIME_CONSTANT;
    p_powerlim->overdrive_low = (word)(d & 0xffff);
    p_powerlim->overdrive_high = (word)(d >> 16);
}

short power_limiter_exec(t_power_limiter *p_powerlim, short in_sample, short probe_sample)
{
    short s;
    dword d, e;
    short v;

    s = (short)(((int32)(p_powerlim->sqr_max_level -
        ((int32) probe_sample) * ((int32) probe_sample))) >> 15);
    p_powerlim->headroom += (((int32)(s - (p_powerlim->headroom >> 16))) *
        ((dword)(p_powerlim->slow_time_constant))) << 1;
    if (p_powerlim->headroom >= 0)
    {
        d = (((dword)(p_powerlim->overdrive_high)) * ((dword)(p_powerlim->headroom >> 16))) << 1;
        v = (short)(d >> 16);
        d <<= 16;
        e = (((dword)(p_powerlim->overdrive_low)) * ((dword)(p_powerlim->headroom >> 16))) << 1;
        d += e;
        if (d < e)
            v++;
    }
    else
    {
        d = -((int32)((((dword)(p_powerlim->overdrive_high)) * ((dword)(-(p_powerlim->headroom >> 16)))) << 1));
        v = (short)(d >> 16);
        d <<= 16;
        e = (((dword)(p_powerlim->overdrive_low)) * ((dword)(-(p_powerlim->headroom >> 16)))) << 1;
        if (d < e)
            v--;
        d -= e;
    }
    s = (short)(((int32)(((int32) in_sample) * ((dword)(p_powerlim->gain >> 16)))) >> 15);
    e = (((dword) POWER_LIMITER_TIME_CONSTANT) * (((dword)(((int32) s) * ((int32) s))) >> 15)) << 1;
    if (d < e)
        v--;
    d -= e;
    d += p_powerlim->gain;
    if (d < p_powerlim->gain)
        v++;
    if (v < 0)
        p_powerlim->gain = 0;
    else if ((v > 0) || (d > 0x80000000))
        p_powerlim->gain = 0x80000000;
    else
        p_powerlim->gain = d;
    return s;
}


/*---------------------------------------------------------------------------*/
