#include "stdafx.h"
#include "talkie.h"

typedef unsigned char UBYTE;

////////////////////////// WORD

Word *Word::first=NULL;
int Word::total=0;

char printword[4096]="";

Word::Word()
{
	next=first;
	first=this;
	count=0;
	txt[0]=0;
	total++;
}

Word::~Word()
{
	total--;
	if (first==this) first=next; else
	{
		for (Word *w=first;w;w=w->next) if (w->next==this)
		{
			w->next=next;
			break;
		}
	}	
}

Word *Word::Add(char *t)
{
	for (Word *w=first;w;w=w->next)
	{
		if (strcmp(t,w->txt)==0)
		{
			w->count++;
			return w;
		}
	}
	w=new Word();
	w->count=1;
	strcpy(w->txt,t);
	return w;
}

////////////////////////// UWLIST

UWList *UWList::Add(UWList *head,UniqueWord *u,UniqueWord *a,UniqueWord *b)
{
	UWList *t=new UWList;
	t->next=head;
	t->uw=u;
	t->w1=a;
	t->w2=b;	
	head=t;
	return head;
}

////////////////////////// UNIQUE WORD

UniqueWord *UniqueWord::first=NULL;
int UniqueWord::total=0;

UniqueWord::UniqueWord()
{
	total++;
	next=first;
	first=this;
	txt[0]=0;
	word=NULL;
}

UniqueWord::~UniqueWord()
{
	total--;
	if (first==this) first=next; else
	{
		for (UniqueWord *w=first;w;w=w->next) if (w->next==this)
		{
			w->next=next;
			break;
		}
	}
}

UniqueWord*UniqueWord::Add(char *t, AudioTag *tg, AudioTag *tg2)
{
	UniqueWord *w=new UniqueWord;
	strcpy(w->txt,t);
	if (tg->len>40000 || tg->len<10)
	{
		Report3D("weird tag 1 %d %s\n",tg->len,tg->txt);
	}
	if (tg2->len>40000 || tg2->len<10)
	{
		Report3D("weird tag 2 %d %s\n",tg2->len,tg2->txt);
	}
	w->tag=tg;
	w->tag2=tg2;
	char nt[32];	
	memset(nt,0,sizeof(nt));
#ifdef MAKEUNIQUE 
	for (int c1=0;c1<(int)strlen(t);c1++) if (isalnum(t[c1]))
	{
		nt[strlen(nt)]=tolower(t[c1]);
	}
#else
	strcpy(nt,t);
#endif
	w->word=Word::Add(nt);
	return w;
}

////////////////////////// WORD PAIR

WordPair *WordPair::first=NULL;
int WordPair::total=0;

WordPair::WordPair()
{
	total++;
	next=first;
	first=this;	
	a=b=NULL;
	countfollow=0;
	follow=NULL;
}

WordPair::~WordPair()
{
	UWList *n;
	for (UWList *ll=follow;ll;ll=n)
	{
		n=ll->next;
		delete ll;
	}
	
	total--;
	if (first==this) first=next; else
	{
		for (WordPair *w=first;w;w=w->next) if (w->next==this)
		{
			w->next=next;
			break;
		}
	}
}

WordPair *WordPair::Find(UniqueWord *a,UniqueWord *b)
{
	Word *w1=a->word;
	Word *w2=b->word;
	for (WordPair *w=first;w;w=w->next)
	{
		if (w->a==w1 && w->b==w2)
		{
			return w;
		}
	}
	return NULL;
}

WordPair *WordPair::Add(UniqueWord *a,UniqueWord *b, UniqueWord *c)
{
	Word *w1=a->word;
	Word *w2=b->word;
	for (WordPair *w=first;w;w=w->next)
	{
		if (w->a==w1 && w->b==w2)
		{
			w->follow=UWList::Add(w->follow,c,a,b);
			w->countfollow++;
			return w;
		}
	}
	w=new WordPair;
	w->a=w1;
	w->b=w2;
	w->follow=UWList::Add(w->follow,c,a,b);		
	w->countfollow++;
	return w;

}

void RunMarkov(UniqueWord *&a,UniqueWord *&b,UniqueWord *&c)
{
	a=b;
	b=c;
	c=NULL;
	WordPair *wp=WordPair::Find(a,b);
	if (!wp) return;
	int tt=wp->countfollow;		
	if (!tt) return;
	UWList *uwl=NULL;
	if (tt==1) // choose the one that came before
	{
		for (uwl=wp->follow;uwl;uwl=uwl->next) if (uwl->w1==a && uwl->w2==b) break;			
	}
	if (!uwl) // pick a random one
	{
		int r=rand()%tt;
		if (tt>1 && (rand()&15)<4 && r==0) r=1+(rand()%(tt-1));
		for (uwl=wp->follow;uwl && r;uwl=uwl->next,r--) ;			
	}
	if (!uwl) return;
	c=uwl->uw;
}



//////////////////////////////////// WAV FILE TAG READING

//'cue' chunk
struct cue_point
{
	ULONG	id; 
	ULONG	start;
	FOURCC	temp1;
	ULONG	temp2;
	ULONG	temp3; 
	ULONG	temp4;
};

//'ltxt' chunk
struct cue_point_text
{
	ULONG	id; 
	ULONG	length;
	ULONG	temp1;
	WORD	temp2;
	WORD	temp3;
	WORD	temp4;
	WORD	temp5;
};


int Talkie::ReadWavTags(char *wavname, AudioTag **tag, bool sfl)
{	
	AudioTag *at=NULL;
	int curtag=0;
	*tag=at;
	if (wavname) 
	{
		/*
		MMIOINFO mmioinfo;
		memset(&mmioinfo,0,sizeof(mmioinfo));
		mmioinfo.fccIOProc=FOURCC_MEM;
		mmioinfo.cchBuffer=bufsize;
		mmioinfo.pchBuffer=(char*)wavptr;		
		HMMIO hMmioFile = mmioOpen(NULL, &mmioinfo, MMIO_READ);
		*/
		HMMIO hMmioFile = mmioOpen(wavname, NULL, MMIO_READ);
		
		WAVEFORMATEX wex;
		if(hMmioFile)
		{
			// Verify that file is a WAVE file.
			MMCKINFO		RiffChunk, ListChunk, SubChunk;
			RiffChunk.fccType = mmioFOURCC('W', 'A', 'V', 'E');
			
			if (mmioDescend(hMmioFile, &RiffChunk, NULL, MMIO_FINDRIFF))
			{
				
				mmioClose(hMmioFile, NULL);
				return 0;				
			}
			
			
			// Find the "fmt " chunk.
			SubChunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
			if (mmioDescend(hMmioFile, &SubChunk, &RiffChunk, MMIO_FINDCHUNK))
			{
				mmioClose(hMmioFile, NULL);
				return 0;
			}
			else
			{
				mmioRead(hMmioFile, (char *)&wex, sizeof(WAVEFORMATEX));
				wex.cbSize = 0;
			}
			//samplespersec=wave_format.nSamplesPerSec;
			mmioAscend(hMmioFile,&SubChunk,0);
			

			if (!sfl)
			{
			
				SubChunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
				if (!mmioDescend(hMmioFile, &SubChunk, &RiffChunk, MMIO_FINDCHUNK))
				{
					short *out=new short[SubChunk.cksize/2+65536];
					memset(out,0,(SubChunk.cksize/2+65536)*2);
					wavdataofs=mmioSeek(hMmioFile,0,SEEK_CUR);
					//mmioRead(hMmioFile, (char *)out, SubChunk.cksize);				
					wavdata=out;
					mmioAscend(hMmioFile,&SubChunk,0);	
				}
			}			
			//else
			{
			
			
				SubChunk.ckid = mmioFOURCC('c', 'u', 'e', ' ');
				if (!mmioDescend(hMmioFile, &SubChunk, &RiffChunk, MMIO_FINDCHUNK))
				{
					//there are cue_points
					ULONG number_of_cue_points;
					mmioRead(hMmioFile, (char *)&number_of_cue_points, sizeof(ULONG));
					//fill in the table (cue point structure * number of structures)
					struct cue_point *cue_point_info_table = (struct cue_point *)malloc(sizeof(struct cue_point) * number_of_cue_points); 
					int numtagalloc=number_of_cue_points;
					at=new AudioTag [numtagalloc];
					
					mmioRead(hMmioFile,(char *)cue_point_info_table,sizeof(struct cue_point) * number_of_cue_points);
					mmioAscend(hMmioFile,&SubChunk,0);
					ListChunk.fccType = mmioFOURCC('a', 'd', 't', 'l');
					if (!mmioDescend(hMmioFile, &ListChunk, NULL, MMIO_FINDLIST))
					{
						

						for (ULONG c1=1;c1<=number_of_cue_points;c1++)
						{
							//now check each cue point to see if it has the label "lhloop"
							//if it has then find the length from the ltxt chunk.
							
							SubChunk.ckid = mmioFOURCC('l', 'a', 'b', 'l');
							if (!mmioDescend(hMmioFile, &SubChunk, &ListChunk, MMIO_FINDCHUNK))
							{
								ULONG						cue_point_id;
								char						cue_name[512];

								mmioRead(hMmioFile,(char *)&cue_point_id,sizeof(ULONG));
								sprintf(cue_name,"");

								//build the cue name
								for(;;)
								{
									char character;
									mmioRead(hMmioFile,(char *)&character,sizeof(char));
									sprintf(cue_name,"%s%c",cue_name,character);

									if (character == 0)	//must be NULL termination
									{
										break;
									}
								}
								//LH_LOG1("found tag = %s - ",cue_name);							

								struct cue_point *cueptr=NULL;
								struct cue_point *cue_point_ptr=NULL;
								int ok=0;
								for (cue_point_ptr=cue_point_info_table;cue_point_ptr<cue_point_info_table+(number_of_cue_points);cue_point_ptr++)
								{
									if (cue_point_ptr->id == cue_point_id)
									{
										cueptr=cue_point_ptr;									
										break;
									}									
								}	
								
								if (cueptr)
								{
									//at[curtag].t=float(cueptr->start)/float(samplespersec);
									//char *cname=cue_name;
									while (cue_name[0] && cue_name[strlen(cue_name)-1]=='-') cue_name[strlen(cue_name)-1]=0;
									strcpy(at[curtag].txt,cue_name);
									at[curtag].pos=cueptr->start;
									curtag++;
								}					
								mmioAscend(hMmioFile,&SubChunk,0);							
							}						
						}
					}
				}
			}
			mmioClose(hMmioFile, NULL);
		}
		else
		{
			//LH_LOG("error opening wav memory file for emotion tags");
		}
	}
	*tag=at;
	return curtag;
}
			

////////////////////////////////////



int __cdecl audiotagcompare( const void *arg1, const void *arg2 )
{
   /* Compare all of both strings: */
   AudioTag *a1=(AudioTag*)arg1;
   AudioTag *a2=(AudioTag*)arg2;
   return a1->pos-a2->pos;
}




/*
void Talkie::FillSource(short *dst, int numout)
{
	if (delay) delaypos%=delay;
	float q=1-lowpass;

	
	

	for (int c1=0;c1<numout;c1++)
	{
		if (grain[0].curleft<=0 || grain[1].curleft<=0)
		{
			for (int g=0;g<2;g++)
			{
				grain[g].lastlevel=grain[g].curlevel;
				grain[g].lastptr=grain[g].curptr;
				grain[g].curlevel=0;
				grain[g].curptr=0;
				grain[g].curleft=10;
			}
			if (!cura || !curb || !curc)
			{
				cura=starta;
				curb=startb;
				curc=startc;
			}
			else
			{
				RunMarkov(cura,curb,curc);				
			}
			if (curc && curc->tag)
			{
				strcat(printword,curc->txt);
				strcat(printword," ");
				
				Report3D(printword);
				Report3D("\n");
				printword[0]=0;

				grain[0].curptr=curc->tag->pos;
				grain[0].curleft=curc->tag->len;				

				grain[1].curptr=curc->tag2->pos;
				grain[1].curleft=curc->tag2->len;				
				
				if (wavfile)
				{
					fseek(wavfile,grain[0].curptr*2,SEEK_SET);
					fread(wavdata+grain[0].curptr,2,grain[0].curleft,wavfile);
					fseek(wavfile,grain[1].curptr*2,SEEK_SET);
					fread(wavdata+grain[1].curptr,2,grain[1].curleft,wavfile);
				}
			}

			
			
		}
		int s=grain[0].Run() ;//+ grain[1].Run();

		if (wet>0.01f)
		{
			s>>=15;
			int ns=delaybuf[delaypos]*feedback;
			ns+=s;
			static int filt=0;
			filt+=(ns-filt)*q;
			delaybuf[delaypos++]=CLIP(filt);
			if (delaypos>=delay) delaypos=0;
			s+=(filt-s)*wet;			
			*dst++ = CLIP(s);
		}
		else
			*dst++ = s>>15;
	}
}
*/

float Time2Row(float t);
float Row2Time(float t);


void Talkie::Render()	
{

	Data d;
	float scpre[2],scpost[2];		
		
	float rowtime = Time2Row(thedoc->GetCurTime());
	
	d = thedoc->channels[TALKDELAY].GetVal(rowtime, &scpre[0], &scpost[0]);	p.delaytime = 1+d.f*500+p.delrand*4.f;
	d = thedoc->channels[TALKFB].GetVal(rowtime, &scpre[0], &scpost[0]);	p.feedback= d.f;
	d = thedoc->channels[TALKTS].GetVal(rowtime, &scpre[0], &scpost[0]);	p.timestretch= (d.f ) + 0.001f;
	d = thedoc->channels[TALKPITCH].GetVal(rowtime, &scpre[0], &scpost[0]);	p.pitch= d.f;
	d = thedoc->channels[TALKWET].GetVal(rowtime, &scpre[0], &scpost[0]);	p.fxmix = d.f;
	d = thedoc->channels[FACEBAL].GetVal(rowtime, &scpre[0], &scpost[0]);	p.balance = d.f;
	
	d = thedoc->channels[TALKNOJUMP].GetVal(rowtime, &scpre[0], &scpost[0]);	
	if (p.nojump!=d.f)
	{
		p.nojump= d.f;
		if (p.nojump==2)
		{
			cura=starta;
			curb=startb;
			curc=startc;
		}
	}
	


	
	wet=p.p[1];//str[1].sval;
	delay=bound(1,p.p[2]*44100/1000,44100);		
	feedback=p.p[3];
	lowpass=p.p[4];
	stretch=p.p[5];
	pitch=float(pow(1.0594630943592952645618252949463,p.p[6]));
	balance=p.balance;
	if (1) // mute?
	{
		//QMDX_SetVolume(hQMixer,ch,0,ed->IsPlaying()?32767*level:0);
		if (!wavdata)
		{		
			{
				
				ProcessWavFile("audio.wav",false);				
				wavfile=fopen("audio.wav","rb");
				
			}
		}
						
	}
	else
	{
		//QMDX_SetVolume(hQMixer,ch,0,0);
		//talkielevel=0;
	}
	
	  
}


void Talkie::ProcessWavFile(char *txt, bool sfl)
{
	AudioTag *tag;
	int numtags=ReadWavTags(txt,&tag,sfl);
	if (!tag || !numtags) return;
	qsort(tag,numtags,sizeof(AudioTag),audiotagcompare);
	tag[numtags-1].len=0;
	for (int c1=0;c1<numtags-1;c1++) 
	{
		tag[c1].len=tag[c1+1].pos-tag[c1].pos;	
		if (tag[c1].len<16) tag[c1].len=16;
	}
	printf("%d tags in wav file\n",numtags);
	// build markov chain
	UniqueWord *a, *b, *c;
	a=b=c=NULL;
	int c2=1891;
	for (c1=0;c1<1891;c1++)
	{
		
		if (stricmp("cut",tag[c1].txt) && tag[c1].len<200000 && tag[c1].len>0)
		{		
			while (stricmp("cut",tag[c2].txt)==0 && c2<numtags) c2++;
			
			//Report3D("%d %d %s %s\n",c1,c2-1891,tag[c1].txt,tag[c2].txt);	_sleep(0);
					
			UniqueWord *uw=UniqueWord::Add(tag[c1].txt,&tag[c1],&tag[c2]);
			c2++;
			a=b;
			b=c;
			c=uw;
			if (a && b && c)
			{
				if (WordPair::total==0)
				{
					starta=a;
					startb=b;
					startc=c;
				}					
				WordPair::Add(a,b,c);				
			}
		}
	}
}

void Talkie::LoadMP3(char *fname, char *mp3mem, int mp3len)
{
	char wv[1024];
	strcpy(wv,fname);
	for (int c1=0;c1<(int)strlen(wv);c1++) if (wv[c1]=='.') wv[c1]=0;
	strcat(wv,".wav");
	ProcessWavFile(wv,true);

	UBYTE *mp3=(UBYTE*)mp3mem;
/*
	MPGAudioDecoder d;
	ULONG header=((ULONG*)mp3)[0];
	d.init(header);

	d.FillinFormat(wex,(ULONG*)mp3);
	int outsize=(__int64(mp3len)*__int64(wex.nSamplesPerSec))/wex.nAvgBytesPerSec;
	wavdata=new short[int(outsize*1.5f)];
	
	unsigned char *in=(unsigned char*)mp3;
	unsigned char *out=(unsigned char*)wavdata;
	int inleft=mp3len;
	outsize=0;
	while (inleft>0 && d.mpegok)
	{
		int inused=0;
		int outused=d.unpackframe(in,out,&inused);
		in+=inused;
		out+=outused;
		inleft-=inused;
		outsize+=outused;
	}
	wavlen=outsize;
	
	d.uninit();
	*/
}




