/* Copyright (c) 2000-2003                             */
/*   Yamashita Lab., Ritsumeikan University            */
/*   Takao Kobayashi, Takashi Masuko, Masatsune Tamura */
/*   Tokyo Institute of Technology                     */
/*   Keiichi Tokuda, Takayoshi Yoshimura, Heiga Zen    */
/*   Nagoya Institute of Technology                    */
/*   All rights reserved                               */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "synthesis.h"
#include "tag.h"
#include "defaults.h"
#include "confpara.h"
#include "misc.h"
#include "tree.h"
#include "hmmsynth.h"
#include "model.h"

int TmpMsg(char *,...);
int ErrMsg(char *,...);
int speakerID(char *);
void restart(int);

void mod_dur_morph( MORPH* morph, int attrID, double rate )
{
	PHONEME *phoneme, *last, *head;
	Model *m,*mh;
	int state;
	double rho,x,diffdur,data;

	if( morph->nmora == 0 )  return;	/* 「」など。*/

	head = morph->mrhead->phead;
	last = morph->mrtail->ptail;
	phoneme = head;

	mh = mhead;
	while( mh->phoneme != phoneme)mh = mh->next;
	m = mh;
	rho = 0;
	x = 0;

	while(1){
		switch(attrID) {
			case TA_SPEED:
			   rho += m->phoneme->time * rate / FRAME_RATE;
			   for(state=2;state <= nstate+1; state++){
			      rho -= m->durmean[state];
			      x += m->durvariance[state];
                           }
			   break;
                }
		if(phoneme == last)break;
		phoneme = phoneme->next;
		m = m->next;
	}

        rho /= x;
	phoneme = head;
	m = mh; 
	diffdur = 0;

	while(1) {
		switch( attrID )  {
			case TA_SPEED:
				m->totalduration = 0;
				for(state=2;state <= nstate +1; state++) {
				        data = m->durmean[state] + rho * m->durvariance[state];
					m->duration[state] = (int)(data + diffdur + 0.5);
					m->totalduration += m->duration[state];
					diffdur += data - (float) m->duration[state];
                                }
				phoneme->time = m->totalduration * FRAME_RATE;
				break;
			case TA_ABSSPEED:
			case TA_MORASEC:
				break;
		}
		if(phoneme == last)break;
		phoneme = phoneme->next;
		m = m->next;
	}
}

void mod_dur_silence( PHONEME *ph, int attrID, double dur )
{
	Model *m,*mh;
	int state;
	double rho,x,diffdur,data;

	mh = mhead;
	while( mh->phoneme != ph)mh = mh->next;
	m = mh;
	rho = 0;
	x = 0;
	switch(attrID) {
		case TA_MSEC:
			rho = dur / FRAME_RATE;
			for(state=2;state <= nstate+1; state++){
			   rho -= m->durmean[state];
			   x += m->durvariance[state];
                        }
                        rho /= x;
	                m = mh; 
	                diffdur = 0;
			m->totalduration = 0;
			for(state=2;state <= nstate +1; state++) {
			        data = m->durmean[state] + rho * m->durvariance[state];
				m->duration[state] = (int)(data + diffdur + 0.5);
				m->totalduration += m->duration[state];
				diffdur += data - (float) m->duration[state];
			}
			ph->time = m->totalduration * FRAME_RATE;
			break;
	}	
}

void modify_duration()
{
	MORPH *morph;
	int i, len;
	double	rate, dur;
	PHONEME *p;

	/* 一番外側のタグから順に処理するよう、逆順に実行する */
	for( i=n_tag-1; i>=0; --i )  {
	  if( tag[i]->id == T_RATE )  {
		morph = mphead;
		len = 0;	/* 累積文字数 */
		sscanf( tag[i]->options[0].val, "%lf", &rate );
		while( morph )  {
			if( tag[i]->start <= len && len+(morph->nbyte) <= tag[i]->end )  {
				mod_dur_morph( morph, tag[i]->options[0].attrID, rate );
			}
			len += morph->nbyte;
			morph = morph->next;
		}
	  } else if( tag[i]->id == T_SILENCE )  {
		if( tag[i]->n_op > 0 )  {
			sscanf( tag[i]->options[0].val, "%lf", &dur );
			if( tag[i]->start_morph->mrhead == NULL ) {
				p = tag[i]->start_morph->next->mrhead->phead;
			} else {
				p = tag[i]->start_morph->mrhead->phead;
			}
			mod_dur_silence( p, tag[i]->options[0].attrID, dur );
		}
	  }
	}
}

void make_cumul_time()
{
	PHONEME *phoneme;
	double	ctime;

	phoneme = phhead;
	ctime = 0.0;
	while( phoneme )  {
		phoneme->ctime = ctime;
		ctime += phoneme->time;
		phoneme = phoneme->next;
	}
}

void mod_f0_morph( MORPH* m1, MORPH* m2, int attrID, double rate )
{
	double	time1, time2, lograte;
	int i, i1, i2;
	MORA *mora1, *mora2;

	if( m1->mrhead == NULL )  {		/* 「」など。*/
		mora1 = m1->next->mrhead;
	} else {
		mora1 = m1->mrhead;
	}
	if( m2->mrtail == NULL )  {		/* 「」など。*/
		mora2 = m2->prev->mrtail;
	} else {
		mora2 = m2->mrtail;
	}
	time1 = mora1->phead->ctime;
	time2 = mora2->ptail->ctime + mora2->ptail->time;

	i1 = ( int ) ( time1 / (double)(FRAME_RATE) );
	i2 = ( int ) ( time2 / (double)(FRAME_RATE) );

	switch( attrID )  {
		case TA_LEVEL:
			lograte = log( rate );
			for( i=i1; i<=i2; i++ )  {
				if( f0.data[i] <= 0.000001 )  continue;
				f0.data[i] += lograte;
			}
			break;
		case TA_ABSLEVEL:
			break;
		case TA_RANGE:
		{
			double ave;
			int n;
			ave = 0.0;  n = 0;
			for( i=0; i<=totalframe; ++i )  {
				if( f0.data[i] <= 0.000001 )  continue;
				ave += f0.data[i];  ++n;
			}
			ave /= (double)n;
			for( i=i1; i<=i2; i++ )  {
				if( f0.data[i] <= 0.000001 )  continue;
				f0.data[i] += ( f0.data[i]-ave ) * rate;
			}
		}
	}
}

void modify_f0()
{
	MORPH *morph, *m1, *m2=NULL;
	int i, len;
	double	rate;

	/* 一番外側のタグから順に処理するよう、逆順に実行する */
	for( i=n_tag-1; i>=0; --i )  {
		if( tag[i]->id != T_PITCH )  continue;

		morph = mphead;
		len = 0;	/* 累積文字数 */
		sscanf( tag[i]->options[0].val, "%lf", &rate );
		m1 = NULL;
		while( morph )  {
			if( m1 == NULL && tag[i]->start <= len )  m1 = morph;
			if( len+(morph->nbyte) <= tag[i]->end )  m2 = morph;
			len += morph->nbyte;
			morph = morph->next;
		}
		mod_f0_morph( m1, m2, tag[i]->options[0].attrID, rate );
	}
}

void mod_power_morph( MORPH* m1, MORPH* m2, int attrID, double rate )
{
	double	time1, time2, lograte;
	int i, i1, i2;
	MORA *mora1, *mora2;

	if( m1->mrhead == NULL )  {		/* 「」など。*/
		mora1 = m1->next->mrhead;
	} else {
		mora1 = m1->mrhead;
	}
	if( m2->mrtail == NULL )  {		/* 「」など。*/
		mora2 = m2->prev->mrtail;
	} else {
		mora2 = m2->mrtail;
	}
	time1 = mora1->phead->ctime;
	time2 = mora2->ptail->ctime + mora2->ptail->time;

	i1 = ( int ) ( time1 / (double)(FRAME_RATE) );
	i2 = ( int ) ( time2 / (double)(FRAME_RATE) );

	switch( attrID )  {
		case TA_LEVEL:
			lograte = log( rate );
			for( i=i1; i<=i2; i++ )  {
				power.data[i] += lograte;
			}
			break;
	}
}

void modify_power()
{
	MORPH *morph, *m1, *m2=NULL;
	int i, len;
	double	rate;

	/* 一番外側のタグから順に処理するよう、逆順に実行する */
	for( i=n_tag-1; i>=0; --i )  {
		if( tag[i]->id != T_VOLUME )  continue;

		morph = mphead;
		len = 0;	/* 累積文字数 */
		sscanf( tag[i]->options[0].val, "%lf", &rate );
		m1 = NULL;
		while( morph )  {
			if( m1 == NULL && tag[i]->start <= len )  m1 = morph;
			if( len+(morph->nbyte) <= tag[i]->end )  m2 = morph;
			len += morph->nbyte;
			morph = morph->next;
		}
		mod_power_morph( m1, m2, tag[i]->options[0].attrID, rate );
	}
}

/*
void refresh_speaker()
{
	int i;

	for( i=0; i<n_speaker; ++i )  {
		if( speaker[i].alpha_saved >= 0 )  {
			speaker[i].alpha = speaker[i].alpha_saved;
			speaker[i].alpha_saved = -1.0;
		}
	}
}
*/

void mod_voice_morph( MORPH* morph, int attrID, char* val )
{
	PHONEME *phoneme, *last;
	int s;
	double	a;

	if( morph->mrhead == NULL )  {	/* 「」など。*/
		phoneme = morph->next->mrhead->phead;
	} else {
		phoneme = morph->mrhead->phead;
	}
	if( morph->mrtail == NULL )  {	/* 「」など。*/
		last = morph->prev->mrtail->ptail;
	} else {
		last = morph->mrtail->ptail;
	}

	while(1){
		switch(attrID) {
		case TA_OPTIONAL:
			s = speakerID( val );
			if( s >= 0 )  phoneme->sid = s;
			break;
		case TA_REQUIRED:
			break;
		case TA_ALPHA:
			a = atof( val );
			if( a >= 0.0 )  {
			  /*
				s = phoneme->sid;
				if( speaker[s].alpha_saved < 0 )  {
					speaker[s].alpha_saved = speaker[s].alpha;
				}
				speaker[s].alpha = a;
			  */
				phoneme->alpha = a;
			}
			break;
		}
		if(phoneme == last)break;
		phoneme = phoneme->next;
	}
}

void modify_voice()
{
	MORPH *morph;
	int i, len, j;
	char *val_speaker, *val_alpha;

	/* 一番外側のタグから順に処理するよう、逆順に実行する */
	for( i=n_tag-1; i>=0; --i )  {
		if( tag[i]->id != T_VOICE )  continue;

		val_speaker = val_alpha = NULL;
		for( j=0; j<tag[i]->n_op; ++j )  {
			switch( tag[i]->options[j].attrID ) {
			case TA_OPTIONAL:
				val_speaker = tag[i]->options[j].val;
				break;
			case TA_REQUIRED:
				break;
			case TA_ALPHA:
				val_alpha = tag[i]->options[j].val;
				break;
			}
		}
		morph = mphead;
		len = 0;	/* 累積文字数 */
/*		spname = (char *)malloc(strlen(tag[i]->options[j].val)-2);	*/
/*		sscanf( tag[i]->options[j].val, "\"%[^\"]", spname );	*/
/*		spname = tag[i]->options[j].val;	*/
		while( morph )  {
			if( tag[i]->start <= len && len+(morph->nbyte) <= tag[i]->end )  {
				/* αの変更より、話者の変更を先に。 */
				if( val_speaker )  {
					mod_voice_morph( morph, TA_OPTIONAL, val_speaker );
				}
				if( val_alpha )  {
					mod_voice_morph( morph, TA_ALPHA, val_alpha );
				}
			}
			len += morph->nbyte;
			morph = morph->next;
		}
/*		free(spname);	*/
	}
}

/* m1 の次の形態素を m1 の形態素にマージする。 */
void merge_morph( MORPH *m1 )
{
	MORPH *m2;
	int len, i;

	m2 = m1->next;

	len = strlen(m1->kanji) + strlen(m2->kanji) + 1;
	m1->kanji = (char *) realloc( m1->kanji, sizeof(char) * len );
	if( ! m1->kanji )  {
		ErrMsg( "* realloc error in 'merge_morph'\n" );
		restart(1);
	}
	strcat( m1->kanji, m2->kanji );

	m1->nmora += m2->nmora;
	m1->nbyte += m2->nbyte;
	m1->hinshiID = m2->hinshiID;
	m1->katsuyogataID = m2->katsuyogataID;
	m1->katsuyokeiID = m2->katsuyokeiID;
	for( i=0; i<m2->n_accent; ++i )  {
		m1->accent[i].prepos = m2->accent[i].prepos;
		m1->accent[i].form = m2->accent[i].form;
		m1->accent[i].ctype = m2->accent[i].ctype;
		m1->accent[i].ctype2 = m2->accent[i].ctype2;
	}
	m1->n_accent = m2->n_accent;
	m1->silence = NON;
	m1->next = m2->next;

	free( m2->kanji );
	free( m2->pron );
	free( m2 );
}

/* 埋め込みタグによる読みやアクセント型の変更 */
void modify_morph()
{
	int i, j, n, len;
	MORPH *morph;
	char *p;

	for( i=0; i<n_tag; ++i )  {
		if( tag[i]->id != T_PRON )  continue;
		if( tag[i]->options[0].attrID != TA_SYM )  continue;

		morph = tag[i]->start_morph;
/*		printf( "modify_morph:" );	*/
		n = 0;
		while( morph != tag[i]->end_morph )  {
/*			printf( "%s", morph->kanji );	*/
			morph = morph->next;
			++n;
		}
/*		printf( "%s (# of merge: %d)\n", morph->kanji, n );	*/

		morph = tag[i]->start_morph;
		for( j=0; j<n; ++j )  {
			merge_morph( morph );
		}

		p = strstr( tag[i]->options[0].val, "’" );
		if( p == NULL )  {
			morph->accentType = 0;
		} else {
			morph->accentType = (p - tag[i]->options[0].val)/2;
			/* ’の削除 */
			do {
				*p = *(p+2);
				++p;
			} while( *p );
		}
/*		printf( "accent: %d\n", morph->accentType );
		printf( "yomi: '%s'\n", tag[i]->options[0].val );
*/
		len = sizeof(char) * (strlen(tag[i]->options[0].val)+1);
		morph->pron = (char *) realloc( morph->pron, len );
		if( ! morph->pron )  {
			ErrMsg( "* realloc error for 'morph.pron' in modify_morph\n" );
			restart(1);
		}
		strcpy( morph->pron, tag[i]->options[0].val );
	}
}

