/************************************************************************/
/*									*/
/* str.c - plays sound-/noisetracker files on a SparcStation		*/
/*									*/
/* Authors:								*/
/*	    Liam Corner - zenith@dcs.warwick.ac.uk			*/
/*	    Marc Espie - espie@dmi.ens.fr				*/
/* Minor modificatios for Linux by					*/
/*	    Hannu Savolainen - hannu@voxware.pp.fi			*/
/* Command-line switches added by					*/
/*	    Muhammad Saggaf - alsaggaf@erl.mit.edu			*/
/*	    Leif Kornstaedt - l_kornst@informatik.uni-kl.de		*/
/* Relaxed program name checking					*/
/*	    Craig Metz - cmetz@thor.tjhsst.edu				*/
/* Stereo and 16 bit support by						*/
/*	    Hannu							*/
/* Some effects and minor bugfixes/ameliorations by			*/
/*	    Leif Kornstaedt - l_kornst@informatik.uni-kl.de		*/
/*									*/
/* Version: 1.20 - 3 November 1991 / 31 January 1993			*/
/* 01/31/93 effects added						*/
/*	    by Leif Kornstaedt						*/
/* 08/27/92 modified to use integer arithmetic				*/
/* 08/31/92 SB Pro stereo support					*/
/*	    by Hannu Savolainen						*/
/*									*/
/************************************************************************/

#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <stdlib.h>
#ifdef __STDC__
#include <fcntl.h>
#include <string.h>
#else /* __STDC__ */
#include <getopt.h>
#include <fcntl.h>
#include <strings.h>
#endif /* __STDC__ */
#include <errno.h>
#include <sys/stat.h>

#include <sys/soundcard.h>
#include "str.h"

/* output file name or dsp device */
char AUDIO[256] = AUDIONAME;

int audio;			/* file handle */
unsigned char *audiobuf;
int abuf_size;
int abuf_ptr;

/* for analyzing the command line: */
char *command;			/* the actual command name used (argv[0]) */
int loop_forever = FALSE;
int quiet_mode = FALSE, verbose_mode = FALSE;
static int use_zcat = FALSE;
int mute[4] = { FALSE, FALSE, FALSE, FALSE };
int dsp_stereo = DSP_DEFAULT_STEREO;
int dsp_samplesize = DSP_DEFAULT_SAMPLESIZE;
long dsp_speed = DEFAULT_DSP_SPEED;

FILE *fp;

void play_song(void);
extern void describe_error(void); /* from ../help.c */

void
usage(command)
char *command;
{
    fprintf(stderr, "Usage: %s [ -cqvzS1234 -b size -s kHz -o output_file ] "
	    "[ filename ... ]\n", command);
    fprintf(stderr, "\t-c\t\tplay infinitely\n"
		    "\t-q\t\tquiet mode\n"
		    "\t-v\t\tverbose mode\n"
		    "\t-z\t\tuse zcat on file\n"
		    "\t-1234\t\tmute voices 1, 2, 3 or 4\n"
		    "\t-S\t\ttoggle stereo\n"
		    "\t-b size\t\tset sample size (8 or 16 bits)\n"
		    "\t-s kHz\t\tuse sampling speed kHz\n"
		    "\t-o file\t\tredirect output to a file\n");
     exit(EXIT_FAILURE);
}

char *
find_file(name)
char *name;
{
    char *dirs;
    struct stat statbuffer;
    static char fname[256];

    strcpy(fname, name);
    if(stat(fname, &statbuffer) != -1 && errno != -ENOENT)
	return fname;
    else {
	dirs = getenv("MODPATH");
	while(dirs != NULL && strlen(dirs)) {
	    if(strchr(dirs, ':') != NULL) {
		strncpy(fname, dirs, strchr(dirs, ':') - dirs);
		fname[strchr(dirs, ':') - dirs] = '\0';
	    } else
		strcpy(fname, dirs);
	    if(fname[strlen(fname) - 1] != '/')
		strcat(fname, "/");
	    strcat(fname, name);
	    if(stat(fname, &statbuffer) != -1 && errno != -ENOENT)
		return fname;
	    if((dirs = strchr(dirs, ':')) != NULL)
		dirs++;
	}
    }

    return NULL;
}

int 
main(argc, argv)
int argc;
char *argv[];
{
    int outdev, c;
    struct stat statbuffer;
    int opmode = O_WRONLY;		/* mode for opening the output device */
    char zcat_cmd[256];
    char *currfile;

    command = argv[0];

    while((c = getopt(argc, argv,
				  "cqvz1234Sb:s:o:"
					      )) != EOF)
	switch(c) {
	case 'c':			/* play infinitely */
	    loop_forever = TRUE;
	    break;
	case 'q':			/* quiet mode */
	    quiet_mode = TRUE;
	    break;
	case 'v':			/* verbose mode */
	    verbose_mode = TRUE;
	    break;
	case 'z':			/* use zcat on file */
	    use_zcat = TRUE;
	    break;
	case '1':
	case '2':
	case '3':
	case '4':
	    mute[c - '1'] = TRUE;
	    break;
	case 'S':			/* toggle stereo */
	    dsp_stereo = !DSP_DEFAULT_STEREO;
	    break;
	case 'b':			/* sample size */
	    dsp_samplesize = atoi(optarg);
	    break;
	case 's':			/* dsp speed */
	    dsp_speed = atoi(optarg);
	    if(dsp_speed < 300)
		dsp_speed *= 1000;
	    break;
	case 'o':			/* output file */
	    strcpy(AUDIO, optarg);
	    break;
	default:
	    usage(command);
	}

    /* output to a character device (TRUE) or a file (FALSE)? */
    outdev = !stat(AUDIO, &statbuffer) && S_ISCHR(statbuffer.st_mode);

    if(!outdev)
	opmode |= O_CREAT;		/* file: create it */
    if((audio = open(AUDIO, opmode, 0)) == -1) {
	perror(AUDIO);
	describe_error();
	exit(EXIT_FAILURE);
    }


    if(outdev) {			/* set dsp parameters */
	if(ioctl(audio, SNDCTL_DSP_SAMPLESIZE,
		&dsp_samplesize) == -1)
	    dsp_samplesize = 8;
	if(ioctl(audio, SNDCTL_DSP_STEREO, &dsp_stereo) == -1)
	    dsp_stereo = FALSE;
	if(ioctl(audio, SNDCTL_DSP_SPEED, &dsp_speed) == -1) {
	    fprintf(stderr, "%s: unable to set playback speed\n", command);
	    perror(AUDIO);
	    exit(EXIT_FAILURE);
	}
	if(!quiet_mode)
	    printf("Playback speed %d Hz (%s) %d bits/sample.\n", dsp_speed,
		    dsp_stereo? "stereo": "mono", dsp_samplesize);
    }

    if(outdev) {			/* get the dsp buffer size */
	ioctl(audio, SNDCTL_DSP_GETBLKSIZE, &abuf_size);
	if(abuf_size < 1) {
 	    perror("audio_size");
	    close(audio);
	    exit(EXIT_FAILURE);
	}
    } else {				/* to a file: use block size of 1kB */
	abuf_size = 1024;
	loop_forever = 0;
    }

    if((audiobuf = malloc(abuf_size)) == NULL) {
	fprintf(stderr, "%s: unable to allocate output buffer\n", command);
	close(audio);
	exit(EXIT_FAILURE);
    }
    abuf_ptr = 0;

    if(optind > argc - 1) {
	if(use_zcat) {
	    if((fp = popen(ZCAT, "rb")) == NULL) {
		fprintf(stderr, "%s: can't execute " ZCAT ".\n", command);
		return(EXIT_FAILURE);
	    }
	} else
	    fp = stdin;
	play_song();
	if(use_zcat)
	    pclose(fp);
    } else
	while(optind <= argc - 1)
	    if((currfile = find_file(argv[optind])) == NULL) {
		fprintf(stderr, "%s: can't find file %s - skipping.\n",
			command, argv[optind]);
		optind++;
	    } else if(use_zcat) {
		if((fp = popen(strcat(strcpy(zcat_cmd, ZCAT " "),
			currfile), "rb")) == NULL) {
		    fprintf(stderr, "%s: can't execute " ZCAT " %s.\n",
			    command, currfile);
		    exit(EXIT_FAILURE);
		} else {
		    play_song();
		    pclose(fp);
		    if(!loop_forever)
			optind++;
		}
	    } else {
		if((fp = fopen(currfile, "rb")) == NULL) {
		    fprintf(stderr, "%s: unable to open tune file %s.\n",
			    command, currfile);
		    exit(EXIT_FAILURE);
		} else {
		    play_song();
		    fclose(fp);
		    if(!loop_forever)
			optind++;
		}
	    }

    if(abuf_ptr)
	write(audio, audiobuf, abuf_ptr);
    close(audio);

    return EXIT_SUCCESS;
}
