// ------------------------------------------------------------------------
// audiofx_timebased.cpp: Routines for time-based effects.
// Copyright (C) 1999-2005,2012 Kai Vehmanen
//
// Attributes:
//     eca-style-version: 3
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
// ------------------------------------------------------------------------

#include <assert.h>
#include <string>

#include <kvu_dbc.h>
#include <kvu_utils.h>

#include "eca-logger.h"

#include "samplebuffer_iterators.h"
#include "sample-ops_impl.h"
#include "audiofx_timebased.h"

static void priv_check_for_zerodelay(long int *dtime, OPERATOR::parameter_t *dtime_msec, long int srate)
{
  if (*dtime == 0) {
    *dtime = 1;
    *dtime_msec = 1 / (CHAIN_OPERATOR::parameter_t)srate * 1000.0;
    ECA_LOG_MSG(ECA_LOGGER::info,
                "WARNING: delay of 0 samples not supported, delay set to 1 sample");
  }
}

EFFECT_DELAY::EFFECT_DELAY (CHAIN_OPERATOR::parameter_t delay_time, int surround_mode, 
			    int num_of_delays, CHAIN_OPERATOR::parameter_t mix_percent,
			    CHAIN_OPERATOR::parameter_t feedback_percent) 
{
  laskuri = 0.0;

  set_parameter(1, delay_time);
  set_parameter(2, surround_mode);
  set_parameter(3, num_of_delays);
  set_parameter(4, mix_percent);
  set_parameter(5, feedback_percent);
}

void EFFECT_DELAY::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const
{
  switch (param) {
  case 1:
    pd->default_value = 100.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = false;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 2:
    pd->default_value = 0.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = true;
    pd->upper_bound = 1.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = true;
    pd->integer = true;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 3:
    pd->default_value = 1.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = false;
    // pd->upper_bound = 0.0f;
    pd->bounded_below = true;
    pd->lower_bound = 1.0f;
    pd->toggled = false;
    pd->integer = true;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 4:
    pd->default_value = 50.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = true;
    pd->upper_bound = 100.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 5:
    pd->default_value = 100.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = true;
    pd->upper_bound = 100.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  default: {}
  }
}

CHAIN_OPERATOR::parameter_t EFFECT_DELAY::get_parameter(int param) const
{
  switch (param) {
  case 1: 
    return dtime_msec;
  case 2:
    return surround;
  case 3:
    return dnum;
  case 4:
    return mix * 100.0;
  case 5:
    return feedback * 100.0;
  }
  return 0.0;
}

void EFFECT_DELAY::set_parameter(int param, CHAIN_OPERATOR::parameter_t value)
{
  switch (param) {
  case 1:
    {
      dtime_msec = value;
      dtime = dtime_msec * (CHAIN_OPERATOR::parameter_t)samples_per_second() / 1000;
      priv_check_for_zerodelay(&dtime, &dtime_msec, samples_per_second());
      std::vector<std::vector<SINGLE_BUFFER> >::iterator p = buffer.begin();
      while(p != buffer.end()) {
	std::vector<SINGLE_BUFFER>::iterator q = p->begin();
	while(q != p->end()) {
	  if (q->size() > static_cast<size_t>(dtime)) {
	    q->resize(static_cast<size_t>(dtime));
	    laskuri = dtime;
	  }
	  ++q;
	}
	++p;
      }
      break;
    }

  case 2: 
    surround = value;
    break;

  case 3: 
    {
      if (value != 0.0) dnum = static_cast<long int>(value);
      else dnum = 1.0;
      std::vector<std::vector<SINGLE_BUFFER> >::iterator p = buffer.begin();
      while(p != buffer.end()) {
	p->resize(static_cast<unsigned int>(dnum));
	++p;
      }
      laskuri = 0;
      break;
    }

  case 4:
    mix = value / 100.0;
    break;

  case 5:
    if (value == 0 || value > 100) {
      feedback = 1.0;
    } else {
    feedback = value / 100.0;
    }
    break;
  }
}

void EFFECT_DELAY::init(SAMPLE_BUFFER* insample)
{
  l.init(insample);
  r.init(insample);

  EFFECT_BASE::init(insample);

  set_parameter(1, dtime_msec);

  buffer.resize(2, std::vector<SINGLE_BUFFER> (static_cast<unsigned int>(dnum)));
  for(size_t i = 0; i < buffer.size(); i++) {
    for(size_t j = 0; j < buffer[i].size(); j++) {
      buffer[i].clear();
    }
  }
}

void EFFECT_DELAY::process(void)
{
  l.begin(SAMPLE_SPECS::ch_left);
  r.begin(SAMPLE_SPECS::ch_right);

  while(!l.end() && !r.end()) {
    SAMPLE_SPECS::sample_t temp2_left = 0.0;
    SAMPLE_SPECS::sample_t temp2_right = 0.0;

    // Initializing the feedback factor to one. (x*1 = x)
    SAMPLE_SPECS::sample_t feedfact = 1;

    for(int nm2 = 0; nm2 < dnum; nm2++) {
      SAMPLE_SPECS::sample_t temp_left = 0.0;
      SAMPLE_SPECS::sample_t temp_right = 0.0;

      // Preparing the factor...
      feedfact *= feedback;

      if (laskuri >= dtime * (nm2 + 1)) {

	switch ((int)surround) {
	case 0: 
	  {
	    // ---
	    // surround
	    temp_left = buffer[SAMPLE_SPECS::ch_left][nm2].front();
	    temp_right = buffer[SAMPLE_SPECS::ch_right][nm2].front();
	    break;
	  }

	case 1: 
	  {
	    // ---
	    // surround
	    temp_left = buffer[SAMPLE_SPECS::ch_right][nm2].front();
	    temp_right = buffer[SAMPLE_SPECS::ch_left][nm2].front();
	    break;
	  }
	case 2: 
	  {
	    if (nm2 % 2 == 0) {
	      temp_left = (buffer[SAMPLE_SPECS::ch_left][nm2].front()
			   + 
			   buffer[SAMPLE_SPECS::ch_right][nm2].front()) / 2.0;
	      temp_right = 0.0;
	    }
	    else {
	      temp_right = (buffer[SAMPLE_SPECS::ch_left][nm2].front()
			   + 
			   buffer[SAMPLE_SPECS::ch_right][nm2].front()) / 2.0;
	      temp_left = 0.0;
	    }
	    break;
	}
	} // switch

	// Applying the reduction.
	temp_left *= feedfact;
	temp_right *= feedfact;

	buffer[SAMPLE_SPECS::ch_left][nm2].pop_front();
	buffer[SAMPLE_SPECS::ch_right][nm2].pop_front();
      }
      buffer[SAMPLE_SPECS::ch_left][nm2].push_back(*l.current());
      buffer[SAMPLE_SPECS::ch_right][nm2].push_back(*r.current());

      temp2_left += temp_left / dnum;
      temp2_right += temp_right / dnum;

    }
    *l.current() = (*l.current() * (1.0 - mix)) + (temp2_left * mix);
    *r.current() = (*r.current() * (1.0 - mix)) + (temp2_right * mix);

    l.next();
    r.next();

    if (laskuri < dtime * dnum) laskuri++;
  }
}

EFFECT_MULTITAP_DELAY::EFFECT_MULTITAP_DELAY (CHAIN_OPERATOR::parameter_t delay_time, 
					      int num_of_delays, 
					      CHAIN_OPERATOR::parameter_t mix_percent)
  : 
  delay_index(0),
  filled (0),
  buffer (0)
{
  set_parameter(1, delay_time);
  set_parameter(2, num_of_delays);
  set_parameter(3, mix_percent);
}

void EFFECT_MULTITAP_DELAY::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const
{
  switch (param) {
  case 1:
    pd->default_value = 100.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = false;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 2:
    pd->default_value = 1.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = false;
    // pd->upper_bound = 0.0f;
    pd->bounded_below = true;
    pd->lower_bound = 1.0f;
    pd->toggled = false;
    pd->integer = true;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 3:
    pd->default_value = 50.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = true;
    pd->upper_bound = 100.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  default: {}
  }
}

CHAIN_OPERATOR::parameter_t EFFECT_MULTITAP_DELAY::get_parameter(int param) const
{
  switch (param) {
  case 1: 
    return dtime_msec;
  case 2:
    return dnum;
  case 3:
    return mix * 100.0;
  }
  return 0.0;
}

void EFFECT_MULTITAP_DELAY::set_parameter(int param, CHAIN_OPERATOR::parameter_t value)
{
  switch (param) {
  case 1:
    {
      dtime_msec = value;
      dtime = static_cast<long int>(dtime_msec * (CHAIN_OPERATOR::parameter_t)samples_per_second() / 1000);
      priv_check_for_zerodelay(&dtime, &dtime_msec, samples_per_second());
      DBC_CHECK(buffer.size() == filled.size());
      for(int n = 0; n < static_cast<int>(buffer.size()); n++) {
	if ((dtime * dnum) > static_cast<int>(buffer[n].size())) {
	  buffer[n].resize(dtime * dnum);
	}
	delay_index[n] = dtime * dnum - 1;
	for(int m = 0; m < static_cast<int>(filled[n].size()); m++) {
	  filled[n][m] = false;
	}
      }
      break;
    }

  case 2: 
    {
      if (value != 0.0) dnum = static_cast<long int>(value);
      else dnum = 1;
      DBC_CHECK(buffer.size() == filled.size());
      for(int n = 0; n < static_cast<int>(buffer.size()); n++) {
	if ((dtime * dnum) > static_cast<int>(buffer[n].size())) {
	  buffer[n].resize(dtime * dnum);
	}
	for(int m = 0; m < static_cast<int>(filled[n].size()); m++) {
	  filled[n][m] = false;
	}
	delay_index[n] = dtime * dnum - 1;
      }
      break;
    }

  case 3:
    mix = value / 100.0;
    break;
  }
}

void EFFECT_MULTITAP_DELAY::init(SAMPLE_BUFFER* insample)
{
  i.init(insample);

  EFFECT_BASE::init(insample);

  set_parameter(1, dtime_msec);

  delay_index.resize(channels(), dtime * dnum - 1);
  filled.resize(channels(), std::vector<bool> (dnum, false));
  buffer.resize(channels(),
                std::vector<SAMPLE_SPECS::sample_t> (dtime *
                                                     dnum));
  for(int i = 0; i < channels(); i++) {
    delay_index[i] = dtime * dnum - 1;
    for(size_t j = 0; j < filled[i].size(); j++) 
      filled[i][j] = false;
    for(size_t j = 0; j < buffer[i].size(); j++)
      buffer[i][j] = 0.0f;
  }
}

void EFFECT_MULTITAP_DELAY::process(void)
{
  long int len = dtime * dnum;

  i.begin();
  while(!i.end()) {
    for(int n = 0; n < channels(); n++) {
      SAMPLE_SPECS::sample_t temp1 = 0.0;
      for(int nm2 = 0; nm2 < dnum; nm2++) {
	if (filled[n][nm2] == true) {
	  DBC_CHECK((delay_index[n] + nm2 * dtime) % len >= 0);
	  DBC_CHECK((delay_index[n] + nm2 * dtime) % len < len);
	  temp1 += buffer[n][(delay_index[n] + nm2 * dtime) % len];
	}
      }
      buffer[n][delay_index[n]] = *i.current(n);
      *i.current(n) = (*i.current(n) * (1.0 - mix)) + (temp1 * mix / dnum);
    
      --(delay_index[n]);
      for(int nm2 = 0; nm2 < dnum; nm2++) {
	if (delay_index[n] < len - dtime * nm2) filled[n][nm2] = true;
      }
      if (delay_index[n] == -1) delay_index[n] = len - 1;
    }
    i.next();
  }
}

EFFECT_FAKE_STEREO::EFFECT_FAKE_STEREO (CHAIN_OPERATOR::parameter_t delay_time)
{
   set_parameter(1, delay_time);
}

void EFFECT_FAKE_STEREO::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const
{
  switch (param) {
  case 1:
    pd->default_value = 20.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = false;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  default: {}
  }
}

CHAIN_OPERATOR::parameter_t EFFECT_FAKE_STEREO::get_parameter(int param) const
{
  switch (param) {
  case 1: 
    return dtime_msec;
  }
  return 0.0;
}

void EFFECT_FAKE_STEREO::set_parameter(int param, CHAIN_OPERATOR::parameter_t value)
{
  switch (param) {
  case 1:
    dtime_msec = value;
    dtime = dtime_msec * (CHAIN_OPERATOR::parameter_t)samples_per_second() / 1000;
    priv_check_for_zerodelay(&dtime, &dtime_msec, samples_per_second());
    std::vector<std::deque<SAMPLE_SPECS::sample_t> >::iterator p = buffer.begin();
    while(p != buffer.end()) {
      if (p->size() > static_cast<size_t>(dtime)) {
	p->resize(static_cast<size_t>(dtime));
      }
      ++p;
    }
    break;
  }
}

void EFFECT_FAKE_STEREO::init(SAMPLE_BUFFER* insample)
{
  l.init(insample);
  r.init(insample);

  EFFECT_BASE::init(insample);

  set_parameter(1, dtime_msec);
  buffer.resize(2);
  for(size_t i = 0; i < buffer.size(); i++) {
    for(size_t j = 0; j < buffer[i].size(); j++) {
      buffer[i].clear();
    }
  }
}

void EFFECT_FAKE_STEREO::process(void)
{
  l.begin(SAMPLE_SPECS::ch_left);
  r.begin(SAMPLE_SPECS::ch_right);
  while(!l.end() && !r.end()) {
    SAMPLE_SPECS::sample_t temp_left = 0;
    SAMPLE_SPECS::sample_t temp_right = 0;
    if (buffer[SAMPLE_SPECS::ch_left].size() >= static_cast<size_t>(dtime)) {
      temp_left = buffer[SAMPLE_SPECS::ch_left].front();
      temp_right = buffer[SAMPLE_SPECS::ch_right].front();

      temp_right = (temp_left + temp_right) / 2.0;
      temp_left = (*l.current() + *r.current()) / 2.0;

      buffer[SAMPLE_SPECS::ch_left].pop_front();
      buffer[SAMPLE_SPECS::ch_right].pop_front();
    }
    else {
      temp_left = (*l.current() + *r.current()) / 2.0;
      temp_right = 0.0;
    }
    buffer[SAMPLE_SPECS::ch_left].push_back(*l.current());
    buffer[SAMPLE_SPECS::ch_right].push_back(*r.current());

    *l.current() = temp_left;
    *r.current() = temp_right;

    l.next();
    r.next();
  }
}

EFFECT_REVERB::EFFECT_REVERB (CHAIN_OPERATOR::parameter_t delay_time, int surround_mode, 
			      CHAIN_OPERATOR::parameter_t feedback_percent) 
{
  set_parameter(1, delay_time);
  set_parameter(2, surround_mode);
  set_parameter(3, feedback_percent);
}

void EFFECT_REVERB::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const
{
  switch (param) {
  case 1:
    pd->default_value = 20.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = false;
    // pd->upper_bound = 0.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 2:
    pd->default_value = 0.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = true;
    pd->upper_bound = 1.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = true;
    pd->integer = true;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 3:
    pd->default_value = 50.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = true;
    pd->upper_bound = 100.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  default: {}
  }
}

CHAIN_OPERATOR::parameter_t EFFECT_REVERB::get_parameter(int param) const 
{
  switch (param) {
  case 1: 
    return dtime_msec;
  case 2:
    return surround;
  case 3:
    return feedback * 100.0;
  }
  return 0.0;
}

void EFFECT_REVERB::set_parameter(int param, CHAIN_OPERATOR::parameter_t value)
{
  switch (param) {
  case 1: 
    {
      dtime_msec = value;
      dtime = dtime_msec * (CHAIN_OPERATOR::parameter_t)samples_per_second() / 1000;
      priv_check_for_zerodelay(&dtime, &dtime_msec, samples_per_second());
      std::vector<std::deque<SAMPLE_SPECS::sample_t> >::iterator p = buffer.begin();
      while(p != buffer.end()) {
        if (p->size() > static_cast<size_t>(dtime)) {
          p->resize(static_cast<size_t>(dtime));
        }
        ++p;
      }
      break;
    }

  case 2: 
    surround = value;
    break;

  case 3: 
    feedback = value / 100.0;
    break;
  }
}

void EFFECT_REVERB::init(SAMPLE_BUFFER* insample)
{
  l.init(insample);
  r.init(insample);

  EFFECT_BASE::init(insample);

  set_parameter(1, dtime_msec);

  buffer.resize(2);
  for(size_t i = 0; i < buffer.size(); i++) {
    for(size_t j = 0; j < buffer[i].size(); j++) {
      buffer[i].clear();
    }
  }
}

void EFFECT_REVERB::process(void)
{
  l.begin(SAMPLE_SPECS::ch_left);
  r.begin(SAMPLE_SPECS::ch_right);
  while(!l.end() && !r.end()) {
    SAMPLE_SPECS::sample_t temp_left = 0.0;
    SAMPLE_SPECS::sample_t temp_right = 0.0;
    if (buffer[SAMPLE_SPECS::ch_left].size() >= static_cast<size_t>(dtime)) {
      temp_left = buffer[SAMPLE_SPECS::ch_left].front();
      temp_right = buffer[SAMPLE_SPECS::ch_right].front();
      
      if (surround == 0) {
	*l.current() = (*l.current() * (1 - feedback)) + (temp_left *  feedback);
	*r.current() = (*r.current() * (1 - feedback)) + (temp_right * feedback);
      }
      else {
	*l.current() = (*l.current() * (1 - feedback)) + (temp_right *  feedback);
	*r.current() = (*r.current() * (1 - feedback)) + (temp_left * feedback);
      }
      buffer[SAMPLE_SPECS::ch_left].pop_front();
      buffer[SAMPLE_SPECS::ch_right].pop_front();
    }
    else {
	*l.current() = (*l.current() * (1 - feedback));
	*r.current() = (*r.current() * (1 - feedback));
    }
    *l.current() = ecaops_flush_to_zero(*l.current());
    *r.current() = ecaops_flush_to_zero(*r.current());
    buffer[SAMPLE_SPECS::ch_left].push_back(*l.current());
    buffer[SAMPLE_SPECS::ch_right].push_back(*r.current());
    l.next();
    r.next();
  }
}

EFFECT_MODULATING_DELAY::EFFECT_MODULATING_DELAY(CHAIN_OPERATOR::parameter_t delay_time, 
						 long int vartime_in_samples,
						 CHAIN_OPERATOR::parameter_t feedback_percent,
						 CHAIN_OPERATOR::parameter_t lfo_freq)
{
  set_parameter(1, delay_time);
  set_parameter(2, vartime_in_samples);
  set_parameter(3, feedback_percent);
  set_parameter(4, lfo_freq);
}

void EFFECT_MODULATING_DELAY::parameter_description(int param, struct PARAM_DESCRIPTION *pd) const
{
  switch (param) {
  case 1:
    pd->default_value = 2.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = false;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 2:
    pd->default_value = 20.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = false;
    // pd->upper_bound = 0.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = true;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 3:
    pd->default_value = 50.0f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = true;
    pd->upper_bound = 100.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  case 4:
    pd->default_value = 0.4f;
    pd->description = get_parameter_name(param);
    pd->bounded_above = false;
    // pd->upper_bound = 0.0f;
    pd->bounded_below = true;
    pd->lower_bound = 0.0f;
    pd->toggled = false;
    pd->integer = false;
    pd->logarithmic = false;
    pd->output = false;
    break;
  default: {}
  }
}

CHAIN_OPERATOR::parameter_t EFFECT_MODULATING_DELAY::get_parameter(int param) const
{
  switch (param) {
  case 1: 
    return dtime_msec;

  case 2: 
    return vartime;

  case 3: 
    return feedback * 100.0;

  case 4: 
    return lfo.get_parameter(1);
  }
  return 0.0;
}

void EFFECT_MODULATING_DELAY::set_parameter(int param, CHAIN_OPERATOR::parameter_t value)
{
  switch (param) {
  case 1:
    {
      dtime_msec = value;
      dtime = static_cast<long int>(dtime_msec * (CHAIN_OPERATOR::parameter_t)samples_per_second() / 1000);
      priv_check_for_zerodelay(&dtime, &dtime_msec, samples_per_second());
      DBC_CHECK(buffer.size() == delay_index.size());
      DBC_CHECK(buffer.size() == filled.size());
      for(int n = 0; n < static_cast<int>(buffer.size()); n++) {
	if (dtime * 2 > static_cast<long int>(buffer[n].size())) {
	  buffer[n].resize(dtime * 2);
	}
	delay_index[n] = 0;
	filled[n] = false;
      }
      break;
    }

  case 2: 
    vartime = value;
    break;

  case 3: 
    feedback = value / 100.0;
    break;

  case 4:
    lfo.set_parameter(1, value);
    break;
  }
}

void EFFECT_MODULATING_DELAY::init(SAMPLE_BUFFER* insample)
{
  i.init(insample);
  lfo.init();

  if (samples_per_second() > 0)
    advance_len_secs_rep = 
      ((double)insample->length_in_samples()) / 
      samples_per_second();
  else
    advance_len_secs_rep = 0;
    
  set_parameter(1, dtime_msec);

  EFFECT_BASE::init(insample);

  filled.resize(channels(), false);
  delay_index.resize(channels(), 2 * dtime);
  buffer.resize(channels(), std::vector<SAMPLE_SPECS::sample_t> (2 * dtime));
  for(size_t i = 0; i < buffer.size(); i++) {
    for(size_t j = 0; j < buffer[i].size(); j++) {
      buffer[i][j] = 0.0f;
    }
  }
}

void EFFECT_MODULATING_DELAY::process(void)
{
  lfo_pos_secs_rep += advance_len_secs_rep;
}

void EFFECT_FLANGER::process(void)
{
  EFFECT_MODULATING_DELAY::process();

  i.begin();
  while(!i.end()) {
    SAMPLE_SPECS::sample_t temp1 = 0.0;
    parameter_t p = vartime * lfo.value(lfo_pos_secs_rep);
    if (filled[i.channel()] == true) {
      DBC_CHECK((dtime + delay_index[i.channel()] + static_cast<long int>(p)) % (dtime * 2) >= 0);
      DBC_CHECK((dtime + delay_index[i.channel()] + static_cast<long int>(p)) % (dtime * 2) < static_cast<long int>(buffer[i.channel()].size()));
      temp1 = buffer[i.channel()][(dtime + delay_index[i.channel()] + static_cast<long int>(p)) % (dtime * 2)];
    }
    *i.current() = ecaops_flush_to_zero((*i.current() * (1.0 - feedback)) + (temp1 * feedback));
    buffer[i.channel()][delay_index[i.channel()]] = *i.current();

    ++(delay_index[i.channel()]);
    if (delay_index[i.channel()] == 2 * dtime) {
      delay_index[i.channel()] = 0;
      filled[i.channel()] = true;
    }
    i.next();
  }
}

void EFFECT_CHORUS::process(void)
{
  EFFECT_MODULATING_DELAY::process();

  i.begin();
  while(!i.end()) {
    SAMPLE_SPECS::sample_t temp1 = 0.0;
    parameter_t p = vartime * lfo.value(lfo_pos_secs_rep);
    if (filled[i.channel()] == true) {
      DBC_CHECK((dtime + delay_index[i.channel()] + static_cast<long int>(p)) % (dtime * 2) >= 0);
      DBC_CHECK((dtime + delay_index[i.channel()] + static_cast<long int>(p)) % (dtime * 2) < static_cast<long int>(buffer[i.channel()].size()));
      temp1 = buffer[i.channel()][(dtime + delay_index[i.channel()] + static_cast<long int>(p)) % (dtime * 2)];
    }
    buffer[i.channel()][delay_index[i.channel()]] = *i.current();
    *i.current() = (*i.current() * (1.0 - feedback)) + (temp1 * feedback);

    ++(delay_index[i.channel()]);
    if (delay_index[i.channel()] == 2 * dtime) {
      delay_index[i.channel()] = 0;
      filled[i.channel()] = true;
    }
    i.next();
  }
}

void EFFECT_PHASER::process(void)
{
  EFFECT_MODULATING_DELAY::process();

  i.begin();
  while(!i.end()) {
    SAMPLE_SPECS::sample_t temp1 = 0.0;
    parameter_t p = vartime * lfo.value(lfo_pos_secs_rep);
    if (filled[i.channel()] == true) {
      DBC_CHECK((dtime + delay_index[i.channel()] + static_cast<long int>(p)) % (dtime * 2) >= 0);
      DBC_CHECK((dtime + delay_index[i.channel()] + static_cast<long int>(p)) % (dtime * 2) < static_cast<long int>(buffer[i.channel()].size()));
      temp1 = buffer[i.channel()][(dtime + delay_index[i.channel()] + static_cast<long int>(p)) % (dtime * 2)];
      //          cerr << "b: "
      //    	   << (delay_index[i.channel()] + static_cast<long int>(p)) % dtime
      //    	   << "," << p << ".\n";
    }
    *i.current() = ecaops_flush_to_zero(*i.current() * (1.0 - feedback) + (-1.0 * temp1 * feedback));
    buffer[i.channel()][delay_index[i.channel()]] = *i.current();

    ++(delay_index[i.channel()]);
    if (delay_index[i.channel()] == 2 * dtime) {
      delay_index[i.channel()] = 0;
      filled[i.channel()] = true;
    }
    i.next();
  }
}
