/*
** This routine converts from ulaw to 16 bit linear, but faster.
**
** Jef Poskanzer
** 23 October 1989
**
** Input: 8 bit ulaw sample
** Output: signed 16 bit linear sample
*/

#include <stdio.h>
#include <local/sfheader.h>

SFHEADER sfh;

char lin_to_ulaw();

/* for info:
typedef union sfheader {
	struct {
		int       sf_magic;
		float     sf_srate;
		int       sf_chans;
		int       sf_packmode;
		char      sf_codes;
	} sfinfo;
	char    filler[SIZEOF_HEADER];
*/

main(argc, argv)
char **argv;
{
	short int sample;	/* current sample to convert */
	int maxsample = 0;	/* highest magnitude seen so far */
	double scale;

	get_sfheader();	/* read soundfile header from stdin into sfh */

	while (get_sample(&sample)) {
		int asample = (sample < 0) ? -sample : sample;
		if (asample > maxsample) maxsample = asample;
	}

	fprintf(stderr, "Greatest magnitude = %d\n", maxsample);
	scale = 32767.0 / maxsample;
	fprintf(stderr, "Scaling factor = %g\n", scale);

	rewind(stdin); get_sfheader();

	while (get_sample(&sample)) {
		putchar(lin_to_ulaw((short)(sample * scale)));
	}

	exit(0);
}

get_sfheader()
{
	/*
	 * Read in soundfile header
	 */
	if (fread((char *) &sfh, sizeof(sfh), 1, stdin) != 1) {
		fprintf(stderr, "Cannot read header\n");
		exit(1);
	}

	if (sfmagic(&sfh) != SF_MAGIC) {
		fprintf(stderr, "Not a soundfile (magic number = %o)\n", sfmagic(&sfh));
		exit(1);
	}

	if (sfsrate(&sfh) != 8192) {
		fprintf(stderr, "Sample rate of %g incompatible with DAC (8192).\n", sfsrate(&sfh));
		exit(1);
	}

	switch (sfchans(&sfh)) {
	case 1:
	case 2:
		break;
	default:
		fprintf(stderr, "Can only deal with mono and stereo files (yet).\n");
		exit(1);
	}

	if (sfclass(&sfh) != SF_SHORT) {
		fprintf(stderr, "Can only cope with 16-bit samples (yet).\n");
		exit(1);
	}
}

/*
 *	Read next sample from stdin - return 0 at EOF
 */
int
get_sample(samplep)
short *samplep;		/* where to put result */
{
	short s[4];

	/* read stereo sample and average */

	if (fread(s, sizeof(short), sfchans(&sfh), stdin) != sfchans(&sfh)) return(0);

	switch (sfchans(&sfh)) {
	case 1:	*samplep = s[0];
		break;
	case 2: *samplep = (s[0] + s[1]) / 2;
		break;
	}

	return(1);
}

char
lin_to_ulaw(sample)
short sample;
{
	 /*
	  * Top bit of u law byte is sign of output: 0 = -ve, 1 = +ve
	  * Bottom 7 bits are non-linear representation of magnitude.
	  */

	 /* Here's what the bytes 128-255 map to (the other way) */
	 static short ulawtab[] = {
	 32124,  31100,  30076,  29052,  28028,  27004,  25980,  24956, 
	 23932,  22908,  21884,  20860,  19836,  18812,  17788,  16764, 
	 15996,  15484,  14972,  14460,  13948,  13436,  12924,  12412, 
	 11900,  11388,  10876,  10364,   9852,   9340,   8828,   8316, 
	  7932,   7676,   7420,   7164,   6908,   6652,   6396,   6140, 
	  5884,   5628,   5372,   5116,   4860,   4604,   4348,   4092, 
	  3900,   3772,   3644,   3516,   3388,   3260,   3132,   3004, 
	  2876,   2748,   2620,   2492,   2364,   2236,   2108,   1980, 
	  1884,   1820,   1756,   1692,   1628,   1564,   1500,   1436, 
	  1372,   1308,   1244,   1180,   1116,   1052,    988,    924, 
	   876,    844,    812,    780,    748,    716,    684,    652, 
	   620,    588,    556,    524,    492,    460,    428,    396, 
	   372,    356,    340,    324,    308,    292,    276,    260, 
	   244,    228,    212,    196,    180,    164,    148,    132, 
	   120,    112,    104,     96,     88,     80,     72,     64, 
	    56,     48,     40,     32,     24,     16,      8,      0
	};

	int i;
	char sign;

	/* extract the sign */
	if (sample < 0) {
		sign = 0;
		sample = -sample;
	} else {
		sign = 128;
	}

	/* find ulaw value - run along array to find value <= the sample.
	 * (yuk - slooooow).
	 * In fact, do it backwards cos small values are more likely.
	 */

	for (i=126; i >= 0 && ulawtab[i] <= sample; i--) ;

	return((i+1) | sign);
}
