// rte23.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "talkie.h"
#pragma warning (disable: 4244)
#pragma warning (disable: 4305)

namespace rte23
{


struct filter
{
	//Init 
//cutoff = cutoff freq in Hz 
//fs = sampling frequency //(e.g. 44100Hz) 
//res = resonance [0 - 1] //(minimum - maximum) 

	float f,k,p,scale,r,y4,y1,y2,y3,oldx,oldy1,oldy2,oldy3;

	void set(float cutoff, float res, float fs=44100.f)
	{
	
		f = 2 * cutoff / fs; //[0 - 1] 
		k = 3.6*f - 1.6*f*f -1; //(Empirical tunning) 
		p = (k+1)*0.5; 
		scale = exp((1-p)*1.386249); 
		r = res*scale; 
		
	}

	filter()
	{
		y1=y2=y3=y4=oldx=oldy1=oldy2=oldy3=0; 
	}
	

	void Run(short *out, int len, int pt = 1)
	{
		pt--;
		for (int i=0;i<len;i++)
		{
			float input = *out / 32767.f;

		
			//Loop 
			//--Inverted feed back for corner peaking 
			float x = input - r*y4; 

			//Four cascaded onepole filters (bilinear transform) 
			y1=x*p + oldx*p - k*y1; 
			y2=y1*p+oldy1*p - k*y2; 
			y3=y2*p+oldy2*p - k*y3; 
			y4=y3*p+oldy3*p - k*y4; 

			//Clipper band limited sigmoid 
			y4 = y4 - (y4*y4*y4)/6; 

			oldx = x; 
			oldy1 = y1; 
			oldy2 = y2; 
			oldy3 = y3;
			
			int o=y4*32767;
			o=bound(-32767,o,32767);
			*out++ = o;
			out+=pt;
		}
	}



};


struct gen
{
	double osct;
	float t;
	float a;
	float d;
	float s;
	float r;
	float vol;
	int down;
	float downt;
	double pitch;
	double dpitch;
	int type;
	filter myfilt;
	bool wah;
	float wahtime;

	gen()
	{
		memset(this,0,sizeof(*this));
		
		wah=0;
		a=0.01f;
		d=0.f;
		s=1.f;
		r=0.5f;


	}

	void SetPitch(double p, double dp=0)
	{
	
		pitch=p/44100.f;
		dpitch=dp;
		
	}

	void Trigger(float v, int hold=0)
	{
		vol=v;
		t=0;
		down=hold;
		downt=t+a+d;
	}

	void Release()
	{
		down=0;
		downt=t;
	}

	short MakeNoise(float lv)
	{
		if (lv<0) lv=0;
		if (lv>1) lv=1;
		lv*=vol;
		
		float oldosc= osct;
		t+=1.f/44100.f;
		double dd = pitch+dpitch*t;
		if (dd>0) osct+=dd;
		
		short o=0;
		if (int(oldosc)!=int(osct)) o=(1)*16384*lv;
		if (int(oldosc*2)!=int(osct*2)) o+=(-1)*8192*lv;
		if (int(oldosc*4)!=int(osct*4)) o+=(1)*4192*lv;

		

		switch (type)
		{
		case 44:
			return (osct-int(osct))*16384*lv;
		case 0:
			if (rand()<100) osct+=0.02;
			return o+short(16384*sin(osct*PI*2)*lv);
		case 1:
			return sin(osct*PI*2)*lv;
		case 2:
			if (rand()<1000) osct+=0.2;
			return ((int(osct)&1)*32768-16384)*lv;
			
		case 3:
			return (rand()-16384)*lv;
		}
		

		return 0;
	}

	void Run(short *out, int len)
	{
		while (len>0 && t<a)
		{
			*out++ += MakeNoise(t/a);
			len--;
		}
		if (!len) goto theend;
		while (len>0 && t<a+d)
		{
			*out++ += MakeNoise(1-((t-a)/d)*(1-s));
			len--;
		}
		if (!len) goto theend;
		if (down)
		{
			while (len>0)
			{
				*out++ += MakeNoise(s);
				len--;
			}
			goto theend;
		}
		while (len>0 && t<downt+r)
		{
			*out++ += MakeNoise((1-(t-downt)/r)*s);
			len--;
		}
		if (!len) goto theend;
		/*
		while (len>0)
		{
			*out++ += MakeNoise(0);
			len--;
		}
		*/
		out+=len;
		len=0;

theend:
		if (wah)
		{
			myfilt.set(pitch*44100.f*(0.7f+0.5f*cos(wahtime*1.1f)),sin(wahtime)*0.4f+0.2f);
			wahtime+=0.01f;
			myfilt.Run(out-len,len);
		}
	}
};


void mix(stereo *out, short *in, int len, float vol, float pan)
{
	pan=bound(-1,pan,1);
	float lv=vol,rv=vol;
	if (pan<0) rv*=pan+1;
	if (pan>0) lv*=1-pan;
	for (int i=0;i<len;i++)
	{
		int l=*in * lv + out->l;
		int r=*in * rv + out->r;
		out->l = bound(-32768,l,32767);
		out->r = bound(-32768,r,32767);
		in++;
		out++;
	}
}

void mix(stereo *out, stereo *in, int len, float vol)
{
	for (int i=0;i<len;i++)
	{
		int l=in->l * vol + out->l;
		int r=in->r * vol + out->r;
		out->l = bound(-32768,l,32767);
		out->r = bound(-32768,r,32767);
		in++;
		out++;
	}
}

void mono2stereo(stereo *out, short *in, int len, float vol, float pan, bool fip=false, int delay=0)
{
	pan=bound(-1,pan,1);
	float lv=vol,rv=vol;
	if (pan<0) rv*=pan+1;
	if (pan>0) lv*=1-pan;
	if (fip) rv=-rv;
	for (int i=0;i<len;i++)
	{
		int l=*in * lv ;
		int r=*in * rv ;
		out[(i+len-delay)%len].l = bound(-32768,l,32767);
		out[(i+delay)%len].r = bound(-32768,r,32767);
		in++;
		//out++;
	}
}

struct echo
{
	int lengthl,lengthr;
	float fb[2][2];	

	stereo buf[44100*8];
	int curl,curr;	

	echo()
	{
		curl=curr=0;
		lengthl=lengthr=1024;
		memset(buf,0,sizeof(buf));
		fb[0][0]=fb[1][1]=0.5f;
		fb[1][0]=fb[0][1]=0.2f;		
	}

	void SetLength(int ll, int lr)
	{
		if (ll>44100*8) ll =44100*8;
		if (lr>44100*8) lr =44100*8;		
		lengthl=ll;
		lengthr=lr;
	}

	void Run(stereo *in, int len, float wet=0.7f)
	{
		
		for (int i=0;i<len;i++)
		{
			curl%=lengthl;
			curr%=lengthr;

			int nl,nr;
			nl = buf[curl].l * fb[0][0] + buf[curr].r * fb[1][0] + in->l;
			nr = buf[curr].r * fb[1][1] + buf[curl].l * fb[0][1] + in->r;
			nl=bound(-32768,nl,32767);
			nr=bound(-32768,nr,32767);
			buf[curl].l=nl;
			buf[curr].r=nr;			
			in->l+=(nl-in->l) * wet;
			in->r+=(nr-in->r) * wet;
			curl++;
			curr++;
			in++;

		}
	}
};

#define MAXBUF (44100*4)

struct sample
{
	bool mono;
	bool reverse;
	union
	{
		stereo *data;
		short *mdata;
	};	
	int len;
	int cur;
	int running;

	sample()
	{
		data=NULL;
		reverse=false;
	}
	
	void Load(char *fname, bool xmono)
	{
		mono=xmono;
		FILE *f=fopen(fname,"rb");
		len=0;
		delete [] data;
		fseek(f,0,SEEK_END);
		len=ftell(f)/4;
		fseek(f,0,0);
		data=new stereo[len];
		fread(data,4,len,f);
		fclose(f);
	}

	void Trigger()
	{
		running=1;
		cur=0;
	}

	void Run(stereo *out, int ln, float vol)
	{
		if (!running) return;
		if (mono)
		{
			for (int i=0;i<ln;i++)
			{
				int nl=out->l + vol*mdata[cur];
				int nr=out->r + vol*mdata[cur];
				out->l = bound(-32768,nl,32767);
				out->r = bound(-32768,nr,32767);
				out++;
				cur++;
				if (cur>=len*2) 
				{
					cur=0;
					running=0;
					break;
				}
			}
		}
		else
		{
			if (reverse)
			{
			
				for (int i=0;i<ln;i++)
				{
					int nl=out->l + vol*data[len-1-cur].l;
					int nr=out->r + vol*data[len-1-cur].r;
					out->l = bound(-32768,nl,32767);
					out->r = bound(-32768,nr,32767);
					out++;
					cur++;
					if (cur==len) 
					{
						cur=0;
						running=0;
						break;
					}
				}
			}
			else
			{
				for (int i=0;i<ln;i++)
				{
					int nl=out->l + vol*data[cur].l;
					int nr=out->r + vol*data[cur].r;
					out->l = bound(-32768,nl,32767);
					out->r = bound(-32768,nr,32767);
					out++;
					cur++;
					if (cur==len) 
					{
						cur=0;
						running=0;
						break;
					}
				}
			}

		}
		
	}
};

struct messer
{
	int curpos;
	int bufhead,buftail,buflen;
	stereo buf[MAXBUF];
	int subpos;
	float speed;
	float pitch;
	int subsize;
	int ps;

	messer()
	{
		memset(this,0,sizeof(*this));
		speed=1;
		pitch=1;
	}

	void Run(stereo *out, stereo *in, int len, float wet)
	{
		subsize=len;
		
		if (buflen<MAXBUF-len) for (int c1=0;c1<len;c1++)
		{
			buf[buftail++] = *in++;
			buflen++;
			if (buftail==MAXBUF) buftail=0;
		}
		
		for (int c1=0;c1<len;c1++)
		{
			if (subpos==0 || subpos==subsize)
			{
				subpos=0;
				ps=bufhead;
				bufhead+=subsize*speed;
				if (bufhead>MAXBUF) bufhead-=MAXBUF;
				buflen-=subsize*speed;
				if (buflen<0) buflen=0;
			}
			stereo o = buf[int(ps+int(c1*pitch)%subsize)%MAXBUF];
			out->l += (o.r-out->l) * wet;
			out->r += (o.l-out->r) * wet;
			out++;

			subpos++;
			

		}

	}
};

#define SUBROWLEN 800
#define SPEED 4
stereo buf0[BLOCKSIZE*2];
stereo buf1[BLOCKSIZE*2];
stereo buf2[BLOCKSIZE*2];

short mbuf0[BLOCKSIZE*2];
short mbuf1[BLOCKSIZE*2];
short mbuf2[BLOCKSIZE*2];

//gen bd;
//gen zap;
gen mygen;
gen mygen3;
gen mygen2;
gen long1[2];
gen long2[2];

//sample zap;
#ifdef AUDIOVIDEO
sample soundscape;
#endif
sample tree;
sample spooky;
sample zaps[13];

filter myfilter;
filter myfilter4;
filter myfilter2;
filter myfilter3;
float mypan=0;
echo myecho;
echo myecho2;
echo zapecho;
messer mymesser;

void initsound()
{
	myecho.SetLength(SUBROWLEN*SPEED*6,SUBROWLEN*SPEED*8);
	myecho2.SetLength(SUBROWLEN*SPEED*4,SUBROWLEN*SPEED*4);
	zapecho.SetLength(23000,22000);
	
	zapecho.fb[0][0]=zapecho.fb[1][1]=0.45f;
	zapecho.fb[1][0]=zapecho.fb[0][1]=0.45f;
	mygen2.a=0.3f;
	mygen2.wah=true;
#ifdef AUDIOVIDEO
	soundscape.Load("soundscape.raw",false);
#endif
	tree.Load("tree.raw",false);
	spooky.Load("spooky.raw",false);
#ifdef AUDIOVIDEO
	soundscape.Trigger();
#endif
	for (int c1=0;c1<13;c1++)
	{
		char buf[256];
		sprintf(buf,"zaps/%d.raw",c1);
		zaps[c1].Load(buf,true);
	}
	

	//zap.Load("zap.raw");
	/*
	bd.SetPitch(200,-0.15f);	
	bd.a=0;
	bd.r=0.5f;
	bd.type=2;	
	
	zap.SetPitch(2200,-0.15f);	
	zap.a=0;
	zap.r=0.5f;
	zap.type=2;	
	*/

	myfilter4.set(800,0.9);
	myfilter2.set(1800,0.7);
	myfilter3.set(1800,0.7);

	
}


double getnote(int n)
{
	return pow(1.0594630943592952645618252949463,(n-60))*300.f;
}

int nicenote[8]={0,0,3,5,5,7,8,12};
int notelist[] = {7,0,2,3,8,7,2,3,2,7,0,8};
int notepos;
int octave=0;
int lengths[] =  {4,1,2,1,2,4,2,1,1,2+8};
int lengthpos;
int row;
float pan2;
#ifdef AUDIOVIDEO
Talkie talker;	
#endif
compressor comp;

void	CALLBACK FillSound(void *user, stereo *ste, int len)
{
	static int to=0;
	static int oldsub=-1;
	static int oldrow=-1;
	static int nexttime=10480;

	int t=to;
	if (t<0) t=2;

	int subrow=t/SUBROWLEN;
	row=subrow/SPEED;

	subrow%=SPEED;

						//0   1   2   3   4   5   6   7   0   1   2   3   4   5   6   7   .
	const char *riddim=  "x     x     x   x     x    x    x     x     x x x     x    x   x";

	memset(buf0,0,sizeof(buf0));

	
	static int toggle=0;
	if (oldsub!=subrow)
	{
		if (oldrow!=row)
		{
			int longprob=15;
			if (row>1500) longprob+=(row-1500);
			if (longprob>450) longprob=longprob=450;
			if (row==49 || ((row > 768 && (rand()&511)<longprob && long1[1-toggle].t > 12)))
			{				
				long1[toggle].a=4;
				long1[toggle].r=8;
				long1[toggle].wah=true;
				long1[toggle].Trigger(0.03f);				
				if (row>100)
				{
					nicenote[rand()&7]+=(rand()%5)-2;
				}
				int pp=60+10+2+nicenote[rand()&7]-12;
				long1[toggle].SetPitch(getnote(pp));
				long2[toggle].a=4;
				long2[toggle].r=8;
				long2[toggle].wah=true;
				long2[toggle].Trigger(0.03f);
				long2[toggle].SetPitch(getnote(pp-12));
				toggle=!toggle;
			}
			if ((row&255)==0 && row!=512 && thedoc->nonotes==0)
			{
				mygen3.a=4;
				mygen3.r=4;
				mygen3.Trigger(0.0052f);
				mygen3.SetPitch(getnote(60-24));
			}
			if (row>65) if (riddim[(row/4)&63]!=' ' && rand()<15000) 
			{
				
				int octavo=0;
				if (row>700 && rand()<row-700) octavo=12*((rand()%3)-2);
				int vv=0;
				int thenote = octavo + 60+10+12+(((rand()&7)==0)?2+nicenote[rand()&7]:rand()<16000?(vv=12+2-6-48):(vv=0));
				if (thenote != octavo + 60+10+12 || (rand()&7)==0)
				{
				
					mygen2.SetPitch(getnote(thenote));
					pan2=rand()/16384.f-1.f;
					if (thedoc->nonotes==0 || (rand()<20000)) if (vv==0) mygen2.Trigger(sin(t*0.000000081f)*0.05f);
				}
				//zap.Trigger(0.4f);			
				//zap.type=1;
				//zap.SetPitch(100,-1.f);
			}

			if (row==nexttime)
			{
				//if ((rand()&7)==0) myecho.curl=rand();
				//if ((rand()&7)==0) myecho.curr=rand();
				if ((rand()&15)==0) octave=(rand()%3)-1;
				
				mygen.SetPitch(getnote(60+12*octave+notelist[notepos]));
				octave=0;
				mypan=(rand()-16384)/20000.f;
				//myecho.fb[rand()&1][rand()&1]=rand()/50000.f;
				notepos=(notepos+1)%(sizeof(notelist)/4);
				if (thedoc->nonotes==0) mygen.Trigger((rand()/200000.f+0.000001f*rand())*0.25);
				nexttime+=lengths[lengthpos]*4;
				lengths[lengthpos]++;

				int rp1=rand()%(sizeof(notelist)/4);
				int rp2=rand()%(sizeof(notelist)/4);
				int t=notelist[rp1];notelist[rp1]=notelist[rp2];notelist[rp2]=t;

				if (rand() < 2000) notelist[rp1]+=(rand()%3)-1;
				
				lengthpos=(lengthpos+1)%(sizeof(lengths)/4);
			}
			
		}
	}

	oldrow=row;
	oldsub=subrow;
	
	memset(mbuf0,0,sizeof(mbuf0));
	memset(mbuf1,0,sizeof(mbuf0));
	memset(mbuf2,0,sizeof(mbuf0));

	int zargon = sin(t*0.000013f)*10*fabsf(sin(t*0.0000001f)*5);
	if (zargon<0) zargon=0;
	if (t<15000) zargon+=(15000-t)*bound(0,t/30000.f,1);
	
	//POINT pp;
	//GetCursorPos(&pp);
	//zargon+=pp.x*10;

	
	if (zargon) for (int c1=0;c1<len;c1++)
	{
		if (rand()<zargon) mbuf0[c1]=rand();
	}

	myfilter4.Run((short*)mbuf0,len);
	if (0) for (int c1=0;c1<len;c1++)
	{
		buf0[c1].l*=rand();
		buf0[c1].r*=rand();
	}

	if (rand() < 262)
	{
		myfilter4.set((rand()%800)+400,0.9);
	}
	

	mygen.Run(mbuf0,len);
	mygen2.Run(mbuf2,len);
	mygen3.Run(mbuf0,len);
	myfilter.set(sin(t*0.0000031f)*1700+2000,cos(0.35+t*0.00042f)*0.43+0.4);
	myfilter.Run(mbuf0,len);				
	mono2stereo(buf0,mbuf0,len,1,mypan);
	mono2stereo(buf2,mbuf2,len,1,pan2);
	mix(buf0,buf2,len,0.8f);
	memset(mbuf2,0,sizeof(mbuf2));
	memset(buf2,0,sizeof(buf2));
		
	
	myecho.Run(buf0,len);
	
	//bd.Run(mbuf2,len);
	//if (GetAsyncKeyState(VK_F1)<0) mix(buf0,mbuf2,len,1.5f,0); // bd	
	
	mymesser.Run(buf1,buf0,len,0.8f);
	myfilter2.Run(((short*)buf1),len,2);
	myfilter3.Run(((short*)buf1)+1,len,2);
	
	mix(buf1,buf0,len,sin(t*0.000001f+24)*4);
	myecho2.Run(buf1,len,0.3f);


	//zap.Run(mbuf1,len);	
	mix(buf1,mbuf1,len,0.5f,0); // bd	

	
	mymesser.speed=-cos(t*0.00001f)*0.4f+0.9f;
	mymesser.pitch=(rand()&1)?2.f:4.f;
		

	memset(mbuf2,0,sizeof(mbuf2));
	for (int tt=0;tt<2;tt++)
	{
		long1[tt].Run(mbuf2,len);
		long2[tt].Run(mbuf2,len);
	}

	mono2stereo(buf2,mbuf2,len,1,-mypan,true);
	mix(buf1,buf2,len,0.2f);

	
	if (0) if ((rand()&127)==0)
	{
		int r=rand()%mymesser.buflen;
		mymesser.bufhead+=r;
		if (mymesser.bufhead>MAXBUF) mymesser.bufhead-=MAXBUF;
		mymesser.buflen-=r;
	}
	

	//if (GetAsyncKeyState(VK_SPACE)&1) zap.Trigger();
	//zap.Run(buf1,len,0.7f);

	


	//memset(buf1)
	
	
	
	spooky.Run(buf1,len,0.3f);
	tree.Run(buf1,len,0.4f);
#ifdef AUDIOVIDEO
	soundscape.Run(buf1,len, 0.03f);
#endif
	if ((thedoc->pulsetrigger & 4) || (GetAsyncKeyState(VK_F11)&1))
	{
		thedoc->pulsetrigger &= ~4;
		//tree.Trigger();
		zaps[rand()%9].Trigger();
		if (rand()<10000)
		{
			zaps[(rand()%4)+9].Trigger();
		}
	}

	if (thedoc->pulsetrigger & 8)
	{
		thedoc->pulsetrigger &= ~8;
		tree.Trigger();

		nexttime = row + 64+8+32;
	}

	if (thedoc->pulsetrigger & 16)
	{
		thedoc->pulsetrigger &= ~16;
		spooky.reverse=0;
		if (row>50) spooky.Trigger();
	}
	if (thedoc->pulsetrigger & 32)
	{
		thedoc->pulsetrigger &= ~32;
		spooky.reverse=1;
		spooky.Trigger();
	}
	


	memset(buf2,0,sizeof(buf2));			
	float zapvol=0.3f;
	for (int c3=0;c3<13;c3++) zaps[c3].Run(buf2,len,c3<9 ? zapvol : zapvol*0.5f);
	zapecho.Run(buf2,len,0.4f);
	mix(buf1,buf2,len,0.1f);

	comp.Run(buf1,ste,len);

	float xmvol = thedoc->mvol;
	xmvol*=xmvol;
	xmvol=bound(0,xmvol,1);
	for (int c1=0;c1<len;c1++) 
	{
		ste[c1].l *= xmvol;
		ste[c1].r *= xmvol;
	}

	
	xmvol*=8;
	if (xmvol>1) xmvol=1;

	stereo buf[BLOCKSIZE*2];
	memset(buf,0,4*len);
#ifdef AUDIOVIDEO
	talker.ProcessSound(buf,(len*32000)/44100);
	for (c1=len-1;c1>=0;c1--) buf0[c1]=buf[(c1*32000)/44100];
#endif
	mix(ste,buf0,len,1.8f*xmvol);

	

	to+=len;
}

};


struct audiothing : public RenderThing
{
	virtual int Do(float time, float dt, stereo *audio, CThingDoc *doc) 
	{
		UpdateParams();
		if (time==0)
		{
#ifdef AUDIOVIDEO
			rte23::talker.ResetSound();
#endif
		}
		rte23::FillSound(NULL,audio,BLOCKSIZE);
		return 1;
	}

	void UpdateParams()
	{
		//POINT mp;
		//GetCursorPos(&mp);
		//rte23::talker.p.timestretch = 1 - mp.x/1600.f;
		//rte23::talker.p.balance = mp.y/1200.f;
		//rte23::talker.p.fxmix=0.5f;
		//rte23::talker.p.delaytime=400;
		//rte23::talker.p.feedback =0.5f;
#ifdef AUDIOVIDEO
		rte23::talker.Render();
#endif
	}

	audiothing() : RenderThing()
	{
		rte23::initsound();
#ifdef AUDIOVIDEO
		rte23::talker.ResetSound();
		rte23::talker.Render();
#endif
		

	}
};

extern  RenderThing *GetAudioProc();

RenderThing *GetAudioProc()
{
	static audiothing *at =NULL;
	if (!at) at = new audiothing;
	return at;
}

/*
void	CALLBACK TalkSound(void *user, stereo *ste, int len)
{
	

}

HWND fgw;

int main(int argc, char* argv[])
{
	printf("Hello World!\n");

	fgw = GetForegroundWindow();

	srand(GetTickCount());
	initsound();

	talker.ResetSound();
	talker.Render(1);

	SoundOut	mysound;
	mysound.fscb=FillSound;

	
	//mysound.fscb=TalkSound;
	mysound.Init();

	
	int tt=GetTickCount();
	while (1)
	{
		POINT mp;
		GetCursorPos(&mp);
		talker.p.timestretch = 1 - mp.x/1600.f;
		talker.p.balance = mp.y/1200.f;
		talker.p.fxmix=0.5f;
		talker.p.delaytime=400;
		talker.p.feedback =0.5f;
		talker.Render(1);

		printf("%8x %5d boost %10.2f%% time %8d clip %d\r",row,(GetTickCount()-tt)/1000,0.9f / (comp.curlevel*comp.curlevel), comp.holdtime/1000, comp.clip);
		flushall();
		if (kbhit())
		{
			switch (getch())
			{
			case 27:return 1;
			case 32:
				//for (int cc=0;cc<16;cc++) FillSound(NULL,ooo,44100);
				break;

			}
		}
	}
	
	return 0;
}

*/