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

				ATTSSW_READ.C
				Read an AT&T audio file format.

Copyright (C) 2002 Greg Kochanski, gpk@ieee.org .

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

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

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#include "gpk_std.h"
#include "gpklib/ieee754.h"
#include "gpklib.h"
#include "gpklib/NaN.h"
#include "gpklib/image.h"
#include "rbfile.h"
#include "gpklib/innerloop4.h"
#include "internals.h"
#include "rw_misc.h"


#define D	if(0)

LOCAL char *read_in_data_ATT(RBFILE& s, image *p);
LOCAL int max(int a, int b) { return a>b ? a : b; }


LOCAL char * parse_hdr_record(char *hdr_rec, struct image *p);
LOCAL int ulaw2linear( unsigned char	u_val);


#define U(x) (0377 & (unsigned)(x))



LOCAL int ascii_chars(C char *c)
{
 for(; *c; c++)
	if(!isascii(*c))
		return 0;

 return 1;
}



LOCAL int is_integer(C char *c)
{
 static char islegal[256];
 static int setup = 0;
 if(!setup)	{
	setup = 1;
	C char *p = "0123456789 \t";
	while(*p)
		islegal[U(*p++)] = 1;
	}

 for(; *c; c++)
	if(!islegal[U(*c)])
		return 0;

 return 1;
}



char * parse_hdr_record(char *hdr_rec, struct image *p)
{
 char err[512];
 static int legal_attribute_char_set = 0;
 static char legal_attribute_char[256];

 if(!legal_attribute_char_set)	{
	C char *s = "-_QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm";
	for(; *s; s++)
		legal_attribute_char[U(*s)] = 1;
	legal_attribute_char_set = 1;
	}

 D fprintf(stderr, "parsing {%s}\n", hdr_rec);

 char *v;
 char *a = &hdr_rec[0];
 char *c = "";
 char *eq = 0;

 for(eq=hdr_rec; *eq && *eq!=' '; eq++)
	if(!legal_attribute_char[U(*eq)])	{
		sprintf(err, "illegal attribute char: %x at pos %u",
				*eq, eq-hdr_rec);
		return strdup(err);
		}
 if(eq == hdr_rec)	{
	sprintf(err, "missing attribute");
	return strdup(err);	/* No attribute.  */
	}

 D fprintf(stderr, "got attribute: {%s}\n", a);

 if(*eq == ' ')	{
	*eq = '\0';

	for(v=eq+1; *v==' '; v++)
		;

	if(*v == '"')	{	/* String. */
		v++;
		char *ev, *t;
		for(t=ev=v; *ev; ev++)	{
			if(*ev=='"' && ev[1]=='"')	{
				ev++;
				*t++ = *ev;
				}
			else if(*ev == '"')
				break;
			else	*t++ = *ev;
			}
		*t = '\0';
		if(*ev)	{
			c = ev+1;
			*ev = '\0';
			}

		while(*c == ' ')
			c++;
		if(*c=='/' || *c=='#')
			c++;
		}
	else	{		/* Mostly numbers, I suppose. */
		chomp_whitespace(v);
		}

	if(*c)	{
		while(*c == ' ')
			c++;
		chomp_whitespace(c);
		}
	}
 else	{	/* No value. */
	v = "";
	}

 if(!ascii_chars(v) || !ascii_chars(c))	{
	sprintf(err, "non-ascii character in value or comment for %s",
			a);
	return strdup(err);
	}

 D fprintf(stderr, "IAttr=%s, val=%s, comment=%s\n", a, v, c);

 add_append_property(p->prop, a, v, c);

 return 0;
}



char * getdata_ATTSSW(struct multi_image *rv, RBFILE& s, BOOL header_only)
{
 char err[1024];
 struct image * p = &rv->image[0];

 s.rewind();
 multi_image_empty(rv);

 p->prop = new proplist;
 assert(p->prop);

 char hdr_rec[10000];
 int hdr_line = 0;
 while(1)	{
	hdr_line++;
	if(!s.fgets(hdr_rec, sizeof(hdr_rec)))	{
		multi_image_destr(rv);
		sprintf(err, "not ascii-image: can't read header, line %d.",
				hdr_line);
		return strdup(err);
		}

	D fprintf(stderr, "DBG: s.fgets done: {%s}\n", hdr_rec);


	if(hdr_rec[0] == '#')
		continue;

	if(hdr_rec[0] == '\0')	{
		multi_image_destr(rv);
		sprintf(err, "not ATT_SSW: null header line %d.",
				hdr_line);
		return strdup(err);
		}

	if(chomp_newline(hdr_rec) < 0)	{
		multi_image_destr(rv);
		sprintf(err, "not ATT_SSW: header missing newline "
				"(line %d too long or null?)", hdr_line);
		return strdup(err);
		}

	if(hdr_rec[0] == '\0')
		break;

	if(hdr_line==1 && strcmp(hdr_rec, "ATT_SSW")!=0)	{
		multi_image_destr(rv);
		sprintf(err, "not ATT_SSW: First line is incorrect.");
		return strdup(err);
		}
	else if(hdr_line == 1)
		continue;
	else if(hdr_line == 2)	{
		if(is_integer(hdr_rec))	{
			char *buf = (char *)malloc(strlen(hdr_rec)+4);
			strcpy(buf, "ID ");
			strcat(buf, hdr_rec);
			parse_hdr_record(buf, p);
			continue;
			}
		else	{
			sprintf(err, "not ATT_SW: second line not integer.");
			multi_image_destr(rv);
			return strdup(err);
			}
		}

	char *hpfail = parse_hdr_record(hdr_rec, p);
	if(hpfail)	{
		multi_image_destr(rv);
		sprintf(err, "not ATT_SSW: can't parse header line %d: %s", hdr_line, hpfail);
		free(hpfail);
		return strdup(err);
		}
	D fprintf(stderr, "DBG: done with {%s}\n", hdr_rec);
	}



 s.commit();		/* Toss out the buffer. */

 if(!header_only)	{
	char * fail = read_in_data_ATT(s, p);
 	if(fail)	{
		multi_image_destr(rv);
		sprintf(err, "not ascii image: read fails after header: %s.", fail);
		return strdup(err);
		}
	}
 p->xscale = p->prop->getd("CDELT1", 1.0);
 p->xscale = p->prop->getd("xscale", p->xscale);
 p->yscale = p->prop->getd("CDELT2", 1.0);
 p->yscale = p->prop->getd("yscale", p->yscale);
 p->xcenter = p->prop->getd("CRVAL1", 0.0)
		+ p->xscale*(0.5*(p->npts-1) - (p->prop->getd("CRPIX1", 1.0)-1.0));
 p->ycenter = p->prop->getd("CRVAL2", 0.0)
		+ p->yscale*(0.5*(p->nlines-1) - (p->prop->getd("CRPIX2", 1.0)-1.0));
 p->zscale = p->prop->getd("BSCALE", 1.0/8192);
 p->zscale = p->prop->getd("zscale", p->zscale);
 p->zshift = p->prop->getd("BZERO", 0.0);
 p->zshift = p->prop->getd("zshift", p->zshift);
 p->i_noise = p->prop->getd("I_NOISE", p->zscale);

 if(!header_only)
	p->prop->add("SAT_LEVEL",
		max(max(abs(ulaw2linear(0)), abs(ulaw2linear(127))),
			max(abs(ulaw2linear(255)), abs(ulaw2linear(128))))
		* p->zscale,
			"Maximum possible amplitude");

 p->prop->add("_FILETYPE", "ATT_SSW", "AT&T speech data format");


 rv->n_image++;

 return 0;
}


LOCAL short sign_extend(short x)
{
 short x0 = x;
 if(x > 127)	{
	x = 127 - x;
	}
 D fprintf(stderr, "Sign_extend(0%o %d) = 0%o %d\n", x0, x0, x, x);
 return x;
}


LOCAL short swapinbyte(short x)
{
 short x0 = x;
 short rv = 0;
 int i;
 for(i=0; i<8; i++, x >>=1)	{
	rv <<= 1;
	if(x & 1)
		rv |= 1;
	}

 D fprintf(stderr, "SWAP(0%o) = 0%o\n", x0, rv);
 return rv;
}




char * read_in_data_ATT(RBFILE& s, image *p)
{
 C static int BUFCHUNK = 32768;
 short *d = 0;
 int na = 0, n = 0;
 BOOL invert = strcmp(p->prop->gets("bit_sense", "NORMAL"), "INV")==0;
 BOOL swap_in_byte = strcmp(p->prop->gets("bit_order", "LE"), "LE")==0;
 int c;
 short map[256];

 D fprintf(stderr, "DBG: invert=%d, swap=%d\n", invert, swap_in_byte);

 if(invert && !swap_in_byte)	{
	int i;
	for(i=0; i<256; i++)
		map[i] = ulaw2linear(0377 & i);
	}
 else if(!swap_in_byte)	{
	int i;
	for(i=0; i<256; i++)
		map[i] = ulaw2linear(~i);
	}
 else if(invert && swap_in_byte)	{
	int i;
	for(i=0; i<256; i++)
		map[i] = ulaw2linear(0377 & swapinbyte(i));
	}
 else if(swap_in_byte)	{
	int i;
	for(i=0; i<256; i++)
		map[i] = ulaw2linear(~swapinbyte(i));
	}



 while((c=s.fgetc()) != EOF)	{
	if(n >= na)	{
		short *tmp = short1alloc(na += na/2 + BUFCHUNK);
		if(d)
			memcpy(tmp, d, n * sizeof(short));
		short1free(d);
		d = tmp;
		}
	d[n++] = map[c];
	}

 if(!image_alloc(p, n, 1)) {
	short1free(d);
	assert(0);
	}

 INNERLOOP(i, 0, n, p->h[i][0] = d[i]);
 short1free(d);
 p->npts = 1;
 p->nlines = n;
 image_setzscale(p, 1/128.0, "arbs", 0.0);

 return 0;
}


/*...... g711.c */
/*
 * This source code is a product of Sun Microsystems, Inc. and is provided
 * for unrestricted use.  Users may copy or modify this source code without
 * charge.
 *
 * SUN SOURCE CODE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
 * THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
 *
 * Sun source code is provided with no support and without any obligation on
 * the part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
 *
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS SOFTWARE
 * OR ANY PART THEREOF.
 *
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even if
 * Sun has been advised of the possibility of such damages.
 *
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */

/*
 * g711.c
 *
 * u-law, A-law and linear PCM conversions.
 */
#define	SIGN_BIT	(0x80)		/* Sign bit for a A-law byte. */
#define	QUANT_MASK	(0xf)		/* Quantization field mask. */
#define	NSEGS		(8)		/* Number of A-law segments. */
#define	SEG_SHIFT	(4)		/* Left shift for segment number. */
#define	SEG_MASK	(0x70)		/* Segment field mask. */

static short seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF,
			    0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};

/* copy from CCITT G.711 specifications */
LOCAL unsigned char _u2a[128] = {			/* u- to A-law conversions */
	1,	1,	2,	2,	3,	3,	4,	4,
	5,	5,	6,	6,	7,	7,	8,	8,
	9,	10,	11,	12,	13,	14,	15,	16,
	17,	18,	19,	20,	21,	22,	23,	24,
	25,	27,	29,	31,	33,	34,	35,	36,
	37,	38,	39,	40,	41,	42,	43,	44,
	46,	48,	49,	50,	51,	52,	53,	54,
	55,	56,	57,	58,	59,	60,	61,	62,
	64,	65,	66,	67,	68,	69,	70,	71,
	72,	73,	74,	75,	76,	77,	78,	79,
	81,	82,	83,	84,	85,	86,	87,	88,
	89,	90,	91,	92,	93,	94,	95,	96,
	97,	98,	99,	100,	101,	102,	103,	104,
	105,	106,	107,	108,	109,	110,	111,	112,
	113,	114,	115,	116,	117,	118,	119,	120,
	121,	122,	123,	124,	125,	126,	127,	128};

LOCAL unsigned char _a2u[128] = {			/* A- to u-law conversions */
	1,	3,	5,	7,	9,	11,	13,	15,
	16,	17,	18,	19,	20,	21,	22,	23,
	24,	25,	26,	27,	28,	29,	30,	31,
	32,	32,	33,	33,	34,	34,	35,	35,
	36,	37,	38,	39,	40,	41,	42,	43,
	44,	45,	46,	47,	48,	48,	49,	49,
	50,	51,	52,	53,	54,	55,	56,	57,
	58,	59,	60,	61,	62,	63,	64,	64,
	65,	66,	67,	68,	69,	70,	71,	72,
	73,	74,	75,	76,	77,	78,	79,	79,
	80,	81,	82,	83,	84,	85,	86,	87,
	88,	89,	90,	91,	92,	93,	94,	95,
	96,	97,	98,	99,	100,	101,	102,	103,
	104,	105,	106,	107,	108,	109,	110,	111,
	112,	113,	114,	115,	116,	117,	118,	119,
	120,	121,	122,	123,	124,	125,	126,	127};

static int
search(
	int		val,
	short		*table,
	int		size)
{
	int		i;

	for (i = 0; i < size; i++) {
		if (val <= *table++)
			return (i);
	}
	return (size);
}

/*
 * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
 *
 * linear2alaw() accepts an 16-bit integer and encodes it as A-law data.
 *
 *		Linear Input Code	Compressed Code
 *	------------------------	---------------
 *	0000000wxyza			000wxyz
 *	0000001wxyza			001wxyz
 *	000001wxyzab			010wxyz
 *	00001wxyzabc			011wxyz
 *	0001wxyzabcd			100wxyz
 *	001wxyzabcde			101wxyz
 *	01wxyzabcdef			110wxyz
 *	1wxyzabcdefg			111wxyz
 *
 * For further information see John C. Bellamy's Digital Telephony, 1982,
 * John Wiley & Sons, pps 98-111 and 472-476.
 */
LOCAL unsigned char
linear2alaw(
	int		pcm_val)	/* 2's complement (16-bit range) */
{
	int		mask;
	int		seg;
	unsigned char	aval;

	if (pcm_val >= 0) {
		mask = 0xD5;		/* sign (7th) bit = 1 */
	} else {
		mask = 0x55;		/* sign bit = 0 */
		pcm_val = -pcm_val - 8;
	}

	/* Convert the scaled magnitude to segment number. */
	seg = search(pcm_val, seg_end, 8);

	/* Combine the sign, segment, and quantization bits. */

	if (seg >= 8)		/* out of range, return maximum value. */
		return (0x7F ^ mask);
	else {
		aval = seg << SEG_SHIFT;
		if (seg < 2)
			aval |= (pcm_val >> 4) & QUANT_MASK;
		else
			aval |= (pcm_val >> (seg + 3)) & QUANT_MASK;
		return (aval ^ mask);
	}
}

/*
 * alaw2linear() - Convert an A-law value to 16-bit linear PCM
 *
 */
LOCAL int
alaw2linear(
	unsigned char	a_val)
{
	int		t;
	int		seg;

	a_val ^= 0x55;

	t = (a_val & QUANT_MASK) << 4;
	seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT;
	switch (seg) {
	case 0:
		t += 8;
		break;
	case 1:
		t += 0x108;
		break;
	default:
		t += 0x108;
		t <<= seg - 1;
	}
	return ((a_val & SIGN_BIT) ? t : -t);
}

#define	BIAS		(0x84)		/* Bias for linear code. */

/*
 * linear2ulaw() - Convert a linear PCM value to u-law
 *
 * In order to simplify the encoding process, the original linear magnitude
 * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
 * (33 - 8191). The result can be seen in the following encoding table:
 *
 *	Biased Linear Input Code	Compressed Code
 *	------------------------	---------------
 *	00000001wxyza			000wxyz
 *	0000001wxyzab			001wxyz
 *	000001wxyzabc			010wxyz
 *	00001wxyzabcd			011wxyz
 *	0001wxyzabcde			100wxyz
 *	001wxyzabcdef			101wxyz
 *	01wxyzabcdefg			110wxyz
 *	1wxyzabcdefgh			111wxyz
 *
 * Each biased linear code has a leading 1 which identifies the segment
 * number. The value of the segment number is equal to 7 minus the number
 * of leading 0's. The quantization interval is directly available as the
 * four bits wxyz.  * The trailing bits (a - h) are ignored.
 *
 * Ordinarily the complement of the resulting code word is used for
 * transmission, and so the code word is complemented before it is returned.
 *
 * For further information see John C. Bellamy's Digital Telephony, 1982,
 * John Wiley & Sons, pps 98-111 and 472-476.
 */
LOCAL unsigned char
linear2ulaw(
	int		pcm_val)	/* 2's complement (16-bit range) */
{
	int		mask;
	int		seg;
	unsigned char	uval;

	/* Get the sign and the magnitude of the value. */
	if (pcm_val < 0) {
		pcm_val = BIAS - pcm_val;
		mask = 0x7F;
	} else {
		pcm_val += BIAS;
		mask = 0xFF;
	}

	/* Convert the scaled magnitude to segment number. */
	seg = search(pcm_val, seg_end, 8);

	/*
	 * Combine the sign, segment, quantization bits;
	 * and complement the code word.
	 */
	if (seg >= 8)		/* out of range, return maximum value. */
		return (0x7F ^ mask);
	else {
		uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF);
		return (uval ^ mask);
	}

}

/*
 * ulaw2linear() - Convert a u-law value to 16-bit linear PCM
 *
 * First, a biased linear code is derived from the code word. An unbiased
 * output can then be obtained by subtracting 33 from the biased code.
 *
 * Note that this function expects to be passed the complement of the
 * original code word. This is in keeping with ISDN conventions.
 */
LOCAL int
ulaw2linear(
	unsigned char	u_val)
{
	int		t;

	/* Complement to obtain normal u-law value. */
	u_val = ~u_val;

	/*
	 * Extract and bias the quantization bits. Then
	 * shift up by the segment number and subtract out the bias.
	 */
	t = ((u_val & QUANT_MASK) << 3) + BIAS;
	t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT;

	return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS));
}
