/************************************************************************
 ************************************************************************
  	FAUST library file
	Copyright (C) 2003-2011 GRAME, Centre National de Creation Musicale
    ---------------------------------------------------------------------
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as 
	published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
 	License along with the GNU C Library; if not, write to the Free
  	Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
  	02111-1307 USA. 
 ************************************************************************
 ************************************************************************/

declare name "Music Library";
declare author "GRAME";
declare copyright "GRAME";
declare version "1.0";
declare license "LGPL"; 

import("math.lib");

//-----------------------------------------------
// 					DELAY LINE
//-----------------------------------------------
frac(n)                 = n-int(n);
index(n)                = &(n-1) ~ +(1);                // n = 2**i
//delay(n,d,x)  = rwtable(n, 0.0, index(n), x, (index(n)-int(d)) & (n-1));
delay(n,d,x)    = x@(int(d)&(n-1));
fdelay(n,d,x)   = delay(n,int(d),x)*(1 - frac(d)) + delay(n,int(d)+1,x)*frac(d);


delay1s(d)		= delay(65536,d);
delay2s(d)		= delay(131072,d);
delay5s(d)		= delay(262144,d);
delay10s(d)		= delay(524288,d);
delay21s(d)		= delay(1048576,d);
delay43s(d)		= delay(2097152,d);

fdelay1s(d)		= fdelay(65536,d);
fdelay2s(d)		= fdelay(131072,d);
fdelay5s(d)		= fdelay(262144,d);
fdelay10s(d)	= fdelay(524288,d);
fdelay21s(d)	= fdelay(1048576,d);
fdelay43s(d)	= fdelay(2097152,d);

millisec	= SR/1000.0;

time1s 	= hslider("time", 0, 0,  1000, 0.1)*millisec;
time2s 	= hslider("time", 0, 0,  2000, 0.1)*millisec;
time5s 	= hslider("time", 0, 0,  5000, 0.1)*millisec;
time10s = hslider("time", 0, 0, 10000, 0.1)*millisec;
time21s = hslider("time", 0, 0, 21000, 0.1)*millisec;
time43s = hslider("time", 0, 0, 43000, 0.1)*millisec;


echo1s  = vgroup("echo  1000", +~(delay(65536,   int(hslider("millisecond", 0, 0,	1000, 0.10)*millisec)-1) * (hslider("feedback", 0, 0,  100, 0.1)/100.0)));
echo2s  = vgroup("echo  2000", +~(delay(131072,  int(hslider("millisecond", 0, 0,	2000, 0.25)*millisec)-1) * (hslider("feedback", 0, 0,  100, 0.1)/100.0)));
echo5s  = vgroup("echo  5000", +~(delay(262144,  int(hslider("millisecond", 0, 0,	5000, 0.50)*millisec)-1) * (hslider("feedback", 0, 0,  100, 0.1)/100.0)));
echo10s = vgroup("echo 10000", +~(delay(524288,  int(hslider("millisecond", 0, 0,  10000, 1.00)*millisec)-1) * (hslider("feedback", 0, 0,  100, 0.1)/100.0)));
echo21s = vgroup("echo 21000", +~(delay(1048576, int(hslider("millisecond", 0, 0,  21000, 1.00)*millisec)-1) * (hslider("feedback", 0, 0,  100, 0.1)/100.0)));
echo43s = vgroup("echo 43000", +~(delay(2097152, int(hslider("millisecond", 0, 0,  43000, 1.00)*millisec)-1) * (hslider("feedback", 0, 0,  100, 0.1)/100.0)));


//--------------------------sdelay(N,it,dt)----------------------------
// s(mooth)delay : a mono delay that doesn't click and doesn't 
// transpose when the delay time is changed. It takes 4 input signals 
// and produces a delayed output signal
//
// USAGE : 	... : sdelay(N,it,dt) : ...
//
// Where :
//	<N>  = maximal delay in samples (must be a constant power of 2, for example 65536)
//	<it> = interpolation time (in samples) for example 1024
//	<dt> = delay time (in samples)
//  <  > = input signal we want to delay
//--------------------------------------------------------------------------

sdelay(N, it, dt) = ctrl(it,dt),_ : ddi(N)

	with {

		//---------------------------ddi(N,i,d0,d1)-------------------------------
		//	DDI (Double Delay with Interpolation) : the input signal is sent to two
		//	delay lines. The outputs of these delay lines are crossfaded with 
		//	an interpolation stage. By acting on this interpolation value one 
		//	can move smoothly from one delay to another. When <i> is 0 we can 
		//	freely change the delay time <d1> of line 1, when it is 1 we can freely change
		//	the delay time <d0> of line 0.
		//
		//	<N>  = maximal delay in samples (must be a power of 2, for example 65536)
		//	<i>  = interpolation value between 0 and 1 used to crossfade the outputs of the 
		//		   two delay lines (0.0: first delay line, 1.0: second delay line)
		//	<d0> = delay time of delay line 0 in samples between 0 and <N>-1
		//	<d1> = delay time of delay line 1 in samples between 0 and <N>-1
		//  <  > = the input signal we want to delay
		//-------------------------------------------------------------------------
		ddi(N, i, d0, d1) = _ <: delay(N,d0), delay(N,d1) : interpolate(i);


		//----------------------------ctrl(it,dt)------------------------------------
		// 	Control logic for a Double Delay with Interpolation according to two 
		//
		//	USAGE : ctrl(it,dt)
		//  where : 
		//	<it> an interpolation time (in samples, for example 256)
		//	<dt> a delay time (in samples)
		//
		//	ctrl produces 3 outputs : an interpolation value <i> and two delay 
		//	times <d0> and <d1>. These signals are used to control a ddi (Double Delay with Interpolation). 
		//	The principle is to detect changes in the input delay time dt, then to 
		//	change the delay time of the delay line currently unused and then by a
		//	smooth crossfade to remove the first delay line and activate the second one.
		//
		//	The control logic has an internal state controlled by 4 elements
		//	<v> : the interpolation variation (0, 1/it, -1/it)
		//	<i> : the interpolation value (between 0 and 1)
		//	<d0>: the delay time of line 0
		//	<d1>: the delay time of line 1
		//
		//	Please note that the last stage (!,_,_,_) cut <v> because it is only 
		//	used internally.
		//-------------------------------------------------------------------------
		ctrl(it, dt) = \(v,ip,d0,d1).( (nv, nip, nd0, nd1) 
			with {

				// interpolation variation
				nv = if (v!=0.0, 							// if variation we are interpolating
						if( (ip>0.0) & (ip<1.0), v , 0),	// 		should we continue or not ?
					 if ((ip==0.0) & (dt!=d0),  1.0/it,		// if true xfade from dl0 to dl1
					 if ((ip==1.0) & (dt!=d1), -1.0/it,		// if true xfade from dl1 to dl0	
					 0)));									// nothing to change
				// interpolation value
				nip = ip+nv : min(1.0) : max(0.0);

				// update delay time of line 0 if needed
				nd0 = if ((ip >= 1.0) & (d1!=dt), dt, d0);

				// update delay time of line 0 if needed
				nd1 = if ((ip <= 0.0) & (d0!=dt), dt, d1);

			} ) ~ (_,_,_,_) : (!,_,_,_);
	};




//-----------------------------------------------
// 			Tempo, beats and pulses
//-----------------------------------------------

tempo(t) 	= (60*SR)/t;			// tempo(t) -> samples

period(p) 	= %(int(p))~+(1);		// signal en dent de scie de periode p
pulse(t) 	= period(t)==0;			// pulse (10000...) de periode p
pulsen(n,t) = period(t)<n;			// pulse (1110000...) de taille n et de periode p
beat(t) 	= pulse(tempo(t));		// pulse au tempo t



//-----------------------------------------------
// 	conversions between db and linear values
//-----------------------------------------------

db2linear(x)	= pow(10, x/20.0);
linear2db(x)	= 20*log10(x);





//===============================================
// 			Random and Noise generators
//===============================================


//-----------------------------------------------
// 			noise : Noise generator
//-----------------------------------------------

random 		= +(12345) ~ *(1103515245);
RANDMAX		= 2147483647.0;

noise 		= random / RANDMAX;


//-----------------------------------------------
// Generates multiple decorrelated random numbers 
// in parallel. Expects n>0.
//-----------------------------------------------

multirandom(n) = randomize(n) ~_
with {
	randomize (1) 	= +(12345) : *(1103515245);
	randomize (n) 	= randomize(1) <: randomize(n-1),_;
};


//-----------------------------------------------
// Generates multiple decorrelated noises
// in parallel. Expects n>0.
//-----------------------------------------------

multinoise(n) = multirandom(n) : par(i,n,/(RANDMAX)) 
with { 
	RANDMAX = 2147483647.0; 
};


//------------------------------------------------

noises(N,i) = multinoise(N) : selector(i,N);
 

//-----------------------------------------------
// 			osc(freq) : Sinusoidal Oscillator
//-----------------------------------------------

tablesize 	= 1 << 16;
samplingfreq	= SR;

time 		= (+(1)~_ ) - 1; 			// 0,1,2,3,...
sinwaveform 	= float(time)*(2.0*PI)/float(tablesize) : sin;

decimal(x)	= x - floor(x);
phase(freq) 	= freq/float(samplingfreq) : (+ : decimal) ~ _ : *(float(tablesize));
osc(freq)	= rdtable(tablesize, sinwaveform, int(phase(freq)) );
osci(freq)	= s1 + d * (s2 - s1)
		with {
			i = int(phase(freq));
			d = decimal(phase(freq));
			s1 = rdtable(tablesize+1,sinwaveform,i);
			s2 = rdtable(tablesize+1,sinwaveform,i+1);};


//-----------------------------------------------
// 			ADSR envelop
//-----------------------------------------------

// a,d,s,r = attack (#samples), decay (sec), sustain (percentage), release (sec)
// t       = trigger signal

adsr(a,d,s,r,t) = env ~ (_,_) : (!,_) // the 2 'state' signals are fed back
with {
    env (p2,y) =
        (t>0) & (p2|(y>=1)),          // p2 = decay-sustain phase
        (y + p1*u - (p2&(y>s))*v*y - p3*w*y)	// y  = envelop signal
	*((p3==0)|(y>=eps)) // cut off tails to prevent denormals
    with {
	p1 = (p2==0) & (t>0) & (y<1);         // p1 = attack phase
	p3 = (t<=0) & (y>0);                  // p3 = release phase
	// #samples in attack, decay, release, must be >0
	na = SR*a+(a==0.0); nd = SR*d+(d==0.0); nr = SR*r+(r==0.0);
	// correct zero sustain level
	z = s+(s==0.0)*db2linear(-60);
	// attack, decay and (-60dB) release rates
	u = 1/na; v = 1-pow(z, 1/nd); w = 1-1/pow(z*db2linear(60), 1/nr);
	// values below this threshold are considered zero in the release phase
	eps = db2linear(-120);
    };
};


//-----------------------------------------------
// 			Spatialisation
//-----------------------------------------------

panner(c) = _ <: *(1-c), *(c);

bus2 = _,_;
bus3 = _,_,_;
bus4 = _,_,_,_;
bus5 = _,_,_,_,_;
bus6 = _,_,_,_,_,_;
bus7 = _,_,_,_,_,_,_;
bus8 = _,_,_,_,_,_,_,_;

gain2(g) = *(g),*(g);
gain3(g) = *(g),*(g),*(g);
gain4(g) = *(g),*(g),*(g),*(g);
gain5(g) = *(g),*(g),*(g),*(g),*(g);
gain6(g) = *(g),*(g),*(g),*(g),*(g),*(g);
gain7(g) = *(g),*(g),*(g),*(g),*(g),*(g),*(g);
gain8(g) = *(g),*(g),*(g),*(g),*(g),*(g),*(g),*(g);


//------------------------------------------------------
//
// 					    GMEM SPAT
//	n-outputs spatializer
//	implementation of L. Pottier 
//
//------------------------------------------------------
// 
//  n = number of outputs
//	r = rotation (between 0 et 1)
// 	d = distance of the source (between 0 et 1)
//
//------------------------------------------------------
spat(n,a,d)	= _ <: par(i, n, *( scaler(i, n, a, d) : smooth(0.9999) ))
	with {
		scaler(i,n,a,d) = (d/2.0+0.5) 
						* sqrt( max(0.0, 1.0 - abs(fmod(a+0.5+float(n-i)/n, 1.0) - 0.5) * n * d) );
		smooth(c) = *(1-c) : +~*(c);
	};



//--------------- Second Order Generic Transfert Function -------------------------
// TF2(b0,b1,b2,a1,a2)
//
//---------------------------------------------------------------------------------

TF2(b0,b1,b2,a1,a2) = sub ~ conv2(a1,a2) : conv3(b0,b1,b2)
	with {
		conv3(k0,k1,k2,x) 	= k0*x + k1*x' + k2*x'';
		conv2(k0,k1,x) 		= k0*x + k1*x';
		sub(x,y)			= y-x;
	};


/*************************** Break Point Functions ***************************

bpf is an environment (a group of related definitions) tha can be used to 
create break-point functions. It contains three functions : 
  - start(x,y) to start a break-point function
  - end(x,y) to end a break-point function
  - point(x,y) to add intermediate points to a break-point function

A minimal break-point function must contain at least a start and an end point :

  f = bpf.start(x0,y0) : bpf.end(x1,y1);

A more involved break-point function can contains any number of intermediate 
points

  f = bpf.start(x0,y0) : bpf.point(x1,y1) : bpf.point(x2,y2) : bpf.end(x3,y3);

In any case the x_{i} must be in increasing order (for all i, x_{i} < x_{i+1})

For example the following definition :

  f = bpf.start(x0,y0) : ... : bpf.point(xi,yi) : ... : bpf.end(xn,yn);

implements a break-point function f such that :

  f(x) = y_{0} when x < x_{0}
  f(x) = y_{n} when x > x_{n}
  f(x) = y_{i} + (y_{i+1}-y_{i})*(x-x_{i})/(x_{i+1}-x_{i}) when x_{i} <= x and x < x_{i+1} 

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

bpf = environment 
{
  // Start a break-point function
  start(x0,y0) = \(x).(x0,y0,x,y0);

  // Add a break-point
  point(x1,y1) = \(x0,y0,x,y).(x1, y1, x , if (x < x0, y, if (x < x1, y0 + (x-x0)*(y1-y0)/(x1-x0), y1)));

  // End a break-point function
  end  (x1,y1) = \(x0,y0,x,y).(if (x < x0, y, if (x < x1, y0 + (x-x0)*(y1-y0)/(x1-x0), y1)));

  // definition of if
  if (c,t,e) = select2(c,e,t);
};