/* Typhoon format to Yamaha OS format sample converter.
 * A lot of this code is taken from the sox code, see
 * copyright notices below. 
 * Modifications  and other code Copyright 1996 Stein Kulseth. 
 * This source code is freely redistributable and may be used for
 * any purpose.  This copyright notice must be maintained.
 * Nobody but the end user is responsible for the consequences
 * of using this software. 
 */


/* 
 * AIFF-C handling is modified from AIFF handling in sox,
 * containing the copyright notes below.
 */
	
	/*
	 * September 25, 1991
	 * Copyright 1991 Guido van Rossum And Sundry Contributors
	 * This source code is freely redistributable and may be used for
	 * any purpose.  This copyright notice must be maintained. 
	 * Guido van Rossum And Sundry Contributors are not responsible for 
	 * the consequences of using this software.
	 */
	
	/*
	 * Sound Tools SGI/Amiga AIFF format.
	 * Used by SGI on 4D/35 and Indigo.
	 * This is a subformat of the EA-IFF-85 format.
	 * This is related to the IFF format used by the Amiga.
	 * But, apparently, not the same.
	 *
	 * Jan 93: new version from Guido Van Rossum that 
	 * correctly skips unwanted sections.
	 */

/* fail and report routines and some other handy routines copied from sox.c */

	/*
	 * July 5, 1991
	 * Copyright 1991 Lance Norskog And Sundry Contributors
	 * This source code is freely redistributable and may be used for
	 * any purpose.  This copyright notice must be maintained.
	 * Lance Norskog And Sundry Contributors are not responsible for
	 * the consequences of using this software.
	 */
	

/* YAMAHA os file handling from tx16w handling in sox */
	/*
	 * January 12, 1995
	 * Copyright 1995 Mark Lakata (lakata@physics.berkeley.edu)
	 * Additions to tx16w.c SOX driver.  This version writes as well as
	 * reads TX16W format.
	 * See disclaimer immediately below.
	 */
	/*
	 * May 20, 1993
	 * Copyright 1993 Rob Talley   (rob@aii.com)
	 * This source code is freely redistributable and may be used for
	 * any purpose. This copyright notice and the following copyright
	 * notice must be maintained intact. No warranty whatsoever is
	 * provided. This code is furnished AS-IS as a component of the
	 * larger work Copyright 1991 Lance Norskog and Sundry Contributors.
	 * Much appreciation to ross-c  for his sampConv utility for SGI/IRIX
	 * from where these methods were derived.
	 */
	
	/*
	 * July 5, 1991
	 * Copyright 1991 Lance Norskog And Sundry Contributors
	 * This source code is freely redistributable and may be used for
	 * any purpose.  This copyright notice must be maintained.
	 * Lance Norskog And Sundry Contributors are not responsible for
	 * the consequences of using this software.
	 */



#include <stdio.h>
#include <math.h>
#include <malloc.h>

#ifdef  __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif


#define AIFCVersion1 0xA2805140L

#define MAXDELTA (1 << bits-1)
#define SPAN (1 << bits)
#define MAXWWM (bits/2)
#define BUFSIZE 1024

#define NOT_SET (-1)
#define LOOP_NONE 0
#define LOOP_FORWARD 1
#define LOOP_REVERSE 2

#define TXMAXLEN 0x3FF80

struct WaveHeader_ {
  unsigned char
    filetype[6], /* = "LM8953", */
    nulls[10],
    dummy_aeg[6],    /* space for the AEG (never mind this) */
    format,	  /* 0x49 = looped, 0xC9 = non-looped */
    sample_rate,     /* 1 = 33 kHz, 2 = 50 kHz, 3 = 16 kHz */
    atc_length[3],   /* I'll get to this... */
    rpt_length[3],
    unused[2];       /* set these to null, to be on the safe side */
} ;

typedef struct {
	short id;
	unsigned long position;
	char *markerName;
} marker_t;

/* GLOBALS - we love 'em :-) */

FILE *ifp, *ofp;
int verbose;
int fill256;
char *myname;
short channels;
unsigned long insamples, outsamples;
short bits;
double rate=0.0;

int	sample;
int	wwidth;
int	bitstr;
int	bsindex;
int	bsoddbytes;

int loop_mode=NOT_SET;
long loop_start=NOT_SET, loop_end=NOT_SET;
int ls_mark=NOT_SET, le_mark=NOT_SET;
marker_t *marker;
short markers;

static char magic1[4] = {0, 0x06, 0x10, 0xF6};
static char magic2[4] = {0, 0x52, 0x00, 0x52};

static long writedone=0;

static unsigned int mask[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};

char * midi_note[] = {
"C0", "C#0", "D0", "Eb0", "E0", "F0", "F#0", "G0", "G#0", "A0", "Bb0", "B0", 
"C1", "C#1", "D1", "Eb1", "E1", "F1", "F#1", "G1", "G#1", "A1", "Bb1", "B1", 
"C2", "C#2", "D2", "Eb2", "E2", "F2", "F#2", "G2", "G#2", "A2", "Bb2", "B2", 
"C3", "C#3", "D3", "Eb3", "E3", "F3", "F#3", "G3", "G#3", "A3", "Bb3", "B3", 
"C4", "C#4", "D4", "Eb4", "E4", "F4", "F#4", "G4", "G#4", "A4", "Bb4", "B4", 
"C5", "C#5", "D5", "Eb5", "E5", "F5", "F#5", "G5", "G#5", "A5", "Bb5", "B5", 
"C6", "C#6", "D6", "Eb6", "E6", "F6", "F#6", "G6", "G#6", "A6", "Bb6", "B6", 
"C7", "C#7", "D7", "Eb7", "E7", "F7", "F#7", "G7", "G#7", "A7", "Bb7", "B7", 
"C8", "C#8", "D8", "Eb8", "E8", "F8", "F#8", "G8", "G#8", "A8", "Bb8", "B8", 
"C9", "C#9", "D9", "Eb9", "E9", "F9", "F#9", "G9", "G#9", "A9", "Bb9", "B9", 
"C10", "C#10", "D10", "Eb10", "E10", "F10", "F#10", "G10", "G#10", "A10", "Bb10", "B10", 
"C11", "C#11", "D11", "Eb11", "E11", "F11", "F#11", "G11"};






#ifdef  __STDC__
#define P1(x) x
#define P2(x,y) x, y
#define P3(x,y,z) x, y, z
#define P4(x,y,z,w) x, y, z, w
#else
#define P1(x)
#define P2(x,y)
#define P3(x,y,z)
#define P4(x,y,z,w)
#endif

/* Read short, bigendian: big first. 68000/SPARC style. */
unsigned short
rbshort(fp)
FILE *fp;
{
	unsigned char uc, uc2;
	uc2 = getc(fp);
	uc  = getc(fp);
	return (uc2 << 8) | uc;
}

/* Read long, bigendian: big first. 68000/SPARC style. */
unsigned long
rblong(fp)
FILE *fp;
{
	unsigned char uc, uc2, uc3, uc4;
/*      if (feof(fp))
		fail(readerr);	  /* No worky! */
	uc  = getc(fp);
	uc2 = getc(fp);
	uc3 = getc(fp);
	uc4 = getc(fp);
	return ((long)uc << 24) | ((long)uc2 << 16) | ((long)uc3 << 8) | (long)uc4;
}



void
#ifdef  __STDC__
fail(char *fmt, ...)
#else
fail(va_alist)
va_dcl
#endif
{
	va_list args;
#ifndef __STDC__
	char *fmt;
#endif

#ifndef DOS
	/* single-threaded machines don't really need this */
	fprintf(stderr, "%s: ", myname);
#endif
#ifdef  __STDC__
	va_start(args, fmt);
#else
	va_start(args);
	fmt = va_arg(args, char *);
#endif
	vfprintf(stderr, fmt, args);
	va_end(args);
	fprintf(stderr, "\n");
	exit(2);
}

void
#ifdef  __STDC__
report(char *fmt, ...)
#else
report(va_alist)
va_dcl
#endif
{
	va_list args;
#ifndef __STDC__
	char *fmt;
#endif

	if (! verbose)
		return;
#ifndef DOS
	/* single-threaded machines don't really need this */
	fprintf(stderr, "%s: ", myname);
#endif
#ifdef  __STDC__
	va_start(args, fmt);
#else
	va_start(args);
	fmt = va_arg(args, char *);
#endif
	vfprintf(stderr, fmt, args);
	va_end(args);
	fprintf(stderr, "\n");
	fflush(stderr);
}


char * readpstring(fp, plength)
FILE *fp;
int *plength;
{
char count, *str;
	count = getc(fp);
	if ((str = malloc(count+1)) == 0)
		fail("malloc failed to allocate %d bytes for AIFF-C pstring", count);
	if (fread(str,1,count,fp) < count)
		fail("AIFF-C pstring does not contain %d characters", count);
	*(str+count) = 0;
	if (plength) *plength = count+1;
	if ((count & 1) == 0) { /* Padding byte */
		getc(fp);
		if (plength) (*plength)++;
	}
	return(str);
}

skip (chunksize, fp)
long chunksize;
FILE *fp;
{
	while ((long) (--chunksize) >= 0) {
       if (getc(fp) == EOF)
	   fail("unexpected EOF in AIFF-C chunk");
    }
}

double read_ieee_extended();

aifcopen(infile) 
char *infile;
{
	char buf[4];
	unsigned long totalsize;
	unsigned long chunksize;
	unsigned long offset;
	unsigned long blocksize;
	int littlendian = 0;
	char *endptr;
	int i;
	int plength;

	if (!(ifp=infile?fopen(infile,"r"):stdin)) 
		fail("Cannot read from %s\n", infile?infile:"stdin");

	/* FORM chunk */
	if (fread(buf, 1, 4, ifp) != 4 || strncmp(buf, "FORM", 4) != 0)
		fail("AIFF-C header does not begin with magic word 'FORM'");
	totalsize = rblong(ifp);
	if (fread(buf, 1, 4, ifp) != 4 || strncmp(buf, "AIFC", 4) != 0)
		fail("AIFF-C 'FORM' chunk does not specify 'AIFC' as type");


	/* Process chunks, Most chunks generate a report only */
	/* The SSND chunk must be the last in the file */
	while (1) {
		if (fread(buf, 1, 4, ifp) != 4)
			fail("Missing SSND chunk in AIFF-C file");
		if (strncmp(buf, "COMM", 4) == 0) {
		char *compstr;
			/* COMM (common) chunk */
			chunksize = rblong(ifp);
			channels = rbshort(ifp);
			insamples = rblong(ifp);
			bits = rbshort(ifp);
			if (rate == 0.0)
				rate = read_ieee_extended(ifp);
			else read_ieee_extended(ifp);
			report("%s signal has:", infile);
			report("%10d channels", channels);
			report("%10ld samples", insamples);
			report("%10d bit samples", bits);
			report("%10.1lf Hz sampling rate", rate);
			if (fread(buf, 1, 4, ifp) != 4)
				fail("Missing compression ID in AIFF-C 'COMM' chunk");
			if (strncmp(buf, "DWVW", 4) != 0) 
				fail("Can only handle DWVW compression, not '%s'",buf);
			compstr = readpstring(ifp, &plength);
			if (verbose) {
				report ("%s compressed with %s", infile, compstr );
			}
			free(compstr);
			if (plength + 22 > chunksize)
				fail("AIFF-C 'COMM' chunk exceeded specified chunksize");
			else skip(chunksize-plength-22, ifp);
		}
		else if (strncmp(buf, "FVER", 4) == 0) {
		unsigned long timestamp;
			/* FVER (File Version) chunk */
			chunksize = rblong(ifp);
			if (chunksize != 4)
				fail("AIFF-C 'FVER' chunk has bad size");
			timestamp = rblong(ifp);
			if (timestamp != AIFCVersion1)
				report("AIFF-C 'FVER' chunk shows unknown version %ul\nIncompatibility may result in erroneous conversion");
		}
		else if (strncmp(buf, "MARK", 4) == 0) {
			/* MARK (marker) chunk */

			chunksize = rblong(ifp);
			markers = rbshort(ifp);
			if (!(marker = malloc(markers * sizeof(marker_t))))
				fail("malloc failed to allocate space for %d AIFF-C markers", markers);
			chunksize -= 2;
		    	for (i=0;i<markers;i++) {
				marker[i].id = rbshort(ifp);
				marker[i].position = rblong(ifp);
				marker[i].markerName = readpstring(ifp, &plength);
				chunksize -= 6 + plength;
				if (verbose) {
					report ("Marker %d \"%s\" at position %d",
						marker[i].id, marker[i].markerName, marker[i].position);
				}
			}
			if (chunksize < 0)
				fail("AIFF-C 'MARK' chunk exceeded specified chunksize");
			else skip(chunksize, ifp);
		}
		else if (strncmp(buf, "CMNT", 4) == 0) {
			/* CMNT (comment) chunk */
		unsigned short numcomments;

			chunksize = rblong(ifp);
			if (verbose) {
				numcomments = rbshort(ifp);
				chunksize -= 2;
		    	for (i=0;i<numcomments;i++) {
				unsigned long tstamp;
				unsigned short markerid, count;
				char *text;

					tstamp =  rblong(ifp);
					markerid = rbshort(ifp);
					count = rbshort(ifp);
					if ((text = malloc(count+1)) == 0)
						fail("malloc failed to allocate %d bytes for AIFF-C comment string", count);
					if (fread(text, 1, count, ifp) < count)
						fail("AIFF-C comment string does not contain %d characters", count);
					*(text + count) = 0;
					if (verbose)
						report ("Comment for marker %d: %*s", markerid, count, text);
					free(text);
					chunksize -= 8 + count + (count & 1);
				}
			}
			if (chunksize < 0)
				fail("AIFF-C 'CMNT' chunk exceeded specified chunksize");
			else skip(chunksize,ifp);
		}
		else if (strncmp(buf, "INST", 4) == 0) {
			/* INST (instrument)chunk */
			chunksize = rblong(ifp);
			if (chunksize == 20) {
				if (verbose) {
					report("Instrument parameters:");
					report("Base Note     :%5s", midi_note[getc(ifp)] );
					report("Detune        :%5d", getc(ifp));
					report("Low Note      :%5d", getc(ifp));
					report("High Note     :%5d", getc(ifp));
					report("Low Velocity  :%5d", getc(ifp));
					report("High Velocity :%5d", getc(ifp));
					report("Gain          :%5d", rbshort(ifp));
				} else skip(8,ifp);


				if (loop_mode == NOT_SET)
					loop_mode = rbshort(ifp);
				else rbshort(ifp);

				ls_mark = rbshort(ifp); 
				le_mark = rbshort(ifp);
				if (verbose) {
					switch (loop_mode) {
					case 0:
						report("Sustain Loop  : None");
						break;
					case 1:
						report("Sustain Loop  : Forward - markers [%d, %d]",
							ls_mark, le_mark);	
						break;
					case 2:
						/* Don't think this should happen with Typhoon Wawes */
						report("Sustain Loop  : ForwardBackward - markers [%d, %d] (yamaha wave will be unlooped)",
							ls_mark, le_mark);	
						break;
					default:
						report("Sustain Loop  : Unknown Mode");
					}
					switch (rbshort(ifp)) {
					case 0:
						report("Release Loop  : None");
						rbshort(ifp);rbshort(ifp);
						break;
					case 1:
						report("Release Loop  : Forward - markers [%d, %d]",
							rbshort(ifp),rbshort(ifp));
						break;
					case 2:
						report("Release Loop  : ForwardBackward - markers [%d, %d]",
							rbshort(ifp),rbshort(ifp));
						break;
					default:
						report("Release Loop  : Unknown Mode");
					}
				} else 
					skip(6,ifp);
			} else skip(chunksize,ifp);
		}
		else if (strncmp(buf, "MIDI", 4) == 0) {
			/* MIDI chunk */
			chunksize = rblong(ifp);
			if (verbose) report ("AIFF-C 'MIDI' chunk - ignored");
			skip(chunksize,ifp);
		}
		else if (strncmp(buf, "AESD", 4) == 0) {
			/* 'AESD' (audio recording) chunk */
			chunksize = rblong(ifp);
			if (verbose) report ("AIFF-C 'AESD' chunk - ignored");
			skip(chunksize,ifp);
		}
		else if (strncmp(buf, "APPL", 4) == 0) {
			/* 'APPL' chunk */
			chunksize = rblong(ifp);
			if (verbose) report ("AIFF-C 'APPL' chunk - ignored");
			skip(chunksize,ifp);
		}
		else if (strncmp(buf, "NAME", 4) == 0) {
			/* 'NAME' chunk */
			chunksize = rblong(ifp);
			if (verbose) {
			char *text;
				if ((text = malloc(chunksize+1)) == 0)
					fail("malloc failed to allocate %d bytes for AIFF-C comment string", chunksize);
				if (fread(text, 1, chunksize, ifp) < chunksize)
					fail("AIFF-C comment string does not contain %d characters", chunksize);
				*(text + chunksize) = 0;
				report ("Name: %s", text);
				free(text);
			} else
				skip(chunksize,ifp);
		}
		else if (strncmp(buf, "AUTH", 4) == 0) {
			/* 'AUTH' chunk */
			chunksize = rblong(ifp);
			if (verbose) {
			char *text;
				if ((text = malloc(chunksize+1)) == 0)
					fail("malloc failed to allocate %d bytes for AIFF-C comment string", chunksize);
				if (fread(text, 1, chunksize, ifp) < chunksize)
					fail("AIFF-C comment string does not contain %d characters", chunksize);
				*(text + chunksize) = 0;
				report ("Author: %s", text);
				free(text);
			} else
				skip(chunksize,ifp);
		}
		else if (strncmp(buf, "(c) ", 4) == 0) {
			/* '(c) ' (copyright) chunk */
			chunksize = rblong(ifp);
			if (verbose) {
			char *text;
				if ((text = malloc(chunksize+1)) == 0)
					fail("malloc failed to allocate %d bytes for AIFF-C comment string", chunksize);
				if (fread(text, 1, chunksize, ifp) < chunksize)
					fail("AIFF-C comment string does not contain %d characters", chunksize);
				*(text + chunksize) = 0;
				report ("Copyright (c): %s", text);
				free(text);
			} else
				skip(chunksize,ifp);
		}
		else if (strncmp(buf, "ANNO", 4) == 0) {
			/* 'ANNO' (annotation) chunk */
			chunksize = rblong(ifp);
			if (verbose) {
			char *text;
				if ((text = malloc(chunksize+1)) == 0)
					fail("malloc failed to allocate %d bytes for AIFF-C comment string", chunksize);
				if (fread(text, 1, chunksize, ifp) < chunksize)
					fail("AIFF-C comment string does not contain %d characters", chunksize);
				*(text + chunksize) = 0;
				report ("Annotated: %s", text);
				free(text);
			} else
				skip(chunksize,ifp);
		}
		else if (strncmp(buf, "SSND", 4) == 0) {
			/* SSND chunk */
			chunksize = rblong(ifp);
			offset = rblong(ifp);
			blocksize = rblong(ifp);
			break;
		}
		else {
			chunksize = rblong(ifp);
			/* Skip the chunk using getc() so we may read
			   from a pipe */
			skip(chunksize,ifp);
		}
	}

	/* SSND chunk just read */
	if (blocksize != 0)
		fail("AIFF-C header specifies nonzero blocksize?!?!");
	while ((long) (--offset) >= 0) {
		if (getc(ifp) == EOF)
			fail("unexpected EOF while skipping AIFF-C offset");
	}
	sample = wwidth = bitstr = bsoddbytes = 0;
	bsindex=-1;
}





static unsigned int read_bit()
{

if (bsindex<0) {
	if ((bitstr = getc(ifp))==EOF) return(EOF);
	bitstr &= 0xFF;
	bsindex = 7;
	bsoddbytes = ! bsoddbytes;
	}
return( (bitstr & mask[bsindex--])?1:0);
}

static unsigned int count_zeros()
{
int count;

for (count=0;count<MAXWWM;count++) 
	switch (read_bit()) {
	case 0: continue;
	case 1: return (count);
	case EOF: return (EOF);
	}
return (MAXWWM);
}


static
dwvwnextchannel()
{
        sample = wwidth = bitstr = 0;
        while ( (bsindex != -1) || bsoddbytes) read_bit();
}


int dwvwread(buf, len)
int *buf, len;
{
int wwm, done;
int delta, i;

for (done = 0;done < len; done++) {
	if ((wwm = count_zeros())==EOF) return (done);
	if (wwm)
		wwidth += read_bit() ? - wwm : wwm;
	wwidth = (wwidth+bits)%bits;
	if (wwidth) {
		delta = 1 << wwidth-1;
		for (i=wwidth-1; i--; ) 
			delta += read_bit() << i; 
		switch (read_bit()) {
		case 0: break;
		case 1: delta = -delta; break;
		case EOF: return (done);
		}
		if (delta == 1-MAXDELTA) 
			delta -= read_bit();
	} else 
		delta = 0;
	sample = (sample + delta);
	if (sample >= MAXDELTA) {
		sample = sample-SPAN;
	} else if (sample < -MAXDELTA) {
		sample = sample+SPAN;
	}
	*buf++ = sample;
}
return (done);	
}

double ConvertFromIeeeExtended();

double read_ieee_extended(fp)
FILE *fp;
{
	char buf[10];
	if (fread(buf, 1, 10, fp) != 10)
		fail("EOF while reading IEEE extended number");
	return ConvertFromIeeeExtended(buf);
}


/*
 * C O N V E R T   F R O M   I E E E   E X T E N D E D  
 */

/* 
 * Copyright (C) 1988-1991 Apple Computer, Inc.
 * All rights reserved.
 *
 * Machine-independent I/O routines for IEEE floating-point numbers.
 *
 * NaN's and infinities are converted to HUGE_VAL or HUGE, which
 * happens to be infinity on IEEE machines.  Unfortunately, it is
 * impossible to preserve NaN's in a machine-independent way.
 * Infinities are, however, preserved on IEEE machines.
 *
 * These routines have been tested on the following machines:
 *    Apple Macintosh, MPW 3.1 C compiler
 *    Apple Macintosh, THINK C compiler
 *    Silicon Graphics IRIS, MIPS compiler
 *    Cray X/MP and Y/MP
 *    Digital Equipment VAX
 *
 *
 * Implemented by Malcolm Slaney and Ken Turkowski.
 *
 * Malcolm Slaney contributions during 1988-1990 include big- and little-
 * endian file I/O, conversion to and from Motorola's extended 80-bit
 * floating-point format, and conversions to and from IEEE single-
 * precision floating-point format.
 *
 * In 1991, Ken Turkowski implemented the conversions to and from
 * IEEE double-precision format, added more precision to the extended
 * conversions, and accommodated conversions involving +/- infinity,
 * NaN's, and denormalized numbers.
 */

#ifndef HUGE_VAL
# define HUGE_VAL HUGE
#endif /*HUGE_VAL*/

# define UnsignedToFloat(u)	 (((double)((long)(u - 2147483647L - 1))) + 2147483648.0)

/****************************************************************
 * Extended precision IEEE floating-point conversion routine.
 ****************************************************************/

double ConvertFromIeeeExtended(bytes)
unsigned char *bytes;	/* LCN */
{
    double    f;
    int    expon;
    unsigned long hiMant, loMant;
    
    expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
    hiMant    =    ((unsigned long)(bytes[2] & 0xFF) << 24)
	    |    ((unsigned long)(bytes[3] & 0xFF) << 16)
	    |    ((unsigned long)(bytes[4] & 0xFF) << 8)
	    |    ((unsigned long)(bytes[5] & 0xFF));
    loMant    =    ((unsigned long)(bytes[6] & 0xFF) << 24)
	    |    ((unsigned long)(bytes[7] & 0xFF) << 16)
	    |    ((unsigned long)(bytes[8] & 0xFF) << 8)
	    |    ((unsigned long)(bytes[9] & 0xFF));

    if (expon == 0 && hiMant == 0 && loMant == 0) {
	f = 0;
    }
    else {
	if (expon == 0x7FFF) {    /* Infinity or NaN */
	    f = HUGE_VAL;
	}
	else {
	    expon -= 16383;
	    f  = ldexp(UnsignedToFloat(hiMant), expon-=31);
	    f += ldexp(UnsignedToFloat(loMant), expon-=32);
	}
    }

    if (bytes[0] & 0x80)
	return -f;
    else
	return f;
}


aifcclose()
{
	fclose(ifp);
}



yamopen(outfile)
char * outfile;
{
  struct WaveHeader_ WH;
  int i;
  long AttackLength, LoopLength;

	if (!(ofp=outfile?fopen(outfile,"w"):stdout)) {
		fprintf(stderr,"%s: Cannot write to %s\n", outfile?outfile:"stdout");
		exit(-1);
	}

    /* Write header */


    strncpy(WH.filetype,"LM8953",6);
    for (i=0;i<10;i++) WH.nulls[i]=0;
    for (i=0;i<6;i++)  WH.dummy_aeg[i]=0;
    for (i=0;i<2;i++)  WH.unused[i]=0;
    for (i=0;i<2;i++)  WH.dummy_aeg[i] = 0;
    for (i=2;i<6;i++)  WH.dummy_aeg[i] = 0x7F;
    
    WH.format = 0xc9; /* initially loop off */

    /* the actual sample rate is not that important ! */  
    if (rate < 24000)      WH.sample_rate = 3;
    else if (rate < 41000) WH.sample_rate = 1;
    else			    WH.sample_rate = 2;
    
   
 
    if (insamples >= TXMAXLEN) {
		/* Should never happen with a Typhoon file */
	fprintf(stderr,"%s: Sound too large for TX16W. Truncating, Loop Off\n", myname);
	outsamples = TXMAXLEN;
	loop_mode = LOOP_NONE;
    }
    else if (insamples <= 0x80) {
		/* Should maybe not happen either ?
		   actually I don't have the reference for why waves
		   must be at least 0x80, but that's how tx16w.c for sox
		   does it */
	fprintf(stderr,"%s: Sound too small for TX16W. extending to 128 samples, Loop Off\n", myname);
	outsamples = 0x80;
	loop_mode = LOOP_NONE;
    }
    else outsamples = insamples;

    if (loop_end == NOT_SET) {
    int i;
	if (le_mark == NOT_SET)
    		loop_end = outsamples;
	else
		for (i=0;i<markers;i++) 
			if (marker[i].id == le_mark) {
				loop_end = marker[i].position;
				break;
			}
    }
    if (loop_start == NOT_SET) {
    int i;
        if (ls_mark == NOT_SET)
    		loop_start = loop_end/2;
        else
                for (i=0;i<markers;i++)
                        if (marker[i].id == ls_mark) {
                                loop_start = marker[i].position;
                                break;
                        }
    }   

    if (loop_end < loop_start+0x40) fail("Loop end (%d) before - or too close to - Loop start (%d)", loop_end , loop_start);

    AttackLength = loop_start;
    LoopLength = loop_end - loop_start;


    if (loop_mode == LOOP_FORWARD) {
	WH.format = 0x49; /* loop on */
    }

    WH.atc_length[0] = 0xFF & AttackLength;
    WH.atc_length[1] = 0xFF & (AttackLength >> 8);
    WH.atc_length[2] = (0x01 & (AttackLength >> 16)) +
	magic1[WH.sample_rate];
    
    WH.rpt_length[0] = 0xFF & LoopLength;
    WH.rpt_length[1] = 0xFF & (LoopLength >> 8);
    WH.rpt_length[2] = (0x01 & (LoopLength >> 16)) +
	magic2[WH.sample_rate];
    
  fwrite(&WH,1,32,ofp);
  writedone = 32;
}

yamwrite(buf, len)
int *buf, len;
{
	int i;
	unsigned int w1,w2;
	
	for (i=0;i<len;i+=2) {

	    w1 =  *buf++;
	    if (i+1==len)
		w2 = 0;
	    else {
		w2 =  *buf++;
	    }
	    putc((w1 >> 4) & 0xFF,ofp);
	    putc((((w1 & 0x0F) << 4) | (w2 & 0x0F)) & 0xFF,ofp);
	    putc((w2 >> 4) & 0xFF,ofp);
	    writedone += 3;
	}
}

yamclose()
{
    
    /* If by some reason wave is shorter than 0x80 fill up */

        while (writedone < 32 + 0x40*3 ) {
            putc(0,ofp);
            putc(0,ofp);
            putc(0,ofp);
            writedone += 3;
        }

    /* Fill up to 256 byte blocks; the TX16W seems to like that */

    if (fill256) {
    while ((writedone % 0x100) != 0) {
	putc(0,ofp);
	writedone++;
    }
    }
    fclose (ofp);
}

usage()
{
	fprintf(stderr,"Usage: %s [-s #][-e #][-r #][-l][-n][-f][-v] ifile ofileL [ofileR]\n", myname);
	fprintf(stderr,"\t-s\tset loop start point [default:typhoon loop start or half of loop end point]\n");
	fprintf(stderr,"\t-e\tset loop end point [default:typhoon loop end or wave end]\n");
	fprintf(stderr,"\t-r\tset sampling rate[default:typhoon sample rate]\n");
	fprintf(stderr,"\t-l\tmake a looped wave (even from a non-looped one)\n");
	fprintf(stderr,"\t-n\tmake a non-looped wave (even from a looped one)\n");
	fprintf(stderr,"\t-f\tfill up file to size N*256\n");
	fprintf(stderr,"\t-v\ttalk a lot\n");
	fprintf(stderr,"\tifile\tinput file (Typhoon compressed file) [default:stdin]\n");
	fprintf(stderr,"\t\tinput file may also be given by '-i ifile'\n");
	fprintf(stderr,"\tofileL\tmono or left channel output file (Yamaha format file) [default:stdout]\n");
	fprintf(stderr,"\tofileR\tright channel output file (Yamaha format file) [default:stdout]\n");
	fprintf(stderr,"\t\toutput files may also be given by '-o ofileL -o ofileR'\n");
	exit (-1);
}

main(argc, argv)
int argc;
char *argv[];
{
char *desc = "Convert sample from TYPHOON format to YAMAHA OS format";
char *opts = "i:o:r:s:e:lnvf";
char *infile=NULL, *outfileleft=NULL, *outfileright=NULL;
int opt;


int buf[BUFSIZE];
int i,samples;
int written = 0;
FILE * cfp;

extern int getopt();
extern int optind;
extern char *optarg;


	myname=argv[0];
	while ((opt = getopt(argc, argv, opts)) != EOF)  switch (opt) {
	case 'i':
		infile = optarg;
		break;
	case 'o':
		outfileleft = outfileright;
		outfileright = optarg;
		break;
	case 'r':
		rate = atof(optarg);
		break;
	case 's':
		loop_start = atol(optarg);
		break;
	case 'e':
		loop_end= atol(optarg);
		break;
	case 'l':
		loop_mode = LOOP_FORWARD;
		break;
	case 'n':
		loop_mode = LOOP_NONE;
		break;
	case 'f':
		fill256=1;
		break;
	case 'v':
		verbose=1;
		break;
	case '?':
		usage();
	}

	while (optind < argc) {
		if (!infile)
			infile = argv[optind++];
		else if (!outfileright)
			outfileright = argv[optind++];
		else if (!outfileleft) {
			outfileleft = outfileright;
			outfileright = argv[optind++];
		}	
		else optind++;
	}


	aifcopen(infile);

	if (channels == 2) {
		samples=0;
		yamopen(outfileleft);
		while (samples<insamples) {
			i = dwvwread(buf, (insamples-samples>BUFSIZE)?BUFSIZE:insamples-samples);
			if (!i) break;
			yamwrite(buf,(outsamples-samples>i)?i:outsamples-samples); 
			samples += i;
		}
		yamclose();
		dwvwnextchannel();
	}
	samples=0;
	yamopen(outfileright);
	while (samples<insamples) {
		i = dwvwread(buf, (insamples-samples>BUFSIZE)?BUFSIZE:insamples-samples);
		if (!i) break;
		yamwrite(buf,(outsamples-samples>i)?i:outsamples-samples); 
		samples += i;
	}
	yamclose();
	aifcclose();
}


