
/*
*	gmod.c	- Module player for GUS and Linux.
*		(C) Hannu Savolainen, 1993
*
*	NOTE!	This program doesn't try to be a complete module player.
*		It's just a too I used while developing the driver. In
*		addition it can be used as an example on programming
*		the VoxWare Sound Driver with GUS.
*/

/*
* Many modifications have been done by Andrew J. Robinson.
* Refer to the ChangeLog for details.
*/


#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/soundcard.h>
#include <sys/ultrasound.h>
#include <stdio.h>

#include <time.h>     /* for randomize */
#include <stdlib.h>   /* for randomize */

#include "defines.h"
#include "structs.h"
#include "tables.h"
#include "protos.h"

SEQ_DEFINEBUF (2048);

int pattern_len[MAX_POSITION];
int pattern_tempo[MAX_POSITION];
pattern *pattern_table[MAX_PATTERN * MAX_TRACK];

struct voice_info voices[MAX_TRACK];

int tune[MAX_POSITION];
short voice_table[MAX_POSITION][MAX_TRACK];
double tick_duration;

int seqfd, mixerfd;
unsigned char sample_ok[MAX_SAMPLES], sample_vol[MAX_SAMPLES];
short sample_ft[MAX_SAMPLES];
int tmp, gus_dev;
double this_time, next_time;
int ticks_per_division;

unsigned char stop_flag = 0;
unsigned char dump_enabled = 1;
unsigned char background = 0;

int
main (int argc, char *argv[])
{
  static char ident[] = IDENT;
  int i, n, j, name_start, outcount, mix_devmask, num_files, rand_swap;
  int exit_code = ERR_NOERROR;
  struct synth_info info;
  struct song_info song_char;
  struct options_info options =
  {255, 0, 0, 1, 1, 0, 0, 0, 255, 0, 0};
  char mixer_name[13];
  char *tmp_argv;

  name_start = parse_args (argc, argv, &options);

  printf ("GMOD version 1.3a\n");
  printf ("Original source (C) Hannu Savolainen, 1993\n");
  printf ("Modifications by Andrew J. Robinson and Robert Sanders\n\n");

  if (name_start == argc)
    {
      printf ("\nUsage: %s [options] modfile . . .\n", argv[0]);
      printf ("Use %s -h for help.\n\n", argv[0]);
      exit (ERR_BADARGS);
    }

  /*
  if (background) {
	i = fork();
        if (i == -1)
		exit(ERR_FORK);
	else if (i != 0)
		exit(ERR_NOERROR);
	}
*/

  if ((seqfd = open ("/dev/sequencer", O_WRONLY, 0)) == -1)
    {
      perror ("/dev/sequencer");
      exit (ERR_SEQUENCER);
    }

  if (ioctl (seqfd, SNDCTL_SEQ_NRSYNTHS, &n) == -1)
    {
      perror ("/dev/sequencer");
      exit (ERR_SEQUENCER);
    }

  for (i = 0; i < n; i++)
    {
      info.device = i;

      if (ioctl (seqfd, SNDCTL_SYNTH_INFO, &info) == -1)
	{
	  perror ("/dev/sequencer");
	  exit (ERR_SEQUENCER);
	}

      if (info.synth_type == SYNTH_TYPE_SAMPLE
	  && info.synth_subtype == SAMPLE_TYPE_GUS)
	gus_dev = i;
    }

  if (gus_dev == -1)
    {
      fprintf (stderr, "Gravis Ultrasound not detected\n");
      exit (ERR_NOGUS);
    }

  if (options.mixer == 255)
    sprintf (mixer_name, "%s", "/dev/mixer");
  else
    sprintf (mixer_name, "%s%u", "/dev/mixer", options.mixer);

  if ((mixerfd = open (mixer_name, O_RDWR, 0)) == -1)
    printf ("Mixer (%s) not available.\n", mixer_name);
  else
    {
      ioctl (mixerfd, SOUND_MIXER_READ_DEVMASK, &mix_devmask);
      if (!(mix_devmask & SOUND_MASK_SYNTH))
	{
	  printf ("This mixer (%s) does not support volume control.\n", mixer_name);
	  close (mixerfd);
	  mixerfd = -1;
	}
    }

  stop_all_channels ();
  if (!background)
    terminal_set (TERMINAL_RAW);

  num_files = argc - name_start;

  if (options.randomize) {
  srand (time(NULL));
  for (i = name_start; i < argc; i++) {
	rand_swap = name_start + (rand() % num_files);
	tmp_argv = argv[i];
	argv[i] = argv[rand_swap];
	argv[rand_swap] = tmp_argv;
  }
	}

  for (i = name_start; i < argc; i++)
    {
      for (j = 0; j < MAX_PATTERN; j++)
	pattern_table[j] = NULL;

      if (load_module (argv[i], &song_char, options))
	{
	  stop_flag = 0;

	  ioctl (seqfd, SNDCTL_SEQ_SYNC, 0);

	  if (song_char.nr_channels < 14)
	    {
	      GUS_NUMVOICES (gus_dev, 14);
	    }
	  else
	    {
	      GUS_NUMVOICES (gus_dev, song_char.nr_channels);
	    }
	  SEQ_DUMPBUF ();
	  ioctl (seqfd, SNDCTL_SEQ_SYNC, 0);

	  timer_set (TIMER_ON);
	  play_module (argv[i], &song_char, options);

	  do
	    {
	      pause ();
	      if (!dump_enabled)
		{
		  dump_enabled = 1;
		  _seqbufptr = 0;
		  ioctl (seqfd, SNDCTL_SEQ_RESET, 0);
		  stop_all_channels ();
		}
	      ioctl (seqfd, SNDCTL_SEQ_GETOUTCOUNT, &outcount);
	    }
	  while (outcount != 1024);

	  timer_set (TIMER_OFF);

	  if (stop_flag == STOP_EXIT)
	    i = argc - 1;
	  else if (stop_flag == STOP_PREV)
	    {
	      i -= 2;
	      if (i < (name_start - 1))
		i = name_start - 1;
	    }
	}
      else
	exit_code = ERR_BADLOAD;
    }

  SEQ_DUMPBUF ();

  close (seqfd);
  if (!background)
    terminal_set (TERMINAL_RESTORE);
  exit (exit_code);
}
