
char ansi_c_is_very_stupid_and_needs_a_variable_here;

#if	defined(BLASTER) || defined(SBLAST)
/*
 * Copyright 1992 Rick Richardson
 * 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. 
 * Rick Richardson, Lance Norskog And Sundry Contributors are not
 * responsible for the consequences of using this software.
 */

/*
 * Direct to Sound Blaster device driver.
 * SBLAST patches by John T. Kohl.
 */

/* Direct to Linux sound driver
 * LINUX_PLAYER patches added by Greg Lee from code
 * found in recplay.c (Linux sound driver distribution)
 * and raw.c (Sox distribution).
 */

#include <sys/types.h>
#ifdef SBLAST
#include <i386/isa/sblast.h>
#else

#ifdef LINUX_PLAYER
#include <malloc.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#undef BYTE
#else

#include <sys/sb.h>
#endif
#endif

#include <signal.h>
#include "st.h"

/* Private data for SKEL file */
typedef struct sbdspstuff {
	int	samples;		/* bytes remaining in current block */
} *sbdsp_t;

IMPORT float volume, amplitude;
IMPORT int summary, verbose;

static got_int = 0;

#ifdef LINUX_PLAYER
IMPORT int playing;
static int dspfd = 0;
static int abuf_size = 0;
static int abuf_cnt = 0;
static char *audiobuf;
static int tmp;
#endif

static void
sigint(s)
{
	if (s) got_int = 1;
	else signal(SIGINT, sigint);
}

/*
 * Do anything required before you start reading samples.
 * Read file header. 
 *	Find out sampling rate, 
 *	size and style of samples, 
 *	mono/stereo/quad.
 */
sbdspstartread(ft) 
ft_t ft;
{
	sbdsp_t sbdsp = (sbdsp_t) ft->priv;
	int off = 0;

	/* If you need to seek around the input file. */
	if (0 && ! ft->seekable)
		fail("SKEL input file must be a file, not a pipe");

#ifdef LINUX_PLAYER
/* ?? */
#else
	if (!ft->info.rate)
		ft->info.rate = 11000;
	ft->info.size = BYTE;
	ft->info.style = UNSIGNED;
	ft->info.channels = 1;
	ioctl(fileno(ft->fp), DSP_IOCTL_RESET, 0);
#ifdef SBLAST
	ioctl(fileno(ft->fp), DSP_IOCTL_VOICE, &off);
	ioctl(fileno(ft->fp), DSP_IOCTL_SPEED, &ft->info.rate);
#else
	ioctl(fileno(ft->fp), DSP_IOCTL_VOICE, 0);
	ioctl(fileno(ft->fp), DSP_IOCTL_SPEED, ft->info.rate);
#endif
#endif
	sigint(0);	/* Prepare to catch SIGINT */
}

/*
 * Read up to len samples from file.
 * Convert to signed longs.
 * Place in buf[].
 * Return number of samples read.
 */

sbdspread(ft, buf, len) 
ft_t ft;
long *buf, len;
{
	sbdsp_t sbdsp = (sbdsp_t) ft->priv;
	int		rc;

	if (got_int) return (0);
	rc = rawread(ft, buf, len);
	if (rc < 0) return 0;
	return (rc);
}

/*
 * Do anything required when you stop reading samples.  
 * Don't close input file! 
 */
sbdspstopread(ft) 
ft_t ft;
{
#ifdef SBLAST
	ioctl(fileno(ft->fp), DSP_IOCTL_FLUSH, 0);
#endif
}

sbdspstartwrite(ft) 
ft_t ft;
{
	sbdsp_t sbdsp = (sbdsp_t) ft->priv;
#ifdef SBLAST
	int on = 1;
#else
#ifdef LINUX_PLAYER
	int samplesize = 8, dsp_stereo;
	char *dspname;
#endif
#endif

	/* If you have to seek around the output file */
	if (0 && ! ft->seekable)
		fail("Output .sbdsp file must be a file, not a pipe");

#ifdef LINUX_PLAYER

	if (playing == 2) dspname = "/dev/dsp1";
	else dspname = "/dev/dsp";

	if (ft->info.rate == 0.0) ft->info.rate = 8000;
	if (ft->info.size == -1) ft->info.size = BYTE;
	if (ft->info.size == BYTE) samplesize = 8;
	else if (ft->info.size == WORD) samplesize = 16;

	ft->info.style = UNSIGNED;
	if (ft->info.channels == -1) ft->info.channels = 1;
	else if (ft->info.channels > 2) ft->info.channels = 2;

	dspfd = open (dspname, O_WRONLY, 0);
	if (dspfd == -1) {
		perror (dspname);
		exit (-1);
	}

	ioctl (dspfd, SNDCTL_DSP_GETBLKSIZE, &abuf_size);
	if (abuf_size < 4096 || abuf_size > 65536) {
		if (abuf_size == -1)
		perror (dspname);
      		else
			fprintf (stderr, "Invalid audio buffers size %d\n", abuf_size);
      		exit (-1);
	}

	if ((audiobuf = malloc (abuf_size)) == NULL) {
		fprintf (stderr,
			"Unable to allocate input/output buffer of size %d\n", abuf_size);
		exit (-1);
	}

	tmp = samplesize;
	if (ioctl(dspfd, SNDCTL_DSP_SAMPLESIZE, &tmp) == -1) {
  		fprintf(stderr, "Unable to set the sample size to %d\n", samplesize);
  		exit(-1);
	}

	if (ft->info.channels == 2) dsp_stereo = 1;
	else dsp_stereo = 0;

	tmp = dsp_stereo;
	if (ioctl (dspfd, SNDCTL_DSP_STEREO, &tmp) == -1) {
		ft->info.channels = 1;
		fprintf(stderr, "Couldn't set to %s\n", dsp_stereo?  "stereo":"mono");
		dsp_stereo = 0;
	}

	tmp = ft->info.rate;
	ioctl (dspfd, SNDCTL_DSP_SPEED, &tmp);
	if (ft->info.rate != tmp) {
		if (ft->info.rate - tmp > tmp/10 || tmp - ft->info.rate > tmp/10)
		fprintf (stderr, "Unable to set audio speed to %d (set to %d)\n",
			ft->info.rate, tmp);
		ft->info.rate = tmp;
	}
#else

	if (!ft->info.rate)
		ft->info.rate = 11000;
	ft->info.size = BYTE;
	ft->info.style = UNSIGNED;
	ft->info.channels = 1;
	ioctl(fileno(ft->fp), DSP_IOCTL_RESET, 0);
#ifdef SBLAST
	ioctl(fileno(ft->fp), DSP_IOCTL_FLUSH, 0);
	ioctl(fileno(ft->fp), DSP_IOCTL_VOICE, &on);
	ioctl(fileno(ft->fp), DSP_IOCTL_SPEED, &ft->info.rate);
#else
	ioctl(fileno(ft->fp), DSP_IOCTL_VOICE, 1);
	ioctl(fileno(ft->fp), DSP_IOCTL_SPEED, ft->info.rate);
#endif
#endif /* LINUX_PLAYER */
}

#ifdef LINUX_PLAYER

void
dspflush()
{
	char *dspname;

	if (playing == 2) dspname = "/dev/dsp1";
	else dspname = "/dev/dsp";

	if (write (dspfd, audiobuf, abuf_cnt) != abuf_cnt) {
		perror (dspname);
		exit (-1);
	}
	abuf_cnt = 0;
}

void
dspput(c)
int c;
{
	if (abuf_cnt == abuf_size) dspflush();
	*(audiobuf + abuf_cnt) = c;
	abuf_cnt++;
}


/* Write short. */
void
dspshort(ft, ui)
ft_t ft;
unsigned int ui;
{
	int c;
	unsigned short us;
	us = ui;
	if (ft->swap)
		us = swapw(us);
	c = (us >> 8) & 0xff;
	dspput(c);
	c = us & 0xff;
	dspput(c);
}

void
sbdspwrite(ft, buf, nsamp) 
ft_t ft;
long *buf, nsamp;
{
	register int datum;
	int abs;
	int done = 0;

	char c;
	unsigned char uc;
	short s;
	unsigned short us;
	long l;
	unsigned long ul;
	double d;
	
	switch(ft->info.size) {
		case BYTE: switch(ft->info.style) {
			case SIGN2:
				while(done < nsamp) {
					/* scale signed up to long's range */
					datum = RIGHT(*buf++, 24);
					dspput(datum);
					done++;
				}
				return;
			case UNSIGNED:
				while(done < nsamp) {
					/* scale signed up to long's range */
					datum = RIGHT(*buf++, 24);
					/* Convert to unsigned */
					datum ^= 128;
					dspput(datum);
					done++;
				}
				return;
			case ULAW:
				/* grab table from Posk stuff */
				while(done < nsamp) {
					/* scale signed up to long's range */
					datum = RIGHT(*buf++, 16);
					datum = st_linear_to_ulaw(datum);
					dspput(datum);
					done++;
				}
				return;
			case ALAW:
				fail("No A-Law support");
				return;
			}
		case WORD: switch(ft->info.style) {
			case SIGN2:
				while(done < nsamp) {
					/* scale signed up to long's range */
					datum = RIGHT(*buf++, 16);
					dspshort(ft, datum);
					done++;
				}
				return;
			case UNSIGNED:
				while(done < nsamp) {
					/* scale signed up to long's range */
					datum = RIGHT(*buf++, 16);
					/* Convert to unsigned */
					datum ^= 0x8000;
					dspshort(ft, datum);
					done++;
				}
				return;
			case ULAW:
				fail("No U-Law support for shorts");
				return;
			case ALAW:
				fail("No A-Law support");
				return;
			}
		}
	/* My, there's a lot of code missing! */
	fail("Drop through in rawwrite!");
}


#else

sbdspwrite(ft, buf, len) 
ft_t ft;
long *buf, len;
{
	sbdsp_t sbdsp = (sbdsp_t) ft->priv;

	if (len == 0) return 0;
	return (rawwrite(ft, buf, len));
}

#endif /* LINUX_PLAYER */

sbdspstopwrite(ft) 
ft_t ft;
{
	/* All samples are already written out. */
	/* If file header needs fixing up, for example it needs the */
 	/* the number of samples in a field, seek back and write them here. */

#ifdef LINUX_PLAYER
	dspflush();
	close(dspfd);
#else
	fflush(ft->fp);
	ioctl(fileno(ft->fp), DSP_IOCTL_FLUSH, 0);
#endif
}
#endif
