<?xml version="1.0"?>
<!DOCTYPE ladspa SYSTEM "ladspa-swh.dtd">
<?xml-stylesheet href="ladspa.css" type="text/css"?>

<ladspa>
  <global>
    <meta name="maker" value="Steve Harris &lt;steve@plugin.org.uk&gt;"/>
    <meta name="copyright" value="GPL"/>
    <meta name="properties" value="HARD_RT_CAPABLE"/>
    <code>
      #include "ladspa-util.h"
      #include "util/db.h"

      /* Minimum buffer size in seconds */
      #define BUFFER_TIME 2
    </code>
  </global>

  <plugin label="lookaheadLimiter" id="1435" class="LimiterPlugin">
    <name>Lookahead limiter</name>

    <callback event="instantiate"><![CDATA[
      buffer_len = 16384;
      buffer_pos = 0;
      fs = s_rate;

      db_init();

      /* Find size for power-of-two interleaved delay buffer */
      while(buffer_len < s_rate * BUFFER_TIME * 2) {
	buffer_len *= 2;
      }
      buffer = calloc(buffer_len, sizeof(float));

      peak = 0.0f;
      peak_dist = 1;
      atten = 0.0f;
    ]]></callback>

    <callback event="activate"><![CDATA[
      memset(buffer, 0, buffer_len * sizeof(float));

      peak = 0.0f;
      peak_dist = 1;
      atten = 0.0f;
    ]]></callback>

    <callback event="run"><![CDATA[
      unsigned long pos;
      const float max = DB_CO(limit);
      float sig, gain;
      const unsigned int delay = delay_s * fs;

      for (pos = 0; pos < sample_count; pos++) {
	buffer[(buffer_pos * 2) & (buffer_len - 1)] = in_1[pos];
	buffer[(buffer_pos * 2 + 1) & (buffer_len - 1)] = in_2[pos];

	/* sig contains the amplitude of the current frame, in dB's realtive
         * to the limit */
	sig = fabs(in_1[pos]) > fabs(in_2[pos]) ? fabs(in_1[pos]) :
		fabs(in_2[pos]);
        //sig = lin2db(sig) - limit;
        sig = CO_DB(sig) - limit;

	if (sig > 0.0f && sig / (float)delay > peak / (float)peak_dist) {
	  peak_dist = delay;
	  peak = sig;
	}

	/* Incremenatlly approach the correct attenuation for the next peak */
	atten -= (atten - peak) / (float)(peak_dist + 1);

	if (peak_dist-- == 0) {
		peak_dist = delay;
		peak = 0.0f;
	}

	gain = 1.0f / db2lin(atten);
	buffer_write(out_1[pos], buffer[(buffer_pos * 2 - delay * 2) &
					(buffer_len - 1)] * gain);
	buffer_write(out_2[pos], buffer[(buffer_pos * 2 - delay * 2 + 1) &
					(buffer_len - 1)] * gain);

	/* Ensure that the signal really can't be over the limit, potentially
         * changes in the lookahead time could cause us to miss peaks */

#if 0
XXX FIXME XXX
	if (out_1[pos] < -max) {
	  buffer_write(out_1[pos], -max);
	} else if (out_1[pos] > max) {
	  buffer_write(out_1[pos], max);
	}
	if (out_2[pos] < -max) {
	  buffer_write(out_2[pos], -max);
	} else if (out_2[pos] > max) {
	  buffer_write(out_2[pos], max);
	}
#endif

	buffer_pos++;
      }

      plugin_data->buffer_pos = buffer_pos;
      plugin_data->peak = peak;
      plugin_data->peak_dist = peak_dist;
      plugin_data->atten = atten;

      *(plugin_data->attenuation) = atten;
      *(plugin_data->latency) = delay;
    ]]></callback>

    <callback event="cleanup"><![CDATA[
       free(plugin_data->buffer);
    ]]></callback>

    <port label="limit" dir="input" type="control" hint="default_0">
      <name>Limit (dB)</name>
      <p>The maximum output amplitude. Peaks over this level will be attenuated as smoothly as possible to bring them as close as possible to this level.</p>
      <range min="-20" max="0"/>
    </port>

    <port label="delay_s" dir="input" type="control" hint="default_middle">
      <name>Lookahead delay</name>
      <p>The delay used by the lookahead predictor. The longer the delay the smoother the limiting will be, but higher the latency.</p>
      <range min="0.001" max="2.0"/>
    </port>

    <port label="attenuation" dir="output" type="control">
      <name>Attenuation (dB)</name>
      <p>The current attenuation of the signal coming out of the delay buffer.</p>
      <range min="0" max="12"/>
    </port>

    <port label="in_1" dir="input" type="audio">
      <name>Input 1</name>
    </port>

    <port label="in_2" dir="input" type="audio">
      <name>Input 2</name>
    </port>

    <port label="out_1" dir="output" type="audio">
      <name>Output 1</name>
    </port>

    <port label="out_2" dir="output" type="audio">
      <name>Output 2</name>
    </port>

    <port label="latency" dir="output" type="control">
      <name>latency</name>
    </port>

    <instance-data label="buffer" type="LADSPA_Data *" />
    <instance-data label="buffer_len" type="unsigned int" />
    <instance-data label="buffer_pos" type="unsigned int" />
    <instance-data label="fs" type="unsigned int" />
    <!-- running value for the attenuation -->
    <instance-data label="atten" type="float" />
    <!-- the next peak, relative to the limit -->
    <instance-data label="peak" type="float" />
    <!-- the number of sample until the next peak -->
    <instance-data label="peak_dist" type="unsigned int" />
  </plugin>
</ladspa>

