#include "config.h"
/* $Id: say.c,v 1.13 1994/11/08 13:30:50 a904209 Exp a904209 $
   $Log: /Source/say.c $
 * 
 * 2     7/31/01 3:15 PM Burgermac
 * Carbonized!
 * 
 * 1     12/27/00 5:00 PM Burgermac
// 
// 5     8/07/99 1:53p Markd
// fixed a bunch of more code for version 1.06
// 
// 4     7/26/99 7:42p Markd
// Added 3 formants to the speech
// 
// 3     7/26/99 7:10p Markd
// Added speech ability
// 
// 2     7/24/99 6:47p Markd
// Added talk feature and added speech code
// 
// 1     7/24/99 6:39p Markd
// 
// 1     6/19/99 12:51p Markd
 * Revision 1.13  1994/11/08  13:30:50  a904209
 * 2.0 release
 *
 * Revision 1.12  1994/11/04  13:32:31  a904209
 * 1.99.1 - Change configure stuff
 *
 * Revision 1.11  1994/11/02  10:55:31  a904209
 * Add autoconf. Tested on SunOS/Solaris
 *
 * Revision 1.10  1994/10/04  17:12:50  a904209
 * 3rd pre-release
 *
 * Revision 1.9  1994/10/04  09:08:27  a904209
 * Next Patch merge
 *
 * Revision 1.8  1994/10/03  08:41:47  a904209
 * 2nd pre-release                                                         
 *
 * Revision 1.7  1994/09/19  15:48:29  a904209
 * Split hplay.c, gdbm dictionary, start of f0 contour, netaudio and HP ports
 *
 * Revision 1.6  1994/04/15  16:47:37  a904209
 * Edits for Solaris2.3 (aka SunOs 5.3)
 *
 * Revision 1.5  1994/02/24  15:03:05  a904209
 * Added contributed linux, NeXT and SGI ports.
 *
 * Revision 1.4  93/11/18  16:29:06  a904209
 * Migrated nsyth.c towards Jon's scheme - merge still incomplete
 * 
 * Revision 1.3  93/11/16  14:32:44  a904209
 * Added RCS Ids, partial merge of Jon's new klatt/parwave
 *
 * Revision 1.3  93/11/16  14:00:58  a904209
 * Add IDs and merge Jon's klatt sources - incomplete
 *
 */
#include <ctype.h>
#include "useconfig.h"
#include <math.h>
#include "proto.h"
#include "nsynth.h"
#include "ascii.h"
#include "darray.h"
#include "holmes.h"
#include "phtoelm.h"
#include "text.h"
#include "getargs.h"
#include "phones.h"
#include "say.h"
#include <stdio.h>

short *SPEECH_Say(float gain, float pitch, float speed, const char *text, int *length);

static int verbose = 0;

unsigned spell_out(const char *word, int n, darray_ptr phone)
{
	unsigned nph = 0;

	// fprintf(stderr, "Spelling '%.*s'\n", n, word);

	while (n-- > 0)
	{
		nph += xlate_string(ASCII[*word++ & 0x7F], phone);
	}

	return nph;
}

int suspect_word(const char *s, int n)
{
	int i = 0;
	int seen_lower = 0;
	int seen_upper = 0;
	int seen_vowel = 0;
	int last = 0;
	for (i = 0; i < n; i++)
	{
		char ch = *s++;
		if (i && last != '-' && isupper(ch))
			seen_upper = 1;
		if (islower(ch))
		{
			seen_lower = 1;
			ch = toupper(ch);
		}
		if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U' || ch == 'Y')
			seen_vowel = 1;
		last = ch;
	}
	return !seen_vowel || (seen_upper && seen_lower) || !seen_lower;
}

static unsigned xlate_word(const char *word, int n, darray_ptr phone);
static unsigned xlate_word(const char *word, int n, darray_ptr phone)
{
	unsigned nph = 0;

	if (*word != '[')
	{
		if (suspect_word(word, n))
		{
			return spell_out(word, n, phone);
		}
		else
		{
			// if (verbose)
				// fprintf(stderr, "Guess %p '%.*s'\n", 0, n, word);
			nph += NRL(word, n, phone);
		}
	}
	else
	{
		if ((++word)[(--n) - 1] == ']')
		{
			n--;
		}

		while (n-- > 0)
		{
			phone_append(phone, *word++);
			nph++;
		}
	}
	phone_append(phone, ' ');

	return nph + 1;
}

static short * talk_buffer = NULL;
static unsigned talk_buffer_length = 0;
static int talk_length = 0;

void say_phones(const char *phone, int len, int verbose)
{
	darray_t elm;
	unsigned frames;

	talk_length = 0;

	darray_init(&elm, sizeof(char), len);

	if ((frames = phone_to_elm(phone, len, &elm)))
	{
		unsigned max_samples = frames*klatt_global.nspfr;
		short *samp;

		if (max_samples > talk_buffer_length)
		{
			if (talk_buffer)
			{
				free(talk_buffer);
			}
			talk_buffer = (short *) malloc(sizeof(short) * max_samples);
			talk_buffer_length = max_samples;
		}

		samp = talk_buffer;

		// if (verbose)
			// fprintf(stderr,"%.*s\n", len, phone);

		if (samp)
		{
			unsigned nsamp = holmes(elm.items, (unsigned char *) darray_find(&elm, 0), max_samples, samp);

			talk_length = nsamp;
		}
	}

	darray_free(&elm);
}

unsigned xlate_string(const char *string, darray_ptr phone)
{
	unsigned nph = 0;
	const char *s = string;
	char ch;

	while (isspace(ch = *s))
		s++;
	while ((ch = *s))
	{
		const char *word = s;
		if (isalpha(ch))
		{
			while (isalpha(ch = *s) || ((ch == '\'' || ch == '-' || ch == '.') && isalpha(s[1])))
				s++;
			if (!ch || isspace(ch) || ispunct(ch) || (isdigit(ch) && !suspect_word(word, s - word)))
				nph += xlate_word(word, s - word, phone);
			else
			{
				while ((ch = *s) && !isspace(ch) && !ispunct(ch))
					s++;
				nph += spell_out(word, s - word, phone);
			}
		}
		else if (isdigit(ch) || (ch == '-' && isdigit(s[1])))
		{
			int sign = (ch == '-') ? -1 : 1;
			int32_t value = 0;
			if (sign < 0)
				ch = *++s;
			while (isdigit(ch = *s))
			{
				value = value * 10 + ch - '0';
				s++;
			}
			if (ch == '.' && isdigit(s[1]))
			{
				word = ++s;
				nph += xlate_cardinal(value * sign, phone);
				nph += xlate_string("point", phone);
				while (isdigit(ch = *s))
					s++;
				nph += spell_out(word, s - word, phone);
			}
			else
			{
				/* check for ordinals, date, time etc. can go in here */
				nph += xlate_cardinal(value * sign, phone);
			}
		}
		else if (ch == '[' && strchr(s, ']'))
		{
			const char *word = s;
			while (*s && *s++ != ']')
				/* nothing */ ;
			nph += xlate_word(word, s - word, phone);
		}
		else if (ispunct(ch))
		{
			switch (ch)
			{
			/* On end of sentence flush the buffer ... */
			case '!':
			case '?':
			case '.':
				if ((!s[1] || isspace(s[1])) && phone->items)
				{
					say_phones((char *) darray_find(phone, 0), phone->items, verbose);
					phone->items = 0;
				}
				s++;
				phone_append(phone, ' ');
				break;

			case '"': /* change pitch ? */
			case ':':
			case '-':
			case ';':
			case ',':
			case '(':
			case ')':
				s++;
				phone_append(phone, ' ');
				break;

			case '[':
			{
				char *e = strchr(s, ']');
				if (e)
				{
					s++;
					while (s < e)
						phone_append(phone, *s++);
					s = e + 1;
					break;
				}
			}

			default:
				nph += spell_out(word, 1, phone);
				s++;
				break;
			}
		}
		else
		{
			while ((ch = *s) && !isspace(ch))
				s++;
			nph += spell_out(word, s - word, phone);
		}
		while (isspace(ch = *s))
			s++;
	}
	return nph;
}

char *concat_args(int argc, const char **argv)
{
	int len = 0;
	int i;
	char *buf;

	for (i = 1; i < argc; i++)
		len += (int)strlen(argv[i]) + 1;
	buf = (char *) malloc(len);
	if (buf)
	{
		char *d = buf;
		for (i = 1; i < argc; )
		{
			const char *s = argv[i++];
			while (*s)
				*d++ = *s++;
			if (i < argc)
				*d++ = ' ';
			else
				*d = '\0';
		}
	}
	return buf;
}

void say_string(const char *s)
{
	darray_t phone;
	darray_init(&phone, sizeof(char), 128);
	xlate_string(s, &phone);
	if (phone.items)
		say_phones((char *) darray_find(&phone, 0), phone.items, verbose);
	darray_free(&phone);
}

#if 0 /* not used */
int darray_fget(FILE *f, darray_ptr p);
int darray_fget(FILE *f, darray_ptr p)
{
	int ch;
	while ((ch = fgetc(f)) != EOF)
	{
		phone_append(p, ch);
		if (ch == '\n')
			break;
	}
	phone_append(p, '\0');
	return p->items - 1;
}

void say_file(FILE *f);
void say_file(FILE *f)
{
	darray_t line;
	darray_t phone;

	darray_init(&line, sizeof(char), 128);
	darray_init(&phone, sizeof(char), 128);
	while (darray_fget(f, &line))
	{
		xlate_string((char *) darray_find(&line, 0), &phone);
		line.items = 0;
	}
	if (phone.items)
		say_phones((char *) darray_find(&phone, 0), phone.items, verbose);
	darray_free(&phone);
	darray_free(&line);
}
#endif

extern float global_gain; /* global gain parameter for voice */
short *SPEECH_Say(float gain, float pitch, float speed, const char *text, int *length)
{
	char talkstring[1024];
	int i, argc, len;
	const char *argv[7] = {
		"speech",
		"-x",
		"",
		"-f",
		"",
		"-c",
		"3",
	};
	char speed_string[32];
	char pitch_string[32];

	global_gain = gain;

	talk_length = 0; /* FS: Reset this, or we can send blank messages to repeat the previous speech message */

	/* set speed */
	sprintf(speed_string, "%d", (int)(10.0f * speed));
	argv[4] = speed_string;

	/* set pitch */
	sprintf(pitch_string, "%d", (int)(1330.0f * pitch));
	argv[2] = pitch_string;

	argc = 7;

	len = (int)strlen( text );
	/* get rid of offending characters */
	for (i = 0; i < len; i++)
	{
		if (
			( text[i] =='?' ) ||
			( text[i] =='!' ) ||
			( text[i] =='.' )
			)
			talkstring[i] = ' ';
		else
			talkstring[i] = text[i];
	}
	// add null terminator
	talkstring[i] = 0;

	argc = init_synth(argc, argv);
	say_string(talkstring);

	*length = talk_length;
	return talk_buffer;
}
