/* aw.cpp

	AlienWah $Revision: 1.11 $
	LADSPA plugin implementation:
	Paul Thompson <set@pobox.com> (C) Dec 1, 2001
	Initial test values:
	freq:       0.6
	startphase: 0
	feedback:   0.5
	delay:      20
*/

#define MAX_DELAY 50

/* (original sine.cpp sample plugin)
   Free software by Richard W.E. Furse. Do with as you will. No
   warranty. 
*/


/* Original effect and demo program:
    Alien-Wah by Nasca Octavian Paul from Tg. Mures, Romania
    e-mail:  <paulnasca@email.ro> or <paulnasca@yahoo.com>.    

    Copyright (C) 2001 Nasca Octavian Paul

    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 USA

 The algorithm was found by me by mistake(I was looking for
something else); I c alled this effect "Alien Wah" because sounds
a bit like wahwah, but more strange .  The ideea of this effect
is very simple: It is a feedback delay who uses comple x numbers.
If x[] represents the input and y[] is the output, so a simple
feedback delay l ooks like this: y[n]=y[n-delay]*fb+x[n]*(1-fb)
'fb' is a real number between 0 and 1.  If you change the fb with
a complex number who has the MODULUS smaller than 1, it will look
like this.

fb=R*(cos(alpha)+i*sin(alpha));  i^2=-1; R<1;
y[n]=y[n-delay]*R*(cos(alpha)+i*sin(alpha))+x[n]*(1-R);

 alpha is the phase of the number and is controlled by the
LFO(Low Frequency Osc illator).  If the 'delay' parameter is low,
the effect sounds more like wah-wah, but if it is big, the effect
will sound very interesting.  The input x[n] has the real part of
the samples from the wavefile and the imagi nary part is zero.
The output of this effect is the real part of y[n].
*/

/*****************************************************************************/

#include <math.h>
#include <complex.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/*****************************************************************************/

#include "ladspa.h"

/*****************************************************************************/

/* The port numbers for the plugin: */

#define AW_FREQ       0
#define AW_STARTPHASE 1
#define AW_FEEDBACK   2
#define AW_DELAY      3
#define AW_INPUT1     4
#define AW_OUTPUT1    5
#define AW_INPUT2     6
#define AW_OUTPUT2    7

/*****************************************************************************/
/* Make number of samples represented by 'delay' proportional to
 * the sample rate, such that delay=1 is 1 sample buffer at
 * 44100hz
 */
#define NORM (1.0 / 44100.0)
#define PI (3.141592653589)
/*****************************************************************************/

class AW {
private:

// should this be a constant?
#define LFO_SKIPSAMPLES 25

// TODO promote all floats to double, even tho ladspa is floaty?
/* Ports */
LADSPA_Data  lfreq;
LADSPA_Data  lstartphase;
LADSPA_Data  lfeedback;
LADSPA_Data  ldelay;
LADSPA_Data * i_buf;
LADSPA_Data * o_buf;
LADSPA_Data * i_buf2;
LADSPA_Data * o_buf2;

/* State */
bool inited;
unsigned long samplerate;
unsigned long t;               //??
unsigned long t2;              //??
unsigned long k;               // index for delaybuf
unsigned long k2;              // index for delaybuf2
float_complex * delaybuf;
float_complex * delaybuf2;
float_complex c;               //??
float_complex c2;              //??
float freq;
float startphase;
float feedback;
unsigned int delay;

/*
 * class constructor
 */
AW(const long lSampleRate) :
	inited(false), 
	samplerate(lSampleRate),
	t(0), t2(0),
	k(0), k2(0),
	c(float_complex(0,0)),
	c2(float_complex(0,0)) {
}

/*
 * simply calls the constructor
 */
friend LADSPA_Handle instantiateAW(const LADSPA_Descriptor *,
	unsigned long SampleRate) {

  return new AW(SampleRate);
}

/*
 * get all the pointers to our ports data 
 */
friend void connectPortToAW(LADSPA_Handle instance, unsigned long port,
	LADSPA_Data * datalocation) {

	switch (port) {
	case AW_FREQ:
		((AW *)instance)->lfreq = *datalocation;
		break;
	case AW_STARTPHASE:
		((AW *)instance)->lstartphase = *datalocation;
		break;
	case AW_FEEDBACK:
		((AW *)instance)->lfeedback = *datalocation;
		break;
	case AW_DELAY:
		((AW *)instance)->ldelay = *datalocation;
		break;
	case AW_INPUT1:
		((AW *)instance)->i_buf = datalocation;
	case AW_OUTPUT1:
		((AW*)instance)->o_buf  = datalocation;
	case AW_INPUT2:
		((AW *)instance)->i_buf2 = datalocation;
	case AW_OUTPUT2:
		((AW*)instance)->o_buf2  = datalocation;
	}
}

/*
 * connect_port may be called before of after here, so we
 * cannot rely upon port data for initialization
 */
friend void activateAW(void * pvHandle) {
}

/*
 * Munge some things based upon the settings passed. Set
 * initial state.
 */
void initState(int chans) {
	inited = true;
	freq = (float)lfreq;
	feedback = ((float)lfeedback)/4 + 0.74; // whyfor?
	if (feedback>0.999) feedback=0.999;
	if (ldelay < 0) ldelay = 1;
	// swh I think this is wrong delay = (unsigned int) (ldelay * samplerate * NORM);
	delay = (unsigned int) ldelay;
printf("delay %d\n", delay);
	if (delay < 1) delay = 1;
	if (delay > MAX_DELAY) delay = MAX_DELAY;
	delaybuf = new float_complex[delay];
	if (chans == 2) {
		delaybuf2 = new float_complex[MAX_DELAY+1];
	}
	for (unsigned int i =0; i<delay; ++i) {
		delaybuf[i] = float_complex(0,0);
	}
}

/*
 * Mono effect
 * Do the effect. 'i_buf' is transformed into 'o_buf'
 */
friend void runAW_Mono(LADSPA_Handle instance, unsigned long samplecount) {
	AW * me = (AW *)instance;
	float lfo;
	float_complex outc;
	float lfoskip = me->freq * 2 * PI / me->samplerate;

	if (! me->inited) me->initState(1);

	for(unsigned int i=0; i<samplecount; ++i) {
		if ((me->t++ % LFO_SKIPSAMPLES) == 0) {
			lfo = 1 + cos(me->t * lfoskip + me->startphase);
			me->c = float_complex(cos(lfo) * me->feedback,
				 sin(lfo) * me->feedback);
		}
		outc = me->c * me->delaybuf[me->k] + (1 - me->feedback) * 
			me->i_buf[i];
		me->delaybuf[me->k] = outc;
		if (++(me->k) >= me->delay) me->k=0;
		me->o_buf[i] = real(outc)*3;
	}
}

/*
 * Stereo effect?
 */
friend void runAW_Stereo(LADSPA_Handle instance, unsigned long samplecount) {
	AW * me = (AW *)instance;
	float lfo;
	float_complex outc;
	float lfoskip = me->freq * 2 * PI / me->samplerate;

	if (! me->inited) me->initState(2);

	for(unsigned int i=0; i<samplecount; ++i) {
		if ((me->t++ % LFO_SKIPSAMPLES) == 0) {
			lfo = 1 + cos(me->t * lfoskip + me->startphase);
			me->c = float_complex(cos(lfo) * me->feedback,
				 sin(lfo) * me->feedback);
		}
		outc = me->c * me->delaybuf[me->k] + (1 - me->feedback) * 
			me->i_buf[i];
		me->delaybuf[me->k] = outc;
		if (++(me->k) >= me->delay) me->k=0;
		me->o_buf[i] = real(outc)*3;
	}

	for(unsigned int i=0; i<samplecount; ++i) {
		if ((me->t2++ % LFO_SKIPSAMPLES) == 0) {
			lfo = 1 + cos(me->t2 * lfoskip);
			me->c2 = float_complex(cos(lfo) * me->feedback,
				 sin(lfo) * me->feedback);
		}
		outc = me->c2 * me->delaybuf2[me->k2] + (1 - me->feedback) * 
			me->i_buf2[i];
		me->delaybuf2[me->k2] = outc;
		if (++(me->k2) >= me->delay) me->k2=0;
		me->o_buf2[i] = real(outc)*3;
	}
}


friend void cleanupAW(void *pvHandle) {
  delete (AW *)pvHandle;
}

};

/*****************************************************************************/

typedef char * char_ptr;

LADSPA_Descriptor * desc[2] = { NULL, NULL };

/*****************************************************************************/

/* Global object used handle startup initialisation and shut down
 tidying. Performs the function of the _init() and _fini() calls in
 the C modules. */
class StartupShutdownHandler {
public:

StartupShutdownHandler() {

  char ** pnames = NULL;
  LADSPA_PortDescriptor * portdesc = NULL;
  LADSPA_PortRangeHint * rangehints = NULL;
  
  for (long plug = 0; plug < 2; plug++) {
    
    desc[plug] = new LADSPA_Descriptor;
    if (desc[plug] == NULL)
	break;
    
/* generic */
    desc[plug]->UniqueID
	= 1561 + plug;
    desc[plug]->Properties
	= LADSPA_PROPERTY_HARD_RT_CAPABLE;
    desc[plug]->Maker
	= strdup("Plugin: Paul<set@pobox.com> Effect: Nasca O. Paul<paulnasca@yahoo.com>");
    desc[plug]->Copyright
	= strdup("GPL");
    desc[plug]->instantiate
	= instantiateAW;
    desc[plug]->connect_port 
	= connectPortToAW;
    desc[plug]->activate
	= activateAW;
    desc[plug]->run_adding
	= NULL;
    desc[plug]->set_run_adding_gain
	= NULL;
    desc[plug]->deactivate
	= NULL;
    desc[plug]->cleanup
	= cleanupAW;
    
/* specific */
    switch (plug) {
    case 0: /* Mono */
	desc[plug]->Label
	  = strdup("alienwah_mono");
	desc[plug]->Name 
	  = strdup("AlienWah for mono");
	desc[plug]->run 
	  = runAW_Mono;
    desc[plug]->PortCount 
	= 6;
    portdesc
	= new LADSPA_PortDescriptor[6];
    desc[plug]->PortDescriptors 
	= (const LADSPA_PortDescriptor *)portdesc;
    portdesc[AW_FREQ]
        = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    portdesc[AW_STARTPHASE]
        = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    portdesc[AW_FEEDBACK]
        = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    portdesc[AW_DELAY]
        = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    portdesc[AW_INPUT1]
	= LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
    portdesc[AW_OUTPUT1]
	= LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
    pnames
	= new char_ptr[6];
    desc[plug]->PortNames
	= (const char **)pnames;
    pnames[AW_FREQ]
	= strdup("Frequency (Hz)");
    pnames[AW_STARTPHASE] 
	= strdup("Initial phase for stereo (radians)");
    pnames[AW_FEEDBACK]
	= strdup("Feedback");
    pnames[AW_DELAY]
	= strdup("Delay (samples)");
    pnames[AW_INPUT1]
	= strdup("Input");
    pnames[AW_OUTPUT1]
	= strdup("Output");

/* range hints */
    rangehints 
	= new LADSPA_PortRangeHint[6];
    desc[plug]->PortRangeHints
	= (const LADSPA_PortRangeHint *)rangehints;

    rangehints[AW_FREQ].HintDescriptor
	= LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_SAMPLE_RATE;
    rangehints[AW_FREQ].LowerBound 
	= 0;

    rangehints[AW_STARTPHASE].HintDescriptor = 0;

    rangehints[AW_FEEDBACK].HintDescriptor
	= LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE;
    rangehints[AW_FEEDBACK].LowerBound
      = 0;
    rangehints[AW_FEEDBACK].UpperBound
      = 1.0;

    rangehints[AW_DELAY].HintDescriptor
	= LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE |
		LADSPA_HINT_INTEGER;
    rangehints[AW_DELAY].LowerBound
      = 5;
    rangehints[AW_DELAY].UpperBound
      = 50;

    rangehints[AW_INPUT1].HintDescriptor = 0;
    rangehints[AW_INPUT2].HintDescriptor = 0;
	break;

    case 1: /* Stereo */
	desc[plug]->Label
	  = strdup("alienwah_stereo");
	desc[plug]->Name 
	  = strdup("AlienWah for stereo");
	desc[plug]->run 
	  = runAW_Stereo;

    desc[plug]->PortCount 
	= 8;
    portdesc
	= new LADSPA_PortDescriptor[8];
    desc[plug]->PortDescriptors 
	= (const LADSPA_PortDescriptor *)portdesc;
    portdesc[AW_FREQ]
        = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    portdesc[AW_STARTPHASE]
        = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    portdesc[AW_FEEDBACK]
        = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    portdesc[AW_DELAY]
        = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL;
    portdesc[AW_INPUT2]
	= LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
    portdesc[AW_OUTPUT2]
	= LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
    portdesc[AW_INPUT1]
	= LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO;
    portdesc[AW_OUTPUT1]
	= LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO;
    pnames
	= new char_ptr[8];
    desc[plug]->PortNames
	= (const char **)pnames;
    pnames[AW_FREQ]
	= strdup("Frequency (Hz)");
    pnames[AW_STARTPHASE] 
	= strdup("Initial phase for stereo (radians)");
    pnames[AW_FEEDBACK]
	= strdup("Feedback");
    pnames[AW_DELAY]
	= strdup("Delay (samples)");
    pnames[AW_INPUT1]
	= strdup("Input1");
    pnames[AW_OUTPUT1]
	= strdup("Output1");
    pnames[AW_INPUT2]
	= strdup("Input2");
    pnames[AW_OUTPUT2]
	= strdup("Output2");

/* range hints */
    rangehints 
	= new LADSPA_PortRangeHint[8];
    desc[plug]->PortRangeHints
	= (const LADSPA_PortRangeHint *)rangehints;

    rangehints[AW_FREQ].HintDescriptor
	= LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_SAMPLE_RATE;
    rangehints[AW_FREQ].LowerBound 
	= 0;

    rangehints[AW_STARTPHASE].HintDescriptor
        = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE;
    rangehints[AW_STARTPHASE].LowerBound
        = 0;
    rangehints[AW_STARTPHASE].LowerBound
        = PI * 2.0;

    rangehints[AW_FEEDBACK].HintDescriptor
	= LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE;
    rangehints[AW_FEEDBACK].LowerBound
      = 0;
    rangehints[AW_FEEDBACK].UpperBound
      = 2.0;

    rangehints[AW_DELAY].HintDescriptor
	= LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE |
		LADSPA_HINT_INTEGER;
    rangehints[AW_DELAY].LowerBound
      = 5;
    rangehints[AW_DELAY].UpperBound
      = 50;

    rangehints[AW_INPUT1].HintDescriptor = 0;
    rangehints[AW_INPUT2].HintDescriptor = 0;
    rangehints[AW_OUTPUT1].HintDescriptor = 0;
    rangehints[AW_OUTPUT2].HintDescriptor = 0;
	break;
    }

  }
}

void deleteDescriptor(LADSPA_Descriptor * psDescriptor) {
  unsigned long lIndex;
  if (psDescriptor) {
    delete [] psDescriptor->Label;
    delete [] psDescriptor->Name;
    delete [] psDescriptor->Maker;
    delete [] psDescriptor->Copyright;
    delete [] psDescriptor->PortDescriptors;
    for (lIndex = 0; lIndex < psDescriptor->PortCount; lIndex++)
	delete [] psDescriptor->PortNames[lIndex];
    delete [] psDescriptor->PortNames;
    delete [] psDescriptor->PortRangeHints;
    delete psDescriptor;
  }
}

~StartupShutdownHandler() {
  deleteDescriptor(desc[0]);
  deleteDescriptor(desc[1]);
}

} g_oShutdownStartupHandler;

/*****************************************************************************/

const LADSPA_Descriptor * 
ladspa_descriptor(unsigned long Index) {
if (Index < 2)
  return desc[Index];
else
  return NULL;
}

/*****************************************************************************/

/* EOF */
